From f5895943d91b41b0368830cdb6eaffb8eda0f4c8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 14 Mar 2014 17:44:49 +0000 Subject: KEYS: Move the flags representing required permission to linux/key.h Move the flags representing required permission to linux/key.h as the perm parameter of security_key_permission() is in terms of them - and not the permissions mask flags used in key->perm. Whilst we're at it: (1) Rename them to be KEY_NEED_xxx rather than KEY_xxx to avoid collisions with symbols in uapi/linux/input.h. (2) Don't use key_perm_t for a mask of required permissions, but rather limit it to the permissions mask attached to the key and arguments related directly to that. Signed-off-by: David Howells Tested-by: Dmitry Kasatkin --- include/linux/key.h | 11 +++++++++++ include/linux/security.h | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/key.h b/include/linux/key.h index 80d677483e31..cd0abb8c9c33 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -309,6 +309,17 @@ static inline key_serial_t key_serial(const struct key *key) extern void key_set_timeout(struct key *, unsigned); +/* + * The permissions required on a key that we're looking up. + */ +#define KEY_NEED_VIEW 0x01 /* Require permission to view attributes */ +#define KEY_NEED_READ 0x02 /* Require permission to read content */ +#define KEY_NEED_WRITE 0x04 /* Require permission to update / modify */ +#define KEY_NEED_SEARCH 0x08 /* Require permission to search (keyring) or find (key) */ +#define KEY_NEED_LINK 0x10 /* Require permission to link */ +#define KEY_NEED_SETATTR 0x20 /* Require permission to change attributes */ +#define KEY_NEED_ALL 0x3f /* All the above permissions */ + /** * key_is_instantiated - Determine if a key has been positively instantiated * @key: The key to check. diff --git a/include/linux/security.h b/include/linux/security.h index 5623a7f965b7..3a5ed0cd2751 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1707,7 +1707,7 @@ struct security_operations { void (*key_free) (struct key *key); int (*key_permission) (key_ref_t key_ref, const struct cred *cred, - key_perm_t perm); + unsigned perm); int (*key_getsecurity)(struct key *key, char **_buffer); #endif /* CONFIG_KEYS */ @@ -3026,7 +3026,7 @@ static inline int security_path_chroot(struct path *path) int security_key_alloc(struct key *key, const struct cred *cred, unsigned long flags); void security_key_free(struct key *key); int security_key_permission(key_ref_t key_ref, - const struct cred *cred, key_perm_t perm); + const struct cred *cred, unsigned perm); int security_key_getsecurity(struct key *key, char **_buffer); #else @@ -3044,7 +3044,7 @@ static inline void security_key_free(struct key *key) static inline int security_key_permission(key_ref_t key_ref, const struct cred *cred, - key_perm_t perm) + unsigned perm) { return 0; } -- cgit v1.2.3 From 476d4af22cec8a9ebc90137712e5ab7070b7379d Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 3 Oct 2014 17:25:00 +0100 Subject: iio: inkern: add iio_read_channel_average_raw Add iio_read_channel_average_raw to support reading averaged raw values in consumer drivers. Signed-off-by: Sebastian Reichel Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 18 ++++++++++++++++++ include/linux/iio/consumer.h | 13 +++++++++++++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 0cf5f8e06cfc..adeba5a0ecf7 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -443,6 +443,24 @@ err_unlock: } EXPORT_SYMBOL_GPL(iio_read_channel_raw); +int iio_read_channel_average_raw(struct iio_channel *chan, int *val) +{ + int ret; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (chan->indio_dev->info == NULL) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); + static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, int raw, int *processed, unsigned int scale) { diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 2752b1fd12be..651f9a0e2765 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -122,6 +122,19 @@ struct iio_channel int iio_read_channel_raw(struct iio_channel *chan, int *val); +/** + * iio_read_channel_average_raw() - read from a given channel + * @chan: The channel being queried. + * @val: Value read back. + * + * Note raw reads from iio channels are in adc counts and hence + * scale will need to be applied if standard units required. + * + * In opposit to the normal iio_read_channel_raw this function + * returns the average of multiple reads. + */ +int iio_read_channel_average_raw(struct iio_channel *chan, int *val); + /** * iio_read_channel_processed() - read processed value from a given channel * @chan: The channel being queried. -- cgit v1.2.3 From 5d2e9fadf43e87e690bfbe607313bf9be47867e4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 25 Mar 2014 10:30:47 +0200 Subject: Bluetooth: Add scan_rsp parameter to mgmt_device_found() In preparation for being able to merge ADV_IND/ADV_SCAN_IND and SCAN_RSP together into a single device found event add a second parameter to the mgmt_device_found function. For now all callers pass NULL as this parameters since we don't yet have storing of the last received advertising report. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/hci_event.c | 10 +++++----- net/bluetooth/mgmt.c | 16 +++++++++++----- 3 files changed, 19 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5f8bc05694ac..a1b8eab8a47d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1264,8 +1264,9 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, u8 *randomizer192, u8 *hash256, u8 *randomizer256, u8 status); void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, - u8 ssp, u8 *eir, u16 eir_len); + u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 + ssp, u8 *eir, u16 eir_len, u8 *scan_rsp, + u8 scan_rsp_len); void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); void mgmt_discovering(struct hci_dev *hdev, u8 discovering); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2388f2c09887..4a2c919d5908 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1827,7 +1827,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, 0, !name_known, ssp, NULL, - 0); + 0, NULL, 0); } hci_dev_unlock(hdev); @@ -3102,7 +3102,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, ssp, NULL, 0); + !name_known, ssp, NULL, 0, NULL, 0); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); @@ -3120,7 +3120,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, false, &ssp); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, ssp, NULL, 0); + !name_known, ssp, NULL, 0, NULL, 0); } } @@ -3309,7 +3309,7 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, eir_len = eir_get_length(info->data, sizeof(info->data)); mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, !name_known, - ssp, info->data, eir_len); + ssp, info->data, eir_len, NULL, 0); } hci_dev_unlock(hdev); @@ -3972,7 +3972,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, } mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, rssi, 0, 1, - data, len); + data, len, NULL, 0); } static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d2d4e0d5aed0..a0ef5c076880 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5669,7 +5669,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 - ssp, u8 *eir, u16 eir_len) + ssp, u8 *eir, u16 eir_len, u8 *scan_rsp, + u8 scan_rsp_len) { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; @@ -5679,8 +5680,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, if (!hci_discovery_active(hdev)) return; - /* Leave 5 bytes for a potential CoD field */ - if (sizeof(*ev) + eir_len + 5 > sizeof(buf)) + /* Make sure that the buffer is big enough. The 5 extra bytes + * are for the potential CoD field. + */ + if (sizeof(*ev) + eir_len + scan_rsp_len + 5 > sizeof(buf)) return; memset(buf, 0, sizeof(buf)); @@ -5707,8 +5710,11 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, dev_class, 3); - ev->eir_len = cpu_to_le16(eir_len); - ev_size = sizeof(*ev) + eir_len; + if (scan_rsp_len > 0) + memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len); + + ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len); + ev_size = sizeof(*ev) + eir_len + scan_rsp_len; mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); } -- cgit v1.2.3 From 3c857757ef6e5a4e472bd3e5c934709c2eb482af Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 25 Mar 2014 10:30:49 +0200 Subject: Bluetooth: Add directed advertising support through connect() When we're in peripheral mode (HCI_ADVERTISING flag is set) the most natural mapping of connect() is to perform directed advertising to the peer device. This patch does the necessary changes to enable directed advertising and keeps the hci_conn state as BT_CONNECT in a similar way as is done for central or BR/EDR connection initiation. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_conn.c | 77 ++++++++++++++++++++++++++++++++++++++++----- net/bluetooth/hci_event.c | 19 +++++++++-- 3 files changed, 87 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index be150cf8cd43..4261a67682c0 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -367,6 +367,7 @@ enum { #define HCI_ERROR_REMOTE_POWER_OFF 0x15 #define HCI_ERROR_LOCAL_HOST_TERM 0x16 #define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 +#define HCI_ERROR_ADVERTISING_TIMEOUT 0x3c /* Flow control modes */ #define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 129c22a85ccf..55a174317925 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -367,9 +367,23 @@ static void le_conn_timeout(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, le_conn_timeout.work); + struct hci_dev *hdev = conn->hdev; BT_DBG(""); + /* We could end up here due to having done directed advertising, + * so clean up the state if necessary. This should however only + * happen with broken hardware or if low duty cycle was used + * (which doesn't have a timeout of its own). + */ + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { + u8 enable = 0x00; + hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), + &enable); + hci_le_conn_failed(conn, HCI_ERROR_ADVERTISING_TIMEOUT); + return; + } + hci_le_create_connection_cancel(conn); } @@ -549,6 +563,11 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status) * favor of connection establishment, we should restart it. */ hci_update_background_scan(hdev); + + /* Re-enable advertising in case this was a failed connection + * attempt as a peripheral. + */ + mgmt_reenable_advertising(hdev); } static void create_le_conn_complete(struct hci_dev *hdev, u8 status) @@ -609,6 +628,45 @@ static void hci_req_add_le_create_conn(struct hci_request *req, conn->state = BT_CONNECT; } +static void hci_req_directed_advertising(struct hci_request *req, + struct hci_conn *conn) +{ + struct hci_dev *hdev = req->hdev; + struct hci_cp_le_set_adv_param cp; + u8 own_addr_type; + u8 enable; + + enable = 0x00; + hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); + + /* Clear the HCI_ADVERTISING bit temporarily so that the + * hci_update_random_address knows that it's safe to go ahead + * and write a new random address. The flag will be set back on + * as soon as the SET_ADV_ENABLE HCI command completes. + */ + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + + /* Set require_privacy to false so that the remote device has a + * chance of identifying us. + */ + if (hci_update_random_address(req, false, &own_addr_type) < 0) + return; + + memset(&cp, 0, sizeof(cp)); + cp.type = LE_ADV_DIRECT_IND; + cp.own_address_type = own_addr_type; + cp.direct_addr_type = conn->dst_type; + bacpy(&cp.direct_addr, &conn->dst); + cp.channel_map = hdev->le_adv_channel_map; + + hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp); + + enable = 0x01; + hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); + + conn->state = BT_CONNECT; +} + struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, u8 dst_type, u8 sec_level, u8 auth_type) { @@ -618,9 +676,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, struct hci_request req; int err; - if (test_bit(HCI_ADVERTISING, &hdev->flags)) - return ERR_PTR(-ENOTSUPP); - /* Some devices send ATT messages as soon as the physical link is * established. To be able to handle these ATT messages, the user- * space first establishes the connection and then starts the pairing @@ -668,13 +723,20 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, return ERR_PTR(-ENOMEM); conn->dst_type = dst_type; - - conn->out = true; - conn->link_mode |= HCI_LM_MASTER; conn->sec_level = BT_SECURITY_LOW; conn->pending_sec_level = sec_level; conn->auth_type = auth_type; + hci_req_init(&req, hdev); + + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { + hci_req_directed_advertising(&req, conn); + goto create_conn; + } + + conn->out = true; + conn->link_mode |= HCI_LM_MASTER; + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { conn->le_conn_min_interval = params->conn_min_interval; @@ -684,8 +746,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn->le_conn_max_interval = hdev->le_conn_max_interval; } - hci_req_init(&req, hdev); - /* If controller is scanning, we stop it since some controllers are * not able to scan and connect at the same time. Also set the * HCI_LE_SCAN_INTERRUPTED flag so that the command complete @@ -699,6 +759,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, hci_req_add_le_create_conn(&req, conn); +create_conn: err = hci_req_run(&req, create_le_conn_complete); if (err) { hci_conn_del(conn); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 4a2c919d5908..81e5236a0119 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -991,10 +991,25 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; + if (status) + return; + hci_dev_lock(hdev); - if (!status) - mgmt_advertising(hdev, *sent); + /* If we're doing connection initation as peripheral. Set a + * timeout in case something goes wrong. + */ + if (*sent) { + struct hci_conn *conn; + + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); + if (conn) + queue_delayed_work(hdev->workqueue, + &conn->le_conn_timeout, + HCI_LE_CONN_TIMEOUT); + } + + mgmt_advertising(hdev, *sent); hci_dev_unlock(hdev); } -- cgit v1.2.3 From b9a6328f2a7f15490de7e45eabb025f8b74a81af Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 25 Mar 2014 10:51:52 +0200 Subject: Bluetooth: Merge ADV_IND/ADV_SCAN_IND and SCAN_RSP together To avoid too many events being sent to user space and to help parsing of all available remote device data it makes sense for us to wait for the scan response and send a single merged Device Found event to user space. This patch adds a few new variables to hci_dev to track the last received ADV_IND/ADV_SCAN_IND, i.e. those which will cause a SCAN_REQ to be send in the case of active scanning. When the SCAN_RSP is received the pending data is passed together with the SCAN_RSP to the mgmt_device_found function which takes care of merging them into a single Device Found event. We also need a bit of extra logic to handle situations where we don't receive a SCAN_RSP after caching some data. In such a scenario we simply have to send out the pending data as it is and then operate on the new report as if there was no pending data. We also need to send out any pending data when scanning stops as well as ensure that the storage is empty at the start of a new active scanning session. These both cases are covered by the update to the hci_cc_le_set_scan_enable function in this patch. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++ net/bluetooth/hci_event.c | 103 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a1b8eab8a47d..59b112397d39 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -68,6 +68,10 @@ struct discovery_state { struct list_head unknown; /* Name state not known */ struct list_head resolve; /* Name needs to be resolved */ __u32 timestamp; + bdaddr_t last_adv_addr; + u8 last_adv_addr_type; + u8 last_adv_data[HCI_MAX_AD_LENGTH]; + u8 last_adv_data_len; }; struct hci_conn_hash { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 81e5236a0119..5816a13c5342 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1033,6 +1033,32 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); } +static bool has_pending_adv_report(struct hci_dev *hdev) +{ + struct discovery_state *d = &hdev->discovery; + + return bacmp(&d->last_adv_addr, BDADDR_ANY); +} + +static void clear_pending_adv_report(struct hci_dev *hdev) +{ + struct discovery_state *d = &hdev->discovery; + + bacpy(&d->last_adv_addr, BDADDR_ANY); + d->last_adv_data_len = 0; +} + +static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 bdaddr_type, u8 *data, u8 len) +{ + struct discovery_state *d = &hdev->discovery; + + bacpy(&d->last_adv_addr, bdaddr); + d->last_adv_addr_type = bdaddr_type; + memcpy(d->last_adv_data, data, len); + d->last_adv_data_len = len; +} + static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { @@ -1051,9 +1077,24 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, switch (cp->enable) { case LE_SCAN_ENABLE: set_bit(HCI_LE_SCAN, &hdev->dev_flags); + if (hdev->le_scan_type == LE_SCAN_ACTIVE) + clear_pending_adv_report(hdev); break; case LE_SCAN_DISABLE: + /* We do this here instead of when setting DISCOVERY_STOPPED + * since the latter would potentially require waiting for + * inquiry to stop too. + */ + if (has_pending_adv_report(hdev)) { + struct discovery_state *d = &hdev->discovery; + + mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, + d->last_adv_addr_type, NULL, 0, 0, + 1, d->last_adv_data, + d->last_adv_data_len, NULL, 0); + } + /* Cancel this timer so that we don't try to disable scanning * when it's already disabled. */ @@ -3979,6 +4020,8 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, u8 bdaddr_type, s8 rssi, u8 *data, u8 len) { + struct discovery_state *d = &hdev->discovery; + /* Passive scanning shouldn't trigger any device found events */ if (hdev->le_scan_type == LE_SCAN_PASSIVE) { if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND) @@ -3986,8 +4029,64 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, return; } - mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, rssi, 0, 1, - data, len, NULL, 0); + /* If there's nothing pending either store the data from this + * event or send an immediate device found event if the data + * should not be stored for later. + */ + if (!has_pending_adv_report(hdev)) { + /* If the report will trigger a SCAN_REQ store it for + * later merging. + */ + if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) { + store_pending_adv_report(hdev, bdaddr, bdaddr_type, + data, len); + return; + } + + mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, + rssi, 0, 1, data, len, NULL, 0); + return; + } + + /* If the pending data doesn't match this report or this isn't a + * scan response (e.g. we got a duplicate ADV_IND) then force + * sending of the pending data. + */ + if (type != LE_ADV_SCAN_RSP || bacmp(bdaddr, &d->last_adv_addr) || + bdaddr_type != d->last_adv_addr_type) { + /* Send out whatever is in the cache */ + mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, + d->last_adv_addr_type, NULL, 0, 0, 1, + d->last_adv_data, d->last_adv_data_len, + NULL, 0); + + /* If the new report will trigger a SCAN_REQ store it for + * later merging. + */ + if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) { + store_pending_adv_report(hdev, bdaddr, bdaddr_type, + data, len); + return; + } + + /* The advertising reports cannot be merged, so clear + * the pending report and send out a device found event. + */ + clear_pending_adv_report(hdev); + mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, + d->last_adv_addr_type, NULL, rssi, 0, 1, + data, len, NULL, 0); + return; + } + + /* If we get here we've got a pending ADV_IND or ADV_SCAN_IND and + * the new event is a SCAN_RSP. We can therefore proceed with + * sending a merged device found event. + */ + mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, + d->last_adv_addr_type, NULL, rssi, 0, 1, data, len, + d->last_adv_data, d->last_adv_data_len); + clear_pending_adv_report(hdev); } static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) -- cgit v1.2.3 From 73cf71d9865ad83c2ab7d09bc71be129088e4ded Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 25 Mar 2014 12:06:19 +0200 Subject: Bluetooth: Fix line splitting of mgmt_device_found parameters The line was incorrectly split between the variable type and its name. This patch fixes the issue. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/mgmt.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 59b112397d39..0ba7617ceb27 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1268,8 +1268,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, u8 *randomizer192, u8 *hash256, u8 *randomizer256, u8 status); void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 - ssp, u8 *eir, u16 eir_len, u8 *scan_rsp, + u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, + u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len); void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a0ef5c076880..37706e89af50 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5668,8 +5668,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, } void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 - ssp, u8 *eir, u16 eir_len, u8 *scan_rsp, + u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, + u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len) { char buf[512]; -- cgit v1.2.3 From ff5cd29f5cb8de0f0bc9016874ddde467d4b0c85 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 25 Mar 2014 14:40:52 +0200 Subject: Bluetooth: Store also RSSI for pending advertising reports Especially in crowded environments it can become frequent that we have to send out whatever pending event there is stored. Since user space has its own filtering of small RSSI changes sending a 0 value will essentially force user space to wake up the higher layers (e.g. over D-Bus) even though the RSSI didn't actually change more than the threshold value. This patch adds storing also of the RSSI for pending advertising reports so that we report an as accurate RSSI as possible when we have to send out the stored information to user space. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0ba7617ceb27..c2a419c2c5c7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -70,6 +70,7 @@ struct discovery_state { __u32 timestamp; bdaddr_t last_adv_addr; u8 last_adv_addr_type; + s8 last_adv_rssi; u8 last_adv_data[HCI_MAX_AD_LENGTH]; u8 last_adv_data_len; }; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 1b2221d62007..2ecb34f2e2ad 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1049,12 +1049,13 @@ static void clear_pending_adv_report(struct hci_dev *hdev) } static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 bdaddr_type, u8 *data, u8 len) + u8 bdaddr_type, s8 rssi, u8 *data, u8 len) { struct discovery_state *d = &hdev->discovery; bacpy(&d->last_adv_addr, bdaddr); d->last_adv_addr_type = bdaddr_type; + d->last_adv_rssi = rssi; memcpy(d->last_adv_data, data, len); d->last_adv_data_len = len; } @@ -4040,7 +4041,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) { store_pending_adv_report(hdev, bdaddr, bdaddr_type, - data, len); + rssi, data, len); return; } @@ -4061,8 +4062,9 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, /* Send out whatever is in the cache, but skip duplicates */ if (!match) mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, - d->last_adv_addr_type, NULL, 0, - 0, 1, d->last_adv_data, + d->last_adv_addr_type, NULL, + d->last_adv_rssi, 0, 1, + d->last_adv_data, d->last_adv_data_len, NULL, 0); /* If the new report will trigger a SCAN_REQ store it for @@ -4070,7 +4072,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) { store_pending_adv_report(hdev, bdaddr, bdaddr_type, - data, len); + rssi, data, len); return; } -- cgit v1.2.3 From ae55f5982a8bc6adbafb337e0b781d30d5617782 Mon Sep 17 00:00:00 2001 From: Lukasz Rymanowski Date: Thu, 27 Mar 2014 20:55:19 +0100 Subject: Bluetooth: Keep msec in DISCOV_INTERLEAVED_TIMEOUT Keep msec instead of jiffies in this define. This is needed by following patch where we want this timeout to be exposed in debugfs. Note: Value of this timeout comes from recommendation in BT Core Spec.4.0, Vol 3, Part C, chapter 13.2.1. Signed-off-by: Lukasz Rymanowski Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/mgmt.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c2a419c2c5c7..08a1d44eeab0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1210,7 +1210,7 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event); #define DISCOV_LE_SCAN_WIN 0x12 #define DISCOV_LE_SCAN_INT 0x12 #define DISCOV_LE_TIMEOUT msecs_to_jiffies(10240) -#define DISCOV_INTERLEAVED_TIMEOUT msecs_to_jiffies(5120) +#define DISCOV_INTERLEAVED_TIMEOUT 5120 /* msec */ #define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04 #define DISCOV_BREDR_INQUIRY_LEN 0x08 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 37706e89af50..944c4fc87905 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3351,6 +3351,8 @@ static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) static void start_discovery_complete(struct hci_dev *hdev, u8 status) { + unsigned long timeout = 0; + BT_DBG("status %d", status); if (status) { @@ -3366,13 +3368,11 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status) switch (hdev->discovery.type) { case DISCOV_TYPE_LE: - queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, - DISCOV_LE_TIMEOUT); + timeout = DISCOV_LE_TIMEOUT; break; case DISCOV_TYPE_INTERLEAVED: - queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, - DISCOV_INTERLEAVED_TIMEOUT); + timeout = msecs_to_jiffies(DISCOV_INTERLEAVED_TIMEOUT); break; case DISCOV_TYPE_BREDR: @@ -3381,6 +3381,11 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status) default: BT_ERR("Invalid discovery type %d", hdev->discovery.type); } + + if (!timeout) + return; + + queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout); } static int start_discovery(struct sock *sk, struct hci_dev *hdev, -- cgit v1.2.3 From b9a7a61e5c3e2f6316c2aedf4ca171bdee7a4804 Mon Sep 17 00:00:00 2001 From: Lukasz Rymanowski Date: Thu, 27 Mar 2014 20:55:20 +0100 Subject: Bluetooth: Add new debugfs parameter With this patch it is possible to control discovery interleaved timeout value from debugfs. It is for fine tuning of this timeout. Signed-off-by: Lukasz Rymanowski Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 4 ++++ net/bluetooth/mgmt.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 08a1d44eeab0..e0c26bc144e5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -199,6 +199,7 @@ struct hci_dev { __u16 le_scan_window; __u16 le_conn_min_interval; __u16 le_conn_max_interval; + __u16 discov_interleaved_timeout; __u8 ssp_debug_mode; __u16 devid_source; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7cf511cb1171..d31f144860d1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1823,6 +1823,9 @@ static int __hci_init(struct hci_dev *hdev) &lowpan_debugfs_fops); debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev, &le_auto_conn_fops); + debugfs_create_u16("discov_interleaved_timeout", 0644, + hdev->debugfs, + &hdev->discov_interleaved_timeout); } return 0; @@ -3785,6 +3788,7 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_conn_max_interval = 0x0038; hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT; + hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT; mutex_init(&hdev->lock); mutex_init(&hdev->req_lock); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 944c4fc87905..b34e13aed51c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3372,7 +3372,7 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status) break; case DISCOV_TYPE_INTERLEAVED: - timeout = msecs_to_jiffies(DISCOV_INTERLEAVED_TIMEOUT); + timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout); break; case DISCOV_TYPE_BREDR: -- cgit v1.2.3 From 3d5a76f08bbac55305da87f4c810279189f64297 Mon Sep 17 00:00:00 2001 From: Lukasz Rymanowski Date: Thu, 27 Mar 2014 20:55:21 +0100 Subject: Bluetooth: Keep msec in DISCOV_LE_TIMEOUT To be consistent, lets use msec for this timeout as well. Note: This define value is a minimum scan time taken from BT Core spec 4.0, Vol 3, Part C, chapter 9.2.6 Signed-off-by: Lukasz Rymanowski Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/mgmt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e0c26bc144e5..d73f41855ada 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1210,7 +1210,7 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event); */ #define DISCOV_LE_SCAN_WIN 0x12 #define DISCOV_LE_SCAN_INT 0x12 -#define DISCOV_LE_TIMEOUT msecs_to_jiffies(10240) +#define DISCOV_LE_TIMEOUT 10240 /* msec */ #define DISCOV_INTERLEAVED_TIMEOUT 5120 /* msec */ #define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04 #define DISCOV_BREDR_INQUIRY_LEN 0x08 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b34e13aed51c..11cb00a2befb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3368,7 +3368,7 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status) switch (hdev->discovery.type) { case DISCOV_TYPE_LE: - timeout = DISCOV_LE_TIMEOUT; + timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT); break; case DISCOV_TYPE_INTERLEAVED: -- cgit v1.2.3 From d728c8ef8bea6e81f44933c0237531cda499577e Mon Sep 17 00:00:00 2001 From: Brad Volkin Date: Tue, 18 Feb 2014 10:15:56 -0800 Subject: drm/i915: Add a CMD_PARSER_VERSION getparam So userspace can query the kernel for command parser support. v2: Add i915_cmd_parser_get_version(), history log, and kerneldoc OTC-Tracker: AXIA-4631 Change-Id: I58af650db9f6753c2dcac9c54ab432fd31db302f Signed-off-by: Brad Volkin Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_cmd_parser.c | 19 +++++++++++++++++++ drivers/gpu/drm/i915/i915_dma.c | 3 +++ drivers/gpu/drm/i915/i915_drv.h | 1 + include/uapi/drm/i915_drm.h | 1 + 4 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index a8f00dbc0dde..bae7c2f33692 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -896,3 +896,22 @@ int i915_parse_cmds(struct intel_ring_buffer *ring, return ret; } + +/** + * i915_cmd_parser_get_version() - get the cmd parser version number + * + * The cmd parser maintains a simple increasing integer version number suitable + * for passing to userspace clients to determine what operations are permitted. + * + * Return: the current version number of the cmd parser + */ +int i915_cmd_parser_get_version(void) +{ + /* + * Command parser version history + * + * 1. Initial version. Checks batches and reports violations, but leaves + * hardware parsing enabled (so does not allow new use cases). + */ + return 1; +} diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 96177eec0a0e..0b38f88c35f0 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1017,6 +1017,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_EXEC_HANDLE_LUT: value = 1; break; + case I915_PARAM_CMD_PARSER_VERSION: + value = i915_cmd_parser_get_version(); + break; default: DRM_DEBUG("Unknown parameter %d\n", param->param); return -EINVAL; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8e1576cf6d63..0801e157a7ff 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2385,6 +2385,7 @@ void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone); const char *i915_cache_level_str(int type); /* i915_cmd_parser.c */ +int i915_cmd_parser_get_version(void); void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring); bool i915_needs_cmd_parser(struct intel_ring_buffer *ring); int i915_parse_cmds(struct intel_ring_buffer *ring, diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 126bfaa8bb6b..8a3e4ef00c3d 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -337,6 +337,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_EXEC_NO_RELOC 25 #define I915_PARAM_HAS_EXEC_HANDLE_LUT 26 #define I915_PARAM_HAS_WT 27 +#define I915_PARAM_CMD_PARSER_VERSION 28 typedef struct drm_i915_getparam { int param; -- cgit v1.2.3 From c2d15359c4b5476c181d8a51bdeba4cead15c120 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 1 Apr 2014 12:59:08 +0300 Subject: drm: Make drm_clflush_virt_range() void* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently drm_cflush_virt_rage() takes a char* so the caller probably has to do pointless casting to avoid compiler warnings. Make the argument void* instead to avoid such issues. v2: Use void* arithmetic (Chris) Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_cache.c | 4 ++-- include/drm/drmP.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index bb8f58012189..4bfad5f5a843 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -125,11 +125,11 @@ drm_clflush_sg(struct sg_table *st) EXPORT_SYMBOL(drm_clflush_sg); void -drm_clflush_virt_range(char *addr, unsigned long length) +drm_clflush_virt_range(void *addr, unsigned long length) { #if defined(CONFIG_X86) if (cpu_has_clflush) { - char *end = addr + length; + void *end = addr + length; mb(); for (; addr < end; addr += boot_cpu_data.x86_clflush_size) clflush(addr); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index daac00a93126..150780bdd66b 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1322,7 +1322,7 @@ extern int drm_remove_magic(struct drm_master *master, drm_magic_t magic); /* Cache management (drm_cache.c) */ void drm_clflush_pages(struct page *pages[], unsigned long num_pages); void drm_clflush_sg(struct sg_table *st); -void drm_clflush_virt_range(char *addr, unsigned long length); +void drm_clflush_virt_range(void *addr, unsigned long length); /* Locking IOCTL support (drm_lock.h) */ extern int drm_lock(struct drm_device *dev, void *data, -- cgit v1.2.3 From c50b960ccc5981627628302701e93e6aceccdb1c Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 28 Mar 2014 10:19:47 +0000 Subject: netfilter: nf_tables: implement proper set selection The current set selection simply choses the first set type that provides the requested features, which always results in the rbtree being chosen by virtue of being the first set in the list. What we actually want to do is choose the implementation that can provide the requested features and is optimal from either a performance or memory perspective depending on the characteristics of the elements and the preferences specified by the user. The elements are not known when creating a set. Even if we would provide them for anonymous (literal) sets, we'd still have standalone sets where the elements are not known in advance. We therefore need an abstract description of the data charcteristics. The kernel already knows the size of the key, this patch starts by introducing a nested set description which so far contains only the maximum amount of elements. Based on this the set implementations are changed to provide an estimate of the required amount of memory and the lookup complexity class. The set ops have a new callback ->estimate() that is invoked during set selection. It receives a structure containing the attributes known to the kernel and is supposed to populate a struct nft_set_estimate with the complexity class and, in case the size is known, the complete amount of memory required, or the amount of memory required per element otherwise. Based on the policy specified by the user (performance/memory, defaulting to performance) the kernel will then select the best suited implementation. Even if the set implementation would allow to add more than the specified maximum amount of elements, they are enforced since new implementations might not be able to add more than maximum based on which they were selected. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 46 ++++++++++++ include/uapi/linux/netfilter/nf_tables.h | 27 +++++++ net/netfilter/nf_tables_api.c | 121 +++++++++++++++++++++++++++---- net/netfilter/nft_hash.c | 45 +++++++++++- net/netfilter/nft_rbtree.c | 21 ++++++ 5 files changed, 242 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index e6bc14d8fa9a..29ff1dc41ef3 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -145,6 +145,44 @@ struct nft_set_iter { const struct nft_set_elem *elem); }; +/** + * struct nft_set_desc - description of set elements + * + * @klen: key length + * @dlen: data length + * @size: number of set elements + */ +struct nft_set_desc { + unsigned int klen; + unsigned int dlen; + unsigned int size; +}; + +/** + * enum nft_set_class - performance class + * + * @NFT_LOOKUP_O_1: constant, O(1) + * @NFT_LOOKUP_O_LOG_N: logarithmic, O(log N) + * @NFT_LOOKUP_O_N: linear, O(N) + */ +enum nft_set_class { + NFT_SET_CLASS_O_1, + NFT_SET_CLASS_O_LOG_N, + NFT_SET_CLASS_O_N, +}; + +/** + * struct nft_set_estimate - estimation of memory and performance + * characteristics + * + * @size: required memory + * @class: lookup performance class + */ +struct nft_set_estimate { + unsigned int size; + enum nft_set_class class; +}; + /** * struct nft_set_ops - nf_tables set operations * @@ -174,7 +212,11 @@ struct nft_set_ops { struct nft_set_iter *iter); unsigned int (*privsize)(const struct nlattr * const nla[]); + bool (*estimate)(const struct nft_set_desc *desc, + u32 features, + struct nft_set_estimate *est); int (*init)(const struct nft_set *set, + const struct nft_set_desc *desc, const struct nlattr * const nla[]); void (*destroy)(const struct nft_set *set); @@ -194,6 +236,8 @@ void nft_unregister_set(struct nft_set_ops *ops); * @name: name of the set * @ktype: key type (numeric type defined by userspace, not used in the kernel) * @dtype: data type (verdict or numeric type defined by userspace) + * @size: maximum set size + * @nelems: number of elements * @ops: set ops * @flags: set flags * @klen: key length @@ -206,6 +250,8 @@ struct nft_set { char name[IFNAMSIZ]; u32 ktype; u32 dtype; + u32 size; + u32 nelems; /* runtime data below here */ const struct nft_set_ops *ops ____cacheline_aligned; u16 flags; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index c88ccbfda5f1..160159274cab 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -211,6 +211,29 @@ enum nft_set_flags { NFT_SET_MAP = 0x8, }; +/** + * enum nft_set_policies - set selection policy + * + * @NFT_SET_POL_PERFORMANCE: prefer high performance over low memory use + * @NFT_SET_POL_MEMORY: prefer low memory use over high performance + */ +enum nft_set_policies { + NFT_SET_POL_PERFORMANCE, + NFT_SET_POL_MEMORY, +}; + +/** + * enum nft_set_desc_attributes - set element description + * + * @NFTA_SET_DESC_SIZE: number of elements in set (NLA_U32) + */ +enum nft_set_desc_attributes { + NFTA_SET_DESC_UNSPEC, + NFTA_SET_DESC_SIZE, + __NFTA_SET_DESC_MAX +}; +#define NFTA_SET_DESC_MAX (__NFTA_SET_DESC_MAX - 1) + /** * enum nft_set_attributes - nf_tables set netlink attributes * @@ -221,6 +244,8 @@ enum nft_set_flags { * @NFTA_SET_KEY_LEN: key data length (NLA_U32) * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32) * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32) + * @NFTA_SET_POLICY: selection policy (NLA_U32) + * @NFTA_SET_DESC: set description (NLA_NESTED) */ enum nft_set_attributes { NFTA_SET_UNSPEC, @@ -231,6 +256,8 @@ enum nft_set_attributes { NFTA_SET_KEY_LEN, NFTA_SET_DATA_TYPE, NFTA_SET_DATA_LEN, + NFTA_SET_POLICY, + NFTA_SET_DESC, __NFTA_SET_MAX }; #define NFTA_SET_MAX (__NFTA_SET_MAX - 1) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 33045a562297..bd3381e16084 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1912,9 +1912,18 @@ void nft_unregister_set(struct nft_set_ops *ops) } EXPORT_SYMBOL_GPL(nft_unregister_set); -static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const nla[]) +/* + * Select a set implementation based on the data characteristics and the + * given policy. The total memory use might not be known if no size is + * given, in that case the amount of memory per element is used. + */ +static const struct nft_set_ops * +nft_select_set_ops(const struct nlattr * const nla[], + const struct nft_set_desc *desc, + enum nft_set_policies policy) { - const struct nft_set_ops *ops; + const struct nft_set_ops *ops, *bops; + struct nft_set_estimate est, best; u32 features; #ifdef CONFIG_MODULES @@ -1932,15 +1941,45 @@ static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const features &= NFT_SET_INTERVAL | NFT_SET_MAP; } - // FIXME: implement selection properly + bops = NULL; + best.size = ~0; + best.class = ~0; + list_for_each_entry(ops, &nf_tables_set_ops, list) { if ((ops->features & features) != features) continue; + if (!ops->estimate(desc, features, &est)) + continue; + + switch (policy) { + case NFT_SET_POL_PERFORMANCE: + if (est.class < best.class) + break; + if (est.class == best.class && est.size < best.size) + break; + continue; + case NFT_SET_POL_MEMORY: + if (est.size < best.size) + break; + if (est.size == best.size && est.class < best.class) + break; + continue; + default: + break; + } + if (!try_module_get(ops->owner)) continue; - return ops; + if (bops != NULL) + module_put(bops->owner); + + bops = ops; + best = est; } + if (bops != NULL) + return bops; + return ERR_PTR(-EOPNOTSUPP); } @@ -1952,6 +1991,12 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { [NFTA_SET_KEY_LEN] = { .type = NLA_U32 }, [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 }, [NFTA_SET_DATA_LEN] = { .type = NLA_U32 }, + [NFTA_SET_POLICY] = { .type = NLA_U32 }, + [NFTA_SET_DESC] = { .type = NLA_NESTED }, +}; + +static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { + [NFTA_SET_DESC_SIZE] = { .type = NLA_U32 }, }; static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, @@ -2043,6 +2088,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, { struct nfgenmsg *nfmsg; struct nlmsghdr *nlh; + struct nlattr *desc; u32 portid = NETLINK_CB(ctx->skb).portid; u32 seq = ctx->nlh->nlmsg_seq; @@ -2076,6 +2122,14 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, goto nla_put_failure; } + desc = nla_nest_start(skb, NFTA_SET_DESC); + if (desc == NULL) + goto nla_put_failure; + if (set->size && + nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size))) + goto nla_put_failure; + nla_nest_end(skb, desc); + return nlmsg_end(skb, nlh); nla_put_failure: @@ -2304,6 +2358,23 @@ err: return err; } +static int nf_tables_set_desc_parse(const struct nft_ctx *ctx, + struct nft_set_desc *desc, + const struct nlattr *nla) +{ + struct nlattr *da[NFTA_SET_DESC_MAX + 1]; + int err; + + err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy); + if (err < 0) + return err; + + if (da[NFTA_SET_DESC_SIZE] != NULL) + desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE])); + + return 0; +} + static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -2318,7 +2389,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, char name[IFNAMSIZ]; unsigned int size; bool create; - u32 ktype, klen, dlen, dtype, flags; + u32 ktype, dtype, flags, policy; + struct nft_set_desc desc; int err; if (nla[NFTA_SET_TABLE] == NULL || @@ -2326,6 +2398,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, nla[NFTA_SET_KEY_LEN] == NULL) return -EINVAL; + memset(&desc, 0, sizeof(desc)); + ktype = NFT_DATA_VALUE; if (nla[NFTA_SET_KEY_TYPE] != NULL) { ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE])); @@ -2333,8 +2407,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, return -EINVAL; } - klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); - if (klen == 0 || klen > FIELD_SIZEOF(struct nft_data, data)) + desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); + if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data)) return -EINVAL; flags = 0; @@ -2346,7 +2420,6 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, } dtype = 0; - dlen = 0; if (nla[NFTA_SET_DATA_TYPE] != NULL) { if (!(flags & NFT_SET_MAP)) return -EINVAL; @@ -2359,15 +2432,25 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, if (dtype != NFT_DATA_VERDICT) { if (nla[NFTA_SET_DATA_LEN] == NULL) return -EINVAL; - dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); - if (dlen == 0 || - dlen > FIELD_SIZEOF(struct nft_data, data)) + desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); + if (desc.dlen == 0 || + desc.dlen > FIELD_SIZEOF(struct nft_data, data)) return -EINVAL; } else - dlen = sizeof(struct nft_data); + desc.dlen = sizeof(struct nft_data); } else if (flags & NFT_SET_MAP) return -EINVAL; + policy = NFT_SET_POL_PERFORMANCE; + if (nla[NFTA_SET_POLICY] != NULL) + policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY])); + + if (nla[NFTA_SET_DESC] != NULL) { + err = nf_tables_set_desc_parse(&ctx, &desc, nla[NFTA_SET_DESC]); + if (err < 0) + return err; + } + create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create); @@ -2398,7 +2481,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, if (!(nlh->nlmsg_flags & NLM_F_CREATE)) return -ENOENT; - ops = nft_select_set_ops(nla); + ops = nft_select_set_ops(nla, &desc, policy); if (IS_ERR(ops)) return PTR_ERR(ops); @@ -2419,12 +2502,13 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, INIT_LIST_HEAD(&set->bindings); set->ops = ops; set->ktype = ktype; - set->klen = klen; + set->klen = desc.klen; set->dtype = dtype; - set->dlen = dlen; + set->dlen = desc.dlen; set->flags = flags; + set->size = desc.size; - err = ops->init(set, nla); + err = ops->init(set, &desc, nla); if (err < 0) goto err2; @@ -2733,6 +2817,9 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, enum nft_registers dreg; int err; + if (set->size && set->nelems == set->size) + return -ENFILE; + err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, nft_set_elem_policy); if (err < 0) @@ -2798,6 +2885,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, err = set->ops->insert(set, &elem); if (err < 0) goto err3; + set->nelems++; return 0; @@ -2867,6 +2955,7 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, goto err2; set->ops->remove(set, &elem); + set->nelems--; nft_data_uninit(&elem.key, NFT_DATA_VALUE); if (set->flags & NFT_SET_MAP) diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 3b1ad876d6b0..01884b315c4a 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,7 @@ #include #include -#define NFT_HASH_MIN_SIZE 4 +#define NFT_HASH_MIN_SIZE 4UL struct nft_hash { struct nft_hash_table __rcu *tbl; @@ -82,6 +83,11 @@ static void nft_hash_tbl_free(const struct nft_hash_table *tbl) kfree(tbl); } +static unsigned int nft_hash_tbl_size(unsigned int nelem) +{ + return max(roundup_pow_of_two(nelem * 4 / 3), NFT_HASH_MIN_SIZE); +} + static struct nft_hash_table *nft_hash_tbl_alloc(unsigned int nbuckets) { struct nft_hash_table *tbl; @@ -335,17 +341,23 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) } static int nft_hash_init(const struct nft_set *set, + const struct nft_set_desc *desc, const struct nlattr * const tb[]) { struct nft_hash *priv = nft_set_priv(set); struct nft_hash_table *tbl; + unsigned int size; if (unlikely(!nft_hash_rnd_initted)) { get_random_bytes(&nft_hash_rnd, 4); nft_hash_rnd_initted = true; } - tbl = nft_hash_tbl_alloc(NFT_HASH_MIN_SIZE); + size = NFT_HASH_MIN_SIZE; + if (desc->size) + size = nft_hash_tbl_size(desc->size); + + tbl = nft_hash_tbl_alloc(size); if (tbl == NULL) return -ENOMEM; RCU_INIT_POINTER(priv->tbl, tbl); @@ -369,8 +381,37 @@ static void nft_hash_destroy(const struct nft_set *set) kfree(tbl); } +static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, + struct nft_set_estimate *est) +{ + unsigned int esize; + + esize = sizeof(struct nft_hash_elem); + if (features & NFT_SET_MAP) + esize += FIELD_SIZEOF(struct nft_hash_elem, data[0]); + + if (desc->size) { + est->size = sizeof(struct nft_hash) + + nft_hash_tbl_size(desc->size) * + sizeof(struct nft_hash_elem *) + + desc->size * esize; + } else { + /* Resizing happens when the load drops below 30% or goes + * above 75%. The average of 52.5% load (approximated by 50%) + * is used for the size estimation of the hash buckets, + * meaning we calculate two buckets per element. + */ + est->size = esize + 2 * sizeof(struct nft_hash_elem *); + } + + est->class = NFT_SET_CLASS_O_1; + + return true; +} + static struct nft_set_ops nft_hash_ops __read_mostly = { .privsize = nft_hash_privsize, + .estimate = nft_hash_estimate, .init = nft_hash_init, .destroy = nft_hash_destroy, .get = nft_hash_get, diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c index e21d69d13506..072e611e9f71 100644 --- a/net/netfilter/nft_rbtree.c +++ b/net/netfilter/nft_rbtree.c @@ -201,6 +201,7 @@ static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[]) } static int nft_rbtree_init(const struct nft_set *set, + const struct nft_set_desc *desc, const struct nlattr * const nla[]) { struct nft_rbtree *priv = nft_set_priv(set); @@ -222,8 +223,28 @@ static void nft_rbtree_destroy(const struct nft_set *set) } } +static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features, + struct nft_set_estimate *est) +{ + unsigned int nsize; + + nsize = sizeof(struct nft_rbtree_elem); + if (features & NFT_SET_MAP) + nsize += FIELD_SIZEOF(struct nft_rbtree_elem, data[0]); + + if (desc->size) + est->size = sizeof(struct nft_rbtree) + desc->size * nsize; + else + est->size = nsize; + + est->class = NFT_SET_CLASS_O_LOG_N; + + return true; +} + static struct nft_set_ops nft_rbtree_ops __read_mostly = { .privsize = nft_rbtree_privsize, + .estimate = nft_rbtree_estimate, .init = nft_rbtree_init, .destroy = nft_rbtree_destroy, .insert = nft_rbtree_insert, -- cgit v1.2.3 From 78f22b6a3a9254460d23060530b48ae02a9394e3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Mar 2014 17:57:27 +0100 Subject: cfg80211: allow userspace to take ownership of interfaces When dynamically creating interfaces from userspace, e.g. for P2P usage, such interfaces are usually owned by the process that created them, i.e. wpa_supplicant. Should wpa_supplicant crash, such interfaces will often cease operating properly and cause problems on restarting the process. To avoid this problem, introduce an ownership concept for interfaces. If an interface is owned by a netlink socket, then it will be destroyed if the netlink socket is closed for any reason, including if the process it belongs to crashed. This gives us a race-free way to get rid of any such interfaces. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 +++ include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/core.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 11 +++++++++++ net/wireless/nl80211.c | 28 +++++++++++++++++++++++++++- 5 files changed, 91 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f3539a15c411..6510ccf53a54 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3194,6 +3194,7 @@ struct cfg80211_cached_keys; * @ibss_dfs_possible: (private) IBSS may change to a DFS channel * @event_list: (private) list for internal event processing * @event_lock: (private) lock for event list + * @owner_nlportid: (private) owner socket port ID */ struct wireless_dev { struct wiphy *wiphy; @@ -3241,6 +3242,8 @@ struct wireless_dev { unsigned long cac_start_time; unsigned int cac_time_ms; + u32 owner_nlportid; + #ifdef CONFIG_CFG80211_WEXT /* wext data */ struct { diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1ba9d626aa83..5e405fd55a71 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1579,6 +1579,10 @@ enum nl80211_commands { * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. * As specified in the &enum nl80211_tdls_peer_capability. * + * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface + * creation then the new interface will be owned by the netlink socket + * that created it and will be destroyed when the socket is closed + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1914,6 +1918,8 @@ enum nl80211_attrs { NL80211_ATTR_TDLS_PEER_CAPABILITY, + NL80211_ATTR_IFACE_SOCKET_OWNER, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/core.c b/net/wireless/core.c index 086cddd03ba6..5a63c3cbda2e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -260,6 +260,45 @@ static void cfg80211_event_work(struct work_struct *work) rtnl_unlock(); } +void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) +{ + struct cfg80211_iface_destroy *item; + + ASSERT_RTNL(); + + spin_lock_irq(&rdev->destroy_list_lock); + while ((item = list_first_entry_or_null(&rdev->destroy_list, + struct cfg80211_iface_destroy, + list))) { + struct wireless_dev *wdev, *tmp; + u32 nlportid = item->nlportid; + + list_del(&item->list); + kfree(item); + spin_unlock_irq(&rdev->destroy_list_lock); + + list_for_each_entry_safe(wdev, tmp, &rdev->wdev_list, list) { + if (nlportid == wdev->owner_nlportid) + rdev_del_virtual_intf(rdev, wdev); + } + + spin_lock_irq(&rdev->destroy_list_lock); + } + spin_unlock_irq(&rdev->destroy_list_lock); +} + +static void cfg80211_destroy_iface_wk(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, + destroy_work); + + rtnl_lock(); + cfg80211_destroy_ifaces(rdev); + rtnl_unlock(); +} + /* exported functions */ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) @@ -318,6 +357,10 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.dev.class = &ieee80211_class; rdev->wiphy.dev.platform_data = rdev; + INIT_LIST_HEAD(&rdev->destroy_list); + spin_lock_init(&rdev->destroy_list_lock); + INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); + #ifdef CONFIG_CFG80211_DEFAULT_PS rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; #endif @@ -675,6 +718,7 @@ void wiphy_unregister(struct wiphy *wiphy) cancel_work_sync(&rdev->conn_work); flush_work(&rdev->event_work); cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); + flush_work(&rdev->destroy_work); #ifdef CONFIG_PM if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) diff --git a/net/wireless/core.h b/net/wireless/core.h index 5b1fdcadd469..6f6f75609852 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -80,6 +80,10 @@ struct cfg80211_registered_device { struct cfg80211_coalesce *coalesce; + spinlock_t destroy_list_lock; + struct list_head destroy_list; + struct work_struct destroy_work; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __aligned(NETDEV_ALIGN); @@ -232,6 +236,13 @@ struct cfg80211_beacon_registration { u32 nlportid; }; +struct cfg80211_iface_destroy { + struct list_head list; + u32 nlportid; +}; + +void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev); + /* free object */ void cfg80211_dev_free(struct cfg80211_registered_device *rdev); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 052c1bf8ffac..b25b5ce4076d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -385,6 +385,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN }, [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, + [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -2514,6 +2515,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; u32 flags; + /* to avoid failing a new interface creation due to pending removal */ + cfg80211_destroy_ifaces(rdev); + memset(¶ms, 0, sizeof(params)); if (!info->attrs[NL80211_ATTR_IFNAME]) @@ -2563,6 +2567,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(wdev); } + if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER]) + wdev->owner_nlportid = info->snd_portid; + switch (type) { case NL80211_IFTYPE_MESH_POINT: if (!info->attrs[NL80211_ATTR_MESH_ID]) @@ -11649,9 +11656,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb, rcu_read_lock(); list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { - list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) + bool schedule_destroy_work = false; + + list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) { cfg80211_mlme_unregister_socket(wdev, notify->portid); + if (wdev->owner_nlportid == notify->portid) + schedule_destroy_work = true; + } + spin_lock_bh(&rdev->beacon_registrations_lock); list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations, list) { @@ -11662,6 +11675,19 @@ static int nl80211_netlink_notify(struct notifier_block * nb, } } spin_unlock_bh(&rdev->beacon_registrations_lock); + + if (schedule_destroy_work) { + struct cfg80211_iface_destroy *destroy; + + destroy = kzalloc(sizeof(*destroy), GFP_ATOMIC); + if (destroy) { + destroy->nlportid = notify->portid; + spin_lock(&rdev->destroy_list_lock); + list_add(&destroy->list, &rdev->destroy_list); + spin_unlock(&rdev->destroy_list_lock); + schedule_work(&rdev->destroy_work); + } + } } rcu_read_unlock(); -- cgit v1.2.3 From 77be2c54c5bd26279abc13807398771d80cda37a Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 27 Mar 2014 11:30:29 +0200 Subject: mac80211: add vif to flush call This will allow the low level driver to make decision based on the vif such as queues etc... Since the vif might be NULL, we can't add it to the tracing functions. Signed-off-by: Emmanuel Grumbach [fix staging rtl8821ae driver] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ar5523/ar5523.c | 3 ++- drivers/net/wireless/ath/ath10k/mac.c | 3 ++- drivers/net/wireless/ath/ath9k/main.c | 3 ++- drivers/net/wireless/ath/carl9170/main.c | 4 +++- drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 3 ++- drivers/net/wireless/cw1200/sta.c | 3 ++- drivers/net/wireless/cw1200/sta.h | 3 ++- drivers/net/wireless/iwlegacy/common.c | 3 ++- drivers/net/wireless/iwlegacy/common.h | 3 ++- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 3 ++- drivers/net/wireless/mac80211_hwsim.c | 4 +++- drivers/net/wireless/p54/main.c | 3 ++- drivers/net/wireless/rt2x00/rt2x00.h | 3 ++- drivers/net/wireless/rt2x00/rt2x00mac.c | 3 ++- drivers/net/wireless/rtlwifi/core.c | 3 ++- drivers/net/wireless/ti/wlcore/main.c | 3 ++- drivers/staging/rtl8821ae/core.c | 14 +++----------- include/net/mac80211.h | 4 +++- net/mac80211/driver-ops.h | 8 +++++++- net/mac80211/util.c | 2 +- 20 files changed, 48 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 507d9a9ee69a..f92050617ae6 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1090,7 +1090,8 @@ static int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return ret; } -static void ar5523_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void ar5523_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct ar5523 *ar = hw->priv; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 511a2f81e7af..d1df99350147 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3544,7 +3544,8 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) return ret; } -static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct ath10k *ar = hw->priv; bool skip; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d69853b848ce..49265c6a1a7e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1883,7 +1883,8 @@ static bool ath9k_has_tx_pending(struct ath_softc *sc) return !!npend; } -static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 4c8cdb097b65..f8ded84b7be8 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1707,7 +1707,9 @@ found: return 0; } -static void carl9170_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void carl9170_op_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 queues, bool drop) { struct ar9170 *ar = hw->priv; unsigned int vid; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 8c5fa4e58139..43c71bfaa474 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -897,7 +897,8 @@ static bool brcms_tx_flush_completed(struct brcms_info *wl) return result; } -static void brcms_ops_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void brcms_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct brcms_info *wl = hw->priv; int ret; diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 103f7bce8932..cd0cad7f7759 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -936,7 +936,8 @@ static int __cw1200_flush(struct cw1200_common *priv, bool drop) return ret; } -void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct cw1200_common *priv = hw->priv; diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h index 35babb62cc6a..b7e386b7662b 100644 --- a/drivers/net/wireless/cw1200/sta.h +++ b/drivers/net/wireless/cw1200/sta.h @@ -40,7 +40,8 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); -void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop); +void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list); diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index 4f42174d9994..ecc674627e6e 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -4755,7 +4755,8 @@ out: } EXPORT_SYMBOL(il_mac_change_interface); -void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct il_priv *il = hw->priv; unsigned long timeout = jiffies + msecs_to_jiffies(500); diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index dfb13c70efe8..ea5c0f863c4e 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1723,7 +1723,8 @@ void il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p); -void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop); +void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); int il_alloc_txq_mem(struct il_priv *il); void il_free_txq_mem(struct il_priv *il); diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index dd55c9cf7ba8..d4fae9fb2ddb 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1091,7 +1091,8 @@ static void iwlagn_configure_filter(struct ieee80211_hw *hw, FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; } -static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 9d7a52f5a410..31c7a1c9ef5f 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1676,7 +1676,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, return 0; } -static void mac80211_hwsim_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void mac80211_hwsim_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 queues, bool drop) { /* Not implemented, queues only on kernel side */ } diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index eede90b63f84..7be3a4839640 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -669,7 +669,8 @@ static unsigned int p54_flush_count(struct p54_common *priv) return total; } -static void p54_flush(struct ieee80211_hw *dev, u32 queues, bool drop) +static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct p54_common *priv = dev->priv; unsigned int total, i; diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index e3b885d8f7db..010b76505243 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1448,7 +1448,8 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); -void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop); +void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index ddeb5a709aa3..30a2367ba8d6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -747,7 +747,8 @@ void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw) } EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll); -void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 4ec424f26672..b1ed6d0796f6 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -1387,7 +1387,8 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw) * before switch channel or power save, or tx buffer packet * maybe send after offchannel or rf sleep, this may cause * dis-association by AP */ -static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void rtl_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ed88d3913483..077eb5b9cd74 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5184,7 +5184,8 @@ out: mutex_unlock(&wl->mutex); } -static void wlcore_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct wl1271 *wl = hw->priv; diff --git a/drivers/staging/rtl8821ae/core.c b/drivers/staging/rtl8821ae/core.c index ff3139b6da65..63ae2d1997d3 100644 --- a/drivers/staging/rtl8821ae/core.c +++ b/drivers/staging/rtl8821ae/core.c @@ -1414,23 +1414,15 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw) * before switch channel or power save, or tx buffer packet * maybe send after offchannel or rf sleep, this may cause * dis-association by AP */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)) -static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void rtl_op_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 queues, bool drop) { struct rtl_priv *rtlpriv = rtl_priv(hw); if (rtlpriv->intf_ops->flush) rtlpriv->intf_ops->flush(hw, queues, drop); } -#else -static void rtl_op_flush(struct ieee80211_hw *hw, bool drop) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - - if (rtlpriv->intf_ops->flush) - rtlpriv->intf_ops->flush(hw, drop); -} -#endif const struct ieee80211_ops rtl_ops = { .start = rtl_op_start, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8248e3909fdf..faa7b9cf9cc7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2609,6 +2609,7 @@ enum ieee80211_roc_type { * of queues to flush, which is useful if different virtual interfaces * use different hardware queues; it may also indicate all queues. * If the parameter @drop is set to %true, pending frames may be dropped. + * Note that vif can be NULL. * The callback can sleep. * * @channel_switch: Drivers that need (or want) to offload the channel @@ -2871,7 +2872,8 @@ struct ieee80211_ops { struct netlink_callback *cb, void *data, int len); #endif - void (*flush)(struct ieee80211_hw *hw, u32 queues, bool drop); + void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); void (*channel_switch)(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch); int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index fc689f5d971e..5331582a2c81 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -726,13 +726,19 @@ static inline void drv_rfkill_poll(struct ieee80211_local *local) } static inline void drv_flush(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, u32 queues, bool drop) { + struct ieee80211_vif *vif = sdata ? &sdata->vif : NULL; + might_sleep(); + if (sdata) + check_sdata_in_driver(sdata); + trace_drv_flush(local, queues, drop); if (local->ops->flush) - local->ops->flush(&local->hw, queues, drop); + local->ops->flush(&local->hw, vif, queues, drop); trace_drv_return_void(local); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 275c94f995f7..5cf62ec74c14 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -554,7 +554,7 @@ void ieee80211_flush_queues(struct ieee80211_local *local, ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_FLUSH); - drv_flush(local, queues, false); + drv_flush(local, sdata, queues, false); ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_FLUSH); -- cgit v1.2.3 From 570dbde137d4604e4e682a5855b4425233344c19 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Sun, 23 Feb 2014 09:12:59 +0200 Subject: cfg80211: Add indoor only and GO concurrent channel attributes The FCC are clarifying some soft configuration requirements, which among other include the following: 1. Indoor operation, where a device can use channels requiring indoor operation, subject to that it can guarantee indoor operation, i.e., the device is connected to AC Power or the device is under the control of a local master that is acting as an AP and is connected to AC Power. 2. Concurrent GO operation, where devices may instantiate a P2P GO while they are under the guidance of an authorized master. For example, on a channel on which a BSS is connected to an authorized master, i.e., with DFS and radar detection capability in the UNII band. See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122 Add support for advertising Indoor-only and GO-Concurrent channel properties. Signed-off-by: David Spinadel Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 +++++ include/uapi/linux/nl80211.h | 23 +++++++++++++++++++++++ net/wireless/nl80211.c | 6 ++++++ net/wireless/reg.c | 2 ++ 4 files changed, 36 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6510ccf53a54..14d8d3417735 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -109,6 +109,9 @@ enum ieee80211_band { * channel as the control or any of the secondary channels. * This may be due to the driver or due to regulatory bandwidth * restrictions. + * @IEEE80211_CHAN_INDOOR_ONLY: see %NL80211_FREQUENCY_ATTR_INDOOR_ONLY + * @IEEE80211_CHAN_GO_CONCURRENT: see %NL80211_FREQUENCY_ATTR_GO_CONCURRENT + * */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, @@ -120,6 +123,8 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_NO_OFDM = 1<<6, IEEE80211_CHAN_NO_80MHZ = 1<<7, IEEE80211_CHAN_NO_160MHZ = 1<<8, + IEEE80211_CHAN_INDOOR_ONLY = 1<<9, + IEEE80211_CHAN_GO_CONCURRENT = 1<<10, }; #define IEEE80211_CHAN_NO_HT40 \ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5e405fd55a71..ac5b2d25f0fc 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2342,9 +2342,30 @@ enum nl80211_band_attr { * using this channel as the primary or any of the secondary channels * isn't possible * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. + * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this + * channel. A channel that has the INDOOR_ONLY attribute can only be + * used when there is a clear assessment that the device is operating in + * an indoor surroundings, i.e., it is connected to AC power (and not + * through portable DC inverters) or is under the control of a master + * that is acting as an AP and is connected to AC power. + * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this + * channel if it's connected concurrently to a BSS on the same channel on + * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz + * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a + * channel that has the GO_CONCURRENT attribute set can be done when there + * is a clear assessment that the device is operating under the guidance of + * an authorized master, i.e., setting up a GO while the device is also + * connected to an AP with DFS and radar detection on the UNII band (it is + * up to user-space, i.e., wpa_supplicant to perform the required + * verifications) * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use + * + * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122 + * for more information on the FCC description of the relaxations allowed + * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and + * NL80211_FREQUENCY_ATTR_GO_CONCURRENT. */ enum nl80211_frequency_attr { __NL80211_FREQUENCY_ATTR_INVALID, @@ -2361,6 +2382,8 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_80MHZ, NL80211_FREQUENCY_ATTR_NO_160MHZ, NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, + NL80211_FREQUENCY_ATTR_INDOOR_ONLY, + NL80211_FREQUENCY_ATTR_GO_CONCURRENT, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b25b5ce4076d..c5ead18ad3ab 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -614,6 +614,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ)) goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_INDOOR_ONLY)) + goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_GO_CONCURRENT) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_GO_CONCURRENT)) + goto nla_put_failure; } if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e0a746d19061..1e9dc1cba335 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -873,6 +873,8 @@ static u32 map_regdom_flags(u32 rd_flags) channel_flags |= IEEE80211_CHAN_RADAR; if (rd_flags & NL80211_RRF_NO_OFDM) channel_flags |= IEEE80211_CHAN_NO_OFDM; + if (rd_flags & NL80211_RRF_NO_OUTDOOR) + channel_flags |= IEEE80211_CHAN_INDOOR_ONLY; return channel_flags; } -- cgit v1.2.3 From 174e0cd28af0fe3c6c634c3e4d9e042c683bd7f7 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 23 Feb 2014 09:13:01 +0200 Subject: cfg80211: Enable GO operation on additional channels Allow GO operation on a channel marked with IEEE80211_CHAN_GO_CONCURRENT iff there is an active station interface that is associated to an AP operating on the same channel in the 2 GHz band or the same UNII band (in the 5 GHz band). This relaxation is not allowed if the channel is marked with IEEE80211_CHAN_RADAR. Note that this is a permissive approach to the FCC definitions, that require a clear assessment that the device operating the AP is an authorized master, i.e., with radar detection and DFS capabilities. It is assumed that such restrictions are enforced by user space. Furthermore, it is assumed, that if the conditions that allowed for the operation of the GO on such a channel change, i.e., the station interface disconnected from the AP, it is the responsibility of user space to evacuate the GO from the channel. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++- include/net/regulatory.h | 6 ++++ net/mac80211/ibss.c | 9 ++++-- net/wireless/Kconfig | 24 +++++++++++++++ net/wireless/chan.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++-- net/wireless/mesh.c | 3 +- net/wireless/nl80211.c | 11 ++++--- net/wireless/reg.c | 29 ++++++++++++++++++ net/wireless/reg.h | 12 ++++++++ net/wireless/trace.h | 11 ++++--- 10 files changed, 169 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 14d8d3417735..5640dc028bfa 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4539,12 +4539,14 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, * cfg80211_reg_can_beacon - check if beaconing is allowed * @wiphy: the wiphy * @chandef: the channel definition + * @iftype: interface type * * Return: %true if there is no secondary channel or the secondary channel(s) * can be used for beaconing (i.e. is not a radar channel etc.) */ bool cfg80211_reg_can_beacon(struct wiphy *wiphy, - struct cfg80211_chan_def *chandef); + struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype); /* * cfg80211_ch_switch_notify - update wdev channel and notify userspace diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 75fc1f5a948d..259992444e80 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -131,6 +131,11 @@ struct regulatory_request { * all country IE information processed by the regulatory core. This will * override %REGULATORY_COUNTRY_IE_FOLLOW_POWER as all country IEs will * be ignored. + * @REGULATORY_ENABLE_RELAX_NO_IR: for devices that wish to allow the + * NO_IR relaxation, which enables transmissions on channels on which + * otherwise initiating radiation is not allowed. This will enable the + * relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration + * option */ enum ieee80211_regulatory_flags { REGULATORY_CUSTOM_REG = BIT(0), @@ -138,6 +143,7 @@ enum ieee80211_regulatory_flags { REGULATORY_DISABLE_BEACON_HINTS = BIT(2), REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(3), REGULATORY_COUNTRY_IE_IGNORE = BIT(4), + REGULATORY_ENABLE_RELAX_NO_IR = BIT(5), }; struct ieee80211_freq_range { diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 35e4f94d7ba1..741445e498b0 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -262,7 +262,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, /* make a copy of the chandef, it could be modified below. */ chandef = *req_chandef; chan = chandef.chan; - if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { + if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef, + NL80211_IFTYPE_ADHOC)) { if (chandef.width == NL80211_CHAN_WIDTH_5 || chandef.width == NL80211_CHAN_WIDTH_10 || chandef.width == NL80211_CHAN_WIDTH_20_NOHT || @@ -274,7 +275,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, chandef.width = NL80211_CHAN_WIDTH_20; chandef.center_freq1 = chan->center_freq; /* check again for downgraded chandef */ - if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { + if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef, + NL80211_IFTYPE_ADHOC)) { sdata_info(sdata, "Failed to join IBSS, beacons forbidden\n"); return; @@ -861,7 +863,8 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, goto disconnect; } - if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef)) { + if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef, + NL80211_IFTYPE_ADHOC)) { sdata_info(sdata, "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", ifibss->bssid, diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 2a891bc6315c..405f3c4cf70c 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -108,6 +108,30 @@ config CFG80211_REG_CELLULAR_HINTS feature if you have tested and validated this feature on your systems. +config CFG80211_REG_RELAX_NO_IR + bool "cfg80211 support for NO_IR relaxation" + depends on CFG80211_CERTIFICATION_ONUS + ---help--- + This option enables support for relaxation of the NO_IR flag for + situations that certain regulatory bodies have provided clarifications + on how relaxation can occur. This feature has an inherent dependency on + userspace features which must have been properly tested and as such is + not enabled by default. + + A relaxation feature example is allowing the operation of a P2P group + owner (GO) on channels marked with NO_IR if there is an additional BSS + interface which associated to an AP which userspace assumes or confirms + to be an authorized master, i.e., with radar detection support and DFS + capabilities. However, note that in order to not create daisy chain + scenarios, this relaxation is not allowed in cases that the BSS client + is associated to P2P GO and in addition the P2P GO instantiated on + a channel due to this relaxation should not allow connection from + non P2P clients. + + The regulatory core will apply these relaxations only for drivers that + support this feature by declaring the appropriate channel flags and + capabilities in their registration flow. + config CFG80211_DEFAULT_PS bool "enable powersave by default" depends on CFG80211 diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 9c9501a35fb5..50202af7fba3 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -661,15 +661,85 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_chandef_usable); +/* + * For GO only, check if the channel can be used under permissive conditions + * mandated by the some regulatory bodies, i.e., the channel is marked with + * IEEE80211_CHAN_GO_CONCURRENT and there is an additional station interface + * associated to an AP on the same channel or on the same UNII band + * (assuming that the AP is an authorized master). + */ +static bool cfg80211_go_permissive_chan(struct cfg80211_registered_device *rdev, + struct ieee80211_channel *chan) +{ + struct wireless_dev *wdev_iter; + struct wiphy *wiphy = wiphy_idx_to_wiphy(rdev->wiphy_idx); + + ASSERT_RTNL(); + + if (!config_enabled(CONFIG_CFG80211_REG_RELAX_NO_IR) || + !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR) || + !(chan->flags & IEEE80211_CHAN_GO_CONCURRENT)) + return false; + + /* + * Generally, it is possible to rely on another device/driver to allow + * the GO concurrent relaxation, however, since the device can further + * enforce the relaxation (by doing a similar verifications as this), + * and thus fail the GO instantiation, consider only the interfaces of + * the current registered device. + */ + list_for_each_entry(wdev_iter, &rdev->wdev_list, list) { + struct ieee80211_channel *other_chan = NULL; + int r1, r2; + + if (wdev_iter->iftype != NL80211_IFTYPE_STATION || + !netif_running(wdev_iter->netdev)) + continue; + + wdev_lock(wdev_iter); + if (wdev_iter->current_bss) + other_chan = wdev_iter->current_bss->pub.channel; + wdev_unlock(wdev_iter); + + if (!other_chan) + continue; + + if (chan == other_chan) + return true; + + if (chan->band != IEEE80211_BAND_5GHZ) + continue; + + r1 = cfg80211_get_unii(chan->center_freq); + r2 = cfg80211_get_unii(other_chan->center_freq); + + if (r1 != -EINVAL && r1 == r2) + return true; + } + + return false; +} + bool cfg80211_reg_can_beacon(struct wiphy *wiphy, - struct cfg80211_chan_def *chandef) + struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype) { + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); bool res; u32 prohibited_flags = IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR; - trace_cfg80211_reg_can_beacon(wiphy, chandef); + trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype); + + /* + * Under certain conditions suggested by the some regulatory bodies + * a GO can operate on channels marked with IEEE80211_NO_IR + * so set this flag only if such relaxations are not enabled and + * the conditions are not met. + */ + if (iftype != NL80211_IFTYPE_P2P_GO || + !cfg80211_go_permissive_chan(rdev, chandef->chan)) + prohibited_flags |= IEEE80211_CHAN_NO_IR; if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 && cfg80211_chandef_dfs_available(wiphy, chandef)) { diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 5af5cc6b2c4c..7031ee0afad8 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -175,7 +175,8 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, scan_width); } - if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef, + NL80211_IFTYPE_MESH_POINT)) return -EINVAL; err = cfg80211_chandef_dfs_required(wdev->wiphy, &setup->chandef); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c5ead18ad3ab..b8d81e41b0f7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1939,7 +1939,7 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, result = -EBUSY; break; } - if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) { + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) { result = -EINVAL; break; } @@ -3271,7 +3271,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) } else if (!nl80211_get_ap_channel(rdev, ¶ms)) return -EINVAL; - if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef, + wdev->iftype)) return -EINVAL; err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); @@ -5941,7 +5942,8 @@ skip_beacons: if (err) return err; - if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef, + wdev->iftype)) return -EINVAL; switch (dev->ieee80211_ptr->iftype) { @@ -6717,7 +6719,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if (err) return err; - if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef, + NL80211_IFTYPE_ADHOC)) return -EINVAL; switch (ibss.chandef.width) { diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2b8c1000c1be..58f48b8f42ae 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2616,6 +2616,35 @@ static void reg_timeout_work(struct work_struct *work) rtnl_unlock(); } +/* + * See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for + * UNII band definitions + */ +int cfg80211_get_unii(int freq) +{ + /* UNII-1 */ + if (freq >= 5150 && freq <= 5250) + return 0; + + /* UNII-2A */ + if (freq > 5250 && freq <= 5350) + return 1; + + /* UNII-2B */ + if (freq > 5350 && freq <= 5470) + return 2; + + /* UNII-2C */ + if (freq > 5470 && freq <= 5725) + return 3; + + /* UNII-3 */ + if (freq > 5725 && freq <= 5825) + return 4; + + return -EINVAL; +} + int __init regulatory_init(void) { int err = 0; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 37c180df34b7..334a53af0fc8 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -104,4 +104,16 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, */ void regulatory_hint_disconnect(void); +/** + * cfg80211_get_unii - get the U-NII band for the frequency + * @freq: the frequency for which we want to get the UNII band. + + * Get a value specifying the U-NII band frequency belongs to. + * U-NII bands are defined by the FCC in C.F.R 47 part 15. + * + * Returns -EINVAL if freq is invalid, 0 for UNII-1, 1 for UNII-2A, + * 2 for UNII-2B, 3 for UNII-2C and 4 for UNII-3. + */ +int cfg80211_get_unii(int freq); + #endif /* __NET_WIRELESS_REG_H */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index aabccf13e07b..47b499f8f54d 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2193,18 +2193,21 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify, ); TRACE_EVENT(cfg80211_reg_can_beacon, - TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), - TP_ARGS(wiphy, chandef), + TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype), + TP_ARGS(wiphy, chandef, iftype), TP_STRUCT__entry( WIPHY_ENTRY CHAN_DEF_ENTRY + __field(enum nl80211_iftype, iftype) ), TP_fast_assign( WIPHY_ASSIGN; CHAN_DEF_ASSIGN(chandef); + __entry->iftype = iftype; ), - TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, - WIPHY_PR_ARG, CHAN_DEF_PR_ARG) + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", iftype=%d", + WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->iftype) ); TRACE_EVENT(cfg80211_chandef_dfs_required, -- cgit v1.2.3 From 52616f2b446eaad8eb2cd78bbd052f0066069757 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 25 Feb 2014 16:26:00 +0200 Subject: cfg80211: Add an option to hint indoor operation Add the option to hint the wireless core that it is operating in an indoor environment. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 3 +++ net/wireless/nl80211.c | 18 ++++++-------- net/wireless/reg.c | 58 +++++++++++++++++++++++++++++++++++++++++++- net/wireless/reg.h | 1 + 4 files changed, 68 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index ac5b2d25f0fc..513bfd7b2e5f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2602,10 +2602,13 @@ enum nl80211_dfs_regions { * present has been registered with the wireless core that * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a * supported feature. + * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the + * platform is operating in an indoor environment. */ enum nl80211_user_reg_hint_type { NL80211_USER_REG_HINT_USER = 0, NL80211_USER_REG_HINT_CELL_BASE = 1, + NL80211_USER_REG_HINT_INDOOR = 2, }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b8d81e41b0f7..85bc830fd7e3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4677,7 +4677,6 @@ static int parse_reg_rule(struct nlattr *tb[], static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) { - int r; char *data = NULL; enum nl80211_user_reg_hint_type user_reg_hint_type; @@ -4690,11 +4689,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) return -EINPROGRESS; - if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) - return -EINVAL; - - data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); - if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]) user_reg_hint_type = nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); @@ -4704,14 +4698,16 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) switch (user_reg_hint_type) { case NL80211_USER_REG_HINT_USER: case NL80211_USER_REG_HINT_CELL_BASE: - break; + if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) + return -EINVAL; + + data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); + return regulatory_hint_user(data, user_reg_hint_type); + case NL80211_USER_REG_HINT_INDOOR: + return regulatory_hint_indoor_user(); default: return -EINVAL; } - - r = regulatory_hint_user(data, user_reg_hint_type); - - return r; } static int nl80211_get_mesh_config(struct sk_buff *skb, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 58f48b8f42ae..55d68c31ad72 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -65,11 +65,26 @@ #define REG_DBG_PRINT(args...) #endif +/** + * enum reg_request_treatment - regulatory request treatment + * + * @REG_REQ_OK: continue processing the regulatory request + * @REG_REQ_IGNORE: ignore the regulatory request + * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should + * be intersected with the current one. + * @REG_REQ_ALREADY_SET: the regulatory request will not change the current + * regulatory settings, and no further processing is required. + * @REG_REQ_USER_HINT_HANDLED: a non alpha2 user hint was handled and no + * further processing is required, i.e., not need to update last_request + * etc. This should be used for user hints that do not provide an alpha2 + * but some other type of regulatory hint, i.e., indoor operation. + */ enum reg_request_treatment { REG_REQ_OK, REG_REQ_IGNORE, REG_REQ_INTERSECT, REG_REQ_ALREADY_SET, + REG_REQ_USER_HINT_HANDLED, }; static struct regulatory_request core_request_world = { @@ -106,6 +121,14 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain; */ static int reg_num_devs_support_basehint; +/* + * State variable indicating if the platform on which the devices + * are attached is operating in an indoor environment. The state variable + * is relevant for all registered devices. + * (protected by RTNL) + */ +static bool reg_is_indoor; + static const struct ieee80211_regdomain *get_cfg80211_regdom(void) { return rtnl_dereference(cfg80211_regdomain); @@ -1128,6 +1151,13 @@ static bool reg_request_cell_base(struct regulatory_request *request) return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; } +static bool reg_request_indoor(struct regulatory_request *request) +{ + if (request->initiator != NL80211_REGDOM_SET_BY_USER) + return false; + return request->user_reg_hint_type == NL80211_USER_REG_HINT_INDOOR; +} + bool reg_last_request_cell_base(void) { return reg_request_cell_base(get_last_request()); @@ -1570,6 +1600,11 @@ __reg_process_hint_user(struct regulatory_request *user_request) { struct regulatory_request *lr = get_last_request(); + if (reg_request_indoor(user_request)) { + reg_is_indoor = true; + return REG_REQ_USER_HINT_HANDLED; + } + if (reg_request_cell_base(user_request)) return reg_ignore_cell_hint(user_request); @@ -1617,7 +1652,8 @@ reg_process_hint_user(struct regulatory_request *user_request) treatment = __reg_process_hint_user(user_request); if (treatment == REG_REQ_IGNORE || - treatment == REG_REQ_ALREADY_SET) { + treatment == REG_REQ_ALREADY_SET || + treatment == REG_REQ_USER_HINT_HANDLED) { kfree(user_request); return treatment; } @@ -1678,6 +1714,7 @@ reg_process_hint_driver(struct wiphy *wiphy, case REG_REQ_OK: break; case REG_REQ_IGNORE: + case REG_REQ_USER_HINT_HANDLED: kfree(driver_request); return treatment; case REG_REQ_INTERSECT: @@ -1777,6 +1814,7 @@ reg_process_hint_country_ie(struct wiphy *wiphy, case REG_REQ_OK: break; case REG_REQ_IGNORE: + case REG_REQ_USER_HINT_HANDLED: /* fall through */ case REG_REQ_ALREADY_SET: kfree(country_ie_request); @@ -1969,6 +2007,22 @@ int regulatory_hint_user(const char *alpha2, return 0; } +int regulatory_hint_indoor_user(void) +{ + struct regulatory_request *request; + + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->wiphy_idx = WIPHY_IDX_INVALID; + request->initiator = NL80211_REGDOM_SET_BY_USER; + request->user_reg_hint_type = NL80211_USER_REG_HINT_INDOOR; + queue_regulatory_request(request); + + return 0; +} + /* Driver hints */ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) { @@ -2136,6 +2190,8 @@ static void restore_regulatory_settings(bool reset_user) ASSERT_RTNL(); + reg_is_indoor = false; + reset_regdomains(true, &world_regdom); restore_alpha2(alpha2, reset_user); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 334a53af0fc8..2a3842828f6d 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -25,6 +25,7 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy); int regulatory_hint_user(const char *alpha2, enum nl80211_user_reg_hint_type user_reg_hint_type); +int regulatory_hint_indoor_user(void); void wiphy_regulatory_register(struct wiphy *wiphy); void wiphy_regulatory_deregister(struct wiphy *wiphy); -- cgit v1.2.3 From cb2d956dd329caa11b5ece454dc52253aa038e73 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 17 Feb 2014 16:52:35 +0200 Subject: cfg80211: refactor cfg80211_can_use_iftype_chan() Separate the code that counts the interface types and channels from the code that check the interface combinations. The new function that checks for combinations is exported so it can be called by the drivers. This is done in preparation for moving the interface combinations checks out of cfg80211. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- Documentation/DocBook/80211.tmpl | 1 + include/net/cfg80211.h | 22 +++++++ net/wireless/util.c | 129 ++++++++++++++++++++++----------------- 3 files changed, 96 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 044b76436e83..d9b9416c989f 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -100,6 +100,7 @@ !Finclude/net/cfg80211.h wdev_priv !Finclude/net/cfg80211.h ieee80211_iface_limit !Finclude/net/cfg80211.h ieee80211_iface_combination +!Finclude/net/cfg80211.h cfg80211_check_combinations Actions and configuration diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5640dc028bfa..4653e9f75d0d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4692,6 +4692,28 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp); */ unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy); +/** + * cfg80211_check_combinations - check interface combinations + * + * @wiphy: the wiphy + * @num_different_channels: the number of different channels we want + * to use for verification + * @radar_detect: a bitmap where each bit corresponds to a channel + * width where radar detection is needed, as in the definition of + * &struct ieee80211_iface_combination.@radar_detect_widths + * @iftype_num: array with the numbers of interfaces of each interface + * type. The index is the interface type as specified in &enum + * nl80211_iftype. + * + * This function can be called by the driver to check whether a + * combination of interfaces and their types are allowed according to + * the interface combinations. + */ +int cfg80211_check_combinations(struct wiphy *wiphy, + const int num_different_channels, + const u8 radar_detect, + const int iftype_num[NUM_NL80211_IFTYPES]); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/util.c b/net/wireless/util.c index e5872ff2c27c..46f404df35e3 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1268,6 +1268,76 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, return res; } +int cfg80211_check_combinations(struct wiphy *wiphy, + const int num_different_channels, + const u8 radar_detect, + const int iftype_num[NUM_NL80211_IFTYPES]) +{ + int i, j, iftype; + int num_interfaces = 0; + u32 used_iftypes = 0; + + for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { + num_interfaces += iftype_num[iftype]; + if (iftype_num[iftype] > 0 && + !(wiphy->software_iftypes & BIT(iftype))) + used_iftypes |= BIT(iftype); + } + + for (i = 0; i < wiphy->n_iface_combinations; i++) { + const struct ieee80211_iface_combination *c; + struct ieee80211_iface_limit *limits; + u32 all_iftypes = 0; + + c = &wiphy->iface_combinations[i]; + + if (num_interfaces > c->max_interfaces) + continue; + if (num_different_channels > c->num_different_channels) + continue; + + limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, + GFP_KERNEL); + if (!limits) + return -ENOMEM; + + for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { + if (wiphy->software_iftypes & BIT(iftype)) + continue; + for (j = 0; j < c->n_limits; j++) { + all_iftypes |= limits[j].types; + if (!(limits[j].types & BIT(iftype))) + continue; + if (limits[j].max < iftype_num[iftype]) + goto cont; + limits[j].max -= iftype_num[iftype]; + } + } + + if (radar_detect && !(c->radar_detect_widths & radar_detect)) + goto cont; + + /* Finally check that all iftypes that we're currently + * using are actually part of this combination. If they + * aren't then we can't use this combination and have + * to continue to the next. + */ + if ((all_iftypes & used_iftypes) != used_iftypes) + goto cont; + + /* This combination covered all interface types and + * supported the requested numbers, so we're good. + */ + kfree(limits); + return 0; + cont: + kfree(limits); + } + + return -EBUSY; +} +EXPORT_SYMBOL(cfg80211_check_combinations); + int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype, @@ -1276,7 +1346,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, u8 radar_detect) { struct wireless_dev *wdev_iter; - u32 used_iftypes = BIT(iftype); int num[NUM_NL80211_IFTYPES]; struct ieee80211_channel *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS]; @@ -1284,7 +1353,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chmode; int num_different_channels = 0; int total = 1; - int i, j; + int i; ASSERT_RTNL(); @@ -1369,65 +1438,13 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, num[wdev_iter->iftype]++; total++; - used_iftypes |= BIT(wdev_iter->iftype); } if (total == 1 && !radar_detect) return 0; - for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { - const struct ieee80211_iface_combination *c; - struct ieee80211_iface_limit *limits; - u32 all_iftypes = 0; - - c = &rdev->wiphy.iface_combinations[i]; - - if (total > c->max_interfaces) - continue; - if (num_different_channels > c->num_different_channels) - continue; - - limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, - GFP_KERNEL); - if (!limits) - return -ENOMEM; - - for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { - if (rdev->wiphy.software_iftypes & BIT(iftype)) - continue; - for (j = 0; j < c->n_limits; j++) { - all_iftypes |= limits[j].types; - if (!(limits[j].types & BIT(iftype))) - continue; - if (limits[j].max < num[iftype]) - goto cont; - limits[j].max -= num[iftype]; - } - } - - if (radar_detect && !(c->radar_detect_widths & radar_detect)) - goto cont; - - /* - * Finally check that all iftypes that we're currently - * using are actually part of this combination. If they - * aren't then we can't use this combination and have - * to continue to the next. - */ - if ((all_iftypes & used_iftypes) != used_iftypes) - goto cont; - - /* - * This combination covered all interface types and - * supported the requested numbers, so we're good. - */ - kfree(limits); - return 0; - cont: - kfree(limits); - } - - return -EBUSY; + return cfg80211_check_combinations(&rdev->wiphy, num_different_channels, + radar_detect, num); } int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, -- cgit v1.2.3 From 2beb6dab2d799ee8934cb0801845e551ad8c70f2 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 18 Feb 2014 11:40:36 +0200 Subject: cfg80211/mac80211: refactor cfg80211_chandef_dfs_required() Some interface types don't require DFS (such as STATION, P2P_CLIENT etc). In order to centralize these decisions, make cfg80211_chandef_dfs_required() take the iftype into consideration. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 +++-- net/mac80211/ibss.c | 32 +++++++++++----------- net/mac80211/mesh.c | 9 +++--- net/wireless/chan.c | 74 ++++++++++++++++++++++++++++++++++++++------------ net/wireless/mesh.c | 7 +++-- net/wireless/nl80211.c | 37 ++++++++++++------------- 6 files changed, 104 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4653e9f75d0d..92a65c331cf4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -446,10 +446,13 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, * cfg80211_chandef_dfs_required - checks if radar detection is required * @wiphy: the wiphy to validate against * @chandef: the channel definition to check - * Return: 1 if radar detection is required, 0 if it is not, < 0 on error + * @iftype: the interface type as specified in &enum nl80211_iftype + * Returns: + * 1 if radar detection is required, 0 if it is not, < 0 on error */ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, - const struct cfg80211_chan_def *chandef); + const struct cfg80211_chan_def *chandef, + enum nl80211_iftype); /** * ieee80211_chandef_rate_flags - returns rate flags for a channel diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 741445e498b0..1759bd661e25 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -228,7 +228,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct beacon_data *presp; enum nl80211_bss_scan_width scan_width; bool have_higher_than_11mbit; - bool radar_required = false; + bool radar_required; int err; sdata_assert_lock(sdata); @@ -284,21 +284,20 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, } err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, - &chandef); + &chandef, NL80211_IFTYPE_ADHOC); if (err < 0) { sdata_info(sdata, "Failed to join IBSS, invalid chandef\n"); return; } - if (err > 0) { - if (!ifibss->userspace_handles_dfs) { - sdata_info(sdata, - "Failed to join IBSS, DFS channel without control program\n"); - return; - } - radar_required = true; + if (err > 0 && !ifibss->userspace_handles_dfs) { + sdata_info(sdata, + "Failed to join IBSS, DFS channel without control program\n"); + return; } + radar_required = err; + mutex_lock(&local->mtx); if (ieee80211_vif_use_channel(sdata, &chandef, ifibss->fixed_channel ? @@ -777,7 +776,8 @@ static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) * unavailable. */ err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, - &ifibss->chandef); + &ifibss->chandef, + NL80211_IFTYPE_ADHOC); if (err > 0) cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef, GFP_ATOMIC); @@ -876,17 +876,17 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, } err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, - ¶ms.chandef); + ¶ms.chandef, + NL80211_IFTYPE_ADHOC); if (err < 0) goto disconnect; - if (err) { + if (err > 0 && !ifibss->userspace_handles_dfs) { /* IBSS-DFS only allowed with a control program */ - if (!ifibss->userspace_handles_dfs) - goto disconnect; - - params.radar_required = true; + goto disconnect; } + params.radar_required = err; + if (cfg80211_chandef_identical(¶ms.chandef, &sdata->vif.bss_conf.chandef)) { ibss_dbg(sdata, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 387f61c4557d..9d292377d3a6 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -903,14 +903,15 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, } err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, - ¶ms.chandef); + ¶ms.chandef, + NL80211_IFTYPE_MESH_POINT); if (err < 0) return false; - if (err) { - params.radar_required = true; + if (err > 0) /* TODO: DFS not (yet) supported */ return false; - } + + params.radar_required = err; if (cfg80211_chandef_identical(¶ms.chandef, &sdata->vif.bss_conf.chandef)) { diff --git a/net/wireless/chan.c b/net/wireless/chan.c index c3180dc03a33..c61bcdd3dfbc 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -326,28 +326,57 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, int cfg80211_chandef_dfs_required(struct wiphy *wiphy, - const struct cfg80211_chan_def *chandef) + const struct cfg80211_chan_def *chandef, + enum nl80211_iftype iftype) { int width; - int r; + int ret; if (WARN_ON(!cfg80211_chandef_valid(chandef))) return -EINVAL; - width = cfg80211_chandef_get_width(chandef); - if (width < 0) - return -EINVAL; + switch (iftype) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_MESH_POINT: + width = cfg80211_chandef_get_width(chandef); + if (width < 0) + return -EINVAL; - r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1, - width); - if (r) - return r; + ret = cfg80211_get_chans_dfs_required(wiphy, + chandef->center_freq1, + width); + if (ret < 0) + return ret; + else if (ret > 0) + return BIT(chandef->width); - if (!chandef->center_freq2) - return 0; + if (!chandef->center_freq2) + return 0; + + ret = cfg80211_get_chans_dfs_required(wiphy, + chandef->center_freq2, + width); + if (ret < 0) + return ret; + else if (ret > 0) + return BIT(chandef->width); - return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2, - width); + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_UNSPECIFIED: + break; + case NUM_NL80211_IFTYPES: + WARN_ON(1); + } + + return 0; } EXPORT_SYMBOL(cfg80211_chandef_dfs_required); @@ -749,7 +778,8 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy, !cfg80211_go_permissive_chan(rdev, chandef->chan)) prohibited_flags |= IEEE80211_CHAN_NO_IR; - if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 && + if (cfg80211_chandef_dfs_required(wiphy, chandef, + NL80211_IFTYPE_UNSPECIFIED) > 0 && cfg80211_chandef_dfs_available(wiphy, chandef)) { /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */ prohibited_flags = IEEE80211_CHAN_DISABLED; @@ -779,6 +809,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, enum cfg80211_chan_mode *chanmode, u8 *radar_detect) { + int ret; + *chan = NULL; *chanmode = CHAN_MODE_UNDEFINED; @@ -821,8 +853,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, *chan = wdev->chandef.chan; *chanmode = CHAN_MODE_SHARED; - if (cfg80211_chandef_dfs_required(wdev->wiphy, - &wdev->chandef)) + ret = cfg80211_chandef_dfs_required(wdev->wiphy, + &wdev->chandef, + wdev->iftype); + WARN_ON(ret < 0); + if (ret > 0) *radar_detect |= BIT(wdev->chandef.width); } return; @@ -831,8 +866,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, *chan = wdev->chandef.chan; *chanmode = CHAN_MODE_SHARED; - if (cfg80211_chandef_dfs_required(wdev->wiphy, - &wdev->chandef)) + ret = cfg80211_chandef_dfs_required(wdev->wiphy, + &wdev->chandef, + wdev->iftype); + WARN_ON(ret < 0); + if (ret > 0) *radar_detect |= BIT(wdev->chandef.width); } return; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 7031ee0afad8..6ebe883653a4 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -179,10 +179,13 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, NL80211_IFTYPE_MESH_POINT)) return -EINVAL; - err = cfg80211_chandef_dfs_required(wdev->wiphy, &setup->chandef); + err = cfg80211_chandef_dfs_required(wdev->wiphy, + &setup->chandef, + NL80211_IFTYPE_MESH_POINT); if (err < 0) return err; - if (err) + + if (err > 0) radar_detect_width = BIT(setup->chandef.width); err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 85bc830fd7e3..4f82b9b71db1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3275,12 +3275,15 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->iftype)) return -EINVAL; - err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); + err = cfg80211_chandef_dfs_required(wdev->wiphy, + ¶ms.chandef, + NL80211_IFTYPE_AP); if (err < 0) return err; - if (err) { - radar_detect_width = BIT(params.chandef.width); + + if (err > 0) { params.radar_required = true; + radar_detect_width = BIT(params.chandef.width); } err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, @@ -5806,7 +5809,8 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, if (wdev->cac_started) return -EBUSY; - err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef); + err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, + NL80211_IFTYPE_UNSPECIFIED); if (err < 0) return err; @@ -5942,22 +5946,15 @@ skip_beacons: wdev->iftype)) return -EINVAL; - switch (dev->ieee80211_ptr->iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - err = cfg80211_chandef_dfs_required(wdev->wiphy, - ¶ms.chandef); - if (err < 0) - return err; - if (err) { - radar_detect_width = BIT(params.chandef.width); - params.radar_required = true; - } - break; - default: - break; + err = cfg80211_chandef_dfs_required(wdev->wiphy, + ¶ms.chandef, + wdev->iftype); + if (err < 0) + return err; + + if (err > 0) { + radar_detect_width = BIT(params.chandef.width); + params.radar_required = true; } err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, -- cgit v1.2.3 From 73de86a38962b18edad3205c2358599dd9c83e9f Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 13 Feb 2014 11:31:59 +0200 Subject: cfg80211/mac80211: move interface counting for combination check to mac80211 Move the counting part of the interface combination check from cfg80211 to mac80211. This is needed to simplify locking when the driver has to perform a combination check by itself (eg. with channel-switch). Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 -- net/mac80211/cfg.c | 2 -- net/mac80211/chan.c | 17 +++++++++++ net/mac80211/ieee80211_i.h | 4 +++ net/mac80211/util.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 13 ++------- net/wireless/ibss.c | 4 +++ net/wireless/mesh.c | 28 ------------------ net/wireless/mlme.c | 14 +-------- net/wireless/nl80211.c | 29 +++---------------- net/wireless/util.c | 5 ++++ 11 files changed, 110 insertions(+), 80 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 92a65c331cf4..fb8afcee62b4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -662,7 +662,6 @@ struct cfg80211_acl_data { * @p2p_opp_ps: P2P opportunistic PS * @acl: ACL configuration used by the drivers which has support for * MAC address based access control - * @radar_required: set if radar detection is required */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -680,7 +679,6 @@ struct cfg80211_ap_settings { u8 p2p_ctwindow; bool p2p_opp_ps; const struct cfg80211_acl_data *acl; - bool radar_required; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 906bc3b05aae..8bf94eaa0456 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -972,7 +972,6 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->needed_rx_chains = sdata->local->rx_chains; mutex_lock(&local->mtx); - sdata->radar_required = params->radar_required; err = ieee80211_vif_use_channel(sdata, ¶ms->chandef, IEEE80211_CHANCTX_SHARED); mutex_unlock(&local->mtx); @@ -2930,7 +2929,6 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, /* whatever, but channel contexts should not complain about that one */ sdata->smps_mode = IEEE80211_SMPS_OFF; sdata->needed_rx_chains = local->rx_chains; - sdata->radar_required = true; err = ieee80211_vif_use_channel(sdata, chandef, IEEE80211_CHANCTX_SHARED); diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index b297bd3043a8..623b336f3efd 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -519,6 +519,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx *ctx; + u8 radar_detect_width = 0; int ret; lockdep_assert_held(&local->mtx); @@ -526,6 +527,22 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); mutex_lock(&local->chanctx_mtx); + + ret = cfg80211_chandef_dfs_required(local->hw.wiphy, + chandef, + sdata->wdev.iftype); + if (ret < 0) + goto out; + if (ret > 0) + radar_detect_width = BIT(chandef->width); + + sdata->radar_required = ret; + + ret = ieee80211_check_combinations(sdata, chandef, mode, + radar_detect_width); + if (ret < 0) + goto out; + __ieee80211_vif_release_channel(sdata); ctx = ieee80211_find_chanctx(local, chandef, mode); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 222c28b75315..09213fd87f8d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1805,6 +1805,10 @@ int ieee80211_cs_headroom(struct ieee80211_local *local, enum nl80211_iftype iftype); void ieee80211_recalc_dtim(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); +int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + enum ieee80211_chanctx_mode chanmode, + u8 radar_detect); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 73af7398850b..436f98870066 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2797,3 +2797,75 @@ void ieee80211_recalc_dtim(struct ieee80211_local *local, ps->dtim_count = dtim_count; } + +int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + enum ieee80211_chanctx_mode chanmode, + u8 radar_detect) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *sdata_iter; + enum nl80211_iftype iftype = sdata->wdev.iftype; + int num[NUM_NL80211_IFTYPES]; + struct ieee80211_chanctx *ctx; + int num_different_channels = 1; + int total = 1; + + lockdep_assert_held(&local->chanctx_mtx); + + if (WARN_ON(hweight32(radar_detect) > 1)) + return -EINVAL; + + if (WARN_ON(chanmode == IEEE80211_CHANCTX_SHARED && !chandef->chan)) + return -EINVAL; + + if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) + return -EINVAL; + + /* Always allow software iftypes */ + if (local->hw.wiphy->software_iftypes & BIT(iftype)) { + if (radar_detect) + return -EINVAL; + return 0; + } + + memset(num, 0, sizeof(num)); + + if (iftype != NL80211_IFTYPE_UNSPECIFIED) + num[iftype] = 1; + + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->conf.radar_enabled) + radar_detect |= BIT(ctx->conf.def.width); + if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { + num_different_channels++; + continue; + } + if ((chanmode == IEEE80211_CHANCTX_SHARED) && + cfg80211_chandef_compatible(chandef, + &ctx->conf.def)) + continue; + num_different_channels++; + } + + list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) { + struct wireless_dev *wdev_iter; + + wdev_iter = &sdata_iter->wdev; + + if (sdata_iter == sdata || + rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL || + local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype)) + continue; + + num[wdev_iter->iftype]++; + total++; + } + + if (total == 1 && !radar_detect) + return 0; + + return cfg80211_check_combinations(local->hw.wiphy, + num_different_channels, + radar_detect, num); +} diff --git a/net/wireless/core.h b/net/wireless/core.h index 6f6f75609852..6684c5d965d8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -417,6 +417,9 @@ cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype) { + /* TODO: For this function, we'll probably need to keep some + * kind of interface combination check in cfg80211... + */ return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, CHAN_MODE_UNDEFINED, 0); } @@ -431,16 +434,6 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, return cfg80211_can_change_interface(rdev, NULL, iftype); } -static inline int -cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - struct ieee80211_channel *chan, - enum cfg80211_chan_mode chanmode) -{ - return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, - chan, chanmode, 0); -} - static inline unsigned int elapsed_jiffies_msecs(unsigned long start) { unsigned long end = jiffies; diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index a6b5bdad039c..faf4961d47d8 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -135,6 +135,10 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, radar_detect_width = BIT(params->chandef.width); } + /* TODO: We need to check the combinations at this point, we + * probably must move this call down to join_ibss() in + * mac80211. + */ err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, check_chan, (params->channel_fixed && diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 6ebe883653a4..3ddfb7cd335e 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -99,7 +99,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, const struct mesh_config *conf) { struct wireless_dev *wdev = dev->ieee80211_ptr; - u8 radar_detect_width = 0; int err; BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); @@ -179,22 +178,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, NL80211_IFTYPE_MESH_POINT)) return -EINVAL; - err = cfg80211_chandef_dfs_required(wdev->wiphy, - &setup->chandef, - NL80211_IFTYPE_MESH_POINT); - if (err < 0) - return err; - - if (err > 0) - radar_detect_width = BIT(setup->chandef.width); - - err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, - setup->chandef.chan, - CHAN_MODE_SHARED, - radar_detect_width); - if (err) - return err; - err = rdev_join_mesh(rdev, dev, conf, setup); if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); @@ -240,17 +223,6 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, if (!netif_running(wdev->netdev)) return -ENETDOWN; - /* cfg80211_can_use_chan() calls - * cfg80211_can_use_iftype_chan() with no radar - * detection, so if we're trying to use a radar - * channel here, something is wrong. - */ - WARN_ON_ONCE(chandef->chan->flags & IEEE80211_CHAN_RADAR); - err = cfg80211_can_use_chan(rdev, wdev, chandef->chan, - CHAN_MODE_SHARED); - if (err) - return err; - err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev, chandef->chan); if (!err) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index c52ff59a3e96..4b4ba707fab9 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -233,14 +233,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, if (!req.bss) return -ENOENT; - err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel, - CHAN_MODE_SHARED); - if (err) - goto out; - err = rdev_auth(rdev, dev, &req); -out: cfg80211_put_bss(&rdev->wiphy, req.bss); return err; } @@ -306,16 +300,10 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, if (!req->bss) return -ENOENT; - err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED); - if (err) - goto out; - err = rdev_assoc(rdev, dev, req); if (!err) cfg80211_hold_bss(bss_from_pub(req->bss)); - -out: - if (err) + else cfg80211_put_bss(&rdev->wiphy, req->bss); return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4f82b9b71db1..2b99aad33ae0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3155,7 +3155,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_ap_settings params; int err; - u8 radar_detect_width = 0; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) @@ -3275,24 +3274,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->iftype)) return -EINVAL; - err = cfg80211_chandef_dfs_required(wdev->wiphy, - ¶ms.chandef, - NL80211_IFTYPE_AP); - if (err < 0) - return err; - - if (err > 0) { - params.radar_required = true; - radar_detect_width = BIT(params.chandef.width); - } - - err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, - params.chandef.chan, - CHAN_MODE_SHARED, - radar_detect_width); - if (err) - return err; - if (info->attrs[NL80211_ATTR_ACL_POLICY]) { params.acl = parse_acl_data(&rdev->wiphy, info); if (IS_ERR(params.acl)) @@ -5823,12 +5804,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, if (!rdev->ops->start_radar_detection) return -EOPNOTSUPP; - err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, - chandef.chan, CHAN_MODE_SHARED, - BIT(chandef.width)); - if (err) - return err; - cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef); if (WARN_ON(!cac_time_ms)) cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; @@ -5957,6 +5932,10 @@ skip_beacons: params.radar_required = true; } + /* TODO: I left this here for now. With channel switch, the + * verification is a bit more complicated, because we only do + * it later when the channel switch really happens. + */ err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, params.chandef.chan, CHAN_MODE_SHARED, diff --git a/net/wireless/util.c b/net/wireless/util.c index 46f404df35e3..9bfc4c621509 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1375,6 +1375,11 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, num[iftype] = 1; + /* TODO: We'll probably not need this anymore, since this + * should only be called with CHAN_MODE_UNDEFINED. There are + * still a couple of pending calls where other chanmodes are + * used, but we should get rid of them. + */ switch (chanmode) { case CHAN_MODE_UNDEFINED: break; -- cgit v1.2.3 From 5d52ee81101943c507f45c76368026935f6bb75a Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 27 Feb 2014 14:33:47 +0200 Subject: mac80211: allow reservation of a running chanctx With single-channel drivers, we need to be able to change a running chanctx if we want to use chanctx reservation. Not all drivers may be able to do this, so add a flag that indicates support for it. Changing a running chanctx can also be used as an optimization in multi-channel drivers when the context needs to be reserved for future usage. Introduce IEEE80211_CHANCTX_RESERVED chanctx mode to mark a channel as reserved so nobody else can use it (since we know it's going to change). In the future, we may allow several vifs to use the same reservation as long as they plan to use the chanctx on the same future channel. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 7 +++++ net/mac80211/chan.c | 79 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 68 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index faa7b9cf9cc7..03ab3c08fb70 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1555,6 +1555,12 @@ struct ieee80211_tx_control { * for a single active channel while using channel contexts. When support * is not enabled the default action is to disconnect when getting the * CSA frame. + * + * @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a + * channel context on-the-fly. This is needed for channel switch + * on single-channel hardware. It can also be used as an + * optimization in certain channel switch cases with + * multi-channel. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1586,6 +1592,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, + IEEE80211_HW_CHANGE_RUNNING_CHANCTX = 1<<29, }; /** diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index baad75610a6a..57b8ab1bc5c1 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -168,6 +168,27 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, } } +static bool ieee80211_chanctx_is_reserved(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + struct ieee80211_sub_if_data *sdata; + bool ret = false; + + lockdep_assert_held(&local->chanctx_mtx); + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata)) + continue; + if (sdata->reserved_chanctx == ctx) { + ret = true; + break; + } + } + + rcu_read_unlock(); + return ret; +} + static struct ieee80211_chanctx * ieee80211_find_chanctx(struct ieee80211_local *local, const struct cfg80211_chan_def *chandef, @@ -183,7 +204,12 @@ ieee80211_find_chanctx(struct ieee80211_local *local, list_for_each_entry(ctx, &local->chanctx_list, list) { const struct cfg80211_chan_def *compat; - if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) + /* We don't support chanctx reservation for multiple + * vifs yet, so don't allow reserved chanctxs to be + * reused. + */ + if ((ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) || + ieee80211_chanctx_is_reserved(local, ctx)) continue; compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); @@ -718,11 +744,20 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, /* try to find another context with the chandef we want */ new_ctx = ieee80211_find_chanctx(local, chandef, mode); if (!new_ctx) { - /* create a new context */ - new_ctx = ieee80211_new_chanctx(local, chandef, mode); - if (IS_ERR(new_ctx)) { - ret = PTR_ERR(new_ctx); - goto out; + if (curr_ctx->refcount == 1 && + (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) { + /* if we're the only users of the chanctx and + * the driver supports changing a running + * context, reserve our current context + */ + new_ctx = curr_ctx; + } else { + /* create a new context and reserve it */ + new_ctx = ieee80211_new_chanctx(local, chandef, mode); + if (IS_ERR(new_ctx)) { + ret = PTR_ERR(new_ctx); + goto out; + } } } @@ -770,22 +805,30 @@ int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.chandef = sdata->reserved_chandef; - /* unref our reservation before assigning */ + /* unref our reservation */ ctx->refcount--; sdata->reserved_chanctx = NULL; - ret = ieee80211_assign_vif_chanctx(sdata, ctx); - if (old_ctx->refcount == 0) - ieee80211_free_chanctx(local, old_ctx); - if (ret) { - /* if assign fails refcount stays the same */ - if (ctx->refcount == 0) - ieee80211_free_chanctx(local, ctx); - goto out; - } + if (old_ctx == ctx) { + /* This is our own context, just change it */ + ret = __ieee80211_vif_change_channel(sdata, old_ctx, + &tmp_changed); + if (ret) + goto out; + } else { + ret = ieee80211_assign_vif_chanctx(sdata, ctx); + if (old_ctx->refcount == 0) + ieee80211_free_chanctx(local, old_ctx); + if (ret) { + /* if assign fails refcount stays the same */ + if (ctx->refcount == 0) + ieee80211_free_chanctx(local, ctx); + goto out; + } - if (sdata->vif.type == NL80211_IFTYPE_AP) - __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); + if (sdata->vif.type == NL80211_IFTYPE_AP) + __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); + } *changed = tmp_changed; -- cgit v1.2.3 From ce26151bc35d9d893ec1b441a261ea145511c89f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 3 Apr 2014 10:03:45 +0300 Subject: cfg80211: update comment about WIPHY_FLAG_CUSTOM_REGULATORY Commit a2f73b6c5db3c ("cfg80211: move regulatory flags to their own variable") renamed WIPHY_FLAG_CUSTOM_REGULATORY to REGULATORY_CUSTOM_REG, but missed to update one comment. Signed-off-by: Kalle Valo Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fb8afcee62b4..9496fe5ea6b4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3609,7 +3609,7 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2); * default channel settings will be disregarded. If no rule is found for a * channel on the regulatory domain the channel will be disabled. * Drivers using this for a wiphy should also set the wiphy flag - * WIPHY_FLAG_CUSTOM_REGULATORY or cfg80211 will set it for the wiphy + * REGULATORY_CUSTOM_REG or cfg80211 will set it for the wiphy * that called this helper. */ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, -- cgit v1.2.3 From 041f607de17666bed0407370e3f4a56e697354a8 Mon Sep 17 00:00:00 2001 From: Rostislav Lisovy Date: Wed, 2 Apr 2014 15:31:55 +0200 Subject: mac80211: Update conf_is_ht() to work properly with 5/10MHz channels The channels with 5/10MHz bandwidth are not HT. We have to reflect this in conf_is_ht() function which returns whether the particular channel is HT or not. Signed-off-by: Rostislav Lisovy Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 03ab3c08fb70..a3044e124229 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4585,7 +4585,9 @@ conf_is_ht40(struct ieee80211_conf *conf) static inline bool conf_is_ht(struct ieee80211_conf *conf) { - return conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; + return (conf->chandef.width != NL80211_CHAN_WIDTH_5) && + (conf->chandef.width != NL80211_CHAN_WIDTH_10) && + (conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT); } static inline enum nl80211_iftype -- cgit v1.2.3 From 9fb6bf02e3ad04c20edb8e46536ce3eeda32c736 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 7 Apr 2014 13:39:33 -0400 Subject: HID: rmi: introduce RMI driver for Synaptics touchpads This driver add support for RMI4 over USB or I2C. The current state is that it uses its own RMI4 implementation, but once RMI4 is merged upstream, the driver will be a transport driver for the RMI4 library. Part of this driver should be considered as temporary. Most of the RMI4 processing and input handling will be deleted at some point. I based my work on Andrew's regarding its port of RMI4 over HID (see https://github.com/mightybigcar/synaptics-rmi4/tree/rmihid ) This repo presents how the driver may looks like at the end: https://github.com/mightybigcar/synaptics-rmi4/blob/rmihid/drivers/input/rmi4/rmi_hid.c Without this temporary solution, the workaround we gave to users is to disable i2c-hid, which leads to disabling the touchscreen on the XPS 11 and 12 (Haswell generation). Related bugs: https://bugzilla.redhat.com/show_bug.cgi?id=1048314 https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1218973 Signed-off-by: Andrew Duggan Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 8 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 2 + drivers/hid/hid-rmi.c | 889 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 2 + 5 files changed, 902 insertions(+) create mode 100644 drivers/hid/hid-rmi.c (limited to 'include') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7af9d0b5dea1..762f15d6ed88 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -657,6 +657,14 @@ config HID_SUNPLUS ---help--- Support for Sunplus wireless desktop. +config HID_RMI + tristate "Synaptics RMI4 device support" + depends on HID + ---help--- + Support for Synaptics RMI4 touchpads. + Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid + and want support for its special functionalities. + config HID_GREENASIA tristate "GreenAsia (Product ID 0x12) game controller support" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fc712dde02a4..a6fa6baf368e 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \ hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o +obj-$(CONFIG_HID_RMI) += hid-rmi.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 9e8064205bc7..f05255d92de7 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1882,6 +1882,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, + { HID_I2C_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c new file mode 100644 index 000000000000..699d631c6920 --- /dev/null +++ b/drivers/hid/hid-rmi.c @@ -0,0 +1,889 @@ +/* + * Copyright (c) 2013 Andrew Duggan + * Copyright (c) 2013 Synaptics Incorporated + * Copyright (c) 2014 Benjamin Tissoires + * Copyright (c) 2014 Red Hat, Inc + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" + +#define RMI_MOUSE_REPORT_ID 0x01 /* Mouse emulation Report */ +#define RMI_WRITE_REPORT_ID 0x09 /* Output Report */ +#define RMI_READ_ADDR_REPORT_ID 0x0a /* Output Report */ +#define RMI_READ_DATA_REPORT_ID 0x0b /* Input Report */ +#define RMI_ATTN_REPORT_ID 0x0c /* Input Report */ +#define RMI_SET_RMI_MODE_REPORT_ID 0x0f /* Feature Report */ + +/* flags */ +#define RMI_READ_REQUEST_PENDING BIT(0) +#define RMI_READ_DATA_PENDING BIT(1) +#define RMI_STARTED BIT(2) + +enum rmi_mode_type { + RMI_MODE_OFF = 0, + RMI_MODE_ATTN_REPORTS = 1, + RMI_MODE_NO_PACKED_ATTN_REPORTS = 2, +}; + +struct rmi_function { + unsigned page; /* page of the function */ + u16 query_base_addr; /* base address for queries */ + u16 command_base_addr; /* base address for commands */ + u16 control_base_addr; /* base address for controls */ + u16 data_base_addr; /* base address for datas */ + unsigned int interrupt_base; /* cross-function interrupt number + * (uniq in the device)*/ + unsigned int interrupt_count; /* number of interrupts */ + unsigned int report_size; /* size of a report */ + unsigned long irq_mask; /* mask of the interrupts + * (to be applied against ATTN IRQ) */ +}; + +/** + * struct rmi_data - stores information for hid communication + * + * @page_mutex: Locks current page to avoid changing pages in unexpected ways. + * @page: Keeps track of the current virtual page + * + * @wait: Used for waiting for read data + * + * @writeReport: output buffer when writing RMI registers + * @readReport: input buffer when reading RMI registers + * + * @input_report_size: size of an input report (advertised by HID) + * @output_report_size: size of an output report (advertised by HID) + * + * @flags: flags for the current device (started, reading, etc...) + * + * @f11: placeholder of internal RMI function F11 description + * @f30: placeholder of internal RMI function F30 description + * + * @max_fingers: maximum finger count reported by the device + * @max_x: maximum x value reported by the device + * @max_y: maximum y value reported by the device + * + * @gpio_led_count: count of GPIOs + LEDs reported by F30 + * @button_count: actual physical buttons count + * @button_mask: button mask used to decode GPIO ATTN reports + * @button_state_mask: pull state of the buttons + * + * @input: pointer to the kernel input device + * + * @reset_work: worker which will be called in case of a mouse report + * @hdev: pointer to the struct hid_device + */ +struct rmi_data { + struct mutex page_mutex; + int page; + + wait_queue_head_t wait; + + u8 *writeReport; + u8 *readReport; + + int input_report_size; + int output_report_size; + + unsigned long flags; + + struct rmi_function f11; + struct rmi_function f30; + + unsigned int max_fingers; + unsigned int max_x; + unsigned int max_y; + unsigned int x_size_mm; + unsigned int y_size_mm; + + unsigned int gpio_led_count; + unsigned int button_count; + unsigned long button_mask; + unsigned long button_state_mask; + + struct input_dev *input; + + struct work_struct reset_work; + struct hid_device *hdev; +}; + +#define RMI_PAGE(addr) (((addr) >> 8) & 0xff) + +static int rmi_write_report(struct hid_device *hdev, u8 *report, int len); + +/** + * rmi_set_page - Set RMI page + * @hdev: The pointer to the hid_device struct + * @page: The new page address. + * + * RMI devices have 16-bit addressing, but some of the physical + * implementations (like SMBus) only have 8-bit addressing. So RMI implements + * a page address at 0xff of every page so we can reliable page addresses + * every 256 registers. + * + * The page_mutex lock must be held when this function is entered. + * + * Returns zero on success, non-zero on failure. + */ +static int rmi_set_page(struct hid_device *hdev, u8 page) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + int retval; + + data->writeReport[0] = RMI_WRITE_REPORT_ID; + data->writeReport[1] = 1; + data->writeReport[2] = 0xFF; + data->writeReport[4] = page; + + retval = rmi_write_report(hdev, data->writeReport, + data->output_report_size); + if (retval != data->output_report_size) { + dev_err(&hdev->dev, + "%s: set page failed: %d.", __func__, retval); + return retval; + } + + data->page = page; + return 0; +} + +static int rmi_set_mode(struct hid_device *hdev, u8 mode) +{ + int ret; + u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode}; + + ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, txbuf, + sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret < 0) { + dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)\n", mode, + ret); + return ret; + } + + return 0; +} + +static int rmi_write_report(struct hid_device *hdev, u8 *report, int len) +{ + int ret; + + ret = hid_hw_output_report(hdev, (void *)report, len); + if (ret < 0) { + dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret); + return ret; + } + + return ret; +} + +static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf, + const int len) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + int ret; + int bytes_read; + int bytes_needed; + int retries; + int read_input_count; + + mutex_lock(&data->page_mutex); + + if (RMI_PAGE(addr) != data->page) { + ret = rmi_set_page(hdev, RMI_PAGE(addr)); + if (ret < 0) + goto exit; + } + + for (retries = 5; retries > 0; retries--) { + data->writeReport[0] = RMI_READ_ADDR_REPORT_ID; + data->writeReport[1] = 0; /* old 1 byte read count */ + data->writeReport[2] = addr & 0xFF; + data->writeReport[3] = (addr >> 8) & 0xFF; + data->writeReport[4] = len & 0xFF; + data->writeReport[5] = (len >> 8) & 0xFF; + + set_bit(RMI_READ_REQUEST_PENDING, &data->flags); + + ret = rmi_write_report(hdev, data->writeReport, + data->output_report_size); + if (ret != data->output_report_size) { + clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); + dev_err(&hdev->dev, + "failed to write request output report (%d)\n", + ret); + goto exit; + } + + bytes_read = 0; + bytes_needed = len; + while (bytes_read < len) { + if (!wait_event_timeout(data->wait, + test_bit(RMI_READ_DATA_PENDING, &data->flags), + msecs_to_jiffies(1000))) { + hid_warn(hdev, "%s: timeout elapsed\n", + __func__); + ret = -EAGAIN; + break; + } + + read_input_count = data->readReport[1]; + memcpy(buf + bytes_read, &data->readReport[2], + read_input_count < bytes_needed ? + read_input_count : bytes_needed); + + bytes_read += read_input_count; + bytes_needed -= read_input_count; + clear_bit(RMI_READ_DATA_PENDING, &data->flags); + } + + if (ret >= 0) { + ret = 0; + break; + } + } + +exit: + clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); + mutex_unlock(&data->page_mutex); + return ret; +} + +static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf) +{ + return rmi_read_block(hdev, addr, buf, 1); +} + +static void rmi_f11_process_touch(struct rmi_data *hdata, int slot, + u8 finger_state, u8 *touch_data) +{ + int x, y, wx, wy; + int wide, major, minor; + int z; + + input_mt_slot(hdata->input, slot); + input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER, + finger_state == 0x01); + if (finger_state == 0x01) { + x = (touch_data[0] << 4) | (touch_data[2] & 0x07); + y = (touch_data[1] << 4) | (touch_data[2] >> 4); + wx = touch_data[3] & 0x07; + wy = touch_data[3] >> 4; + wide = (wx > wy); + major = max(wx, wy); + minor = min(wx, wy); + z = touch_data[4]; + + /* y is inverted */ + y = hdata->max_y - y; + + input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x); + input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y); + input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide); + input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z); + input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + } +} + +static void rmi_reset_work(struct work_struct *work) +{ + struct rmi_data *hdata = container_of(work, struct rmi_data, + reset_work); + + /* switch the device to RMI if we receive a generic mouse report */ + rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS); +} + +static inline int rmi_schedule_reset(struct hid_device *hdev) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + return schedule_work(&hdata->reset_work); +} + +static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data, + int size) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + int offset; + int i; + + if (size < hdata->f11.report_size) + return 0; + + if (!(irq & hdata->f11.irq_mask)) + return 0; + + offset = (hdata->max_fingers >> 2) + 1; + for (i = 0; i < hdata->max_fingers; i++) { + int fs_byte_position = i >> 2; + int fs_bit_position = (i & 0x3) << 1; + int finger_state = (data[fs_byte_position] >> fs_bit_position) & + 0x03; + + rmi_f11_process_touch(hdata, i, finger_state, + &data[offset + 5 * i]); + } + input_mt_sync_frame(hdata->input); + input_sync(hdata->input); + return hdata->f11.report_size; +} + +static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data, + int size) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + int i; + int button = 0; + bool value; + + if (!(irq & hdata->f30.irq_mask)) + return 0; + + for (i = 0; i < hdata->gpio_led_count; i++) { + if (test_bit(i, &hdata->button_mask)) { + value = (data[i / 8] >> (i & 0x07)) & BIT(0); + if (test_bit(i, &hdata->button_state_mask)) + value = !value; + input_event(hdata->input, EV_KEY, BTN_LEFT + button++, + value); + } + } + return hdata->f30.report_size; +} + +static int rmi_input_event(struct hid_device *hdev, u8 *data, int size) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + unsigned long irq_mask = 0; + unsigned index = 2; + + if (!(test_bit(RMI_STARTED, &hdata->flags))) + return 0; + + irq_mask |= hdata->f11.irq_mask; + irq_mask |= hdata->f30.irq_mask; + + if (data[1] & ~irq_mask) + hid_warn(hdev, "unknown intr source:%02lx %s:%d\n", + data[1] & ~irq_mask, __FILE__, __LINE__); + + if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) { + index += rmi_f11_input_event(hdev, data[1], &data[index], + size - index); + index += rmi_f30_input_event(hdev, data[1], &data[index], + size - index); + } else { + index += rmi_f30_input_event(hdev, data[1], &data[index], + size - index); + index += rmi_f11_input_event(hdev, data[1], &data[index], + size - index); + } + + return 1; +} + +static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + + if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) { + hid_err(hdev, "no read request pending\n"); + return 0; + } + + memcpy(hdata->readReport, data, size < hdata->input_report_size ? + size : hdata->input_report_size); + set_bit(RMI_READ_DATA_PENDING, &hdata->flags); + wake_up(&hdata->wait); + + return 1; +} + +static int rmi_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + switch (data[0]) { + case RMI_READ_DATA_REPORT_ID: + return rmi_read_data_event(hdev, data, size); + case RMI_ATTN_REPORT_ID: + return rmi_input_event(hdev, data, size); + case RMI_MOUSE_REPORT_ID: + rmi_schedule_reset(hdev); + break; + } + + return 0; +} + +static int rmi_post_reset(struct hid_device *hdev) +{ + return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); +} + +static int rmi_post_resume(struct hid_device *hdev) +{ + return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); +} + +#define RMI4_MAX_PAGE 0xff +#define RMI4_PAGE_SIZE 0x0100 + +#define PDT_START_SCAN_LOCATION 0x00e9 +#define PDT_END_SCAN_LOCATION 0x0005 +#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff) + +struct pdt_entry { + u8 query_base_addr:8; + u8 command_base_addr:8; + u8 control_base_addr:8; + u8 data_base_addr:8; + u8 interrupt_source_count:3; + u8 bits3and4:2; + u8 function_version:2; + u8 bit7:1; + u8 function_number:8; +} __attribute__((__packed__)); + +static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count) +{ + return GENMASK(irq_count + irq_base - 1, irq_base); +} + +static void rmi_register_function(struct rmi_data *data, + struct pdt_entry *pdt_entry, int page, unsigned interrupt_count) +{ + struct rmi_function *f = NULL; + u16 page_base = page << 8; + + switch (pdt_entry->function_number) { + case 0x11: + f = &data->f11; + break; + case 0x30: + f = &data->f30; + break; + } + + if (f) { + f->page = page; + f->query_base_addr = page_base | pdt_entry->query_base_addr; + f->command_base_addr = page_base | pdt_entry->command_base_addr; + f->control_base_addr = page_base | pdt_entry->control_base_addr; + f->data_base_addr = page_base | pdt_entry->data_base_addr; + f->interrupt_base = interrupt_count; + f->interrupt_count = pdt_entry->interrupt_source_count; + f->irq_mask = rmi_gen_mask(f->interrupt_base, + f->interrupt_count); + } +} + +static int rmi_scan_pdt(struct hid_device *hdev) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + struct pdt_entry entry; + int page; + bool page_has_function; + int i; + int retval; + int interrupt = 0; + u16 page_start, pdt_start , pdt_end; + + hid_info(hdev, "Scanning PDT...\n"); + + for (page = 0; (page <= RMI4_MAX_PAGE); page++) { + page_start = RMI4_PAGE_SIZE * page; + pdt_start = page_start + PDT_START_SCAN_LOCATION; + pdt_end = page_start + PDT_END_SCAN_LOCATION; + + page_has_function = false; + for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) { + retval = rmi_read_block(hdev, i, &entry, sizeof(entry)); + if (retval) { + hid_err(hdev, + "Read of PDT entry at %#06x failed.\n", + i); + goto error_exit; + } + + if (RMI4_END_OF_PDT(entry.function_number)) + break; + + page_has_function = true; + + hid_info(hdev, "Found F%02X on page %#04x\n", + entry.function_number, page); + + rmi_register_function(data, &entry, page, interrupt); + interrupt += entry.interrupt_source_count; + } + + if (!page_has_function) + break; + } + + hid_info(hdev, "%s: Done with PDT scan.\n", __func__); + retval = 0; + +error_exit: + return retval; +} + +static int rmi_populate_f11(struct hid_device *hdev) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + u8 buf[20]; + int ret; + bool has_query12; + bool has_physical_props; + unsigned x_size, y_size; + + if (!data->f11.query_base_addr) { + hid_err(hdev, "No 2D sensor found, giving up.\n"); + return -ENODEV; + } + + /* query 0 contains some useful information */ + ret = rmi_read(hdev, data->f11.query_base_addr, buf); + if (ret) { + hid_err(hdev, "can not get query 0: %d.\n", ret); + return ret; + } + has_query12 = !!(buf[0] & BIT(5)); + + /* query 1 to get the max number of fingers */ + ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf); + if (ret) { + hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret); + return ret; + } + data->max_fingers = (buf[0] & 0x07) + 1; + if (data->max_fingers > 5) + data->max_fingers = 10; + + data->f11.report_size = data->max_fingers * 5 + + DIV_ROUND_UP(data->max_fingers, 4); + + if (!(buf[0] & BIT(4))) { + hid_err(hdev, "No absolute events, giving up.\n"); + return -ENODEV; + } + + /* + * query 12 to know if the physical properties are reported + * (query 12 is at offset 10 for HID devices) + */ + if (has_query12) { + ret = rmi_read(hdev, data->f11.query_base_addr + 10, buf); + if (ret) { + hid_err(hdev, "can not get query 12: %d.\n", ret); + return ret; + } + has_physical_props = !!(buf[0] & BIT(5)); + + if (has_physical_props) { + ret = rmi_read_block(hdev, + data->f11.query_base_addr + 11, buf, 4); + if (ret) { + hid_err(hdev, "can not read query 15-18: %d.\n", + ret); + return ret; + } + + x_size = buf[0] | (buf[1] << 8); + y_size = buf[2] | (buf[3] << 8); + + data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10); + data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10); + + hid_info(hdev, "%s: size in mm: %d x %d\n", + __func__, data->x_size_mm, data->y_size_mm); + } + } + + /* retrieve the ctrl registers */ + ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 20); + if (ret) { + hid_err(hdev, "can not read ctrl block of size 20: %d.\n", ret); + return ret; + } + + data->max_x = buf[6] | (buf[7] << 8); + data->max_y = buf[8] | (buf[9] << 8); + + return 0; +} + +static int rmi_populate_f30(struct hid_device *hdev) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + u8 buf[20]; + int ret; + bool has_gpio, has_led; + unsigned bytes_per_ctrl; + u8 ctrl2_addr; + int ctrl2_3_length; + int i; + + /* function F30 is for physical buttons */ + if (!data->f30.query_base_addr) { + hid_err(hdev, "No GPIO/LEDs found, giving up.\n"); + return -ENODEV; + } + + ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2); + if (ret) { + hid_err(hdev, "can not get F30 query registers: %d.\n", ret); + return ret; + } + + has_gpio = !!(buf[0] & BIT(3)); + has_led = !!(buf[0] & BIT(2)); + data->gpio_led_count = buf[1] & 0x1f; + + /* retrieve ctrl 2 & 3 registers */ + bytes_per_ctrl = (data->gpio_led_count + 7) / 8; + /* Ctrl0 is present only if both has_gpio and has_led are set*/ + ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0; + /* Ctrl1 is always be present */ + ctrl2_addr += bytes_per_ctrl; + ctrl2_3_length = 2 * bytes_per_ctrl; + + data->f30.report_size = bytes_per_ctrl; + + ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr, + buf, ctrl2_3_length); + if (ret) { + hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n", + ctrl2_3_length, ret); + return ret; + } + + for (i = 0; i < data->gpio_led_count; i++) { + int byte_position = i >> 3; + int bit_position = i & 0x07; + u8 dir_byte = buf[byte_position]; + u8 data_byte = buf[byte_position + bytes_per_ctrl]; + bool dir = (dir_byte >> bit_position) & BIT(0); + bool dat = (data_byte >> bit_position) & BIT(0); + + if (dir == 0) { + /* input mode */ + if (dat) { + /* actual buttons have pull up resistor */ + data->button_count++; + set_bit(i, &data->button_mask); + set_bit(i, &data->button_state_mask); + } + } + + } + + return 0; +} + +static int rmi_populate(struct hid_device *hdev) +{ + int ret; + + ret = rmi_scan_pdt(hdev); + if (ret) { + hid_err(hdev, "PDT scan failed with code %d.\n", ret); + return ret; + } + + ret = rmi_populate_f11(hdev); + if (ret) { + hid_err(hdev, "Error while initializing F11 (%d).\n", ret); + return ret; + } + + ret = rmi_populate_f30(hdev); + if (ret) + hid_warn(hdev, "Error while initializing F30 (%d).\n", ret); + + return 0; +} + +static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + struct input_dev *input = hi->input; + int ret; + int res_x, res_y, i; + + data->input = input; + + hid_dbg(hdev, "Opening low level driver\n"); + ret = hid_hw_open(hdev); + if (ret) + return; + + /* Allow incoming hid reports */ + hid_device_io_start(hdev); + + ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); + if (ret < 0) { + dev_err(&hdev->dev, "failed to set rmi mode\n"); + goto exit; + } + + ret = rmi_set_page(hdev, 0); + if (ret < 0) { + dev_err(&hdev->dev, "failed to set page select to 0.\n"); + goto exit; + } + + ret = rmi_populate(hdev); + if (ret) + goto exit; + + __set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0); + + if (data->x_size_mm && data->x_size_mm) { + res_x = (data->max_x - 1) / data->x_size_mm; + res_y = (data->max_y - 1) / data->x_size_mm; + + input_abs_set_res(input, ABS_MT_POSITION_X, res_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, res_y); + } + + input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); + + input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER); + + if (data->button_count) { + __set_bit(EV_KEY, input->evbit); + for (i = 0; i < data->button_count; i++) + __set_bit(BTN_LEFT + i, input->keybit); + + if (data->button_count == 1) + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + } + + set_bit(RMI_STARTED, &data->flags); + +exit: + hid_device_io_stop(hdev); + hid_hw_close(hdev); +} + +static int rmi_input_mapping(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + /* we want to make HID ignore the advertised HID collection */ + return -1; +} + +static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct rmi_data *data = NULL; + int ret; + size_t alloc_size; + + data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + INIT_WORK(&data->reset_work, rmi_reset_work); + data->hdev = hdev; + + hid_set_drvdata(hdev, data); + + hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT] + .report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3) + + 1 /* report id */; + data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT] + .report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3) + + 1 /* report id */; + + alloc_size = data->output_report_size + data->input_report_size; + + data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL); + if (!data->writeReport) { + ret = -ENOMEM; + return ret; + } + + data->readReport = data->writeReport + data->output_report_size; + + init_waitqueue_head(&data->wait); + + mutex_init(&data->page_mutex); + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + if (!test_bit(RMI_STARTED, &data->flags)) { + hid_hw_stop(hdev); + return -EIO; + } + + hid_hw_stop(hdev); + return 0; +} + +static void rmi_remove(struct hid_device *hdev) +{ + struct rmi_data *hdata = hid_get_drvdata(hdev); + + clear_bit(RMI_STARTED, &hdata->flags); + + hid_hw_stop(hdev); +} + +static const struct hid_device_id rmi_id[] = { + { HID_I2C_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(hid, rmi_id); + +static struct hid_driver rmi_driver = { + .name = "hid-rmi", + .id_table = rmi_id, + .probe = rmi_probe, + .remove = rmi_remove, + .raw_event = rmi_raw_event, + .input_mapping = rmi_input_mapping, + .input_configured = rmi_input_configured, +#ifdef CONFIG_PM + .resume = rmi_post_resume, + .reset_resume = rmi_post_reset, +#endif +}; + +module_hid_driver(rmi_driver); + +MODULE_AUTHOR("Andrew Duggan "); +MODULE_DESCRIPTION("RMI HID driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/hid.h b/include/linux/hid.h index 720e3a10608c..54f855b2c902 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -570,6 +570,8 @@ struct hid_descriptor { .bus = BUS_USB, .vendor = (ven), .product = (prod) #define HID_BLUETOOTH_DEVICE(ven, prod) \ .bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod) +#define HID_I2C_DEVICE(ven, prod) \ + .bus = BUS_I2C, .vendor = (ven), .product = (prod) #define HID_REPORT_ID(rep) \ .report_type = (rep) -- cgit v1.2.3 From 59c3d45e487315e6e05a3f2310b61109f8e503e7 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 8 Apr 2014 09:15:35 -0600 Subject: block: remove 'q' parameter from kblockd_schedule_*_work() The queue parameter is never used, just get rid of it. Signed-off-by: Jens Axboe --- block/blk-core.c | 6 +++--- block/blk-flush.c | 2 +- block/blk-mq.c | 7 ++----- block/cfq-iosched.c | 2 +- drivers/scsi/scsi_lib.c | 2 +- include/linux/blkdev.h | 4 ++-- 6 files changed, 10 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 34d7c196338b..f7d2c3335dfa 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2904,14 +2904,14 @@ free_and_out: } EXPORT_SYMBOL_GPL(blk_rq_prep_clone); -int kblockd_schedule_work(struct request_queue *q, struct work_struct *work) +int kblockd_schedule_work(struct work_struct *work) { return queue_work(kblockd_workqueue, work); } EXPORT_SYMBOL(kblockd_schedule_work); -int kblockd_schedule_delayed_work(struct request_queue *q, - struct delayed_work *dwork, unsigned long delay) +int kblockd_schedule_delayed_work(struct delayed_work *dwork, + unsigned long delay) { return queue_delayed_work(kblockd_workqueue, dwork, delay); } diff --git a/block/blk-flush.c b/block/blk-flush.c index 43e6b4755e9a..77f20458910c 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -144,7 +144,7 @@ static bool blk_flush_queue_rq(struct request *rq, bool add_front) { if (rq->q->mq_ops) { INIT_WORK(&rq->mq_flush_work, mq_flush_run); - kblockd_schedule_work(rq->q, &rq->mq_flush_work); + kblockd_schedule_work(&rq->mq_flush_work); return false; } else { if (add_front) diff --git a/block/blk-mq.c b/block/blk-mq.c index 1d2a9bdbee57..9c8f1f4ada7f 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -608,11 +608,8 @@ void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async) if (!async) __blk_mq_run_hw_queue(hctx); - else { - struct request_queue *q = hctx->queue; - - kblockd_schedule_delayed_work(q, &hctx->delayed_work, 0); - } + else + kblockd_schedule_delayed_work(&hctx->delayed_work, 0); } void blk_mq_run_queues(struct request_queue *q, bool async) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index e0985f1955e7..5063a0bd831a 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -908,7 +908,7 @@ static inline void cfq_schedule_dispatch(struct cfq_data *cfqd) { if (cfqd->busy_queues) { cfq_log(cfqd, "schedule dispatch"); - kblockd_schedule_work(cfqd->queue, &cfqd->unplug_work); + kblockd_schedule_work(&cfqd->unplug_work); } } diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 5681c05ac506..91f99f4ce2e8 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -139,7 +139,7 @@ static void __scsi_queue_insert(struct scsi_cmnd *cmd, int reason, int unbusy) */ spin_lock_irqsave(q->queue_lock, flags); blk_requeue_request(q, cmd->request); - kblockd_schedule_work(q, &device->requeue_work); + kblockd_schedule_work(&device->requeue_work); spin_unlock_irqrestore(q->queue_lock, flags); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 1e1fa3f93d5f..2425945d36ab 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1359,8 +1359,8 @@ static inline void put_dev_sector(Sector p) } struct work_struct; -int kblockd_schedule_work(struct request_queue *q, struct work_struct *work); -int kblockd_schedule_delayed_work(struct request_queue *q, struct delayed_work *dwork, unsigned long delay); +int kblockd_schedule_work(struct work_struct *work); +int kblockd_schedule_delayed_work(struct delayed_work *dwork, unsigned long delay); #ifdef CONFIG_BLK_CGROUP /* -- cgit v1.2.3 From 8ab14595b6dffecea264dcca2d6d9eea7c59273a Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 8 Apr 2014 09:17:40 -0600 Subject: block: add kblockd_schedule_delayed_work_on() Same function as kblockd_schedule_delayed_work(), but allow the caller to pass in a CPU that the work should be executed on. This just directly extends and maps into the workqueue API, and will be used to make the blk-mq mappings more strict. Signed-off-by: Jens Axboe --- block/blk-core.c | 7 +++++++ include/linux/blkdev.h | 1 + 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index f7d2c3335dfa..7af4a4898dcb 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2917,6 +2917,13 @@ int kblockd_schedule_delayed_work(struct delayed_work *dwork, } EXPORT_SYMBOL(kblockd_schedule_delayed_work); +int kblockd_schedule_delayed_work_on(int cpu, struct delayed_work *dwork, + unsigned long delay) +{ + return queue_delayed_work_on(cpu, kblockd_workqueue, dwork, delay); +} +EXPORT_SYMBOL(kblockd_schedule_delayed_work_on); + #define PLUG_MAGIC 0x91827364 /** diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 2425945d36ab..5a31307c5ded 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1361,6 +1361,7 @@ static inline void put_dev_sector(Sector p) struct work_struct; int kblockd_schedule_work(struct work_struct *work); int kblockd_schedule_delayed_work(struct delayed_work *dwork, unsigned long delay); +int kblockd_schedule_delayed_work_on(int cpu, struct delayed_work *dwork, unsigned long delay); #ifdef CONFIG_BLK_CGROUP /* -- cgit v1.2.3 From e4043dcf30811f5db15181168e2aac172514302a Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 9 Apr 2014 10:18:23 -0600 Subject: blk-mq: ensure that hardware queues are always run on the mapped CPUs Instead of providing soft mappings with no guarantees on hardware queues always being run on the right CPU, switch to a hard mapping guarantee that ensure that we always run the hardware queue on (one of, if more) the mapped CPU. Signed-off-by: Jens Axboe --- block/blk-mq.c | 66 ++++++++++++++++++++++++++++++++++++++------------ include/linux/blk-mq.h | 1 + 2 files changed, 52 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 9c8f1f4ada7f..5455ed19de1c 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -209,11 +209,14 @@ static struct request *blk_mq_alloc_request_pinned(struct request_queue *q, break; } - blk_mq_put_ctx(ctx); - if (!(gfp & __GFP_WAIT)) + if (gfp & __GFP_WAIT) { + __blk_mq_run_hw_queue(hctx); + blk_mq_put_ctx(ctx); + } else { + blk_mq_put_ctx(ctx); break; + } - __blk_mq_run_hw_queue(hctx); blk_mq_wait_for_tags(hctx->tags); } while (1); @@ -514,6 +517,8 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) LIST_HEAD(rq_list); int bit, queued; + WARN_ON(!preempt_count()); + if (unlikely(test_bit(BLK_MQ_S_STOPPED, &hctx->state))) return; @@ -606,10 +611,22 @@ void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async) if (unlikely(test_bit(BLK_MQ_S_STOPPED, &hctx->state))) return; - if (!async) + if (!async && cpumask_test_cpu(smp_processor_id(), hctx->cpumask)) __blk_mq_run_hw_queue(hctx); - else + else if (hctx->queue->nr_hw_queues == 1) kblockd_schedule_delayed_work(&hctx->delayed_work, 0); + else { + unsigned int cpu; + + /* + * It'd be great if the workqueue API had a way to pass + * in a mask and had some smarts for more clever placement + * than the first CPU. Or we could round-robin here. For now, + * just queue on the first CPU. + */ + cpu = cpumask_first(hctx->cpumask); + kblockd_schedule_delayed_work_on(cpu, &hctx->delayed_work, 0); + } } void blk_mq_run_queues(struct request_queue *q, bool async) @@ -623,7 +640,9 @@ void blk_mq_run_queues(struct request_queue *q, bool async) test_bit(BLK_MQ_S_STOPPED, &hctx->state)) continue; + preempt_disable(); blk_mq_run_hw_queue(hctx, async); + preempt_enable(); } } EXPORT_SYMBOL(blk_mq_run_queues); @@ -648,7 +667,10 @@ EXPORT_SYMBOL(blk_mq_stop_hw_queues); void blk_mq_start_hw_queue(struct blk_mq_hw_ctx *hctx) { clear_bit(BLK_MQ_S_STOPPED, &hctx->state); + + preempt_disable(); __blk_mq_run_hw_queue(hctx); + preempt_enable(); } EXPORT_SYMBOL(blk_mq_start_hw_queue); @@ -662,7 +684,9 @@ void blk_mq_start_stopped_hw_queues(struct request_queue *q) continue; clear_bit(BLK_MQ_S_STOPPED, &hctx->state); + preempt_disable(); blk_mq_run_hw_queue(hctx, true); + preempt_enable(); } } EXPORT_SYMBOL(blk_mq_start_stopped_hw_queues); @@ -672,7 +696,10 @@ static void blk_mq_work_fn(struct work_struct *work) struct blk_mq_hw_ctx *hctx; hctx = container_of(work, struct blk_mq_hw_ctx, delayed_work.work); + + preempt_disable(); __blk_mq_run_hw_queue(hctx); + preempt_enable(); } static void __blk_mq_insert_request(struct blk_mq_hw_ctx *hctx, @@ -716,10 +743,10 @@ void blk_mq_insert_request(struct request *rq, bool at_head, bool run_queue, spin_unlock(&ctx->lock); } - blk_mq_put_ctx(current_ctx); - if (run_queue) blk_mq_run_hw_queue(hctx, async); + + blk_mq_put_ctx(current_ctx); } static void blk_mq_insert_requests(struct request_queue *q, @@ -755,9 +782,8 @@ static void blk_mq_insert_requests(struct request_queue *q, } spin_unlock(&ctx->lock); - blk_mq_put_ctx(current_ctx); - blk_mq_run_hw_queue(hctx, from_schedule); + blk_mq_put_ctx(current_ctx); } static int plug_ctx_cmp(void *priv, struct list_head *a, struct list_head *b) @@ -876,7 +902,6 @@ static void blk_mq_make_request(struct request_queue *q, struct bio *bio) if (unlikely(is_flush_fua)) { blk_mq_bio_to_request(rq, bio); - blk_mq_put_ctx(ctx); blk_insert_flush(rq); goto run_queue; } @@ -914,7 +939,6 @@ static void blk_mq_make_request(struct request_queue *q, struct bio *bio) } spin_unlock(&ctx->lock); - blk_mq_put_ctx(ctx); /* * For a SYNC request, send it to the hardware immediately. For an @@ -923,6 +947,7 @@ static void blk_mq_make_request(struct request_queue *q, struct bio *bio) */ run_queue: blk_mq_run_hw_queue(hctx, !is_sync || is_flush_fua); + blk_mq_put_ctx(ctx); } /* @@ -990,9 +1015,9 @@ static void blk_mq_hctx_notify(void *data, unsigned long action, blk_mq_hctx_mark_pending(hctx, ctx); spin_unlock(&ctx->lock); - blk_mq_put_ctx(ctx); blk_mq_run_hw_queue(hctx, true); + blk_mq_put_ctx(ctx); } static int blk_mq_init_hw_commands(struct blk_mq_hw_ctx *hctx, @@ -1255,12 +1280,13 @@ static void blk_mq_init_cpu_queues(struct request_queue *q, __ctx->queue = q; /* If the cpu isn't online, the cpu is mapped to first hctx */ - hctx = q->mq_ops->map_queue(q, i); - hctx->nr_ctx++; - if (!cpu_online(i)) continue; + hctx = q->mq_ops->map_queue(q, i); + cpumask_set_cpu(i, hctx->cpumask); + hctx->nr_ctx++; + /* * Set local node, IFF we have more than one hw queue. If * not, we remain on the home node of the device @@ -1277,6 +1303,7 @@ static void blk_mq_map_swqueue(struct request_queue *q) struct blk_mq_ctx *ctx; queue_for_each_hw_ctx(q, hctx, i) { + cpumask_clear(hctx->cpumask); hctx->nr_ctx = 0; } @@ -1285,7 +1312,11 @@ static void blk_mq_map_swqueue(struct request_queue *q) */ queue_for_each_ctx(q, ctx, i) { /* If the cpu isn't online, the cpu is mapped to first hctx */ + if (!cpu_online(i)) + continue; + hctx = q->mq_ops->map_queue(q, i); + cpumask_set_cpu(i, hctx->cpumask); ctx->index_hw = hctx->nr_ctx; hctx->ctxs[hctx->nr_ctx++] = ctx; } @@ -1329,6 +1360,9 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_reg *reg, if (!hctxs[i]) goto err_hctxs; + if (!zalloc_cpumask_var(&hctxs[i]->cpumask, GFP_KERNEL)) + goto err_hctxs; + hctxs[i]->numa_node = NUMA_NO_NODE; hctxs[i]->queue_num = i; } @@ -1392,6 +1426,7 @@ err_hctxs: for (i = 0; i < reg->nr_hw_queues; i++) { if (!hctxs[i]) break; + free_cpumask_var(hctxs[i]->cpumask); reg->ops->free_hctx(hctxs[i], i); } kfree(hctxs); @@ -1413,6 +1448,7 @@ void blk_mq_free_queue(struct request_queue *q) blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); if (q->mq_ops->exit_hctx) q->mq_ops->exit_hctx(hctx, i); + free_cpumask_var(hctx->cpumask); q->mq_ops->free_hctx(hctx, i); } diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 0120451545d8..b6ee48740458 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -19,6 +19,7 @@ struct blk_mq_hw_ctx { unsigned long state; /* BLK_MQ_S_* flags */ struct delayed_work delayed_work; + cpumask_var_t cpumask; unsigned long flags; /* BLK_MQ_F_* flags */ -- cgit v1.2.3 From 848ef58695d8c013f24633352586279cfb40e9d9 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 1 Apr 2014 17:02:53 +0300 Subject: net: rfkill: gpio: remove unused and obsolete platform parameters After upgrading to descriptor based gpios, the gpio numbers are not used anymore. The power_clk_name and the platform specific setup and close hooks are not used by anybody, and we should not encourage use of such things, so removing them. Acked-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Heikki Krogerus Signed-off-by: Johannes Berg --- include/linux/rfkill-gpio.h | 10 ---------- net/rfkill/rfkill-gpio.c | 15 +-------------- 2 files changed, 1 insertion(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/rfkill-gpio.h b/include/linux/rfkill-gpio.h index 4d09f6eab359..20bcb55498cd 100644 --- a/include/linux/rfkill-gpio.h +++ b/include/linux/rfkill-gpio.h @@ -27,21 +27,11 @@ * struct rfkill_gpio_platform_data - platform data for rfkill gpio device. * for unused gpio's, the expected value is -1. * @name: name for the gpio rf kill instance - * @reset_gpio: GPIO which is used for reseting rfkill switch - * @shutdown_gpio: GPIO which is used for shutdown of rfkill switch - * @power_clk_name: [optional] name of clk to turn off while blocked - * @gpio_runtime_close: clean up platform specific gpio configuration - * @gpio_runtime_setup: set up platform specific gpio configuration */ struct rfkill_gpio_platform_data { char *name; - int reset_gpio; - int shutdown_gpio; - const char *power_clk_name; enum rfkill_type type; - void (*gpio_runtime_close)(struct platform_device *); - int (*gpio_runtime_setup)(struct platform_device *); }; #endif /* __RFKILL_GPIO_H */ diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index bd2a5b90400c..0adda445dfe7 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -87,7 +87,6 @@ static int rfkill_gpio_probe(struct platform_device *pdev) { struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; struct rfkill_gpio_data *rfkill; - const char *clk_name = NULL; struct gpio_desc *gpio; int ret; int len; @@ -101,7 +100,6 @@ static int rfkill_gpio_probe(struct platform_device *pdev) if (ret) return ret; } else if (pdata) { - clk_name = pdata->power_clk_name; rfkill->name = pdata->name; rfkill->type = pdata->type; } else { @@ -120,7 +118,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev) snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name); snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name); - rfkill->clk = devm_clk_get(&pdev->dev, clk_name); + rfkill->clk = devm_clk_get(&pdev->dev, NULL); gpio = devm_gpiod_get_index(&pdev->dev, rfkill->reset_name, 0); if (!IS_ERR(gpio)) { @@ -146,14 +144,6 @@ static int rfkill_gpio_probe(struct platform_device *pdev) return -EINVAL; } - if (pdata && pdata->gpio_runtime_setup) { - ret = pdata->gpio_runtime_setup(pdev); - if (ret) { - dev_err(&pdev->dev, "can't set up gpio\n"); - return ret; - } - } - rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev, rfkill->type, &rfkill_gpio_ops, rfkill); @@ -174,10 +164,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev) static int rfkill_gpio_remove(struct platform_device *pdev) { struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); - struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; - if (pdata && pdata->gpio_runtime_close) - pdata->gpio_runtime_close(pdev); rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); -- cgit v1.2.3 From 79ea9934b8df700fa306c8ced2d3bbf94ff276a8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 2 Apr 2014 16:31:46 +0200 Subject: ARM: shmobile: r8a7790: Rename VSP1_(SY|RT) clocks to VSP1_(S|R) The r8a7790 has four VSP1 instances, two of them being named VSPS (which stands for "VSP Standard") and VSPR (which stands for "VSP for Resizing"). The clock section in the SoC datasheet misunderstood the abbreviations as meaning VSP System and VSP Realtime, and named the corresponding clocks VSP1(SY) and VSP1(RT). This mistake has been carried over to the kernel code. Fix this by renaming the VSP1_SY and VSP1_RT clocks to VSP1_S and VSP1_R. Signed-off-by: Laurent Pinchart Acked-by: Magnus Damm Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7790.dtsi | 2 +- include/dt-bindings/clock/r8a7790-clock.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index 618e5b537eaf..10b326bdf831 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -673,7 +673,7 @@ renesas,clock-indices = < R8A7790_CLK_TMU1 R8A7790_CLK_TMU3 R8A7790_CLK_TMU2 R8A7790_CLK_CMT0 R8A7790_CLK_TMU0 R8A7790_CLK_VSP1_DU1 - R8A7790_CLK_VSP1_DU0 R8A7790_CLK_VSP1_RT R8A7790_CLK_VSP1_SY + R8A7790_CLK_VSP1_DU0 R8A7790_CLK_VSP1_R R8A7790_CLK_VSP1_S >; clock-output-names = "tmu1", "tmu3", "tmu2", "cmt0", "tmu0", "vsp1-du1", diff --git a/include/dt-bindings/clock/r8a7790-clock.h b/include/dt-bindings/clock/r8a7790-clock.h index 6548a5fbcf4a..9a7c4c5a35d1 100644 --- a/include/dt-bindings/clock/r8a7790-clock.h +++ b/include/dt-bindings/clock/r8a7790-clock.h @@ -33,8 +33,8 @@ #define R8A7790_CLK_TMU0 25 #define R8A7790_CLK_VSP1_DU1 27 #define R8A7790_CLK_VSP1_DU0 28 -#define R8A7790_CLK_VSP1_RT 30 -#define R8A7790_CLK_VSP1_SY 31 +#define R8A7790_CLK_VSP1_R 30 +#define R8A7790_CLK_VSP1_S 31 /* MSTP2 */ #define R8A7790_CLK_SCIFA2 2 -- cgit v1.2.3 From 58ea1d53ba93620ac50fef9d9720b2323971f243 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 2 Apr 2014 16:31:47 +0200 Subject: ARM: shmobile: r8a7791: Rename VSP1_SY clocks to VSP1_S The r8a7791 has three VSP1 instances, one of them being named VSPS (which stands for "VSP Standard"). The clock section in the SoC datasheet misunderstood the abbreviation as meaning VSP System, and named the corresponding clock VSP1(SY). This mistake has been carried over to the kernel code. Fix this by renaming the VSP1_SY clock to VSP1_S. Signed-off-by: Laurent Pinchart Acked-by: Magnus Damm Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7791.dtsi | 2 +- include/dt-bindings/clock/r8a7791-clock.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index 46181708e59c..aa1cba94196c 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -688,7 +688,7 @@ renesas,clock-indices = < R8A7791_CLK_TMU1 R8A7791_CLK_TMU3 R8A7791_CLK_TMU2 R8A7791_CLK_CMT0 R8A7791_CLK_TMU0 R8A7791_CLK_VSP1_DU1 - R8A7791_CLK_VSP1_DU0 R8A7791_CLK_VSP1_SY + R8A7791_CLK_VSP1_DU0 R8A7791_CLK_VSP1_S >; clock-output-names = "tmu1", "tmu3", "tmu2", "cmt0", "tmu0", "vsp1-du1", diff --git a/include/dt-bindings/clock/r8a7791-clock.h b/include/dt-bindings/clock/r8a7791-clock.h index 30f82f286e29..f069bc6627cb 100644 --- a/include/dt-bindings/clock/r8a7791-clock.h +++ b/include/dt-bindings/clock/r8a7791-clock.h @@ -32,7 +32,7 @@ #define R8A7791_CLK_TMU0 25 #define R8A7791_CLK_VSP1_DU1 27 #define R8A7791_CLK_VSP1_DU0 28 -#define R8A7791_CLK_VSP1_SY 31 +#define R8A7791_CLK_VSP1_S 31 /* MSTP2 */ #define R8A7791_CLK_SCIFA2 2 -- cgit v1.2.3 From c6e8f325e769aabd059f8ff7d29e406345f83929 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 10 Mar 2014 12:26:56 +0100 Subject: ARM: shmobile: r8a7791: add IIC0/1 clock macros Signed-off-by: Wolfram Sang Acked-by: Laurent Pinchart Acked-by: Magnus Damm Signed-off-by: Simon Horman --- include/dt-bindings/clock/r8a7791-clock.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/dt-bindings/clock/r8a7791-clock.h b/include/dt-bindings/clock/r8a7791-clock.h index 30f82f286e29..2df1a11faeb3 100644 --- a/include/dt-bindings/clock/r8a7791-clock.h +++ b/include/dt-bindings/clock/r8a7791-clock.h @@ -51,6 +51,8 @@ #define R8A7791_CLK_SDHI1 12 #define R8A7791_CLK_SDHI0 14 #define R8A7791_CLK_MMCIF0 15 +#define R8A7791_CLK_IIC0 18 +#define R8A7791_CLK_IIC1 23 #define R8A7791_CLK_SSUSB 28 #define R8A7791_CLK_CMT1 29 #define R8A7791_CLK_USBDMAC0 30 -- cgit v1.2.3 From 6225b99aa620d6e260228a30cc5d24cde60cb1e7 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Mon, 7 Apr 2014 15:04:21 +0900 Subject: ARM: shmobile: r8a7791: Add EHCI MSTP clock Add support for EHCI clock gating via the MSTP703 bit on r8a7791. Signed-off-by: Magnus Damm Acked-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7791.dtsi | 6 +++--- include/dt-bindings/clock/r8a7791-clock.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index 1004355496f2..3b28c8f41fdc 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -774,19 +774,19 @@ mstp7_clks: mstp7_clks@e615014c { compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks"; reg = <0 0xe615014c 0 4>, <0 0xe61501c4 0 4>; - clocks = <&mp_clk>, <&zs_clk>, <&p_clk>, <&p_clk>, <&zs_clk>, + clocks = <&mp_clk>, <&mp_clk>, <&zs_clk>, <&p_clk>, <&p_clk>, <&zs_clk>, <&zs_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&zx_clk>, <&zx_clk>, <&zx_clk>; #clock-cells = <1>; renesas,clock-indices = < - R8A7791_CLK_HSUSB R8A7791_CLK_HSCIF2 R8A7791_CLK_SCIF5 + R8A7791_CLK_EHCI R8A7791_CLK_HSUSB R8A7791_CLK_HSCIF2 R8A7791_CLK_SCIF5 R8A7791_CLK_SCIF4 R8A7791_CLK_HSCIF1 R8A7791_CLK_HSCIF0 R8A7791_CLK_SCIF3 R8A7791_CLK_SCIF2 R8A7791_CLK_SCIF1 R8A7791_CLK_SCIF0 R8A7791_CLK_DU1 R8A7791_CLK_DU0 R8A7791_CLK_LVDS0 >; clock-output-names = - "hsusb", "hscif2", "scif5", "scif4", "hscif1", "hscif0", + "ehci", "hsusb", "hscif2", "scif5", "scif4", "hscif1", "hscif0", "scif3", "scif2", "scif1", "scif0", "du1", "du0", "lvds0"; }; mstp8_clks: mstp8_clks@e6150990 { diff --git a/include/dt-bindings/clock/r8a7791-clock.h b/include/dt-bindings/clock/r8a7791-clock.h index 2df1a11faeb3..602354365819 100644 --- a/include/dt-bindings/clock/r8a7791-clock.h +++ b/include/dt-bindings/clock/r8a7791-clock.h @@ -63,6 +63,7 @@ #define R8A7791_CLK_PWM 23 /* MSTP7 */ +#define R8A7791_CLK_EHCI 3 #define R8A7791_CLK_HSUSB 4 #define R8A7791_CLK_HSCIF2 13 #define R8A7791_CLK_SCIF5 14 -- cgit v1.2.3 From 01d968e905968602c4958c416cfed7ad84b7489f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 11 Mar 2014 22:24:36 +0100 Subject: ARM: shmobile: r8a7790: add IIC0-2 clock macros Signed-off-by: Wolfram Sang Acked-by: Laurent Pinchart Signed-off-by: Simon Horman --- include/dt-bindings/clock/r8a7790-clock.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/dt-bindings/clock/r8a7790-clock.h b/include/dt-bindings/clock/r8a7790-clock.h index 6548a5fbcf4a..4d3e935b3f1b 100644 --- a/include/dt-bindings/clock/r8a7790-clock.h +++ b/include/dt-bindings/clock/r8a7790-clock.h @@ -50,6 +50,7 @@ #define R8A7790_CLK_SYS_DMAC0 19 /* MSTP3 */ +#define R8A7790_CLK_IIC2 0 #define R8A7790_CLK_TPU0 4 #define R8A7790_CLK_MMCIF1 5 #define R8A7790_CLK_SDHI3 11 @@ -57,6 +58,8 @@ #define R8A7790_CLK_SDHI1 13 #define R8A7790_CLK_SDHI0 14 #define R8A7790_CLK_MMCIF0 15 +#define R8A7790_CLK_IIC0 18 +#define R8A7790_CLK_IIC1 23 #define R8A7790_CLK_SSUSB 28 #define R8A7790_CLK_CMT1 29 #define R8A7790_CLK_USBDMAC0 30 -- cgit v1.2.3 From a39f75f7907fa3a708751dc283e3ab3e7da526b8 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 26 Mar 2014 13:40:23 +0800 Subject: ASoC: core: Move the default regmap I/O setting to snd_soc_register_codec() Add the default regmap I/O setting to snd_soc_register_codec() while the CODEC is initialising, which will be called by CODEC driver device probe(), and then we can make XXX_set_cache_io() go away entirely from each CODEC ASoC probe. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 28 ++++++++++++++++++---------- sound/soc/soc-io.c | 9 +++------ 3 files changed, 22 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168d8ff4..2f62436026d2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -754,6 +754,7 @@ struct snd_soc_codec_driver { unsigned int freq_in, unsigned int freq_out); /* codec IO */ + struct regmap *(*get_regmap)(struct device *); unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); int (*display_register)(struct snd_soc_codec *, char *, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 051c006281f5..5071a3a0ac83 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1137,16 +1137,6 @@ static int soc_probe_codec(struct snd_soc_card *card, codec->dapm.idle_bias_off = driver->idle_bias_off; - if (!codec->write && dev_get_regmap(codec->dev, NULL)) { - /* Set the default I/O up try regmap */ - ret = snd_soc_codec_set_cache_io(codec, NULL); - if (ret < 0) { - dev_err(codec->dev, - "Failed to set cache I/O: %d\n", ret); - goto err_probe; - } - } - if (driver->probe) { ret = driver->probe(codec); if (ret < 0) { @@ -4263,6 +4253,7 @@ int snd_soc_register_codec(struct device *dev, int num_dai) { struct snd_soc_codec *codec; + struct regmap *regmap; int ret, i; dev_dbg(dev, "codec register %s\n", dev_name(dev)); @@ -4294,6 +4285,23 @@ int snd_soc_register_codec(struct device *dev, codec->num_dai = num_dai; mutex_init(&codec->mutex); + if (!codec->write) { + if (codec_drv->get_regmap) + regmap = codec_drv->get_regmap(dev); + else + regmap = dev_get_regmap(dev, NULL); + + if (regmap) { + ret = snd_soc_codec_set_cache_io(codec, regmap); + if (ret && ret != -ENOTSUPP) { + dev_err(codec->dev, + "Failed to set cache I/O:%d\n", + ret); + return ret; + } + } + } + for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 260efc8466fc..6480e8f29310 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -60,14 +60,11 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, { int ret; - /* Device has made its own regmap arrangements */ if (!regmap) - codec->control_data = dev_get_regmap(codec->dev, NULL); - else - codec->control_data = regmap; + return -EINVAL; - if (IS_ERR(codec->control_data)) - return PTR_ERR(codec->control_data); + /* Device has made its own regmap arrangements */ + codec->control_data = regmap; codec->write = hw_write; codec->read = hw_read; -- cgit v1.2.3 From ea53bf77d147e7e560ac007fdaa30fb98c37c712 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Mar 2014 09:02:04 +0100 Subject: ASoC: Add snd_soc_kcontrol_codec() helper function For CODEC controls snd_kcontrol_chip() currently returns a pointer to the CODEC that registered the control. With the upcoming consolidation of platform and CODEC controls this will change. Prepare for this by introducing the snd_soc_kcontrol_codec() helper function that will hide the implementation details of how the CODEC for a control can be obtained. This will allow us to change this easily in the future. The patch also updates all CODEC drivers to use the new helper function. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown --- include/sound/soc.h | 14 ++++++++++++++ sound/soc/codecs/88pm860x-codec.c | 8 ++++---- sound/soc/codecs/ab8500-codec.c | 12 ++++++------ sound/soc/codecs/adav80x.c | 4 ++-- sound/soc/codecs/ak4641.c | 4 ++-- sound/soc/codecs/cs4270.c | 2 +- sound/soc/codecs/cs4271.c | 4 ++-- sound/soc/codecs/cs42l51.c | 4 ++-- sound/soc/codecs/da7210.c | 4 ++-- sound/soc/codecs/da7213.c | 4 ++-- sound/soc/codecs/da732x.c | 4 ++-- sound/soc/codecs/da9055.c | 2 +- sound/soc/codecs/lm4857.c | 4 ++-- sound/soc/codecs/max9768.c | 4 ++-- sound/soc/codecs/max98088.c | 12 ++++++------ sound/soc/codecs/max98090.c | 4 ++-- sound/soc/codecs/max98095.c | 16 ++++++++-------- sound/soc/codecs/pcm1681.c | 4 ++-- sound/soc/codecs/rt5631.c | 4 ++-- sound/soc/codecs/sgtl5000.c | 4 ++-- sound/soc/codecs/sta32x.c | 4 ++-- sound/soc/codecs/tas5086.c | 4 ++-- sound/soc/codecs/tlv320aic23.c | 4 ++-- sound/soc/codecs/tlv320dac33.c | 4 ++-- sound/soc/codecs/twl4030.c | 10 +++++----- sound/soc/codecs/twl6040.c | 8 ++++---- sound/soc/codecs/wl1273.c | 12 ++++++------ sound/soc/codecs/wm2000.c | 8 ++++---- sound/soc/codecs/wm8350.c | 4 ++-- sound/soc/codecs/wm8400.c | 2 +- sound/soc/codecs/wm8580.c | 2 +- sound/soc/codecs/wm8731.c | 4 ++-- sound/soc/codecs/wm8753.c | 4 ++-- sound/soc/codecs/wm8804.c | 4 ++-- sound/soc/codecs/wm8903.c | 4 ++-- sound/soc/codecs/wm8904.c | 14 +++++++------- sound/soc/codecs/wm8955.c | 4 ++-- sound/soc/codecs/wm8958-dsp2.c | 32 ++++++++++++++++---------------- sound/soc/codecs/wm8960.c | 4 ++-- sound/soc/codecs/wm8962.c | 8 ++++---- sound/soc/codecs/wm8983.c | 4 ++-- sound/soc/codecs/wm8985.c | 4 ++-- sound/soc/codecs/wm8990.c | 2 +- sound/soc/codecs/wm8991.c | 2 +- sound/soc/codecs/wm8994.c | 10 +++++----- sound/soc/codecs/wm8996.c | 4 ++-- sound/soc/codecs/wm9081.c | 4 ++-- sound/soc/codecs/wm_adsp.c | 4 ++-- sound/soc/codecs/wm_hubs.c | 2 +- 49 files changed, 154 insertions(+), 140 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168d8ff4..e150030b754d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1228,6 +1228,20 @@ static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec) return snd_soc_component_is_active(&codec->component); } +/** + * snd_soc_kcontrol_codec() - Returns the CODEC that registered the control + * @kcontrol: The control for which to get the CODEC + * + * Note: This function will only work correctly if the control has been + * registered with snd_soc_add_codec_controls() or via table based setup of + * snd_soc_codec_driver. Otherwise the behavior is undefined. + */ +static inline struct snd_soc_codec *snd_soc_kcontrol_codec( + struct snd_kcontrol *kcontrol) +{ + return snd_kcontrol_chip(kcontrol); +} + int snd_soc_util_init(void); void snd_soc_util_exit(void); diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index b07e17160f94..b18cafa5a858 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -276,7 +276,7 @@ static int snd_soc_get_volsw_2r_st(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; int val[2], val2[2], i; @@ -300,7 +300,7 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; int err; @@ -333,7 +333,7 @@ static int snd_soc_get_volsw_2r_out(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -353,7 +353,7 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 1ad92cbf0b24..1fb4402bf72d 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -1139,7 +1139,7 @@ static void anc_configure(struct snd_soc_codec *codec, static int sid_status_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); mutex_lock(&codec->mutex); @@ -1153,7 +1153,7 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol, static int sid_status_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); unsigned int param, sidconf, val; int status = 1; @@ -1208,7 +1208,7 @@ out: static int anc_status_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); mutex_lock(&codec->mutex); @@ -1221,7 +1221,7 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol, static int anc_status_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); struct device *dev = codec->dev; bool apply_fir, apply_iir; @@ -1306,7 +1306,7 @@ static int filter_control_info(struct snd_kcontrol *kcontrol, static int filter_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct filter_control *fc = (struct filter_control *)kcontrol->private_value; unsigned int i; @@ -1322,7 +1322,7 @@ static int filter_control_get(struct snd_kcontrol *kcontrol, static int filter_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct filter_control *fc = (struct filter_control *)kcontrol->private_value; unsigned int i; diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 5062e34ee8dc..cf170b5ef426 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -315,7 +315,7 @@ static int adav80x_set_deemph(struct snd_soc_codec *codec) static int adav80x_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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]; @@ -330,7 +330,7 @@ static int adav80x_put_deemph(struct snd_kcontrol *kcontrol, static int adav80x_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 868c0e2da1ec..7afe8f482088 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -74,7 +74,7 @@ static int ak4641_set_deemph(struct snd_soc_codec *codec) static int ak4641_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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]; @@ -89,7 +89,7 @@ static int ak4641_put_deemph(struct snd_kcontrol *kcontrol, static int ak4641_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 3920e6264948..9947a9583679 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -438,7 +438,7 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute) static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); int left = !ucontrol->value.integer.value[0]; int right = !ucontrol->value.integer.value[1]; diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index aef4965750c7..93cec52f4733 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -284,7 +284,7 @@ static int cs4271_set_deemph(struct snd_soc_codec *codec) static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -294,7 +294,7 @@ static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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]; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 6c0da2baa154..23acaa0263dc 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -55,7 +55,7 @@ struct cs42l51_private { static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3; switch (value) { @@ -83,7 +83,7 @@ static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned char val; switch (ucontrol->value.integer.value[0]) { diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 137e8ebc092c..21810e5f3321 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -335,7 +335,7 @@ static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel, static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); if (ucontrol->value.integer.value[0]) { /* Check if noise suppression is enabled */ @@ -358,7 +358,7 @@ static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol, static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); u8 val; if (ucontrol->value.integer.value[0]) { diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 738fa18a50d2..9ec577f0edb4 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -345,7 +345,7 @@ static void da7213_alc_calib(struct snd_soc_codec *codec) static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); int ret; @@ -361,7 +361,7 @@ static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol, static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); /* Force ALC offset calibration if enabling ALC */ diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 48f3fef68484..2fae31cb0067 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -332,7 +332,7 @@ static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum, static int da732x_hpf_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value; unsigned int reg = enum_ctrl->reg; unsigned int sel = ucontrol->value.integer.value[0]; @@ -360,7 +360,7 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol, static int da732x_hpf_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value; unsigned int reg = enum_ctrl->reg; int val; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index 4ff06b50fbba..ad19cc56702b 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -484,7 +484,7 @@ static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val) static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); u8 reg_val, adc_left, adc_right, mic_left, mic_right; int avg_left_data, avg_right_data, offset_l, offset_r; diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index 4f048db9f55f..a924bb9d7886 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -49,7 +49,7 @@ static const struct reg_default lm4857_default_regs[] = { static int lm4857_get_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = lm4857->mode; @@ -60,7 +60,7 @@ static int lm4857_get_mode(struct snd_kcontrol *kcontrol, static int lm4857_set_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); uint8_t value = ucontrol->value.integer.value[0]; diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index ec481fc428c7..e1c196a41930 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -43,7 +43,7 @@ static struct reg_default max9768_default_regs[] = { static int max9768_get_gpio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec); int val = gpio_get_value_cansleep(max9768->mute_gpio); @@ -55,7 +55,7 @@ static int max9768_get_gpio(struct snd_kcontrol *kcontrol, static int max9768_set_gpio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec); gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index ef7cf89f5623..9134982807b5 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -635,7 +635,7 @@ static SOC_ENUM_SINGLE_DECL(max98088_dai1_adc_filter_enum, static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); unsigned int sel = ucontrol->value.integer.value[0]; @@ -649,7 +649,7 @@ static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol, static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = max98088->mic1pre; @@ -659,7 +659,7 @@ static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol, static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); unsigned int sel = ucontrol->value.integer.value[0]; @@ -673,7 +673,7 @@ static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol, static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = max98088->mic2pre; @@ -1750,7 +1750,7 @@ static void max98088_setup_eq2(struct snd_soc_codec *codec) static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); struct max98088_pdata *pdata = max98088->pdata; int channel = max98088_get_channel(codec, kcontrol->id.name); @@ -1782,7 +1782,7 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol, static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec); int channel = max98088_get_channel(codec, kcontrol->id.name); struct max98088_cdata *cdata; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index f7b0b37aa858..49d12387ac2f 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -426,7 +426,7 @@ static const unsigned int max98090_rcv_lout_tlv[] = { static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -466,7 +466,7 @@ static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol, static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 03f0536e6f61..5d4c621dbf99 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -612,7 +612,7 @@ static SOC_ENUM_SINGLE_DECL(max98095_dai3_dac_filter_enum, static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); unsigned int sel = ucontrol->value.integer.value[0]; @@ -626,7 +626,7 @@ static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol, static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = max98095->mic1pre; @@ -636,7 +636,7 @@ static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol, static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); unsigned int sel = ucontrol->value.integer.value[0]; @@ -650,7 +650,7 @@ static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol, static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = max98095->mic2pre; @@ -1737,7 +1737,7 @@ static int max98095_get_eq_channel(const char *name) static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); struct max98095_pdata *pdata = max98095->pdata; int channel = max98095_get_eq_channel(kcontrol->id.name); @@ -1801,7 +1801,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); int channel = max98095_get_eq_channel(kcontrol->id.name); struct max98095_cdata *cdata; @@ -1891,7 +1891,7 @@ static int max98095_get_bq_channel(struct snd_soc_codec *codec, static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); struct max98095_pdata *pdata = max98095->pdata; int channel = max98095_get_bq_channel(codec, kcontrol->id.name); @@ -1952,7 +1952,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); int channel = max98095_get_bq_channel(codec, kcontrol->id.name); struct max98095_cdata *cdata; diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index e427544183d7..a722a023c262 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -115,7 +115,7 @@ static int pcm1681_set_deemph(struct snd_soc_codec *codec) static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -126,7 +126,7 @@ static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol, static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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]; diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index d4c229f0233f..30e234708579 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -188,7 +188,7 @@ static unsigned int mic_bst_tlv[] = { static int rt5631_dmic_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = rt5631->dmic_used_flag; @@ -199,7 +199,7 @@ static int rt5631_dmic_get(struct snd_kcontrol *kcontrol, static int rt5631_dmic_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec); rt5631->dmic_used_flag = ucontrol->value.integer.value[0]; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index d3ed1be5a186..b56caefcf664 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -296,7 +296,7 @@ static int dac_info_volsw(struct snd_kcontrol *kcontrol, static int dac_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int reg; int l; int r; @@ -349,7 +349,7 @@ static int dac_get_volsw(struct snd_kcontrol *kcontrol, static int dac_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int reg; int l; int r; diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 12577749b17b..0579d187135b 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -243,7 +243,7 @@ static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol, static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; unsigned int cfud; @@ -272,7 +272,7 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index a895a5e4bdf2..d48491a4a19d 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -272,7 +272,7 @@ static int tas5086_set_deemph(struct snd_soc_codec *codec) static int tas5086_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -283,7 +283,7 @@ static int tas5086_get_deemph(struct snd_kcontrol *kcontrol, static int tas5086_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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]; diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 20864ee8793b..686b8b85b956 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -82,7 +82,7 @@ static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0); static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); u16 val, reg; val = (ucontrol->value.integer.value[0] & 0x07); @@ -105,7 +105,7 @@ static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol, static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); u16 val; val = snd_soc_read(codec, TLV320AIC23_ANLG) & (0x1C0); diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 6bfc8a17331b..517055ab65ef 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -442,7 +442,7 @@ static int dac33_playback_event(struct snd_soc_dapm_widget *w, static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = dac33->fifo_mode; @@ -453,7 +453,7 @@ static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol, static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); int ret = 0; diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 975e0f760ac1..69e12a311ba2 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -830,7 +830,7 @@ static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg = mc->reg; unsigned int shift = mc->shift; unsigned int rshift = mc->rshift; @@ -859,7 +859,7 @@ static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg = mc->reg; unsigned int shift = mc->shift; unsigned int rshift = mc->rshift; @@ -888,7 +888,7 @@ static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -915,7 +915,7 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -956,7 +956,7 @@ static SOC_ENUM_SINGLE_DECL(twl4030_op_modes_enum, static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); if (twl4030->configured) { diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index bd3a20647fdf..0f6067f04e29 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -484,7 +484,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum, static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); ucontrol->value.enumerated.item[0] = priv->hs_power_mode; @@ -495,7 +495,7 @@ static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int high_perf = ucontrol->value.enumerated.item[0]; int ret = 0; @@ -512,7 +512,7 @@ static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol, static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); ucontrol->value.enumerated.item[0] = priv->pll_power_mode; @@ -523,7 +523,7 @@ static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol, static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); priv->pll_power_mode = ucontrol->value.enumerated.item[0]; diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 6be5f80b65f1..4ead0dc02b87 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -172,7 +172,7 @@ out: static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = wl1273->mode; @@ -190,7 +190,7 @@ static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); if (wl1273->mode == ucontrol->value.integer.value[0]) @@ -214,7 +214,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route); static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); dev_dbg(codec->dev, "%s: enter.\n", __func__); @@ -227,7 +227,7 @@ static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); int val, r = 0; @@ -251,7 +251,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings); static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); dev_dbg(codec->dev, "%s: enter.\n", __func__); @@ -264,7 +264,7 @@ static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol, static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); int r; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 83a2c872925c..a4c352cc3464 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -607,7 +607,7 @@ static int wm2000_anc_set_mode(struct wm2000_priv *wm2000) static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -618,7 +618,7 @@ static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol, static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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 ret; @@ -640,7 +640,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -651,7 +651,7 @@ static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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 ret; diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 757256bf7672..42a72b2404d5 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -302,7 +302,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out = NULL; struct soc_mixer_control *mc = @@ -345,7 +345,7 @@ static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_priv->out1; struct wm8350_output *out2 = &wm8350_priv->out2; diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 146564feaea0..edfdbcd9d316 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -93,7 +93,7 @@ static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0); static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; int reg = mc->reg; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index af7ed8b5d4e1..7665ff6aea6d 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -252,7 +252,7 @@ static int wm8580_out_vu(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index d74f43975b90..763b265d9528 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -119,7 +119,7 @@ static int wm8731_set_deemph(struct snd_soc_codec *codec) static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -130,7 +130,7 @@ static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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 ret = 0; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index cbb8d55052a4..53e57b4049a8 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -234,7 +234,7 @@ SOC_ENUM_SINGLE(WM8753_OUTCTL, 2, 2, wm8753_rout2_phase), static int wm8753_get_dai(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = wm8753->dai_func; @@ -244,7 +244,7 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol, static int wm8753_set_dai(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); u16 ioctl; diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index ee76f0fb4299..589455c3bfcd 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -106,7 +106,7 @@ static int txsrc_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec; unsigned int src; - codec = snd_kcontrol_chip(kcontrol); + codec = snd_soc_kcontrol_codec(kcontrol); src = snd_soc_read(codec, WM8804_SPDTX4); if (src & 0x40) ucontrol->value.integer.value[0] = 1; @@ -122,7 +122,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec; unsigned int src, txpwr; - codec = snd_kcontrol_chip(kcontrol); + codec = snd_soc_kcontrol_codec(kcontrol); if (ucontrol->value.integer.value[0] != 0 && ucontrol->value.integer.value[0] != 1) diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index b0084a127d18..b84940c359a1 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -439,7 +439,7 @@ static int wm8903_set_deemph(struct snd_soc_codec *codec) static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -450,7 +450,7 @@ static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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 ret = 0; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 49c35c36935e..f7c549949c54 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -391,7 +391,7 @@ static void wm8904_set_drc(struct snd_soc_codec *codec) static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); struct wm8904_pdata *pdata = wm8904->pdata; int value = ucontrol->value.integer.value[0]; @@ -409,7 +409,7 @@ static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol, static int wm8904_get_drc_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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->drc_cfg; @@ -462,7 +462,7 @@ static void wm8904_set_retune_mobile(struct snd_soc_codec *codec) static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); struct wm8904_pdata *pdata = wm8904->pdata; int value = ucontrol->value.integer.value[0]; @@ -480,7 +480,7 @@ static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, static int wm8904_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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->retune_mobile_cfg; @@ -520,7 +520,7 @@ static int wm8904_set_deemph(struct snd_soc_codec *codec) static int wm8904_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -530,7 +530,7 @@ static int wm8904_get_deemph(struct snd_kcontrol *kcontrol, static int wm8904_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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]; @@ -570,7 +570,7 @@ static SOC_ENUM_SINGLE_DECL(hpf_mode, WM8904_ADC_DIGITAL_0, 5, static int wm8904_adc_osr_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int val; int ret; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index fecd4e4f4c57..7e443c4f6f85 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -390,7 +390,7 @@ static int wm8955_set_deemph(struct snd_soc_codec *codec) static int wm8955_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -400,7 +400,7 @@ static int wm8955_get_deemph(struct snd_kcontrol *kcontrol, static int wm8955_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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]; diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index 7ac2e511403c..b2ebb104d879 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -456,7 +456,7 @@ static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif) static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; int value = ucontrol->value.integer.value[0]; @@ -478,7 +478,7 @@ static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol, static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg; @@ -500,7 +500,7 @@ static int wm8958_mbc_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int mbc = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc]; @@ -512,7 +512,7 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int mbc = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0]) @@ -546,7 +546,7 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol, static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; int value = ucontrol->value.integer.value[0]; @@ -568,7 +568,7 @@ static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol, static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ucontrol->value.enumerated.item[0] = wm8994->vss_cfg; @@ -579,7 +579,7 @@ static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol, static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; int value = ucontrol->value.integer.value[0]; @@ -601,7 +601,7 @@ static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol, static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg; @@ -623,7 +623,7 @@ static int wm8958_vss_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int vss = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = wm8994->vss_ena[vss]; @@ -635,7 +635,7 @@ static int wm8958_vss_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int vss = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0]) @@ -684,7 +684,7 @@ static int wm8958_hpf_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int hpf = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (hpf < 3) @@ -699,7 +699,7 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int hpf = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (hpf < 3) { @@ -746,7 +746,7 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol, static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; int value = ucontrol->value.integer.value[0]; @@ -768,7 +768,7 @@ static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol, static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg; @@ -790,7 +790,7 @@ static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int eq = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq]; @@ -802,7 +802,7 @@ static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int eq = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0]) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index d04e9cad445c..a145d0431b63 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -178,7 +178,7 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec) static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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; @@ -188,7 +188,7 @@ static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(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]; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 5522d2566c67..37986c84cbff 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1552,7 +1552,7 @@ static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int shift = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift); @@ -1564,7 +1564,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int shift = kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int old = wm8962->dsp2_ena; int ret = 0; @@ -1602,7 +1602,7 @@ out: static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int ret; /* Apply the update (if any) */ @@ -1632,7 +1632,7 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol, static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int ret; /* Apply the update (if any) */ diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 2b9bfa53efbf..19d5baa38f5c 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -552,7 +552,7 @@ static const struct snd_soc_dapm_route wm8983_audio_map[] = { static int eqmode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg; reg = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF); @@ -567,7 +567,7 @@ static int eqmode_get(struct snd_kcontrol *kcontrol, static int eqmode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int regpwr2, regpwr3; unsigned int reg_eq; diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 5473dc969585..ad23ffb8346c 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -526,7 +526,7 @@ static const struct snd_soc_dapm_route wm8985_dapm_routes[] = { static int eqmode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg; reg = snd_soc_read(codec, WM8985_EQ1_LOW_SHELF); @@ -541,7 +541,7 @@ static int eqmode_get(struct snd_kcontrol *kcontrol, static int eqmode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int regpwr2, regpwr3; unsigned int reg_eq; diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index c413c1991453..b5c1f0f07058 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -132,7 +132,7 @@ static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0); static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; int reg = mc->reg; diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 844cc4a60d66..b8fd284fc0c0 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -154,7 +154,7 @@ static const unsigned int out_sidetone_tlv[] = { static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int reg = kcontrol->private_value & 0xff; int ret; u16 val; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 6303537f54c6..3eb390be7dee 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -298,7 +298,7 @@ static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); int mask, ret; /* Can't enable both ADC and DAC paths simultaneously */ @@ -355,7 +355,7 @@ static int wm8994_get_drc(const char *name) static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; struct wm8994_pdata *pdata = &control->pdata; @@ -378,7 +378,7 @@ static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol, static int wm8994_get_drc_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int drc = wm8994_get_drc(kcontrol->id.name); @@ -462,7 +462,7 @@ static int wm8994_get_retune_mobile_block(const char *name) static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; struct wm8994_pdata *pdata = &control->pdata; @@ -485,7 +485,7 @@ static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int block = wm8994_get_retune_mobile_block(kcontrol->id.name); diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index c6cbb3b8ace9..69266332760e 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -412,7 +412,7 @@ static int wm8996_get_retune_mobile_block(const char *name) static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); struct wm8996_pdata *pdata = &wm8996->pdata; int block = wm8996_get_retune_mobile_block(kcontrol->id.name); @@ -434,7 +434,7 @@ static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, static int wm8996_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); int block = wm8996_get_retune_mobile_block(kcontrol->id.name); diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index d18eff31fbbc..185eb97769e7 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -340,7 +340,7 @@ static SOC_ENUM_SINGLE_DECL(speaker_mode, WM9081_ANALOGUE_SPEAKER_2, 6, static int speaker_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg; reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2); @@ -361,7 +361,7 @@ static int speaker_mode_get(struct snd_kcontrol *kcontrol, static int speaker_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned int reg_pwr = snd_soc_read(codec, WM9081_POWER_MANAGEMENT); unsigned int reg2 = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index bb5f7b4e3ebb..d9686dcd024c 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -242,7 +242,7 @@ struct wm_coeff_ctl { static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); @@ -254,7 +254,7 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index b6209662ab13..916817fe6632 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -337,7 +337,7 @@ static void enable_dc_servo(struct snd_soc_codec *codec) static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); int ret; -- cgit v1.2.3 From f6272ff8a5f42c614f4a338013f5323979121e0f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Mar 2014 09:02:05 +0100 Subject: ASoC: Add snd_soc_kcontrol_platform() helper function For platform controls snd_kcontrol_chip() currently returns a pointer to the platform that registered the control. With the upcoming consolidation of platform and CODEC controls this will change. Prepare for this by introducing the snd_soc_kcontrol_platform() helper function that will hide the implementation details of how the platform for a control can be obtained. This will allow us to change this easily in the future. The patch also updates all platforms to use this new helper function. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 14 ++++++++++++++ sound/soc/intel/sst-haswell-pcm.c | 8 ++++---- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index e150030b754d..14e7457e2347 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1242,6 +1242,20 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec( return snd_kcontrol_chip(kcontrol); } +/** + * snd_soc_kcontrol_platform() - Returns the platform that registerd the control + * @kcontrol: The control for which to get the platform + * + * Note: This function will only work correctly if the control has been + * registered with snd_soc_add_platform_controls() or via table based setup of + * a snd_soc_platform_driver. Otherwise the behavior is undefined. + */ +static inline struct snd_soc_codec *snd_soc_kcontrol_platform( + struct snd_kcontrol *kcontrol) +{ + return snd_kcontrol_chip(kcontrol); +} + int snd_soc_util_init(void); void snd_soc_util_exit(void); diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 0a32dd13a23d..67a5eb3c6196 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -136,7 +136,7 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value) static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct hsw_priv_data *pdata = @@ -174,7 +174,7 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct hsw_priv_data *pdata = @@ -206,7 +206,7 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, static int hsw_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); struct sst_hsw *hsw = pdata->hsw; u32 volume; @@ -231,7 +231,7 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol, static int hsw_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); struct sst_hsw *hsw = pdata->hsw; unsigned int volume = 0; -- cgit v1.2.3 From 20a0ec27ea11af0251ffeb5ee2b96cc5c72cb517 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Mar 2014 09:02:09 +0100 Subject: ASoC: Remove IO register modifier callbacks There are no ASoC drivers left that use them and new drivers are supposed to use regmap for this. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 14 --------- sound/soc/soc-cache.c | 2 -- sound/soc/soc-core.c | 80 ++++++--------------------------------------------- 3 files changed, 8 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 14e7457e2347..a355d0f9a6f9 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -393,12 +393,6 @@ int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_codec_readable_register(struct snd_soc_codec *codec, - unsigned int reg); -int snd_soc_codec_writable_register(struct snd_soc_codec *codec, - unsigned int reg); int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, struct regmap *regmap); int snd_soc_cache_sync(struct snd_soc_codec *codec); @@ -692,9 +686,6 @@ struct snd_soc_codec { struct list_head list; struct list_head card_list; int num_dai; - int (*volatile_register)(struct snd_soc_codec *, unsigned int); - int (*readable_register)(struct snd_soc_codec *, unsigned int); - int (*writable_register)(struct snd_soc_codec *, unsigned int); /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ @@ -756,11 +747,6 @@ struct snd_soc_codec_driver { /* codec IO */ unsigned int (*read)(struct snd_soc_codec *, unsigned int); int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); - int (*display_register)(struct snd_soc_codec *, char *, - size_t, unsigned int); - int (*volatile_register)(struct snd_soc_codec *, unsigned int); - int (*readable_register)(struct snd_soc_codec *, unsigned int); - int (*writable_register)(struct snd_soc_codec *, unsigned int); unsigned int reg_cache_size; short reg_cache_step; short reg_word_size; diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index bfed3e4c45ff..3fa77d5f9b75 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -162,8 +162,6 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) i, codec_drv->reg_word_size) == val) continue; - WARN_ON(!snd_soc_codec_writable_register(codec, i)); - ret = snd_soc_write(codec, i, val); if (ret) return ret; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 5c0ed3931285..41bd24348520 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -154,22 +154,15 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, step = codec->driver->reg_cache_step; for (i = 0; i < codec->driver->reg_cache_size; i += step) { - if (!snd_soc_codec_readable_register(codec, i)) - continue; - if (codec->driver->display_register) { - count += codec->driver->display_register(codec, buf + count, - PAGE_SIZE - count, i); - } else { - /* only support larger than PAGE_SIZE bytes debugfs - * entries for the default case */ - if (p >= pos) { - if (total + len >= count - 1) - break; - format_register_str(codec, i, buf + total, len); - total += len; - } - p += len; + /* only support larger than PAGE_SIZE bytes debugfs + * entries for the default case */ + if (p >= pos) { + if (total + len >= count - 1) + break; + format_register_str(codec, i, buf + total, len); + total += len; } + p += len; } total = min(total, count - 1); @@ -1979,60 +1972,6 @@ static struct platform_driver soc_driver = { .remove = soc_remove, }; -/** - * snd_soc_codec_volatile_register: Report if a register is volatile. - * - * @codec: CODEC to query. - * @reg: Register to query. - * - * Boolean function indiciating if a CODEC register is volatile. - */ -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - if (codec->volatile_register) - return codec->volatile_register(codec, reg); - else - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register); - -/** - * snd_soc_codec_readable_register: Report if a register is readable. - * - * @codec: CODEC to query. - * @reg: Register to query. - * - * Boolean function indicating if a CODEC register is readable. - */ -int snd_soc_codec_readable_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - if (codec->readable_register) - return codec->readable_register(codec, reg); - else - return 1; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_readable_register); - -/** - * snd_soc_codec_writable_register: Report if a register is writable. - * - * @codec: CODEC to query. - * @reg: Register to query. - * - * Boolean function indicating if a CODEC register is writable. - */ -int snd_soc_codec_writable_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - if (codec->writable_register) - return codec->writable_register(codec, reg); - else - return 1; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register); - /** * snd_soc_new_ac97_codec - initailise AC97 device * @codec: audio codec @@ -4136,9 +4075,6 @@ int snd_soc_register_codec(struct device *dev, codec->write = codec_drv->write; codec->read = codec_drv->read; - codec->volatile_register = codec_drv->volatile_register; - codec->readable_register = codec_drv->readable_register; - codec->writable_register = codec_drv->writable_register; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; -- cgit v1.2.3 From 98e639fb8a3ed1bf2bd512626c3cfc2992a57113 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Mar 2014 09:02:11 +0100 Subject: ASoC: Track which components have been registered with snd_soc_register_component() snd_soc_unregister_component() takes the parent device of the component as a parameter and then looks up the component based on this. This is a problem if multiple components are registered for the same parent device. Currently drivers do not do this, but some drivers register a CPU DAI component and a platform for the same parent device. This will become a problem once platforms are also made components. To make sure that snd_soc_unregister_component() will not accidentally unregister the platform in such a case only consider components that were registered with snd_soc_register_component(). This is only meant as short term stopgap solution to be able to continue componentisation. Long term we'll need something different. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index a355d0f9a6f9..f8a79c17628e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -662,6 +662,7 @@ struct snd_soc_component { unsigned int active; unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */ + unsigned int registered_as_component:1; struct list_head list; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 41bd24348520..3314efb365e3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3879,6 +3879,7 @@ int snd_soc_register_component(struct device *dev, } cmpnt->ignore_pmdown_time = true; + cmpnt->registered_as_component = true; return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, NULL, dai_drv, num_dai, true); @@ -3894,7 +3895,7 @@ void snd_soc_unregister_component(struct device *dev) struct snd_soc_component *cmpnt; list_for_each_entry(cmpnt, &component_list, list) { - if (dev == cmpnt->dev) + if (dev == cmpnt->dev && cmpnt->registered_as_component) goto found; } return; -- cgit v1.2.3 From b37f1d123c69c0d7730704d65b83eaac780c0e3b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 18 Mar 2014 09:02:12 +0100 Subject: ASoC: Let snd_soc_platform subclass snd_soc_component There is an increasing amount of code that is very similar between platforms, CODECS and other components. Making platforms a component will allow us to share this code. For now the patch just adds component and component_driver fields to the platform and platform_driver structs and registers the platform as a component. Followup patches will be used to consolidate code between the different types of components. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 16 ++++++++++++++++ sound/soc/soc-core.c | 14 ++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index f8a79c17628e..94a2dc20ad6e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -778,6 +778,7 @@ struct snd_soc_platform_driver { int (*remove)(struct snd_soc_platform *); int (*suspend)(struct snd_soc_dai *dai); int (*resume)(struct snd_soc_dai *dai); + struct snd_soc_component_driver component_driver; /* pcm creation and destruction */ int (*pcm_new)(struct snd_soc_pcm_runtime *); @@ -831,6 +832,8 @@ struct snd_soc_platform { struct list_head list; struct list_head card_list; + struct snd_soc_component component; + struct snd_soc_dapm_context dapm; #ifdef CONFIG_DEBUG_FS @@ -1107,6 +1110,19 @@ static inline struct snd_soc_codec *snd_soc_component_to_codec( return container_of(component, struct snd_soc_codec, component); } +/** + * snd_soc_component_to_platform() - Casts a component to the platform it is embedded in + * @component: The component to cast to a platform + * + * This function must only be used on components that are known to be platforms. + * Otherwise the behavior is undefined. + */ +static inline struct snd_soc_platform *snd_soc_component_to_platform( + struct snd_soc_component *component) +{ + return container_of(component, struct snd_soc_platform, component); +} + /* codec IO */ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); unsigned int snd_soc_write(struct snd_soc_codec *codec, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3314efb365e3..a95c7e524dfc 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3921,6 +3921,8 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_component); int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, const struct snd_soc_platform_driver *platform_drv) { + int ret; + /* create platform component name */ platform->name = fmt_single_name(dev, &platform->id); if (platform->name == NULL) @@ -3933,6 +3935,16 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->dapm.stream_event = platform_drv->stream_event; mutex_init(&platform->mutex); + /* register component */ + ret = __snd_soc_register_component(dev, &platform->component, + &platform_drv->component_driver, + NULL, NULL, 0, false); + if (ret < 0) { + dev_err(platform->component.dev, + "ASoC: Failed to register component: %d\n", ret); + return ret; + } + mutex_lock(&client_mutex); list_add(&platform->list, &platform_list); mutex_unlock(&client_mutex); @@ -3974,6 +3986,8 @@ EXPORT_SYMBOL_GPL(snd_soc_register_platform); */ void snd_soc_remove_platform(struct snd_soc_platform *platform) { + snd_soc_unregister_component(platform->dev); + mutex_lock(&client_mutex); list_del(&platform->list); mutex_unlock(&client_mutex); -- cgit v1.2.3 From 1a39019e939f620f39a1b914231ab6ba9013b208 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 8 Apr 2014 11:18:10 +0800 Subject: ASoC: core: Allow snd_soc_update_bits use 32 bits register Change reg's type from unsigned short to unsigned int. So that we can use 32 bits reg value in snd_soc_update_bits. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- include/sound/soc.h | 6 +++--- sound/soc/soc-core.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168d8ff4..4ed706bf11d1 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -469,12 +469,12 @@ static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, #endif /* codec register bit access */ -int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, +int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value); int snd_soc_update_bits_locked(struct snd_soc_codec *codec, - unsigned short reg, unsigned int mask, + unsigned int reg, unsigned int mask, unsigned int value); -int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, +int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value); int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 777453158347..7f0a9297e420 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2330,7 +2330,7 @@ EXPORT_SYMBOL_GPL(snd_soc_write); * * Returns 1 for change, 0 for no change, or negative error code. */ -int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, +int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value) { bool change; @@ -2371,7 +2371,7 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits); * Returns 1 for change else 0. */ int snd_soc_update_bits_locked(struct snd_soc_codec *codec, - unsigned short reg, unsigned int mask, + unsigned int reg, unsigned int mask, unsigned int value) { int change; @@ -2396,7 +2396,7 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked); * * Returns 1 for change else 0. */ -int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, +int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value) { int change; @@ -2911,7 +2911,7 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, int min = mc->min; int mask = (1 << (fls(min + max) - 1)) - 1; int err = 0; - unsigned short val, val_mask, val2 = 0; + unsigned int val, val_mask, val2 = 0; val_mask = mask << shift; val = (ucontrol->value.integer.value[0] + min) & mask; -- cgit v1.2.3 From 71d97a7943017faf03707836d00a260a108f4c89 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Fri, 28 Mar 2014 10:46:18 +0800 Subject: ASoC: rt5640: Use the platform data for DMIC settings The patch uses the platform data for DMIC settings. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- include/sound/rt5640.h | 4 +++ sound/soc/codecs/rt5640.c | 77 ++++++++++++++--------------------------------- 2 files changed, 27 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/include/sound/rt5640.h b/include/sound/rt5640.h index 27cc75ed67f8..59d26dd81e45 100644 --- a/include/sound/rt5640.h +++ b/include/sound/rt5640.h @@ -16,6 +16,10 @@ struct rt5640_platform_data { bool in1_diff; bool in2_diff; + bool dmic_en; + bool dmic1_data_pin; /* 0 = IN1P; 1 = GPIO3 */ + bool dmic2_data_pin; /* 0 = IN1N; 1 = GPIO4 */ + int ldo1_en; /* GPIO for LDO1_EN */ }; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index f0717db3e935..6ede622ad44f 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -872,54 +872,6 @@ static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP, static const struct snd_kcontrol_new rt5640_sdi_mux = SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum); -static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, RT5640_GPIO_CTRL1, - RT5640_GP2_PIN_MASK | RT5640_GP3_PIN_MASK, - RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP3_PIN_DMIC1_SDA); - snd_soc_update_bits(codec, RT5640_DMIC, - RT5640_DMIC_1L_LH_MASK | RT5640_DMIC_1R_LH_MASK | - RT5640_DMIC_1_DP_MASK, - RT5640_DMIC_1L_LH_FALLING | RT5640_DMIC_1R_LH_RISING | - RT5640_DMIC_1_DP_IN1P); - break; - - default: - return 0; - } - - return 0; -} - -static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, RT5640_GPIO_CTRL1, - RT5640_GP2_PIN_MASK | RT5640_GP4_PIN_MASK, - RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP4_PIN_DMIC2_SDA); - snd_soc_update_bits(codec, RT5640_DMIC, - RT5640_DMIC_2L_LH_MASK | RT5640_DMIC_2R_LH_MASK | - RT5640_DMIC_2_DP_MASK, - RT5640_DMIC_2L_LH_FALLING | RT5640_DMIC_2R_LH_RISING | - RT5640_DMIC_2_DP_IN1N); - break; - - default: - return 0; - } - - return 0; -} - static void hp_amp_power_on(struct snd_soc_codec *codec) { struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); @@ -1054,12 +1006,10 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, set_dmic_clk, SND_SOC_DAPM_PRE_PMU), - SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC, - RT5640_DMIC_1_EN_SFT, 0, rt5640_set_dmic1_event, - SND_SOC_DAPM_PRE_PMU), - SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC, - RT5640_DMIC_2_EN_SFT, 0, rt5640_set_dmic2_event, - SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC, RT5640_DMIC_1_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC, RT5640_DMIC_2_EN_SFT, 0, + NULL, 0), /* Boost */ SND_SOC_DAPM_PGA("BST1", RT5640_PWR_ANLG2, RT5640_PWR_BST1_BIT, 0, NULL, 0), @@ -2187,6 +2137,25 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, RT5640_IN_DF2, RT5640_IN_DF2); + if (rt5640->pdata.dmic_en) { + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL); + + if (rt5640->pdata.dmic1_data_pin) { + regmap_update_bits(rt5640->regmap, RT5640_DMIC, + RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3); + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA); + } + + if (rt5640->pdata.dmic2_data_pin) { + regmap_update_bits(rt5640->regmap, RT5640_DMIC, + RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4); + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA); + } + } + rt5640->hp_mute = 1; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640, -- cgit v1.2.3 From 2439ea1f0f8f4cc98dfae0d1cd5ba88f6c3ee9ad Mon Sep 17 00:00:00 2001 From: Sven Brandau Date: Wed, 2 Apr 2014 10:25:05 +0200 Subject: ASoC: sta350: Add codec driver The TI STA350 is an integrated 2.1-channel power amplifier that is controllable over I2C. This patch adds an ASoC driver for it. At a glance, this chip is very similar to the STA320 for which a driver already exists. In details, however, the register maps contain subtle differences which made a whole new driver easier to write and maintain. [daniel@zonque.org: cleanups, DT property rework, rebased on asoc-next] Signed-off-by: Sven Brandau Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/st,sta350.txt | 107 ++ include/sound/sta350.h | 52 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/sta350.c | 1266 ++++++++++++++++++++ sound/soc/codecs/sta350.h | 228 ++++ 6 files changed, 1660 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/st,sta350.txt create mode 100644 include/sound/sta350.h create mode 100644 sound/soc/codecs/sta350.c create mode 100644 sound/soc/codecs/sta350.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/st,sta350.txt b/Documentation/devicetree/bindings/sound/st,sta350.txt new file mode 100644 index 000000000000..950188891abd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,sta350.txt @@ -0,0 +1,107 @@ +STA350 audio CODEC + +The driver for this device only supports I2C. + +Required properties: + + - compatible: "st,sta350" + - reg: the I2C address of the device for I2C + - reset-gpios: a GPIO spec for the reset pin. If specified, it will be + deasserted before communication to the codec starts. + + - power-down-gpios: a GPIO spec for the power down pin. If specified, + it will be deasserted before communication to the codec + starts. + + - vdd-dig-supply: regulator spec, providing 3.3V + - vdd-pll-supply: regulator spec, providing 3.3V + - vcc-supply: regulator spec, providing 5V - 26V + +Optional properties: + + - st,output-conf: number, Selects the output configuration: + 0: 2-channel (full-bridge) power, 2-channel data-out + 1: 2 (half-bridge). 1 (full-bridge) on-board power + 2: 2 Channel (Full-Bridge) Power, 1 Channel FFX + 3: 1 Channel Mono-Parallel + If parameter is missing, mode 0 will be enabled. + + - st,ch1-output-mapping: Channel 1 output mapping + - st,ch2-output-mapping: Channel 2 output mapping + - st,ch3-output-mapping: Channel 3 output mapping + 0: Channel 1 + 1: Channel 2 + 2: Channel 3 + If parameter is missing, channel 1 is choosen. + + - st,thermal-warning-recover: + If present, thermal warning recovery is enabled. + + - st,thermal-warning-adjustment: + If present, thermal warning adjustment is enabled. + + - st,fault-detect-recovery: + If present, then fault recovery will be enabled. + + - st,ffx-power-output-mode: string + The FFX power output mode selects how the FFX output timing is + configured. Must be one of these values: + - "drop-compensation" + - "tapered-compensation" + - "full-power-mode" + - "variable-drop-compensation" (default) + + - st,drop-compensation-ns: number + Only required for "st,ffx-power-output-mode" == + "variable-drop-compensation". + Specifies the drop compensation in nanoseconds. + The value must be in the range of 0..300, and only + multiples of 20 are allowed. Default is 140ns. + + - st,overcurrent-warning-adjustment: + If present, overcurrent warning adjustment is enabled. + + - st,max-power-use-mpcc: + If present, then MPCC bits are used for MPC coefficients, + otherwise standard MPC coefficients are used. + + - st,max-power-corr: + If present, power bridge correction for THD reduction near maximum + power output is enabled. + + - st,am-reduction-mode: + If present, FFX mode runs in AM reduction mode, otherwise normal + FFX mode is used. + + - st,odd-pwm-speed-mode: + If present, PWM speed mode run on odd speed mode (341.3 kHz) on all + channels. If not present, normal PWM spped mode (384 kHz) will be used. + + - st,distortion-compensation: + If present, distortion compensation variable uses DCC coefficient. + If not present, preset DC coefficient is used. + + - st,invalid-input-detect-mute: + If not present, automatic invalid input detect mute is enabled. + + + +Example: + +codec: sta350@38 { + compatible = "st,sta350"; + reg = <0x1c>; + reset-gpios = <&gpio1 19 0>; + power-down-gpios = <&gpio1 16 0>; + st,output-conf = <0x3>; // set output to 2-channel + // (full-bridge) power, + // 2-channel data-out + st,ch1-output-mapping = <0>; // set channel 1 output ch 1 + st,ch2-output-mapping = <0>; // set channel 2 output ch 1 + st,ch3-output-mapping = <0>; // set channel 3 output ch 1 + st,max-power-correction; // enables power bridge + // correction for THD reduction + // near maximum power output + st,invalid-input-detect-mute; // mute if no valid digital + // audio signal is provided. +}; diff --git a/include/sound/sta350.h b/include/sound/sta350.h new file mode 100644 index 000000000000..3a3298106b22 --- /dev/null +++ b/include/sound/sta350.h @@ -0,0 +1,52 @@ +/* + * Platform data for ST STA350 ASoC codec driver. + * + * Copyright: 2014 Raumfeld GmbH + * Author: Sven Brandau + * + * 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 __LINUX_SND__STA350_H +#define __LINUX_SND__STA350_H + +#define STA350_OCFG_2CH 0 +#define STA350_OCFG_2_1CH 1 +#define STA350_OCFG_1CH 3 + +#define STA350_OM_CH1 0 +#define STA350_OM_CH2 1 +#define STA350_OM_CH3 2 + +#define STA350_THERMAL_ADJUSTMENT_ENABLE 1 +#define STA350_THERMAL_RECOVERY_ENABLE 2 +#define STA350_FAULT_DETECT_RECOVERY_BYPASS 1 + +#define STA350_FFX_PM_DROP_COMP 0 +#define STA350_FFX_PM_TAPERED_COMP 1 +#define STA350_FFX_PM_FULL_POWER 2 +#define STA350_FFX_PM_VARIABLE_DROP_COMP 3 + + +struct sta350_platform_data { + u8 output_conf; + u8 ch1_output_mapping; + u8 ch2_output_mapping; + u8 ch3_output_mapping; + u8 ffx_power_output_mode; + u8 drop_compensation_ns; + unsigned int thermal_warning_recovery:1; + unsigned int thermal_warning_adjustment:1; + unsigned int fault_detect_recovery:1; + unsigned int oc_warning_adjustment:1; + unsigned int max_power_use_mpcc:1; + unsigned int max_power_correction:1; + unsigned int am_reduction_mode:1; + unsigned int odd_pwm_speed_mode:1; + unsigned int distortion_compensation:1; + unsigned int invalid_input_detect_mute:1; +}; + +#endif /* __LINUX_SND__STA350_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f0e840137887..c7b853f520cf 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -80,6 +80,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_SSM2602_SPI if SPI_MASTER select SND_SOC_SSM2602_I2C if I2C select SND_SOC_STA32X if I2C + select SND_SOC_STA350 if I2C select SND_SOC_STA529 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS5086 if I2C @@ -435,6 +436,10 @@ config SND_SOC_SSM2602_I2C config SND_SOC_STA32X tristate +config SND_SOC_STA350 + tristate "STA350 speaker amplifier" + depends on I2C + config SND_SOC_STA529 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3c4d275d064b..efdb4d060201 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -74,6 +74,7 @@ snd-soc-ssm2602-objs := ssm2602.o snd-soc-ssm2602-spi-objs := ssm2602-spi.o snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o snd-soc-sta32x-objs := sta32x.o +snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o @@ -221,6 +222,7 @@ obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o +obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c new file mode 100644 index 000000000000..552e92a6b770 --- /dev/null +++ b/sound/soc/codecs/sta350.c @@ -0,0 +1,1266 @@ +/* + * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system + * + * Copyright: 2014 Raumfeld GmbH + * Author: Sven Brandau + * + * based on code from: + * Raumfeld GmbH + * Johannes Stezenbach + * Wolfson Microelectronics PLC. + * Mark Brown + * Freescale Semiconductor, Inc. + * Timur Tabi + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sta350.h" + +#define STA350_RATES (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +#define STA350_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) + +/* Power-up register defaults */ +static const struct reg_default sta350_regs[] = { + { 0x0, 0x63 }, + { 0x1, 0x80 }, + { 0x2, 0xdf }, + { 0x3, 0x40 }, + { 0x4, 0xc2 }, + { 0x5, 0x5c }, + { 0x6, 0x00 }, + { 0x7, 0xff }, + { 0x8, 0x60 }, + { 0x9, 0x60 }, + { 0xa, 0x60 }, + { 0xb, 0x00 }, + { 0xc, 0x00 }, + { 0xd, 0x00 }, + { 0xe, 0x00 }, + { 0xf, 0x40 }, + { 0x10, 0x80 }, + { 0x11, 0x77 }, + { 0x12, 0x6a }, + { 0x13, 0x69 }, + { 0x14, 0x6a }, + { 0x15, 0x69 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x00 }, + { 0x19, 0x00 }, + { 0x1a, 0x00 }, + { 0x1b, 0x00 }, + { 0x1c, 0x00 }, + { 0x1d, 0x00 }, + { 0x1e, 0x00 }, + { 0x1f, 0x00 }, + { 0x20, 0x00 }, + { 0x21, 0x00 }, + { 0x22, 0x00 }, + { 0x23, 0x00 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x00 }, + { 0x27, 0x2a }, + { 0x28, 0xc0 }, + { 0x29, 0xf3 }, + { 0x2a, 0x33 }, + { 0x2b, 0x00 }, + { 0x2c, 0x0c }, + { 0x31, 0x00 }, + { 0x36, 0x00 }, + { 0x37, 0x00 }, + { 0x38, 0x00 }, + { 0x39, 0x01 }, + { 0x3a, 0xee }, + { 0x3b, 0xff }, + { 0x3c, 0x7e }, + { 0x3d, 0xc0 }, + { 0x3e, 0x26 }, + { 0x3f, 0x00 }, + { 0x48, 0x00 }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x04 }, + { 0x4c, 0x00 }, +}; + +static const struct regmap_range sta350_write_regs_range[] = { + regmap_reg_range(STA350_CONFA, STA350_AUTO2), + regmap_reg_range(STA350_C1CFG, STA350_FDRC2), + regmap_reg_range(STA350_EQCFG, STA350_EVOLRES), + regmap_reg_range(STA350_NSHAPE, STA350_MISC2), +}; + +static const struct regmap_range sta350_read_regs_range[] = { + regmap_reg_range(STA350_CONFA, STA350_AUTO2), + regmap_reg_range(STA350_C1CFG, STA350_STATUS), + regmap_reg_range(STA350_EQCFG, STA350_EVOLRES), + regmap_reg_range(STA350_NSHAPE, STA350_MISC2), +}; + +static const struct regmap_range sta350_volatile_regs_range[] = { + regmap_reg_range(STA350_CFADDR2, STA350_CFUD), + regmap_reg_range(STA350_STATUS, STA350_STATUS), +}; + +static const struct regmap_access_table sta350_write_regs = { + .yes_ranges = sta350_write_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_write_regs_range), +}; + +static const struct regmap_access_table sta350_read_regs = { + .yes_ranges = sta350_read_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_read_regs_range), +}; + +static const struct regmap_access_table sta350_volatile_regs = { + .yes_ranges = sta350_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_volatile_regs_range), +}; + +/* regulator power supply names */ +static const char * const sta350_supply_names[] = { + "vdd-dig", /* digital supply, 3.3V */ + "vdd-pll", /* pll supply, 3.3V */ + "vcc" /* power amp supply, 5V - 26V */ +}; + +/* codec private data */ +struct sta350_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[ARRAY_SIZE(sta350_supply_names)]; + struct sta350_platform_data *pdata; + + unsigned int mclk; + unsigned int format; + + u32 coef_shadow[STA350_COEF_COUNT]; + int shutdown; + + struct gpio_desc *gpiod_nreset; + struct gpio_desc *gpiod_power_down; + + struct mutex coeff_lock; +}; + +static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1); +static const DECLARE_TLV_DB_SCALE(tone_tlv, -1200, 200, 0); + +static const char * const sta350_drc_ac[] = { + "Anti-Clipping", "Dynamic Range Compression" +}; +static const char * const sta350_auto_gc_mode[] = { + "User", "AC no clipping", "AC limited clipping (10%)", + "DRC nighttime listening mode" +}; +static const char * const sta350_auto_xo_mode[] = { + "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", + "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", + "340Hz", "360Hz" +}; +static const char * const sta350_binary_output[] = { + "FFX 3-state output - normal operation", "Binary output" +}; +static const char * const sta350_limiter_select[] = { + "Limiter Disabled", "Limiter #1", "Limiter #2" +}; +static const char * const sta350_limiter_attack_rate[] = { + "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024", + "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752", + "0.0645", "0.0564", "0.0501", "0.0451" +}; +static const char * const sta350_limiter_release_rate[] = { + "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299", + "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137", + "0.0134", "0.0117", "0.0110", "0.0104" +}; +static const char * const sta350_noise_shaper_type[] = { + "Third order", "Fourth order" +}; + +static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_attack_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_release_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0), + 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_attack_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0), + 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0), + 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_release_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0), + 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0), + 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0), + 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0), +); + +static SOC_ENUM_SINGLE_DECL(sta350_drc_ac_enum, + STA350_CONFD, STA350_CONFD_DRC_SHIFT, + sta350_drc_ac); +static SOC_ENUM_SINGLE_DECL(sta350_noise_shaper_enum, + STA350_CONFE, STA350_CONFE_NSBW_SHIFT, + sta350_noise_shaper_type); +static SOC_ENUM_SINGLE_DECL(sta350_auto_gc_enum, + STA350_AUTO1, STA350_AUTO1_AMGC_SHIFT, + sta350_auto_gc_mode); +static SOC_ENUM_SINGLE_DECL(sta350_auto_xo_enum, + STA350_AUTO2, STA350_AUTO2_XO_SHIFT, + sta350_auto_xo_mode); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch1_enum, + STA350_C1CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch2_enum, + STA350_C2CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch3_enum, + STA350_C3CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch1_enum, + STA350_C1CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch2_enum, + STA350_C2CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch3_enum, + STA350_C3CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter1_attack_rate_enum, + STA350_L1AR, STA350_LxA_SHIFT, + sta350_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter2_attack_rate_enum, + STA350_L2AR, STA350_LxA_SHIFT, + sta350_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter1_release_rate_enum, + STA350_L1AR, STA350_LxR_SHIFT, + sta350_limiter_release_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter2_release_rate_enum, + STA350_L2AR, STA350_LxR_SHIFT, + sta350_limiter_release_rate); + +/* + * byte array controls for setting biquad, mixer, scaling coefficients; + * for biquads all five coefficients need to be set in one go, + * mixer and pre/postscale coefs can be set individually; + * each coef is 24bit, the bytes are ordered in the same way + * as given in the STA350 data sheet (big endian; b1, b2, a1, a2, b0) + */ + +static int sta350_coefficient_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int numcoef = kcontrol->private_value >> 16; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 3 * numcoef; + return 0; +} + +static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud, val; + int i, ret = 0; + + mutex_lock(&sta350->coeff_lock); + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + + regmap_write(sta350->regmap, STA350_CFADDR2, index); + if (numcoef == 1) { + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); + } else if (numcoef == 5) { + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); + } else { + ret = -EINVAL; + goto exit_unlock; + } + + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +exit_unlock: + mutex_unlock(&sta350->coeff_lock); + + return ret; +} + +static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud; + int i; + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + + regmap_write(sta350->regmap, STA350_CFADDR2, index); + for (i = 0; i < numcoef && (index + i < STA350_COEF_COUNT); i++) + sta350->coef_shadow[index + i] = + (ucontrol->value.bytes.data[3 * i] << 16) + | (ucontrol->value.bytes.data[3 * i + 1] << 8) + | (ucontrol->value.bytes.data[3 * i + 2]); + for (i = 0; i < 3 * numcoef; i++) + regmap_write(sta350->regmap, STA350_B1CF1 + i, + ucontrol->value.bytes.data[i]); + if (numcoef == 1) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); + else if (numcoef == 5) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x02); + else + return -EINVAL; + + return 0; +} + +static int sta350_sync_coef_shadow(struct snd_soc_codec *codec) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + unsigned int cfud; + int i; + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + + for (i = 0; i < STA350_COEF_COUNT; i++) { + regmap_write(sta350->regmap, STA350_CFADDR2, i); + regmap_write(sta350->regmap, STA350_B1CF1, + (sta350->coef_shadow[i] >> 16) & 0xff); + regmap_write(sta350->regmap, STA350_B1CF2, + (sta350->coef_shadow[i] >> 8) & 0xff); + regmap_write(sta350->regmap, STA350_B1CF3, + (sta350->coef_shadow[i]) & 0xff); + /* + * chip documentation does not say if the bits are + * self-clearing, so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); + } + return 0; +} + +static int sta350_cache_sync(struct snd_soc_codec *codec) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + unsigned int mute; + int rc; + + /* mute during register sync */ + regmap_read(sta350->regmap, STA350_CFUD, &mute); + regmap_write(sta350->regmap, STA350_MMUTE, mute | STA350_MMUTE_MMUTE); + sta350_sync_coef_shadow(codec); + rc = regcache_sync(sta350->regmap); + regmap_write(sta350->regmap, STA350_MMUTE, mute); + return rc; +} + +#define SINGLE_COEF(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta350_coefficient_info, \ + .get = sta350_coefficient_get,\ + .put = sta350_coefficient_put, \ + .private_value = index | (1 << 16) } + +#define BIQUAD_COEFS(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta350_coefficient_info, \ + .get = sta350_coefficient_get,\ + .put = sta350_coefficient_put, \ + .private_value = index | (5 << 16) } + +static const struct snd_kcontrol_new sta350_snd_controls[] = { +SOC_SINGLE_TLV("Master Volume", STA350_MVOL, 0, 0xff, 1, mvol_tlv), +/* VOL */ +SOC_SINGLE_TLV("Ch1 Volume", STA350_C1VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch2 Volume", STA350_C2VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch3 Volume", STA350_C3VOL, 0, 0xff, 1, chvol_tlv), +/* CONFD */ +SOC_SINGLE("High Pass Filter Bypass Switch", + STA350_CONFD, STA350_CONFD_HPB_SHIFT, 1, 1), +SOC_SINGLE("De-emphasis Filter Switch", + STA350_CONFD, STA350_CONFD_DEMP_SHIFT, 1, 0), +SOC_SINGLE("DSP Bypass Switch", + STA350_CONFD, STA350_CONFD_DSPB_SHIFT, 1, 0), +SOC_SINGLE("Post-scale Link Switch", + STA350_CONFD, STA350_CONFD_PSL_SHIFT, 1, 0), +SOC_SINGLE("Biquad Coefficient Link Switch", + STA350_CONFD, STA350_CONFD_BQL_SHIFT, 1, 0), +SOC_ENUM("Compressor/Limiter Switch", sta350_drc_ac_enum), +SOC_ENUM("Noise Shaper Bandwidth", sta350_noise_shaper_enum), +SOC_SINGLE("Zero-detect Mute Enable Switch", + STA350_CONFD, STA350_CONFD_ZDE_SHIFT, 1, 0), +SOC_SINGLE("Submix Mode Switch", + STA350_CONFD, STA350_CONFD_SME_SHIFT, 1, 0), +/* CONFE */ +SOC_SINGLE("Zero Cross Switch", STA350_CONFE, STA350_CONFE_ZCE_SHIFT, 1, 0), +SOC_SINGLE("Soft Ramp Switch", STA350_CONFE, STA350_CONFE_SVE_SHIFT, 1, 0), +/* MUTE */ +SOC_SINGLE("Master Switch", STA350_MMUTE, STA350_MMUTE_MMUTE_SHIFT, 1, 1), +SOC_SINGLE("Ch1 Switch", STA350_MMUTE, STA350_MMUTE_C1M_SHIFT, 1, 1), +SOC_SINGLE("Ch2 Switch", STA350_MMUTE, STA350_MMUTE_C2M_SHIFT, 1, 1), +SOC_SINGLE("Ch3 Switch", STA350_MMUTE, STA350_MMUTE_C3M_SHIFT, 1, 1), +/* AUTOx */ +SOC_ENUM("Automode GC", sta350_auto_gc_enum), +SOC_ENUM("Automode XO", sta350_auto_xo_enum), +/* CxCFG */ +SOC_SINGLE("Ch1 Tone Control Bypass Switch", + STA350_C1CFG, STA350_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Tone Control Bypass Switch", + STA350_C2CFG, STA350_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch1 EQ Bypass Switch", + STA350_C1CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 EQ Bypass Switch", + STA350_C2CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch1 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch3 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_ENUM("Ch1 Binary Output Select", sta350_binary_output_ch1_enum), +SOC_ENUM("Ch2 Binary Output Select", sta350_binary_output_ch2_enum), +SOC_ENUM("Ch3 Binary Output Select", sta350_binary_output_ch3_enum), +SOC_ENUM("Ch1 Limiter Select", sta350_limiter_ch1_enum), +SOC_ENUM("Ch2 Limiter Select", sta350_limiter_ch2_enum), +SOC_ENUM("Ch3 Limiter Select", sta350_limiter_ch3_enum), +/* TONE */ +SOC_SINGLE_RANGE_TLV("Bass Tone Control Volume", + STA350_TONE, STA350_TONE_BTC_SHIFT, 1, 13, 0, tone_tlv), +SOC_SINGLE_RANGE_TLV("Treble Tone Control Volume", + STA350_TONE, STA350_TONE_TTC_SHIFT, 1, 13, 0, tone_tlv), +SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta350_limiter1_attack_rate_enum), +SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta350_limiter2_attack_rate_enum), +SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta350_limiter1_release_rate_enum), +SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta350_limiter2_release_rate_enum), + +/* + * depending on mode, the attack/release thresholds have + * two different enum definitions; provide both + */ +SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", + STA350_L1ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", + STA350_L2ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", + STA350_L1ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", + STA350_L2ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", + STA350_L1ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", + STA350_L2ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", + STA350_L1ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_drc_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", + STA350_L2ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_drc_release_tlv), + +BIQUAD_COEFS("Ch1 - Biquad 1", 0), +BIQUAD_COEFS("Ch1 - Biquad 2", 5), +BIQUAD_COEFS("Ch1 - Biquad 3", 10), +BIQUAD_COEFS("Ch1 - Biquad 4", 15), +BIQUAD_COEFS("Ch2 - Biquad 1", 20), +BIQUAD_COEFS("Ch2 - Biquad 2", 25), +BIQUAD_COEFS("Ch2 - Biquad 3", 30), +BIQUAD_COEFS("Ch2 - Biquad 4", 35), +BIQUAD_COEFS("High-pass", 40), +BIQUAD_COEFS("Low-pass", 45), +SINGLE_COEF("Ch1 - Prescale", 50), +SINGLE_COEF("Ch2 - Prescale", 51), +SINGLE_COEF("Ch1 - Postscale", 52), +SINGLE_COEF("Ch2 - Postscale", 53), +SINGLE_COEF("Ch3 - Postscale", 54), +SINGLE_COEF("Thermal warning - Postscale", 55), +SINGLE_COEF("Ch1 - Mix 1", 56), +SINGLE_COEF("Ch1 - Mix 2", 57), +SINGLE_COEF("Ch2 - Mix 1", 58), +SINGLE_COEF("Ch2 - Mix 2", 59), +SINGLE_COEF("Ch3 - Mix 1", 60), +SINGLE_COEF("Ch3 - Mix 2", 61), +}; + +static const struct snd_soc_dapm_widget sta350_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LEFT"), +SND_SOC_DAPM_OUTPUT("RIGHT"), +SND_SOC_DAPM_OUTPUT("SUB"), +}; + +static const struct snd_soc_dapm_route sta350_dapm_routes[] = { + { "LEFT", NULL, "DAC" }, + { "RIGHT", NULL, "DAC" }, + { "SUB", NULL, "DAC" }, + { "DAC", NULL, "Playback" }, +}; + +/* MCLK interpolation ratio per fs */ +static struct { + int fs; + int ir; +} interpolation_ratios[] = { + { 32000, 0 }, + { 44100, 0 }, + { 48000, 0 }, + { 88200, 1 }, + { 96000, 1 }, + { 176400, 2 }, + { 192000, 2 }, +}; + +/* MCLK to fs clock ratios */ +static int mcs_ratio_table[3][6] = { + { 768, 512, 384, 256, 128, 576 }, + { 384, 256, 192, 128, 64, 0 }, + { 192, 128, 96, 64, 32, 0 }, +}; + +/** + * sta350_set_dai_sysclk - configure MCLK + * @codec_dai: the codec DAI + * @clk_id: the clock ID (ignored) + * @freq: the MCLK input frequency + * @dir: the clock direction (ignored) + * + * The value of MCLK is used to determine which sample rates are supported + * by the STA350, based on the mcs_ratio_table. + * + * This function must be called by the machine driver's 'startup' function, + * otherwise the list of supported sample rates will not be available in + * time for ALSA. + */ +static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "mclk=%u\n", freq); + sta350->mclk = freq; + + return 0; +} + +/** + * sta350_set_dai_fmt - configure the codec for the selected audio format + * @codec_dai: the codec DAI + * @fmt: a SND_SOC_DAIFMT_x value indicating the data format + * + * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the + * codec accordingly. + */ +static int sta350_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + unsigned int confb = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + sta350->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + confb |= STA350_CONFB_C2IM; + break; + case SND_SOC_DAIFMT_NB_IF: + confb |= STA350_CONFB_C1IM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(sta350->regmap, STA350_CONFB, + STA350_CONFB_C1IM | STA350_CONFB_C2IM, confb); +} + +/** + * sta350_hw_params - program the STA350 with the given hardware parameters. + * @substream: the audio stream + * @params: the hardware parameters to set + * @dai: the SOC DAI (ignored) + * + * This function programs the hardware with the values provided. + * Specifically, the sample rate and the data format. + */ +static int sta350_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + int i, mcs = -EINVAL, ir = -EINVAL; + unsigned int confa, confb; + unsigned int rate, ratio; + int ret; + + if (!sta350->mclk) { + dev_err(codec->dev, + "sta350->mclk is unset. Unable to determine ratio\n"); + return -EIO; + } + + rate = params_rate(params); + ratio = sta350->mclk / rate; + dev_dbg(codec->dev, "rate: %u, ratio: %u\n", rate, ratio); + + for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { + if (interpolation_ratios[i].fs == rate) { + ir = interpolation_ratios[i].ir; + break; + } + } + + if (ir < 0) { + dev_err(codec->dev, "Unsupported samplerate: %u\n", rate); + return -EINVAL; + } + + for (i = 0; i < 6; i++) { + if (mcs_ratio_table[ir][i] == ratio) { + mcs = i; + break; + } + } + + if (mcs < 0) { + dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio); + return -EINVAL; + } + + confa = (ir << STA350_CONFA_IR_SHIFT) | + (mcs << STA350_CONFA_MCS_SHIFT); + confb = 0; + + switch (params_width(params)) { + case 24: + dev_dbg(codec->dev, "24bit\n"); + /* fall through */ + case 32: + dev_dbg(codec->dev, "24bit or 32bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x2; + break; + } + + break; + case 20: + dev_dbg(codec->dev, "20bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x4; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x5; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x6; + break; + } + + break; + case 18: + dev_dbg(codec->dev, "18bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x8; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x9; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xa; + break; + } + + break; + case 16: + dev_dbg(codec->dev, "16bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0xd; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xe; + break; + } + + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(sta350->regmap, STA350_CONFA, + STA350_CONFA_MCS_MASK | STA350_CONFA_IR_MASK, + confa); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sta350->regmap, STA350_CONFB, + STA350_CONFB_SAI_MASK | STA350_CONFB_SAIFB, + confb); + if (ret < 0) + return ret; + + return 0; +} + +static int sta350_startup_sequence(struct sta350_priv *sta350) +{ + if (sta350->gpiod_power_down) + gpiod_set_value(sta350->gpiod_power_down, 1); + + if (sta350->gpiod_nreset) { + gpiod_set_value(sta350->gpiod_nreset, 0); + mdelay(1); + gpiod_set_value(sta350->gpiod_nreset, 1); + mdelay(1); + } + + return 0; +} + +/** + * sta350_set_bias_level - DAPM callback + * @codec: the codec device + * @level: DAPM power level + * + * This is called by ALSA to put the codec into low power mode + * or to wake it up. If the codec is powered off completely + * all registers must be restored after power on. + */ +static int sta350_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + int ret; + + dev_dbg(codec->dev, "level = %d\n", level); + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* Full power on */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, + STA350_CONFF_PWDN | STA350_CONFF_EAPD); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable( + ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(codec->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + sta350_startup_sequence(sta350); + sta350_cache_sync(codec); + } + + /* Power down */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, + 0); + + break; + + case SND_SOC_BIAS_OFF: + /* The chip runs through the power down sequence for us */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, 0); + + /* power down: low */ + if (sta350->gpiod_power_down) + gpiod_set_value(sta350->gpiod_power_down, 0); + + if (sta350->gpiod_nreset) + gpiod_set_value(sta350->gpiod_nreset, 0); + + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), + sta350->supplies); + break; + } + codec->dapm.bias_level = level; + return 0; +} + +static const struct snd_soc_dai_ops sta350_dai_ops = { + .hw_params = sta350_hw_params, + .set_sysclk = sta350_set_dai_sysclk, + .set_fmt = sta350_set_dai_fmt, +}; + +static struct snd_soc_dai_driver sta350_dai = { + .name = "sta350-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = STA350_RATES, + .formats = STA350_FORMATS, + }, + .ops = &sta350_dai_ops, +}; + +#ifdef CONFIG_PM +static int sta350_suspend(struct snd_soc_codec *codec) +{ + sta350_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int sta350_resume(struct snd_soc_codec *codec) +{ + sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} +#else +#define sta350_suspend NULL +#define sta350_resume NULL +#endif + +static int sta350_probe(struct snd_soc_codec *codec) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + struct sta350_platform_data *pdata = sta350->pdata; + int i, ret = 0, thermal = 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = sta350_startup_sequence(sta350); + if (ret < 0) { + dev_err(codec->dev, "Failed to startup device\n"); + return ret; + } + + /* CONFA */ + if (!pdata->thermal_warning_recovery) + thermal |= STA350_CONFA_TWAB; + if (!pdata->thermal_warning_adjustment) + thermal |= STA350_CONFA_TWRB; + if (!pdata->fault_detect_recovery) + thermal |= STA350_CONFA_FDRB; + regmap_update_bits(sta350->regmap, STA350_CONFA, + STA350_CONFA_TWAB | STA350_CONFA_TWRB | + STA350_CONFA_FDRB, + thermal); + + /* CONFC */ + regmap_update_bits(sta350->regmap, STA350_CONFC, + STA350_CONFC_OM_MASK, + pdata->ffx_power_output_mode + << STA350_CONFC_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_CONFC, + STA350_CONFC_CSZ_MASK, + pdata->drop_compensation_ns + << STA350_CONFC_CSZ_SHIFT); + regmap_update_bits(sta350->regmap, + STA350_CONFC, + STA350_CONFC_OCRB, + pdata->oc_warning_adjustment ? + STA350_CONFC_OCRB : 0); + + /* CONFE */ + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_MPCV, + pdata->max_power_use_mpcc ? + STA350_CONFE_MPCV : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_MPC, + pdata->max_power_correction ? + STA350_CONFE_MPC : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_AME, + pdata->am_reduction_mode ? + STA350_CONFE_AME : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_PWMS, + pdata->odd_pwm_speed_mode ? + STA350_CONFE_PWMS : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_DCCV, + pdata->distortion_compensation ? + STA350_CONFE_DCCV : 0); + /* CONFF */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_IDE, + pdata->invalid_input_detect_mute ? + STA350_CONFF_IDE : 0); + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_OCFG_MASK, + pdata->output_conf + << STA350_CONFF_OCFG_SHIFT); + + /* channel to output mapping */ + regmap_update_bits(sta350->regmap, STA350_C1CFG, + STA350_CxCFG_OM_MASK, + pdata->ch1_output_mapping + << STA350_CxCFG_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_C2CFG, + STA350_CxCFG_OM_MASK, + pdata->ch2_output_mapping + << STA350_CxCFG_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_C3CFG, + STA350_CxCFG_OM_MASK, + pdata->ch3_output_mapping + << STA350_CxCFG_OM_SHIFT); + + /* initialize coefficient shadow RAM with reset values */ + for (i = 4; i <= 49; i += 5) + sta350->coef_shadow[i] = 0x400000; + for (i = 50; i <= 54; i++) + sta350->coef_shadow[i] = 0x7fffff; + sta350->coef_shadow[55] = 0x5a9df7; + sta350->coef_shadow[56] = 0x7fffff; + sta350->coef_shadow[59] = 0x7fffff; + sta350->coef_shadow[60] = 0x400000; + sta350->coef_shadow[61] = 0x400000; + + sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); + + return 0; +} + +static int sta350_remove(struct snd_soc_codec *codec) +{ + struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec); + + sta350_set_bias_level(codec, SND_SOC_BIAS_OFF); + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); + + return 0; +} + +static const struct snd_soc_codec_driver sta350_codec = { + .probe = sta350_probe, + .remove = sta350_remove, + .suspend = sta350_suspend, + .resume = sta350_resume, + .set_bias_level = sta350_set_bias_level, + .controls = sta350_snd_controls, + .num_controls = ARRAY_SIZE(sta350_snd_controls), + .dapm_widgets = sta350_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets), + .dapm_routes = sta350_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes), +}; + +static const struct regmap_config sta350_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = STA350_MISC2, + .reg_defaults = sta350_regs, + .num_reg_defaults = ARRAY_SIZE(sta350_regs), + .cache_type = REGCACHE_RBTREE, + .wr_table = &sta350_write_regs, + .rd_table = &sta350_read_regs, + .volatile_table = &sta350_volatile_regs, +}; + +#ifdef CONFIG_OF +static const struct of_device_id st350_dt_ids[] = { + { .compatible = "st,sta350", }, + { } +}; +MODULE_DEVICE_TABLE(of, st350_dt_ids); + +static const char * const sta350_ffx_modes[] = { + [STA350_FFX_PM_DROP_COMP] = "drop-compensation", + [STA350_FFX_PM_TAPERED_COMP] = "tapered-compensation", + [STA350_FFX_PM_FULL_POWER] = "full-power-mode", + [STA350_FFX_PM_VARIABLE_DROP_COMP] = "variable-drop-compensation", +}; + +static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350) +{ + struct device_node *np = dev->of_node; + struct sta350_platform_data *pdata; + const char *ffx_power_mode; + u16 tmp; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + of_property_read_u8(np, "st,output-conf", + &pdata->output_conf); + of_property_read_u8(np, "st,ch1-output-mapping", + &pdata->ch1_output_mapping); + of_property_read_u8(np, "st,ch2-output-mapping", + &pdata->ch2_output_mapping); + of_property_read_u8(np, "st,ch3-output-mapping", + &pdata->ch3_output_mapping); + + if (of_get_property(np, "st,thermal-warning-recovery", NULL)) + pdata->thermal_warning_recovery = 1; + if (of_get_property(np, "st,thermal-warning-adjustment", NULL)) + pdata->thermal_warning_adjustment = 1; + if (of_get_property(np, "st,fault-detect-recovery", NULL)) + pdata->fault_detect_recovery = 1; + + pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP; + if (!of_property_read_string(np, "st,ffx-power-output-mode", + &ffx_power_mode)) { + int i, mode = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sta350_ffx_modes); i++) + if (!strcasecmp(ffx_power_mode, sta350_ffx_modes[i])) + mode = i; + + if (mode < 0) + dev_warn(dev, "Unsupported ffx output mode: %s\n", + ffx_power_mode); + else + pdata->ffx_power_output_mode = mode; + } + + tmp = 140; + of_property_read_u16(np, "st,drop-compensation-ns", &tmp); + pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20; + + if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL)) + pdata->oc_warning_adjustment = 1; + + /* CONFE */ + if (of_get_property(np, "st,max-power-use-mpcc", NULL)) + pdata->max_power_use_mpcc = 1; + + if (of_get_property(np, "st,max-power-correction", NULL)) + pdata->max_power_correction = 1; + + if (of_get_property(np, "st,am-reduction-mode", NULL)) + pdata->am_reduction_mode = 1; + + if (of_get_property(np, "st,odd-pwm-speed-mode", NULL)) + pdata->odd_pwm_speed_mode = 1; + + if (of_get_property(np, "st,distortion-compensation", NULL)) + pdata->distortion_compensation = 1; + + /* CONFF */ + if (of_get_property(np, "st,invalid-input-detect-mute", NULL)) + pdata->invalid_input_detect_mute = 1; + + sta350->pdata = pdata; + + return 0; +} +#endif + +static int sta350_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct sta350_priv *sta350; + int ret, i; + + sta350 = devm_kzalloc(dev, sizeof(struct sta350_priv), GFP_KERNEL); + if (!sta350) + return -ENOMEM; + + mutex_init(&sta350->coeff_lock); + sta350->pdata = dev_get_platdata(dev); + +#ifdef CONFIG_OF + if (dev->of_node) { + ret = sta350_probe_dt(dev, sta350); + if (ret < 0) + return ret; + } +#endif + + /* GPIOs */ + sta350->gpiod_nreset = devm_gpiod_get(dev, "reset"); + if (IS_ERR(sta350->gpiod_nreset)) { + ret = PTR_ERR(sta350->gpiod_nreset); + if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + sta350->gpiod_nreset = NULL; + } else { + gpiod_direction_output(sta350->gpiod_nreset, 0); + } + + sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down"); + if (IS_ERR(sta350->gpiod_power_down)) { + ret = PTR_ERR(sta350->gpiod_power_down); + if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + sta350->gpiod_power_down = NULL; + } else { + gpiod_direction_output(sta350->gpiod_power_down, 0); + } + + /* regulators */ + for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++) + sta350->supplies[i].supply = sta350_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + sta350->regmap = devm_regmap_init_i2c(i2c, &sta350_regmap); + if (IS_ERR(sta350->regmap)) { + ret = PTR_ERR(sta350->regmap); + dev_err(dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, sta350); + + ret = snd_soc_register_codec(dev, &sta350_codec, &sta350_dai, 1); + if (ret < 0) + dev_err(dev, "Failed to register codec (%d)\n", ret); + + return ret; +} + +static int sta350_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id sta350_i2c_id[] = { + { "sta350", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sta350_i2c_id); + +static struct i2c_driver sta350_i2c_driver = { + .driver = { + .name = "sta350", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(st350_dt_ids), + }, + .probe = sta350_i2c_probe, + .remove = sta350_i2c_remove, + .id_table = sta350_i2c_id, +}; + +module_i2c_driver(sta350_i2c_driver); + +MODULE_DESCRIPTION("ASoC STA350 driver"); +MODULE_AUTHOR("Sven Brandau "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h new file mode 100644 index 000000000000..c3248f0fad2c --- /dev/null +++ b/sound/soc/codecs/sta350.h @@ -0,0 +1,228 @@ +/* + * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system + * + * Copyright: 2011 Raumfeld GmbH + * Author: Sven Brandau + * + * based on code from: + * Raumfeld GmbH + * Johannes Stezenbach + * Wolfson Microelectronics PLC. + * Mark Brown + * + * 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 _ASOC_STA_350_H +#define _ASOC_STA_350_H + +/* STA50 register addresses */ + +#define STA350_REGISTER_COUNT 0x4D +#define STA350_COEF_COUNT 62 + +#define STA350_CONFA 0x00 +#define STA350_CONFB 0x01 +#define STA350_CONFC 0x02 +#define STA350_CONFD 0x03 +#define STA350_CONFE 0x04 +#define STA350_CONFF 0x05 +#define STA350_MMUTE 0x06 +#define STA350_MVOL 0x07 +#define STA350_C1VOL 0x08 +#define STA350_C2VOL 0x09 +#define STA350_C3VOL 0x0a +#define STA350_AUTO1 0x0b +#define STA350_AUTO2 0x0c +#define STA350_AUTO3 0x0d +#define STA350_C1CFG 0x0e +#define STA350_C2CFG 0x0f +#define STA350_C3CFG 0x10 +#define STA350_TONE 0x11 +#define STA350_L1AR 0x12 +#define STA350_L1ATRT 0x13 +#define STA350_L2AR 0x14 +#define STA350_L2ATRT 0x15 +#define STA350_CFADDR2 0x16 +#define STA350_B1CF1 0x17 +#define STA350_B1CF2 0x18 +#define STA350_B1CF3 0x19 +#define STA350_B2CF1 0x1a +#define STA350_B2CF2 0x1b +#define STA350_B2CF3 0x1c +#define STA350_A1CF1 0x1d +#define STA350_A1CF2 0x1e +#define STA350_A1CF3 0x1f +#define STA350_A2CF1 0x20 +#define STA350_A2CF2 0x21 +#define STA350_A2CF3 0x22 +#define STA350_B0CF1 0x23 +#define STA350_B0CF2 0x24 +#define STA350_B0CF3 0x25 +#define STA350_CFUD 0x26 +#define STA350_MPCC1 0x27 +#define STA350_MPCC2 0x28 +#define STA350_DCC1 0x29 +#define STA350_DCC2 0x2a +#define STA350_FDRC1 0x2b +#define STA350_FDRC2 0x2c +#define STA350_STATUS 0x2d +/* reserved: 0x2d - 0x30 */ +#define STA350_EQCFG 0x31 +#define STA350_EATH1 0x32 +#define STA350_ERTH1 0x33 +#define STA350_EATH2 0x34 +#define STA350_ERTH2 0x35 +#define STA350_CONFX 0x36 +#define STA350_SVCA 0x37 +#define STA350_SVCB 0x38 +#define STA350_RMS0A 0x39 +#define STA350_RMS0B 0x3a +#define STA350_RMS0C 0x3b +#define STA350_RMS1A 0x3c +#define STA350_RMS1B 0x3d +#define STA350_RMS1C 0x3e +#define STA350_EVOLRES 0x3f +/* reserved: 0x40 - 0x47 */ +#define STA350_NSHAPE 0x48 +#define STA350_CTXB4B1 0x49 +#define STA350_CTXB7B5 0x4a +#define STA350_MISC1 0x4b +#define STA350_MISC2 0x4c + +/* 0x00 CONFA */ +#define STA350_CONFA_MCS_MASK 0x03 +#define STA350_CONFA_MCS_SHIFT 0 +#define STA350_CONFA_IR_MASK 0x18 +#define STA350_CONFA_IR_SHIFT 3 +#define STA350_CONFA_TWRB BIT(5) +#define STA350_CONFA_TWAB BIT(6) +#define STA350_CONFA_FDRB BIT(7) + +/* 0x01 CONFB */ +#define STA350_CONFB_SAI_MASK 0x0f +#define STA350_CONFB_SAI_SHIFT 0 +#define STA350_CONFB_SAIFB BIT(4) +#define STA350_CONFB_DSCKE BIT(5) +#define STA350_CONFB_C1IM BIT(6) +#define STA350_CONFB_C2IM BIT(7) + +/* 0x02 CONFC */ +#define STA350_CONFC_OM_MASK 0x03 +#define STA350_CONFC_OM_SHIFT 0 +#define STA350_CONFC_CSZ_MASK 0x3c +#define STA350_CONFC_CSZ_SHIFT 2 +#define STA350_CONFC_OCRB BIT(7) + +/* 0x03 CONFD */ +#define STA350_CONFD_HPB_SHIFT 0 +#define STA350_CONFD_DEMP_SHIFT 1 +#define STA350_CONFD_DSPB_SHIFT 2 +#define STA350_CONFD_PSL_SHIFT 3 +#define STA350_CONFD_BQL_SHIFT 4 +#define STA350_CONFD_DRC_SHIFT 5 +#define STA350_CONFD_ZDE_SHIFT 6 +#define STA350_CONFD_SME_SHIFT 7 + +/* 0x04 CONFE */ +#define STA350_CONFE_MPCV BIT(0) +#define STA350_CONFE_MPCV_SHIFT 0 +#define STA350_CONFE_MPC BIT(1) +#define STA350_CONFE_MPC_SHIFT 1 +#define STA350_CONFE_NSBW BIT(2) +#define STA350_CONFE_NSBW_SHIFT 2 +#define STA350_CONFE_AME BIT(3) +#define STA350_CONFE_AME_SHIFT 3 +#define STA350_CONFE_PWMS BIT(4) +#define STA350_CONFE_PWMS_SHIFT 4 +#define STA350_CONFE_DCCV BIT(5) +#define STA350_CONFE_DCCV_SHIFT 5 +#define STA350_CONFE_ZCE BIT(6) +#define STA350_CONFE_ZCE_SHIFT 6 +#define STA350_CONFE_SVE BIT(7) +#define STA350_CONFE_SVE_SHIFT 7 + +/* 0x05 CONFF */ +#define STA350_CONFF_OCFG_MASK 0x03 +#define STA350_CONFF_OCFG_SHIFT 0 +#define STA350_CONFF_IDE BIT(2) +#define STA350_CONFF_BCLE BIT(3) +#define STA350_CONFF_LDTE BIT(4) +#define STA350_CONFF_ECLE BIT(5) +#define STA350_CONFF_PWDN BIT(6) +#define STA350_CONFF_EAPD BIT(7) + +/* 0x06 MMUTE */ +#define STA350_MMUTE_MMUTE 0x01 +#define STA350_MMUTE_MMUTE_SHIFT 0 +#define STA350_MMUTE_C1M 0x02 +#define STA350_MMUTE_C1M_SHIFT 1 +#define STA350_MMUTE_C2M 0x04 +#define STA350_MMUTE_C2M_SHIFT 2 +#define STA350_MMUTE_C3M 0x08 +#define STA350_MMUTE_C3M_SHIFT 3 +#define STA350_MMUTE_LOC_MASK 0xC0 +#define STA350_MMUTE_LOC_SHIFT 6 + +/* 0x0b AUTO1 */ +#define STA350_AUTO1_AMGC_MASK 0x30 +#define STA350_AUTO1_AMGC_SHIFT 4 + +/* 0x0c AUTO2 */ +#define STA350_AUTO2_AMAME 0x01 +#define STA350_AUTO2_AMAM_MASK 0x0e +#define STA350_AUTO2_AMAM_SHIFT 1 +#define STA350_AUTO2_XO_MASK 0xf0 +#define STA350_AUTO2_XO_SHIFT 4 + +/* 0x0d AUTO3 */ +#define STA350_AUTO3_PEQ_MASK 0x1f +#define STA350_AUTO3_PEQ_SHIFT 0 + +/* 0x0e 0x0f 0x10 CxCFG */ +#define STA350_CxCFG_TCB_SHIFT 0 +#define STA350_CxCFG_EQBP_SHIFT 1 +#define STA350_CxCFG_VBP_SHIFT 2 +#define STA350_CxCFG_BO_SHIFT 3 +#define STA350_CxCFG_LS_SHIFT 4 +#define STA350_CxCFG_OM_MASK 0xc0 +#define STA350_CxCFG_OM_SHIFT 6 + +/* 0x11 TONE */ +#define STA350_TONE_BTC_SHIFT 0 +#define STA350_TONE_TTC_SHIFT 4 + +/* 0x12 0x13 0x14 0x15 limiter attack/release */ +#define STA350_LxA_SHIFT 0 +#define STA350_LxR_SHIFT 4 + +/* 0x26 CFUD */ +#define STA350_CFUD_W1 0x01 +#define STA350_CFUD_WA 0x02 +#define STA350_CFUD_R1 0x04 +#define STA350_CFUD_RA 0x08 + + +/* biquad filter coefficient table offsets */ +#define STA350_C1_BQ_BASE 0 +#define STA350_C2_BQ_BASE 20 +#define STA350_CH_BQ_NUM 4 +#define STA350_BQ_NUM_COEF 5 +#define STA350_XO_HP_BQ_BASE 40 +#define STA350_XO_LP_BQ_BASE 45 +#define STA350_C1_PRESCALE 50 +#define STA350_C2_PRESCALE 51 +#define STA350_C1_POSTSCALE 52 +#define STA350_C2_POSTSCALE 53 +#define STA350_C3_POSTSCALE 54 +#define STA350_TW_POSTSCALE 55 +#define STA350_C1_MIX1 56 +#define STA350_C1_MIX2 57 +#define STA350_C2_MIX1 58 +#define STA350_C2_MIX2 59 +#define STA350_C3_MIX1 60 +#define STA350_C3_MIX2 61 + +#endif /* _ASOC_STA_350_H */ -- cgit v1.2.3 From 766e3721990d2c78e0d614b57753f105adbaa8c5 Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Fri, 4 Apr 2014 16:27:17 +0800 Subject: spi: convert spi-bfin-v3.c to a multiplatform driver Spi v3 controller is not only used on Blackfin. So rename it and use ioread/iowrite api to make it work on other platform. Signed-off-by: Scott Jiang Signed-off-by: Mark Brown --- arch/blackfin/include/asm/bfin_spi3.h | 258 --------- arch/blackfin/mach-bf609/boards/ezkit.c | 22 +- drivers/spi/Kconfig | 4 +- drivers/spi/Makefile | 2 +- drivers/spi/spi-adi-v3.c | 985 ++++++++++++++++++++++++++++++++ drivers/spi/spi-bfin-v3.c | 965 ------------------------------- include/linux/spi/adi_spi3.h | 254 ++++++++ 7 files changed, 1253 insertions(+), 1237 deletions(-) delete mode 100644 arch/blackfin/include/asm/bfin_spi3.h create mode 100644 drivers/spi/spi-adi-v3.c delete mode 100644 drivers/spi/spi-bfin-v3.c create mode 100644 include/linux/spi/adi_spi3.h (limited to 'include') diff --git a/arch/blackfin/include/asm/bfin_spi3.h b/arch/blackfin/include/asm/bfin_spi3.h deleted file mode 100644 index 0957e65a54be..000000000000 --- a/arch/blackfin/include/asm/bfin_spi3.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Analog Devices SPI3 controller driver - * - * Copyright (c) 2011 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _SPI_CHANNEL_H_ -#define _SPI_CHANNEL_H_ - -#include - -/* SPI_CONTROL */ -#define SPI_CTL_EN 0x00000001 /* Enable */ -#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */ -#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */ -#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */ -#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */ -#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */ -#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */ -#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in-between transfers */ -#define SPI_CTL_EMISO 0x00000100 /* Enable MISO */ -#define SPI_CTL_SIZE 0x00000600 /* Word Transfer Size */ -#define SPI_CTL_SIZE08 0x00000000 /* SIZE: 8 bits */ -#define SPI_CTL_SIZE16 0x00000200 /* SIZE: 16 bits */ -#define SPI_CTL_SIZE32 0x00000400 /* SIZE: 32 bits */ -#define SPI_CTL_LSBF 0x00001000 /* LSB First */ -#define SPI_CTL_FCEN 0x00002000 /* Flow-Control Enable */ -#define SPI_CTL_FCCH 0x00004000 /* Flow-Control Channel Selection */ -#define SPI_CTL_FCPL 0x00008000 /* Flow-Control Polarity */ -#define SPI_CTL_FCWM 0x00030000 /* Flow-Control Water-Mark */ -#define SPI_CTL_FIFO0 0x00000000 /* FCWM: TFIFO empty or RFIFO Full */ -#define SPI_CTL_FIFO1 0x00010000 /* FCWM: TFIFO 75% or more empty or RFIFO 75% or more full */ -#define SPI_CTL_FIFO2 0x00020000 /* FCWM: TFIFO 50% or more empty or RFIFO 50% or more full */ -#define SPI_CTL_FMODE 0x00040000 /* Fast-mode Enable */ -#define SPI_CTL_MIOM 0x00300000 /* Multiple I/O Mode */ -#define SPI_CTL_MIO_DIS 0x00000000 /* MIOM: Disable */ -#define SPI_CTL_MIO_DUAL 0x00100000 /* MIOM: Enable DIOM (Dual I/O Mode) */ -#define SPI_CTL_MIO_QUAD 0x00200000 /* MIOM: Enable QUAD (Quad SPI Mode) */ -#define SPI_CTL_SOSI 0x00400000 /* Start on MOSI */ -/* SPI_RX_CONTROL */ -#define SPI_RXCTL_REN 0x00000001 /* Receive Channel Enable */ -#define SPI_RXCTL_RTI 0x00000004 /* Receive Transfer Initiate */ -#define SPI_RXCTL_RWCEN 0x00000008 /* Receive Word Counter Enable */ -#define SPI_RXCTL_RDR 0x00000070 /* Receive Data Request */ -#define SPI_RXCTL_RDR_DIS 0x00000000 /* RDR: Disabled */ -#define SPI_RXCTL_RDR_NE 0x00000010 /* RDR: RFIFO not empty */ -#define SPI_RXCTL_RDR_25 0x00000020 /* RDR: RFIFO 25% full */ -#define SPI_RXCTL_RDR_50 0x00000030 /* RDR: RFIFO 50% full */ -#define SPI_RXCTL_RDR_75 0x00000040 /* RDR: RFIFO 75% full */ -#define SPI_RXCTL_RDR_FULL 0x00000050 /* RDR: RFIFO full */ -#define SPI_RXCTL_RDO 0x00000100 /* Receive Data Over-Run */ -#define SPI_RXCTL_RRWM 0x00003000 /* FIFO Regular Water-Mark */ -#define SPI_RXCTL_RWM_0 0x00000000 /* RRWM: RFIFO Empty */ -#define SPI_RXCTL_RWM_25 0x00001000 /* RRWM: RFIFO 25% full */ -#define SPI_RXCTL_RWM_50 0x00002000 /* RRWM: RFIFO 50% full */ -#define SPI_RXCTL_RWM_75 0x00003000 /* RRWM: RFIFO 75% full */ -#define SPI_RXCTL_RUWM 0x00070000 /* FIFO Urgent Water-Mark */ -#define SPI_RXCTL_UWM_DIS 0x00000000 /* RUWM: Disabled */ -#define SPI_RXCTL_UWM_25 0x00010000 /* RUWM: RFIFO 25% full */ -#define SPI_RXCTL_UWM_50 0x00020000 /* RUWM: RFIFO 50% full */ -#define SPI_RXCTL_UWM_75 0x00030000 /* RUWM: RFIFO 75% full */ -#define SPI_RXCTL_UWM_FULL 0x00040000 /* RUWM: RFIFO full */ -/* SPI_TX_CONTROL */ -#define SPI_TXCTL_TEN 0x00000001 /* Transmit Channel Enable */ -#define SPI_TXCTL_TTI 0x00000004 /* Transmit Transfer Initiate */ -#define SPI_TXCTL_TWCEN 0x00000008 /* Transmit Word Counter Enable */ -#define SPI_TXCTL_TDR 0x00000070 /* Transmit Data Request */ -#define SPI_TXCTL_TDR_DIS 0x00000000 /* TDR: Disabled */ -#define SPI_TXCTL_TDR_NF 0x00000010 /* TDR: TFIFO not full */ -#define SPI_TXCTL_TDR_25 0x00000020 /* TDR: TFIFO 25% empty */ -#define SPI_TXCTL_TDR_50 0x00000030 /* TDR: TFIFO 50% empty */ -#define SPI_TXCTL_TDR_75 0x00000040 /* TDR: TFIFO 75% empty */ -#define SPI_TXCTL_TDR_EMPTY 0x00000050 /* TDR: TFIFO empty */ -#define SPI_TXCTL_TDU 0x00000100 /* Transmit Data Under-Run */ -#define SPI_TXCTL_TRWM 0x00003000 /* FIFO Regular Water-Mark */ -#define SPI_TXCTL_RWM_FULL 0x00000000 /* TRWM: TFIFO full */ -#define SPI_TXCTL_RWM_25 0x00001000 /* TRWM: TFIFO 25% empty */ -#define SPI_TXCTL_RWM_50 0x00002000 /* TRWM: TFIFO 50% empty */ -#define SPI_TXCTL_RWM_75 0x00003000 /* TRWM: TFIFO 75% empty */ -#define SPI_TXCTL_TUWM 0x00070000 /* FIFO Urgent Water-Mark */ -#define SPI_TXCTL_UWM_DIS 0x00000000 /* TUWM: Disabled */ -#define SPI_TXCTL_UWM_25 0x00010000 /* TUWM: TFIFO 25% empty */ -#define SPI_TXCTL_UWM_50 0x00020000 /* TUWM: TFIFO 50% empty */ -#define SPI_TXCTL_UWM_75 0x00030000 /* TUWM: TFIFO 75% empty */ -#define SPI_TXCTL_UWM_EMPTY 0x00040000 /* TUWM: TFIFO empty */ -/* SPI_CLOCK */ -#define SPI_CLK_BAUD 0x0000FFFF /* Baud Rate */ -/* SPI_DELAY */ -#define SPI_DLY_STOP 0x000000FF /* Transfer delay time in multiples of SCK period */ -#define SPI_DLY_LEADX 0x00000100 /* Extended (1 SCK) LEAD Control */ -#define SPI_DLY_LAGX 0x00000200 /* Extended (1 SCK) LAG control */ -/* SPI_SSEL */ -#define SPI_SLVSEL_SSE1 0x00000002 /* SPISSEL1 Enable */ -#define SPI_SLVSEL_SSE2 0x00000004 /* SPISSEL2 Enable */ -#define SPI_SLVSEL_SSE3 0x00000008 /* SPISSEL3 Enable */ -#define SPI_SLVSEL_SSE4 0x00000010 /* SPISSEL4 Enable */ -#define SPI_SLVSEL_SSE5 0x00000020 /* SPISSEL5 Enable */ -#define SPI_SLVSEL_SSE6 0x00000040 /* SPISSEL6 Enable */ -#define SPI_SLVSEL_SSE7 0x00000080 /* SPISSEL7 Enable */ -#define SPI_SLVSEL_SSEL1 0x00000200 /* SPISSEL1 Value */ -#define SPI_SLVSEL_SSEL2 0x00000400 /* SPISSEL2 Value */ -#define SPI_SLVSEL_SSEL3 0x00000800 /* SPISSEL3 Value */ -#define SPI_SLVSEL_SSEL4 0x00001000 /* SPISSEL4 Value */ -#define SPI_SLVSEL_SSEL5 0x00002000 /* SPISSEL5 Value */ -#define SPI_SLVSEL_SSEL6 0x00004000 /* SPISSEL6 Value */ -#define SPI_SLVSEL_SSEL7 0x00008000 /* SPISSEL7 Value */ -/* SPI_RWC */ -#define SPI_RWC_VALUE 0x0000FFFF /* Received Word-Count */ -/* SPI_RWCR */ -#define SPI_RWCR_VALUE 0x0000FFFF /* Received Word-Count Reload */ -/* SPI_TWC */ -#define SPI_TWC_VALUE 0x0000FFFF /* Transmitted Word-Count */ -/* SPI_TWCR */ -#define SPI_TWCR_VALUE 0x0000FFFF /* Transmitted Word-Count Reload */ -/* SPI_IMASK */ -#define SPI_IMSK_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_IMASKCL */ -#define SPI_IMSK_CLR_RUW 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_CLR_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_CLR_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_CLR_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_CLR_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_CLR_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_CLR_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_CLR_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_CLR_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_CLR_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_IMASKST */ -#define SPI_IMSK_SET_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_SET_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ -#define SPI_IMSK_SET_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ -#define SPI_IMSK_SET_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ -#define SPI_IMSK_SET_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ -#define SPI_IMSK_SET_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ -#define SPI_IMSK_SET_RSM 0x00000100 /* Receive Start Interrupt Mask */ -#define SPI_IMSK_SET_TSM 0x00000200 /* Transmit Start Interrupt Mask */ -#define SPI_IMSK_SET_RFM 0x00000400 /* Receive Finish Interrupt Mask */ -#define SPI_IMSK_SET_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ -/* SPI_STATUS */ -#define SPI_STAT_SPIF 0x00000001 /* SPI Finished */ -#define SPI_STAT_RUWM 0x00000002 /* Receive Urgent Water-Mark Breached */ -#define SPI_STAT_TUWM 0x00000004 /* Transmit Urgent Water-Mark Breached */ -#define SPI_STAT_ROE 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_STAT_TUE 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_STAT_TCE 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_STAT_MODF 0x00000080 /* Mode Fault Error Indication */ -#define SPI_STAT_RS 0x00000100 /* Receive Start Indication */ -#define SPI_STAT_TS 0x00000200 /* Transmit Start Indication */ -#define SPI_STAT_RF 0x00000400 /* Receive Finish Indication */ -#define SPI_STAT_TF 0x00000800 /* Transmit Finish Indication */ -#define SPI_STAT_RFS 0x00007000 /* SPI_RFIFO status */ -#define SPI_STAT_RFIFO_EMPTY 0x00000000 /* RFS: RFIFO Empty */ -#define SPI_STAT_RFIFO_25 0x00001000 /* RFS: RFIFO 25% Full */ -#define SPI_STAT_RFIFO_50 0x00002000 /* RFS: RFIFO 50% Full */ -#define SPI_STAT_RFIFO_75 0x00003000 /* RFS: RFIFO 75% Full */ -#define SPI_STAT_RFIFO_FULL 0x00004000 /* RFS: RFIFO Full */ -#define SPI_STAT_TFS 0x00070000 /* SPI_TFIFO status */ -#define SPI_STAT_TFIFO_FULL 0x00000000 /* TFS: TFIFO full */ -#define SPI_STAT_TFIFO_25 0x00010000 /* TFS: TFIFO 25% empty */ -#define SPI_STAT_TFIFO_50 0x00020000 /* TFS: TFIFO 50% empty */ -#define SPI_STAT_TFIFO_75 0x00030000 /* TFS: TFIFO 75% empty */ -#define SPI_STAT_TFIFO_EMPTY 0x00040000 /* TFS: TFIFO empty */ -#define SPI_STAT_FCS 0x00100000 /* Flow-Control Stall Indication */ -#define SPI_STAT_RFE 0x00400000 /* SPI_RFIFO Empty */ -#define SPI_STAT_TFF 0x00800000 /* SPI_TFIFO Full */ -/* SPI_ILAT */ -#define SPI_ILAT_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ -#define SPI_ILAT_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ -#define SPI_ILAT_ROI 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_ILAT_TUI 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_ILAT_TCI 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_ILAT_MFI 0x00000080 /* Mode Fault Error Indication */ -#define SPI_ILAT_RSI 0x00000100 /* Receive Start Indication */ -#define SPI_ILAT_TSI 0x00000200 /* Transmit Start Indication */ -#define SPI_ILAT_RFI 0x00000400 /* Receive Finish Indication */ -#define SPI_ILAT_TFI 0x00000800 /* Transmit Finish Indication */ -/* SPI_ILATCL */ -#define SPI_ILAT_CLR_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ -#define SPI_ILAT_CLR_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ -#define SPI_ILAT_CLR_ROI 0x00000010 /* Receive Over-Run Error Indication */ -#define SPI_ILAT_CLR_TUI 0x00000020 /* Transmit Under-Run Error Indication */ -#define SPI_ILAT_CLR_TCI 0x00000040 /* Transmit Collision Error Indication */ -#define SPI_ILAT_CLR_MFI 0x00000080 /* Mode Fault Error Indication */ -#define SPI_ILAT_CLR_RSI 0x00000100 /* Receive Start Indication */ -#define SPI_ILAT_CLR_TSI 0x00000200 /* Transmit Start Indication */ -#define SPI_ILAT_CLR_RFI 0x00000400 /* Receive Finish Indication */ -#define SPI_ILAT_CLR_TFI 0x00000800 /* Transmit Finish Indication */ - -/* - * bfin spi3 registers layout - */ -struct bfin_spi_regs { - u32 revid; - u32 control; - u32 rx_control; - u32 tx_control; - u32 clock; - u32 delay; - u32 ssel; - u32 rwc; - u32 rwcr; - u32 twc; - u32 twcr; - u32 reserved0; - u32 emask; - u32 emaskcl; - u32 emaskst; - u32 reserved1; - u32 status; - u32 elat; - u32 elatcl; - u32 reserved2; - u32 rfifo; - u32 reserved3; - u32 tfifo; -}; - -#define MAX_CTRL_CS 8 /* cs in spi controller */ - -/* device.platform_data for SSP controller devices */ -struct bfin_spi3_master { - u16 num_chipselect; - u16 pin_req[7]; -}; - -/* spi_board_info.controller_data for SPI slave devices, - * copied to spi_device.platform_data ... mostly for dma tuning - */ -struct bfin_spi3_chip { - u32 control; - u16 cs_chg_udelay; /* Some devices require 16-bit delays */ - u32 tx_dummy_val; /* tx value for rx only transfer */ - bool enable_dma; -}; - -#endif /* _SPI_CHANNEL_H_ */ diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c index 943f7e95ec15..1ba4600de69f 100644 --- a/arch/blackfin/mach-bf609/boards/ezkit.c +++ b/arch/blackfin/mach-bf609/boards/ezkit.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -767,13 +767,13 @@ static struct flash_platform_data bfin_spi_flash_data = { .type = "w25q32", }; -static struct bfin_spi3_chip spi_flash_chip_info = { +static struct adi_spi3_chip spi_flash_chip_info = { .enable_dma = true, /* use dma transfer with this chip*/ }; #endif #if IS_ENABLED(CONFIG_SPI_SPIDEV) -static struct bfin_spi3_chip spidev_chip_info = { +static struct adi_spi3_chip spidev_chip_info = { .enable_dma = true, }; #endif @@ -1736,7 +1736,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { }, #endif }; -#if IS_ENABLED(CONFIG_SPI_BFIN_V3) +#if IS_ENABLED(CONFIG_SPI_ADI_V3) /* SPI (0) */ static struct resource bfin_spi0_resource[] = { { @@ -1777,13 +1777,13 @@ static struct resource bfin_spi1_resource[] = { }; /* SPI controller data */ -static struct bfin_spi3_master bf60x_spi_master_info0 = { +static struct adi_spi3_master bf60x_spi_master_info0 = { .num_chipselect = MAX_CTRL_CS + MAX_BLACKFIN_GPIOS, .pin_req = {P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0}, }; static struct platform_device bf60x_spi_master0 = { - .name = "bfin-spi3", + .name = "adi-spi3", .id = 0, /* Bus number */ .num_resources = ARRAY_SIZE(bfin_spi0_resource), .resource = bfin_spi0_resource, @@ -1792,13 +1792,13 @@ static struct platform_device bf60x_spi_master0 = { }, }; -static struct bfin_spi3_master bf60x_spi_master_info1 = { +static struct adi_spi3_master bf60x_spi_master_info1 = { .num_chipselect = MAX_CTRL_CS + MAX_BLACKFIN_GPIOS, .pin_req = {P_SPI1_SCK, P_SPI1_MISO, P_SPI1_MOSI, 0}, }; static struct platform_device bf60x_spi_master1 = { - .name = "bfin-spi3", + .name = "adi-spi3", .id = 1, /* Bus number */ .num_resources = ARRAY_SIZE(bfin_spi1_resource), .resource = bfin_spi1_resource, @@ -1990,7 +1990,7 @@ static struct platform_device *ezkit_devices[] __initdata = { &bfin_sdh_device, #endif -#if IS_ENABLED(CONFIG_SPI_BFIN_V3) +#if IS_ENABLED(CONFIG_SPI_ADI_V3) &bf60x_spi_master0, &bf60x_spi_master1, #endif @@ -2051,8 +2051,8 @@ static struct pinctrl_map __initdata bfin_pinmux_map[] = { PIN_MAP_MUX_GROUP_DEFAULT("bfin_sir.1", "pinctrl-adi2.0", NULL, "uart1"), PIN_MAP_MUX_GROUP_DEFAULT("bfin-sdh.0", "pinctrl-adi2.0", NULL, "rsi0"), PIN_MAP_MUX_GROUP_DEFAULT("stmmaceth.0", "pinctrl-adi2.0", NULL, "eth0"), - PIN_MAP_MUX_GROUP_DEFAULT("bfin-spi3.0", "pinctrl-adi2.0", NULL, "spi0"), - PIN_MAP_MUX_GROUP_DEFAULT("bfin-spi3.1", "pinctrl-adi2.0", NULL, "spi1"), + PIN_MAP_MUX_GROUP_DEFAULT("adi-spi3.0", "pinctrl-adi2.0", NULL, "spi0"), + PIN_MAP_MUX_GROUP_DEFAULT("adi-spi3.1", "pinctrl-adi2.0", NULL, "spi1"), PIN_MAP_MUX_GROUP_DEFAULT("i2c-bfin-twi.0", "pinctrl-adi2.0", NULL, "twi0"), PIN_MAP_MUX_GROUP_DEFAULT("i2c-bfin-twi.1", "pinctrl-adi2.0", NULL, "twi1"), PIN_MAP_MUX_GROUP_DEFAULT("bfin-rotary", "pinctrl-adi2.0", NULL, "rotary"), diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60f2b41c7310..a52e0edb7146 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -91,8 +91,8 @@ config SPI_BFIN5XX help This is the SPI controller master driver for Blackfin 5xx processor. -config SPI_BFIN_V3 - tristate "SPI controller v3 for Blackfin" +config SPI_ADI_V3 + tristate "SPI controller v3 for ADI" depends on BF60x help This is the SPI controller v3 master driver diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bd792669e563..71e65dfc0ea3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o -obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o +obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o diff --git a/drivers/spi/spi-adi-v3.c b/drivers/spi/spi-adi-v3.c new file mode 100644 index 000000000000..0c2914cfcdb5 --- /dev/null +++ b/drivers/spi/spi-adi-v3.c @@ -0,0 +1,985 @@ +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2014 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +enum adi_spi_state { + START_STATE, + RUNNING_STATE, + DONE_STATE, + ERROR_STATE +}; + +struct adi_spi_master; + +struct adi_spi_transfer_ops { + void (*write) (struct adi_spi_master *); + void (*read) (struct adi_spi_master *); + void (*duplex) (struct adi_spi_master *); +}; + +/* runtime info for spi master */ +struct adi_spi_master { + /* SPI framework hookup */ + struct spi_master *master; + + /* Regs base of SPI controller */ + struct adi_spi_regs __iomem *regs; + + /* Pin request list */ + u16 *pin_req; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message transfer state info */ + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct adi_spi_device *cur_chip; + unsigned transfer_len; + + /* transfer buffer */ + void *tx; + void *tx_end; + void *rx; + void *rx_end; + + /* dma info */ + unsigned int tx_dma; + unsigned int rx_dma; + dma_addr_t tx_dma_addr; + dma_addr_t rx_dma_addr; + unsigned long dummy_buffer; /* used in unidirectional transfer */ + unsigned long tx_dma_size; + unsigned long rx_dma_size; + int tx_num; + int rx_num; + + /* store register value for suspend/resume */ + u32 control; + u32 ssel; + + unsigned long sclk; + enum adi_spi_state state; + + const struct adi_spi_transfer_ops *ops; +}; + +struct adi_spi_device { + u32 control; + u32 clock; + u32 ssel; + + u8 cs; + u16 cs_chg_udelay; /* Some devices require > 255usec delay */ + u32 cs_gpio; + u32 tx_dummy_val; /* tx value for rx only transfer */ + bool enable_dma; + const struct adi_spi_transfer_ops *ops; +}; + +static void adi_spi_enable(struct adi_spi_master *drv_data) +{ + u32 ctl; + + ctl = ioread32(&drv_data->regs->control); + ctl |= SPI_CTL_EN; + iowrite32(ctl, &drv_data->regs->control); +} + +static void adi_spi_disable(struct adi_spi_master *drv_data) +{ + u32 ctl; + + ctl = ioread32(&drv_data->regs->control); + ctl &= ~SPI_CTL_EN; + iowrite32(ctl, &drv_data->regs->control); +} + +/* Caculate the SPI_CLOCK register value based on input HZ */ +static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz) +{ + u32 spi_clock = sclk / speed_hz; + + if (spi_clock) + spi_clock--; + return spi_clock; +} + +static int adi_spi_flush(struct adi_spi_master *drv_data) +{ + unsigned long limit = loops_per_jiffy << 1; + + /* wait for stop and clear stat */ + while (!(ioread32(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit) + cpu_relax(); + + iowrite32(0xFFFFFFFF, &drv_data->regs->status); + + return limit; +} + +/* Chip select operation functions for cs_change flag */ +static void adi_spi_cs_active(struct adi_spi_master *drv_data, struct adi_spi_device *chip) +{ + if (likely(chip->cs < MAX_CTRL_CS)) { + u32 reg; + reg = ioread32(&drv_data->regs->ssel); + reg &= ~chip->ssel; + iowrite32(reg, &drv_data->regs->ssel); + } else { + gpio_set_value(chip->cs_gpio, 0); + } +} + +static void adi_spi_cs_deactive(struct adi_spi_master *drv_data, + struct adi_spi_device *chip) +{ + if (likely(chip->cs < MAX_CTRL_CS)) { + u32 reg; + reg = ioread32(&drv_data->regs->ssel); + reg |= chip->ssel; + iowrite32(reg, &drv_data->regs->ssel); + } else { + gpio_set_value(chip->cs_gpio, 1); + } + + /* Move delay here for consistency */ + if (chip->cs_chg_udelay) + udelay(chip->cs_chg_udelay); +} + +/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ +static inline void adi_spi_cs_enable(struct adi_spi_master *drv_data, + struct adi_spi_device *chip) +{ + if (chip->cs < MAX_CTRL_CS) { + u32 reg; + reg = ioread32(&drv_data->regs->ssel); + reg |= chip->ssel >> 8; + iowrite32(reg, &drv_data->regs->ssel); + } +} + +static inline void adi_spi_cs_disable(struct adi_spi_master *drv_data, + struct adi_spi_device *chip) +{ + if (chip->cs < MAX_CTRL_CS) { + u32 reg; + reg = ioread32(&drv_data->regs->ssel); + reg &= ~(chip->ssel >> 8); + iowrite32(reg, &drv_data->regs->ssel); + } +} + +/* stop controller and re-config current chip*/ +static void adi_spi_restore_state(struct adi_spi_master *drv_data) +{ + struct adi_spi_device *chip = drv_data->cur_chip; + + /* Clear status and disable clock */ + iowrite32(0xFFFFFFFF, &drv_data->regs->status); + iowrite32(0x0, &drv_data->regs->rx_control); + iowrite32(0x0, &drv_data->regs->tx_control); + adi_spi_disable(drv_data); + + /* Load the registers */ + iowrite32(chip->control, &drv_data->regs->control); + iowrite32(chip->clock, &drv_data->regs->clock); + + adi_spi_enable(drv_data); + drv_data->tx_num = drv_data->rx_num = 0; + /* we always choose tx transfer initiate */ + iowrite32(SPI_RXCTL_REN, &drv_data->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &drv_data->regs->tx_control); + adi_spi_cs_active(drv_data, chip); +} + +/* discard invalid rx data and empty rfifo */ +static inline void dummy_read(struct adi_spi_master *drv_data) +{ + while (!(ioread32(&drv_data->regs->status) & SPI_STAT_RFE)) + ioread32(&drv_data->regs->rfifo); +} + +static void adi_spi_u8_write(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + iowrite32(*(u8 *)(drv_data->tx++), &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv_data->regs->rfifo); + } +} + +static void adi_spi_u8_read(struct adi_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(tx_val, &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(drv_data->rx++) = ioread32(&drv_data->regs->rfifo); + } +} + +static void adi_spi_u8_duplex(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(*(u8 *)(drv_data->tx++), &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(drv_data->rx++) = ioread32(&drv_data->regs->rfifo); + } +} + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u8 = { + .write = adi_spi_u8_write, + .read = adi_spi_u8_read, + .duplex = adi_spi_u8_duplex, +}; + +static void adi_spi_u16_write(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + iowrite32(*(u16 *)drv_data->tx, &drv_data->regs->tfifo); + drv_data->tx += 2; + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv_data->regs->rfifo); + } +} + +static void adi_spi_u16_read(struct adi_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(tx_val, &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)drv_data->rx = ioread32(&drv_data->regs->rfifo); + drv_data->rx += 2; + } +} + +static void adi_spi_u16_duplex(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(*(u16 *)drv_data->tx, &drv_data->regs->tfifo); + drv_data->tx += 2; + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)drv_data->rx = ioread32(&drv_data->regs->rfifo); + drv_data->rx += 2; + } +} + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u16 = { + .write = adi_spi_u16_write, + .read = adi_spi_u16_read, + .duplex = adi_spi_u16_duplex, +}; + +static void adi_spi_u32_write(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + iowrite32(*(u32 *)drv_data->tx, &drv_data->regs->tfifo); + drv_data->tx += 4; + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + ioread32(&drv_data->regs->rfifo); + } +} + +static void adi_spi_u32_read(struct adi_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(tx_val, &drv_data->regs->tfifo); + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)drv_data->rx = ioread32(&drv_data->regs->rfifo); + drv_data->rx += 4; + } +} + +static void adi_spi_u32_duplex(struct adi_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + iowrite32(*(u32 *)drv_data->tx, &drv_data->regs->tfifo); + drv_data->tx += 4; + while (ioread32(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)drv_data->rx = ioread32(&drv_data->regs->rfifo); + drv_data->rx += 4; + } +} + +static const struct adi_spi_transfer_ops adi_spi_transfer_ops_u32 = { + .write = adi_spi_u32_write, + .read = adi_spi_u32_read, + .duplex = adi_spi_u32_duplex, +}; + + +/* test if there is more transfer to be done */ +static void adi_spi_next_transfer(struct adi_spi_master *drv) +{ + struct spi_message *msg = drv->cur_msg; + struct spi_transfer *t = drv->cur_transfer; + + /* Move to next transfer */ + if (t->transfer_list.next != &msg->transfers) { + drv->cur_transfer = list_entry(t->transfer_list.next, + struct spi_transfer, transfer_list); + drv->state = RUNNING_STATE; + } else { + drv->state = DONE_STATE; + drv->cur_transfer = NULL; + } +} + +static void adi_spi_giveback(struct adi_spi_master *drv_data) +{ + struct adi_spi_device *chip = drv_data->cur_chip; + + adi_spi_cs_deactive(drv_data, chip); + spi_finalize_current_message(drv_data->master); +} + +static int adi_spi_setup_transfer(struct adi_spi_master *drv) +{ + struct spi_transfer *t = drv->cur_transfer; + u32 cr, cr_width; + + if (t->tx_buf) { + drv->tx = (void *)t->tx_buf; + drv->tx_end = drv->tx + t->len; + } else { + drv->tx = NULL; + } + + if (t->rx_buf) { + drv->rx = t->rx_buf; + drv->rx_end = drv->rx + t->len; + } else { + drv->rx = NULL; + } + + drv->transfer_len = t->len; + + /* bits per word setup */ + switch (t->bits_per_word) { + case 8: + cr_width = SPI_CTL_SIZE08; + drv->ops = &adi_spi_transfer_ops_u8; + break; + case 16: + cr_width = SPI_CTL_SIZE16; + drv->ops = &adi_spi_transfer_ops_u16; + break; + case 32: + cr_width = SPI_CTL_SIZE32; + drv->ops = &adi_spi_transfer_ops_u32; + break; + default: + return -EINVAL; + } + cr = ioread32(&drv->regs->control) & ~SPI_CTL_SIZE; + cr |= cr_width; + iowrite32(cr, &drv->regs->control); + + /* speed setup */ + iowrite32(hz_to_spi_clock(drv->sclk, t->speed_hz), &drv->regs->clock); + return 0; +} + +static int adi_spi_dma_xfer(struct adi_spi_master *drv_data) +{ + struct spi_transfer *t = drv_data->cur_transfer; + struct spi_message *msg = drv_data->cur_msg; + struct adi_spi_device *chip = drv_data->cur_chip; + u32 dma_config; + unsigned long word_count, word_size; + void *tx_buf, *rx_buf; + + switch (t->bits_per_word) { + case 8: + dma_config = WDSIZE_8 | PSIZE_8; + word_count = drv_data->transfer_len; + word_size = 1; + break; + case 16: + dma_config = WDSIZE_16 | PSIZE_16; + word_count = drv_data->transfer_len / 2; + word_size = 2; + break; + default: + dma_config = WDSIZE_32 | PSIZE_32; + word_count = drv_data->transfer_len / 4; + word_size = 4; + break; + } + + if (!drv_data->rx) { + tx_buf = drv_data->tx; + rx_buf = &drv_data->dummy_buffer; + drv_data->tx_dma_size = drv_data->transfer_len; + drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer); + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, 0); + } else if (!drv_data->tx) { + drv_data->dummy_buffer = chip->tx_dummy_val; + tx_buf = &drv_data->dummy_buffer; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer); + drv_data->rx_dma_size = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, 0); + set_dma_x_modify(drv_data->rx_dma, word_size); + } else { + tx_buf = drv_data->tx; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = drv_data->rx_dma_size + = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, word_size); + } + + drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)tx_buf, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->tx_dma_addr)) + return -ENOMEM; + + drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)rx_buf, + drv_data->rx_dma_size, + DMA_FROM_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->rx_dma_addr)) { + dma_unmap_single(&msg->spi->dev, + drv_data->tx_dma_addr, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + return -ENOMEM; + } + + dummy_read(drv_data); + set_dma_x_count(drv_data->tx_dma, word_count); + set_dma_x_count(drv_data->rx_dma, word_count); + set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr); + set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr); + dma_config |= DMAFLOW_STOP | RESTART | DI_EN; + set_dma_config(drv_data->tx_dma, dma_config); + set_dma_config(drv_data->rx_dma, dma_config | WNR); + enable_dma(drv_data->tx_dma); + enable_dma(drv_data->rx_dma); + + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RDR_NE, + &drv_data->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF, + &drv_data->regs->tx_control); + + return 0; +} + +static int adi_spi_pio_xfer(struct adi_spi_master *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + + if (!drv_data->rx) { + /* write only half duplex */ + drv_data->ops->write(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; + } else if (!drv_data->tx) { + /* read only half duplex */ + drv_data->ops->read(drv_data); + if (drv_data->rx != drv_data->rx_end) + return -EIO; + } else { + /* full duplex mode */ + drv_data->ops->duplex(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; + } + + if (!adi_spi_flush(drv_data)) + return -EIO; + msg->actual_length += drv_data->transfer_len; + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +static void adi_spi_pump_transfers(unsigned long data) +{ + struct adi_spi_master *drv_data = (struct adi_spi_master *)data; + struct spi_message *msg = NULL; + struct spi_transfer *t = NULL; + struct adi_spi_device *chip = NULL; + int ret; + + /* Get current state information */ + msg = drv_data->cur_msg; + t = drv_data->cur_transfer; + chip = drv_data->cur_chip; + + /* Handle for abort */ + if (drv_data->state == ERROR_STATE) { + msg->status = -EIO; + adi_spi_giveback(drv_data); + return; + } + + if (drv_data->state == RUNNING_STATE) { + if (t->delay_usecs) + udelay(t->delay_usecs); + if (t->cs_change) + adi_spi_cs_deactive(drv_data, chip); + adi_spi_next_transfer(drv_data); + t = drv_data->cur_transfer; + } + /* Handle end of message */ + if (drv_data->state == DONE_STATE) { + msg->status = 0; + adi_spi_giveback(drv_data); + return; + } + + if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) { + /* Schedule next transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + return; + } + + ret = adi_spi_setup_transfer(drv_data); + if (ret) { + msg->status = ret; + adi_spi_giveback(drv_data); + } + + iowrite32(0xFFFFFFFF, &drv_data->regs->status); + adi_spi_cs_active(drv_data, chip); + drv_data->state = RUNNING_STATE; + + if (chip->enable_dma) + ret = adi_spi_dma_xfer(drv_data); + else + ret = adi_spi_pio_xfer(drv_data); + if (ret) { + msg->status = ret; + adi_spi_giveback(drv_data); + } +} + +static int adi_spi_transfer_one_message(struct spi_master *master, + struct spi_message *m) +{ + struct adi_spi_master *drv_data = spi_master_get_devdata(master); + + drv_data->cur_msg = m; + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + adi_spi_restore_state(drv_data); + + drv_data->state = START_STATE; + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + struct spi_transfer, transfer_list); + + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +#define MAX_SPI_SSEL 7 + +static const u16 ssel[][MAX_SPI_SSEL] = { + {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3, + P_SPI0_SSEL4, P_SPI0_SSEL5, + P_SPI0_SSEL6, P_SPI0_SSEL7}, + + {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3, + P_SPI1_SSEL4, P_SPI1_SSEL5, + P_SPI1_SSEL6, P_SPI1_SSEL7}, + + {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3, + P_SPI2_SSEL4, P_SPI2_SSEL5, + P_SPI2_SSEL6, P_SPI2_SSEL7}, +}; + +static int adi_spi_setup(struct spi_device *spi) +{ + struct adi_spi_master *drv_data = spi_master_get_devdata(spi->master); + struct adi_spi_device *chip = spi_get_ctldata(spi); + u32 ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE; + int ret = -EINVAL; + + if (!chip) { + struct adi_spi3_chip *chip_info = spi->controller_data; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + dev_err(&spi->dev, "can not allocate chip data\n"); + return -ENOMEM; + } + if (chip_info) { + if (chip_info->control & ~ctl_reg) { + dev_err(&spi->dev, + "do not set bits that the SPI framework manages\n"); + goto error; + } + chip->control = chip_info->control; + chip->cs_chg_udelay = chip_info->cs_chg_udelay; + chip->tx_dummy_val = chip_info->tx_dummy_val; + chip->enable_dma = chip_info->enable_dma; + } + chip->cs = spi->chip_select; + + if (chip->cs < MAX_CTRL_CS) { + chip->ssel = (1 << chip->cs) << 8; + ret = peripheral_request(ssel[spi->master->bus_num] + [chip->cs-1], dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "peripheral_request() error\n"); + goto error; + } + } else { + chip->cs_gpio = chip->cs - MAX_CTRL_CS; + ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH, + dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "gpio_request_one() error\n"); + goto error; + } + } + spi_set_ctldata(spi, chip); + } + + /* force a default base state */ + chip->control &= ctl_reg; + + if (spi->mode & SPI_CPOL) + chip->control |= SPI_CTL_CPOL; + if (spi->mode & SPI_CPHA) + chip->control |= SPI_CTL_CPHA; + if (spi->mode & SPI_LSB_FIRST) + chip->control |= SPI_CTL_LSBF; + chip->control |= SPI_CTL_MSTR; + /* we choose software to controll cs */ + chip->control &= ~SPI_CTL_ASSEL; + + chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz); + + adi_spi_cs_enable(drv_data, chip); + adi_spi_cs_deactive(drv_data, chip); + + return 0; +error: + if (chip) { + kfree(chip); + spi_set_ctldata(spi, NULL); + } + + return ret; +} + +static void adi_spi_cleanup(struct spi_device *spi) +{ + struct adi_spi_device *chip = spi_get_ctldata(spi); + struct adi_spi_master *drv_data = spi_master_get_devdata(spi->master); + + if (!chip) + return; + + if (chip->cs < MAX_CTRL_CS) { + peripheral_free(ssel[spi->master->bus_num] + [chip->cs-1]); + adi_spi_cs_disable(drv_data, chip); + } else { + gpio_free(chip->cs_gpio); + } + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + +static irqreturn_t adi_spi_tx_dma_isr(int irq, void *dev_id) +{ + struct adi_spi_master *drv_data = dev_id; + u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma); + u32 tx_ctl; + + clear_dma_irqstat(drv_data->tx_dma); + if (dma_stat & DMA_DONE) { + drv_data->tx_num++; + } else { + dev_err(&drv_data->master->dev, + "spi tx dma error: %d\n", dma_stat); + if (drv_data->tx) + drv_data->state = ERROR_STATE; + } + tx_ctl = ioread32(&drv_data->regs->tx_control); + tx_ctl &= ~SPI_TXCTL_TDR_NF; + iowrite32(tx_ctl, &drv_data->regs->tx_control); + return IRQ_HANDLED; +} + +static irqreturn_t adi_spi_rx_dma_isr(int irq, void *dev_id) +{ + struct adi_spi_master *drv_data = dev_id; + struct spi_message *msg = drv_data->cur_msg; + u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma); + + clear_dma_irqstat(drv_data->rx_dma); + if (dma_stat & DMA_DONE) { + drv_data->rx_num++; + /* we may fail on tx dma */ + if (drv_data->state != ERROR_STATE) + msg->actual_length += drv_data->transfer_len; + } else { + drv_data->state = ERROR_STATE; + dev_err(&drv_data->master->dev, + "spi rx dma error: %d\n", dma_stat); + } + iowrite32(0, &drv_data->regs->tx_control); + iowrite32(0, &drv_data->regs->rx_control); + if (drv_data->rx_num != drv_data->tx_num) + dev_dbg(&drv_data->master->dev, + "dma interrupt missing: tx=%d,rx=%d\n", + drv_data->tx_num, drv_data->rx_num); + tasklet_schedule(&drv_data->pump_transfers); + return IRQ_HANDLED; +} + +static int adi_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_spi3_master *info = dev_get_platdata(dev); + struct spi_master *master; + struct adi_spi_master *drv_data; + struct resource *mem, *res; + unsigned int tx_dma, rx_dma; + unsigned long sclk; + int ret; + + if (!info) { + dev_err(dev, "platform data missing!\n"); + return -ENODEV; + } + + sclk = get_sclk1(); + if (!sclk) { + dev_err(dev, "can not get sclk1\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(dev, "can not get tx dma resource\n"); + return -ENXIO; + } + tx_dma = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(dev, "can not get rx dma resource\n"); + return -ENXIO; + } + rx_dma = res->start; + + /* allocate master with space for drv_data */ + master = spi_alloc_master(dev, sizeof(*drv_data)); + if (!master) { + dev_err(dev, "can not alloc spi_master\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, master); + + /* the mode bits supported by this driver */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + + master->bus_num = pdev->id; + master->num_chipselect = info->num_chipselect; + master->cleanup = adi_spi_cleanup; + master->setup = adi_spi_setup; + master->transfer_one_message = adi_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(8); + + drv_data = spi_master_get_devdata(master); + drv_data->master = master; + drv_data->tx_dma = tx_dma; + drv_data->rx_dma = rx_dma; + drv_data->pin_req = info->pin_req; + drv_data->sclk = sclk; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drv_data->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(drv_data->regs)) { + ret = PTR_ERR(drv_data->regs); + goto err_put_master; + } + + /* request tx and rx dma */ + ret = request_dma(tx_dma, "SPI_TX_DMA"); + if (ret) { + dev_err(dev, "can not request SPI TX DMA channel\n"); + goto err_put_master; + } + set_dma_callback(tx_dma, adi_spi_tx_dma_isr, drv_data); + + ret = request_dma(rx_dma, "SPI_RX_DMA"); + if (ret) { + dev_err(dev, "can not request SPI RX DMA channel\n"); + goto err_free_tx_dma; + } + set_dma_callback(drv_data->rx_dma, adi_spi_rx_dma_isr, drv_data); + + /* request CLK, MOSI and MISO */ + ret = peripheral_request_list(drv_data->pin_req, "adi-spi3"); + if (ret < 0) { + dev_err(dev, "can not request spi pins\n"); + goto err_free_rx_dma; + } + + iowrite32(SPI_CTL_MSTR | SPI_CTL_CPHA, &drv_data->regs->control); + iowrite32(0x0000FE00, &drv_data->regs->ssel); + iowrite32(0x0, &drv_data->regs->delay); + + tasklet_init(&drv_data->pump_transfers, + adi_spi_pump_transfers, (unsigned long)drv_data); + /* register with the SPI framework */ + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(dev, "can not register spi master\n"); + goto err_free_peripheral; + } + + return ret; + +err_free_peripheral: + peripheral_free_list(drv_data->pin_req); +err_free_rx_dma: + free_dma(rx_dma); +err_free_tx_dma: + free_dma(tx_dma); +err_put_master: + spi_master_put(master); + + return ret; +} + +static int adi_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct adi_spi_master *drv_data = spi_master_get_devdata(master); + + adi_spi_disable(drv_data); + peripheral_free_list(drv_data->pin_req); + free_dma(drv_data->rx_dma); + free_dma(drv_data->tx_dma); + return 0; +} + +#ifdef CONFIG_PM +static int adi_spi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct adi_spi_master *drv_data = spi_master_get_devdata(master); + + spi_master_suspend(master); + + drv_data->control = ioread32(&drv_data->regs->control); + drv_data->ssel = ioread32(&drv_data->regs->ssel); + + iowrite32(SPI_CTL_MSTR | SPI_CTL_CPHA, &drv_data->regs->control); + iowrite32(0x0000FE00, &drv_data->regs->ssel); + dma_disable_irq(drv_data->rx_dma); + dma_disable_irq(drv_data->tx_dma); + + return 0; +} + +static int adi_spi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct adi_spi_master *drv_data = spi_master_get_devdata(master); + int ret = 0; + + /* bootrom may modify spi and dma status when resume in spi boot mode */ + disable_dma(drv_data->rx_dma); + + dma_enable_irq(drv_data->rx_dma); + dma_enable_irq(drv_data->tx_dma); + iowrite32(drv_data->control, &drv_data->regs->control); + iowrite32(drv_data->ssel, &drv_data->regs->ssel); + + ret = spi_master_resume(master); + if (ret) { + free_dma(drv_data->rx_dma); + free_dma(drv_data->tx_dma); + } + + return ret; +} +#endif +static const struct dev_pm_ops adi_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(adi_spi_suspend, adi_spi_resume) +}; + +MODULE_ALIAS("platform:adi-spi3"); +static struct platform_driver adi_spi_driver = { + .driver = { + .name = "adi-spi3", + .owner = THIS_MODULE, + .pm = &adi_spi_pm_ops, + }, + .remove = adi_spi_remove, +}; + +module_platform_driver_probe(adi_spi_driver, adi_spi_probe); + +MODULE_DESCRIPTION("Analog Devices SPI3 controller driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-bfin-v3.c b/drivers/spi/spi-bfin-v3.c deleted file mode 100644 index 4089d0e0d84e..000000000000 --- a/drivers/spi/spi-bfin-v3.c +++ /dev/null @@ -1,965 +0,0 @@ -/* - * Analog Devices SPI3 controller driver - * - * Copyright (c) 2013 Analog Devices Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -enum bfin_spi_state { - START_STATE, - RUNNING_STATE, - DONE_STATE, - ERROR_STATE -}; - -struct bfin_spi_master; - -struct bfin_spi_transfer_ops { - void (*write) (struct bfin_spi_master *); - void (*read) (struct bfin_spi_master *); - void (*duplex) (struct bfin_spi_master *); -}; - -/* runtime info for spi master */ -struct bfin_spi_master { - /* SPI framework hookup */ - struct spi_master *master; - - /* Regs base of SPI controller */ - struct bfin_spi_regs __iomem *regs; - - /* Pin request list */ - u16 *pin_req; - - /* Message Transfer pump */ - struct tasklet_struct pump_transfers; - - /* Current message transfer state info */ - struct spi_message *cur_msg; - struct spi_transfer *cur_transfer; - struct bfin_spi_device *cur_chip; - unsigned transfer_len; - - /* transfer buffer */ - void *tx; - void *tx_end; - void *rx; - void *rx_end; - - /* dma info */ - unsigned int tx_dma; - unsigned int rx_dma; - dma_addr_t tx_dma_addr; - dma_addr_t rx_dma_addr; - unsigned long dummy_buffer; /* used in unidirectional transfer */ - unsigned long tx_dma_size; - unsigned long rx_dma_size; - int tx_num; - int rx_num; - - /* store register value for suspend/resume */ - u32 control; - u32 ssel; - - unsigned long sclk; - enum bfin_spi_state state; - - const struct bfin_spi_transfer_ops *ops; -}; - -struct bfin_spi_device { - u32 control; - u32 clock; - u32 ssel; - - u8 cs; - u16 cs_chg_udelay; /* Some devices require > 255usec delay */ - u32 cs_gpio; - u32 tx_dummy_val; /* tx value for rx only transfer */ - bool enable_dma; - const struct bfin_spi_transfer_ops *ops; -}; - -static void bfin_spi_enable(struct bfin_spi_master *drv_data) -{ - bfin_write_or(&drv_data->regs->control, SPI_CTL_EN); -} - -static void bfin_spi_disable(struct bfin_spi_master *drv_data) -{ - bfin_write_and(&drv_data->regs->control, ~SPI_CTL_EN); -} - -/* Caculate the SPI_CLOCK register value based on input HZ */ -static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz) -{ - u32 spi_clock = sclk / speed_hz; - - if (spi_clock) - spi_clock--; - return spi_clock; -} - -static int bfin_spi_flush(struct bfin_spi_master *drv_data) -{ - unsigned long limit = loops_per_jiffy << 1; - - /* wait for stop and clear stat */ - while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit) - cpu_relax(); - - bfin_write(&drv_data->regs->status, 0xFFFFFFFF); - - return limit; -} - -/* Chip select operation functions for cs_change flag */ -static void bfin_spi_cs_active(struct bfin_spi_master *drv_data, struct bfin_spi_device *chip) -{ - if (likely(chip->cs < MAX_CTRL_CS)) - bfin_write_and(&drv_data->regs->ssel, ~chip->ssel); - else - gpio_set_value(chip->cs_gpio, 0); -} - -static void bfin_spi_cs_deactive(struct bfin_spi_master *drv_data, - struct bfin_spi_device *chip) -{ - if (likely(chip->cs < MAX_CTRL_CS)) - bfin_write_or(&drv_data->regs->ssel, chip->ssel); - else - gpio_set_value(chip->cs_gpio, 1); - - /* Move delay here for consistency */ - if (chip->cs_chg_udelay) - udelay(chip->cs_chg_udelay); -} - -/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ -static inline void bfin_spi_cs_enable(struct bfin_spi_master *drv_data, - struct bfin_spi_device *chip) -{ - if (chip->cs < MAX_CTRL_CS) - bfin_write_or(&drv_data->regs->ssel, chip->ssel >> 8); -} - -static inline void bfin_spi_cs_disable(struct bfin_spi_master *drv_data, - struct bfin_spi_device *chip) -{ - if (chip->cs < MAX_CTRL_CS) - bfin_write_and(&drv_data->regs->ssel, ~(chip->ssel >> 8)); -} - -/* stop controller and re-config current chip*/ -static void bfin_spi_restore_state(struct bfin_spi_master *drv_data) -{ - struct bfin_spi_device *chip = drv_data->cur_chip; - - /* Clear status and disable clock */ - bfin_write(&drv_data->regs->status, 0xFFFFFFFF); - bfin_write(&drv_data->regs->rx_control, 0x0); - bfin_write(&drv_data->regs->tx_control, 0x0); - bfin_spi_disable(drv_data); - - SSYNC(); - - /* Load the registers */ - bfin_write(&drv_data->regs->control, chip->control); - bfin_write(&drv_data->regs->clock, chip->clock); - - bfin_spi_enable(drv_data); - drv_data->tx_num = drv_data->rx_num = 0; - /* we always choose tx transfer initiate */ - bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN); - bfin_write(&drv_data->regs->tx_control, - SPI_TXCTL_TEN | SPI_TXCTL_TTI); - bfin_spi_cs_active(drv_data, chip); -} - -/* discard invalid rx data and empty rfifo */ -static inline void dummy_read(struct bfin_spi_master *drv_data) -{ - while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)) - bfin_read(&drv_data->regs->rfifo); -} - -static void bfin_spi_u8_write(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->tx < drv_data->tx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++))); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - bfin_read(&drv_data->regs->rfifo); - } -} - -static void bfin_spi_u8_read(struct bfin_spi_master *drv_data) -{ - u32 tx_val = drv_data->cur_chip->tx_dummy_val; - - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, tx_val); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo); - } -} - -static void bfin_spi_u8_duplex(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++))); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo); - } -} - -static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = { - .write = bfin_spi_u8_write, - .read = bfin_spi_u8_read, - .duplex = bfin_spi_u8_duplex, -}; - -static void bfin_spi_u16_write(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->tx < drv_data->tx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx)); - drv_data->tx += 2; - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - bfin_read(&drv_data->regs->rfifo); - } -} - -static void bfin_spi_u16_read(struct bfin_spi_master *drv_data) -{ - u32 tx_val = drv_data->cur_chip->tx_dummy_val; - - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, tx_val); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); - drv_data->rx += 2; - } -} - -static void bfin_spi_u16_duplex(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx)); - drv_data->tx += 2; - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); - drv_data->rx += 2; - } -} - -static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = { - .write = bfin_spi_u16_write, - .read = bfin_spi_u16_read, - .duplex = bfin_spi_u16_duplex, -}; - -static void bfin_spi_u32_write(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->tx < drv_data->tx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx)); - drv_data->tx += 4; - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - bfin_read(&drv_data->regs->rfifo); - } -} - -static void bfin_spi_u32_read(struct bfin_spi_master *drv_data) -{ - u32 tx_val = drv_data->cur_chip->tx_dummy_val; - - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, tx_val); - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); - drv_data->rx += 4; - } -} - -static void bfin_spi_u32_duplex(struct bfin_spi_master *drv_data) -{ - dummy_read(drv_data); - while (drv_data->rx < drv_data->rx_end) { - bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx)); - drv_data->tx += 4; - while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) - cpu_relax(); - *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); - drv_data->rx += 4; - } -} - -static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u32 = { - .write = bfin_spi_u32_write, - .read = bfin_spi_u32_read, - .duplex = bfin_spi_u32_duplex, -}; - - -/* test if there is more transfer to be done */ -static void bfin_spi_next_transfer(struct bfin_spi_master *drv) -{ - struct spi_message *msg = drv->cur_msg; - struct spi_transfer *t = drv->cur_transfer; - - /* Move to next transfer */ - if (t->transfer_list.next != &msg->transfers) { - drv->cur_transfer = list_entry(t->transfer_list.next, - struct spi_transfer, transfer_list); - drv->state = RUNNING_STATE; - } else { - drv->state = DONE_STATE; - drv->cur_transfer = NULL; - } -} - -static void bfin_spi_giveback(struct bfin_spi_master *drv_data) -{ - struct bfin_spi_device *chip = drv_data->cur_chip; - - bfin_spi_cs_deactive(drv_data, chip); - spi_finalize_current_message(drv_data->master); -} - -static int bfin_spi_setup_transfer(struct bfin_spi_master *drv) -{ - struct spi_transfer *t = drv->cur_transfer; - u32 cr, cr_width; - - if (t->tx_buf) { - drv->tx = (void *)t->tx_buf; - drv->tx_end = drv->tx + t->len; - } else { - drv->tx = NULL; - } - - if (t->rx_buf) { - drv->rx = t->rx_buf; - drv->rx_end = drv->rx + t->len; - } else { - drv->rx = NULL; - } - - drv->transfer_len = t->len; - - /* bits per word setup */ - switch (t->bits_per_word) { - case 8: - cr_width = SPI_CTL_SIZE08; - drv->ops = &bfin_bfin_spi_transfer_ops_u8; - break; - case 16: - cr_width = SPI_CTL_SIZE16; - drv->ops = &bfin_bfin_spi_transfer_ops_u16; - break; - case 32: - cr_width = SPI_CTL_SIZE32; - drv->ops = &bfin_bfin_spi_transfer_ops_u32; - break; - default: - return -EINVAL; - } - cr = bfin_read(&drv->regs->control) & ~SPI_CTL_SIZE; - cr |= cr_width; - bfin_write(&drv->regs->control, cr); - - /* speed setup */ - bfin_write(&drv->regs->clock, - hz_to_spi_clock(drv->sclk, t->speed_hz)); - return 0; -} - -static int bfin_spi_dma_xfer(struct bfin_spi_master *drv_data) -{ - struct spi_transfer *t = drv_data->cur_transfer; - struct spi_message *msg = drv_data->cur_msg; - struct bfin_spi_device *chip = drv_data->cur_chip; - u32 dma_config; - unsigned long word_count, word_size; - void *tx_buf, *rx_buf; - - switch (t->bits_per_word) { - case 8: - dma_config = WDSIZE_8 | PSIZE_8; - word_count = drv_data->transfer_len; - word_size = 1; - break; - case 16: - dma_config = WDSIZE_16 | PSIZE_16; - word_count = drv_data->transfer_len / 2; - word_size = 2; - break; - default: - dma_config = WDSIZE_32 | PSIZE_32; - word_count = drv_data->transfer_len / 4; - word_size = 4; - break; - } - - if (!drv_data->rx) { - tx_buf = drv_data->tx; - rx_buf = &drv_data->dummy_buffer; - drv_data->tx_dma_size = drv_data->transfer_len; - drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer); - set_dma_x_modify(drv_data->tx_dma, word_size); - set_dma_x_modify(drv_data->rx_dma, 0); - } else if (!drv_data->tx) { - drv_data->dummy_buffer = chip->tx_dummy_val; - tx_buf = &drv_data->dummy_buffer; - rx_buf = drv_data->rx; - drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer); - drv_data->rx_dma_size = drv_data->transfer_len; - set_dma_x_modify(drv_data->tx_dma, 0); - set_dma_x_modify(drv_data->rx_dma, word_size); - } else { - tx_buf = drv_data->tx; - rx_buf = drv_data->rx; - drv_data->tx_dma_size = drv_data->rx_dma_size - = drv_data->transfer_len; - set_dma_x_modify(drv_data->tx_dma, word_size); - set_dma_x_modify(drv_data->rx_dma, word_size); - } - - drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev, - (void *)tx_buf, - drv_data->tx_dma_size, - DMA_TO_DEVICE); - if (dma_mapping_error(&msg->spi->dev, - drv_data->tx_dma_addr)) - return -ENOMEM; - - drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev, - (void *)rx_buf, - drv_data->rx_dma_size, - DMA_FROM_DEVICE); - if (dma_mapping_error(&msg->spi->dev, - drv_data->rx_dma_addr)) { - dma_unmap_single(&msg->spi->dev, - drv_data->tx_dma_addr, - drv_data->tx_dma_size, - DMA_TO_DEVICE); - return -ENOMEM; - } - - dummy_read(drv_data); - set_dma_x_count(drv_data->tx_dma, word_count); - set_dma_x_count(drv_data->rx_dma, word_count); - set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr); - set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr); - dma_config |= DMAFLOW_STOP | RESTART | DI_EN; - set_dma_config(drv_data->tx_dma, dma_config); - set_dma_config(drv_data->rx_dma, dma_config | WNR); - enable_dma(drv_data->tx_dma); - enable_dma(drv_data->rx_dma); - SSYNC(); - - bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN | SPI_RXCTL_RDR_NE); - SSYNC(); - bfin_write(&drv_data->regs->tx_control, - SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF); - - return 0; -} - -static int bfin_spi_pio_xfer(struct bfin_spi_master *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - - if (!drv_data->rx) { - /* write only half duplex */ - drv_data->ops->write(drv_data); - if (drv_data->tx != drv_data->tx_end) - return -EIO; - } else if (!drv_data->tx) { - /* read only half duplex */ - drv_data->ops->read(drv_data); - if (drv_data->rx != drv_data->rx_end) - return -EIO; - } else { - /* full duplex mode */ - drv_data->ops->duplex(drv_data); - if (drv_data->tx != drv_data->tx_end) - return -EIO; - } - - if (!bfin_spi_flush(drv_data)) - return -EIO; - msg->actual_length += drv_data->transfer_len; - tasklet_schedule(&drv_data->pump_transfers); - return 0; -} - -static void bfin_spi_pump_transfers(unsigned long data) -{ - struct bfin_spi_master *drv_data = (struct bfin_spi_master *)data; - struct spi_message *msg = NULL; - struct spi_transfer *t = NULL; - struct bfin_spi_device *chip = NULL; - int ret; - - /* Get current state information */ - msg = drv_data->cur_msg; - t = drv_data->cur_transfer; - chip = drv_data->cur_chip; - - /* Handle for abort */ - if (drv_data->state == ERROR_STATE) { - msg->status = -EIO; - bfin_spi_giveback(drv_data); - return; - } - - if (drv_data->state == RUNNING_STATE) { - if (t->delay_usecs) - udelay(t->delay_usecs); - if (t->cs_change) - bfin_spi_cs_deactive(drv_data, chip); - bfin_spi_next_transfer(drv_data); - t = drv_data->cur_transfer; - } - /* Handle end of message */ - if (drv_data->state == DONE_STATE) { - msg->status = 0; - bfin_spi_giveback(drv_data); - return; - } - - if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) { - /* Schedule next transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); - return; - } - - ret = bfin_spi_setup_transfer(drv_data); - if (ret) { - msg->status = ret; - bfin_spi_giveback(drv_data); - } - - bfin_write(&drv_data->regs->status, 0xFFFFFFFF); - bfin_spi_cs_active(drv_data, chip); - drv_data->state = RUNNING_STATE; - - if (chip->enable_dma) - ret = bfin_spi_dma_xfer(drv_data); - else - ret = bfin_spi_pio_xfer(drv_data); - if (ret) { - msg->status = ret; - bfin_spi_giveback(drv_data); - } -} - -static int bfin_spi_transfer_one_message(struct spi_master *master, - struct spi_message *m) -{ - struct bfin_spi_master *drv_data = spi_master_get_devdata(master); - - drv_data->cur_msg = m; - drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); - bfin_spi_restore_state(drv_data); - - drv_data->state = START_STATE; - drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, - struct spi_transfer, transfer_list); - - tasklet_schedule(&drv_data->pump_transfers); - return 0; -} - -#define MAX_SPI_SSEL 7 - -static const u16 ssel[][MAX_SPI_SSEL] = { - {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3, - P_SPI0_SSEL4, P_SPI0_SSEL5, - P_SPI0_SSEL6, P_SPI0_SSEL7}, - - {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3, - P_SPI1_SSEL4, P_SPI1_SSEL5, - P_SPI1_SSEL6, P_SPI1_SSEL7}, - - {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3, - P_SPI2_SSEL4, P_SPI2_SSEL5, - P_SPI2_SSEL6, P_SPI2_SSEL7}, -}; - -static int bfin_spi_setup(struct spi_device *spi) -{ - struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master); - struct bfin_spi_device *chip = spi_get_ctldata(spi); - u32 bfin_ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE; - int ret = -EINVAL; - - if (!chip) { - struct bfin_spi3_chip *chip_info = spi->controller_data; - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) { - dev_err(&spi->dev, "can not allocate chip data\n"); - return -ENOMEM; - } - if (chip_info) { - if (chip_info->control & ~bfin_ctl_reg) { - dev_err(&spi->dev, - "do not set bits that the SPI framework manages\n"); - goto error; - } - chip->control = chip_info->control; - chip->cs_chg_udelay = chip_info->cs_chg_udelay; - chip->tx_dummy_val = chip_info->tx_dummy_val; - chip->enable_dma = chip_info->enable_dma; - } - chip->cs = spi->chip_select; - if (chip->cs < MAX_CTRL_CS) { - chip->ssel = (1 << chip->cs) << 8; - ret = peripheral_request(ssel[spi->master->bus_num] - [chip->cs-1], dev_name(&spi->dev)); - if (ret) { - dev_err(&spi->dev, "peripheral_request() error\n"); - goto error; - } - } else { - chip->cs_gpio = chip->cs - MAX_CTRL_CS; - ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH, - dev_name(&spi->dev)); - if (ret) { - dev_err(&spi->dev, "gpio_request_one() error\n"); - goto error; - } - } - spi_set_ctldata(spi, chip); - } - - /* force a default base state */ - chip->control &= bfin_ctl_reg; - - if (spi->mode & SPI_CPOL) - chip->control |= SPI_CTL_CPOL; - if (spi->mode & SPI_CPHA) - chip->control |= SPI_CTL_CPHA; - if (spi->mode & SPI_LSB_FIRST) - chip->control |= SPI_CTL_LSBF; - chip->control |= SPI_CTL_MSTR; - /* we choose software to controll cs */ - chip->control &= ~SPI_CTL_ASSEL; - - chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz); - - bfin_spi_cs_enable(drv_data, chip); - bfin_spi_cs_deactive(drv_data, chip); - - return 0; -error: - if (chip) { - kfree(chip); - spi_set_ctldata(spi, NULL); - } - - return ret; -} - -static void bfin_spi_cleanup(struct spi_device *spi) -{ - struct bfin_spi_device *chip = spi_get_ctldata(spi); - struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master); - - if (!chip) - return; - - if (chip->cs < MAX_CTRL_CS) { - peripheral_free(ssel[spi->master->bus_num] - [chip->cs-1]); - bfin_spi_cs_disable(drv_data, chip); - } else { - gpio_free(chip->cs_gpio); - } - - kfree(chip); - spi_set_ctldata(spi, NULL); -} - -static irqreturn_t bfin_spi_tx_dma_isr(int irq, void *dev_id) -{ - struct bfin_spi_master *drv_data = dev_id; - u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma); - - clear_dma_irqstat(drv_data->tx_dma); - if (dma_stat & DMA_DONE) { - drv_data->tx_num++; - } else { - dev_err(&drv_data->master->dev, - "spi tx dma error: %d\n", dma_stat); - if (drv_data->tx) - drv_data->state = ERROR_STATE; - } - bfin_write_and(&drv_data->regs->tx_control, ~SPI_TXCTL_TDR_NF); - return IRQ_HANDLED; -} - -static irqreturn_t bfin_spi_rx_dma_isr(int irq, void *dev_id) -{ - struct bfin_spi_master *drv_data = dev_id; - struct spi_message *msg = drv_data->cur_msg; - u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma); - - clear_dma_irqstat(drv_data->rx_dma); - if (dma_stat & DMA_DONE) { - drv_data->rx_num++; - /* we may fail on tx dma */ - if (drv_data->state != ERROR_STATE) - msg->actual_length += drv_data->transfer_len; - } else { - drv_data->state = ERROR_STATE; - dev_err(&drv_data->master->dev, - "spi rx dma error: %d\n", dma_stat); - } - bfin_write(&drv_data->regs->tx_control, 0); - bfin_write(&drv_data->regs->rx_control, 0); - if (drv_data->rx_num != drv_data->tx_num) - dev_dbg(&drv_data->master->dev, - "dma interrupt missing: tx=%d,rx=%d\n", - drv_data->tx_num, drv_data->rx_num); - tasklet_schedule(&drv_data->pump_transfers); - return IRQ_HANDLED; -} - -static int bfin_spi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct bfin_spi3_master *info = dev_get_platdata(dev); - struct spi_master *master; - struct bfin_spi_master *drv_data; - struct resource *mem, *res; - unsigned int tx_dma, rx_dma; - unsigned long sclk; - int ret; - - if (!info) { - dev_err(dev, "platform data missing!\n"); - return -ENODEV; - } - - sclk = get_sclk1(); - if (!sclk) { - dev_err(dev, "can not get sclk1\n"); - return -ENXIO; - } - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(dev, "can not get tx dma resource\n"); - return -ENXIO; - } - tx_dma = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(dev, "can not get rx dma resource\n"); - return -ENXIO; - } - rx_dma = res->start; - - /* allocate master with space for drv_data */ - master = spi_alloc_master(dev, sizeof(*drv_data)); - if (!master) { - dev_err(dev, "can not alloc spi_master\n"); - return -ENOMEM; - } - platform_set_drvdata(pdev, master); - - /* the mode bits supported by this driver */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; - - master->bus_num = pdev->id; - master->num_chipselect = info->num_chipselect; - master->cleanup = bfin_spi_cleanup; - master->setup = bfin_spi_setup; - master->transfer_one_message = bfin_spi_transfer_one_message; - master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | - SPI_BPW_MASK(8); - - drv_data = spi_master_get_devdata(master); - drv_data->master = master; - drv_data->tx_dma = tx_dma; - drv_data->rx_dma = rx_dma; - drv_data->pin_req = info->pin_req; - drv_data->sclk = sclk; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - drv_data->regs = devm_ioremap_resource(dev, mem); - if (IS_ERR(drv_data->regs)) { - ret = PTR_ERR(drv_data->regs); - goto err_put_master; - } - - /* request tx and rx dma */ - ret = request_dma(tx_dma, "SPI_TX_DMA"); - if (ret) { - dev_err(dev, "can not request SPI TX DMA channel\n"); - goto err_put_master; - } - set_dma_callback(tx_dma, bfin_spi_tx_dma_isr, drv_data); - - ret = request_dma(rx_dma, "SPI_RX_DMA"); - if (ret) { - dev_err(dev, "can not request SPI RX DMA channel\n"); - goto err_free_tx_dma; - } - set_dma_callback(drv_data->rx_dma, bfin_spi_rx_dma_isr, drv_data); - - /* request CLK, MOSI and MISO */ - ret = peripheral_request_list(drv_data->pin_req, "bfin-spi3"); - if (ret < 0) { - dev_err(dev, "can not request spi pins\n"); - goto err_free_rx_dma; - } - - bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA); - bfin_write(&drv_data->regs->ssel, 0x0000FE00); - bfin_write(&drv_data->regs->delay, 0x0); - - tasklet_init(&drv_data->pump_transfers, - bfin_spi_pump_transfers, (unsigned long)drv_data); - /* register with the SPI framework */ - ret = devm_spi_register_master(dev, master); - if (ret) { - dev_err(dev, "can not register spi master\n"); - goto err_free_peripheral; - } - - return ret; - -err_free_peripheral: - peripheral_free_list(drv_data->pin_req); -err_free_rx_dma: - free_dma(rx_dma); -err_free_tx_dma: - free_dma(tx_dma); -err_put_master: - spi_master_put(master); - - return ret; -} - -static int bfin_spi_remove(struct platform_device *pdev) -{ - struct spi_master *master = platform_get_drvdata(pdev); - struct bfin_spi_master *drv_data = spi_master_get_devdata(master); - - bfin_spi_disable(drv_data); - - peripheral_free_list(drv_data->pin_req); - free_dma(drv_data->rx_dma); - free_dma(drv_data->tx_dma); - - return 0; -} - -#ifdef CONFIG_PM -static int bfin_spi_suspend(struct device *dev) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct bfin_spi_master *drv_data = spi_master_get_devdata(master); - - spi_master_suspend(master); - - drv_data->control = bfin_read(&drv_data->regs->control); - drv_data->ssel = bfin_read(&drv_data->regs->ssel); - - bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA); - bfin_write(&drv_data->regs->ssel, 0x0000FE00); - dma_disable_irq(drv_data->rx_dma); - dma_disable_irq(drv_data->tx_dma); - - return 0; -} - -static int bfin_spi_resume(struct device *dev) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct bfin_spi_master *drv_data = spi_master_get_devdata(master); - int ret = 0; - - /* bootrom may modify spi and dma status when resume in spi boot mode */ - disable_dma(drv_data->rx_dma); - - dma_enable_irq(drv_data->rx_dma); - dma_enable_irq(drv_data->tx_dma); - bfin_write(&drv_data->regs->control, drv_data->control); - bfin_write(&drv_data->regs->ssel, drv_data->ssel); - - ret = spi_master_resume(master); - if (ret) { - free_dma(drv_data->rx_dma); - free_dma(drv_data->tx_dma); - } - - return ret; -} -#endif -static const struct dev_pm_ops bfin_spi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(bfin_spi_suspend, bfin_spi_resume) -}; - -MODULE_ALIAS("platform:bfin-spi3"); -static struct platform_driver bfin_spi_driver = { - .driver = { - .name = "bfin-spi3", - .owner = THIS_MODULE, - .pm = &bfin_spi_pm_ops, - }, - .remove = bfin_spi_remove, -}; - -module_platform_driver_probe(bfin_spi_driver, bfin_spi_probe); - -MODULE_DESCRIPTION("Analog Devices SPI3 controller driver"); -MODULE_AUTHOR("Scott Jiang "); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi/adi_spi3.h b/include/linux/spi/adi_spi3.h new file mode 100644 index 000000000000..c84123aa1d06 --- /dev/null +++ b/include/linux/spi/adi_spi3.h @@ -0,0 +1,254 @@ +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2014 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ADI_SPI3_H_ +#define _ADI_SPI3_H_ + +#include + +/* SPI_CONTROL */ +#define SPI_CTL_EN 0x00000001 /* Enable */ +#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */ +#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */ +#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */ +#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */ +#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */ +#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */ +#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in-between transfers */ +#define SPI_CTL_EMISO 0x00000100 /* Enable MISO */ +#define SPI_CTL_SIZE 0x00000600 /* Word Transfer Size */ +#define SPI_CTL_SIZE08 0x00000000 /* SIZE: 8 bits */ +#define SPI_CTL_SIZE16 0x00000200 /* SIZE: 16 bits */ +#define SPI_CTL_SIZE32 0x00000400 /* SIZE: 32 bits */ +#define SPI_CTL_LSBF 0x00001000 /* LSB First */ +#define SPI_CTL_FCEN 0x00002000 /* Flow-Control Enable */ +#define SPI_CTL_FCCH 0x00004000 /* Flow-Control Channel Selection */ +#define SPI_CTL_FCPL 0x00008000 /* Flow-Control Polarity */ +#define SPI_CTL_FCWM 0x00030000 /* Flow-Control Water-Mark */ +#define SPI_CTL_FIFO0 0x00000000 /* FCWM: TFIFO empty or RFIFO Full */ +#define SPI_CTL_FIFO1 0x00010000 /* FCWM: TFIFO 75% or more empty or RFIFO 75% or more full */ +#define SPI_CTL_FIFO2 0x00020000 /* FCWM: TFIFO 50% or more empty or RFIFO 50% or more full */ +#define SPI_CTL_FMODE 0x00040000 /* Fast-mode Enable */ +#define SPI_CTL_MIOM 0x00300000 /* Multiple I/O Mode */ +#define SPI_CTL_MIO_DIS 0x00000000 /* MIOM: Disable */ +#define SPI_CTL_MIO_DUAL 0x00100000 /* MIOM: Enable DIOM (Dual I/O Mode) */ +#define SPI_CTL_MIO_QUAD 0x00200000 /* MIOM: Enable QUAD (Quad SPI Mode) */ +#define SPI_CTL_SOSI 0x00400000 /* Start on MOSI */ +/* SPI_RX_CONTROL */ +#define SPI_RXCTL_REN 0x00000001 /* Receive Channel Enable */ +#define SPI_RXCTL_RTI 0x00000004 /* Receive Transfer Initiate */ +#define SPI_RXCTL_RWCEN 0x00000008 /* Receive Word Counter Enable */ +#define SPI_RXCTL_RDR 0x00000070 /* Receive Data Request */ +#define SPI_RXCTL_RDR_DIS 0x00000000 /* RDR: Disabled */ +#define SPI_RXCTL_RDR_NE 0x00000010 /* RDR: RFIFO not empty */ +#define SPI_RXCTL_RDR_25 0x00000020 /* RDR: RFIFO 25% full */ +#define SPI_RXCTL_RDR_50 0x00000030 /* RDR: RFIFO 50% full */ +#define SPI_RXCTL_RDR_75 0x00000040 /* RDR: RFIFO 75% full */ +#define SPI_RXCTL_RDR_FULL 0x00000050 /* RDR: RFIFO full */ +#define SPI_RXCTL_RDO 0x00000100 /* Receive Data Over-Run */ +#define SPI_RXCTL_RRWM 0x00003000 /* FIFO Regular Water-Mark */ +#define SPI_RXCTL_RWM_0 0x00000000 /* RRWM: RFIFO Empty */ +#define SPI_RXCTL_RWM_25 0x00001000 /* RRWM: RFIFO 25% full */ +#define SPI_RXCTL_RWM_50 0x00002000 /* RRWM: RFIFO 50% full */ +#define SPI_RXCTL_RWM_75 0x00003000 /* RRWM: RFIFO 75% full */ +#define SPI_RXCTL_RUWM 0x00070000 /* FIFO Urgent Water-Mark */ +#define SPI_RXCTL_UWM_DIS 0x00000000 /* RUWM: Disabled */ +#define SPI_RXCTL_UWM_25 0x00010000 /* RUWM: RFIFO 25% full */ +#define SPI_RXCTL_UWM_50 0x00020000 /* RUWM: RFIFO 50% full */ +#define SPI_RXCTL_UWM_75 0x00030000 /* RUWM: RFIFO 75% full */ +#define SPI_RXCTL_UWM_FULL 0x00040000 /* RUWM: RFIFO full */ +/* SPI_TX_CONTROL */ +#define SPI_TXCTL_TEN 0x00000001 /* Transmit Channel Enable */ +#define SPI_TXCTL_TTI 0x00000004 /* Transmit Transfer Initiate */ +#define SPI_TXCTL_TWCEN 0x00000008 /* Transmit Word Counter Enable */ +#define SPI_TXCTL_TDR 0x00000070 /* Transmit Data Request */ +#define SPI_TXCTL_TDR_DIS 0x00000000 /* TDR: Disabled */ +#define SPI_TXCTL_TDR_NF 0x00000010 /* TDR: TFIFO not full */ +#define SPI_TXCTL_TDR_25 0x00000020 /* TDR: TFIFO 25% empty */ +#define SPI_TXCTL_TDR_50 0x00000030 /* TDR: TFIFO 50% empty */ +#define SPI_TXCTL_TDR_75 0x00000040 /* TDR: TFIFO 75% empty */ +#define SPI_TXCTL_TDR_EMPTY 0x00000050 /* TDR: TFIFO empty */ +#define SPI_TXCTL_TDU 0x00000100 /* Transmit Data Under-Run */ +#define SPI_TXCTL_TRWM 0x00003000 /* FIFO Regular Water-Mark */ +#define SPI_TXCTL_RWM_FULL 0x00000000 /* TRWM: TFIFO full */ +#define SPI_TXCTL_RWM_25 0x00001000 /* TRWM: TFIFO 25% empty */ +#define SPI_TXCTL_RWM_50 0x00002000 /* TRWM: TFIFO 50% empty */ +#define SPI_TXCTL_RWM_75 0x00003000 /* TRWM: TFIFO 75% empty */ +#define SPI_TXCTL_TUWM 0x00070000 /* FIFO Urgent Water-Mark */ +#define SPI_TXCTL_UWM_DIS 0x00000000 /* TUWM: Disabled */ +#define SPI_TXCTL_UWM_25 0x00010000 /* TUWM: TFIFO 25% empty */ +#define SPI_TXCTL_UWM_50 0x00020000 /* TUWM: TFIFO 50% empty */ +#define SPI_TXCTL_UWM_75 0x00030000 /* TUWM: TFIFO 75% empty */ +#define SPI_TXCTL_UWM_EMPTY 0x00040000 /* TUWM: TFIFO empty */ +/* SPI_CLOCK */ +#define SPI_CLK_BAUD 0x0000FFFF /* Baud Rate */ +/* SPI_DELAY */ +#define SPI_DLY_STOP 0x000000FF /* Transfer delay time in multiples of SCK period */ +#define SPI_DLY_LEADX 0x00000100 /* Extended (1 SCK) LEAD Control */ +#define SPI_DLY_LAGX 0x00000200 /* Extended (1 SCK) LAG control */ +/* SPI_SSEL */ +#define SPI_SLVSEL_SSE1 0x00000002 /* SPISSEL1 Enable */ +#define SPI_SLVSEL_SSE2 0x00000004 /* SPISSEL2 Enable */ +#define SPI_SLVSEL_SSE3 0x00000008 /* SPISSEL3 Enable */ +#define SPI_SLVSEL_SSE4 0x00000010 /* SPISSEL4 Enable */ +#define SPI_SLVSEL_SSE5 0x00000020 /* SPISSEL5 Enable */ +#define SPI_SLVSEL_SSE6 0x00000040 /* SPISSEL6 Enable */ +#define SPI_SLVSEL_SSE7 0x00000080 /* SPISSEL7 Enable */ +#define SPI_SLVSEL_SSEL1 0x00000200 /* SPISSEL1 Value */ +#define SPI_SLVSEL_SSEL2 0x00000400 /* SPISSEL2 Value */ +#define SPI_SLVSEL_SSEL3 0x00000800 /* SPISSEL3 Value */ +#define SPI_SLVSEL_SSEL4 0x00001000 /* SPISSEL4 Value */ +#define SPI_SLVSEL_SSEL5 0x00002000 /* SPISSEL5 Value */ +#define SPI_SLVSEL_SSEL6 0x00004000 /* SPISSEL6 Value */ +#define SPI_SLVSEL_SSEL7 0x00008000 /* SPISSEL7 Value */ +/* SPI_RWC */ +#define SPI_RWC_VALUE 0x0000FFFF /* Received Word-Count */ +/* SPI_RWCR */ +#define SPI_RWCR_VALUE 0x0000FFFF /* Received Word-Count Reload */ +/* SPI_TWC */ +#define SPI_TWC_VALUE 0x0000FFFF /* Transmitted Word-Count */ +/* SPI_TWCR */ +#define SPI_TWCR_VALUE 0x0000FFFF /* Transmitted Word-Count Reload */ +/* SPI_IMASK */ +#define SPI_IMSK_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_IMASKCL */ +#define SPI_IMSK_CLR_RUW 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_CLR_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_CLR_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_CLR_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_CLR_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_CLR_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_CLR_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_CLR_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_IMASKST */ +#define SPI_IMSK_SET_RUWM 0x00000002 /* Receive Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_TUWM 0x00000004 /* Transmit Urgent Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_ROM 0x00000010 /* Receive Over-Run Error Interrupt Mask */ +#define SPI_IMSK_SET_TUM 0x00000020 /* Transmit Under-Run Error Interrupt Mask */ +#define SPI_IMSK_SET_TCM 0x00000040 /* Transmit Collision Error Interrupt Mask */ +#define SPI_IMSK_SET_MFM 0x00000080 /* Mode Fault Error Interrupt Mask */ +#define SPI_IMSK_SET_RSM 0x00000100 /* Receive Start Interrupt Mask */ +#define SPI_IMSK_SET_TSM 0x00000200 /* Transmit Start Interrupt Mask */ +#define SPI_IMSK_SET_RFM 0x00000400 /* Receive Finish Interrupt Mask */ +#define SPI_IMSK_SET_TFM 0x00000800 /* Transmit Finish Interrupt Mask */ +/* SPI_STATUS */ +#define SPI_STAT_SPIF 0x00000001 /* SPI Finished */ +#define SPI_STAT_RUWM 0x00000002 /* Receive Urgent Water-Mark Breached */ +#define SPI_STAT_TUWM 0x00000004 /* Transmit Urgent Water-Mark Breached */ +#define SPI_STAT_ROE 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_STAT_TUE 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_STAT_TCE 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_STAT_MODF 0x00000080 /* Mode Fault Error Indication */ +#define SPI_STAT_RS 0x00000100 /* Receive Start Indication */ +#define SPI_STAT_TS 0x00000200 /* Transmit Start Indication */ +#define SPI_STAT_RF 0x00000400 /* Receive Finish Indication */ +#define SPI_STAT_TF 0x00000800 /* Transmit Finish Indication */ +#define SPI_STAT_RFS 0x00007000 /* SPI_RFIFO status */ +#define SPI_STAT_RFIFO_EMPTY 0x00000000 /* RFS: RFIFO Empty */ +#define SPI_STAT_RFIFO_25 0x00001000 /* RFS: RFIFO 25% Full */ +#define SPI_STAT_RFIFO_50 0x00002000 /* RFS: RFIFO 50% Full */ +#define SPI_STAT_RFIFO_75 0x00003000 /* RFS: RFIFO 75% Full */ +#define SPI_STAT_RFIFO_FULL 0x00004000 /* RFS: RFIFO Full */ +#define SPI_STAT_TFS 0x00070000 /* SPI_TFIFO status */ +#define SPI_STAT_TFIFO_FULL 0x00000000 /* TFS: TFIFO full */ +#define SPI_STAT_TFIFO_25 0x00010000 /* TFS: TFIFO 25% empty */ +#define SPI_STAT_TFIFO_50 0x00020000 /* TFS: TFIFO 50% empty */ +#define SPI_STAT_TFIFO_75 0x00030000 /* TFS: TFIFO 75% empty */ +#define SPI_STAT_TFIFO_EMPTY 0x00040000 /* TFS: TFIFO empty */ +#define SPI_STAT_FCS 0x00100000 /* Flow-Control Stall Indication */ +#define SPI_STAT_RFE 0x00400000 /* SPI_RFIFO Empty */ +#define SPI_STAT_TFF 0x00800000 /* SPI_TFIFO Full */ +/* SPI_ILAT */ +#define SPI_ILAT_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ +#define SPI_ILAT_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ +#define SPI_ILAT_ROI 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_ILAT_TUI 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_ILAT_TCI 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_ILAT_MFI 0x00000080 /* Mode Fault Error Indication */ +#define SPI_ILAT_RSI 0x00000100 /* Receive Start Indication */ +#define SPI_ILAT_TSI 0x00000200 /* Transmit Start Indication */ +#define SPI_ILAT_RFI 0x00000400 /* Receive Finish Indication */ +#define SPI_ILAT_TFI 0x00000800 /* Transmit Finish Indication */ +/* SPI_ILATCL */ +#define SPI_ILAT_CLR_RUWMI 0x00000002 /* Receive Urgent Water Mark Interrupt */ +#define SPI_ILAT_CLR_TUWMI 0x00000004 /* Transmit Urgent Water Mark Interrupt */ +#define SPI_ILAT_CLR_ROI 0x00000010 /* Receive Over-Run Error Indication */ +#define SPI_ILAT_CLR_TUI 0x00000020 /* Transmit Under-Run Error Indication */ +#define SPI_ILAT_CLR_TCI 0x00000040 /* Transmit Collision Error Indication */ +#define SPI_ILAT_CLR_MFI 0x00000080 /* Mode Fault Error Indication */ +#define SPI_ILAT_CLR_RSI 0x00000100 /* Receive Start Indication */ +#define SPI_ILAT_CLR_TSI 0x00000200 /* Transmit Start Indication */ +#define SPI_ILAT_CLR_RFI 0x00000400 /* Receive Finish Indication */ +#define SPI_ILAT_CLR_TFI 0x00000800 /* Transmit Finish Indication */ + +/* + * adi spi3 registers layout + */ +struct adi_spi_regs { + u32 revid; + u32 control; + u32 rx_control; + u32 tx_control; + u32 clock; + u32 delay; + u32 ssel; + u32 rwc; + u32 rwcr; + u32 twc; + u32 twcr; + u32 reserved0; + u32 emask; + u32 emaskcl; + u32 emaskst; + u32 reserved1; + u32 status; + u32 elat; + u32 elatcl; + u32 reserved2; + u32 rfifo; + u32 reserved3; + u32 tfifo; +}; + +#define MAX_CTRL_CS 8 /* cs in spi controller */ + +/* device.platform_data for SSP controller devices */ +struct adi_spi3_master { + u16 num_chipselect; + u16 pin_req[7]; +}; + +/* spi_board_info.controller_data for SPI slave devices, + * copied to spi_device.platform_data ... mostly for dma tuning + */ +struct adi_spi3_chip { + u32 control; + u16 cs_chg_udelay; /* Some devices require 16-bit delays */ + u32 tx_dummy_val; /* tx value for rx only transfer */ + bool enable_dma; +}; + +#endif /* _ADI_SPI3_H_ */ -- cgit v1.2.3 From e885cd805fc6e65ef5150a211c7bac02f925af04 Mon Sep 17 00:00:00 2001 From: Mark Salter Date: Fri, 10 Jan 2014 14:26:06 -0500 Subject: efi: create memory map iteration helper There are a lot of places in the kernel which iterate through an EFI memory map. Most of these places use essentially the same for-loop code. This patch adds a for_each_efi_memory_desc() helper to clean up all of the existing duplicate code and avoid more in the future. Signed-off-by: Mark Salter Signed-off-by: Leif Lindholm Signed-off-by: Matt Fleming --- include/linux/efi.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/efi.h b/include/linux/efi.h index 6c100ff0cae4..82d0abb2b19f 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -863,6 +863,12 @@ extern int efi_set_rtc_mmss(const struct timespec *now); extern void efi_reserve_boot_services(void); extern struct efi_memory_map memmap; +/* Iterate through an efi_memory_map */ +#define for_each_efi_memory_desc(m, md) \ + for ((md) = (m)->map; \ + (md) <= (efi_memory_desc_t *)((m)->map_end - (m)->desc_size); \ + (md) = (void *)(md) + (m)->desc_size) + /** * efi_range_is_wc - check the WC bit on an address range * @start: starting kvirt address -- cgit v1.2.3 From 61fbb1d278cf09f29d67629b139b3ca08acbf177 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Wed, 19 Feb 2014 09:25:49 +0900 Subject: clk: samsung: add clock-driver for s3c2416, s3c2443 and s3c2450 The three SoCs share a common clock tree which only differs in the existence of some special clocks. As with all parts common to these three SoCs the driver is named after the s3c2443, as it was the first SoC introducing this structure and there exists no other label to describe this s3c24xx epoch. The clock structure is built according to the manuals of the included SoCs and might include changes in comparison to the previous clock structure. As an example the sclk_uart gate was never handled previously and the div_uart was made to be the clock used by the serial driver. Signed-off-by: Heiko Stuebner Acked-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c24xx/Kconfig | 6 + drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-s3c2443.c | 462 ++++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/s3c2443.h | 92 +++++++ 4 files changed, 561 insertions(+) create mode 100644 drivers/clk/samsung/clk-s3c2443.c create mode 100644 include/dt-bindings/clock/s3c2443.h (limited to 'include') diff --git a/arch/arm/mach-s3c24xx/Kconfig b/arch/arm/mach-s3c24xx/Kconfig index 40cf50b9940c..abf9179c9d03 100644 --- a/arch/arm/mach-s3c24xx/Kconfig +++ b/arch/arm/mach-s3c24xx/Kconfig @@ -647,6 +647,12 @@ config S3C2443_COMMON Common code for the S3C2443 and similar processors, which includes the S3C2416 and S3C2450. +config S3C2443_COMMON_CLK + bool + help + Temporary symbol to build the clock driver based on the common clock + framework. + config S3C2443_DMA bool help diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 8eb4799237f0..4c892c63a8dd 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c new file mode 100644 index 000000000000..8e4f4517e95c --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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. + * + * Common Clock Framework support for S3C2443 and following SoCs. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +/* S3C2416 clock controller register offsets */ +#define LOCKCON0 0x00 +#define LOCKCON1 0x04 +#define MPLLCON 0x10 +#define EPLLCON 0x18 +#define EPLLCON_K 0x1C +#define CLKSRC 0x20 +#define CLKDIV0 0x24 +#define CLKDIV1 0x28 +#define CLKDIV2 0x2C +#define HCLKCON 0x30 +#define PCLKCON 0x34 +#define SCLKCON 0x38 + +/* the soc types */ +enum supported_socs { + S3C2416, + S3C2443, + S3C2450, +}; + +/* list of PLLs to be registered */ +enum s3c2443_plls { + mpll, epll, +}; + +static void __iomem *reg_base; + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *s3c2443_save; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static unsigned long s3c2443_clk_regs[] __initdata = { + LOCKCON0, + LOCKCON1, + MPLLCON, + EPLLCON, + EPLLCON_K, + CLKSRC, + CLKDIV0, + CLKDIV1, + CLKDIV2, + PCLKCON, + HCLKCON, + SCLKCON, +}; + +static int s3c2443_clk_suspend(void) +{ + samsung_clk_save(reg_base, s3c2443_save, + ARRAY_SIZE(s3c2443_clk_regs)); + + return 0; +} + +static void s3c2443_clk_resume(void) +{ + samsung_clk_restore(reg_base, s3c2443_save, + ARRAY_SIZE(s3c2443_clk_regs)); +} + +static struct syscore_ops s3c2443_clk_syscore_ops = { + .suspend = s3c2443_clk_suspend, + .resume = s3c2443_clk_resume, +}; + +static void s3c2443_clk_sleep_init(void) +{ + s3c2443_save = samsung_clk_alloc_reg_dump(s3c2443_clk_regs, + ARRAY_SIZE(s3c2443_clk_regs)); + if (!s3c2443_save) { + pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", + __func__); + return; + } + + register_syscore_ops(&s3c2443_clk_syscore_ops); + return; +} +#else +static void s3c2443_clk_sleep_init(void) {} +#endif + +PNAME(epllref_p) = { "mpllref", "mpllref", "xti", "ext" }; +PNAME(esysclk_p) = { "epllref", "epll" }; +PNAME(mpllref_p) = { "xti", "mdivclk" }; +PNAME(msysclk_p) = { "mpllref", "mpll" }; +PNAME(armclk_p) = { "armdiv" , "hclk" }; +PNAME(i2s0_p) = { "div_i2s0", "ext_i2s", "epllref", "epllref" }; + +struct samsung_mux_clock s3c2443_common_muxes[] __initdata = { + MUX(0, "epllref", epllref_p, CLKSRC, 7, 2), + MUX(ESYSCLK, "esysclk", esysclk_p, CLKSRC, 6, 1), + MUX(0, "mpllref", mpllref_p, CLKSRC, 3, 1), + MUX_A(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1, "msysclk"), + MUX_A(ARMCLK, "armclk", armclk_p, CLKDIV0, 13, 1, "armclk"), + MUX(0, "mux_i2s0", i2s0_p, CLKSRC, 14, 2), +}; + +static struct clk_div_table hclk_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 3, .div = 4 }, + { /* sentinel */ }, +}; + +static struct clk_div_table mdivclk_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 3 }, + { .val = 2, .div = 5 }, + { .val = 3, .div = 7 }, + { .val = 4, .div = 9 }, + { .val = 5, .div = 11 }, + { .val = 6, .div = 13 }, + { .val = 7, .div = 15 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2443_common_dividers[] __initdata = { + DIV_T(0, "mdivclk", "xti", CLKDIV0, 6, 3, mdivclk_d), + DIV(0, "prediv", "msysclk", CLKDIV0, 4, 2), + DIV_T(HCLK, "hclk", "prediv", CLKDIV0, 0, 2, hclk_d), + DIV(PCLK, "pclk", "hclk", CLKDIV0, 2, 1), + DIV(0, "div_hsspi0_epll", "esysclk", CLKDIV1, 24, 2), + DIV(0, "div_fimd", "esysclk", CLKDIV1, 16, 8), + DIV(0, "div_i2s0", "esysclk", CLKDIV1, 12, 4), + DIV(0, "div_uart", "esysclk", CLKDIV1, 8, 4), + DIV(0, "div_hsmmc1", "esysclk", CLKDIV1, 6, 2), + DIV(0, "div_usbhost", "esysclk", CLKDIV1, 4, 2), +}; + +struct samsung_gate_clock s3c2443_common_gates[] __initdata = { + GATE(SCLK_HSMMC_EXT, "sclk_hsmmcext", "ext", SCLKCON, 13, 0, 0), + GATE(SCLK_HSMMC1, "sclk_hsmmc1", "div_hsmmc1", SCLKCON, 12, 0, 0), + GATE(SCLK_FIMD, "sclk_fimd", "div_fimd", SCLKCON, 10, 0, 0), + GATE(SCLK_I2S0, "sclk_i2s0", "mux_i2s0", SCLKCON, 9, 0, 0), + GATE(SCLK_UART, "sclk_uart", "div_uart", SCLKCON, 8, 0, 0), + GATE(SCLK_USBH, "sclk_usbhost", "div_usbhost", SCLKCON, 1, 0, 0), + GATE(HCLK_DRAM, "dram", "hclk", HCLKCON, 19, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_SSMC, "ssmc", "hclk", HCLKCON, 18, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_HSMMC1, "hsmmc1", "hclk", HCLKCON, 16, 0, 0), + GATE(HCLK_USBD, "usb-device", "hclk", HCLKCON, 12, 0, 0), + GATE(HCLK_USBH, "usb-host", "hclk", HCLKCON, 11, 0, 0), + GATE(HCLK_LCD, "lcd", "hclk", HCLKCON, 9, 0, 0), + GATE(HCLK_DMA5, "dma5", "hclk", HCLKCON, 5, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA4, "dma4", "hclk", HCLKCON, 4, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA3, "dma3", "hclk", HCLKCON, 3, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA2, "dma2", "hclk", HCLKCON, 2, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA1, "dma1", "hclk", HCLKCON, 1, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA0, "dma0", "hclk", HCLKCON, 0, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_GPIO, "gpio", "pclk", PCLKCON, 13, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_RTC, "rtc", "pclk", PCLKCON, 12, 0, 0), + GATE(PCLK_WDT, "wdt", "pclk", PCLKCON, 11, 0, 0), + GATE(PCLK_PWM, "pwm", "pclk", PCLKCON, 10, 0, 0), + GATE(PCLK_I2S0, "i2s0", "pclk", PCLKCON, 9, 0, 0), + GATE(PCLK_AC97, "ac97", "pclk", PCLKCON, 8, 0, 0), + GATE(PCLK_ADC, "adc", "pclk", PCLKCON, 7, 0, 0), + GATE(PCLK_SPI0, "spi0", "pclk", PCLKCON, 6, 0, 0), + GATE(PCLK_I2C0, "i2c0", "pclk", PCLKCON, 4, 0, 0), + GATE(PCLK_UART3, "uart3", "pclk", PCLKCON, 3, 0, 0), + GATE(PCLK_UART2, "uart2", "pclk", PCLKCON, 2, 0, 0), + GATE(PCLK_UART1, "uart1", "pclk", PCLKCON, 1, 0, 0), + GATE(PCLK_UART0, "uart0", "pclk", PCLKCON, 0, 0, 0), +}; + +struct samsung_clock_alias s3c2443_common_aliases[] __initdata = { + ALIAS(HCLK, NULL, "hclk"), + ALIAS(HCLK_SSMC, NULL, "nand"), + ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"), + ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"), + ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"), + ALIAS(PCLK_UART3, "s3c2440-uart.3", "uart"), + ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"), + ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"), + ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"), + ALIAS(PCLK_UART3, "s3c2440-uart.3", "clk_uart_baud2"), + ALIAS(SCLK_UART, NULL, "clk_uart_baud3"), + ALIAS(PCLK_PWM, NULL, "timers"), + ALIAS(PCLK_RTC, NULL, "rtc"), + ALIAS(PCLK_WDT, NULL, "watchdog"), + ALIAS(PCLK_ADC, NULL, "adc"), + ALIAS(PCLK_I2C0, "s3c2410-i2c.0", "i2c"), + ALIAS(HCLK_USBD, NULL, "usb-device"), + ALIAS(HCLK_USBH, NULL, "usb-host"), + ALIAS(SCLK_USBH, NULL, "usb-bus-host"), + ALIAS(PCLK_SPI0, "s3c2443-spi.0", "spi"), + ALIAS(PCLK_SPI0, "s3c2443-spi.0", "spi_busclk0"), + ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "hsmmc"), + ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"), + ALIAS(PCLK_I2S0, "samsung-i2s.0", "iis"), + ALIAS(SCLK_I2S0, NULL, "i2s-if"), + ALIAS(HCLK_LCD, NULL, "lcd"), + ALIAS(SCLK_FIMD, NULL, "sclk_fimd"), +}; + +/* S3C2416 specific clocks */ + +static struct samsung_pll_clock s3c2416_pll_clks[] __initdata = { + [mpll] = PLL(pll_6552_s3c2416, 0, "mpll", "mpllref", + LOCKCON0, MPLLCON, NULL), + [epll] = PLL(pll_6553, 0, "epll", "epllref", + LOCKCON1, EPLLCON, NULL), +}; + +PNAME(s3c2416_hsmmc0_p) = { "sclk_hsmmc0", "sclk_hsmmcext" }; +PNAME(s3c2416_hsmmc1_p) = { "sclk_hsmmc1", "sclk_hsmmcext" }; +PNAME(s3c2416_hsspi0_p) = { "hsspi0_epll", "hsspi0_mpll" }; + +static struct clk_div_table armdiv_s3c2416_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 5, .div = 6 }, + { .val = 7, .div = 8 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2416_dividers[] __initdata = { + DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 3, armdiv_s3c2416_d), + DIV(0, "div_hsspi0_mpll", "msysclk", CLKDIV2, 0, 4), + DIV(0, "div_hsmmc0", "esysclk", CLKDIV2, 6, 2), +}; + +struct samsung_mux_clock s3c2416_muxes[] __initdata = { + MUX(MUX_HSMMC0, "mux_hsmmc0", s3c2416_hsmmc0_p, CLKSRC, 16, 1), + MUX(MUX_HSMMC1, "mux_hsmmc1", s3c2416_hsmmc1_p, CLKSRC, 17, 1), + MUX(MUX_HSSPI0, "mux_hsspi0", s3c2416_hsspi0_p, CLKSRC, 18, 1), +}; + +struct samsung_gate_clock s3c2416_gates[] __initdata = { + GATE(0, "hsspi0_mpll", "div_hsspi0_mpll", SCLKCON, 19, 0, 0), + GATE(0, "hsspi0_epll", "div_hsspi0_epll", SCLKCON, 14, 0, 0), + GATE(0, "sclk_hsmmc0", "div_hsmmc0", SCLKCON, 6, 0, 0), + GATE(HCLK_2D, "2d", "hclk", HCLKCON, 20, 0, 0), + GATE(HCLK_HSMMC0, "hsmmc0", "hclk", HCLKCON, 15, 0, 0), + GATE(HCLK_IROM, "irom", "hclk", HCLKCON, 13, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_PCM, "pcm", "pclk", PCLKCON, 19, 0, 0), +}; + +struct samsung_clock_alias s3c2416_aliases[] __initdata = { + ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"), + ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"), + ALIAS(MUX_HSMMC0, "s3c-sdhci.0", "mmc_busclk.2"), + ALIAS(MUX_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"), + ALIAS(MUX_HSSPI0, "s3c2443-spi.0", "spi_busclk2"), + ALIAS(ARMDIV, NULL, "armdiv"), +}; + +/* S3C2443 specific clocks */ + +static struct samsung_pll_clock s3c2443_pll_clks[] __initdata = { + [mpll] = PLL(pll_3000, 0, "mpll", "mpllref", + LOCKCON0, MPLLCON, NULL), + [epll] = PLL(pll_2126, 0, "epll", "epllref", + LOCKCON1, EPLLCON, NULL), +}; + +static struct clk_div_table armdiv_s3c2443_d[] = { + { .val = 0, .div = 1 }, + { .val = 8, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 9, .div = 4 }, + { .val = 10, .div = 6 }, + { .val = 11, .div = 8 }, + { .val = 13, .div = 12 }, + { .val = 15, .div = 16 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2443_dividers[] __initdata = { + DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 4, armdiv_s3c2443_d), + DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4), +}; + +struct samsung_gate_clock s3c2443_gates[] __initdata = { + GATE(SCLK_HSSPI0, "sclk_hsspi0", "div_hsspi0_epll", SCLKCON, 14, 0, 0), + GATE(SCLK_CAM, "sclk_cam", "div_cam", SCLKCON, 11, 0, 0), + GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0), + GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 15, 0, 0), + GATE(PCLK_SDI, "sdi", "pclk", PCLKCON, 5, 0, 0), +}; + +struct samsung_clock_alias s3c2443_aliases[] __initdata = { + ALIAS(SCLK_HSSPI0, "s3c2443-spi.0", "spi_busclk2"), + ALIAS(SCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"), + ALIAS(SCLK_CAM, NULL, "camif-upll"), + ALIAS(PCLK_SPI1, "s3c2410-spi.0", "spi"), + ALIAS(PCLK_SDI, NULL, "sdi"), + ALIAS(HCLK_CFC, NULL, "cfc"), + ALIAS(ARMDIV, NULL, "armdiv"), +}; + +/* S3C2450 specific clocks */ + +PNAME(s3c2450_cam_p) = { "div_cam", "hclk" }; +PNAME(s3c2450_hsspi1_p) = { "hsspi1_epll", "hsspi1_mpll" }; +PNAME(i2s1_p) = { "div_i2s1", "ext_i2s", "epllref", "epllref" }; + +struct samsung_div_clock s3c2450_dividers[] __initdata = { + DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4), + DIV(0, "div_hsspi1_epll", "esysclk", CLKDIV2, 24, 2), + DIV(0, "div_hsspi1_mpll", "msysclk", CLKDIV2, 16, 4), + DIV(0, "div_i2s1", "esysclk", CLKDIV2, 12, 4), +}; + +struct samsung_mux_clock s3c2450_muxes[] __initdata = { + MUX(0, "mux_cam", s3c2450_cam_p, CLKSRC, 20, 1), + MUX(MUX_HSSPI1, "mux_hsspi1", s3c2450_hsspi1_p, CLKSRC, 19, 1), + MUX(0, "mux_i2s1", i2s1_p, CLKSRC, 12, 2), +}; + +struct samsung_gate_clock s3c2450_gates[] __initdata = { + GATE(SCLK_I2S1, "sclk_i2s1", "div_i2s1", SCLKCON, 5, 0, 0), + GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, 0, 0), + GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0), + GATE(HCLK_DMA7, "dma7", "hclk", HCLKCON, 7, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA6, "dma6", "hclk", HCLKCON, 6, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_I2S1, "i2s1", "pclk", PCLKCON, 17, 0, 0), + GATE(PCLK_I2C1, "i2c1", "pclk", PCLKCON, 16, 0, 0), + GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 14, 0, 0), +}; + +struct samsung_clock_alias s3c2450_aliases[] __initdata = { + ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi"), + ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi_busclk0"), + ALIAS(MUX_HSSPI1, "s3c2443-spi.1", "spi_busclk2"), + ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"), +}; + +/* + * fixed rate clocks generated outside the soc + * Only necessary until the devicetree-move is complete + */ +struct samsung_fixed_rate_clock s3c2443_common_frate_clks[] __initdata = { + FRATE(0, "xti", NULL, CLK_IS_ROOT, 0), + FRATE(0, "ext", NULL, CLK_IS_ROOT, 0), + FRATE(0, "ext_i2s", NULL, CLK_IS_ROOT, 0), + FRATE(0, "ext_uart", NULL, CLK_IS_ROOT, 0), +}; + +static void __init s3c2443_common_clk_register_fixed_ext(unsigned long xti_f) +{ + s3c2443_common_frate_clks[0].fixed_rate = xti_f; + samsung_clk_register_fixed_rate(s3c2443_common_frate_clks, + ARRAY_SIZE(s3c2443_common_frate_clks)); +} + +void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, + int current_soc, + void __iomem *base) +{ + reg_base = base; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + samsung_clk_init(np, reg_base, NR_CLKS); + + /* Register external clocks only in non-dt cases */ + if (!np) + s3c2443_common_clk_register_fixed_ext(xti_f); + + /* Register PLLs. */ + if (current_soc == S3C2416 || current_soc == S3C2450) + samsung_clk_register_pll(s3c2416_pll_clks, + ARRAY_SIZE(s3c2416_pll_clks), reg_base); + else + samsung_clk_register_pll(s3c2443_pll_clks, + ARRAY_SIZE(s3c2443_pll_clks), reg_base); + + /* Register common internal clocks. */ + samsung_clk_register_mux(s3c2443_common_muxes, + ARRAY_SIZE(s3c2443_common_muxes)); + samsung_clk_register_div(s3c2443_common_dividers, + ARRAY_SIZE(s3c2443_common_dividers)); + samsung_clk_register_gate(s3c2443_common_gates, + ARRAY_SIZE(s3c2443_common_gates)); + samsung_clk_register_alias(s3c2443_common_aliases, + ARRAY_SIZE(s3c2443_common_aliases)); + + /* Register SoC-specific clocks. */ + switch (current_soc) { + case S3C2450: + samsung_clk_register_div(s3c2450_dividers, + ARRAY_SIZE(s3c2450_dividers)); + samsung_clk_register_mux(s3c2450_muxes, + ARRAY_SIZE(s3c2450_muxes)); + samsung_clk_register_gate(s3c2450_gates, + ARRAY_SIZE(s3c2450_gates)); + samsung_clk_register_alias(s3c2450_aliases, + ARRAY_SIZE(s3c2450_aliases)); + /* fall through, as s3c2450 extends the s3c2416 clocks */ + case S3C2416: + samsung_clk_register_div(s3c2416_dividers, + ARRAY_SIZE(s3c2416_dividers)); + samsung_clk_register_mux(s3c2416_muxes, + ARRAY_SIZE(s3c2416_muxes)); + samsung_clk_register_gate(s3c2416_gates, + ARRAY_SIZE(s3c2416_gates)); + samsung_clk_register_alias(s3c2416_aliases, + ARRAY_SIZE(s3c2416_aliases)); + break; + case S3C2443: + samsung_clk_register_div(s3c2443_dividers, + ARRAY_SIZE(s3c2443_dividers)); + samsung_clk_register_gate(s3c2443_gates, + ARRAY_SIZE(s3c2443_gates)); + samsung_clk_register_alias(s3c2443_aliases, + ARRAY_SIZE(s3c2443_aliases)); + break; + } + + s3c2443_clk_sleep_init(); +} + +static void __init s3c2416_clk_init(struct device_node *np) +{ + s3c2443_common_clk_init(np, 0, S3C2416, 0); +} +CLK_OF_DECLARE(s3c2416_clk, "samsung,s3c2416-clock", s3c2416_clk_init); + +static void __init s3c2443_clk_init(struct device_node *np) +{ + s3c2443_common_clk_init(np, 0, S3C2443, 0); +} +CLK_OF_DECLARE(s3c2443_clk, "samsung,s3c2443-clock", s3c2443_clk_init); + +static void __init s3c2450_clk_init(struct device_node *np) +{ + s3c2443_common_clk_init(np, 0, S3C2450, 0); +} +CLK_OF_DECLARE(s3c2450_clk, "samsung,s3c2450-clock", s3c2450_clk_init); diff --git a/include/dt-bindings/clock/s3c2443.h b/include/dt-bindings/clock/s3c2443.h new file mode 100644 index 000000000000..37e66b054d64 --- /dev/null +++ b/include/dt-bindings/clock/s3c2443.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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. + * + * Device Tree binding constants clock controllers of Samsung S3C2443 and later. + */ + +#ifndef _DT_BINDINGS_CLOCK_SAMSUNG_S3C2443_CLOCK_H +#define _DT_BINDINGS_CLOCK_SAMSUNG_S3C2443_CLOCK_H + +/* + * Let each exported clock get a unique index, which is used on DT-enabled + * platforms to lookup the clock from a clock specifier. These indices are + * therefore considered an ABI and so must not be changed. This implies + * that new clocks should be added either in free spaces between clock groups + * or at the end. + */ + +/* Core clocks. */ +#define MSYSCLK 1 +#define ESYSCLK 2 +#define ARMDIV 3 +#define ARMCLK 4 +#define HCLK 5 +#define PCLK 6 + +/* Special clocks */ +#define SCLK_HSSPI0 16 +#define SCLK_FIMD 17 +#define SCLK_I2S0 18 +#define SCLK_I2S1 19 +#define SCLK_HSMMC1 20 +#define SCLK_HSMMC_EXT 21 +#define SCLK_CAM 22 +#define SCLK_UART 23 +#define SCLK_USBH 24 + +/* Muxes */ +#define MUX_HSSPI0 32 +#define MUX_HSSPI1 33 +#define MUX_HSMMC0 34 +#define MUX_HSMMC1 35 + +/* hclk-gates */ +#define HCLK_DMA0 48 +#define HCLK_DMA1 49 +#define HCLK_DMA2 50 +#define HCLK_DMA3 51 +#define HCLK_DMA4 52 +#define HCLK_DMA5 53 +#define HCLK_DMA6 54 +#define HCLK_DMA7 55 +#define HCLK_CAM 56 +#define HCLK_LCD 57 +#define HCLK_USBH 58 +#define HCLK_USBD 59 +#define HCLK_IROM 60 +#define HCLK_HSMMC0 61 +#define HCLK_HSMMC1 62 +#define HCLK_CFC 63 +#define HCLK_SSMC 64 +#define HCLK_DRAM 65 +#define HCLK_2D 66 + +/* pclk-gates */ +#define PCLK_UART0 72 +#define PCLK_UART1 73 +#define PCLK_UART2 74 +#define PCLK_UART3 75 +#define PCLK_I2C0 76 +#define PCLK_SDI 77 +#define PCLK_SPI0 78 +#define PCLK_ADC 79 +#define PCLK_AC97 80 +#define PCLK_I2S0 81 +#define PCLK_PWM 82 +#define PCLK_WDT 83 +#define PCLK_RTC 84 +#define PCLK_GPIO 85 +#define PCLK_SPI1 86 +#define PCLK_CHIPID 87 +#define PCLK_I2C1 88 +#define PCLK_I2S1 89 +#define PCLK_PCM 90 + +/* Total number of clocks. */ +#define NR_CLKS (PCLK_PCM + 1) + +#endif /* _DT_BINDINGS_CLOCK_SAMSUNG_S3C2443_CLOCK_H */ -- cgit v1.2.3 From ca2e90ac1809c49c2306df4e23e17dad67c785b6 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 25 Feb 2014 09:50:44 +0900 Subject: clk: samsung: add clock controller driver for s3c2412 This driver can handle the clock controller in the s3c2412 soc. The clock structure is built according to the manuals of the included SoCs and might include changes in comparison to the previous clock structure. Signed-off-by: Heiko Stuebner Reviewed-by: Tomasz Figa Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-s3c2412.c | 269 ++++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/s3c2412.h | 73 ++++++++++ 3 files changed, 343 insertions(+) create mode 100644 drivers/clk/samsung/clk-s3c2412.c create mode 100644 include/dt-bindings/clock/s3c2412.h (limited to 'include') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 4c892c63a8dd..77313e27bc39 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c new file mode 100644 index 000000000000..0f11a07d5c01 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2412.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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. + * + * Common Clock Framework support for S3C2412 and S3C2413. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +#define LOCKTIME 0x00 +#define MPLLCON 0x04 +#define UPLLCON 0x08 +#define CLKCON 0x0c +#define CLKDIVN 0x14 +#define CLKSRC 0x1c + +/* list of PLLs to be registered */ +enum s3c2412_plls { + mpll, upll, +}; + +static void __iomem *reg_base; + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *s3c2412_save; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static unsigned long s3c2412_clk_regs[] __initdata = { + LOCKTIME, + MPLLCON, + UPLLCON, + CLKCON, + CLKDIVN, + CLKSRC, +}; + +static int s3c2412_clk_suspend(void) +{ + samsung_clk_save(reg_base, s3c2412_save, + ARRAY_SIZE(s3c2412_clk_regs)); + + return 0; +} + +static void s3c2412_clk_resume(void) +{ + samsung_clk_restore(reg_base, s3c2412_save, + ARRAY_SIZE(s3c2412_clk_regs)); +} + +static struct syscore_ops s3c2412_clk_syscore_ops = { + .suspend = s3c2412_clk_suspend, + .resume = s3c2412_clk_resume, +}; + +static void s3c2412_clk_sleep_init(void) +{ + s3c2412_save = samsung_clk_alloc_reg_dump(s3c2412_clk_regs, + ARRAY_SIZE(s3c2412_clk_regs)); + if (!s3c2412_save) { + pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", + __func__); + return; + } + + register_syscore_ops(&s3c2412_clk_syscore_ops); + return; +} +#else +static void s3c2412_clk_sleep_init(void) {} +#endif + +static struct clk_div_table divxti_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2412_dividers[] __initdata = { + DIV_T(0, "div_xti", "xti", CLKSRC, 0, 3, divxti_d), + DIV(0, "div_cam", "mux_cam", CLKDIVN, 16, 4), + DIV(0, "div_i2s", "mux_i2s", CLKDIVN, 12, 4), + DIV(0, "div_uart", "mux_uart", CLKDIVN, 8, 4), + DIV(0, "div_usb", "mux_usb", CLKDIVN, 6, 1), + DIV(0, "div_hclk_half", "hclk", CLKDIVN, 5, 1), + DIV(ARMDIV, "armdiv", "msysclk", CLKDIVN, 3, 1), + DIV(PCLK, "pclk", "hclk", CLKDIVN, 2, 1), + DIV(HCLK, "hclk", "armdiv", CLKDIVN, 0, 2), +}; + +struct samsung_fixed_factor_clock s3c2412_ffactor[] __initdata = { + FFACTOR(0, "ff_hclk", "hclk", 2, 1, CLK_SET_RATE_PARENT), +}; + +/* + * The first two use the OM[4] setting, which is not readable from + * software, so assume it is set to xti. + */ +PNAME(erefclk_p) = { "xti", "xti", "xti", "ext" }; +PNAME(urefclk_p) = { "xti", "xti", "xti", "ext" }; + +PNAME(camclk_p) = { "usysclk", "hclk" }; +PNAME(usbclk_p) = { "usysclk", "hclk" }; +PNAME(i2sclk_p) = { "erefclk", "mpll" }; +PNAME(uartclk_p) = { "erefclk", "mpll" }; +PNAME(usysclk_p) = { "urefclk", "upll" }; +PNAME(msysclk_p) = { "mdivclk", "mpll" }; +PNAME(mdivclk_p) = { "xti", "div_xti" }; +PNAME(armclk_p) = { "armdiv", "hclk" }; + +struct samsung_mux_clock s3c2412_muxes[] __initdata = { + MUX(0, "erefclk", erefclk_p, CLKSRC, 14, 2), + MUX(0, "urefclk", urefclk_p, CLKSRC, 12, 2), + MUX(0, "mux_cam", camclk_p, CLKSRC, 11, 1), + MUX(0, "mux_usb", usbclk_p, CLKSRC, 10, 1), + MUX(0, "mux_i2s", i2sclk_p, CLKSRC, 9, 1), + MUX(0, "mux_uart", uartclk_p, CLKSRC, 8, 1), + MUX(USYSCLK, "usysclk", usysclk_p, CLKSRC, 5, 1), + MUX(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1), + MUX(MDIVCLK, "mdivclk", mdivclk_p, CLKSRC, 3, 1), + MUX(ARMCLK, "armclk", armclk_p, CLKDIVN, 4, 1), +}; + +static struct samsung_pll_clock s3c2412_plls[] __initdata = { + [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", + LOCKTIME, MPLLCON, NULL), + [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "urefclk", + LOCKTIME, UPLLCON, NULL), +}; + +struct samsung_gate_clock s3c2412_gates[] __initdata = { + GATE(PCLK_WDT, "wdt", "pclk", CLKCON, 28, 0, 0), + GATE(PCLK_SPI, "spi", "pclk", CLKCON, 27, 0, 0), + GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 26, 0, 0), + GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 25, 0, 0), + GATE(PCLK_ADC, "adc", "pclk", CLKCON, 24, 0, 0), + GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 23, 0, 0), + GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 22, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 21, 0, 0), + GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 20, 0, 0), + GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 19, 0, 0), + GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 18, 0, 0), + GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 17, 0, 0), + GATE(PCLK_USBD, "usb-device", "pclk", CLKCON, 16, 0, 0), + GATE(SCLK_CAM, "sclk_cam", "div_cam", CLKCON, 15, 0, 0), + GATE(SCLK_UART, "sclk_uart", "div_uart", CLKCON, 14, 0, 0), + GATE(SCLK_I2S, "sclk_i2s", "div_i2s", CLKCON, 13, 0, 0), + GATE(SCLK_USBH, "sclk_usbh", "div_usb", CLKCON, 12, 0, 0), + GATE(SCLK_USBD, "sclk_usbd", "div_usb", CLKCON, 11, 0, 0), + GATE(HCLK_HALF, "hclk_half", "div_hclk_half", CLKCON, 10, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_X2, "hclkx2", "ff_hclk", CLKCON, 9, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_SDRAM, "sdram", "hclk", CLKCON, 8, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0), + GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0), + GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0), + GATE(HCLK_DMA3, "dma3", "hclk", CLKCON, 3, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA2, "dma2", "hclk", CLKCON, 2, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA1, "dma1", "hclk", CLKCON, 1, CLK_IGNORE_UNUSED, 0), + GATE(HCLK_DMA0, "dma0", "hclk", CLKCON, 0, CLK_IGNORE_UNUSED, 0), +}; + +struct samsung_clock_alias s3c2412_aliases[] __initdata = { + ALIAS(PCLK_UART0, "s3c2412-uart.0", "uart"), + ALIAS(PCLK_UART1, "s3c2412-uart.1", "uart"), + ALIAS(PCLK_UART2, "s3c2412-uart.2", "uart"), + ALIAS(PCLK_UART0, "s3c2412-uart.0", "clk_uart_baud2"), + ALIAS(PCLK_UART1, "s3c2412-uart.1", "clk_uart_baud2"), + ALIAS(PCLK_UART2, "s3c2412-uart.2", "clk_uart_baud2"), + ALIAS(SCLK_UART, NULL, "clk_uart_baud3"), + ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"), + ALIAS(PCLK_ADC, NULL, "adc"), + ALIAS(PCLK_RTC, NULL, "rtc"), + ALIAS(PCLK_PWM, NULL, "timers"), + ALIAS(HCLK_LCD, NULL, "lcd"), + ALIAS(PCLK_USBD, NULL, "usb-device"), + ALIAS(SCLK_USBD, NULL, "usb-bus-gadget"), + ALIAS(HCLK_USBH, NULL, "usb-host"), + ALIAS(SCLK_USBH, NULL, "usb-bus-host"), + ALIAS(ARMCLK, NULL, "armclk"), + ALIAS(HCLK, NULL, "hclk"), + ALIAS(MPLL, NULL, "mpll"), + ALIAS(MSYSCLK, NULL, "fclk"), +}; + +/* + * fixed rate clocks generated outside the soc + * Only necessary until the devicetree-move is complete + */ +#define XTI 1 +struct samsung_fixed_rate_clock s3c2412_common_frate_clks[] __initdata = { + FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0), + FRATE(0, "ext", NULL, CLK_IS_ROOT, 0), +}; + +static void __init s3c2412_common_clk_register_fixed_ext(unsigned long xti_f, + unsigned long ext_f) +{ + /* xtal alias is necessary for the current cpufreq driver */ + struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal"); + + s3c2412_common_frate_clks[0].fixed_rate = xti_f; + s3c2412_common_frate_clks[1].fixed_rate = ext_f; + samsung_clk_register_fixed_rate(s3c2412_common_frate_clks, + ARRAY_SIZE(s3c2412_common_frate_clks)); + + samsung_clk_register_alias(&xti_alias, 1); +} + +void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, + unsigned long ext_f, void __iomem *base) +{ + reg_base = base; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + samsung_clk_init(np, reg_base, NR_CLKS); + + /* Register external clocks only in non-dt cases */ + if (!np) + s3c2412_common_clk_register_fixed_ext(xti_f, ext_f); + + /* Register PLLs. */ + samsung_clk_register_pll(s3c2412_plls, ARRAY_SIZE(s3c2412_plls), + reg_base); + + /* Register common internal clocks. */ + samsung_clk_register_mux(s3c2412_muxes, ARRAY_SIZE(s3c2412_muxes)); + samsung_clk_register_div(s3c2412_dividers, + ARRAY_SIZE(s3c2412_dividers)); + samsung_clk_register_gate(s3c2412_gates, ARRAY_SIZE(s3c2412_gates)); + samsung_clk_register_fixed_factor(s3c2412_ffactor, + ARRAY_SIZE(s3c2412_ffactor)); + samsung_clk_register_alias(s3c2412_aliases, + ARRAY_SIZE(s3c2412_aliases)); + + s3c2412_clk_sleep_init(); +} + +static void __init s3c2412_clk_init(struct device_node *np) +{ + s3c2412_common_clk_init(np, 0, 0, 0); +} +CLK_OF_DECLARE(s3c2412_clk, "samsung,s3c2412-clock", s3c2412_clk_init); diff --git a/include/dt-bindings/clock/s3c2412.h b/include/dt-bindings/clock/s3c2412.h new file mode 100644 index 000000000000..aac1dcfda81c --- /dev/null +++ b/include/dt-bindings/clock/s3c2412.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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. + * + * Device Tree binding constants clock controllers of Samsung S3C2412. + */ + +#ifndef _DT_BINDINGS_CLOCK_SAMSUNG_S3C2412_CLOCK_H +#define _DT_BINDINGS_CLOCK_SAMSUNG_S3C2412_CLOCK_H + +/* + * Let each exported clock get a unique index, which is used on DT-enabled + * platforms to lookup the clock from a clock specifier. These indices are + * therefore considered an ABI and so must not be changed. This implies + * that new clocks should be added either in free spaces between clock groups + * or at the end. + */ + +/* Core clocks. */ + +/* id 1 is reserved */ +#define MPLL 2 +#define UPLL 3 +#define MDIVCLK 4 +#define MSYSCLK 5 +#define USYSCLK 6 +#define HCLK 7 +#define PCLK 8 +#define ARMDIV 9 +#define ARMCLK 10 + + +/* Special clocks */ +#define SCLK_CAM 16 +#define SCLK_UART 17 +#define SCLK_I2S 18 +#define SCLK_USBD 19 +#define SCLK_USBH 20 + +/* pclk-gates */ +#define PCLK_WDT 32 +#define PCLK_SPI 33 +#define PCLK_I2S 34 +#define PCLK_I2C 35 +#define PCLK_ADC 36 +#define PCLK_RTC 37 +#define PCLK_GPIO 38 +#define PCLK_UART2 39 +#define PCLK_UART1 40 +#define PCLK_UART0 41 +#define PCLK_SDI 42 +#define PCLK_PWM 43 +#define PCLK_USBD 44 + +/* hclk-gates */ +#define HCLK_HALF 48 +#define HCLK_X2 49 +#define HCLK_SDRAM 50 +#define HCLK_USBH 51 +#define HCLK_LCD 52 +#define HCLK_NAND 53 +#define HCLK_DMA3 54 +#define HCLK_DMA2 55 +#define HCLK_DMA1 56 +#define HCLK_DMA0 57 + +/* Total number of clocks. */ +#define NR_CLKS (HCLK_DMA0 + 1) + +#endif /* _DT_BINDINGS_CLOCK_SAMSUNG_S3C2412_CLOCK_H */ -- cgit v1.2.3 From f39d2fa0122e6abd8505a3598f3aa535d0d5aade Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:35 +0800 Subject: mtd: spi-nor: copy the SPI NOR commands to a new header file This patch adds a new header :spi-nor.h, and copies all the SPI NOR commands and relative macros into this new header. This hearder can be used by the m25p80.c and other spi-nor controller, such as Freescale's Quadspi. Signed-off-by: Huang Shijie Acked-by: Marek Vasut Signed-off-by: Brian Norris --- include/linux/mtd/spi-nor.h | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 include/linux/mtd/spi-nor.h (limited to 'include') diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 000000000000..483fc2a086c4 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,55 @@ +#ifndef __LINUX_MTD_SPI_NOR_H +#define __LINUX_MTD_SPI_NOR_H + +/* Flash opcodes. */ +#define OPCODE_WREN 0x06 /* Write enable */ +#define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ +#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ +#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ +#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ +#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define OPCODE_RDID 0x9f /* Read JEDEC ID */ +#define OPCODE_RDCR 0x35 /* Read configuration register */ + +/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ +#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ +#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ + +/* Used for SST flashes only. */ +#define OPCODE_BP 0x02 /* Byte program */ +#define OPCODE_WRDI 0x04 /* Write disable */ +#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ + +/* Used for Macronix and Winbond flashes. */ +#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ +#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ + +/* Used for Spansion flashes only. */ +#define OPCODE_BRWR 0x17 /* Bank register write */ + +/* Status Register bits. */ +#define SR_WIP 1 /* Write in progress */ +#define SR_WEL 2 /* Write enable latch */ +/* meaning of other SR_* bits may differ between vendors */ +#define SR_BP0 4 /* Block protect 0 */ +#define SR_BP1 8 /* Block protect 1 */ +#define SR_BP2 0x10 /* Block protect 2 */ +#define SR_SRWD 0x80 /* SR write protect */ + +#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ + +/* Configuration Register bits. */ +#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ + +#endif -- cgit v1.2.3 From 6e602ef73334550bbbb8be1041a3ce6eecbd42f1 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:36 +0800 Subject: mtd: spi-nor: add the basic data structures The spi_nor{} is cloned from the m25p{}. The spi_nor{} can be used by both the m25p80 and spi-nor controller. We also add the spi_nor_xfer_cfg{} which can be used by the two fundamental primitives: read_xfer/write_xfer. 1) the hooks for spi_nor{}: @prepare/unpreare: used to do some work before or after the read/write/erase/lock/unlock. @read_xfer/write_xfer: We can use these two hooks to code all the following hooks if the driver tries to implement them by itself. @read_reg: used to read the registers, such as read status register, read configure register. @write_reg: used to write the registers, such as write enable, erase sector. @read_id: read out the ID info. @wait_till_ready: wait till the NOR becomes ready. @read: read out the data from the NOR. @write: write data to the NOR. @erase: erase a sector of the NOR. 2) Add a new field sst_write_second for the SST NOR write. Signed-off-by: Huang Shijie Acked-by: Marek Vasut Signed-off-by: Brian Norris --- include/linux/mtd/spi-nor.h | 110 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 483fc2a086c4..3a3c3872c8cd 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -52,4 +52,114 @@ /* Configuration Register bits. */ #define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ +enum read_mode { + SPI_NOR_NORMAL = 0, + SPI_NOR_FAST, + SPI_NOR_DUAL, + SPI_NOR_QUAD, +}; + +/** + * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer + * @wren: command for "Write Enable", or 0x00 for not required + * @cmd: command for operation + * @cmd_pins: number of pins to send @cmd (1, 2, 4) + * @addr: address for operation + * @addr_pins: number of pins to send @addr (1, 2, 4) + * @addr_width: number of address bytes + * (3,4, or 0 for address not required) + * @mode: mode data + * @mode_pins: number of pins to send @mode (1, 2, 4) + * @mode_cycles: number of mode cycles (0 for mode not required) + * @dummy_cycles: number of dummy cycles (0 for dummy not required) + */ +struct spi_nor_xfer_cfg { + u8 wren; + u8 cmd; + u8 cmd_pins; + u32 addr; + u8 addr_pins; + u8 addr_width; + u8 mode; + u8 mode_pins; + u8 mode_cycles; + u8 dummy_cycles; +}; + +#define SPI_NOR_MAX_CMD_SIZE 8 +enum spi_nor_ops { + SPI_NOR_OPS_READ = 0, + SPI_NOR_OPS_WRITE, + SPI_NOR_OPS_ERASE, + SPI_NOR_OPS_LOCK, + SPI_NOR_OPS_UNLOCK, +}; + +/** + * struct spi_nor - Structure for defining a the SPI NOR layer + * @mtd: point to a mtd_info structure + * @lock: the lock for the read/write/erase/lock/unlock operations + * @dev: point to a spi device, or a spi nor controller device. + * @page_size: the page size of the SPI NOR + * @addr_width: number of address bytes + * @erase_opcode: the opcode for erasing a sector + * @read_opcode: the read opcode + * @read_dummy: the dummy needed by the read operation + * @program_opcode: the program opcode + * @flash_read: the mode of the read + * @sst_write_second: used by the SST write operation + * @cfg: used by the read_xfer/write_xfer + * @cmd_buf: used by the write_reg + * @prepare: [OPTIONAL] do some preparations for the + * read/write/erase/lock/unlock operations + * @unprepare: [OPTIONAL] do some post work after the + * read/write/erase/lock/unlock operations + * @read_xfer: [OPTIONAL] the read fundamental primitive + * @write_xfer: [OPTIONAL] the writefundamental primitive + * @read_reg: [DRIVER-SPECIFIC] read out the register + * @write_reg: [DRIVER-SPECIFIC] write data to the register + * @read_id: [REPLACEABLE] read out the ID data, and find + * the proper spi_device_id + * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR + * at the offset @offs + * @priv: the private data + */ +struct spi_nor { + struct mtd_info *mtd; + struct mutex lock; + struct device *dev; + u32 page_size; + u8 addr_width; + u8 erase_opcode; + u8 read_opcode; + u8 read_dummy; + u8 program_opcode; + enum read_mode flash_read; + bool sst_write_second; + struct spi_nor_xfer_cfg cfg; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + + int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); + void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); + int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, + u8 *buf, size_t len); + int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, + u8 *buf, size_t len); + int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); + int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int write_enable); + const struct spi_device_id *(*read_id)(struct spi_nor *nor); + int (*wait_till_ready)(struct spi_nor *nor); + + int (*read)(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *read_buf); + void (*write)(struct spi_nor *nor, loff_t to, + size_t len, size_t *retlen, const u_char *write_buf); + int (*erase)(struct spi_nor *nor, loff_t offs); + + void *priv; +}; #endif -- cgit v1.2.3 From b199489d37b21c5e294f95bf265acc5dde3fc3a2 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:37 +0800 Subject: mtd: spi-nor: add the framework for SPI NOR This patch cloned most of the m25p80.c. In theory, it adds a new spi-nor layer. Before this patch, the layer is like: MTD ------------------------ m25p80 ------------------------ spi bus driver ------------------------ SPI NOR chip After this patch, the layer is like: MTD ------------------------ spi-nor ------------------------ m25p80 ------------------------ spi bus driver ------------------------ SPI NOR chip With the spi-nor controller driver(Freescale Quadspi), it looks like: MTD ------------------------ spi-nor ------------------------ fsl-quadspi ------------------------ SPI NOR chip New APIs: spi_nor_scan: used to scan a spi-nor flash. Signed-off-by: Huang Shijie Acked-by: Marek Vasut [Brian: rebased to include additional m25p_ids[] entry] Signed-off-by: Brian Norris --- drivers/mtd/Kconfig | 2 + drivers/mtd/Makefile | 1 + drivers/mtd/spi-nor/Kconfig | 6 + drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/spi-nor.c | 1088 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 20 + 6 files changed, 1118 insertions(+) create mode 100644 drivers/mtd/spi-nor/Kconfig create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/spi-nor.c (limited to 'include') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5d49a2129618..94b821042d9d 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -321,6 +321,8 @@ source "drivers/mtd/onenand/Kconfig" source "drivers/mtd/lpddr/Kconfig" +source "drivers/mtd/spi-nor/Kconfig" + source "drivers/mtd/ubi/Kconfig" endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4cfb31e6c966..40fd15344387 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ +obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor/ obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig new file mode 100644 index 000000000000..41591af67aca --- /dev/null +++ b/drivers/mtd/spi-nor/Kconfig @@ -0,0 +1,6 @@ +config MTD_SPI_NOR_BASE + bool "the framework for SPI-NOR support" + depends on MTD + help + This is the framework for the SPI NOR which can be used by the SPI + device drivers and the SPI-NOR device driver. diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 000000000000..7dfe1f9b6940 --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor.o diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c new file mode 100644 index 000000000000..50b929095bdb --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -0,0 +1,1088 @@ +/* + * Cloned most of the code from the m25p80.c + * + * This code 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 max times to check status register before we give up. */ +#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ + +#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) + +/* + * Read the status register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_sr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, OPCODE_RDSR, &val, 1); + if (ret < 0) { + pr_err("error %d reading SR\n", (int) ret); + return ret; + } + + return val; +} + +/* + * Read configuration register, returning its value in the + * location. Return the configuration register value. + * Returns negative if error occured. + */ +static int read_cr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, OPCODE_RDCR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading CR\n", ret); + return ret; + } + + return val; +} + +/* + * Dummy Cycle calculation for different type of read. + * It can be used to support more commands with + * different dummy cycle requirements. + */ +static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) +{ + switch (nor->flash_read) { + case SPI_NOR_FAST: + case SPI_NOR_DUAL: + case SPI_NOR_QUAD: + return 1; + case SPI_NOR_NORMAL: + return 0; + } + return 0; +} + +/* + * Write status register 1 byte + * Returns negative if error occurred. + */ +static inline int write_sr(struct spi_nor *nor, u8 val) +{ + nor->cmd_buf[0] = val; + return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); +} + +/* + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static inline int write_enable(struct spi_nor *nor) +{ + return nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); +} + +/* + * Send write disble instruction to the chip. + */ +static inline int write_disable(struct spi_nor *nor) +{ + return nor->write_reg(nor, OPCODE_WRDI, NULL, 0, 0); +} + +static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +{ + return mtd->priv; +} + +/* Enable/disable 4-byte addressing mode. */ +static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) +{ + int status; + bool need_wren = false; + u8 cmd; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_ST: /* Micron, actually */ + /* Some Micron need WREN command; all will accept it */ + need_wren = true; + case CFI_MFR_MACRONIX: + case 0xEF /* winbond */: + if (need_wren) + write_enable(nor); + + cmd = enable ? OPCODE_EN4B : OPCODE_EX4B; + status = nor->write_reg(nor, cmd, NULL, 0, 0); + if (need_wren) + write_disable(nor); + + return status; + default: + /* Spansion style */ + nor->cmd_buf[0] = enable << 7; + return nor->write_reg(nor, OPCODE_BRWR, nor->cmd_buf, 1, 0); + } +} + +static int spi_nor_wait_till_ready(struct spi_nor *nor) +{ + unsigned long deadline; + int sr; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + cond_resched(); + + sr = read_sr(nor); + if (sr < 0) + break; + else if (!(sr & SR_WIP)) + return 0; + } while (!time_after_eq(jiffies, deadline)); + + return -ETIMEDOUT; +} + +/* + * Service routine to read status register until ready, or timeout occurs. + * Returns non-zero if error. + */ +static int wait_till_ready(struct spi_nor *nor) +{ + return nor->wait_till_ready(nor); +} + +/* + * Erase the whole flash memory + * + * Returns 0 if successful, non-zero otherwise. + */ +static int erase_chip(struct spi_nor *nor) +{ + int ret; + + dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10)); + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + return ret; + + /* Send write enable, then erase commands. */ + write_enable(nor); + + return nor->write_reg(nor, OPCODE_CHIP_ERASE, NULL, 0, 0); +} + +static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + int ret = 0; + + mutex_lock(&nor->lock); + + if (nor->prepare) { + ret = nor->prepare(nor, ops); + if (ret) { + dev_err(nor->dev, "failed in the preparation.\n"); + mutex_unlock(&nor->lock); + return ret; + } + } + return ret; +} + +static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + if (nor->unprepare) + nor->unprepare(nor, ops); + mutex_unlock(&nor->lock); +} + +/* + * Erase an address range on the nor chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 addr, len; + uint32_t rem; + int ret; + + dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, + (long long)instr->len); + + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) + return -EINVAL; + + addr = instr->addr; + len = instr->len; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE); + if (ret) + return ret; + + /* whole-chip erase? */ + if (len == mtd->size) { + if (erase_chip(nor)) { + ret = -EIO; + goto erase_err; + } + + /* REVISIT in some cases we could speed up erasing large regions + * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up + * to use "small sector erase", but that's not always optimal. + */ + + /* "sector"-at-a-time erase */ + } else { + while (len) { + if (nor->erase(nor, addr)) { + ret = -EIO; + goto erase_err; + } + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + } + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; + +erase_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + instr->state = MTD_ERASE_FAILED; + return ret; +} + +static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + uint32_t offset = ofs; + uint8_t status_old, status_new; + int ret = 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); + if (ret) + return ret; + + /* Wait until finished previous command */ + ret = wait_till_ready(nor); + if (ret) + goto err; + + status_old = read_sr(nor); + + if (offset < mtd->size - (mtd->size / 2)) + status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 4)) + status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + else if (offset < mtd->size - (mtd->size / 8)) + status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 16)) + status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; + else if (offset < mtd->size - (mtd->size / 32)) + status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 64)) + status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; + else + status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + + /* Only modify protection if it will not unlock other areas */ + if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) > + (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { + write_enable(nor); + ret = write_sr(nor, status_new); + if (ret) + goto err; + } + +err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); + return ret; +} + +static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + uint32_t offset = ofs; + uint8_t status_old, status_new; + int ret = 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); + if (ret) + return ret; + + /* Wait until finished previous command */ + ret = wait_till_ready(nor); + if (ret) + goto err; + + status_old = read_sr(nor); + + if (offset+len > mtd->size - (mtd->size / 64)) + status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0); + else if (offset+len > mtd->size - (mtd->size / 32)) + status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + else if (offset+len > mtd->size - (mtd->size / 16)) + status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; + else if (offset+len > mtd->size - (mtd->size / 8)) + status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; + else if (offset+len > mtd->size - (mtd->size / 4)) + status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; + else if (offset+len > mtd->size - (mtd->size / 2)) + status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; + else + status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + + /* Only modify protection if it will not lock other areas */ + if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) < + (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { + write_enable(nor); + ret = write_sr(nor, status_new); + if (ret) + goto err; + } + +err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); + return ret; +} + +struct flash_info { + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + u16 ext_id; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + + u16 page_size; + u16 addr_width; + + u16 flags; +#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ +#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ +#define SST_WRITE 0x04 /* use SST byte programming */ +#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ +#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ +#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ +#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ +}; + +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .jedec_id = (_jedec_id), \ + .ext_id = (_ext_id), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), \ + }) + +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = (_page_size), \ + .addr_width = (_addr_width), \ + .flags = (_flags), \ + }) + +/* NOTE: double check command sets and memory organization when you add + * more nor chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +const struct spi_device_id spi_nor_ids[] = { + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, + + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, + { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, + + { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, + { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, + + { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, + + /* EON -- en25xxx */ + { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, + { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, + { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + + /* ESMT */ + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, + + /* Everspin */ + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + + /* GigaDevice */ + { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, + { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, + + /* Intel/Numonyx -- xxxs33b */ + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, + + /* Macronix */ + { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, + { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, + { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, + { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, + { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) }, + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, + + /* Micron */ + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, + { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, + { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, + { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, + { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, + { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, + { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, + { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, + { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, + { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, + { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, + { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, + { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, + { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, + { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, + { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, + { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, + { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, + { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, + { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, + { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, + { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, + + { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, + { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, + { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, + { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, + { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, + { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, + { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, + { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, + { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, + + { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, + { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, + { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, + + { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, + { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, + { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, + + { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, + { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, + { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, + { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, + + /* Catalyst / On Semiconductor -- non-JEDEC */ + { "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { }, +}; + +static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) +{ + int tmp; + u8 id[5]; + u32 jedec; + u16 ext_jedec; + struct flash_info *info; + + tmp = nor->read_reg(nor, OPCODE_RDID, id, 5); + if (tmp < 0) { + dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + ext_jedec = id[3] << 8 | id[4]; + + for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { + info = (void *)spi_nor_ids[tmp].driver_data; + if (info->jedec_id == jedec) { + if (info->ext_id == 0 || info->ext_id == ext_jedec) + return &spi_nor_ids[tmp]; + } + } + dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); + return ERR_PTR(-ENODEV); +} + +static const struct spi_device_id *jedec_probe(struct spi_nor *nor) +{ + return nor->read_id(nor); +} + +static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ); + if (ret) + return ret; + + ret = nor->read(nor, from, len, retlen, buf); + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ); + return ret; +} + +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + size_t actual; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + goto time_out; + + write_enable(nor); + + nor->sst_write_second = false; + + actual = to % 2; + /* Start write from odd address. */ + if (actual) { + nor->program_opcode = OPCODE_BP; + + /* write one byte. */ + nor->write(nor, to, 1, retlen, buf); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + } + to += actual; + + /* Write out most of the data here. */ + for (; actual < len - 1; actual += 2) { + nor->program_opcode = OPCODE_AAI_WP; + + /* write two bytes. */ + nor->write(nor, to, 2, retlen, buf + actual); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + to += 2; + nor->sst_write_second = true; + } + nor->sst_write_second = false; + + write_disable(nor); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + + /* Write out trailing byte if it exists. */ + if (actual != len) { + write_enable(nor); + + nor->program_opcode = OPCODE_BP; + nor->write(nor, to, 1, retlen, buf + actual); + + ret = wait_till_ready(nor); + if (ret) + goto time_out; + write_disable(nor); + } +time_out: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return ret; +} + +/* + * Write an address range to the nor chip. Data must be written in + * FLASH_PAGESIZE chunks. The address range may be any size provided + * it is within the physical boundaries. + */ +static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 page_offset, page_size, i; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + goto write_err; + + write_enable(nor); + + page_offset = to & (nor->page_size - 1); + + /* do all the bytes fit onto one page? */ + if (page_offset + len <= nor->page_size) { + nor->write(nor, to, len, retlen, buf); + } else { + /* the size of data remaining on the first page */ + page_size = nor->page_size - page_offset; + nor->write(nor, to, page_size, retlen, buf); + + /* write everything in nor->page_size chunks */ + for (i = page_size; i < len; i += page_size) { + page_size = len - i; + if (page_size > nor->page_size) + page_size = nor->page_size; + + wait_till_ready(nor); + write_enable(nor); + + nor->write(nor, to + i, page_size, retlen, buf + i); + } + } + +write_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return 0; +} + +static int macronix_quad_enable(struct spi_nor *nor) +{ + int ret, val; + + val = read_sr(nor); + write_enable(nor); + + nor->cmd_buf[0] = val | SR_QUAD_EN_MX; + nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + + if (wait_till_ready(nor)) + return 1; + + ret = read_sr(nor); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + dev_err(nor->dev, "Macronix Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Write status Register and configuration register with 2 bytes + * The first byte will be written to the status register, while the + * second byte will be written to the configuration register. + * Return negative if error occured. + */ +static int write_sr_cr(struct spi_nor *nor, u16 val) +{ + nor->cmd_buf[0] = val & 0xff; + nor->cmd_buf[1] = (val >> 8); + + return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 2, 0); +} + +static int spansion_quad_enable(struct spi_nor *nor) +{ + int ret; + int quad_en = CR_QUAD_EN_SPAN << 8; + + write_enable(nor); + + ret = write_sr_cr(nor, quad_en); + if (ret < 0) { + dev_err(nor->dev, + "error while writing configuration register\n"); + return -EINVAL; + } + + /* read back and check it */ + ret = read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) +{ + int status; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_MACRONIX: + status = macronix_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Macronix quad-read not enabled\n"); + return -EINVAL; + } + return status; + default: + status = spansion_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Spansion quad-read not enabled\n"); + return -EINVAL; + } + return status; + } +} + +static int spi_nor_check(struct spi_nor *nor) +{ + if (!nor->dev || !nor->read || !nor->write || + !nor->read_reg || !nor->write_reg || !nor->erase) { + pr_err("spi-nor: please fill all the necessary fields!\n"); + return -EINVAL; + } + + if (!nor->read_id) + nor->read_id = spi_nor_read_id; + if (!nor->wait_till_ready) + nor->wait_till_ready = spi_nor_wait_till_ready; + + return 0; +} + +int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, + enum read_mode mode) +{ + struct flash_info *info; + struct flash_platform_data *data; + struct device *dev = nor->dev; + struct mtd_info *mtd = nor->mtd; + struct device_node *np = dev->of_node; + int ret; + int i; + + ret = spi_nor_check(nor); + if (ret) + return ret; + + /* Platform data helps sort out which chip type we have, as + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. + */ + data = dev_get_platdata(dev); + if (data && data->type) { + const struct spi_device_id *plat_id; + + for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { + plat_id = &spi_nor_ids[i]; + if (strcmp(data->type, plat_id->name)) + continue; + break; + } + + if (i < ARRAY_SIZE(spi_nor_ids) - 1) + id = plat_id; + else + dev_warn(dev, "unrecognized id %s\n", data->type); + } + + info = (void *)id->driver_data; + + if (info->jedec_id) { + const struct spi_device_id *jid; + + jid = jedec_probe(nor); + if (IS_ERR(jid)) { + return PTR_ERR(jid); + } else if (jid != id) { + /* + * JEDEC knows better, so overwrite platform ID. We + * can't trust partitions any longer, but we'll let + * mtd apply them anyway, since some partitions may be + * marked read-only, and we don't want to lose that + * information, even if it's not 100% accurate. + */ + dev_warn(dev, "found %s, expected %s\n", + jid->name, id->name); + id = jid; + info = (void *)jid->driver_data; + } + } + + mutex_init(&nor->lock); + + /* + * Atmel, SST and Intel/Numonyx serial nor tend to power + * up with the software protection bits set + */ + + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { + write_enable(nor); + write_sr(nor, 0); + } + + if (data && data->name) + mtd->name = data->name; + else + mtd->name = dev_name(dev); + + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; + mtd->size = info->sector_size * info->n_sectors; + mtd->_erase = spi_nor_erase; + mtd->_read = spi_nor_read; + + /* nor protection support for STmicro chips */ + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; + } + + /* sst nor chips use AAI word program */ + if (info->flags & SST_WRITE) + mtd->_write = sst_write; + else + mtd->_write = spi_nor_write; + + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + nor->erase_opcode = OPCODE_BE_4K; + mtd->erasesize = 4096; + } else if (info->flags & SECT_4K_PMC) { + nor->erase_opcode = OPCODE_BE_4K_PMC; + mtd->erasesize = 4096; + } else { + nor->erase_opcode = OPCODE_SE; + mtd->erasesize = info->sector_size; + } + + if (info->flags & SPI_NOR_NO_ERASE) + mtd->flags |= MTD_NO_ERASE; + + mtd->dev.parent = dev; + nor->page_size = info->page_size; + mtd->writebufsize = nor->page_size; + + if (np) { + /* If we were instantiated by DT, use it */ + if (of_property_read_bool(np, "m25p,fast-read")) + nor->flash_read = SPI_NOR_FAST; + else + nor->flash_read = SPI_NOR_NORMAL; + } else { + /* If we weren't instantiated by DT, default to fast-read */ + nor->flash_read = SPI_NOR_FAST; + } + + /* Some devices cannot do fast-read, no matter what DT tells us */ + if (info->flags & SPI_NOR_NO_FR) + nor->flash_read = SPI_NOR_NORMAL; + + /* Quad/Dual-read mode takes precedence over fast/normal */ + if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { + ret = set_quad_mode(nor, info->jedec_id); + if (ret) { + dev_err(dev, "quad mode not supported\n"); + return ret; + } + nor->flash_read = SPI_NOR_QUAD; + } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { + nor->flash_read = SPI_NOR_DUAL; + } + + /* Default commands */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: + nor->read_opcode = OPCODE_QUAD_READ; + break; + case SPI_NOR_DUAL: + nor->read_opcode = OPCODE_DUAL_READ; + break; + case SPI_NOR_FAST: + nor->read_opcode = OPCODE_FAST_READ; + break; + case SPI_NOR_NORMAL: + nor->read_opcode = OPCODE_NORM_READ; + break; + default: + dev_err(dev, "No Read opcode defined\n"); + return -EINVAL; + } + + nor->program_opcode = OPCODE_PP; + + if (info->addr_width) + nor->addr_width = info->addr_width; + else if (mtd->size > 0x1000000) { + /* enable 4-byte addressing if the device exceeds 16MiB */ + nor->addr_width = 4; + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + switch (nor->flash_read) { + case SPI_NOR_QUAD: + nor->read_opcode = OPCODE_QUAD_READ_4B; + break; + case SPI_NOR_DUAL: + nor->read_opcode = OPCODE_DUAL_READ_4B; + break; + case SPI_NOR_FAST: + nor->read_opcode = OPCODE_FAST_READ_4B; + break; + case SPI_NOR_NORMAL: + nor->read_opcode = OPCODE_NORM_READ_4B; + break; + } + nor->program_opcode = OPCODE_PP_4B; + /* No small sector erase for 4-byte command set */ + nor->erase_opcode = OPCODE_SE_4B; + mtd->erasesize = info->sector_size; + } else + set_4byte(nor, info->jedec_id, 1); + } else { + nor->addr_width = 3; + } + + nor->read_dummy = spi_nor_read_dummy_cycles(nor); + + dev_info(dev, "%s (%lld Kbytes)\n", id->name, + (long long)mtd->size >> 10); + + dev_dbg(dev, + "mtd .name = %s, .size = 0x%llx (%lldMiB), " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", + mtd->name, (long long)mtd->size, (long long)(mtd->size >> 20), + mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions); + + if (mtd->numeraseregions) + for (i = 0; i < mtd->numeraseregions; i++) + dev_dbg(dev, + "mtd.eraseregions[%d] = { .offset = 0x%llx, " + ".erasesize = 0x%.8x (%uKiB), " + ".numblocks = %d }\n", + i, (long long)mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].erasesize / 1024, + mtd->eraseregions[i].numblocks); + return 0; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huang Shijie "); +MODULE_AUTHOR("Mike Lavender"); +MODULE_DESCRIPTION("framework for SPI NOR"); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 3a3c3872c8cd..16d8409abcdc 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -162,4 +162,24 @@ struct spi_nor { void *priv; }; + +/** + * spi_nor_scan() - scan the SPI NOR + * @nor: the spi_nor structure + * @id: the spi_device_id provided by the driver + * @mode: the read mode supported by the driver + * + * The drivers can use this fuction to scan the SPI NOR. + * In the scanning, it will try to get all the necessary information to + * fill the mtd_info{} and the spi_nor{}. + * + * The board may assigns a spi_device_id with @id which be used to compared with + * the spi_device_id detected by the scanning. + * + * Return: 0 for success, others for failure. + */ +int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, + enum read_mode mode); +extern const struct spi_device_id spi_nor_ids[]; + #endif -- cgit v1.2.3 From 0d8c11c01274bde227d368daa8954911dd324a9f Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 24 Feb 2014 18:37:40 +0800 Subject: mtd: spi-nor: add a helper to find the spi_device_id Add the spi_nor_match_id() to find the proper spi_device_id with the NOR flash's name in the spi_nor_ids table. Signed-off-by: Huang Shijie Acked-by: Marek Vasut Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 12 ++++++++++++ include/linux/mtd/spi-nor.h | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 50b929095bdb..f7c9e638623b 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1082,6 +1082,18 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, return 0; } +const struct spi_device_id *spi_nor_match_id(char *name) +{ + const struct spi_device_id *id = spi_nor_ids; + + while (id->name[0]) { + if (!strcmp(name, id->name)) + return id; + id++; + } + return NULL; +} + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huang Shijie "); MODULE_AUTHOR("Mike Lavender"); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 16d8409abcdc..41dae78fbd1d 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -182,4 +182,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, enum read_mode mode); extern const struct spi_device_id spi_nor_ids[]; +/** + * spi_nor_match_id() - find the spi_device_id by the name + * @name: the name of the spi_device_id + * + * The drivers use this function to find the spi_device_id + * specified by the @name. + * + * Return: returns the right spi_device_id pointer on success, + * and returns NULL on failure. + */ +const struct spi_device_id *spi_nor_match_id(char *name); + #endif -- cgit v1.2.3 From 8eabdd1ec122cf6b77cf73e798f134fbace1b8d1 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 10 Apr 2014 16:27:28 +0800 Subject: mtd: spi-nor: add the copyright information Add the copyright information for spi-nor.c and spi-nor.h. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 6 +++++- include/linux/mtd/spi-nor.h | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 5cd86eb2a5f0..6c64ab95dee2 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1,5 +1,9 @@ /* - * Cloned most of the code from the m25p80.c + * Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with + * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c + * + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 41dae78fbd1d..9428d285489b 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -1,3 +1,12 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * 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 __LINUX_MTD_SPI_NOR_H #define __LINUX_MTD_SPI_NOR_H -- cgit v1.2.3 From becd0cb8666de4bfaaf6eb3042f69066c8fb8677 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:10:23 -0700 Subject: mtd: spi-nor: drop \t after #define Spacing is a little non-standard here. Fix up tabs vs. spaces. Signed-off-by: Brian Norris Acked-by: Huang Shijie Reviewed-by: Marek Vasut --- include/linux/mtd/spi-nor.h | 72 ++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 9428d285489b..a6e87190ead1 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -11,55 +11,55 @@ #define __LINUX_MTD_SPI_NOR_H /* Flash opcodes. */ -#define OPCODE_WREN 0x06 /* Write enable */ -#define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ -#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ -#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ -#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ -#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ -#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ -#define OPCODE_RDID 0x9f /* Read JEDEC ID */ -#define OPCODE_RDCR 0x35 /* Read configuration register */ +#define OPCODE_WREN 0x06 /* Write enable */ +#define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ +#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ +#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ +#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ +#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define OPCODE_RDID 0x9f /* Read JEDEC ID */ +#define OPCODE_RDCR 0x35 /* Read configuration register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ -#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ -#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ +#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ +#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ /* Used for SST flashes only. */ -#define OPCODE_BP 0x02 /* Byte program */ -#define OPCODE_WRDI 0x04 /* Write disable */ -#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ +#define OPCODE_BP 0x02 /* Byte program */ +#define OPCODE_WRDI 0x04 /* Write disable */ +#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ /* Used for Macronix and Winbond flashes. */ -#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ -#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ +#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ +#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ /* Used for Spansion flashes only. */ -#define OPCODE_BRWR 0x17 /* Bank register write */ +#define OPCODE_BRWR 0x17 /* Bank register write */ /* Status Register bits. */ -#define SR_WIP 1 /* Write in progress */ -#define SR_WEL 2 /* Write enable latch */ +#define SR_WIP 1 /* Write in progress */ +#define SR_WEL 2 /* Write enable latch */ /* meaning of other SR_* bits may differ between vendors */ -#define SR_BP0 4 /* Block protect 0 */ -#define SR_BP1 8 /* Block protect 1 */ -#define SR_BP2 0x10 /* Block protect 2 */ -#define SR_SRWD 0x80 /* SR write protect */ +#define SR_BP0 4 /* Block protect 0 */ +#define SR_BP1 8 /* Block protect 1 */ +#define SR_BP2 0x10 /* Block protect 2 */ +#define SR_SRWD 0x80 /* SR write protect */ -#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ +#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ /* Configuration Register bits. */ -#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ +#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ enum read_mode { SPI_NOR_NORMAL = 0, @@ -95,7 +95,7 @@ struct spi_nor_xfer_cfg { u8 dummy_cycles; }; -#define SPI_NOR_MAX_CMD_SIZE 8 +#define SPI_NOR_MAX_CMD_SIZE 8 enum spi_nor_ops { SPI_NOR_OPS_READ = 0, SPI_NOR_OPS_WRITE, -- cgit v1.2.3 From b02e7f3ef0beb72da8fc64542f0ac977996ec56b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 18:15:31 -0700 Subject: mtd: spi-nor: re-name OPCODE_* to SPINOR_OP_* Qualify these with a better namespace, and prepare them for use in more drivers. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut Acked-by: Huang Shijie --- drivers/mtd/devices/m25p80.c | 4 +-- drivers/mtd/spi-nor/fsl-quadspi.c | 58 +++++++++++++++++------------------ drivers/mtd/spi-nor/spi-nor.c | 64 +++++++++++++++++++-------------------- include/linux/mtd/spi-nor.h | 54 ++++++++++++++++----------------- 4 files changed, 90 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 4af6400ccd95..1557d8f672c1 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -86,7 +86,7 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, spi_message_init(&m); - if (nor->program_opcode == OPCODE_AAI_WP && nor->sst_write_second) + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) cmd_sz = 1; flash->command[0] = nor->program_opcode; @@ -171,7 +171,7 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset) return ret; /* Send write enable, then erase commands. */ - ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 6dc08ed950c8..2977f026f39d 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -294,12 +294,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) lut_base = SEQID_QUAD_READ * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_QUAD_READ; + cmd = SPINOR_OP_QUAD_READ; addrlen = ADDR24BIT; dummy = 8; } else { /* use the 4-byte address */ - cmd = OPCODE_QUAD_READ; + cmd = SPINOR_OP_QUAD_READ; addrlen = ADDR32BIT; dummy = 8; } @@ -311,17 +311,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Write enable */ lut_base = SEQID_WREN * 4; - writel(LUT0(CMD, PAD1, OPCODE_WREN), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base)); /* Page Program */ lut_base = SEQID_PP * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_PP; + cmd = SPINOR_OP_PP; addrlen = ADDR24BIT; } else { /* use the 4-byte address */ - cmd = OPCODE_PP; + cmd = SPINOR_OP_PP; addrlen = ADDR32BIT; } @@ -331,18 +331,18 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Read Status */ lut_base = SEQID_RDSR * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDSR) | LUT1(READ, PAD1, 0x1), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Erase a sector */ lut_base = SEQID_SE * 4; if (q->nor_size <= SZ_16M) { - cmd = OPCODE_SE; + cmd = SPINOR_OP_SE; addrlen = ADDR24BIT; } else { /* use the 4-byte address */ - cmd = OPCODE_SE; + cmd = SPINOR_OP_SE; addrlen = ADDR32BIT; } @@ -351,35 +351,35 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) /* Erase the whole chip */ lut_base = SEQID_CHIP_ERASE * 4; - writel(LUT0(CMD, PAD1, OPCODE_CHIP_ERASE), + writel(LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE), base + QUADSPI_LUT(lut_base)); /* READ ID */ lut_base = SEQID_RDID * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDID) | LUT1(READ, PAD1, 0x8), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8), base + QUADSPI_LUT(lut_base)); /* Write Register */ lut_base = SEQID_WRSR * 4; - writel(LUT0(CMD, PAD1, OPCODE_WRSR) | LUT1(WRITE, PAD1, 0x2), + writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2), base + QUADSPI_LUT(lut_base)); /* Read Configuration Register */ lut_base = SEQID_RDCR * 4; - writel(LUT0(CMD, PAD1, OPCODE_RDCR) | LUT1(READ, PAD1, 0x1), + writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1), base + QUADSPI_LUT(lut_base)); /* Write disable */ lut_base = SEQID_WRDI * 4; - writel(LUT0(CMD, PAD1, OPCODE_WRDI), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_WRDI), base + QUADSPI_LUT(lut_base)); /* Enter 4 Byte Mode (Micron) */ lut_base = SEQID_EN4B * 4; - writel(LUT0(CMD, PAD1, OPCODE_EN4B), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_EN4B), base + QUADSPI_LUT(lut_base)); /* Enter 4 Byte Mode (Spansion) */ lut_base = SEQID_BRWR * 4; - writel(LUT0(CMD, PAD1, OPCODE_BRWR), base + QUADSPI_LUT(lut_base)); + writel(LUT0(CMD, PAD1, SPINOR_OP_BRWR), base + QUADSPI_LUT(lut_base)); fsl_qspi_lock_lut(q); } @@ -388,29 +388,29 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) { switch (cmd) { - case OPCODE_QUAD_READ: + case SPINOR_OP_QUAD_READ: return SEQID_QUAD_READ; - case OPCODE_WREN: + case SPINOR_OP_WREN: return SEQID_WREN; - case OPCODE_WRDI: + case SPINOR_OP_WRDI: return SEQID_WRDI; - case OPCODE_RDSR: + case SPINOR_OP_RDSR: return SEQID_RDSR; - case OPCODE_SE: + case SPINOR_OP_SE: return SEQID_SE; - case OPCODE_CHIP_ERASE: + case SPINOR_OP_CHIP_ERASE: return SEQID_CHIP_ERASE; - case OPCODE_PP: + case SPINOR_OP_PP: return SEQID_PP; - case OPCODE_RDID: + case SPINOR_OP_RDID: return SEQID_RDID; - case OPCODE_WRSR: + case SPINOR_OP_WRSR: return SEQID_WRSR; - case OPCODE_RDCR: + case SPINOR_OP_RDCR: return SEQID_RDCR; - case OPCODE_EN4B: + case SPINOR_OP_EN4B: return SEQID_EN4B; - case OPCODE_BRWR: + case SPINOR_OP_BRWR: return SEQID_BRWR; default: dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); @@ -688,7 +688,7 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, if (ret) return ret; - if (opcode == OPCODE_CHIP_ERASE) + if (opcode == SPINOR_OP_CHIP_ERASE) fsl_qspi_invalid(q); } else if (len > 0) { @@ -750,7 +750,7 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs) return ret; /* Send write enable, then erase commands. */ - ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 6c64ab95dee2..1716f3ce9949 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -38,7 +38,7 @@ static int read_sr(struct spi_nor *nor) int ret; u8 val; - ret = nor->read_reg(nor, OPCODE_RDSR, &val, 1); + ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1); if (ret < 0) { pr_err("error %d reading SR\n", (int) ret); return ret; @@ -57,7 +57,7 @@ static int read_cr(struct spi_nor *nor) int ret; u8 val; - ret = nor->read_reg(nor, OPCODE_RDCR, &val, 1); + ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1); if (ret < 0) { dev_err(nor->dev, "error %d reading CR\n", ret); return ret; @@ -91,7 +91,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) static inline int write_sr(struct spi_nor *nor, u8 val) { nor->cmd_buf[0] = val; - return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); } /* @@ -100,7 +100,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val) */ static inline int write_enable(struct spi_nor *nor) { - return nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); } /* @@ -108,7 +108,7 @@ static inline int write_enable(struct spi_nor *nor) */ static inline int write_disable(struct spi_nor *nor) { - return nor->write_reg(nor, OPCODE_WRDI, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0); } static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) @@ -132,7 +132,7 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) if (need_wren) write_enable(nor); - cmd = enable ? OPCODE_EN4B : OPCODE_EX4B; + cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; status = nor->write_reg(nor, cmd, NULL, 0, 0); if (need_wren) write_disable(nor); @@ -141,7 +141,7 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) default: /* Spansion style */ nor->cmd_buf[0] = enable << 7; - return nor->write_reg(nor, OPCODE_BRWR, nor->cmd_buf, 1, 0); + return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); } } @@ -193,7 +193,7 @@ static int erase_chip(struct spi_nor *nor) /* Send write enable, then erase commands. */ write_enable(nor); - return nor->write_reg(nor, OPCODE_CHIP_ERASE, NULL, 0, 0); + return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); } static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) @@ -253,7 +253,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) } /* REVISIT in some cases we could speed up erasing large regions - * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up + * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up * to use "small sector erase", but that's not always optimal. */ @@ -385,7 +385,7 @@ struct flash_info { u32 jedec_id; u16 ext_id; - /* The size listed here is what works with OPCODE_SE, which isn't + /* The size listed here is what works with SPINOR_OP_SE, which isn't * necessarily called a "sector" by the vendor. */ unsigned sector_size; @@ -395,11 +395,11 @@ struct flash_info { u16 addr_width; u16 flags; -#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ +#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */ #define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ #define SST_WRITE 0x04 /* use SST byte programming */ #define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ -#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ +#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ #define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ }; @@ -598,7 +598,7 @@ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) u16 ext_jedec; struct flash_info *info; - tmp = nor->read_reg(nor, OPCODE_RDID, id, 5); + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5); if (tmp < 0) { dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); return ERR_PTR(tmp); @@ -670,7 +670,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, actual = to % 2; /* Start write from odd address. */ if (actual) { - nor->program_opcode = OPCODE_BP; + nor->program_opcode = SPINOR_OP_BP; /* write one byte. */ nor->write(nor, to, 1, retlen, buf); @@ -682,7 +682,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, /* Write out most of the data here. */ for (; actual < len - 1; actual += 2) { - nor->program_opcode = OPCODE_AAI_WP; + nor->program_opcode = SPINOR_OP_AAI_WP; /* write two bytes. */ nor->write(nor, to, 2, retlen, buf + actual); @@ -703,7 +703,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, if (actual != len) { write_enable(nor); - nor->program_opcode = OPCODE_BP; + nor->program_opcode = SPINOR_OP_BP; nor->write(nor, to, 1, retlen, buf + actual); ret = wait_till_ready(nor); @@ -777,7 +777,7 @@ static int macronix_quad_enable(struct spi_nor *nor) write_enable(nor); nor->cmd_buf[0] = val | SR_QUAD_EN_MX; - nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 1, 0); + nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); if (wait_till_ready(nor)) return 1; @@ -802,7 +802,7 @@ static int write_sr_cr(struct spi_nor *nor, u16 val) nor->cmd_buf[0] = val & 0xff; nor->cmd_buf[1] = (val >> 8); - return nor->write_reg(nor, OPCODE_WRSR, nor->cmd_buf, 2, 0); + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0); } static int spansion_quad_enable(struct spi_nor *nor) @@ -967,13 +967,13 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { - nor->erase_opcode = OPCODE_BE_4K; + nor->erase_opcode = SPINOR_OP_BE_4K; mtd->erasesize = 4096; } else if (info->flags & SECT_4K_PMC) { - nor->erase_opcode = OPCODE_BE_4K_PMC; + nor->erase_opcode = SPINOR_OP_BE_4K_PMC; mtd->erasesize = 4096; } else { - nor->erase_opcode = OPCODE_SE; + nor->erase_opcode = SPINOR_OP_SE; mtd->erasesize = info->sector_size; } @@ -1014,23 +1014,23 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Default commands */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = OPCODE_QUAD_READ; + nor->read_opcode = SPINOR_OP_QUAD_READ; break; case SPI_NOR_DUAL: - nor->read_opcode = OPCODE_DUAL_READ; + nor->read_opcode = SPINOR_OP_DUAL_READ; break; case SPI_NOR_FAST: - nor->read_opcode = OPCODE_FAST_READ; + nor->read_opcode = SPINOR_OP_FAST_READ; break; case SPI_NOR_NORMAL: - nor->read_opcode = OPCODE_NORM_READ; + nor->read_opcode = SPINOR_OP_NORM_READ; break; default: dev_err(dev, "No Read opcode defined\n"); return -EINVAL; } - nor->program_opcode = OPCODE_PP; + nor->program_opcode = SPINOR_OP_PP; if (info->addr_width) nor->addr_width = info->addr_width; @@ -1041,21 +1041,21 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Dedicated 4-byte command set */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = OPCODE_QUAD_READ_4B; + nor->read_opcode = SPINOR_OP_QUAD_READ_4B; break; case SPI_NOR_DUAL: - nor->read_opcode = OPCODE_DUAL_READ_4B; + nor->read_opcode = SPINOR_OP_DUAL_READ_4B; break; case SPI_NOR_FAST: - nor->read_opcode = OPCODE_FAST_READ_4B; + nor->read_opcode = SPINOR_OP_FAST_READ_4B; break; case SPI_NOR_NORMAL: - nor->read_opcode = OPCODE_NORM_READ_4B; + nor->read_opcode = SPINOR_OP_NORM_READ_4B; break; } - nor->program_opcode = OPCODE_PP_4B; + nor->program_opcode = SPINOR_OP_PP_4B; /* No small sector erase for 4-byte command set */ - nor->erase_opcode = OPCODE_SE_4B; + nor->erase_opcode = SPINOR_OP_SE_4B; mtd->erasesize = info->sector_size; } else set_4byte(nor, info->jedec_id, 1); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index a6e87190ead1..f1fe1a6659a3 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -11,41 +11,41 @@ #define __LINUX_MTD_SPI_NOR_H /* Flash opcodes. */ -#define OPCODE_WREN 0x06 /* Write enable */ -#define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ -#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ -#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ -#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ -#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ -#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ -#define OPCODE_RDID 0x9f /* Read JEDEC ID */ -#define OPCODE_RDCR 0x35 /* Read configuration register */ +#define SPINOR_OP_WREN 0x06 /* Write enable */ +#define SPINOR_OP_RDSR 0x05 /* Read status register */ +#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ +#define SPINOR_OP_NORM_READ 0x03 /* Read data bytes (low frequency) */ +#define SPINOR_OP_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define SPINOR_OP_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ +#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ +#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */ +#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */ +#define SPINOR_OP_RDCR 0x35 /* Read configuration register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ -#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ -#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ -#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ +#define SPINOR_OP_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define SPINOR_OP_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define SPINOR_OP_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ /* Used for SST flashes only. */ -#define OPCODE_BP 0x02 /* Byte program */ -#define OPCODE_WRDI 0x04 /* Write disable */ -#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ +#define SPINOR_OP_BP 0x02 /* Byte program */ +#define SPINOR_OP_WRDI 0x04 /* Write disable */ +#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ /* Used for Macronix and Winbond flashes. */ -#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ -#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ +#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ +#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ /* Used for Spansion flashes only. */ -#define OPCODE_BRWR 0x17 /* Bank register write */ +#define SPINOR_OP_BRWR 0x17 /* Bank register write */ /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ -- cgit v1.2.3 From 58b89a1f4c2a65b10b8f7b90b6ff2161b19bb0d1 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 8 Apr 2014 19:16:49 -0700 Subject: mtd: spi-nor: unify read opcode variants with ST SPI FSM serial_flash_cmds.h defines our opcodes a little differently. Let's borrow its naming, since it's borrowed from the SFDP standard, and it's more extensible. This prepares us for merging serial_flash_cmds.h and spi-nor.h opcode listing. Signed-off-by: Brian Norris Reviewed-by: Marek Vasut Acked-by: Huang Shijie --- drivers/mtd/spi-nor/fsl-quadspi.c | 6 +++--- drivers/mtd/spi-nor/spi-nor.c | 16 ++++++++-------- include/linux/mtd/spi-nor.h | 24 ++++++++++++++++-------- 3 files changed, 27 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 2977f026f39d..b41bbbc531ff 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -294,12 +294,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) lut_base = SEQID_QUAD_READ * 4; if (q->nor_size <= SZ_16M) { - cmd = SPINOR_OP_QUAD_READ; + cmd = SPINOR_OP_READ_1_1_4; addrlen = ADDR24BIT; dummy = 8; } else { /* use the 4-byte address */ - cmd = SPINOR_OP_QUAD_READ; + cmd = SPINOR_OP_READ_1_1_4; addrlen = ADDR32BIT; dummy = 8; } @@ -388,7 +388,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) { switch (cmd) { - case SPINOR_OP_QUAD_READ: + case SPINOR_OP_READ_1_1_4: return SEQID_QUAD_READ; case SPINOR_OP_WREN: return SEQID_WREN; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1716f3ce9949..d6f44d527701 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1014,16 +1014,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Default commands */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = SPINOR_OP_QUAD_READ; + nor->read_opcode = SPINOR_OP_READ_1_1_4; break; case SPI_NOR_DUAL: - nor->read_opcode = SPINOR_OP_DUAL_READ; + nor->read_opcode = SPINOR_OP_READ_1_1_2; break; case SPI_NOR_FAST: - nor->read_opcode = SPINOR_OP_FAST_READ; + nor->read_opcode = SPINOR_OP_READ_FAST; break; case SPI_NOR_NORMAL: - nor->read_opcode = SPINOR_OP_NORM_READ; + nor->read_opcode = SPINOR_OP_READ; break; default: dev_err(dev, "No Read opcode defined\n"); @@ -1041,16 +1041,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, /* Dedicated 4-byte command set */ switch (nor->flash_read) { case SPI_NOR_QUAD: - nor->read_opcode = SPINOR_OP_QUAD_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_1_1_4; break; case SPI_NOR_DUAL: - nor->read_opcode = SPINOR_OP_DUAL_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_1_1_2; break; case SPI_NOR_FAST: - nor->read_opcode = SPINOR_OP_FAST_READ_4B; + nor->read_opcode = SPINOR_OP_READ4_FAST; break; case SPI_NOR_NORMAL: - nor->read_opcode = SPINOR_OP_NORM_READ_4B; + nor->read_opcode = SPINOR_OP_READ4; break; } nor->program_opcode = SPINOR_OP_PP_4B; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index f1fe1a6659a3..53241842a7ab 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -10,14 +10,22 @@ #ifndef __LINUX_MTD_SPI_NOR_H #define __LINUX_MTD_SPI_NOR_H +/* + * Note on opcode nomenclature: some opcodes have a format like + * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number + * of I/O lines used for the opcode, address, and data (respectively). The + * FUNCTION has an optional suffix of '4', to represent an opcode which + * requires a 4-byte (32-bit) address. + */ + /* Flash opcodes. */ #define SPINOR_OP_WREN 0x06 /* Write enable */ #define SPINOR_OP_RDSR 0x05 /* Read status register */ #define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ -#define SPINOR_OP_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define SPINOR_OP_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define SPINOR_OP_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ -#define SPINOR_OP_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */ +#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ +#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ #define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ #define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ @@ -28,10 +36,10 @@ #define SPINOR_OP_RDCR 0x35 /* Read configuration register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define SPINOR_OP_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define SPINOR_OP_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define SPINOR_OP_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ -#define SPINOR_OP_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */ +#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ +#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ -- cgit v1.2.3 From 785b3c4e0951252cdbc0cd902292bf5c9f08897a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 14 Apr 2014 21:31:12 +0200 Subject: ASoC: Remove deprecated ENUM/MUX macros Since there are no users left, we can remove the deprecated ENUM and MUX macros which are just alias for other macros. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 12 ------------ include/sound/soc.h | 2 -- 2 files changed, 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ef78f562f4a8..5ec03b5e2054 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -107,10 +107,6 @@ struct device; { .id = snd_soc_dapm_mux, .name = wname, \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .kcontrol_news = wcontrols, .num_kcontrols = 1} -#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ - SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) -#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ - SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ @@ -166,10 +162,6 @@ struct device; SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} -#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ - wevent, wflags) \ - SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, wevent, \ - wflags) /* additional sequencing control within an event type */ #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ @@ -305,16 +297,12 @@ struct device; .get = snd_soc_dapm_get_enum_double, \ .put = snd_soc_dapm_put_enum_double, \ .private_value = (unsigned long)&xenum } -#define SOC_DAPM_ENUM_VIRT(xname, xenum) \ - SOC_DAPM_ENUM(xname, xenum) #define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ .get = xget, \ .put = xput, \ .private_value = (unsigned long)&xenum } -#define SOC_DAPM_VALUE_ENUM(xname, xenum) \ - SOC_DAPM_ENUM(xname, xenum) #define SOC_DAPM_PIN_SWITCH(xname) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \ .info = snd_soc_dapm_info_pin_switch, \ diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168d8ff4..98e85a4796b6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -196,8 +196,6 @@ .info = snd_soc_info_enum_double, \ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \ .private_value = (unsigned long)&xenum } -#define SOC_VALUE_ENUM(xname, xenum) \ - SOC_ENUM(xname, xenum) #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ -- cgit v1.2.3 From 97f53d710b9f63cbef1c86ee39d9ecfdda6e674c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 10:09:07 +0200 Subject: regulator: s2mps11: Add external GPIO control for S2MPS14 Add support for external control over GPIO for LDO10, LDO11 and LDO12 S2MPS14 regulators. External control can be turned on by writing 0x0 to control register which in case of other regulators is used for disabling them. These LDO10-LDO12 regulators can be disabled only by I2C GPIO or PWREN pin so the patch actually allows proper way of disabling them. Additionally the GPIO control has two benefits: - It is faster than toggling it over I2C bus. - It allows disabling the regulator during suspend to RAM; The AP will enable it during resume. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 67 +++++++++++++++++++++++++++++++++++-- include/linux/mfd/samsung/s2mps14.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 3aba0331fb5d..6dad0aa74a47 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ struct s2mps11_info { * was enabled. */ unsigned int s2mps14_suspend_state:30; + /* Array of size rdev_num with GPIO-s for external sleep control */ + int *ext_control_gpio; }; static int get_ramp_delay(int ramp_delay) @@ -409,6 +412,8 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev) if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) val = S2MPS14_ENABLE_SUSPEND; + else if (s2mps11->ext_control_gpio[rdev_get_id(rdev)]) + val = S2MPS14_ENABLE_EXT_CONTROL; else val = rdev->desc->enable_mask; @@ -565,8 +570,40 @@ static const struct regulator_desc s2mps14_regulators[] = { regulator_desc_s2mps14_buck1235(5), }; -static int s2mps11_pmic_dt_parse(struct platform_device *pdev, +static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11, + struct regulator_dev *rdev) +{ + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, S2MPS14_ENABLE_EXT_CONTROL); +} + +static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev, struct of_regulator_match *rdata, struct s2mps11_info *s2mps11) +{ + int *gpio = s2mps11->ext_control_gpio; + unsigned int i; + unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11, + S2MPS14_LDO12 }; + + for (i = 0; i < ARRAY_SIZE(valid_regulators); i++) { + unsigned int reg = valid_regulators[i]; + + if (!rdata[reg].init_data || !rdata[reg].of_node) + continue; + + gpio[reg] = of_get_named_gpio(rdata[reg].of_node, + "samsung,ext-control-gpios", 0); + if (!gpio_is_valid(gpio[reg])) + gpio[reg] = 0; + else + dev_dbg(&pdev->dev, "Using GPIO %d for ext-control over %d/%s\n", + gpio[reg], reg, rdata[reg].name); + } +} + +static int s2mps11_pmic_dt_parse(struct platform_device *pdev, + struct of_regulator_match *rdata, struct s2mps11_info *s2mps11, + enum sec_device_type dev_type) { struct device_node *reg_np; @@ -577,6 +614,9 @@ static int s2mps11_pmic_dt_parse(struct platform_device *pdev, } of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num); + if (dev_type == S2MPS14X) + s2mps14_pmic_dt_parse_ext_control_gpio(pdev, rdata, s2mps11); + of_node_put(reg_np); return 0; @@ -613,6 +653,12 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) return -EINVAL; }; + s2mps11->ext_control_gpio = devm_kzalloc(&pdev->dev, + sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num, + GFP_KERNEL); + if (!s2mps11->ext_control_gpio) + return -ENOMEM; + if (!iodev->dev->of_node) { if (iodev->pdata) { pdata = iodev->pdata; @@ -631,7 +677,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) for (i = 0; i < s2mps11->rdev_num; i++) rdata[i].name = regulators[i].name; - ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11); + ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11, dev_type); if (ret) goto out; @@ -652,6 +698,12 @@ common_reg: config.of_node = rdata[i].of_node; } + if (s2mps11->ext_control_gpio[i]) { + config.ena_gpio = s2mps11->ext_control_gpio[i]; + config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH; + } else + config.ena_gpio = config.ena_gpio_flags = 0; + regulator = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(regulator)) { @@ -660,6 +712,17 @@ common_reg: i); goto out; } + + if (s2mps11->ext_control_gpio[i]) { + ret = s2mps14_pmic_enable_ext_control(s2mps11, + regulator); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to enable GPIO control over %s: %d\n", + regulator->desc->name, ret); + goto out; + } + } } out: diff --git a/include/linux/mfd/samsung/s2mps14.h b/include/linux/mfd/samsung/s2mps14.h index 4b449b8ac548..900cd7a04314 100644 --- a/include/linux/mfd/samsung/s2mps14.h +++ b/include/linux/mfd/samsung/s2mps14.h @@ -148,6 +148,8 @@ enum s2mps14_regulators { #define S2MPS14_ENABLE_SHIFT 6 /* On/Off controlled by PWREN */ #define S2MPS14_ENABLE_SUSPEND (0x01 << S2MPS14_ENABLE_SHIFT) +/* On/Off controlled by LDO10EN or EMMCEN */ +#define S2MPS14_ENABLE_EXT_CONTROL (0x00 << S2MPS14_ENABLE_SHIFT) #define S2MPS14_LDO_N_VOLTAGES (S2MPS14_LDO_VSEL_MASK + 1) #define S2MPS14_BUCK_N_VOLTAGES (S2MPS14_BUCK_VSEL_MASK + 1) -- cgit v1.2.3 From 73679e50820123ebdedc67ebcda4562d1d6e4aba Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Tue, 15 Apr 2014 12:05:22 -0400 Subject: compiler-intel.h: Remove duplicate definition barrier is already defined as __memory_barrier in compiler.h Remove this unnecessary redefinition. Signed-off-by: Pranith Kumar Link: http://lkml.kernel.org/r/CAJhHMCAnYPy0%2BqD-1KBnJPLt3XgAjdR12j%2BySSnPgmZcpbE7HQ@mail.gmail.com Signed-off-by: H. Peter Anvin --- include/linux/compiler-intel.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/compiler-intel.h b/include/linux/compiler-intel.h index 5529c5239421..ba147a1727e6 100644 --- a/include/linux/compiler-intel.h +++ b/include/linux/compiler-intel.h @@ -13,12 +13,9 @@ /* Intel ECC compiler doesn't support gcc specific asm stmts. * It uses intrinsics to do the equivalent things. */ -#undef barrier #undef RELOC_HIDE #undef OPTIMIZER_HIDE_VAR -#define barrier() __memory_barrier() - #define RELOC_HIDE(ptr, off) \ ({ unsigned long __ptr; \ __ptr = (unsigned long) (ptr); \ -- cgit v1.2.3 From fdb9c293decf7e06795f7d9ae409df907c7ae1b6 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Tue, 15 Apr 2014 12:39:14 -0500 Subject: percpu: Replace __get_cpu_var with this_cpu_ptr __this_cpu_ptr is being phased out. Use raw_cpu_ptr instead which was introduced in 3.15-rc1. One case of using __get_cpu_var in the get_cpu_var macro for address calculation was remaining in include/linux/percpu.h. tj: Updated patch description. Signed-off-by: Christoph Lameter Signed-off-by: Tejun Heo --- include/linux/percpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index e7a0b95ed527..539b3caa5748 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -29,7 +29,7 @@ */ #define get_cpu_var(var) (*({ \ preempt_disable(); \ - &__get_cpu_var(var); })) + this_cpu_ptr(&var); })) /* * The weird & is necessary because sparse considers (void)(var) to be -- cgit v1.2.3 From b4f42e2831ff9b9fa19252265d7c8985d47eefb9 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 10 Apr 2014 09:46:28 -0600 Subject: block: remove struct request buffer member This was used in the olden days, back when onions were proper yellow. Basically it mapped to the current buffer to be transferred. With highmem being added more than a decade ago, most drivers map pages out of a bio, and rq->buffer isn't pointing at anything valid. Convert old style drivers to just use bio_data(). For the discard payload use case, just reference the page in the bio. Signed-off-by: Jens Axboe --- block/blk-core.c | 21 ++++++--------------- block/blk-map.c | 3 --- drivers/block/amiflop.c | 2 +- drivers/block/ataflop.c | 2 +- drivers/block/floppy.c | 18 +++++++++--------- drivers/block/hd.c | 10 +++++----- drivers/block/mg_disk.c | 12 ++++++------ drivers/block/paride/pcd.c | 2 +- drivers/block/paride/pd.c | 4 ++-- drivers/block/paride/pf.c | 4 ++-- drivers/block/skd_main.c | 5 ++--- drivers/block/swim.c | 2 +- drivers/block/swim3.c | 6 +++--- drivers/block/xen-blkfront.c | 4 ++-- drivers/block/xsysace.c | 4 ++-- drivers/block/z2ram.c | 6 ++++-- drivers/ide/ide-disk.c | 5 ++--- drivers/md/dm.c | 1 - drivers/mtd/mtd_blkdevs.c | 3 +-- drivers/mtd/ubi/block.c | 2 +- drivers/scsi/scsi_lib.c | 3 --- drivers/scsi/sd.c | 10 ++++------ include/linux/blkdev.h | 1 - 23 files changed, 55 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 1fe9ff6e6802..ae6227fd07aa 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -146,8 +146,8 @@ void blk_dump_rq_flags(struct request *rq, char *msg) printk(KERN_INFO " sector %llu, nr/cnr %u/%u\n", (unsigned long long)blk_rq_pos(rq), blk_rq_sectors(rq), blk_rq_cur_sectors(rq)); - printk(KERN_INFO " bio %p, biotail %p, buffer %p, len %u\n", - rq->bio, rq->biotail, rq->buffer, blk_rq_bytes(rq)); + printk(KERN_INFO " bio %p, biotail %p, len %u\n", + rq->bio, rq->biotail, blk_rq_bytes(rq)); if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { printk(KERN_INFO " cdb: "); @@ -1360,7 +1360,6 @@ void blk_add_request_payload(struct request *rq, struct page *page, rq->__data_len = rq->resid_len = len; rq->nr_phys_segments = 1; - rq->buffer = bio_data(bio); } EXPORT_SYMBOL_GPL(blk_add_request_payload); @@ -1402,12 +1401,6 @@ bool bio_attempt_front_merge(struct request_queue *q, struct request *req, bio->bi_next = req->bio; req->bio = bio; - /* - * may not be valid. if the low level driver said - * it didn't need a bounce buffer then it better - * not touch req->buffer either... - */ - req->buffer = bio_data(bio); req->__sector = bio->bi_iter.bi_sector; req->__data_len += bio->bi_iter.bi_size; req->ioprio = ioprio_best(req->ioprio, bio_prio(bio)); @@ -2434,7 +2427,6 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) } req->__data_len -= total_bytes; - req->buffer = bio_data(req->bio); /* update sector only for requests with clear definition of sector */ if (req->cmd_type == REQ_TYPE_FS) @@ -2752,10 +2744,9 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, /* Bit 0 (R/W) is identical in rq->cmd_flags and bio->bi_rw */ rq->cmd_flags |= bio->bi_rw & REQ_WRITE; - if (bio_has_data(bio)) { + if (bio_has_data(bio)) rq->nr_phys_segments = bio_phys_segments(q, bio); - rq->buffer = bio_data(bio); - } + rq->__data_len = bio->bi_iter.bi_size; rq->bio = rq->biotail = bio; @@ -2831,7 +2822,7 @@ EXPORT_SYMBOL_GPL(blk_rq_unprep_clone); /* * Copy attributes of the original request to the clone request. - * The actual data parts (e.g. ->cmd, ->buffer, ->sense) are not copied. + * The actual data parts (e.g. ->cmd, ->sense) are not copied. */ static void __blk_rq_prep_clone(struct request *dst, struct request *src) { @@ -2857,7 +2848,7 @@ static void __blk_rq_prep_clone(struct request *dst, struct request *src) * * Description: * Clones bios in @rq_src to @rq, and copies attributes of @rq_src to @rq. - * The actual data parts of @rq_src (e.g. ->cmd, ->buffer, ->sense) + * The actual data parts of @rq_src (e.g. ->cmd, ->sense) * are not copied, and copying such parts is the caller's responsibility. * Also, pages which the original bios are pointing to are not copied * and the cloned bios just point same pages. diff --git a/block/blk-map.c b/block/blk-map.c index f7b22bc21518..f890d4345b0c 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -155,7 +155,6 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, if (!bio_flagged(bio, BIO_USER_MAPPED)) rq->cmd_flags |= REQ_COPY_USER; - rq->buffer = NULL; return 0; unmap_rq: blk_rq_unmap_user(bio); @@ -238,7 +237,6 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, blk_queue_bounce(q, &bio); bio_get(bio); blk_rq_bio_prep(q, rq, bio); - rq->buffer = NULL; return 0; } EXPORT_SYMBOL(blk_rq_map_user_iov); @@ -325,7 +323,6 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf, } blk_queue_bounce(q, &rq->bio); - rq->buffer = NULL; return 0; } EXPORT_SYMBOL(blk_rq_map_kern); diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 748dea4f34dc..758da2287d9a 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -1406,7 +1406,7 @@ next_segment: track = block / (floppy->dtype->sects * floppy->type->sect_mult); sector = block % (floppy->dtype->sects * floppy->type->sect_mult); - data = rq->buffer + 512 * cnt; + data = bio_data(rq->bio) + 512 * cnt; #ifdef DEBUG printk("access to track %d, sector %d, with buffer at " "0x%08lx\n", track, sector, data); diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 96b629e1f0c9..7e8a55f8917c 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1484,7 +1484,7 @@ repeat: ReqCnt = 0; ReqCmd = rq_data_dir(fd_request); ReqBlock = blk_rq_pos(fd_request); - ReqBuffer = fd_request->buffer; + ReqBuffer = bio_data(fd_request->bio); setup_req_params( drive ); do_fd_action( drive ); diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 8f5565bf34cd..5f69c910c3ac 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -2351,7 +2351,7 @@ static void rw_interrupt(void) } if (CT(COMMAND) != FD_READ || - raw_cmd->kernel_data == current_req->buffer) { + raw_cmd->kernel_data == bio_data(current_req->bio)) { /* transfer directly from buffer */ cont->done(1); } else if (CT(COMMAND) == FD_READ) { @@ -2640,7 +2640,7 @@ static int make_raw_rw_request(void) raw_cmd->flags &= ~FD_RAW_WRITE; raw_cmd->flags |= FD_RAW_READ; COMMAND = FM_MODE(_floppy, FD_READ); - } else if ((unsigned long)current_req->buffer < MAX_DMA_ADDRESS) { + } else if ((unsigned long)bio_data(current_req->bio) < MAX_DMA_ADDRESS) { unsigned long dma_limit; int direct, indirect; @@ -2654,13 +2654,13 @@ static int make_raw_rw_request(void) */ max_size = buffer_chain_size(); dma_limit = (MAX_DMA_ADDRESS - - ((unsigned long)current_req->buffer)) >> 9; + ((unsigned long)bio_data(current_req->bio))) >> 9; if ((unsigned long)max_size > dma_limit) max_size = dma_limit; /* 64 kb boundaries */ - if (CROSS_64KB(current_req->buffer, max_size << 9)) + if (CROSS_64KB(bio_data(current_req->bio), max_size << 9)) max_size = (K_64 - - ((unsigned long)current_req->buffer) % + ((unsigned long)bio_data(current_req->bio)) % K_64) >> 9; direct = transfer_size(ssize, max_sector, max_size) - fsector_t; /* @@ -2677,7 +2677,7 @@ static int make_raw_rw_request(void) (DP->read_track & (1 << DRS->probed_format)))))) { max_size = blk_rq_sectors(current_req); } else { - raw_cmd->kernel_data = current_req->buffer; + raw_cmd->kernel_data = bio_data(current_req->bio); raw_cmd->length = current_count_sectors << 9; if (raw_cmd->length == 0) { DPRINT("%s: zero dma transfer attempted\n", __func__); @@ -2731,7 +2731,7 @@ static int make_raw_rw_request(void) raw_cmd->length = ((raw_cmd->length - 1) | (ssize - 1)) + 1; raw_cmd->length <<= 9; if ((raw_cmd->length < current_count_sectors << 9) || - (raw_cmd->kernel_data != current_req->buffer && + (raw_cmd->kernel_data != bio_data(current_req->bio) && CT(COMMAND) == FD_WRITE && (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max || aligned_sector_t < buffer_min)) || @@ -2739,7 +2739,7 @@ static int make_raw_rw_request(void) raw_cmd->length <= 0 || current_count_sectors <= 0) { DPRINT("fractionary current count b=%lx s=%lx\n", raw_cmd->length, current_count_sectors); - if (raw_cmd->kernel_data != current_req->buffer) + if (raw_cmd->kernel_data != bio_data(current_req->bio)) pr_info("addr=%d, length=%ld\n", (int)((raw_cmd->kernel_data - floppy_track_buffer) >> 9), @@ -2756,7 +2756,7 @@ static int make_raw_rw_request(void) return 0; } - if (raw_cmd->kernel_data != current_req->buffer) { + if (raw_cmd->kernel_data != bio_data(current_req->bio)) { if (raw_cmd->kernel_data < floppy_track_buffer || current_count_sectors < 0 || raw_cmd->length < 0 || diff --git a/drivers/block/hd.c b/drivers/block/hd.c index bf397bf108b7..8a290c08262f 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -464,11 +464,11 @@ static void read_intr(void) ok_to_read: req = hd_req; - insw(HD_DATA, req->buffer, 256); + insw(HD_DATA, bio_data(req->bio), 256); #ifdef DEBUG printk("%s: read: sector %ld, remaining = %u, buffer=%p\n", req->rq_disk->disk_name, blk_rq_pos(req) + 1, - blk_rq_sectors(req) - 1, req->buffer+512); + blk_rq_sectors(req) - 1, bio_data(req->bio)+512); #endif if (hd_end_request(0, 512)) { SET_HANDLER(&read_intr); @@ -505,7 +505,7 @@ static void write_intr(void) ok_to_write: if (hd_end_request(0, 512)) { SET_HANDLER(&write_intr); - outsw(HD_DATA, req->buffer, 256); + outsw(HD_DATA, bio_data(req->bio), 256); return; } @@ -624,7 +624,7 @@ repeat: printk("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=%p\n", req->rq_disk->disk_name, req_data_dir(req) == READ ? "read" : "writ", - cyl, head, sec, nsect, req->buffer); + cyl, head, sec, nsect, bio_data(req->bio)); #endif if (req->cmd_type == REQ_TYPE_FS) { switch (rq_data_dir(req)) { @@ -643,7 +643,7 @@ repeat: bad_rw_intr(); goto repeat; } - outsw(HD_DATA, req->buffer, 256); + outsw(HD_DATA, bio_data(req->bio), 256); break; default: printk("unknown hd-command\n"); diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c index eb59b1241366..e352cac707e8 100644 --- a/drivers/block/mg_disk.c +++ b/drivers/block/mg_disk.c @@ -479,7 +479,7 @@ static unsigned int mg_out(struct mg_host *host, static void mg_read_one(struct mg_host *host, struct request *req) { - u16 *buff = (u16 *)req->buffer; + u16 *buff = (u16 *)bio_data(req->bio); u32 i; for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) @@ -496,7 +496,7 @@ static void mg_read(struct request *req) mg_bad_rw_intr(host); MG_DBG("requested %d sects (from %ld), buffer=0x%p\n", - blk_rq_sectors(req), blk_rq_pos(req), req->buffer); + blk_rq_sectors(req), blk_rq_pos(req), bio_data(req->bio)); do { if (mg_wait(host, ATA_DRQ, @@ -514,7 +514,7 @@ static void mg_read(struct request *req) static void mg_write_one(struct mg_host *host, struct request *req) { - u16 *buff = (u16 *)req->buffer; + u16 *buff = (u16 *)bio_data(req->bio); u32 i; for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) @@ -534,7 +534,7 @@ static void mg_write(struct request *req) } MG_DBG("requested %d sects (from %ld), buffer=0x%p\n", - rem, blk_rq_pos(req), req->buffer); + rem, blk_rq_pos(req), bio_data(req->bio)); if (mg_wait(host, ATA_DRQ, MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) { @@ -585,7 +585,7 @@ ok_to_read: mg_read_one(host, req); MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n", - blk_rq_pos(req), blk_rq_sectors(req) - 1, req->buffer); + blk_rq_pos(req), blk_rq_sectors(req) - 1, bio_data(req->bio)); /* send read confirm */ outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND); @@ -624,7 +624,7 @@ ok_to_write: /* write 1 sector and set handler if remains */ mg_write_one(host, req); MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n", - blk_rq_pos(req), blk_rq_sectors(req), req->buffer); + blk_rq_pos(req), blk_rq_sectors(req), bio_data(req->bio)); host->mg_do_intr = mg_write_intr; mod_timer(&host->timer, jiffies + 3 * HZ); } diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index e76bdc074dbe..719cb1bc1640 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -747,7 +747,7 @@ static void do_pcd_request(struct request_queue * q) pcd_current = cd; pcd_sector = blk_rq_pos(pcd_req); pcd_count = blk_rq_cur_sectors(pcd_req); - pcd_buf = pcd_req->buffer; + pcd_buf = bio_data(pcd_req->bio); pcd_busy = 1; ps_set_intr(do_pcd_read, NULL, 0, nice); return; diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index 19ad8f0c83ef..fea7e76a00de 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -454,7 +454,7 @@ static enum action do_pd_io_start(void) if (pd_block + pd_count > get_capacity(pd_req->rq_disk)) return Fail; pd_run = blk_rq_sectors(pd_req); - pd_buf = pd_req->buffer; + pd_buf = bio_data(pd_req->bio); pd_retries = 0; if (pd_cmd == READ) return do_pd_read_start(); @@ -485,7 +485,7 @@ static int pd_next_buf(void) spin_lock_irqsave(&pd_lock, saved_flags); __blk_end_request_cur(pd_req, 0); pd_count = blk_rq_cur_sectors(pd_req); - pd_buf = pd_req->buffer; + pd_buf = bio_data(pd_req->bio); spin_unlock_irqrestore(&pd_lock, saved_flags); return 0; } diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c index f5c86d523ba0..9a15fd3c9349 100644 --- a/drivers/block/paride/pf.c +++ b/drivers/block/paride/pf.c @@ -795,7 +795,7 @@ repeat: } pf_cmd = rq_data_dir(pf_req); - pf_buf = pf_req->buffer; + pf_buf = bio_data(pf_req->bio); pf_retries = 0; pf_busy = 1; @@ -827,7 +827,7 @@ static int pf_next_buf(void) if (!pf_req) return 1; pf_count = blk_rq_cur_sectors(pf_req); - pf_buf = pf_req->buffer; + pf_buf = bio_data(pf_req->bio); } return 0; } diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c index a69dd93d1bd5..36bcedfd930c 100644 --- a/drivers/block/skd_main.c +++ b/drivers/block/skd_main.c @@ -563,7 +563,6 @@ skd_prep_discard_cdb(struct skd_scsi_request *scsi_req, req = skreq->req; blk_add_request_payload(req, page, len); - req->buffer = buf; } static void skd_request_fn_not_online(struct request_queue *q); @@ -856,10 +855,10 @@ static void skd_end_request(struct skd_device *skdev, if ((io_flags & REQ_DISCARD) && (skreq->discard_page == 1)) { + struct bio *bio = req->bio; pr_debug("%s:%s:%d, free the page!", skdev->name, __func__, __LINE__); - free_page((unsigned long)req->buffer); - req->buffer = NULL; + __free_page(bio->bi_io_vec->bv_page); } if (unlikely(error)) { diff --git a/drivers/block/swim.c b/drivers/block/swim.c index b02d53a399f3..6b44bbe528b7 100644 --- a/drivers/block/swim.c +++ b/drivers/block/swim.c @@ -549,7 +549,7 @@ static void redo_fd_request(struct request_queue *q) case READ: err = floppy_read_sectors(fs, blk_rq_pos(req), blk_rq_cur_sectors(req), - req->buffer); + bio_data(req->bio)); break; } done: diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index c74f7b56e7c4..523ee8fd4c15 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -342,7 +342,7 @@ static void start_request(struct floppy_state *fs) swim3_dbg("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%u buf=%p\n", req->rq_disk->disk_name, req->cmd, (long)blk_rq_pos(req), blk_rq_sectors(req), - req->buffer); + bio_data(req->bio)); swim3_dbg(" errors=%d current_nr_sectors=%u\n", req->errors, blk_rq_cur_sectors(req)); #endif @@ -479,11 +479,11 @@ static inline void setup_transfer(struct floppy_state *fs) /* Set up 3 dma commands: write preamble, data, postamble */ init_dma(cp, OUTPUT_MORE, write_preamble, sizeof(write_preamble)); ++cp; - init_dma(cp, OUTPUT_MORE, req->buffer, 512); + init_dma(cp, OUTPUT_MORE, bio_data(req->bio), 512); ++cp; init_dma(cp, OUTPUT_LAST, write_postamble, sizeof(write_postamble)); } else { - init_dma(cp, INPUT_LAST, req->buffer, n * 512); + init_dma(cp, INPUT_LAST, bio_data(req->bio), n * 512); } ++cp; out_le16(&cp->command, DBDMA_STOP); diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index efe1b4761735..283a30e88287 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -612,10 +612,10 @@ static void do_blkif_request(struct request_queue *rq) } pr_debug("do_blk_req %p: cmd %p, sec %lx, " - "(%u/%u) buffer:%p [%s]\n", + "(%u/%u) [%s]\n", req, req->cmd, (unsigned long)blk_rq_pos(req), blk_rq_cur_sectors(req), blk_rq_sectors(req), - req->buffer, rq_data_dir(req) ? "write" : "read"); + rq_data_dir(req) ? "write" : "read"); if (blkif_queue_request(req)) { blk_requeue_request(rq, req); diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 1393b8871a28..ab3ea62e5dfc 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -661,7 +661,7 @@ static void ace_fsm_dostate(struct ace_device *ace) rq_data_dir(req)); ace->req = req; - ace->data_ptr = req->buffer; + ace->data_ptr = bio_data(req->bio); ace->data_count = blk_rq_cur_sectors(req) * ACE_BUF_PER_SECTOR; ace_out32(ace, ACE_MPULBA, blk_rq_pos(req) & 0x0FFFFFFF); @@ -733,7 +733,7 @@ static void ace_fsm_dostate(struct ace_device *ace) * blk_rq_sectors(ace->req), * blk_rq_cur_sectors(ace->req)); */ - ace->data_ptr = ace->req->buffer; + ace->data_ptr = bio_data(ace->req->bio); ace->data_count = blk_rq_cur_sectors(ace->req) * 16; ace_fsm_yieldirq(ace); break; diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index 27de5046708a..968f9e52effa 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -87,13 +87,15 @@ static void do_z2_request(struct request_queue *q) while (len) { unsigned long addr = start & Z2RAM_CHUNKMASK; unsigned long size = Z2RAM_CHUNKSIZE - addr; + void *buffer = bio_data(req->bio); + if (len < size) size = len; addr += z2ram_map[ start >> Z2RAM_CHUNKSHIFT ]; if (rq_data_dir(req) == READ) - memcpy(req->buffer, (char *)addr, size); + memcpy(buffer, (char *)addr, size); else - memcpy((char *)addr, req->buffer, size); + memcpy((char *)addr, buffer, size); start += size; len -= size; } diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 16f69be820c7..ee880382e3bc 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -188,10 +188,9 @@ static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq, ledtrig_ide_activity(); - pr_debug("%s: %sing: block=%llu, sectors=%u, buffer=0x%08lx\n", + pr_debug("%s: %sing: block=%llu, sectors=%u\n", drive->name, rq_data_dir(rq) == READ ? "read" : "writ", - (unsigned long long)block, blk_rq_sectors(rq), - (unsigned long)rq->buffer); + (unsigned long long)block, blk_rq_sectors(rq)); if (hwif->rw_disk) hwif->rw_disk(drive, rq); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 455e64916498..6a71bc7c9133 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1544,7 +1544,6 @@ static int setup_clone(struct request *clone, struct request *rq, clone->cmd = rq->cmd; clone->cmd_len = rq->cmd_len; clone->sense = rq->sense; - clone->buffer = rq->buffer; clone->end_io = end_clone_request; clone->end_io_data = tio; diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 0b2ccb68c0d0..4dbfaee9aa95 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -82,8 +82,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, block = blk_rq_pos(req) << 9 >> tr->blkshift; nsect = blk_rq_cur_bytes(req) >> tr->blkshift; - - buf = req->buffer; + buf = bio_data(req->bio); if (req->cmd_type != REQ_TYPE_FS) return -EIO; diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index 7ff473c871a9..ee774ba3728d 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -253,7 +253,7 @@ static int do_ubiblock_request(struct ubiblock *dev, struct request *req) * flash access anyway. */ mutex_lock(&dev->dev_mutex); - ret = ubiblock_read(dev, req->buffer, sec, len); + ret = ubiblock_read(dev, bio_data(req->bio), sec, len); mutex_unlock(&dev->dev_mutex); return ret; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 0f3bddcb6b1a..3cc82d3dec78 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1018,8 +1018,6 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb, return BLKPREP_DEFER; } - req->buffer = NULL; - /* * Next, walk the list, and fill in the addresses and sizes of * each segment. @@ -1156,7 +1154,6 @@ int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) BUG_ON(blk_rq_bytes(req)); memset(&cmd->sdb, 0, sizeof(cmd->sdb)); - req->buffer = NULL; } cmd->cmd_len = req->cmd_len; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index efcbcd182863..06d154d20faa 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -739,14 +739,11 @@ static int sd_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq) blk_add_request_payload(rq, page, len); ret = scsi_setup_blk_pc_cmnd(sdp, rq); - rq->buffer = page_address(page); rq->__data_len = nr_bytes; out: - if (ret != BLKPREP_OK) { + if (ret != BLKPREP_OK) __free_page(page); - rq->buffer = NULL; - } return ret; } @@ -843,8 +840,9 @@ static void sd_unprep_fn(struct request_queue *q, struct request *rq) struct scsi_cmnd *SCpnt = rq->special; if (rq->cmd_flags & REQ_DISCARD) { - free_page((unsigned long)rq->buffer); - rq->buffer = NULL; + struct bio *bio = rq->bio; + + __free_page(bio->bi_io_vec->bv_page); } if (SCpnt->cmnd != rq->cmd) { mempool_free(SCpnt->cmnd, sd_cdb_pool); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 86a8df13a5fe..eb5e94803892 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -178,7 +178,6 @@ struct request { unsigned short ioprio; void *special; /* opaque pointer available for LLD use */ - char *buffer; /* kaddr of the current segment if available */ int tag; int errors; -- cgit v1.2.3 From e9b267d91f6ddbc694cb40aa962b0b2cec03971d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 15 Apr 2014 13:59:10 -0600 Subject: blk-mq: add ->init_request and ->exit_request methods The current blk_mq_init_commands/blk_mq_free_commands interface has a two problems: 1) Because only the constructor is passed to blk_mq_init_commands there is no easy way to clean up when a comman initialization failed. The current code simply leaks the allocations done in the constructor. 2) There is no good place to call blk_mq_free_commands: before blk_cleanup_queue there is no guarantee that all outstanding commands have completed, so we can't free them yet. After blk_cleanup_queue the queue has usually been freed. This can be worked around by grabbing an unconditional reference before calling blk_cleanup_queue and dropping it after blk_mq_free_commands is done, although that's not exatly pretty and driver writers are guaranteed to get it wrong sooner or later. Both issues are easily fixed by making the request constructor and destructor normal blk_mq_ops methods. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-mq.c | 105 ++++++++++++++------------------------------- drivers/block/virtio_blk.c | 23 +++++----- include/linux/blk-mq.h | 14 +++++- 3 files changed, 55 insertions(+), 87 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index e644feec068c..48d2d8495f5e 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1031,74 +1031,20 @@ static void blk_mq_hctx_notify(void *data, unsigned long action, blk_mq_put_ctx(ctx); } -static int blk_mq_init_hw_commands(struct blk_mq_hw_ctx *hctx, - int (*init)(void *, struct blk_mq_hw_ctx *, - struct request *, unsigned int), - void *data) +static void blk_mq_free_rq_map(struct blk_mq_hw_ctx *hctx, void *driver_data) { - unsigned int i; - int ret = 0; - - for (i = 0; i < hctx->queue_depth; i++) { - struct request *rq = hctx->rqs[i]; - - ret = init(data, hctx, rq, i); - if (ret) - break; - } - - return ret; -} - -int blk_mq_init_commands(struct request_queue *q, - int (*init)(void *, struct blk_mq_hw_ctx *, - struct request *, unsigned int), - void *data) -{ - struct blk_mq_hw_ctx *hctx; - unsigned int i; - int ret = 0; - - queue_for_each_hw_ctx(q, hctx, i) { - ret = blk_mq_init_hw_commands(hctx, init, data); - if (ret) - break; - } - - return ret; -} -EXPORT_SYMBOL(blk_mq_init_commands); - -static void blk_mq_free_hw_commands(struct blk_mq_hw_ctx *hctx, - void (*free)(void *, struct blk_mq_hw_ctx *, - struct request *, unsigned int), - void *data) -{ - unsigned int i; + struct page *page; - for (i = 0; i < hctx->queue_depth; i++) { - struct request *rq = hctx->rqs[i]; + if (hctx->rqs && hctx->queue->mq_ops->exit_request) { + int i; - free(data, hctx, rq, i); + for (i = 0; i < hctx->queue_depth; i++) { + if (!hctx->rqs[i]) + continue; + hctx->queue->mq_ops->exit_request(driver_data, hctx, + hctx->rqs[i], i); + } } -} - -void blk_mq_free_commands(struct request_queue *q, - void (*free)(void *, struct blk_mq_hw_ctx *, - struct request *, unsigned int), - void *data) -{ - struct blk_mq_hw_ctx *hctx; - unsigned int i; - - queue_for_each_hw_ctx(q, hctx, i) - blk_mq_free_hw_commands(hctx, free, data); -} -EXPORT_SYMBOL(blk_mq_free_commands); - -static void blk_mq_free_rq_map(struct blk_mq_hw_ctx *hctx) -{ - struct page *page; while (!list_empty(&hctx->page_list)) { page = list_first_entry(&hctx->page_list, struct page, lru); @@ -1123,10 +1069,12 @@ static size_t order_to_size(unsigned int order) } static int blk_mq_init_rq_map(struct blk_mq_hw_ctx *hctx, - unsigned int reserved_tags, int node) + struct blk_mq_reg *reg, void *driver_data, int node) { + unsigned int reserved_tags = reg->reserved_tags; unsigned int i, j, entries_per_page, max_order = 4; size_t rq_size, left; + int error; INIT_LIST_HEAD(&hctx->page_list); @@ -1175,14 +1123,23 @@ static int blk_mq_init_rq_map(struct blk_mq_hw_ctx *hctx, for (j = 0; j < to_do; j++) { hctx->rqs[i] = p; blk_rq_init(hctx->queue, hctx->rqs[i]); + if (reg->ops->init_request) { + error = reg->ops->init_request(driver_data, + hctx, hctx->rqs[i], i); + if (error) + goto err_rq_map; + } + p += rq_size; i++; } } - if (i < (reserved_tags + BLK_MQ_TAG_MIN)) + if (i < (reserved_tags + BLK_MQ_TAG_MIN)) { + error = -ENOMEM; goto err_rq_map; - else if (i != hctx->queue_depth) { + } + if (i != hctx->queue_depth) { hctx->queue_depth = i; pr_warn("%s: queue depth set to %u because of low memory\n", __func__, i); @@ -1190,12 +1147,14 @@ static int blk_mq_init_rq_map(struct blk_mq_hw_ctx *hctx, hctx->tags = blk_mq_init_tags(hctx->queue_depth, reserved_tags, node); if (!hctx->tags) { -err_rq_map: - blk_mq_free_rq_map(hctx); - return -ENOMEM; + error = -ENOMEM; + goto err_rq_map; } return 0; +err_rq_map: + blk_mq_free_rq_map(hctx, driver_data); + return error; } static int blk_mq_init_hw_queues(struct request_queue *q, @@ -1228,7 +1187,7 @@ static int blk_mq_init_hw_queues(struct request_queue *q, blk_mq_hctx_notify, hctx); blk_mq_register_cpu_notifier(&hctx->cpu_notifier); - if (blk_mq_init_rq_map(hctx, reg->reserved_tags, node)) + if (blk_mq_init_rq_map(hctx, reg, driver_data, node)) break; /* @@ -1268,7 +1227,7 @@ static int blk_mq_init_hw_queues(struct request_queue *q, reg->ops->exit_hctx(hctx, j); blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); - blk_mq_free_rq_map(hctx); + blk_mq_free_rq_map(hctx, driver_data); kfree(hctx->ctxs); } @@ -1455,7 +1414,7 @@ void blk_mq_free_queue(struct request_queue *q) queue_for_each_hw_ctx(q, hctx, i) { kfree(hctx->ctx_map); kfree(hctx->ctxs); - blk_mq_free_rq_map(hctx); + blk_mq_free_rq_map(hctx, q->queuedata); blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); if (q->mq_ops->exit_hctx) q->mq_ops->exit_hctx(hctx, i); diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index c7d02bc9d945..d06206abd340 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -480,11 +480,22 @@ static const struct device_attribute dev_attr_cache_type_rw = __ATTR(cache_type, S_IRUGO|S_IWUSR, virtblk_cache_type_show, virtblk_cache_type_store); +static int virtblk_init_request(void *data, struct blk_mq_hw_ctx *hctx, + struct request *rq, unsigned int nr) +{ + struct virtio_blk *vblk = data; + struct virtblk_req *vbr = blk_mq_rq_to_pdu(rq); + + sg_init_table(vbr->sg, vblk->sg_elems); + return 0; +} + static struct blk_mq_ops virtio_mq_ops = { .queue_rq = virtio_queue_rq, .map_queue = blk_mq_map_queue, .alloc_hctx = blk_mq_alloc_single_hw_queue, .free_hctx = blk_mq_free_single_hw_queue, + .init_request = virtblk_init_request, .complete = virtblk_request_done, }; @@ -497,16 +508,6 @@ static struct blk_mq_reg virtio_mq_reg = { }; module_param_named(queue_depth, virtio_mq_reg.queue_depth, uint, 0444); -static int virtblk_init_vbr(void *data, struct blk_mq_hw_ctx *hctx, - struct request *rq, unsigned int nr) -{ - struct virtio_blk *vblk = data; - struct virtblk_req *vbr = blk_mq_rq_to_pdu(rq); - - sg_init_table(vbr->sg, vblk->sg_elems); - return 0; -} - static int virtblk_probe(struct virtio_device *vdev) { struct virtio_blk *vblk; @@ -577,8 +578,6 @@ static int virtblk_probe(struct virtio_device *vdev) goto out_put_disk; } - blk_mq_init_commands(q, virtblk_init_vbr, vblk); - q->queuedata = vblk; virtblk_name_format("vd", index, vblk->disk->disk_name, DISK_NAME_LEN); diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index b6ee48740458..29c1a6e83814 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -67,6 +67,10 @@ typedef struct blk_mq_hw_ctx *(alloc_hctx_fn)(struct blk_mq_reg *,unsigned int); typedef void (free_hctx_fn)(struct blk_mq_hw_ctx *, unsigned int); typedef int (init_hctx_fn)(struct blk_mq_hw_ctx *, void *, unsigned int); typedef void (exit_hctx_fn)(struct blk_mq_hw_ctx *, unsigned int); +typedef int (init_request_fn)(void *, struct blk_mq_hw_ctx *, + struct request *, unsigned int); +typedef void (exit_request_fn)(void *, struct blk_mq_hw_ctx *, + struct request *, unsigned int); struct blk_mq_ops { /* @@ -99,6 +103,14 @@ struct blk_mq_ops { */ init_hctx_fn *init_hctx; exit_hctx_fn *exit_hctx; + + /* + * Called for every command allocated by the block layer to allow + * the driver to set up driver specific data. + * Ditto for exit/teardown. + */ + init_request_fn *init_request; + exit_request_fn *exit_request; }; enum { @@ -118,8 +130,6 @@ enum { struct request_queue *blk_mq_init_queue(struct blk_mq_reg *, void *); int blk_mq_register_disk(struct gendisk *); void blk_mq_unregister_disk(struct gendisk *); -int blk_mq_init_commands(struct request_queue *, int (*init)(void *data, struct blk_mq_hw_ctx *, struct request *, unsigned int), void *data); -void blk_mq_free_commands(struct request_queue *, void (*free)(void *data, struct blk_mq_hw_ctx *, struct request *, unsigned int), void *data); void blk_mq_flush_plug_list(struct blk_plug *plug, bool from_schedule); -- cgit v1.2.3 From 24d2f90309b23f2cfe016b2aebc5f0d6e01c57fd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 15 Apr 2014 14:14:00 -0600 Subject: blk-mq: split out tag initialization, support shared tags Add a new blk_mq_tag_set structure that gets set up before we initialize the queue. A single blk_mq_tag_set structure can be shared by multiple queues. Signed-off-by: Christoph Hellwig Modular export of blk_mq_{alloc,free}_tagset added by me. Signed-off-by: Jens Axboe --- block/blk-mq-cpumap.c | 6 +- block/blk-mq-tag.c | 14 --- block/blk-mq-tag.h | 19 +++- block/blk-mq.c | 244 +++++++++++++++++++++++++-------------------- block/blk-mq.h | 5 +- drivers/block/null_blk.c | 92 ++++++++++------- drivers/block/virtio_blk.c | 48 +++++---- include/linux/blk-mq.h | 34 +++---- 8 files changed, 262 insertions(+), 200 deletions(-) (limited to 'include') diff --git a/block/blk-mq-cpumap.c b/block/blk-mq-cpumap.c index 097921329619..5d0f93cf358c 100644 --- a/block/blk-mq-cpumap.c +++ b/block/blk-mq-cpumap.c @@ -80,17 +80,17 @@ int blk_mq_update_queue_map(unsigned int *map, unsigned int nr_queues) return 0; } -unsigned int *blk_mq_make_queue_map(struct blk_mq_reg *reg) +unsigned int *blk_mq_make_queue_map(struct blk_mq_tag_set *set) { unsigned int *map; /* If cpus are offline, map them to first hctx */ map = kzalloc_node(sizeof(*map) * num_possible_cpus(), GFP_KERNEL, - reg->numa_node); + set->numa_node); if (!map) return NULL; - if (!blk_mq_update_queue_map(map, reg->nr_hw_queues)) + if (!blk_mq_update_queue_map(map, set->nr_hw_queues)) return map; kfree(map); diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index 83ae96c51a27..7a799c46c32d 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -1,25 +1,11 @@ #include #include -#include #include #include "blk.h" #include "blk-mq.h" #include "blk-mq-tag.h" -/* - * Per tagged queue (tag address space) map - */ -struct blk_mq_tags { - unsigned int nr_tags; - unsigned int nr_reserved_tags; - unsigned int nr_batch_move; - unsigned int nr_max_cache; - - struct percpu_ida free_tags; - struct percpu_ida reserved_tags; -}; - void blk_mq_wait_for_tags(struct blk_mq_tags *tags) { int tag = blk_mq_get_tag(tags, __GFP_WAIT, false); diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h index 947ba2c6148e..b602e3fa66ea 100644 --- a/block/blk-mq-tag.h +++ b/block/blk-mq-tag.h @@ -1,7 +1,24 @@ #ifndef INT_BLK_MQ_TAG_H #define INT_BLK_MQ_TAG_H -struct blk_mq_tags; +#include + +/* + * Tag address space map. + */ +struct blk_mq_tags { + unsigned int nr_tags; + unsigned int nr_reserved_tags; + unsigned int nr_batch_move; + unsigned int nr_max_cache; + + struct percpu_ida free_tags; + struct percpu_ida reserved_tags; + + struct request **rqs; + struct list_head page_list; +}; + extern struct blk_mq_tags *blk_mq_init_tags(unsigned int nr_tags, unsigned int reserved_tags, int node); extern void blk_mq_free_tags(struct blk_mq_tags *tags); diff --git a/block/blk-mq.c b/block/blk-mq.c index 2a5a0fed10a3..9180052d42cc 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -81,7 +81,7 @@ static struct request *__blk_mq_alloc_request(struct blk_mq_hw_ctx *hctx, tag = blk_mq_get_tag(hctx->tags, gfp, reserved); if (tag != BLK_MQ_TAG_FAIL) { - rq = hctx->rqs[tag]; + rq = hctx->tags->rqs[tag]; blk_rq_init(hctx->queue, rq); rq->tag = tag; @@ -404,6 +404,12 @@ static void blk_mq_requeue_request(struct request *rq) rq->nr_phys_segments--; } +struct request *blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag) +{ + return tags->rqs[tag]; +} +EXPORT_SYMBOL(blk_mq_tag_to_rq); + struct blk_mq_timeout_data { struct blk_mq_hw_ctx *hctx; unsigned long *next; @@ -425,12 +431,13 @@ static void blk_mq_timeout_check(void *__data, unsigned long *free_tags) do { struct request *rq; - tag = find_next_zero_bit(free_tags, hctx->queue_depth, tag); - if (tag >= hctx->queue_depth) + tag = find_next_zero_bit(free_tags, hctx->tags->nr_tags, tag); + if (tag >= hctx->tags->nr_tags) break; - rq = hctx->rqs[tag++]; - + rq = blk_mq_tag_to_rq(hctx->tags, tag++); + if (rq->q != hctx->queue) + continue; if (!test_bit(REQ_ATOM_STARTED, &rq->atomic_flags)) continue; @@ -969,11 +976,11 @@ struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q, const int cpu) } EXPORT_SYMBOL(blk_mq_map_queue); -struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_reg *reg, +struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_tag_set *set, unsigned int hctx_index) { return kmalloc_node(sizeof(struct blk_mq_hw_ctx), - GFP_KERNEL | __GFP_ZERO, reg->numa_node); + GFP_KERNEL | __GFP_ZERO, set->numa_node); } EXPORT_SYMBOL(blk_mq_alloc_single_hw_queue); @@ -1030,31 +1037,31 @@ static void blk_mq_hctx_notify(void *data, unsigned long action, blk_mq_put_ctx(ctx); } -static void blk_mq_free_rq_map(struct blk_mq_hw_ctx *hctx, void *driver_data) +static void blk_mq_free_rq_map(struct blk_mq_tag_set *set, + struct blk_mq_tags *tags, unsigned int hctx_idx) { struct page *page; - if (hctx->rqs && hctx->queue->mq_ops->exit_request) { + if (tags->rqs && set->ops->exit_request) { int i; - for (i = 0; i < hctx->queue_depth; i++) { - if (!hctx->rqs[i]) + for (i = 0; i < tags->nr_tags; i++) { + if (!tags->rqs[i]) continue; - hctx->queue->mq_ops->exit_request(driver_data, hctx, - hctx->rqs[i], i); + set->ops->exit_request(set->driver_data, tags->rqs[i], + hctx_idx, i); } } - while (!list_empty(&hctx->page_list)) { - page = list_first_entry(&hctx->page_list, struct page, lru); + while (!list_empty(&tags->page_list)) { + page = list_first_entry(&tags->page_list, struct page, lru); list_del_init(&page->lru); __free_pages(page, page->private); } - kfree(hctx->rqs); + kfree(tags->rqs); - if (hctx->tags) - blk_mq_free_tags(hctx->tags); + blk_mq_free_tags(tags); } static size_t order_to_size(unsigned int order) @@ -1067,30 +1074,36 @@ static size_t order_to_size(unsigned int order) return ret; } -static int blk_mq_init_rq_map(struct blk_mq_hw_ctx *hctx, - struct blk_mq_reg *reg, void *driver_data, int node) +static struct blk_mq_tags *blk_mq_init_rq_map(struct blk_mq_tag_set *set, + unsigned int hctx_idx) { - unsigned int reserved_tags = reg->reserved_tags; + struct blk_mq_tags *tags; unsigned int i, j, entries_per_page, max_order = 4; size_t rq_size, left; - int error; - INIT_LIST_HEAD(&hctx->page_list); + tags = blk_mq_init_tags(set->queue_depth, set->reserved_tags, + set->numa_node); + if (!tags) + return NULL; - hctx->rqs = kmalloc_node(hctx->queue_depth * sizeof(struct request *), - GFP_KERNEL, node); - if (!hctx->rqs) - return -ENOMEM; + INIT_LIST_HEAD(&tags->page_list); + + tags->rqs = kmalloc_node(set->queue_depth * sizeof(struct request *), + GFP_KERNEL, set->numa_node); + if (!tags->rqs) { + blk_mq_free_tags(tags); + return NULL; + } /* * rq_size is the size of the request plus driver payload, rounded * to the cacheline size */ - rq_size = round_up(sizeof(struct request) + hctx->cmd_size, + rq_size = round_up(sizeof(struct request) + set->cmd_size, cache_line_size()); - left = rq_size * hctx->queue_depth; + left = rq_size * set->queue_depth; - for (i = 0; i < hctx->queue_depth;) { + for (i = 0; i < set->queue_depth; ) { int this_order = max_order; struct page *page; int to_do; @@ -1100,7 +1113,8 @@ static int blk_mq_init_rq_map(struct blk_mq_hw_ctx *hctx, this_order--; do { - page = alloc_pages_node(node, GFP_KERNEL, this_order); + page = alloc_pages_node(set->numa_node, GFP_KERNEL, + this_order); if (page) break; if (!this_order--) @@ -1110,22 +1124,22 @@ static int blk_mq_init_rq_map(struct blk_mq_hw_ctx *hctx, } while (1); if (!page) - break; + goto fail; page->private = this_order; - list_add_tail(&page->lru, &hctx->page_list); + list_add_tail(&page->lru, &tags->page_list); p = page_address(page); entries_per_page = order_to_size(this_order) / rq_size; - to_do = min(entries_per_page, hctx->queue_depth - i); + to_do = min(entries_per_page, set->queue_depth - i); left -= to_do * rq_size; for (j = 0; j < to_do; j++) { - hctx->rqs[i] = p; - if (reg->ops->init_request) { - error = reg->ops->init_request(driver_data, - hctx, hctx->rqs[i], i); - if (error) - goto err_rq_map; + tags->rqs[i] = p; + if (set->ops->init_request) { + if (set->ops->init_request(set->driver_data, + tags->rqs[i], hctx_idx, i, + set->numa_node)) + goto fail; } p += rq_size; @@ -1133,30 +1147,16 @@ static int blk_mq_init_rq_map(struct blk_mq_hw_ctx *hctx, } } - if (i < (reserved_tags + BLK_MQ_TAG_MIN)) { - error = -ENOMEM; - goto err_rq_map; - } - if (i != hctx->queue_depth) { - hctx->queue_depth = i; - pr_warn("%s: queue depth set to %u because of low memory\n", - __func__, i); - } + return tags; - hctx->tags = blk_mq_init_tags(hctx->queue_depth, reserved_tags, node); - if (!hctx->tags) { - error = -ENOMEM; - goto err_rq_map; - } - - return 0; -err_rq_map: - blk_mq_free_rq_map(hctx, driver_data); - return error; +fail: + pr_warn("%s: failed to allocate requests\n", __func__); + blk_mq_free_rq_map(set, tags, hctx_idx); + return NULL; } static int blk_mq_init_hw_queues(struct request_queue *q, - struct blk_mq_reg *reg, void *driver_data) + struct blk_mq_tag_set *set) { struct blk_mq_hw_ctx *hctx; unsigned int i, j; @@ -1170,23 +1170,21 @@ static int blk_mq_init_hw_queues(struct request_queue *q, node = hctx->numa_node; if (node == NUMA_NO_NODE) - node = hctx->numa_node = reg->numa_node; + node = hctx->numa_node = set->numa_node; INIT_DELAYED_WORK(&hctx->delayed_work, blk_mq_work_fn); spin_lock_init(&hctx->lock); INIT_LIST_HEAD(&hctx->dispatch); hctx->queue = q; hctx->queue_num = i; - hctx->flags = reg->flags; - hctx->queue_depth = reg->queue_depth; - hctx->cmd_size = reg->cmd_size; + hctx->flags = set->flags; + hctx->cmd_size = set->cmd_size; blk_mq_init_cpu_notifier(&hctx->cpu_notifier, blk_mq_hctx_notify, hctx); blk_mq_register_cpu_notifier(&hctx->cpu_notifier); - if (blk_mq_init_rq_map(hctx, reg, driver_data, node)) - break; + hctx->tags = set->tags[i]; /* * Allocate space for all possible cpus to avoid allocation in @@ -1206,8 +1204,8 @@ static int blk_mq_init_hw_queues(struct request_queue *q, hctx->nr_ctx_map = num_maps; hctx->nr_ctx = 0; - if (reg->ops->init_hctx && - reg->ops->init_hctx(hctx, driver_data, i)) + if (set->ops->init_hctx && + set->ops->init_hctx(hctx, set->driver_data, i)) break; } @@ -1221,11 +1219,10 @@ static int blk_mq_init_hw_queues(struct request_queue *q, if (i == j) break; - if (reg->ops->exit_hctx) - reg->ops->exit_hctx(hctx, j); + if (set->ops->exit_hctx) + set->ops->exit_hctx(hctx, j); blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); - blk_mq_free_rq_map(hctx, driver_data); kfree(hctx->ctxs); } @@ -1290,41 +1287,25 @@ static void blk_mq_map_swqueue(struct request_queue *q) } } -struct request_queue *blk_mq_init_queue(struct blk_mq_reg *reg, - void *driver_data) +struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) { struct blk_mq_hw_ctx **hctxs; struct blk_mq_ctx *ctx; struct request_queue *q; int i; - if (!reg->nr_hw_queues || - !reg->ops->queue_rq || !reg->ops->map_queue || - !reg->ops->alloc_hctx || !reg->ops->free_hctx) - return ERR_PTR(-EINVAL); - - if (!reg->queue_depth) - reg->queue_depth = BLK_MQ_MAX_DEPTH; - else if (reg->queue_depth > BLK_MQ_MAX_DEPTH) { - pr_err("blk-mq: queuedepth too large (%u)\n", reg->queue_depth); - reg->queue_depth = BLK_MQ_MAX_DEPTH; - } - - if (reg->queue_depth < (reg->reserved_tags + BLK_MQ_TAG_MIN)) - return ERR_PTR(-EINVAL); - ctx = alloc_percpu(struct blk_mq_ctx); if (!ctx) return ERR_PTR(-ENOMEM); - hctxs = kmalloc_node(reg->nr_hw_queues * sizeof(*hctxs), GFP_KERNEL, - reg->numa_node); + hctxs = kmalloc_node(set->nr_hw_queues * sizeof(*hctxs), GFP_KERNEL, + set->numa_node); if (!hctxs) goto err_percpu; - for (i = 0; i < reg->nr_hw_queues; i++) { - hctxs[i] = reg->ops->alloc_hctx(reg, i); + for (i = 0; i < set->nr_hw_queues; i++) { + hctxs[i] = set->ops->alloc_hctx(set, i); if (!hctxs[i]) goto err_hctxs; @@ -1335,11 +1316,11 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_reg *reg, hctxs[i]->queue_num = i; } - q = blk_alloc_queue_node(GFP_KERNEL, reg->numa_node); + q = blk_alloc_queue_node(GFP_KERNEL, set->numa_node); if (!q) goto err_hctxs; - q->mq_map = blk_mq_make_queue_map(reg); + q->mq_map = blk_mq_make_queue_map(set); if (!q->mq_map) goto err_map; @@ -1347,33 +1328,34 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_reg *reg, blk_queue_rq_timeout(q, 30000); q->nr_queues = nr_cpu_ids; - q->nr_hw_queues = reg->nr_hw_queues; + q->nr_hw_queues = set->nr_hw_queues; q->queue_ctx = ctx; q->queue_hw_ctx = hctxs; - q->mq_ops = reg->ops; + q->mq_ops = set->ops; q->queue_flags |= QUEUE_FLAG_MQ_DEFAULT; q->sg_reserved_size = INT_MAX; blk_queue_make_request(q, blk_mq_make_request); - blk_queue_rq_timed_out(q, reg->ops->timeout); - if (reg->timeout) - blk_queue_rq_timeout(q, reg->timeout); + blk_queue_rq_timed_out(q, set->ops->timeout); + if (set->timeout) + blk_queue_rq_timeout(q, set->timeout); - if (reg->ops->complete) - blk_queue_softirq_done(q, reg->ops->complete); + if (set->ops->complete) + blk_queue_softirq_done(q, set->ops->complete); blk_mq_init_flush(q); - blk_mq_init_cpu_queues(q, reg->nr_hw_queues); + blk_mq_init_cpu_queues(q, set->nr_hw_queues); - q->flush_rq = kzalloc(round_up(sizeof(struct request) + reg->cmd_size, - cache_line_size()), GFP_KERNEL); + q->flush_rq = kzalloc(round_up(sizeof(struct request) + + set->cmd_size, cache_line_size()), + GFP_KERNEL); if (!q->flush_rq) goto err_hw; - if (blk_mq_init_hw_queues(q, reg, driver_data)) + if (blk_mq_init_hw_queues(q, set)) goto err_flush_rq; blk_mq_map_swqueue(q); @@ -1391,11 +1373,11 @@ err_hw: err_map: blk_cleanup_queue(q); err_hctxs: - for (i = 0; i < reg->nr_hw_queues; i++) { + for (i = 0; i < set->nr_hw_queues; i++) { if (!hctxs[i]) break; free_cpumask_var(hctxs[i]->cpumask); - reg->ops->free_hctx(hctxs[i], i); + set->ops->free_hctx(hctxs[i], i); } kfree(hctxs); err_percpu: @@ -1412,7 +1394,6 @@ void blk_mq_free_queue(struct request_queue *q) queue_for_each_hw_ctx(q, hctx, i) { kfree(hctx->ctx_map); kfree(hctx->ctxs); - blk_mq_free_rq_map(hctx, q->queuedata); blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); if (q->mq_ops->exit_hctx) q->mq_ops->exit_hctx(hctx, i); @@ -1473,6 +1454,53 @@ static int blk_mq_queue_reinit_notify(struct notifier_block *nb, return NOTIFY_OK; } +int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set) +{ + int i; + + if (!set->nr_hw_queues) + return -EINVAL; + if (!set->queue_depth || set->queue_depth > BLK_MQ_MAX_DEPTH) + return -EINVAL; + if (set->queue_depth < set->reserved_tags + BLK_MQ_TAG_MIN) + return -EINVAL; + + if (!set->nr_hw_queues || + !set->ops->queue_rq || !set->ops->map_queue || + !set->ops->alloc_hctx || !set->ops->free_hctx) + return -EINVAL; + + + set->tags = kmalloc_node(set->nr_hw_queues * sizeof(struct blk_mq_tags), + GFP_KERNEL, set->numa_node); + if (!set->tags) + goto out; + + for (i = 0; i < set->nr_hw_queues; i++) { + set->tags[i] = blk_mq_init_rq_map(set, i); + if (!set->tags[i]) + goto out_unwind; + } + + return 0; + +out_unwind: + while (--i >= 0) + blk_mq_free_rq_map(set, set->tags[i], i); +out: + return -ENOMEM; +} +EXPORT_SYMBOL(blk_mq_alloc_tag_set); + +void blk_mq_free_tag_set(struct blk_mq_tag_set *set) +{ + int i; + + for (i = 0; i < set->nr_hw_queues; i++) + blk_mq_free_rq_map(set, set->tags[i], i); +} +EXPORT_SYMBOL(blk_mq_free_tag_set); + void blk_mq_disable_hotplug(void) { mutex_lock(&all_q_mutex); diff --git a/block/blk-mq.h b/block/blk-mq.h index 7964dadb7d64..5fa14f19f752 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -1,6 +1,8 @@ #ifndef INT_BLK_MQ_H #define INT_BLK_MQ_H +struct blk_mq_tag_set; + struct blk_mq_ctx { struct { spinlock_t lock; @@ -46,8 +48,7 @@ void blk_mq_disable_hotplug(void); /* * CPU -> queue mappings */ -struct blk_mq_reg; -extern unsigned int *blk_mq_make_queue_map(struct blk_mq_reg *reg); +extern unsigned int *blk_mq_make_queue_map(struct blk_mq_tag_set *set); extern int blk_mq_update_queue_map(unsigned int *map, unsigned int nr_queues); void blk_mq_add_timer(struct request *rq); diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 71df69d90900..8e7e3a0b0d24 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -32,6 +32,7 @@ struct nullb { unsigned int index; struct request_queue *q; struct gendisk *disk; + struct blk_mq_tag_set tag_set; struct hrtimer timer; unsigned int queue_depth; spinlock_t lock; @@ -320,10 +321,11 @@ static int null_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *rq) return BLK_MQ_RQ_QUEUE_OK; } -static struct blk_mq_hw_ctx *null_alloc_hctx(struct blk_mq_reg *reg, unsigned int hctx_index) +static struct blk_mq_hw_ctx *null_alloc_hctx(struct blk_mq_tag_set *set, + unsigned int hctx_index) { - int b_size = DIV_ROUND_UP(reg->nr_hw_queues, nr_online_nodes); - int tip = (reg->nr_hw_queues % nr_online_nodes); + int b_size = DIV_ROUND_UP(set->nr_hw_queues, nr_online_nodes); + int tip = (set->nr_hw_queues % nr_online_nodes); int node = 0, i, n; /* @@ -338,7 +340,7 @@ static struct blk_mq_hw_ctx *null_alloc_hctx(struct blk_mq_reg *reg, unsigned in tip--; if (!tip) - b_size = reg->nr_hw_queues / nr_online_nodes; + b_size = set->nr_hw_queues / nr_online_nodes; } } @@ -387,13 +389,17 @@ static struct blk_mq_ops null_mq_ops = { .map_queue = blk_mq_map_queue, .init_hctx = null_init_hctx, .complete = null_softirq_done_fn, + .alloc_hctx = blk_mq_alloc_single_hw_queue, + .free_hctx = blk_mq_free_single_hw_queue, }; -static struct blk_mq_reg null_mq_reg = { - .ops = &null_mq_ops, - .queue_depth = 64, - .cmd_size = sizeof(struct nullb_cmd), - .flags = BLK_MQ_F_SHOULD_MERGE, +static struct blk_mq_ops null_mq_ops_pernode = { + .queue_rq = null_queue_rq, + .map_queue = blk_mq_map_queue, + .init_hctx = null_init_hctx, + .complete = null_softirq_done_fn, + .alloc_hctx = null_alloc_hctx, + .free_hctx = null_free_hctx, }; static void null_del_dev(struct nullb *nullb) @@ -402,6 +408,8 @@ static void null_del_dev(struct nullb *nullb) del_gendisk(nullb->disk); blk_cleanup_queue(nullb->q); + if (queue_mode == NULL_Q_MQ) + blk_mq_free_tag_set(&nullb->tag_set); put_disk(nullb->disk); kfree(nullb); } @@ -506,7 +514,7 @@ static int null_add_dev(void) nullb = kzalloc_node(sizeof(*nullb), GFP_KERNEL, home_node); if (!nullb) - return -ENOMEM; + goto out; spin_lock_init(&nullb->lock); @@ -514,49 +522,47 @@ static int null_add_dev(void) submit_queues = nr_online_nodes; if (setup_queues(nullb)) - goto err; + goto out_free_nullb; if (queue_mode == NULL_Q_MQ) { - null_mq_reg.numa_node = home_node; - null_mq_reg.queue_depth = hw_queue_depth; - null_mq_reg.nr_hw_queues = submit_queues; - - if (use_per_node_hctx) { - null_mq_reg.ops->alloc_hctx = null_alloc_hctx; - null_mq_reg.ops->free_hctx = null_free_hctx; - } else { - null_mq_reg.ops->alloc_hctx = blk_mq_alloc_single_hw_queue; - null_mq_reg.ops->free_hctx = blk_mq_free_single_hw_queue; - } - - nullb->q = blk_mq_init_queue(&null_mq_reg, nullb); + if (use_per_node_hctx) + nullb->tag_set.ops = &null_mq_ops_pernode; + else + nullb->tag_set.ops = &null_mq_ops; + nullb->tag_set.nr_hw_queues = submit_queues; + nullb->tag_set.queue_depth = hw_queue_depth; + nullb->tag_set.numa_node = home_node; + nullb->tag_set.cmd_size = sizeof(struct nullb_cmd); + nullb->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; + nullb->tag_set.driver_data = nullb; + + if (blk_mq_alloc_tag_set(&nullb->tag_set)) + goto out_cleanup_queues; + + nullb->q = blk_mq_init_queue(&nullb->tag_set); + if (!nullb->q) + goto out_cleanup_tags; } else if (queue_mode == NULL_Q_BIO) { nullb->q = blk_alloc_queue_node(GFP_KERNEL, home_node); + if (!nullb->q) + goto out_cleanup_queues; blk_queue_make_request(nullb->q, null_queue_bio); init_driver_queues(nullb); } else { nullb->q = blk_init_queue_node(null_request_fn, &nullb->lock, home_node); + if (!nullb->q) + goto out_cleanup_queues; blk_queue_prep_rq(nullb->q, null_rq_prep_fn); - if (nullb->q) - blk_queue_softirq_done(nullb->q, null_softirq_done_fn); + blk_queue_softirq_done(nullb->q, null_softirq_done_fn); init_driver_queues(nullb); } - if (!nullb->q) - goto queue_fail; - nullb->q->queuedata = nullb; queue_flag_set_unlocked(QUEUE_FLAG_NONROT, nullb->q); disk = nullb->disk = alloc_disk_node(1, home_node); - if (!disk) { -queue_fail: - blk_cleanup_queue(nullb->q); - cleanup_queues(nullb); -err: - kfree(nullb); - return -ENOMEM; - } + if (!disk) + goto out_cleanup_blk_queue; mutex_lock(&lock); list_add_tail(&nullb->list, &nullb_list); @@ -579,6 +585,18 @@ err: sprintf(disk->disk_name, "nullb%d", nullb->index); add_disk(disk); return 0; + +out_cleanup_blk_queue: + blk_cleanup_queue(nullb->q); +out_cleanup_tags: + if (queue_mode == NULL_Q_MQ) + blk_mq_free_tag_set(&nullb->tag_set); +out_cleanup_queues: + cleanup_queues(nullb); +out_free_nullb: + kfree(nullb); +out: + return -ENOMEM; } static int __init null_init(void) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index d06206abd340..f909a8821e65 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -30,6 +30,9 @@ struct virtio_blk /* The disk structure for the kernel. */ struct gendisk *disk; + /* Block layer tags. */ + struct blk_mq_tag_set tag_set; + /* Process context for config space updates */ struct work_struct config_work; @@ -480,8 +483,9 @@ static const struct device_attribute dev_attr_cache_type_rw = __ATTR(cache_type, S_IRUGO|S_IWUSR, virtblk_cache_type_show, virtblk_cache_type_store); -static int virtblk_init_request(void *data, struct blk_mq_hw_ctx *hctx, - struct request *rq, unsigned int nr) +static int virtblk_init_request(void *data, struct request *rq, + unsigned int hctx_idx, unsigned int request_idx, + unsigned int numa_node) { struct virtio_blk *vblk = data; struct virtblk_req *vbr = blk_mq_rq_to_pdu(rq); @@ -495,18 +499,12 @@ static struct blk_mq_ops virtio_mq_ops = { .map_queue = blk_mq_map_queue, .alloc_hctx = blk_mq_alloc_single_hw_queue, .free_hctx = blk_mq_free_single_hw_queue, - .init_request = virtblk_init_request, .complete = virtblk_request_done, + .init_request = virtblk_init_request, }; -static struct blk_mq_reg virtio_mq_reg = { - .ops = &virtio_mq_ops, - .nr_hw_queues = 1, - .queue_depth = 0, /* Set in virtblk_probe */ - .numa_node = NUMA_NO_NODE, - .flags = BLK_MQ_F_SHOULD_MERGE, -}; -module_param_named(queue_depth, virtio_mq_reg.queue_depth, uint, 0444); +static unsigned int virtblk_queue_depth; +module_param_named(queue_depth, virtblk_queue_depth, uint, 0444); static int virtblk_probe(struct virtio_device *vdev) { @@ -562,20 +560,32 @@ static int virtblk_probe(struct virtio_device *vdev) } /* Default queue sizing is to fill the ring. */ - if (!virtio_mq_reg.queue_depth) { - virtio_mq_reg.queue_depth = vblk->vq->num_free; + if (!virtblk_queue_depth) { + virtblk_queue_depth = vblk->vq->num_free; /* ... but without indirect descs, we use 2 descs per req */ if (!virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC)) - virtio_mq_reg.queue_depth /= 2; + virtblk_queue_depth /= 2; } - virtio_mq_reg.cmd_size = + + memset(&vblk->tag_set, 0, sizeof(vblk->tag_set)); + vblk->tag_set.ops = &virtio_mq_ops; + vblk->tag_set.nr_hw_queues = 1; + vblk->tag_set.queue_depth = virtblk_queue_depth; + vblk->tag_set.numa_node = NUMA_NO_NODE; + vblk->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; + vblk->tag_set.cmd_size = sizeof(struct virtblk_req) + sizeof(struct scatterlist) * sg_elems; + vblk->tag_set.driver_data = vblk; - q = vblk->disk->queue = blk_mq_init_queue(&virtio_mq_reg, vblk); + err = blk_mq_alloc_tag_set(&vblk->tag_set); + if (err) + goto out_put_disk; + + q = vblk->disk->queue = blk_mq_init_queue(&vblk->tag_set); if (!q) { err = -ENOMEM; - goto out_put_disk; + goto out_free_tags; } q->queuedata = vblk; @@ -678,6 +688,8 @@ static int virtblk_probe(struct virtio_device *vdev) out_del_disk: del_gendisk(vblk->disk); blk_cleanup_queue(vblk->disk->queue); +out_free_tags: + blk_mq_free_tag_set(&vblk->tag_set); out_put_disk: put_disk(vblk->disk); out_free_vq: @@ -704,6 +716,8 @@ static void virtblk_remove(struct virtio_device *vdev) del_gendisk(vblk->disk); blk_cleanup_queue(vblk->disk->queue); + blk_mq_free_tag_set(&vblk->tag_set); + /* Stop all the virtqueues. */ vdev->config->reset(vdev); diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 29c1a6e83814..a4ea0ce83b07 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -33,8 +33,6 @@ struct blk_mq_hw_ctx { unsigned int nr_ctx_map; unsigned long *ctx_map; - struct request **rqs; - struct list_head page_list; struct blk_mq_tags *tags; unsigned long queued; @@ -42,7 +40,6 @@ struct blk_mq_hw_ctx { #define BLK_MQ_MAX_DISPATCH_ORDER 10 unsigned long dispatched[BLK_MQ_MAX_DISPATCH_ORDER]; - unsigned int queue_depth; unsigned int numa_node; unsigned int cmd_size; /* per-request extra data */ @@ -50,7 +47,7 @@ struct blk_mq_hw_ctx { struct kobject kobj; }; -struct blk_mq_reg { +struct blk_mq_tag_set { struct blk_mq_ops *ops; unsigned int nr_hw_queues; unsigned int queue_depth; @@ -59,18 +56,22 @@ struct blk_mq_reg { int numa_node; unsigned int timeout; unsigned int flags; /* BLK_MQ_F_* */ + void *driver_data; + + struct blk_mq_tags **tags; }; typedef int (queue_rq_fn)(struct blk_mq_hw_ctx *, struct request *); typedef struct blk_mq_hw_ctx *(map_queue_fn)(struct request_queue *, const int); -typedef struct blk_mq_hw_ctx *(alloc_hctx_fn)(struct blk_mq_reg *,unsigned int); +typedef struct blk_mq_hw_ctx *(alloc_hctx_fn)(struct blk_mq_tag_set *, + unsigned int); typedef void (free_hctx_fn)(struct blk_mq_hw_ctx *, unsigned int); typedef int (init_hctx_fn)(struct blk_mq_hw_ctx *, void *, unsigned int); typedef void (exit_hctx_fn)(struct blk_mq_hw_ctx *, unsigned int); -typedef int (init_request_fn)(void *, struct blk_mq_hw_ctx *, - struct request *, unsigned int); -typedef void (exit_request_fn)(void *, struct blk_mq_hw_ctx *, - struct request *, unsigned int); +typedef int (init_request_fn)(void *, struct request *, unsigned int, + unsigned int, unsigned int); +typedef void (exit_request_fn)(void *, struct request *, unsigned int, + unsigned int); struct blk_mq_ops { /* @@ -127,10 +128,13 @@ enum { BLK_MQ_MAX_DEPTH = 2048, }; -struct request_queue *blk_mq_init_queue(struct blk_mq_reg *, void *); +struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *); int blk_mq_register_disk(struct gendisk *); void blk_mq_unregister_disk(struct gendisk *); +int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set); +void blk_mq_free_tag_set(struct blk_mq_tag_set *set); + void blk_mq_flush_plug_list(struct blk_plug *plug, bool from_schedule); void blk_mq_insert_request(struct request *, bool, bool, bool); @@ -139,10 +143,10 @@ void blk_mq_free_request(struct request *rq); bool blk_mq_can_queue(struct blk_mq_hw_ctx *); struct request *blk_mq_alloc_request(struct request_queue *q, int rw, gfp_t gfp); struct request *blk_mq_alloc_reserved_request(struct request_queue *q, int rw, gfp_t gfp); -struct request *blk_mq_rq_from_tag(struct request_queue *q, unsigned int tag); +struct request *blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag); struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *, const int ctx_index); -struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_reg *, unsigned int); +struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_tag_set *, unsigned int); void blk_mq_free_single_hw_queue(struct blk_mq_hw_ctx *, unsigned int); bool blk_mq_end_io_partial(struct request *rq, int error, @@ -173,12 +177,6 @@ static inline void *blk_mq_rq_to_pdu(struct request *rq) return (void *) rq + sizeof(*rq); } -static inline struct request *blk_mq_tag_to_rq(struct blk_mq_hw_ctx *hctx, - unsigned int tag) -{ - return hctx->rqs[tag]; -} - #define queue_for_each_hw_ctx(q, hctx, i) \ for ((i) = 0; (i) < (q)->nr_hw_queues && \ ({ hctx = (q)->queue_hw_ctx[i]; 1; }); (i)++) -- cgit v1.2.3 From fb3ccb5da71273e7f0d50b50bc879e50cedd60e7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 14 Apr 2014 10:30:12 +0200 Subject: block: all blk-mq requests are tagged Instead of setting the REQ_QUEUED flag on each of them just take it into account in the only macro checking it. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index eb5e94803892..95bb551273ab 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1101,7 +1101,8 @@ static inline bool blk_needs_flush_plug(struct task_struct *tsk) /* * tag stuff */ -#define blk_rq_tagged(rq) ((rq)->cmd_flags & REQ_QUEUED) +#define blk_rq_tagged(rq) \ + ((rq)->mq_ctx || ((rq)->cmd_flags & REQ_QUEUED)) extern int blk_queue_start_tag(struct request_queue *, struct request *); extern struct request *blk_queue_find_tag(struct request_queue *, int); extern void blk_queue_end_tag(struct request_queue *, struct request *); -- cgit v1.2.3 From 66097ca7889965e1b85de5cf699d7d728d84f47a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 15 Apr 2014 22:24:21 +0200 Subject: ASoC: Fix snd_soc_kcontrol_platform() return type This should obviously be snd_soc_platform * and not snd_soc_codec * Fixes: f6272ff8a5f4 ("ASoC: Add snd_soc_kcontrol_platform() helper function") Reported-by: kbuild test robot Reported-by: Stephen Rothwell Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 94a2dc20ad6e..81454b0dd624 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1253,7 +1253,7 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec( * registered with snd_soc_add_platform_controls() or via table based setup of * a snd_soc_platform_driver. Otherwise the behavior is undefined. */ -static inline struct snd_soc_codec *snd_soc_kcontrol_platform( +static inline struct snd_soc_platform *snd_soc_kcontrol_platform( struct snd_kcontrol *kcontrol) { return snd_kcontrol_chip(kcontrol); -- cgit v1.2.3 From 5c7ab1574987f131f983b15ecfd717faf114f4df Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 15 Apr 2014 19:38:57 +0200 Subject: ALSA: sound/atmel-ac97c.h: Remove unused flags from platform data This platform data member is unused, so remove it. Signed-off-by: Alexander Stein Acked-by: Alexandre Belloni Acked-by: Nicolas Ferre Signed-off-by: Takashi Iwai --- include/sound/atmel-ac97c.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/sound/atmel-ac97c.h b/include/sound/atmel-ac97c.h index e6aabdb45865..00e6c289a936 100644 --- a/include/sound/atmel-ac97c.h +++ b/include/sound/atmel-ac97c.h @@ -23,7 +23,6 @@ * @reset_pin: GPIO pin wired to the reset input on the external AC97 codec, * optional to use, set to -ENODEV if not in use. AC97 layer will * try to do a software reset of the external codec anyway. - * @flags: Flags for which directions should be enabled. * * If the user do not want to use a DMA channel for playback or capture, i.e. * only one feature is required on the board. The slave for playback or capture @@ -33,7 +32,6 @@ struct ac97c_platform_data { struct dw_dma_slave rx_dws; struct dw_dma_slave tx_dws; - unsigned int flags; int reset_pin; }; -- cgit v1.2.3 From 81b3b2711072b6047d5f332cd8751a1c5c9a3fb2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 28 Jan 2014 12:36:48 +0100 Subject: clocksource: sh_cmt: Add support for multiple channels per device CMT hardware devices can support multiple channels, with global registers and per-channel registers. The sh_cmt driver currently models the hardware with one Linux device per channel. This model makes it difficult to handle global registers in a clean way. Add support for a new model that uses one Linux device per timer with multiple channels per device. This requires changes to platform data, add new channel configuration fields. Support for the legacy model is kept and will be removed after all platforms switch to the new model. Signed-off-by: Laurent Pinchart --- drivers/clocksource/sh_cmt.c | 304 +++++++++++++++++++++++++++++++++---------- include/linux/sh_timer.h | 1 + 2 files changed, 237 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index c753efcfe9f5..1efe7d64efca 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -53,7 +53,16 @@ struct sh_cmt_device; * channel registers block. All other versions have a shared start/stop register * located in the global space. * - * Note that CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing 32-bit + * Channels are indexed from 0 to N-1 in the documentation. The channel index + * infers the start/stop bit position in the control register and the channel + * registers block address. Some CMT instances have a subset of channels + * available, in which case the index in the documentation doesn't match the + * "real" index as implemented in hardware. This is for instance the case with + * CMT0 on r8a7740, which is a 32-bit variant with a single channel numbered 0 + * in the documentation but using start/stop bit 5 and having its registers + * block at 0x60. + * + * Similarly CMT0 on r8a73a4, r8a7790 and r8a7791, while implementing 32-bit * channels only, is a 48-bit gen2 CMT with the 48-bit channels unavailable. */ @@ -85,10 +94,14 @@ struct sh_cmt_info { struct sh_cmt_channel { struct sh_cmt_device *cmt; - unsigned int index; - void __iomem *base; + unsigned int index; /* Index in the documentation */ + unsigned int hwidx; /* Real hardware index */ + + void __iomem *iostart; + void __iomem *ioctrl; + unsigned int timer_bit; unsigned long flags; unsigned long match_value; unsigned long next_match_value; @@ -105,6 +118,7 @@ struct sh_cmt_device { struct platform_device *pdev; const struct sh_cmt_info *info; + bool legacy; void __iomem *mapbase_ch; void __iomem *mapbase; @@ -112,6 +126,9 @@ struct sh_cmt_device { struct sh_cmt_channel *channels; unsigned int num_channels; + + bool has_clockevent; + bool has_clocksource; }; #define SH_CMT16_CMCSR_CMF (1 << 7) @@ -223,41 +240,47 @@ static const struct sh_cmt_info sh_cmt_info[] = { static inline unsigned long sh_cmt_read_cmstr(struct sh_cmt_channel *ch) { - return ch->cmt->info->read_control(ch->cmt->mapbase, 0); + if (ch->iostart) + return ch->cmt->info->read_control(ch->iostart, 0); + else + return ch->cmt->info->read_control(ch->cmt->mapbase, 0); } -static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) +static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, + unsigned long value) { - return ch->cmt->info->read_control(ch->base, CMCSR); + if (ch->iostart) + ch->cmt->info->write_control(ch->iostart, 0, value); + else + ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); } -static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) +static inline unsigned long sh_cmt_read_cmcsr(struct sh_cmt_channel *ch) { - return ch->cmt->info->read_count(ch->base, CMCNT); + return ch->cmt->info->read_control(ch->ioctrl, CMCSR); } -static inline void sh_cmt_write_cmstr(struct sh_cmt_channel *ch, +static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->info->write_control(ch->cmt->mapbase, 0, value); + ch->cmt->info->write_control(ch->ioctrl, CMCSR, value); } -static inline void sh_cmt_write_cmcsr(struct sh_cmt_channel *ch, - unsigned long value) +static inline unsigned long sh_cmt_read_cmcnt(struct sh_cmt_channel *ch) { - ch->cmt->info->write_control(ch->base, CMCSR, value); + return ch->cmt->info->read_count(ch->ioctrl, CMCNT); } static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->info->write_count(ch->base, CMCNT, value); + ch->cmt->info->write_count(ch->ioctrl, CMCNT, value); } static inline void sh_cmt_write_cmcor(struct sh_cmt_channel *ch, unsigned long value) { - ch->cmt->info->write_count(ch->base, CMCOR, value); + ch->cmt->info->write_count(ch->ioctrl, CMCOR, value); } static unsigned long sh_cmt_get_counter(struct sh_cmt_channel *ch, @@ -286,7 +309,6 @@ static DEFINE_RAW_SPINLOCK(sh_cmt_lock); static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start) { - struct sh_timer_config *cfg = ch->cmt->pdev->dev.platform_data; unsigned long flags, value; /* start stop register shared by multiple timer channels */ @@ -294,9 +316,9 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_channel *ch, int start) value = sh_cmt_read_cmstr(ch); if (start) - value |= 1 << cfg->timer_bit; + value |= 1 << ch->timer_bit; else - value &= ~(1 << cfg->timer_bit); + value &= ~(1 << ch->timer_bit); sh_cmt_write_cmstr(ch, value); raw_spin_unlock_irqrestore(&sh_cmt_lock, flags); @@ -790,27 +812,72 @@ static void sh_cmt_register_clockevent(struct sh_cmt_channel *ch, static int sh_cmt_register(struct sh_cmt_channel *ch, const char *name, bool clockevent, bool clocksource) { - if (clockevent) + if (clockevent) { + ch->cmt->has_clockevent = true; sh_cmt_register_clockevent(ch, name); + } - if (clocksource) + if (clocksource) { + ch->cmt->has_clocksource = true; sh_cmt_register_clocksource(ch, name); + } return 0; } static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, - struct sh_cmt_device *cmt) + unsigned int hwidx, bool clockevent, + bool clocksource, struct sh_cmt_device *cmt) { - struct sh_timer_config *cfg = cmt->pdev->dev.platform_data; int irq; int ret; + /* Skip unused channels. */ + if (!clockevent && !clocksource) + return 0; + ch->cmt = cmt; - ch->base = cmt->mapbase_ch; ch->index = index; + ch->hwidx = hwidx; + + /* + * Compute the address of the channel control register block. For the + * timers with a per-channel start/stop register, compute its address + * as well. + * + * For legacy configuration the address has been mapped explicitly. + */ + if (cmt->legacy) { + ch->ioctrl = cmt->mapbase_ch; + } else { + switch (cmt->info->model) { + case SH_CMT_16BIT: + ch->ioctrl = cmt->mapbase + 2 + ch->hwidx * 6; + break; + case SH_CMT_32BIT: + case SH_CMT_48BIT: + ch->ioctrl = cmt->mapbase + 0x10 + ch->hwidx * 0x10; + break; + case SH_CMT_32BIT_FAST: + /* + * The 32-bit "fast" timer has a single channel at hwidx + * 5 but is located at offset 0x40 instead of 0x60 for + * some reason. + */ + ch->ioctrl = cmt->mapbase + 0x40; + break; + case SH_CMT_48BIT_GEN2: + ch->iostart = cmt->mapbase + ch->hwidx * 0x100; + ch->ioctrl = ch->iostart + 0x10; + break; + } + } + + if (cmt->legacy) + irq = platform_get_irq(cmt->pdev, 0); + else + irq = platform_get_irq(cmt->pdev, ch->index); - irq = platform_get_irq(cmt->pdev, 0); if (irq < 0) { dev_err(&cmt->pdev->dev, "ch%u: failed to get irq\n", ch->index); @@ -825,9 +892,15 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, ch->match_value = ch->max_match_value; raw_spin_lock_init(&ch->lock); + if (cmt->legacy) { + ch->timer_bit = ch->hwidx; + } else { + ch->timer_bit = cmt->info->model == SH_CMT_48BIT_GEN2 + ? 0 : ch->hwidx; + } + ret = sh_cmt_register(ch, dev_name(&cmt->pdev->dev), - cfg->clockevent_rating != 0, - cfg->clocksource_rating != 0); + clockevent, clocksource); if (ret) { dev_err(&cmt->pdev->dev, "ch%u: registration failed\n", ch->index); @@ -847,97 +920,180 @@ static int sh_cmt_setup_channel(struct sh_cmt_channel *ch, unsigned int index, return 0; } -static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) +static int sh_cmt_map_memory(struct sh_cmt_device *cmt) { - struct sh_timer_config *cfg = pdev->dev.platform_data; - struct resource *res, *res2; - int ret; - ret = -ENXIO; + struct resource *mem; - cmt->pdev = pdev; + mem = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&cmt->pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } - if (!cfg) { - dev_err(&cmt->pdev->dev, "missing platform data\n"); - goto err0; + cmt->mapbase = ioremap_nocache(mem->start, resource_size(mem)); + if (cmt->mapbase == NULL) { + dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n"); + return -ENXIO; } + return 0; +} + +static int sh_cmt_map_memory_legacy(struct sh_cmt_device *cmt) +{ + struct sh_timer_config *cfg = cmt->pdev->dev.platform_data; + struct resource *res, *res2; + + /* map memory, let mapbase_ch point to our channel */ res = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&cmt->pdev->dev, "failed to get I/O memory\n"); - goto err0; + return -ENXIO; } - /* optional resource for the shared timer start/stop register */ - res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1); - - /* map memory, let mapbase_ch point to our channel */ cmt->mapbase_ch = ioremap_nocache(res->start, resource_size(res)); if (cmt->mapbase_ch == NULL) { dev_err(&cmt->pdev->dev, "failed to remap I/O memory\n"); - goto err0; + return -ENXIO; } + /* optional resource for the shared timer start/stop register */ + res2 = platform_get_resource(cmt->pdev, IORESOURCE_MEM, 1); + /* map second resource for CMSTR */ cmt->mapbase = ioremap_nocache(res2 ? res2->start : res->start - cfg->channel_offset, res2 ? resource_size(res2) : 2); if (cmt->mapbase == NULL) { dev_err(&cmt->pdev->dev, "failed to remap I/O second memory\n"); - goto err1; + iounmap(cmt->mapbase_ch); + return -ENXIO; } - /* get hold of clock */ + /* identify the model based on the resources */ + if (resource_size(res) == 6) + cmt->info = &sh_cmt_info[SH_CMT_16BIT]; + else if (res2 && (resource_size(res2) == 4)) + cmt->info = &sh_cmt_info[SH_CMT_48BIT_GEN2]; + else + cmt->info = &sh_cmt_info[SH_CMT_32BIT]; + + return 0; +} + +static void sh_cmt_unmap_memory(struct sh_cmt_device *cmt) +{ + iounmap(cmt->mapbase); + if (cmt->mapbase_ch) + iounmap(cmt->mapbase_ch); +} + +static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev) +{ + struct sh_timer_config *cfg = pdev->dev.platform_data; + const struct platform_device_id *id = pdev->id_entry; + unsigned int hw_channels; + int ret; + + memset(cmt, 0, sizeof(*cmt)); + cmt->pdev = pdev; + + if (!cfg) { + dev_err(&cmt->pdev->dev, "missing platform data\n"); + return -ENXIO; + } + + cmt->info = (const struct sh_cmt_info *)id->driver_data; + cmt->legacy = cmt->info ? false : true; + + /* Get hold of clock. */ cmt->clk = clk_get(&cmt->pdev->dev, "cmt_fck"); if (IS_ERR(cmt->clk)) { dev_err(&cmt->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(cmt->clk); - goto err2; + return PTR_ERR(cmt->clk); } ret = clk_prepare(cmt->clk); if (ret < 0) - goto err3; + goto err_clk_put; - /* identify the model based on the resources */ - if (resource_size(res) == 6) - cmt->info = &sh_cmt_info[SH_CMT_16BIT]; - else if (res2 && (resource_size(res2) == 4)) - cmt->info = &sh_cmt_info[SH_CMT_48BIT_GEN2]; + /* + * Map the memory resource(s). We need to support both the legacy + * platform device configuration (with one device per channel) and the + * new version (with multiple channels per device). + */ + if (cmt->legacy) + ret = sh_cmt_map_memory_legacy(cmt); else - cmt->info = &sh_cmt_info[SH_CMT_32BIT]; + ret = sh_cmt_map_memory(cmt); - cmt->channels = kzalloc(sizeof(*cmt->channels), GFP_KERNEL); + if (ret < 0) + goto err_clk_unprepare; + + /* Allocate and setup the channels. */ + if (cmt->legacy) { + cmt->num_channels = 1; + hw_channels = 0; + } else { + cmt->num_channels = hweight8(cfg->channels_mask); + hw_channels = cfg->channels_mask; + } + + cmt->channels = kzalloc(cmt->num_channels * sizeof(*cmt->channels), + GFP_KERNEL); if (cmt->channels == NULL) { ret = -ENOMEM; - goto err4; + goto err_unmap; } - cmt->num_channels = 1; + if (cmt->legacy) { + ret = sh_cmt_setup_channel(&cmt->channels[0], + cfg->timer_bit, cfg->timer_bit, + cfg->clockevent_rating != 0, + cfg->clocksource_rating != 0, cmt); + if (ret < 0) + goto err_unmap; + } else { + unsigned int mask = hw_channels; + unsigned int i; - ret = sh_cmt_setup_channel(&cmt->channels[0], cfg->timer_bit, cmt); - if (ret < 0) - goto err4; + /* + * Use the first channel as a clock event device and the second + * channel as a clock source. If only one channel is available + * use it for both. + */ + for (i = 0; i < cmt->num_channels; ++i) { + unsigned int hwidx = ffs(mask) - 1; + bool clocksource = i == 1 || cmt->num_channels == 1; + bool clockevent = i == 0; + + ret = sh_cmt_setup_channel(&cmt->channels[i], i, hwidx, + clockevent, clocksource, + cmt); + if (ret < 0) + goto err_unmap; + + mask &= ~(1 << hwidx); + } + } platform_set_drvdata(pdev, cmt); return 0; -err4: + +err_unmap: kfree(cmt->channels); + sh_cmt_unmap_memory(cmt); +err_clk_unprepare: clk_unprepare(cmt->clk); -err3: +err_clk_put: clk_put(cmt->clk); -err2: - iounmap(cmt->mapbase); -err1: - iounmap(cmt->mapbase_ch); -err0: return ret; } static int sh_cmt_probe(struct platform_device *pdev) { struct sh_cmt_device *cmt = platform_get_drvdata(pdev); - struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (!is_early_platform_device(pdev)) { @@ -966,7 +1122,7 @@ static int sh_cmt_probe(struct platform_device *pdev) return 0; out: - if (cfg->clockevent_rating || cfg->clocksource_rating) + if (cmt->has_clockevent || cmt->has_clocksource) pm_runtime_irq_safe(&pdev->dev); else pm_runtime_idle(&pdev->dev); @@ -979,12 +1135,24 @@ static int sh_cmt_remove(struct platform_device *pdev) return -EBUSY; /* cannot unregister clockevent and clocksource */ } +static const struct platform_device_id sh_cmt_id_table[] = { + { "sh_cmt", 0 }, + { "sh-cmt-16", (kernel_ulong_t)&sh_cmt_info[SH_CMT_16BIT] }, + { "sh-cmt-32", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT] }, + { "sh-cmt-32-fast", (kernel_ulong_t)&sh_cmt_info[SH_CMT_32BIT_FAST] }, + { "sh-cmt-48", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT] }, + { "sh-cmt-48-gen2", (kernel_ulong_t)&sh_cmt_info[SH_CMT_48BIT_GEN2] }, + { } +}; +MODULE_DEVICE_TABLE(platform, sh_cmt_id_table); + static struct platform_driver sh_cmt_device_driver = { .probe = sh_cmt_probe, .remove = sh_cmt_remove, .driver = { .name = "sh_cmt", - } + }, + .id_table = sh_cmt_id_table, }; static int __init sh_cmt_init(void) diff --git a/include/linux/sh_timer.h b/include/linux/sh_timer.h index 4d9dcd138315..8e1e036d6d45 100644 --- a/include/linux/sh_timer.h +++ b/include/linux/sh_timer.h @@ -7,6 +7,7 @@ struct sh_timer_config { int timer_bit; unsigned long clockevent_rating; unsigned long clocksource_rating; + unsigned int channels_mask; }; #endif /* __SH_TIMER_H__ */ -- cgit v1.2.3 From 63151a449ebaef062ffac5b302206565ff5ef62e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 16 Apr 2014 09:44:52 +0200 Subject: blk-mq: allow drivers to hook into I/O completion Split out the bottom half of blk_mq_end_io so that drivers can perform work when they know a request has been completed, but before it has been freed. This also obsoletes blk_mq_end_io_partial as drivers can now pass any value to blk_update_request directly. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-mq.c | 16 ++++++++++------ include/linux/blk-mq.h | 9 ++------- 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index b59a8d027dff..86d66e0e900c 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -294,20 +294,24 @@ void blk_mq_clone_flush_request(struct request *flush_rq, hctx->cmd_size); } -bool blk_mq_end_io_partial(struct request *rq, int error, unsigned int nr_bytes) +inline void __blk_mq_end_io(struct request *rq, int error) { - if (blk_update_request(rq, error, blk_rq_bytes(rq))) - return true; - blk_account_io_done(rq); if (rq->end_io) rq->end_io(rq, error); else blk_mq_free_request(rq); - return false; } -EXPORT_SYMBOL(blk_mq_end_io_partial); +EXPORT_SYMBOL(__blk_mq_end_io); + +void blk_mq_end_io(struct request *rq, int error) +{ + if (blk_update_request(rq, error, blk_rq_bytes(rq))) + BUG(); + __blk_mq_end_io(rq, error); +} +EXPORT_SYMBOL(blk_mq_end_io); static void __blk_mq_complete_request_remote(void *data) { diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index a4ea0ce83b07..a81b474b794f 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -149,13 +149,8 @@ struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *, const int ctx_ind struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_tag_set *, unsigned int); void blk_mq_free_single_hw_queue(struct blk_mq_hw_ctx *, unsigned int); -bool blk_mq_end_io_partial(struct request *rq, int error, - unsigned int nr_bytes); -static inline void blk_mq_end_io(struct request *rq, int error) -{ - bool done = !blk_mq_end_io_partial(rq, error, blk_rq_bytes(rq)); - BUG_ON(!done); -} +void blk_mq_end_io(struct request *rq, int error); +void __blk_mq_end_io(struct request *rq, int error); void blk_mq_complete_request(struct request *rq); -- cgit v1.2.3 From 1b4a325858f695a9b5041313602d34b36f463724 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 16 Apr 2014 09:44:54 +0200 Subject: blk-mq: add async parameter to blk_mq_start_stopped_hw_queues Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-mq.c | 4 ++-- drivers/block/virtio_blk.c | 4 ++-- include/linux/blk-mq.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 963a82109386..da3808823e44 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -700,7 +700,7 @@ void blk_mq_start_hw_queue(struct blk_mq_hw_ctx *hctx) } EXPORT_SYMBOL(blk_mq_start_hw_queue); -void blk_mq_start_stopped_hw_queues(struct request_queue *q) +void blk_mq_start_stopped_hw_queues(struct request_queue *q, bool async) { struct blk_mq_hw_ctx *hctx; int i; @@ -711,7 +711,7 @@ void blk_mq_start_stopped_hw_queues(struct request_queue *q) clear_bit(BLK_MQ_S_STOPPED, &hctx->state); preempt_disable(); - blk_mq_run_hw_queue(hctx, true); + blk_mq_run_hw_queue(hctx, async); preempt_enable(); } } diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index f909a8821e65..7a51f065edcd 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -151,7 +151,7 @@ static void virtblk_done(struct virtqueue *vq) /* In case queue is stopped waiting for more buffers. */ if (req_done) - blk_mq_start_stopped_hw_queues(vblk->disk->queue); + blk_mq_start_stopped_hw_queues(vblk->disk->queue, true); } static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req) @@ -762,7 +762,7 @@ static int virtblk_restore(struct virtio_device *vdev) vblk->config_enable = true; ret = init_vq(vdev->priv); if (!ret) - blk_mq_start_stopped_hw_queues(vblk->disk->queue); + blk_mq_start_stopped_hw_queues(vblk->disk->queue, true); return ret; } diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index a81b474b794f..9ecfab96d8c9 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -157,7 +157,7 @@ void blk_mq_complete_request(struct request *rq); void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx); void blk_mq_start_hw_queue(struct blk_mq_hw_ctx *hctx); void blk_mq_stop_hw_queues(struct request_queue *q); -void blk_mq_start_stopped_hw_queues(struct request_queue *q); +void blk_mq_start_stopped_hw_queues(struct request_queue *q, bool async); /* * Driver command data is immediately after the request. So subtract request -- cgit v1.2.3 From 70f4db639c5b2479e08657392cbf3ba3cceea11c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 16 Apr 2014 10:48:08 -0600 Subject: blk-mq: add blk_mq_delay_queue Add a blk-mq equivalent to blk_delay_queue so that the scsi layer can ask to be kicked again after a delay. Signed-off-by: Christoph Hellwig Modified by me to kill the unnecessary preempt disable/enable in the delayed workqueue handler. Signed-off-by: Jens Axboe --- block/blk-core.c | 6 ++++-- block/blk-mq.c | 45 +++++++++++++++++++++++++++++++++++++++------ include/linux/blk-mq.h | 4 +++- 3 files changed, 46 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index ae6227fd07aa..90b6e63b8769 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -251,8 +251,10 @@ void blk_sync_queue(struct request_queue *q) struct blk_mq_hw_ctx *hctx; int i; - queue_for_each_hw_ctx(q, hctx, i) - cancel_delayed_work_sync(&hctx->delayed_work); + queue_for_each_hw_ctx(q, hctx, i) { + cancel_delayed_work_sync(&hctx->run_work); + cancel_delayed_work_sync(&hctx->delay_work); + } } else { cancel_delayed_work_sync(&q->delay_work); } diff --git a/block/blk-mq.c b/block/blk-mq.c index da3808823e44..0cf52dddfa6b 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -640,7 +640,7 @@ void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async) if (!async && cpumask_test_cpu(smp_processor_id(), hctx->cpumask)) __blk_mq_run_hw_queue(hctx); else if (hctx->queue->nr_hw_queues == 1) - kblockd_schedule_delayed_work(&hctx->delayed_work, 0); + kblockd_schedule_delayed_work(&hctx->run_work, 0); else { unsigned int cpu; @@ -651,7 +651,7 @@ void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async) * just queue on the first CPU. */ cpu = cpumask_first(hctx->cpumask); - kblockd_schedule_delayed_work_on(cpu, &hctx->delayed_work, 0); + kblockd_schedule_delayed_work_on(cpu, &hctx->run_work, 0); } } @@ -675,7 +675,8 @@ EXPORT_SYMBOL(blk_mq_run_queues); void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx) { - cancel_delayed_work(&hctx->delayed_work); + cancel_delayed_work(&hctx->run_work); + cancel_delayed_work(&hctx->delay_work); set_bit(BLK_MQ_S_STOPPED, &hctx->state); } EXPORT_SYMBOL(blk_mq_stop_hw_queue); @@ -717,15 +718,46 @@ void blk_mq_start_stopped_hw_queues(struct request_queue *q, bool async) } EXPORT_SYMBOL(blk_mq_start_stopped_hw_queues); -static void blk_mq_work_fn(struct work_struct *work) +static void blk_mq_run_work_fn(struct work_struct *work) { struct blk_mq_hw_ctx *hctx; - hctx = container_of(work, struct blk_mq_hw_ctx, delayed_work.work); + hctx = container_of(work, struct blk_mq_hw_ctx, run_work.work); __blk_mq_run_hw_queue(hctx); } +static void blk_mq_delay_work_fn(struct work_struct *work) +{ + struct blk_mq_hw_ctx *hctx; + + hctx = container_of(work, struct blk_mq_hw_ctx, delay_work.work); + + if (test_and_clear_bit(BLK_MQ_S_STOPPED, &hctx->state)) + __blk_mq_run_hw_queue(hctx); +} + +void blk_mq_delay_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs) +{ + unsigned long tmo = msecs_to_jiffies(msecs); + + if (hctx->queue->nr_hw_queues == 1) + kblockd_schedule_delayed_work(&hctx->delay_work, tmo); + else { + unsigned int cpu; + + /* + * It'd be great if the workqueue API had a way to pass + * in a mask and had some smarts for more clever placement + * than the first CPU. Or we could round-robin here. For now, + * just queue on the first CPU. + */ + cpu = cpumask_first(hctx->cpumask); + kblockd_schedule_delayed_work_on(cpu, &hctx->delay_work, tmo); + } +} +EXPORT_SYMBOL(blk_mq_delay_queue); + static void __blk_mq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq, bool at_head) { @@ -1179,7 +1211,8 @@ static int blk_mq_init_hw_queues(struct request_queue *q, if (node == NUMA_NO_NODE) node = hctx->numa_node = set->numa_node; - INIT_DELAYED_WORK(&hctx->delayed_work, blk_mq_work_fn); + INIT_DELAYED_WORK(&hctx->run_work, blk_mq_run_work_fn); + INIT_DELAYED_WORK(&hctx->delay_work, blk_mq_delay_work_fn); spin_lock_init(&hctx->lock); INIT_LIST_HEAD(&hctx->dispatch); hctx->queue = q; diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 9ecfab96d8c9..ae868e77bc2f 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -18,7 +18,8 @@ struct blk_mq_hw_ctx { } ____cacheline_aligned_in_smp; unsigned long state; /* BLK_MQ_S_* flags */ - struct delayed_work delayed_work; + struct delayed_work run_work; + struct delayed_work delay_work; cpumask_var_t cpumask; unsigned long flags; /* BLK_MQ_F_* flags */ @@ -158,6 +159,7 @@ void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx); void blk_mq_start_hw_queue(struct blk_mq_hw_ctx *hctx); void blk_mq_stop_hw_queues(struct request_queue *q); void blk_mq_start_stopped_hw_queues(struct request_queue *q, bool async); +void blk_mq_delay_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs); /* * Driver command data is immediately after the request. So subtract request -- cgit v1.2.3 From 2f268556567ebeb3538f99b9bdad177581439dcb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 16 Apr 2014 09:44:56 +0200 Subject: blk-mq: add blk_mq_start_hw_queues Add a helper to unconditionally kick contexts of a queue. This will be needed by the SCSI layer to provide fair queueing between multiple devices on a single host. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-mq.c | 11 +++++++++++ include/linux/blk-mq.h | 1 + 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 0cf52dddfa6b..543bbc08a261 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -701,6 +701,17 @@ void blk_mq_start_hw_queue(struct blk_mq_hw_ctx *hctx) } EXPORT_SYMBOL(blk_mq_start_hw_queue); +void blk_mq_start_hw_queues(struct request_queue *q) +{ + struct blk_mq_hw_ctx *hctx; + int i; + + queue_for_each_hw_ctx(q, hctx, i) + blk_mq_start_hw_queue(hctx); +} +EXPORT_SYMBOL(blk_mq_start_hw_queues); + + void blk_mq_start_stopped_hw_queues(struct request_queue *q, bool async) { struct blk_mq_hw_ctx *hctx; diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index ae868e77bc2f..391377e53367 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -158,6 +158,7 @@ void blk_mq_complete_request(struct request *rq); void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx); void blk_mq_start_hw_queue(struct blk_mq_hw_ctx *hctx); void blk_mq_stop_hw_queues(struct request_queue *q); +void blk_mq_start_hw_queues(struct request_queue *q); void blk_mq_start_stopped_hw_queues(struct request_queue *q, bool async); void blk_mq_delay_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs); -- cgit v1.2.3 From ed0791b2f83cec4e77d88c4e9baabcebf9254a78 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 16 Apr 2014 09:44:57 +0200 Subject: blk-mq: add blk_mq_requeue_request This allows to requeue a request that has been accepted by ->queue_rq earlier. This is needed by the SCSI layer in various error conditions. The existing internal blk_mq_requeue_request is renamed to __blk_mq_requeue_request as it is a lower level building block for this funtionality. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-mq.c | 18 ++++++++++++++++-- include/linux/blk-mq.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 543bbc08a261..ee225cc312b8 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -400,7 +400,7 @@ static void blk_mq_start_request(struct request *rq, bool last) rq->cmd_flags |= REQ_END; } -static void blk_mq_requeue_request(struct request *rq) +static void __blk_mq_requeue_request(struct request *rq) { struct request_queue *q = rq->q; @@ -413,6 +413,20 @@ static void blk_mq_requeue_request(struct request *rq) rq->nr_phys_segments--; } +void blk_mq_requeue_request(struct request *rq) +{ + struct request_queue *q = rq->q; + + __blk_mq_requeue_request(rq); + blk_clear_rq_complete(rq); + + trace_block_rq_requeue(q, rq); + + BUG_ON(blk_queued_rq(rq)); + blk_mq_insert_request(rq, true, true, false); +} +EXPORT_SYMBOL(blk_mq_requeue_request); + struct request *blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag) { return tags->rqs[tag]; @@ -602,7 +616,7 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) * time */ list_add(&rq->queuelist, &rq_list); - blk_mq_requeue_request(rq); + __blk_mq_requeue_request(rq); break; default: pr_err("blk-mq: bad return on queue: %d\n", ret); diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 391377e53367..ab469d525894 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -153,6 +153,8 @@ void blk_mq_free_single_hw_queue(struct blk_mq_hw_ctx *, unsigned int); void blk_mq_end_io(struct request *rq, int error); void __blk_mq_end_io(struct request *rq, int error); +void blk_mq_requeue_request(struct request *rq); + void blk_mq_complete_request(struct request *rq); void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx); -- cgit v1.2.3 From f88a164b72bd51fe4c89e06ac9939f2afe39c7ed Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 16 Apr 2014 09:44:58 +0200 Subject: blk-mq: rename mq_flush_work struct request member We will use this work_struct to requeue scsi commands from the completion handler as well, so give it a more generic name. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-flush.c | 6 +++--- include/linux/blkdev.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/block/blk-flush.c b/block/blk-flush.c index c41fc19f75d1..ec7a224d6733 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -134,7 +134,7 @@ static void mq_flush_run(struct work_struct *work) { struct request *rq; - rq = container_of(work, struct request, mq_flush_work); + rq = container_of(work, struct request, requeue_work); memset(&rq->csd, 0, sizeof(rq->csd)); blk_mq_insert_request(rq, false, true, false); @@ -143,8 +143,8 @@ static void mq_flush_run(struct work_struct *work) static bool blk_flush_queue_rq(struct request *rq, bool add_front) { if (rq->q->mq_ops) { - INIT_WORK(&rq->mq_flush_work, mq_flush_run); - kblockd_schedule_work(&rq->mq_flush_work); + INIT_WORK(&rq->requeue_work, mq_flush_run); + kblockd_schedule_work(&rq->requeue_work); return false; } else { if (add_front) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 95bb551273ab..71288083a46f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -98,7 +98,7 @@ struct request { struct list_head queuelist; union { struct call_single_data csd; - struct work_struct mq_flush_work; + struct work_struct requeue_work; unsigned long fifo_time; }; -- cgit v1.2.3 From 12120077b2612a243d158605640cd39266906667 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 16 Apr 2014 09:44:59 +0200 Subject: block: export blk_finish_request This allows to mirror the blk-mq code flow for more a more readable I/O completion handler in SCSI. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-core.c | 3 ++- include/linux/blkdev.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 90b6e63b8769..c4269701cb4f 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2497,7 +2497,7 @@ EXPORT_SYMBOL_GPL(blk_unprep_request); /* * queue lock must be held */ -static void blk_finish_request(struct request *req, int error) +void blk_finish_request(struct request *req, int error) { if (blk_rq_tagged(req)) blk_queue_end_tag(req->q, req); @@ -2523,6 +2523,7 @@ static void blk_finish_request(struct request *req, int error) __blk_put_request(req->q, req); } } +EXPORT_SYMBOL(blk_finish_request); /** * blk_end_bidi_request - Complete a bidi request diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 71288083a46f..20b26d4e53a2 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -936,6 +936,7 @@ extern struct request *blk_fetch_request(struct request_queue *q); */ extern bool blk_update_request(struct request *rq, int error, unsigned int nr_bytes); +extern void blk_finish_request(struct request *rq, int error); extern bool blk_end_request(struct request *rq, int error, unsigned int nr_bytes); extern void blk_end_request_all(struct request *rq, int error); -- cgit v1.2.3 From 49fd524f95cb4cc699d435e0ebb08b1c6220da6d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 16 Apr 2014 10:57:18 -0600 Subject: bsg: update check for rq based driver for blk-mq bsg currently checks ->request_fn to check whether a queue can handle struct request. But with blk-mq, we don't have a request_fn yet are request based. Add a queue_is_rq_based() helper and use that in bsg, I'm guessing this is not the last place we need to update for this. Besides, it better explains what is being checked. Signed-off-by: Jens Axboe --- block/bsg.c | 2 +- include/linux/blkdev.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/block/bsg.c b/block/bsg.c index 420a5a9f1b23..e5214c148096 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -1008,7 +1008,7 @@ int bsg_register_queue(struct request_queue *q, struct device *parent, /* * we need a proper transport to send commands, not a stacked device */ - if (!q->request_fn) + if (!queue_is_rq_based(q)) return 0; bcd = &q->bsg_dev; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 20b26d4e53a2..74ee55fefcf0 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -612,6 +612,15 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q) #define rq_data_dir(rq) (((rq)->cmd_flags & 1) != 0) +/* + * Driver can handle struct request, if it either has an old style + * request_fn defined, or is blk-mq based. + */ +static inline bool queue_is_rq_based(struct request_queue *q) +{ + return q->request_fn || q->mq_ops; +} + static inline unsigned int blk_queue_cluster(struct request_queue *q) { return q->limits.cluster; -- cgit v1.2.3 From ba76a6e6a5eaa3736df743818394978af456cf70 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 17 Mar 2014 09:54:19 -0300 Subject: [media] v4l2-subdev.h: fix sparse error with v4l2_subdev_notify The notify function is a void function, yet the v4l2_subdev_notify define uses it in a ? : construction, which causes sparse warnings. Replace the define by a static inline function and move it to v4l2-device.h, which is where it belongs since it needs to know the v4l2_device struct. This wasn't a problem when it was a define, but as a static inline function this no longer compiles in v4l2-subdev.h. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-device.h | 8 ++++++++ include/media/v4l2-subdev.h | 5 ----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index c9b1593923f6..ffb69da3ce9e 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -120,6 +120,14 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); int __must_check v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev); +/* Send a notification to v4l2_device. */ +static inline void v4l2_subdev_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + if (sd && sd->v4l2_dev && sd->v4l2_dev->notify) + sd->v4l2_dev->notify(sd, notification, arg); +} + /* Iterate over all subdevs. */ #define v4l2_device_for_each_subdev(sd, v4l2_dev) \ list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 28f4d8c3cf7d..ee1cb2d354a8 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -691,11 +691,6 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, (!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \ (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD)) -/* Send a notification to v4l2_device. */ -#define v4l2_subdev_notify(sd, notification, arg) \ - ((!(sd) || !(sd)->v4l2_dev || !(sd)->v4l2_dev->notify) ? -ENODEV : \ - (sd)->v4l2_dev->notify((sd), (notification), (arg))) - #define v4l2_subdev_has_op(sd, o, f) \ ((sd)->ops->o && (sd)->ops->o->f) -- cgit v1.2.3 From 2dd477dbeb53937213d741db53110ac39ca245d1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 17 Mar 2014 09:54:22 -0300 Subject: [media] v4l2-common.h: remove __user annotation in struct v4l2_edid The edid array is copied to kernelspace by the v4l2 core, so drivers shouldn't see the __user annotation. This conforms to other structs like v4l2_ext_controls where the data pointed to is copied to from user to kernelspace. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/v4l2-common.h b/include/uapi/linux/v4l2-common.h index 9bf508ad0957..2f6f8cafe773 100644 --- a/include/uapi/linux/v4l2-common.h +++ b/include/uapi/linux/v4l2-common.h @@ -75,7 +75,7 @@ struct v4l2_edid { __u32 start_block; __u32 blocks; __u32 reserved[5]; - __u8 __user *edid; + __u8 *edid; }; #endif /* __V4L2_COMMON__ */ -- cgit v1.2.3 From 3d7543b9196cb0de8e65750f1da9ad155c22e12f Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 22 Mar 2014 07:57:59 -0300 Subject: [media] media: davinci: vpbe: use v4l2_fh for priority handling This patch migrates the vpbe driver to use v4l2_fh for priority handling. This also fixes v4l2-compliance test. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_display.c | 39 +++++---------------------- include/media/davinci/vpbe_display.h | 6 ++--- 2 files changed, 9 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 656708252962..a9ad949d0c19 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -680,29 +680,6 @@ static int vpbe_try_format(struct vpbe_display *disp_dev, return 0; } -static int vpbe_display_g_priority(struct file *file, void *priv, - enum v4l2_priority *p) -{ - struct vpbe_fh *fh = file->private_data; - struct vpbe_layer *layer = fh->layer; - - *p = v4l2_prio_max(&layer->prio); - - return 0; -} - -static int vpbe_display_s_priority(struct file *file, void *priv, - enum v4l2_priority p) -{ - struct vpbe_fh *fh = file->private_data; - struct vpbe_layer *layer = fh->layer; - int ret; - - ret = v4l2_prio_change(&layer->prio, &fh->prio, p); - - return ret; -} - static int vpbe_display_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1492,6 +1469,7 @@ static int vpbe_display_open(struct file *file) { struct vpbe_fh *fh = NULL; struct vpbe_layer *layer = video_drvdata(file); + struct video_device *vdev = video_devdata(file); struct vpbe_display *disp_dev = layer->disp_dev; struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; struct osd_state *osd_device = disp_dev->osd_device; @@ -1504,6 +1482,7 @@ static int vpbe_display_open(struct file *file) "unable to allocate memory for file handle object\n"); return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe display open plane = %d\n", layer->device_id); @@ -1532,9 +1511,7 @@ static int vpbe_display_open(struct file *file) layer->usrs++; /* Set io_allowed member to false */ fh->io_allowed = 0; - /* Initialize priority of this instance to default priority */ - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&layer->prio, &fh->prio); + v4l2_fh_add(&fh->fh); v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe display device opened successfully\n"); return 0; @@ -1589,8 +1566,9 @@ static int vpbe_display_release(struct file *file) osd_device->ops.release_layer(osd_device, layer->layer_info.id); } - /* Close the priority */ - v4l2_prio_close(&layer->prio, fh->prio); + + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); file->private_data = NULL; mutex_unlock(&layer->opslock); @@ -1618,8 +1596,6 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { .vidioc_cropcap = vpbe_display_cropcap, .vidioc_g_crop = vpbe_display_g_crop, .vidioc_s_crop = vpbe_display_s_crop, - .vidioc_g_priority = vpbe_display_g_priority, - .vidioc_s_priority = vpbe_display_s_priority, .vidioc_s_std = vpbe_display_s_std, .vidioc_g_std = vpbe_display_g_std, .vidioc_enum_output = vpbe_display_enum_output, @@ -1699,8 +1675,6 @@ static int init_vpbe_layer(int i, struct vpbe_display *disp_dev, vpbe_display_layer->layer_info.id = ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); - /* Initialize prio member of layer object */ - v4l2_prio_init(&vpbe_display_layer->prio); return 0; } @@ -1727,6 +1701,7 @@ static int register_device(struct vpbe_layer *vpbe_display_layer, vpbe_display_layer->disp_dev = disp_dev; /* set the driver data in platform device */ platform_set_drvdata(pdev, disp_dev); + set_bit(V4L2_FL_USE_FH_PRIO, &vpbe_display_layer->video_dev.flags); video_set_drvdata(&vpbe_display_layer->video_dev, vpbe_display_layer); diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h index 8dffffedbb59..637749a91432 100644 --- a/include/media/davinci/vpbe_display.h +++ b/include/media/davinci/vpbe_display.h @@ -16,6 +16,7 @@ /* Header files */ #include #include +#include #include #include #include @@ -94,8 +95,6 @@ struct vpbe_layer { * has selected */ enum v4l2_memory memory; - /* Used to keep track of state of the priority */ - struct v4l2_prio_state prio; /* Used to store pixel format */ struct v4l2_pix_format pix_fmt; enum v4l2_field buf_field; @@ -134,14 +133,13 @@ struct vpbe_display { /* File handle structure */ struct vpbe_fh { + struct v4l2_fh fh; /* vpbe device structure */ struct vpbe_display *disp_dev; /* pointer to layer object for opened device */ struct vpbe_layer *layer; /* Indicates whether this file handle is doing IO */ unsigned char io_allowed; - /* Used to keep track priority of this instance */ - enum v4l2_priority prio; }; struct buf_config_params { -- cgit v1.2.3 From 3bdaa382b294946a6b8661c3eb77e595940c8a61 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 22 Mar 2014 08:39:24 -0300 Subject: [media] media: davinci: vpfe: use v4l2_fh for priority handling This patch migrates the vpfe driver to use v4l2_fh for priority handling. Signed-off-by: Lad, Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpfe_capture.c | 13 ++++++------- include/media/davinci/vpfe_capture.h | 6 ++---- 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 0379cb9f9a9c..ac6c8c6ac7d0 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -498,6 +498,7 @@ unlock: static int vpfe_open(struct file *file) { struct vpfe_device *vpfe_dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); struct vpfe_fh *fh; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); @@ -517,6 +518,7 @@ static int vpfe_open(struct file *file) /* store pointer to fh in private_data member of file */ file->private_data = fh; fh->vpfe_dev = vpfe_dev; + v4l2_fh_init(&fh->fh, vdev); mutex_lock(&vpfe_dev->lock); /* If decoder is not initialized. initialize it */ if (!vpfe_dev->initialized) { @@ -529,9 +531,7 @@ static int vpfe_open(struct file *file) vpfe_dev->usrs++; /* Set io_allowed member to false */ fh->io_allowed = 0; - /* Initialize priority of this instance to default priority */ - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&vpfe_dev->prio, &fh->prio); + v4l2_fh_add(&fh->fh); mutex_unlock(&vpfe_dev->lock); return 0; } @@ -740,8 +740,8 @@ static int vpfe_release(struct file *file) /* Decrement device usrs counter */ vpfe_dev->usrs--; - /* Close the priority */ - v4l2_prio_close(&vpfe_dev->prio, fh->prio); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); /* If this is the last file handle */ if (!vpfe_dev->usrs) { vpfe_dev->initialized = 0; @@ -1910,14 +1910,13 @@ static int vpfe_probe(struct platform_device *pdev) /* Initialize field of the device objects */ vpfe_dev->numbuffers = config_params.numbuffers; - /* Initialize prio member of device object */ - v4l2_prio_init(&vpfe_dev->prio); /* register video device */ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "trying to register vpfe device.\n"); v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "video_dev=%x\n", (int)&vpfe_dev->video_dev); vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + set_bit(V4L2_FL_USE_FH_PRIO, &vpfe_dev->video_dev->flags); ret = video_register_device(vpfe_dev->video_dev, VFL_TYPE_GRABBER, -1); diff --git a/include/media/davinci/vpfe_capture.h b/include/media/davinci/vpfe_capture.h index cc973ed845a7..288772e6900a 100644 --- a/include/media/davinci/vpfe_capture.h +++ b/include/media/davinci/vpfe_capture.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -110,8 +111,6 @@ struct vpfe_device { struct v4l2_device v4l2_dev; /* parent device */ struct device *pdev; - /* Used to keep track of state of the priority */ - struct v4l2_prio_state prio; /* number of open instances of the channel */ u32 usrs; /* Indicates id of the field which is being displayed */ @@ -174,11 +173,10 @@ struct vpfe_device { /* File handle structure */ struct vpfe_fh { + struct v4l2_fh fh; struct vpfe_device *vpfe_dev; /* Indicates whether this file handle is doing IO */ u8 io_allowed; - /* Used to keep track priority of this instance */ - enum v4l2_priority prio; }; struct vpfe_config_params { -- cgit v1.2.3 From ebf9edd39a9fab23758571801063820603a6465c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 8 Apr 2014 05:00:39 -0300 Subject: [media] v4l2-dv-timings.h: add CEA-861-F 4K timings Add the CEA-861-F timings for 3840x2160p24/25/30/50/60 and 4096x2160p24/25/30/50/60. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-dv-timings.h | 70 ++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h index b6a5fe00a470..6c8f159e416e 100644 --- a/include/uapi/linux/v4l2-dv-timings.h +++ b/include/uapi/linux/v4l2-dv-timings.h @@ -173,6 +173,76 @@ V4L2_DV_FL_CAN_REDUCE_FPS) \ } +#define V4L2_DV_BT_CEA_3840X2160P24 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 297000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_3840X2160P25 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 297000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_3840X2160P30 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 297000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_3840X2160P50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 594000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_3840X2160P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 594000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_4096X2160P24 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 297000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_4096X2160P25 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 297000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_4096X2160P30 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 297000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_4096X2160P50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 594000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_4096X2160P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \ + 594000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + /* VESA Discrete Monitor Timings as per version 1.0, revision 12 */ -- cgit v1.2.3 From 74753cffa6fae399aef0f0a1a3315196e6e339cf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 7 Apr 2014 09:23:50 -0300 Subject: [media] vb2: add vb2_fileio_is_active and check it more often Added a vb2_fileio_is_active inline function that returns true if fileio is in progress. Check for this too in mmap() (you don't want apps mmap()ing buffers used by fileio) and expbuf() (same reason). In addition drivers should be able to check for this in queue_setup() to return an error if an attempt is made to read() or write() with V4L2_FIELD_ALTERNATE being configured. This is illegal (there is no way to pass the TOP/BOTTOM information around using file I/O). However, in order to be able to check for this the init_fileio function needs to set q->fileio early on, before the buffers are allocated. So switch to using internal functions (__reqbufs, vb2_internal_qbuf and vb2_internal_streamon) to skip the fileio check. Well, that's why the internal functions were created... Signed-off-by: Hans Verkuil Acked-by: Pawel Osciak Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 39 ++++++++++++++++++++------------ include/media/videobuf2-core.h | 17 ++++++++++++++ 2 files changed, 41 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 60fbb17cc913..b80fd24debfa 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -828,7 +828,7 @@ static int __verify_memory_type(struct vb2_queue *q, * create_bufs is called with count == 0, but count == 0 should still * do the memory and type validation. */ - if (q->fileio) { + if (vb2_fileio_is_active(q)) { dprintk(1, "file io in progress\n"); return -EBUSY; } @@ -1670,7 +1670,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) struct vb2_buffer *vb; int ret; - if (q->fileio) { + if (vb2_fileio_is_active(q)) { dprintk(1, "file io in progress\n"); return -EBUSY; } @@ -1838,7 +1838,7 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) */ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { - if (q->fileio) { + if (vb2_fileio_is_active(q)) { dprintk(1, "file io in progress\n"); return -EBUSY; } @@ -2058,7 +2058,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n */ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) { - if (q->fileio) { + if (vb2_fileio_is_active(q)) { dprintk(1, "file io in progress\n"); return -EBUSY; } @@ -2188,7 +2188,7 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) */ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { - if (q->fileio) { + if (vb2_fileio_is_active(q)) { dprintk(1, "file io in progress\n"); return -EBUSY; } @@ -2235,7 +2235,7 @@ static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) */ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { - if (q->fileio) { + if (vb2_fileio_is_active(q)) { dprintk(1, "file io in progress\n"); return -EBUSY; } @@ -2320,6 +2320,11 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) return -EINVAL; } + if (vb2_fileio_is_active(q)) { + dprintk(1, "expbuf: file io in progress\n"); + return -EBUSY; + } + vb_plane = &vb->planes[eb->plane]; dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE); @@ -2395,6 +2400,10 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) return -EINVAL; } } + if (vb2_fileio_is_active(q)) { + dprintk(1, "mmap: file io in progress\n"); + return -EBUSY; + } /* * Find the plane corresponding to the offset passed by userspace. @@ -2504,7 +2513,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) /* * Start file I/O emulator only if streaming API has not been used yet. */ - if (q->num_buffers == 0 && q->fileio == NULL) { + if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) { if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && (req_events & (POLLIN | POLLRDNORM))) { if (__vb2_init_fileio(q, 1)) @@ -2709,7 +2718,8 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) fileio->req.count = count; fileio->req.memory = V4L2_MEMORY_MMAP; fileio->req.type = q->type; - ret = vb2_reqbufs(q, &fileio->req); + q->fileio = fileio; + ret = __reqbufs(q, &fileio->req); if (ret) goto err_kfree; @@ -2747,7 +2757,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) b->type = q->type; b->memory = q->memory; b->index = i; - ret = vb2_qbuf(q, b); + ret = vb2_internal_qbuf(q, b); if (ret) goto err_reqbufs; fileio->bufs[i].queued = 1; @@ -2763,19 +2773,18 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) /* * Start streaming. */ - ret = vb2_streamon(q, q->type); + ret = vb2_internal_streamon(q, q->type); if (ret) goto err_reqbufs; - q->fileio = fileio; - return ret; err_reqbufs: fileio->req.count = 0; - vb2_reqbufs(q, &fileio->req); + __reqbufs(q, &fileio->req); err_kfree: + q->fileio = NULL; kfree(fileio); return ret; } @@ -2833,7 +2842,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ /* * Initialize emulator on first call. */ - if (!q->fileio) { + if (!vb2_fileio_is_active(q)) { ret = __vb2_init_fileio(q, read); dprintk(3, "vb2_init_fileio result: %d\n", ret); if (ret) @@ -3201,7 +3210,7 @@ unsigned int vb2_fop_poll(struct file *file, poll_table *wait) /* Try to be smart: only lock if polling might start fileio, otherwise locking will only introduce unwanted delays. */ - if (q->num_buffers == 0 && q->fileio == NULL) { + if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) { if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && (req_events & (POLLIN | POLLRDNORM))) must_lock = true; diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index af4621109726..b1859f6953b2 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -471,6 +471,23 @@ static inline bool vb2_is_streaming(struct vb2_queue *q) return q->streaming; } +/** + * vb2_fileio_is_active() - return true if fileio is active. + * @q: videobuf queue + * + * This returns true if read() or write() is used to stream the data + * as opposed to stream I/O. This is almost never an important distinction, + * except in rare cases. One such case is that using read() or write() to + * stream a format using V4L2_FIELD_ALTERNATE is not allowed since there + * is no way you can pass the field information of each buffer to/from + * userspace. A driver that supports this field format should check for + * this in the queue_setup op and reject it if this function returns true. + */ +static inline bool vb2_fileio_is_active(struct vb2_queue *q) +{ + return q->fileio; +} + /** * vb2_is_busy() - return busy status of the queue * @q: videobuf queue -- cgit v1.2.3 From 3415a89f48dce655ae353bc70a8e292764e8e931 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Apr 2014 07:33:00 -0300 Subject: [media] vb2: add thread support In order to implement vb2 DVB support you need to be able to start a kernel thread that queues and dequeues buffers, calling a callback function for every buffer. This patch adds support for that. It's based on drivers/media/v4l2-core/videobuf-dvb.c, but with all the DVB specific stuff stripped out, thus making it much more generic. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 146 +++++++++++++++++++++++++++++++ include/media/videobuf2-core.h | 32 +++++++ 2 files changed, 178 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 2075bac748c0..3f0cdb150dfd 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -6,6 +6,9 @@ * Author: Pawel Osciak * Marek Szyprowski * + * The vb2_thread implementation was based on code from videobuf-dvb.c: + * (c) 2004 Gerd Knorr [SUSE Labs] + * * 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. @@ -18,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -3005,6 +3010,147 @@ size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, } EXPORT_SYMBOL_GPL(vb2_write); +struct vb2_threadio_data { + struct task_struct *thread; + vb2_thread_fnc fnc; + void *priv; + bool stop; +}; + +static int vb2_thread(void *data) +{ + struct vb2_queue *q = data; + struct vb2_threadio_data *threadio = q->threadio; + struct vb2_fileio_data *fileio = q->fileio; + bool set_timestamp = false; + int prequeue = 0; + int index = 0; + int ret = 0; + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + prequeue = q->num_buffers; + set_timestamp = + (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == + V4L2_BUF_FLAG_TIMESTAMP_COPY; + } + + set_freezable(); + + for (;;) { + struct vb2_buffer *vb; + + /* + * Call vb2_dqbuf to get buffer back. + */ + memset(&fileio->b, 0, sizeof(fileio->b)); + fileio->b.type = q->type; + fileio->b.memory = q->memory; + if (prequeue) { + fileio->b.index = index++; + prequeue--; + } else { + call_void_qop(q, wait_finish, q); + ret = vb2_internal_dqbuf(q, &fileio->b, 0); + call_void_qop(q, wait_prepare, q); + dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); + } + if (threadio->stop) + break; + if (ret) + break; + try_to_freeze(); + + vb = q->bufs[fileio->b.index]; + if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR)) + ret = threadio->fnc(vb, threadio->priv); + if (ret) + break; + call_void_qop(q, wait_finish, q); + if (set_timestamp) + v4l2_get_timestamp(&fileio->b.timestamp); + ret = vb2_internal_qbuf(q, &fileio->b); + call_void_qop(q, wait_prepare, q); + if (ret) + break; + } + + /* Hmm, linux becomes *very* unhappy without this ... */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return 0; +} + +/* + * This function should not be used for anything else but the videobuf2-dvb + * support. If you think you have another good use-case for this, then please + * contact the linux-media mailinglist first. + */ +int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, + const char *thread_name) +{ + struct vb2_threadio_data *threadio; + int ret = 0; + + if (q->threadio) + return -EBUSY; + if (vb2_is_busy(q)) + return -EBUSY; + if (WARN_ON(q->fileio)) + return -EBUSY; + + threadio = kzalloc(sizeof(*threadio), GFP_KERNEL); + if (threadio == NULL) + return -ENOMEM; + threadio->fnc = fnc; + threadio->priv = priv; + + ret = __vb2_init_fileio(q, !V4L2_TYPE_IS_OUTPUT(q->type)); + dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); + if (ret) + goto nomem; + q->threadio = threadio; + threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name); + if (IS_ERR(threadio->thread)) { + ret = PTR_ERR(threadio->thread); + threadio->thread = NULL; + goto nothread; + } + return 0; + +nothread: + __vb2_cleanup_fileio(q); +nomem: + kfree(threadio); + return ret; +} +EXPORT_SYMBOL_GPL(vb2_thread_start); + +int vb2_thread_stop(struct vb2_queue *q) +{ + struct vb2_threadio_data *threadio = q->threadio; + struct vb2_fileio_data *fileio = q->fileio; + int err; + + if (threadio == NULL) + return 0; + call_void_qop(q, wait_finish, q); + threadio->stop = true; + vb2_internal_streamoff(q, q->type); + call_void_qop(q, wait_prepare, q); + q->fileio = NULL; + fileio->req.count = 0; + vb2_reqbufs(q, &fileio->req); + kfree(fileio); + err = kthread_stop(threadio->thread); + threadio->thread = NULL; + kfree(threadio); + q->fileio = NULL; + q->threadio = NULL; + return err; +} +EXPORT_SYMBOL_GPL(vb2_thread_stop); /* * The following functions are not part of the vb2 core API, but are helper diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index b1859f6953b2..46e76096c22a 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -20,6 +20,7 @@ struct vb2_alloc_ctx; struct vb2_fileio_data; +struct vb2_threadio_data; /** * struct vb2_mem_ops - memory handling/memory allocator operations @@ -375,6 +376,7 @@ struct v4l2_fh; * @start_streaming_called: start_streaming() was called successfully and we * started streaming. * @fileio: file io emulator internal data, used only if emulator is active + * @threadio: thread io internal data, used only if thread is active */ struct vb2_queue { enum v4l2_buf_type type; @@ -411,6 +413,7 @@ struct vb2_queue { unsigned int start_streaming_called:1; struct vb2_fileio_data *fileio; + struct vb2_threadio_data *threadio; #ifdef CONFIG_VIDEO_ADV_DEBUG /* @@ -461,6 +464,35 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, loff_t *ppos, int nonblock); size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, loff_t *ppos, int nonblock); +/** + * vb2_thread_fnc - callback function for use with vb2_thread + * + * This is called whenever a buffer is dequeued in the thread. + */ +typedef int (*vb2_thread_fnc)(struct vb2_buffer *vb, void *priv); + +/** + * vb2_thread_start() - start a thread for the given queue. + * @q: videobuf queue + * @fnc: callback function + * @priv: priv pointer passed to the callback function + * @thread_name:the name of the thread. This will be prefixed with "vb2-". + * + * This starts a thread that will queue and dequeue until an error occurs + * or @vb2_thread_stop is called. + * + * This function should not be used for anything else but the videobuf2-dvb + * support. If you think you have another good use-case for this, then please + * contact the linux-media mailinglist first. + */ +int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, + const char *thread_name); + +/** + * vb2_thread_stop() - stop the thread for the given queue. + * @q: videobuf queue + */ +int vb2_thread_stop(struct vb2_queue *q); /** * vb2_is_streaming() - return streaming status of the queue -- cgit v1.2.3 From 701b57ee3387b8e3749845b02310b5625fbd8da0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 9 Oct 2013 08:01:05 -0300 Subject: [media] vb2: Add videobuf2-dvb support With the new vb2_thread_start/stop core code it is very easy to implement videobuf2-dvb. This should simplify converting existing videobuf drivers to vb2. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/Kconfig | 4 + drivers/media/v4l2-core/Makefile | 1 + drivers/media/v4l2-core/videobuf2-dvb.c | 336 ++++++++++++++++++++++++++++++++ include/media/videobuf2-dvb.h | 58 ++++++ 4 files changed, 399 insertions(+) create mode 100644 drivers/media/v4l2-core/videobuf2-dvb.c create mode 100644 include/media/videobuf2-dvb.h (limited to 'include') diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 2189bfb2e828..9ca0f8d59a14 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -83,3 +83,7 @@ config VIDEOBUF2_DMA_SG #depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS + +config VIDEOBUF2_DVB + tristate + select VIDEOBUF2_CORE diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index c6ae7bad951e..63d29f27538c 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o +obj-$(CONFIG_VIDEOBUF2_DVB) += videobuf2-dvb.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/v4l2-core/videobuf2-dvb.c b/drivers/media/v4l2-core/videobuf2-dvb.c new file mode 100644 index 000000000000..d09269846b7e --- /dev/null +++ b/drivers/media/v4l2-core/videobuf2-dvb.c @@ -0,0 +1,336 @@ +/* + * + * some helper function for simple DVB cards which simply DMA the + * complete transport stream and let the computer sort everything else + * (i.e. we are using the software demux, ...). Also uses the + * video-buf to manage DMA buffers. + * + * (c) 2004 Gerd Knorr [SUSE Labs] + * + * 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 +#include +#include + +#include + +/* ------------------------------------------------------------------ */ + +MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------------ */ + +static int dvb_fnc(struct vb2_buffer *vb, void *priv) +{ + struct vb2_dvb *dvb = priv; + + dvb_dmx_swfilter(&dvb->demux, vb2_plane_vaddr(vb, 0), + vb2_get_plane_payload(vb, 0)); + return 0; +} + +static int vb2_dvb_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct vb2_dvb *dvb = demux->priv; + int rc = 0; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + + if (!dvb->dvbq.threadio) { + rc = vb2_thread_start(&dvb->dvbq, dvb_fnc, dvb, dvb->name); + if (rc) + dvb->nfeeds--; + } + if (!rc) + rc = dvb->nfeeds; + mutex_unlock(&dvb->lock); + return rc; +} + +static int vb2_dvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct vb2_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + if (0 == dvb->nfeeds) + err = vb2_thread_stop(&dvb->dvbq); + mutex_unlock(&dvb->lock); + return err; +} + +static int vb2_dvb_register_adapter(struct vb2_dvb_frontends *fe, + struct module *module, + void *adapter_priv, + struct device *device, + char *adapter_name, + short *adapter_nr, + int mfe_shared) +{ + int result; + + mutex_init(&fe->lock); + + /* register adapter */ + result = dvb_register_adapter(&fe->adapter, adapter_name, module, + device, adapter_nr); + if (result < 0) { + pr_warn("%s: dvb_register_adapter failed (errno = %d)\n", + adapter_name, result); + } + fe->adapter.priv = adapter_priv; + fe->adapter.mfe_shared = mfe_shared; + + return result; +} + +static int vb2_dvb_register_frontend(struct dvb_adapter *adapter, + struct vb2_dvb *dvb) +{ + int result; + + /* register frontend */ + result = dvb_register_frontend(adapter, dvb->frontend); + if (result < 0) { + pr_warn("%s: dvb_register_frontend failed (errno = %d)\n", + dvb->name, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = vb2_dvb_start_feed; + dvb->demux.stop_feed = vb2_dvb_stop_feed; + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + pr_warn("%s: dvb_dmx_init failed (errno = %d)\n", + dvb->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, adapter); + + if (result < 0) { + pr_warn("%s: dvb_dmxdev_init failed (errno = %d)\n", + dvb->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + pr_warn("%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dvb->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + pr_warn("%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dvb->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + pr_warn("%s: connect_frontend failed (errno = %d)\n", + dvb->name, result); + goto fail_fe_conn; + } + + /* register network adapter */ + result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx); + if (result < 0) { + pr_warn("%s: dvb_net_init failed (errno = %d)\n", + dvb->name, result); + goto fail_fe_conn; + } + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb->frontend = NULL; + + return result; +} + +/* ------------------------------------------------------------------ */ +/* Register a single adapter and one or more frontends */ +int vb2_dvb_register_bus(struct vb2_dvb_frontends *f, + struct module *module, + void *adapter_priv, + struct device *device, + short *adapter_nr, + int mfe_shared) +{ + struct list_head *list, *q; + struct vb2_dvb_frontend *fe; + int res; + + fe = vb2_dvb_get_frontend(f, 1); + if (!fe) { + pr_warn("Unable to register the adapter which has no frontends\n"); + return -EINVAL; + } + + /* Bring up the adapter */ + res = vb2_dvb_register_adapter(f, module, adapter_priv, device, + fe->dvb.name, adapter_nr, mfe_shared); + if (res < 0) { + pr_warn("vb2_dvb_register_adapter failed (errno = %d)\n", res); + return res; + } + + /* Attach all of the frontends to the adapter */ + mutex_lock(&f->lock); + list_for_each_safe(list, q, &f->felist) { + fe = list_entry(list, struct vb2_dvb_frontend, felist); + res = vb2_dvb_register_frontend(&f->adapter, &fe->dvb); + if (res < 0) { + pr_warn("%s: vb2_dvb_register_frontend failed (errno = %d)\n", + fe->dvb.name, res); + goto err; + } + } + mutex_unlock(&f->lock); + return 0; + +err: + mutex_unlock(&f->lock); + vb2_dvb_unregister_bus(f); + return res; +} +EXPORT_SYMBOL(vb2_dvb_register_bus); + +void vb2_dvb_unregister_bus(struct vb2_dvb_frontends *f) +{ + vb2_dvb_dealloc_frontends(f); + + dvb_unregister_adapter(&f->adapter); +} +EXPORT_SYMBOL(vb2_dvb_unregister_bus); + +struct vb2_dvb_frontend *vb2_dvb_get_frontend( + struct vb2_dvb_frontends *f, int id) +{ + struct list_head *list, *q; + struct vb2_dvb_frontend *fe, *ret = NULL; + + mutex_lock(&f->lock); + + list_for_each_safe(list, q, &f->felist) { + fe = list_entry(list, struct vb2_dvb_frontend, felist); + if (fe->id == id) { + ret = fe; + break; + } + } + + mutex_unlock(&f->lock); + + return ret; +} +EXPORT_SYMBOL(vb2_dvb_get_frontend); + +int vb2_dvb_find_frontend(struct vb2_dvb_frontends *f, + struct dvb_frontend *p) +{ + struct list_head *list, *q; + struct vb2_dvb_frontend *fe = NULL; + int ret = 0; + + mutex_lock(&f->lock); + + list_for_each_safe(list, q, &f->felist) { + fe = list_entry(list, struct vb2_dvb_frontend, felist); + if (fe->dvb.frontend == p) { + ret = fe->id; + break; + } + } + + mutex_unlock(&f->lock); + + return ret; +} +EXPORT_SYMBOL(vb2_dvb_find_frontend); + +struct vb2_dvb_frontend *vb2_dvb_alloc_frontend( + struct vb2_dvb_frontends *f, int id) +{ + struct vb2_dvb_frontend *fe; + + fe = kzalloc(sizeof(struct vb2_dvb_frontend), GFP_KERNEL); + if (fe == NULL) + return NULL; + + fe->id = id; + mutex_init(&fe->dvb.lock); + + mutex_lock(&f->lock); + list_add_tail(&fe->felist, &f->felist); + mutex_unlock(&f->lock); + return fe; +} +EXPORT_SYMBOL(vb2_dvb_alloc_frontend); + +void vb2_dvb_dealloc_frontends(struct vb2_dvb_frontends *f) +{ + struct list_head *list, *q; + struct vb2_dvb_frontend *fe; + + mutex_lock(&f->lock); + list_for_each_safe(list, q, &f->felist) { + fe = list_entry(list, struct vb2_dvb_frontend, felist); + if (fe->dvb.net.dvbdev) { + dvb_net_release(&fe->dvb.net); + fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, + &fe->dvb.fe_mem); + fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, + &fe->dvb.fe_hw); + dvb_dmxdev_release(&fe->dvb.dmxdev); + dvb_dmx_release(&fe->dvb.demux); + dvb_unregister_frontend(fe->dvb.frontend); + } + if (fe->dvb.frontend) + /* always allocated, may have been reset */ + dvb_frontend_detach(fe->dvb.frontend); + list_del(list); /* remove list entry */ + kfree(fe); /* free frontend allocation */ + } + mutex_unlock(&f->lock); +} +EXPORT_SYMBOL(vb2_dvb_dealloc_frontends); diff --git a/include/media/videobuf2-dvb.h b/include/media/videobuf2-dvb.h new file mode 100644 index 000000000000..8f61456f1394 --- /dev/null +++ b/include/media/videobuf2-dvb.h @@ -0,0 +1,58 @@ +#ifndef _VIDEOBUF2_DVB_H_ +#define _VIDEOBUF2_DVB_H_ + +#include +#include +#include +#include +#include +#include + +struct vb2_dvb { + /* filling that the job of the driver */ + char *name; + struct dvb_frontend *frontend; + struct vb2_queue dvbq; + + /* video-buf-dvb state info */ + struct mutex lock; + int nfeeds; + + /* vb2_dvb_(un)register manages this */ + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; +}; + +struct vb2_dvb_frontend { + struct list_head felist; + int id; + struct vb2_dvb dvb; +}; + +struct vb2_dvb_frontends { + struct list_head felist; + struct mutex lock; + struct dvb_adapter adapter; + int active_fe_id; /* Indicates which frontend in the felist is in use */ + int gate; /* Frontend with gate control 0=!MFE,1=fe0,2=fe1 etc */ +}; + +int vb2_dvb_register_bus(struct vb2_dvb_frontends *f, + struct module *module, + void *adapter_priv, + struct device *device, + short *adapter_nr, + int mfe_shared); + +void vb2_dvb_unregister_bus(struct vb2_dvb_frontends *f); + +struct vb2_dvb_frontend *vb2_dvb_alloc_frontend(struct vb2_dvb_frontends *f, int id); +void vb2_dvb_dealloc_frontends(struct vb2_dvb_frontends *f); + +struct vb2_dvb_frontend *vb2_dvb_get_frontend(struct vb2_dvb_frontends *f, int id); +int vb2_dvb_find_frontend(struct vb2_dvb_frontends *f, struct dvb_frontend *p); + +#endif /* _VIDEOBUF2_DVB_H_ */ -- cgit v1.2.3 From a5d92ad32dad94fd8f3f61778561d532bb3a2f77 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Mon, 17 Mar 2014 10:57:00 +0000 Subject: efivars: Stop passing a struct argument to efivar_validate() In preparation for compat support, we can't assume that user variable object is represented by a 'struct efi_variable'. Convert the validation functions to take the variable name as an argument, which is the only piece of the struct that was ever used anyway. Cc: Mike Waychison Signed-off-by: Matt Fleming --- drivers/firmware/efi/efivars.c | 6 ++++-- drivers/firmware/efi/vars.c | 30 +++++++++++++++--------------- include/linux/efi.h | 6 ++++-- 3 files changed, 23 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 2c21cccc2572..5ee2cfb96698 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -231,7 +231,7 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) } if ((attributes & ~EFI_VARIABLE_MASK) != 0 || - efivar_validate(new_var, data, size) == false) { + efivar_validate(name, data, size) == false) { printk(KERN_ERR "efivars: Malformed variable content\n"); return -EINVAL; } @@ -339,6 +339,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, { struct efi_variable *new_var = (struct efi_variable *)buf; struct efivar_entry *new_entry; + efi_char16_t *name; unsigned long size; u32 attributes; u8 *data; @@ -351,11 +352,12 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, return -EINVAL; attributes = new_var->Attributes; + name = new_var->VariableName; size = new_var->DataSize; data = new_var->Data; if ((attributes & ~EFI_VARIABLE_MASK) != 0 || - efivar_validate(new_var, data, size) == false) { + efivar_validate(name, data, size) == false) { printk(KERN_ERR "efivars: Malformed variable content\n"); return -EINVAL; } diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index b22659cccca4..f0a43646a2f3 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -42,7 +42,7 @@ DECLARE_WORK(efivar_work, NULL); EXPORT_SYMBOL_GPL(efivar_work); static bool -validate_device_path(struct efi_variable *var, int match, u8 *buffer, +validate_device_path(efi_char16_t *var_name, int match, u8 *buffer, unsigned long len) { struct efi_generic_dev_path *node; @@ -75,7 +75,7 @@ validate_device_path(struct efi_variable *var, int match, u8 *buffer, } static bool -validate_boot_order(struct efi_variable *var, int match, u8 *buffer, +validate_boot_order(efi_char16_t *var_name, int match, u8 *buffer, unsigned long len) { /* An array of 16-bit integers */ @@ -86,18 +86,18 @@ validate_boot_order(struct efi_variable *var, int match, u8 *buffer, } static bool -validate_load_option(struct efi_variable *var, int match, u8 *buffer, +validate_load_option(efi_char16_t *var_name, int match, u8 *buffer, unsigned long len) { u16 filepathlength; int i, desclength = 0, namelen; - namelen = ucs2_strnlen(var->VariableName, sizeof(var->VariableName)); + namelen = ucs2_strnlen(var_name, EFI_VAR_NAME_LEN); /* Either "Boot" or "Driver" followed by four digits of hex */ for (i = match; i < match+4; i++) { - if (var->VariableName[i] > 127 || - hex_to_bin(var->VariableName[i] & 0xff) < 0) + if (var_name[i] > 127 || + hex_to_bin(var_name[i] & 0xff) < 0) return true; } @@ -132,12 +132,12 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, /* * And, finally, check the filepath */ - return validate_device_path(var, match, buffer + desclength + 6, + return validate_device_path(var_name, match, buffer + desclength + 6, filepathlength); } static bool -validate_uint16(struct efi_variable *var, int match, u8 *buffer, +validate_uint16(efi_char16_t *var_name, int match, u8 *buffer, unsigned long len) { /* A single 16-bit integer */ @@ -148,7 +148,7 @@ validate_uint16(struct efi_variable *var, int match, u8 *buffer, } static bool -validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, +validate_ascii_string(efi_char16_t *var_name, int match, u8 *buffer, unsigned long len) { int i; @@ -166,7 +166,7 @@ validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, struct variable_validate { char *name; - bool (*validate)(struct efi_variable *var, int match, u8 *data, + bool (*validate)(efi_char16_t *var_name, int match, u8 *data, unsigned long len); }; @@ -189,10 +189,10 @@ static const struct variable_validate variable_validate[] = { }; bool -efivar_validate(struct efi_variable *var, u8 *data, unsigned long len) +efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long len) { int i; - u16 *unicode_name = var->VariableName; + u16 *unicode_name = var_name; for (i = 0; variable_validate[i].validate != NULL; i++) { const char *name = variable_validate[i].name; @@ -208,7 +208,7 @@ efivar_validate(struct efi_variable *var, u8 *data, unsigned long len) /* Wildcard in the matching name means we've matched */ if (c == '*') - return variable_validate[i].validate(var, + return variable_validate[i].validate(var_name, match, data, len); /* Case sensitive match */ @@ -217,7 +217,7 @@ efivar_validate(struct efi_variable *var, u8 *data, unsigned long len) /* Reached the end of the string while matching */ if (!c) - return variable_validate[i].validate(var, + return variable_validate[i].validate(var_name, match, data, len); } } @@ -805,7 +805,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes, *set = false; - if (efivar_validate(&entry->var, data, *size) == false) + if (efivar_validate(name, data, *size) == false) return -EINVAL; /* diff --git a/include/linux/efi.h b/include/linux/efi.h index 82d0abb2b19f..6a4d8e27d1d7 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1039,8 +1039,10 @@ struct efivars { * and we use a page for reading/writing. */ +#define EFI_VAR_NAME_LEN 1024 + struct efi_variable { - efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; + efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)]; efi_guid_t VendorGuid; unsigned long DataSize; __u8 Data[1024]; @@ -1122,7 +1124,7 @@ int efivar_entry_iter(int (*func)(struct efivar_entry *, void *), struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid, struct list_head *head, bool remove); -bool efivar_validate(struct efi_variable *var, u8 *data, unsigned long len); +bool efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long len); extern struct work_struct efivar_work; void efivar_run_worker(void); -- cgit v1.2.3 From f848a5a8dcb655553423f77cc98909a04e64173d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 31 Mar 2014 21:50:38 +0300 Subject: KVM: support any-length wildcard ioeventfd It is sometimes benefitial to ignore IO size, and only match on address. In hindsight this would have been a better default than matching length when KVM_IOEVENTFD_FLAG_DATAMATCH is not set, In particular, this kind of access can be optimized on VMX: there no need to do page lookups. This can currently be done with many ioeventfds but in a suboptimal way. However we can't change kernel/userspace ABI without risk of breaking some applications. Use len = 0 to mean "ignore length for matching" in a more optimal way. Signed-off-by: Michael S. Tsirkin Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/x86.c | 1 + include/uapi/linux/kvm.h | 3 ++- virt/kvm/eventfd.c | 27 ++++++++++++++++++++++----- 3 files changed, 25 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8b8fc0b792ba..bc4aaf68190c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2644,6 +2644,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_IRQ_INJECT_STATUS: case KVM_CAP_IRQFD: case KVM_CAP_IOEVENTFD: + case KVM_CAP_IOEVENTFD_NO_LENGTH: case KVM_CAP_PIT2: case KVM_CAP_PIT_STATE2: case KVM_CAP_SET_IDENTITY_MAP_ADDR: diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index a8f4ee5d2e82..39098a61f41c 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -529,7 +529,7 @@ enum { struct kvm_ioeventfd { __u64 datamatch; __u64 addr; /* legal pio/mmio address */ - __u32 len; /* 1, 2, 4, or 8 bytes */ + __u32 len; /* 1, 2, 4, or 8 bytes; or 0 to ignore length */ __s32 fd; __u32 flags; __u8 pad[36]; @@ -743,6 +743,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97 #define KVM_CAP_ENABLE_CAP_VM 98 #define KVM_CAP_S390_IRQCHIP 99 +#define KVM_CAP_IOEVENTFD_NO_LENGTH 100 #ifdef KVM_CAP_IRQ_ROUTING diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 29c2a04e036e..2721996bb9c2 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -600,7 +600,15 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val) { u64 _val; - if (!(addr == p->addr && len == p->length)) + if (addr != p->addr) + /* address must be precise for a hit */ + return false; + + if (!p->length) + /* length = 0 means only look at the address, so always a hit */ + return true; + + if (len != p->length) /* address-range must be precise for a hit */ return false; @@ -671,9 +679,11 @@ ioeventfd_check_collision(struct kvm *kvm, struct _ioeventfd *p) list_for_each_entry(_p, &kvm->ioeventfds, list) if (_p->bus_idx == p->bus_idx && - _p->addr == p->addr && _p->length == p->length && - (_p->wildcard || p->wildcard || - _p->datamatch == p->datamatch)) + _p->addr == p->addr && + (!_p->length || !p->length || + (_p->length == p->length && + (_p->wildcard || p->wildcard || + _p->datamatch == p->datamatch)))) return true; return false; @@ -697,8 +707,9 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) int ret; bus_idx = ioeventfd_bus_from_flags(args->flags); - /* must be natural-word sized */ + /* must be natural-word sized, or 0 to ignore length */ switch (args->len) { + case 0: case 1: case 2: case 4: @@ -716,6 +727,12 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) if (args->flags & ~KVM_IOEVENTFD_VALID_FLAG_MASK) return -EINVAL; + /* ioeventfd with no length can't be combined with DATAMATCH */ + if (!args->len && + args->flags & (KVM_IOEVENTFD_FLAG_PIO | + KVM_IOEVENTFD_FLAG_DATAMATCH)) + return -EINVAL; + eventfd = eventfd_ctx_fdget(args->fd); if (IS_ERR(eventfd)) return PTR_ERR(eventfd); -- cgit v1.2.3 From 68c3b4d1676d870f0453c31d5a52e7e65c7448ae Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 31 Mar 2014 21:50:44 +0300 Subject: KVM: VMX: speed up wildcard MMIO EVENTFD With KVM, MMIO is much slower than PIO, due to the need to do page walk and emulation. But with EPT, it does not have to be: we know the address from the VMCS so if the address is unique, we can look up the eventfd directly, bypassing emulation. Unfortunately, this only works if userspace does not need to match on access length and data. The implementation adds a separate FAST_MMIO bus internally. This serves two purposes: - minimize overhead for old userspace that does not use eventfd with lengtth = 0 - minimize disruption in other code (since we don't know the length, devices on the MMIO bus only get a valid address in write, this way we don't need to touch all devices to teach them to handle an invalid length) At the moment, this optimization only has effect for EPT on x86. It will be possible to speed up MMIO for NPT and MMU using the same idea in the future. With this patch applied, on VMX MMIO EVENTFD is essentially as fast as PIO. I was unable to detect any measureable slowdown to non-eventfd MMIO. Making MMIO faster is important for the upcoming virtio 1.0 which includes an MMIO signalling capability. The idea was suggested by Peter Anvin. Lots of thanks to Gleb for pre-review and suggestions. Signed-off-by: Michael S. Tsirkin Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 4 ++++ include/linux/kvm_host.h | 1 + include/uapi/linux/kvm.h | 1 + virt/kvm/eventfd.c | 16 ++++++++++++++++ virt/kvm/kvm_main.c | 1 + 5 files changed, 23 insertions(+) (limited to 'include') diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 1f68c5831924..eb3f2b1b764c 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -5528,6 +5528,10 @@ static int handle_ept_misconfig(struct kvm_vcpu *vcpu) gpa_t gpa; gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS); + if (!kvm_io_bus_write(vcpu->kvm, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) { + skip_emulated_instruction(vcpu); + return 1; + } ret = handle_mmio_page_fault_common(vcpu, gpa, true); if (likely(ret == RET_MMIO_PF_EMULATE)) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 7d21cf9f4380..6c3c2eb96d06 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -163,6 +163,7 @@ enum kvm_bus { KVM_MMIO_BUS, KVM_PIO_BUS, KVM_VIRTIO_CCW_NOTIFY_BUS, + KVM_FAST_MMIO_BUS, KVM_NR_BUSES }; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 39098a61f41c..d8a6ce4c2a83 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -515,6 +515,7 @@ enum { kvm_ioeventfd_flag_nr_pio, kvm_ioeventfd_flag_nr_deassign, kvm_ioeventfd_flag_nr_virtio_ccw_notify, + kvm_ioeventfd_flag_nr_fast_mmio, kvm_ioeventfd_flag_nr_max, }; diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 2721996bb9c2..912ec5a95e2c 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -770,6 +770,16 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) if (ret < 0) goto unlock_fail; + /* When length is ignored, MMIO is also put on a separate bus, for + * faster lookups. + */ + if (!args->len && !(args->flags & KVM_IOEVENTFD_FLAG_PIO)) { + ret = kvm_io_bus_register_dev(kvm, KVM_FAST_MMIO_BUS, + p->addr, 0, &p->dev); + if (ret < 0) + goto register_fail; + } + kvm->buses[bus_idx]->ioeventfd_count++; list_add_tail(&p->list, &kvm->ioeventfds); @@ -777,6 +787,8 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) return 0; +register_fail: + kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev); unlock_fail: mutex_unlock(&kvm->slots_lock); @@ -816,6 +828,10 @@ kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) continue; kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev); + if (!p->length) { + kvm_io_bus_unregister_dev(kvm, KVM_FAST_MMIO_BUS, + &p->dev); + } kvm->buses[bus_idx]->ioeventfd_count--; ioeventfd_release(p); ret = 0; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 56baae8c2f56..96456ac888ba 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2922,6 +2922,7 @@ static int __kvm_io_bus_read(struct kvm_io_bus *bus, struct kvm_io_range *range, return -EOPNOTSUPP; } +EXPORT_SYMBOL_GPL(kvm_io_bus_write); /* kvm_io_bus_read - called under kvm->slots_lock */ int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, -- cgit v1.2.3 From febdbfe8a91ce0d11939d4940b592eb0dba8d663 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 6 Feb 2014 18:16:07 +0100 Subject: arch: Prepare for smp_mb__{before,after}_atomic() Since the smp_mb__{before,after}*() ops are fundamentally dependent on how an arch can implement atomics it doesn't make sense to have 3 variants of them. They must all be the same. Furthermore, the 3 variants suggest they're only valid for those 3 atomic ops, while we have many more where they could be applied. So move away from smp_mb__{before,after}_{atomic,clear}_{dec,inc,bit}() and reduce the interface to just the two: smp_mb__{before,after}_atomic(). This patch prepares the way by introducing default implementations in asm-generic/barrier.h that default to a full barrier and providing __deprecated inlines for the previous 6 barriers if they're not provided by the arch. This should allow for a mostly painless transition (lots of deprecated warns in the interim). Signed-off-by: Peter Zijlstra Acked-by: Paul E. McKenney Link: http://lkml.kernel.org/n/tip-wr59327qdyi9mbzn6x937s4e@git.kernel.org Cc: Arnd Bergmann Cc: "Chen, Gong" Cc: John Sullivan Cc: Linus Torvalds Cc: Mauro Carvalho Chehab Cc: Srinivas Pandruvada Cc: "Theodore Ts'o" Cc: linux-arch@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar --- include/asm-generic/atomic.h | 7 +------ include/asm-generic/barrier.h | 8 ++++++++ include/asm-generic/bitops.h | 9 +-------- include/linux/atomic.h | 36 ++++++++++++++++++++++++++++++++++++ include/linux/bitops.h | 20 ++++++++++++++++++++ kernel/sched/core.c | 16 ++++++++++++++++ 6 files changed, 82 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/asm-generic/atomic.h b/include/asm-generic/atomic.h index 33bd2de3bc1e..9c79e7603459 100644 --- a/include/asm-generic/atomic.h +++ b/include/asm-generic/atomic.h @@ -16,6 +16,7 @@ #define __ASM_GENERIC_ATOMIC_H #include +#include #ifdef CONFIG_SMP /* Force people to define core atomics */ @@ -182,11 +183,5 @@ static inline void atomic_set_mask(unsigned int mask, atomic_t *v) } #endif -/* Assume that atomic operations are already serializing */ -#define smp_mb__before_atomic_dec() barrier() -#define smp_mb__after_atomic_dec() barrier() -#define smp_mb__before_atomic_inc() barrier() -#define smp_mb__after_atomic_inc() barrier() - #endif /* __KERNEL__ */ #endif /* __ASM_GENERIC_ATOMIC_H */ diff --git a/include/asm-generic/barrier.h b/include/asm-generic/barrier.h index 6f692f8ac664..1402fa855388 100644 --- a/include/asm-generic/barrier.h +++ b/include/asm-generic/barrier.h @@ -62,6 +62,14 @@ #define set_mb(var, value) do { (var) = (value); mb(); } while (0) #endif +#ifndef smp_mb__before_atomic +#define smp_mb__before_atomic() smp_mb() +#endif + +#ifndef smp_mb__after_atomic +#define smp_mb__after_atomic() smp_mb() +#endif + #define smp_store_release(p, v) \ do { \ compiletime_assert_atomic_type(*p); \ diff --git a/include/asm-generic/bitops.h b/include/asm-generic/bitops.h index 280ca7a96f75..dcdcacf2fd2b 100644 --- a/include/asm-generic/bitops.h +++ b/include/asm-generic/bitops.h @@ -11,14 +11,7 @@ #include #include - -/* - * clear_bit may not imply a memory barrier - */ -#ifndef smp_mb__before_clear_bit -#define smp_mb__before_clear_bit() smp_mb() -#define smp_mb__after_clear_bit() smp_mb() -#endif +#include #include #include diff --git a/include/linux/atomic.h b/include/linux/atomic.h index 5b08a8540ecf..fef3a809e7cf 100644 --- a/include/linux/atomic.h +++ b/include/linux/atomic.h @@ -3,6 +3,42 @@ #define _LINUX_ATOMIC_H #include +/* + * Provide __deprecated wrappers for the new interface, avoid flag day changes. + * We need the ugly external functions to break header recursion hell. + */ +#ifndef smp_mb__before_atomic_inc +static inline void __deprecated smp_mb__before_atomic_inc(void) +{ + extern void __smp_mb__before_atomic(void); + __smp_mb__before_atomic(); +} +#endif + +#ifndef smp_mb__after_atomic_inc +static inline void __deprecated smp_mb__after_atomic_inc(void) +{ + extern void __smp_mb__after_atomic(void); + __smp_mb__after_atomic(); +} +#endif + +#ifndef smp_mb__before_atomic_dec +static inline void __deprecated smp_mb__before_atomic_dec(void) +{ + extern void __smp_mb__before_atomic(void); + __smp_mb__before_atomic(); +} +#endif + +#ifndef smp_mb__after_atomic_dec +static inline void __deprecated smp_mb__after_atomic_dec(void) +{ + extern void __smp_mb__after_atomic(void); + __smp_mb__after_atomic(); +} +#endif + /** * atomic_add_unless - add unless the number is already a given value * @v: pointer of type atomic_t diff --git a/include/linux/bitops.h b/include/linux/bitops.h index be5fd38bd5a0..cbc5833fb221 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -32,6 +32,26 @@ extern unsigned long __sw_hweight64(__u64 w); */ #include +/* + * Provide __deprecated wrappers for the new interface, avoid flag day changes. + * We need the ugly external functions to break header recursion hell. + */ +#ifndef smp_mb__before_clear_bit +static inline void __deprecated smp_mb__before_clear_bit(void) +{ + extern void __smp_mb__before_atomic(void); + __smp_mb__before_atomic(); +} +#endif + +#ifndef smp_mb__after_clear_bit +static inline void __deprecated smp_mb__after_clear_bit(void) +{ + extern void __smp_mb__after_atomic(void); + __smp_mb__after_atomic(); +} +#endif + #define for_each_set_bit(bit, addr, size) \ for ((bit) = find_first_bit((addr), (size)); \ (bit) < (size); \ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 268a45ea238c..8a70ec091760 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -90,6 +90,22 @@ #define CREATE_TRACE_POINTS #include +#ifdef smp_mb__before_atomic +void __smp_mb__before_atomic(void) +{ + smp_mb__before_atomic(); +} +EXPORT_SYMBOL(__smp_mb__before_atomic); +#endif + +#ifdef smp_mb__after_atomic +void __smp_mb__after_atomic(void) +{ + smp_mb__after_atomic(); +} +EXPORT_SYMBOL(__smp_mb__after_atomic); +#endif + void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period) { unsigned long delta; -- cgit v1.2.3 From 27e4f9d0012a9bb7011aade862f08679d2921ab0 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 9 Apr 2014 12:50:34 +0200 Subject: sched/wait: Explain the shadowing and type inconsistencies Stick in a comment before someone else tries to fix the sparse warning this generates. Requested-by: Andrew Morton Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-o2ro6f3vkxklni0bc8f7m68s@git.kernel.org Cc: Linus Torvalds Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar --- include/linux/wait.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/wait.h b/include/linux/wait.h index 559044c79232..2b563a15a77d 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -191,11 +191,23 @@ wait_queue_head_t *bit_waitqueue(void *, int); (!__builtin_constant_p(state) || \ state == TASK_INTERRUPTIBLE || state == TASK_KILLABLE) \ +/* + * The below macro ___wait_event() has an explicit shadow of the __ret + * variable when used from the wait_event_*() macros. + * + * This is so that both can use the ___wait_cond_timeout() construct + * to wrap the condition. + * + * The type inconsistency of the wait_event_*() __ret variable is also + * on purpose; we use long where we can return timeout values and int + * otherwise. + */ + #define ___wait_event(wq, condition, state, exclusive, ret, cmd) \ ({ \ __label__ __out; \ wait_queue_t __wait; \ - long __ret = ret; \ + long __ret = ret; /* explicit shadow */ \ \ INIT_LIST_HEAD(&__wait.task_list); \ if (exclusive) \ -- cgit v1.2.3 From 08f8aeb55d7727d644dbbbbfb798fe937d47751d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 8 Apr 2014 14:27:25 +0200 Subject: sched: Remove set_need_resched() The last user is gone now, so we can safely remove this function. Signed-off-by: Peter Zijlstra Cc: Mike Galbraith Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar --- include/linux/thread_info.h | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'include') diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index fddbe2023a5d..cb0cec94fda3 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -104,20 +104,6 @@ static inline int test_ti_thread_flag(struct thread_info *ti, int flag) #define test_thread_flag(flag) \ test_ti_thread_flag(current_thread_info(), flag) -static inline __deprecated void set_need_resched(void) -{ - /* - * Use of this function in deprecated. - * - * As of this writing there are only a few users in the DRM tree left - * all of which are wrong and can be removed without causing too much - * grief. - * - * The DRM people are aware and are working on removing the last few - * instances. - */ -} - #define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) #if defined TIF_RESTORE_SIGMASK && !defined HAVE_SET_RESTORE_SIGMASK -- cgit v1.2.3 From c464c76eec4be587604ca082e8cded7e6b89f3bf Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 18 Mar 2014 16:56:41 +0800 Subject: perf: Allow building PMU drivers as modules This patch adds support for building PMU driver as module. It exports the functions perf_pmu_{register,unregister}() and adds reference tracking for the PMU driver module. When the PMU driver is built as a module, each active event of the PMU holds a reference to the driver module. Signed-off-by: Yan, Zheng Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1395133004-23205-1-git-send-email-zheng.z.yan@intel.com Cc: eranian@google.com Cc: Arnaldo Carvalho de Melo Cc: Linus Torvalds Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 1 + kernel/events/core.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3356abcfff18..af6dcf1d9e47 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -172,6 +172,7 @@ struct perf_event; struct pmu { struct list_head entry; + struct module *module; struct device *dev; const struct attribute_group **attr_groups; const char *name; diff --git a/kernel/events/core.c b/kernel/events/core.c index f83a71a3e46d..5129b1201050 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "internal.h" @@ -3229,6 +3230,9 @@ static void __free_event(struct perf_event *event) if (event->ctx) put_ctx(event->ctx); + if (event->pmu) + module_put(event->pmu->module); + call_rcu(&event->rcu_head, free_event_rcu); } static void free_event(struct perf_event *event) @@ -6551,6 +6555,7 @@ free_pdc: free_percpu(pmu->pmu_disable_count); goto unlock; } +EXPORT_SYMBOL_GPL(perf_pmu_register); void perf_pmu_unregister(struct pmu *pmu) { @@ -6572,6 +6577,7 @@ void perf_pmu_unregister(struct pmu *pmu) put_device(pmu->dev); free_pmu_context(pmu); } +EXPORT_SYMBOL_GPL(perf_pmu_unregister); struct pmu *perf_init_event(struct perf_event *event) { @@ -6585,6 +6591,10 @@ struct pmu *perf_init_event(struct perf_event *event) pmu = idr_find(&pmu_idr, event->attr.type); rcu_read_unlock(); if (pmu) { + if (!try_module_get(pmu->module)) { + pmu = ERR_PTR(-ENODEV); + goto unlock; + } event->pmu = pmu; ret = pmu->event_init(event); if (ret) @@ -6593,6 +6603,10 @@ struct pmu *perf_init_event(struct perf_event *event) } list_for_each_entry_rcu(pmu, &pmus, entry) { + if (!try_module_get(pmu->module)) { + pmu = ERR_PTR(-ENODEV); + goto unlock; + } event->pmu = pmu; ret = pmu->event_init(event); if (!ret) @@ -6771,6 +6785,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, err_pmu: if (event->destroy) event->destroy(event); + module_put(pmu->module); err_ns: if (event->ns) put_pid_ns(event->ns); -- cgit v1.2.3 From 4e857c58efeb99393cba5a5d0d8ec7117183137c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 17 Mar 2014 18:06:10 +0100 Subject: arch: Mass conversion of smp_mb__*() Mostly scripted conversion of the smp_mb__* barriers. Signed-off-by: Peter Zijlstra Acked-by: Paul E. McKenney Link: http://lkml.kernel.org/n/tip-55dhyhocezdw1dg7u19hmh1u@git.kernel.org Cc: Linus Torvalds Cc: linux-arch@vger.kernel.org Signed-off-by: Ingo Molnar --- block/blk-iopoll.c | 4 +- crypto/chainiv.c | 2 +- drivers/base/power/domain.c | 2 +- drivers/block/mtip32xx/mtip32xx.c | 4 +- drivers/cpuidle/coupled.c | 2 +- drivers/firewire/ohci.c | 2 +- drivers/gpu/drm/drm_irq.c | 10 ++-- drivers/gpu/drm/i915/i915_irq.c | 2 +- drivers/md/bcache/bcache.h | 2 +- drivers/md/bcache/closure.h | 2 +- drivers/md/dm-bufio.c | 8 ++-- drivers/md/dm-snap.c | 4 +- drivers/md/dm.c | 2 +- drivers/md/raid5.c | 2 +- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 6 +-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 6 +-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 18 ++++---- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c | 26 +++++------ drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c | 8 ++-- drivers/net/ethernet/broadcom/cnic.c | 8 ++-- drivers/net/ethernet/brocade/bna/bnad.c | 6 +-- drivers/net/ethernet/chelsio/cxgb/cxgb2.c | 2 +- drivers/net/ethernet/chelsio/cxgb3/sge.c | 6 +-- drivers/net/ethernet/chelsio/cxgb4/sge.c | 2 +- drivers/net/ethernet/chelsio/cxgb4vf/sge.c | 2 +- drivers/net/ethernet/freescale/gianfar.c | 8 ++-- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 8 ++-- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 6 +-- drivers/net/wireless/ti/wlcore/main.c | 2 +- drivers/pci/xen-pcifront.c | 4 +- drivers/scsi/isci/remote_device.c | 2 +- drivers/target/loopback/tcm_loop.c | 4 +- drivers/target/target_core_alua.c | 26 +++++------ drivers/target/target_core_device.c | 6 +-- drivers/target/target_core_iblock.c | 2 +- drivers/target/target_core_pr.c | 56 +++++++++++------------ drivers/target/target_core_transport.c | 16 +++---- drivers/target/target_core_ua.c | 10 ++-- drivers/tty/n_tty.c | 2 +- drivers/tty/serial/mxs-auart.c | 4 +- drivers/usb/gadget/tcm_usb_gadget.c | 4 +- drivers/usb/serial/usb_wwan.c | 2 +- drivers/vhost/scsi.c | 2 +- drivers/w1/w1_family.c | 4 +- drivers/xen/xen-pciback/pciback_ops.c | 4 +- fs/btrfs/btrfs_inode.h | 2 +- fs/btrfs/extent_io.c | 2 +- fs/btrfs/inode.c | 6 +-- fs/btrfs/ioctl.c | 2 +- fs/buffer.c | 2 +- fs/ext4/resize.c | 2 +- fs/gfs2/glock.c | 8 ++-- fs/gfs2/glops.c | 2 +- fs/gfs2/lock_dlm.c | 4 +- fs/gfs2/recovery.c | 2 +- fs/gfs2/sys.c | 4 +- fs/jbd2/commit.c | 6 +-- fs/nfs/dir.c | 12 ++--- fs/nfs/inode.c | 2 +- fs/nfs/nfs4filelayoutdev.c | 4 +- fs/nfs/nfs4state.c | 4 +- fs/nfs/pagelist.c | 6 +-- fs/nfs/pnfs.c | 2 +- fs/nfs/pnfs.h | 2 +- fs/nfs/write.c | 4 +- fs/ubifs/lpt_commit.c | 4 +- fs/ubifs/tnc_commit.c | 4 +- include/asm-generic/bitops/atomic.h | 2 +- include/asm-generic/bitops/lock.h | 2 +- include/linux/buffer_head.h | 2 +- include/linux/genhd.h | 2 +- include/linux/interrupt.h | 8 ++-- include/linux/netdevice.h | 2 +- include/linux/sched.h | 6 +-- include/linux/sunrpc/sched.h | 8 ++-- include/linux/sunrpc/xprt.h | 8 ++-- include/linux/tracehook.h | 2 +- include/net/ip_vs.h | 4 +- kernel/debug/debug_core.c | 4 +- kernel/futex.c | 4 +- kernel/kmod.c | 2 +- kernel/rcu/tree.c | 22 ++++----- kernel/rcu/tree_plugin.h | 8 ++-- kernel/sched/cpupri.c | 6 +-- kernel/sched/wait.c | 2 +- mm/backing-dev.c | 2 +- mm/filemap.c | 4 +- net/atm/pppoatm.c | 2 +- net/bluetooth/hci_event.c | 4 +- net/core/dev.c | 8 ++-- net/core/link_watch.c | 2 +- net/ipv4/inetpeer.c | 2 +- net/ipv4/tcp_output.c | 4 +- net/netfilter/nf_conntrack_core.c | 2 +- net/rds/ib_recv.c | 4 +- net/rds/iw_recv.c | 4 +- net/rds/send.c | 6 +-- net/rds/tcp_send.c | 2 +- net/sunrpc/auth.c | 2 +- net/sunrpc/auth_gss/auth_gss.c | 2 +- net/sunrpc/backchannel_rqst.c | 4 +- net/sunrpc/xprt.c | 4 +- net/sunrpc/xprtsock.c | 16 +++---- net/unix/af_unix.c | 2 +- sound/pci/bt87x.c | 4 +- 106 files changed, 284 insertions(+), 288 deletions(-) (limited to 'include') diff --git a/block/blk-iopoll.c b/block/blk-iopoll.c index c11d24e379e2..f8c6a11b13f0 100644 --- a/block/blk-iopoll.c +++ b/block/blk-iopoll.c @@ -49,7 +49,7 @@ EXPORT_SYMBOL(blk_iopoll_sched); void __blk_iopoll_complete(struct blk_iopoll *iop) { list_del(&iop->list); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit_unlock(IOPOLL_F_SCHED, &iop->state); } EXPORT_SYMBOL(__blk_iopoll_complete); @@ -161,7 +161,7 @@ EXPORT_SYMBOL(blk_iopoll_disable); void blk_iopoll_enable(struct blk_iopoll *iop) { BUG_ON(!test_bit(IOPOLL_F_SCHED, &iop->state)); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit_unlock(IOPOLL_F_SCHED, &iop->state); } EXPORT_SYMBOL(blk_iopoll_enable); diff --git a/crypto/chainiv.c b/crypto/chainiv.c index 834d8dd3d4fc..9c294c8f9a07 100644 --- a/crypto/chainiv.c +++ b/crypto/chainiv.c @@ -126,7 +126,7 @@ static int async_chainiv_schedule_work(struct async_chainiv_ctx *ctx) int err = ctx->err; if (!ctx->queue.qlen) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(CHAINIV_STATE_INUSE, &ctx->state); if (!ctx->queue.qlen || diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index ae098a261fcd..eee55c1e5fde 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -105,7 +105,7 @@ static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) { atomic_inc(&genpd->sd_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } static void genpd_acquire_lock(struct generic_pm_domain *genpd) diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 59c5abe32f06..4fd8d6c1c3d2 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -224,9 +224,9 @@ static int get_slot(struct mtip_port *port) */ static inline void release_slot(struct mtip_port *port, int tag) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(tag, port->allocated); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } /* diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c index cb6654bfad77..73fe2f8d7f96 100644 --- a/drivers/cpuidle/coupled.c +++ b/drivers/cpuidle/coupled.c @@ -159,7 +159,7 @@ void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a) { int n = dev->coupled->online_count; - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(a); while (atomic_read(a) < n) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 8db663219560..995dd42a2627 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -3498,7 +3498,7 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base) } clear_bit_unlock(0, &ctx->flushing_completions); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } tasklet_enable(&ctx->context.tasklet); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index c2676b5908d9..ec5c3f4cdd01 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -156,7 +156,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) */ if ((vblrc > 0) && (abs64(diff_ns) > 1000000)) { atomic_inc(&dev->vblank[crtc].count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } /* Invalidate all timestamps while vblank irq's are off. */ @@ -864,9 +864,9 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) vblanktimestamp(dev, crtc, tslot) = t_vblank; } - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_add(diff, &dev->vblank[crtc].count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } /** @@ -1330,9 +1330,9 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) /* Increment cooked vblank count. This also atomically commits * the timestamp computed above. */ - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&dev->vblank[crtc].count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } else { DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", crtc, (int) diff_ns); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7753249b3a95..5409bfafff63 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2147,7 +2147,7 @@ static void i915_error_work_func(struct work_struct *work) * updates before * the counter increment. */ - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&dev_priv->gpu_error.reset_counter); kobject_uevent_env(&dev->primary->kdev->kobj, diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index 82c9c5d35251..d2ebcf323094 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -828,7 +828,7 @@ static inline bool cached_dev_get(struct cached_dev *dc) return false; /* Paired with the mb in cached_dev_attach */ - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); return true; } diff --git a/drivers/md/bcache/closure.h b/drivers/md/bcache/closure.h index 7ef7461912be..a08e3eeac3c5 100644 --- a/drivers/md/bcache/closure.h +++ b/drivers/md/bcache/closure.h @@ -243,7 +243,7 @@ static inline void set_closure_fn(struct closure *cl, closure_fn *fn, cl->fn = fn; cl->wq = wq; /* between atomic_dec() in closure_put() */ - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); } static inline void closure_queue(struct closure *cl) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 66c5d130c8c2..4e84095833db 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -607,9 +607,9 @@ static void write_endio(struct bio *bio, int error) BUG_ON(!test_bit(B_WRITING, &b->state)); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(B_WRITING, &b->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&b->state, B_WRITING); } @@ -997,9 +997,9 @@ static void read_endio(struct bio *bio, int error) BUG_ON(!test_bit(B_READING, &b->state)); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(B_READING, &b->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&b->state, B_READING); } diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index ebddef5237e4..8e0caed0bf74 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -642,7 +642,7 @@ static void free_pending_exception(struct dm_snap_pending_exception *pe) struct dm_snapshot *s = pe->snap; mempool_free(pe, s->pending_pool); - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&s->pending_exceptions_count); } @@ -783,7 +783,7 @@ static int init_hash_tables(struct dm_snapshot *s) static void merge_shutdown(struct dm_snapshot *s) { clear_bit_unlock(RUNNING_MERGE, &s->state_bits); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&s->state_bits, RUNNING_MERGE); } diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 455e64916498..2db768e4553f 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2447,7 +2447,7 @@ static void dm_wq_work(struct work_struct *work) static void dm_queue_flush(struct mapped_device *md) { clear_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); queue_work(md->wq, &md->work); } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index ad1b9bea446e..2afef4ec9312 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -4400,7 +4400,7 @@ static void raid5_unplug(struct blk_plug_cb *blk_cb, bool from_schedule) * STRIPE_ON_UNPLUG_LIST clear but the stripe * is still in our list */ - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(STRIPE_ON_UNPLUG_LIST, &sh->state); /* * STRIPE_ON_RELEASE_LIST could be set here. In that diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index de02db802ace..e35580618936 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -399,7 +399,7 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) /* clear 'streaming' status bit */ clear_bit(ADAP_STREAMING, &adap->state_bits); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&adap->state_bits, ADAP_STREAMING); skip_feed_stop: @@ -550,7 +550,7 @@ static int dvb_usb_fe_init(struct dvb_frontend *fe) err: if (!adap->suspend_resume_active) { clear_bit(ADAP_INIT, &adap->state_bits); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&adap->state_bits, ADAP_INIT); } @@ -591,7 +591,7 @@ err: if (!adap->suspend_resume_active) { adap->active_fe = -1; clear_bit(ADAP_SLEEP, &adap->state_bits); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&adap->state_bits, ADAP_SLEEP); } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 9261d5313b5b..dd57c7c5a3da 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2781,7 +2781,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) case LOAD_OPEN: netif_tx_start_all_queues(bp->dev); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); break; case LOAD_DIAG: @@ -4939,9 +4939,9 @@ void bnx2x_update_coalesce_sb_index(struct bnx2x *bp, u8 fw_sb_id, void bnx2x_schedule_sp_rtnl(struct bnx2x *bp, enum sp_rtnl_flag flag, u32 verbose) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(flag, &bp->sp_rtnl_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); DP((BNX2X_MSG_SP | verbose), "Scheduling sp_rtnl task [Flag: %d]\n", flag); schedule_delayed_work(&bp->sp_rtnl_task, 0); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index a78edaccceee..16391db2e8c9 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -1858,10 +1858,10 @@ void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe) return; #endif - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&bp->cq_spq_left); /* push the change in bp->spq_left and towards the memory */ - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); DP(BNX2X_MSG_SP, "bp->cq_spq_left %x\n", atomic_read(&bp->cq_spq_left)); @@ -1876,11 +1876,11 @@ void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe) * sp_state is cleared, and this order prevents * races */ - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(BNX2X_AFEX_PENDING_VIFSET_MCP_ACK, &bp->sp_state); wmb(); clear_bit(BNX2X_AFEX_FCOE_Q_UPDATE_PENDING, &bp->sp_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* schedule the sp task as mcp ack is required */ bnx2x_schedule_sp_task(bp); @@ -5272,9 +5272,9 @@ static void bnx2x_after_function_update(struct bnx2x *bp) __clear_bit(RAMROD_COMP_WAIT, &queue_params.ramrod_flags); /* mark latest Q bit */ - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(BNX2X_AFEX_FCOE_Q_UPDATE_PENDING, &bp->sp_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* send Q update ramrod for FCoE Q */ rc = bnx2x_queue_state_change(bp, &queue_params); @@ -5500,7 +5500,7 @@ next_spqe: spqe_cnt++; } /* for */ - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_add(spqe_cnt, &bp->eq_spq_left); bp->eq_cons = sw_cons; @@ -13869,9 +13869,9 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl) case DRV_CTL_RET_L2_SPQ_CREDIT_CMD: { int count = ctl->data.credit.credit_count; - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_add(count, &bp->cq_spq_left); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); break; } case DRV_CTL_ULP_REGISTER_CMD: { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index 31297266b743..d725317c4277 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -258,16 +258,16 @@ static bool bnx2x_raw_check_pending(struct bnx2x_raw_obj *o) static void bnx2x_raw_clear_pending(struct bnx2x_raw_obj *o) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(o->state, o->pstate); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } static void bnx2x_raw_set_pending(struct bnx2x_raw_obj *o) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(o->state, o->pstate); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } /** @@ -2131,7 +2131,7 @@ static int bnx2x_set_rx_mode_e1x(struct bnx2x *bp, /* The operation is completed */ clear_bit(p->state, p->pstate); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return 0; } @@ -3576,16 +3576,16 @@ error_exit1: static void bnx2x_mcast_clear_sched(struct bnx2x_mcast_obj *o) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(o->sched_state, o->raw.pstate); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } static void bnx2x_mcast_set_sched(struct bnx2x_mcast_obj *o) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(o->sched_state, o->raw.pstate); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } static bool bnx2x_mcast_check_sched(struct bnx2x_mcast_obj *o) @@ -4200,7 +4200,7 @@ int bnx2x_queue_state_change(struct bnx2x *bp, if (rc) { o->next_state = BNX2X_Q_STATE_MAX; clear_bit(pending_bit, pending); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return rc; } @@ -4288,7 +4288,7 @@ static int bnx2x_queue_comp_cmd(struct bnx2x *bp, wmb(); clear_bit(cmd, &o->pending); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return 0; } @@ -5279,7 +5279,7 @@ static inline int bnx2x_func_state_change_comp(struct bnx2x *bp, wmb(); clear_bit(cmd, &o->pending); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return 0; } @@ -5926,7 +5926,7 @@ int bnx2x_func_state_change(struct bnx2x *bp, if (rc) { o->next_state = BNX2X_F_STATE_MAX; clear_bit(cmd, pending); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return rc; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 5c523b32db70..f82ac5ac2336 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -1626,9 +1626,9 @@ static void bnx2x_vf_handle_filters_eqe(struct bnx2x *bp, struct bnx2x_virtf *vf) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(BNX2X_FILTER_RX_MODE_PENDING, &vf->filter_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } static void bnx2x_vf_handle_rss_update_eqe(struct bnx2x *bp, @@ -2960,9 +2960,9 @@ void bnx2x_iov_task(struct work_struct *work) void bnx2x_schedule_iov_task(struct bnx2x *bp, enum bnx2x_iov_flag flag) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(flag, &bp->iov_task_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); DP(BNX2X_MSG_IOV, "Scheduling iov task [Flag: %d]\n", flag); queue_delayed_work(bnx2x_iov_wq, &bp->iov_task, 0); } diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 09f3fefcbf9c..4dd48d2fa804 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -436,7 +436,7 @@ static int cnic_offld_prep(struct cnic_sock *csk) static int cnic_close_prep(struct cnic_sock *csk) { clear_bit(SK_F_CONNECT_START, &csk->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (test_and_clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags)) { while (test_and_set_bit(SK_F_OFFLD_SCHED, &csk->flags)) @@ -450,7 +450,7 @@ static int cnic_close_prep(struct cnic_sock *csk) static int cnic_abort_prep(struct cnic_sock *csk) { clear_bit(SK_F_CONNECT_START, &csk->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); while (test_and_set_bit(SK_F_OFFLD_SCHED, &csk->flags)) msleep(1); @@ -3646,7 +3646,7 @@ static int cnic_cm_destroy(struct cnic_sock *csk) csk_hold(csk); clear_bit(SK_F_INUSE, &csk->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); while (atomic_read(&csk->ref_count) != 1) msleep(1); cnic_cm_cleanup(csk); @@ -4026,7 +4026,7 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe) L4_KCQE_COMPLETION_STATUS_PARITY_ERROR) set_bit(SK_F_HW_ERR, &csk->flags); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(SK_F_OFFLD_SCHED, &csk->flags); cnic_cm_upcall(cp, csk, opcode); break; diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 675550fe8ee9..3a77f9ead004 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -249,7 +249,7 @@ bnad_tx_complete(struct bnad *bnad, struct bna_tcb *tcb) if (likely(test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))) bna_ib_ack(tcb->i_dbell, sent); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags); return sent; @@ -1126,7 +1126,7 @@ bnad_tx_cleanup(struct delayed_work *work) bnad_txq_cleanup(bnad, tcb); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags); } @@ -2992,7 +2992,7 @@ bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev) sent = bnad_txcmpl_process(bnad, tcb); if (likely(test_bit(BNAD_TXQ_TX_STARTED, &tcb->flags))) bna_ib_ack(tcb->i_dbell, sent); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags); } else { netif_stop_queue(netdev); diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index 0fe7ff750d77..05613a85ce61 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -281,7 +281,7 @@ static int cxgb_close(struct net_device *dev) if (adapter->params.stats_update_period && !(adapter->open_device_map & PORT_MASK)) { /* Stop statistics accumulation. */ - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); spin_lock(&adapter->work_lock); /* sync with update task */ spin_unlock(&adapter->work_lock); cancel_mac_stats_update(adapter); diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c index 8b069f96e920..3dfcf600fcc6 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c @@ -1379,7 +1379,7 @@ static inline int check_desc_avail(struct adapter *adap, struct sge_txq *q, struct sge_qset *qs = txq_to_qset(q, qid); set_bit(qid, &qs->txq_stopped); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (should_restart_tx(q) && test_and_clear_bit(qid, &qs->txq_stopped)) @@ -1492,7 +1492,7 @@ static void restart_ctrlq(unsigned long data) if (!skb_queue_empty(&q->sendq)) { set_bit(TXQ_CTRL, &qs->txq_stopped); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (should_restart_tx(q) && test_and_clear_bit(TXQ_CTRL, &qs->txq_stopped)) @@ -1697,7 +1697,7 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); if (unlikely(q->size - q->in_use < ndesc)) { set_bit(TXQ_OFLD, &qs->txq_stopped); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (should_restart_tx(q) && test_and_clear_bit(TXQ_OFLD, &qs->txq_stopped)) diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index ca95cf2954eb..e249528c8e60 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2031,7 +2031,7 @@ static void sge_rx_timer_cb(unsigned long data) struct sge_fl *fl = s->egr_map[id]; clear_bit(id, s->starving_fl); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (fl_starving(fl)) { rxq = container_of(fl, struct sge_eth_rxq, fl); diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index 9cfa4b4bb089..9d88c1d50b49 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -1951,7 +1951,7 @@ static void sge_rx_timer_cb(unsigned long data) struct sge_fl *fl = s->egr_map[id]; clear_bit(id, s->starving_fl); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* * Since we are accessing fl without a lock there's a diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 9125d9abf099..d82f092cae90 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1797,9 +1797,9 @@ void stop_gfar(struct net_device *dev) netif_tx_stop_all_queues(dev); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(GFAR_DOWN, &priv->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); disable_napi(priv); @@ -2042,9 +2042,9 @@ int startup_gfar(struct net_device *ndev) gfar_init_tx_rx_base(priv); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(GFAR_DOWN, &priv->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* Start Rx/Tx DMA and enable the interrupts */ gfar_start(priv); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 861b722c2672..1e526c072a44 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4671,7 +4671,7 @@ static void i40e_service_event_complete(struct i40e_pf *pf) BUG_ON(!test_bit(__I40E_SERVICE_SCHED, &pf->state)); /* flush memory to make sure state is correct before next watchog */ - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__I40E_SERVICE_SCHED, &pf->state); } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c4c526b7f99f..2fecc2626de5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -376,7 +376,7 @@ static void ixgbe_service_event_complete(struct ixgbe_adapter *adapter) BUG_ON(!test_bit(__IXGBE_SERVICE_SCHED, &adapter->state)); /* flush memory to make sure state is correct before next watchdog */ - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__IXGBE_SERVICE_SCHED, &adapter->state); } @@ -4671,7 +4671,7 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) if (hw->mac.ops.enable_tx_laser) hw->mac.ops.enable_tx_laser(hw); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__IXGBE_DOWN, &adapter->state); ixgbe_napi_enable_all(adapter); @@ -5567,7 +5567,7 @@ static int ixgbe_resume(struct pci_dev *pdev) e_dev_err("Cannot enable PCI device from suspend\n"); return err; } - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__IXGBE_DISABLED, &adapter->state); pci_set_master(pdev); @@ -8541,7 +8541,7 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev) e_err(probe, "Cannot re-enable PCI device after reset.\n"); result = PCI_ERS_RESULT_DISCONNECT; } else { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__IXGBE_DISABLED, &adapter->state); adapter->hw.hw_addr = adapter->io_addr; pci_set_master(pdev); diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index d0799e8e31e4..de2793b06305 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1668,7 +1668,7 @@ static void ixgbevf_up_complete(struct ixgbevf_adapter *adapter) spin_unlock_bh(&adapter->mbx_lock); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__IXGBEVF_DOWN, &adapter->state); ixgbevf_napi_enable_all(adapter); @@ -3354,7 +3354,7 @@ static int ixgbevf_resume(struct pci_dev *pdev) dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n"); return err; } - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__IXGBEVF_DISABLED, &adapter->state); pci_set_master(pdev); @@ -3712,7 +3712,7 @@ static pci_ers_result_t ixgbevf_io_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__IXGBEVF_DISABLED, &adapter->state); pci_set_master(pdev); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ed88d3913483..e71eae353368 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -543,7 +543,7 @@ static int wlcore_irq_locked(struct wl1271 *wl) * wl1271_ps_elp_wakeup cannot be called concurrently. */ clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); ret = wlcore_fw_status(wl, wl->fw_status); if (ret < 0) diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 179b8edc2262..53df39a22c8a 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -662,9 +662,9 @@ static void pcifront_do_aer(struct work_struct *data) notify_remote_via_evtchn(pdev->evtchn); /*in case of we lost an aer request in four lines time_window*/ - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(_PDEVB_op_active, &pdev->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); schedule_pcifront_aer_op(pdev); diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 96a26f454673..cc51f38b116d 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1541,7 +1541,7 @@ void isci_remote_device_release(struct kref *kref) clear_bit(IDEV_STOP_PENDING, &idev->flags); clear_bit(IDEV_IO_READY, &idev->flags); clear_bit(IDEV_GONE, &idev->flags); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(IDEV_ALLOCATED, &idev->flags); wake_up(&ihost->eventq); } diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index c886ad1c39fb..73ab75ddaf42 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -951,7 +951,7 @@ static int tcm_loop_port_link( struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; atomic_inc(&tl_tpg->tl_tpg_port_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); /* * Add Linux/SCSI struct scsi_device by HCTL */ @@ -986,7 +986,7 @@ static void tcm_loop_port_unlink( scsi_device_put(sd); atomic_dec(&tl_tpg->tl_tpg_port_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n"); } diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index fcbe6125b73e..0b79b852f4b2 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -393,7 +393,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) continue; atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&dev->t10_alua.tg_pt_gps_lock); @@ -404,7 +404,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) spin_lock(&dev->t10_alua.tg_pt_gps_lock); atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); break; } spin_unlock(&dev->t10_alua.tg_pt_gps_lock); @@ -990,7 +990,7 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) * TARGET PORT GROUPS command */ atomic_inc(&mem->tg_pt_gp_mem_ref_cnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&tg_pt_gp->tg_pt_gp_lock); spin_lock_bh(&port->sep_alua_lock); @@ -1020,7 +1020,7 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) spin_lock(&tg_pt_gp->tg_pt_gp_lock); atomic_dec(&mem->tg_pt_gp_mem_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } spin_unlock(&tg_pt_gp->tg_pt_gp_lock); /* @@ -1054,7 +1054,7 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_pending_state)); spin_lock(&dev->t10_alua.tg_pt_gps_lock); atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); spin_unlock(&dev->t10_alua.tg_pt_gps_lock); if (tg_pt_gp->tg_pt_gp_transition_complete) @@ -1116,7 +1116,7 @@ static int core_alua_do_transition_tg_pt( */ spin_lock(&dev->t10_alua.tg_pt_gps_lock); atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&dev->t10_alua.tg_pt_gps_lock); if (!explicit && tg_pt_gp->tg_pt_gp_implicit_trans_secs) { @@ -1159,7 +1159,7 @@ int core_alua_do_port_transition( spin_lock(&local_lu_gp_mem->lu_gp_mem_lock); lu_gp = local_lu_gp_mem->lu_gp; atomic_inc(&lu_gp->lu_gp_ref_cnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock); /* * For storage objects that are members of the 'default_lu_gp', @@ -1176,7 +1176,7 @@ int core_alua_do_port_transition( rc = core_alua_do_transition_tg_pt(l_tg_pt_gp, new_state, explicit); atomic_dec(&lu_gp->lu_gp_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); return rc; } /* @@ -1190,7 +1190,7 @@ int core_alua_do_port_transition( dev = lu_gp_mem->lu_gp_mem_dev; atomic_inc(&lu_gp_mem->lu_gp_mem_ref_cnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&lu_gp->lu_gp_lock); spin_lock(&dev->t10_alua.tg_pt_gps_lock); @@ -1219,7 +1219,7 @@ int core_alua_do_port_transition( tg_pt_gp->tg_pt_gp_alua_nacl = NULL; } atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&dev->t10_alua.tg_pt_gps_lock); /* * core_alua_do_transition_tg_pt() will always return @@ -1230,7 +1230,7 @@ int core_alua_do_port_transition( spin_lock(&dev->t10_alua.tg_pt_gps_lock); atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); if (rc) break; } @@ -1238,7 +1238,7 @@ int core_alua_do_port_transition( spin_lock(&lu_gp->lu_gp_lock); atomic_dec(&lu_gp_mem->lu_gp_mem_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } spin_unlock(&lu_gp->lu_gp_lock); @@ -1252,7 +1252,7 @@ int core_alua_do_port_transition( } atomic_dec(&lu_gp->lu_gp_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); return rc; } diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 65001e133670..72618776ede4 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -225,7 +225,7 @@ struct se_dev_entry *core_get_se_deve_from_rtpi( continue; atomic_inc(&deve->pr_ref_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock_irq(&nacl->device_list_lock); return deve; @@ -1392,7 +1392,7 @@ int core_dev_add_initiator_node_lun_acl( spin_lock(&lun->lun_acl_lock); list_add_tail(&lacl->lacl_list, &lun->lun_acl_list); atomic_inc(&lun->lun_acl_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&lun->lun_acl_lock); pr_debug("%s_TPG[%hu]_LUN[%u->%u] - Added %s ACL for " @@ -1426,7 +1426,7 @@ int core_dev_del_initiator_node_lun_acl( spin_lock(&lun->lun_acl_lock); list_del(&lacl->lacl_list); atomic_dec(&lun->lun_acl_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); spin_unlock(&lun->lun_acl_lock); core_disable_device_list_for_node(lun, NULL, lacl->mapped_lun, diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 9e0232cca92e..7e6b857c6b3f 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -323,7 +323,7 @@ static void iblock_bio_done(struct bio *bio, int err) * Bump the ib_bio_err_cnt and release bio. */ atomic_inc(&ibr->ib_bio_err_cnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } bio_put(bio); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 3013287a2aaa..df357862286e 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -675,7 +675,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( spin_lock(&dev->se_port_lock); list_for_each_entry_safe(port, port_tmp, &dev->dev_sep_list, sep_list) { atomic_inc(&port->sep_tg_pt_ref_cnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&dev->se_port_lock); spin_lock_bh(&port->sep_alua_lock); @@ -710,7 +710,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( continue; atomic_inc(&deve_tmp->pr_ref_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock_bh(&port->sep_alua_lock); /* * Grab a configfs group dependency that is released @@ -723,9 +723,9 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( pr_err("core_scsi3_lunacl_depend" "_item() failed\n"); atomic_dec(&port->sep_tg_pt_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); atomic_dec(&deve_tmp->pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); goto out; } /* @@ -740,9 +740,9 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( sa_res_key, all_tg_pt, aptpl); if (!pr_reg_atp) { atomic_dec(&port->sep_tg_pt_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); atomic_dec(&deve_tmp->pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); core_scsi3_lunacl_undepend_item(deve_tmp); goto out; } @@ -755,7 +755,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( spin_lock(&dev->se_port_lock); atomic_dec(&port->sep_tg_pt_ref_cnt); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } spin_unlock(&dev->se_port_lock); @@ -1110,7 +1110,7 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg( continue; } atomic_inc(&pr_reg->pr_res_holders); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&pr_tmpl->registration_lock); return pr_reg; } @@ -1125,7 +1125,7 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg( continue; atomic_inc(&pr_reg->pr_res_holders); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&pr_tmpl->registration_lock); return pr_reg; } @@ -1155,7 +1155,7 @@ static struct t10_pr_registration *core_scsi3_locate_pr_reg( static void core_scsi3_put_pr_reg(struct t10_pr_registration *pr_reg) { atomic_dec(&pr_reg->pr_res_holders); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } static int core_scsi3_check_implicit_release( @@ -1349,7 +1349,7 @@ static void core_scsi3_tpg_undepend_item(struct se_portal_group *tpg) &tpg->tpg_group.cg_item); atomic_dec(&tpg->tpg_pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } static int core_scsi3_nodeacl_depend_item(struct se_node_acl *nacl) @@ -1369,7 +1369,7 @@ static void core_scsi3_nodeacl_undepend_item(struct se_node_acl *nacl) if (nacl->dynamic_node_acl) { atomic_dec(&nacl->acl_pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); return; } @@ -1377,7 +1377,7 @@ static void core_scsi3_nodeacl_undepend_item(struct se_node_acl *nacl) &nacl->acl_group.cg_item); atomic_dec(&nacl->acl_pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve) @@ -1408,7 +1408,7 @@ static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve) */ if (!lun_acl) { atomic_dec(&se_deve->pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); return; } nacl = lun_acl->se_lun_nacl; @@ -1418,7 +1418,7 @@ static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve) &lun_acl->se_lun_group.cg_item); atomic_dec(&se_deve->pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } static sense_reason_t @@ -1552,14 +1552,14 @@ core_scsi3_decode_spec_i_port( continue; atomic_inc(&tmp_tpg->tpg_pr_ref_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&dev->se_port_lock); if (core_scsi3_tpg_depend_item(tmp_tpg)) { pr_err(" core_scsi3_tpg_depend_item()" " for tmp_tpg\n"); atomic_dec(&tmp_tpg->tpg_pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out_unmap; } @@ -1573,7 +1573,7 @@ core_scsi3_decode_spec_i_port( tmp_tpg, i_str); if (dest_node_acl) { atomic_inc(&dest_node_acl->acl_pr_ref_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } spin_unlock_irq(&tmp_tpg->acl_node_lock); @@ -1587,7 +1587,7 @@ core_scsi3_decode_spec_i_port( pr_err("configfs_depend_item() failed" " for dest_node_acl->acl_group\n"); atomic_dec(&dest_node_acl->acl_pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); core_scsi3_tpg_undepend_item(tmp_tpg); ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out_unmap; @@ -1647,7 +1647,7 @@ core_scsi3_decode_spec_i_port( pr_err("core_scsi3_lunacl_depend_item()" " failed\n"); atomic_dec(&dest_se_deve->pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); core_scsi3_nodeacl_undepend_item(dest_node_acl); core_scsi3_tpg_undepend_item(dest_tpg); ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -3168,14 +3168,14 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, continue; atomic_inc(&dest_se_tpg->tpg_pr_ref_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&dev->se_port_lock); if (core_scsi3_tpg_depend_item(dest_se_tpg)) { pr_err("core_scsi3_tpg_depend_item() failed" " for dest_se_tpg\n"); atomic_dec(&dest_se_tpg->tpg_pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out_put_pr_reg; } @@ -3273,7 +3273,7 @@ after_iport_check: initiator_str); if (dest_node_acl) { atomic_inc(&dest_node_acl->acl_pr_ref_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } spin_unlock_irq(&dest_se_tpg->acl_node_lock); @@ -3289,7 +3289,7 @@ after_iport_check: pr_err("core_scsi3_nodeacl_depend_item() for" " dest_node_acl\n"); atomic_dec(&dest_node_acl->acl_pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); dest_node_acl = NULL; ret = TCM_INVALID_PARAMETER_LIST; goto out; @@ -3314,7 +3314,7 @@ after_iport_check: if (core_scsi3_lunacl_depend_item(dest_se_deve)) { pr_err("core_scsi3_lunacl_depend_item() failed\n"); atomic_dec(&dest_se_deve->pr_ref_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); dest_se_deve = NULL; ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out; @@ -3880,7 +3880,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) add_desc_len = 0; atomic_inc(&pr_reg->pr_res_holders); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock(&pr_tmpl->registration_lock); /* * Determine expected length of $FABRIC_MOD specific @@ -3894,7 +3894,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) " out of buffer: %d\n", cmd->data_length); spin_lock(&pr_tmpl->registration_lock); atomic_dec(&pr_reg->pr_res_holders); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); break; } /* @@ -3956,7 +3956,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) spin_lock(&pr_tmpl->registration_lock); atomic_dec(&pr_reg->pr_res_holders); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); /* * Set the ADDITIONAL DESCRIPTOR LENGTH */ diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index d4b98690a736..4badca1cd625 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -736,7 +736,7 @@ void target_qf_do_work(struct work_struct *work) list_for_each_entry_safe(cmd, cmd_tmp, &qf_cmd_list, se_qf_node) { list_del(&cmd->se_qf_node); atomic_dec(&dev->dev_qf_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue" " context: %s\n", cmd->se_tfo->get_fabric_name(), cmd, @@ -1148,7 +1148,7 @@ transport_check_alloc_task_attr(struct se_cmd *cmd) * Dormant to Active status. */ cmd->se_ordered_id = atomic_inc_return(&dev->dev_ordered_id); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); pr_debug("Allocated se_ordered_id: %u for Task Attr: 0x%02x on %s\n", cmd->se_ordered_id, cmd->sam_task_attr, dev->transport->name); @@ -1705,7 +1705,7 @@ static bool target_handle_task_attr(struct se_cmd *cmd) return false; case MSG_ORDERED_TAG: atomic_inc(&dev->dev_ordered_sync); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); pr_debug("Added ORDERED for CDB: 0x%02x to ordered list, " " se_ordered_id: %u\n", @@ -1723,7 +1723,7 @@ static bool target_handle_task_attr(struct se_cmd *cmd) * For SIMPLE and UNTAGGED Task Attribute commands */ atomic_inc(&dev->simple_cmds); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); break; } @@ -1828,7 +1828,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd) if (cmd->sam_task_attr == MSG_SIMPLE_TAG) { atomic_dec(&dev->simple_cmds); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); dev->dev_cur_ordered_id++; pr_debug("Incremented dev->dev_cur_ordered_id: %u for" " SIMPLE: %u\n", dev->dev_cur_ordered_id, @@ -1840,7 +1840,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd) cmd->se_ordered_id); } else if (cmd->sam_task_attr == MSG_ORDERED_TAG) { atomic_dec(&dev->dev_ordered_sync); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); dev->dev_cur_ordered_id++; pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED:" @@ -1899,7 +1899,7 @@ static void transport_handle_queue_full( spin_lock_irq(&dev->qf_cmd_lock); list_add_tail(&cmd->se_qf_node, &cmd->se_dev->qf_cmd_list); atomic_inc(&dev->dev_qf_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); spin_unlock_irq(&cmd->se_dev->qf_cmd_lock); schedule_work(&cmd->se_dev->qf_work_queue); @@ -2875,7 +2875,7 @@ void transport_send_task_abort(struct se_cmd *cmd) if (cmd->se_tfo->write_pending_status(cmd) != 0) { cmd->transport_state |= CMD_T_ABORTED; cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS; - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); return; } } diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c index 505519b10cb7..101858e245b3 100644 --- a/drivers/target/target_core_ua.c +++ b/drivers/target/target_core_ua.c @@ -162,7 +162,7 @@ int core_scsi3_ua_allocate( spin_unlock_irq(&nacl->device_list_lock); atomic_inc(&deve->ua_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); return 0; } list_add_tail(&ua->ua_nacl_list, &deve->ua_list); @@ -175,7 +175,7 @@ int core_scsi3_ua_allocate( asc, ascq); atomic_inc(&deve->ua_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); return 0; } @@ -190,7 +190,7 @@ void core_scsi3_ua_release_all( kmem_cache_free(se_ua_cache, ua); atomic_dec(&deve->ua_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } spin_unlock(&deve->ua_lock); } @@ -251,7 +251,7 @@ void core_scsi3_ua_for_check_condition( kmem_cache_free(se_ua_cache, ua); atomic_dec(&deve->ua_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } spin_unlock(&deve->ua_lock); spin_unlock_irq(&nacl->device_list_lock); @@ -310,7 +310,7 @@ int core_scsi3_ua_clear_for_request_sense( kmem_cache_free(se_ua_cache, ua); atomic_dec(&deve->ua_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } spin_unlock(&deve->ua_lock); spin_unlock_irq(&nacl->device_list_lock); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 41fe8a047d37..746ae80b972f 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2041,7 +2041,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, if (found) clear_bit(eol, ldata->read_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); ldata->read_tail += c; if (found) { diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index aa97fd845b4d..4b5b3c2fe328 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -200,7 +200,7 @@ static void dma_tx_callback(void *param) /* clear the bit used to serialize the DMA tx. */ clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* wake up the possible processes. */ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -275,7 +275,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) mxs_auart_dma_tx(s, i); } else { clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } return; } diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index f058c0368d61..819875c7e394 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -1851,7 +1851,7 @@ static int usbg_port_link(struct se_portal_group *se_tpg, struct se_lun *lun) struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); atomic_inc(&tpg->tpg_port_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); return 0; } @@ -1861,7 +1861,7 @@ static void usbg_port_unlink(struct se_portal_group *se_tpg, struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg); atomic_dec(&tpg->tpg_port_count); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } static int usbg_check_stop_free(struct se_cmd *se_cmd) diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 640fe0173236..f1ec1680e822 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -325,7 +325,7 @@ static void usb_wwan_outdat_callback(struct urb *urb) for (i = 0; i < N_OUT_URB; ++i) { if (portdata->out_urbs[i] == urb) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(i, &portdata->out_busy); break; } diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index cf50ce93975b..aeb513108448 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -1255,7 +1255,7 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs, tpg->tv_tpg_vhost_count++; tpg->vhost_scsi = vs; vs_tpg[tpg->tport_tpgt] = tpg; - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); match = true; } mutex_unlock(&tpg->tv_tpg_mutex); diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index 3bff6b37b472..3651ec801f45 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c @@ -139,9 +139,9 @@ void w1_family_get(struct w1_family *f) void __w1_family_get(struct w1_family *f) { - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&f->refcnt); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } EXPORT_SYMBOL(w1_unregister_family); diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index 607e41460c0d..c4a0666de6f5 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -348,9 +348,9 @@ void xen_pcibk_do_op(struct work_struct *data) notify_remote_via_irq(pdev->evtchn_irq); /* Mark that we're done. */ - smp_mb__before_clear_bit(); /* /after/ clearing PCIF_active */ + smp_mb__before_atomic(); /* /after/ clearing PCIF_active */ clear_bit(_PDEVF_op_active, &pdev->flags); - smp_mb__after_clear_bit(); /* /before/ final check for work */ + smp_mb__after_atomic(); /* /before/ final check for work */ /* Check to see if the driver domain tried to start another request in * between clearing _XEN_PCIF_active and clearing _PDEVF_op_active. diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index c9a24444ec9a..2256e9cceec5 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -279,7 +279,7 @@ static inline void btrfs_inode_block_unlocked_dio(struct inode *inode) static inline void btrfs_inode_resume_unlocked_dio(struct inode *inode) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(BTRFS_INODE_READDIO_NEED_LOCK, &BTRFS_I(inode)->runtime_flags); } diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 3955e475ceec..f29a54e454d4 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3458,7 +3458,7 @@ static int lock_extent_buffer_for_io(struct extent_buffer *eb, static void end_extent_buffer_writeback(struct extent_buffer *eb) { clear_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK); } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5f805bc944fa..5a3b8371772e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7126,7 +7126,7 @@ static void btrfs_end_dio_bio(struct bio *bio, int err) * before atomic variable goto zero, we must make sure * dip->errors is perceived to be set. */ - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); } /* if there are more bios still pending for this dio, just exit */ @@ -7306,7 +7306,7 @@ out_err: * before atomic variable goto zero, we must * make sure dip->errors is perceived to be set. */ - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); if (atomic_dec_and_test(&dip->pending_bios)) bio_io_error(dip->orig_bio); @@ -7449,7 +7449,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, return 0; atomic_inc(&inode->i_dio_count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); /* * The generic stuff only does filemap_write_and_wait_range, which diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index e79ff6b90cb7..f45040a4bb76 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -642,7 +642,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, return -EINVAL; atomic_inc(&root->will_be_snapshoted); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); btrfs_wait_nocow_write(root); ret = btrfs_start_delalloc_inodes(root, 0); diff --git a/fs/buffer.c b/fs/buffer.c index 9ddb9fc7d923..6a8110c03a47 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -77,7 +77,7 @@ EXPORT_SYMBOL(__lock_buffer); void unlock_buffer(struct buffer_head *bh) { clear_bit_unlock(BH_Lock, &bh->b_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&bh->b_state, BH_Lock); } EXPORT_SYMBOL(unlock_buffer); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index f3b84cd9de56..08b3c116915b 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -42,7 +42,7 @@ int ext4_resize_begin(struct super_block *sb) void ext4_resize_end(struct super_block *sb) { clear_bit_unlock(EXT4_RESIZING, &EXT4_SB(sb)->s_resize_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } static ext4_group_t ext4_meta_bg_first_group(struct super_block *sb, diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index aec7f73832f0..c355f7320e44 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -277,7 +277,7 @@ static inline int may_grant(const struct gfs2_glock *gl, const struct gfs2_holde static void gfs2_holder_wake(struct gfs2_holder *gh) { clear_bit(HIF_WAIT, &gh->gh_iflags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&gh->gh_iflags, HIF_WAIT); } @@ -411,7 +411,7 @@ static void gfs2_demote_wake(struct gfs2_glock *gl) { gl->gl_demote_state = LM_ST_EXCLUSIVE; clear_bit(GLF_DEMOTE, &gl->gl_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&gl->gl_flags, GLF_DEMOTE); } @@ -620,7 +620,7 @@ out: out_sched: clear_bit(GLF_LOCK, &gl->gl_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); gl->gl_lockref.count++; if (queue_delayed_work(glock_workqueue, &gl->gl_work, 0) == 0) gl->gl_lockref.count--; @@ -628,7 +628,7 @@ out_sched: out_unlock: clear_bit(GLF_LOCK, &gl->gl_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return; } diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 54b66809e818..74d9a3dbf16f 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -221,7 +221,7 @@ static void inode_go_sync(struct gfs2_glock *gl) * Writeback of the data mapping may cause the dirty flag to be set * so we have to clear it again here. */ - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(GLF_DIRTY, &gl->gl_flags); } diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index c1eb555dc588..91f274de1246 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -1134,7 +1134,7 @@ static void gdlm_recover_done(void *arg, struct dlm_slot *slots, int num_slots, queue_delayed_work(gfs2_control_wq, &sdp->sd_control_work, 0); clear_bit(DFL_DLM_RECOVERY, &ls->ls_recover_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&ls->ls_recover_flags, DFL_DLM_RECOVERY); spin_unlock(&ls->ls_recover_spin); } @@ -1271,7 +1271,7 @@ static int gdlm_mount(struct gfs2_sbd *sdp, const char *table) ls->ls_first = !!test_bit(DFL_FIRST_MOUNT, &ls->ls_recover_flags); clear_bit(SDF_NOJOURNALID, &sdp->sd_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&sdp->sd_flags, SDF_NOJOURNALID); return 0; diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 7ad4094d68c0..fe7a56fb6084 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -587,7 +587,7 @@ fail: gfs2_recovery_done(sdp, jd->jd_jid, LM_RD_GAVEUP); done: clear_bit(JDF_RECOVERY, &jd->jd_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&jd->jd_flags, JDF_RECOVERY); } diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index de25d5577e5d..529d9a9eb897 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -333,7 +333,7 @@ static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len) set_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags); else if (val == 0) { clear_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); gfs2_glock_thaw(sdp); } else { ret = -EINVAL; @@ -482,7 +482,7 @@ static ssize_t jid_store(struct gfs2_sbd *sdp, const char *buf, size_t len) rv = jid = -EINVAL; sdp->sd_lockstruct.ls_jid = jid; clear_bit(SDF_NOJOURNALID, &sdp->sd_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&sdp->sd_flags, SDF_NOJOURNALID); out: spin_unlock(&sdp->sd_jindex_spin); diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 5f26139a165a..6fac74349856 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -43,7 +43,7 @@ static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate) clear_buffer_uptodate(bh); if (orig_bh) { clear_bit_unlock(BH_Shadow, &orig_bh->b_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&orig_bh->b_state, BH_Shadow); } unlock_buffer(bh); @@ -239,7 +239,7 @@ static int journal_submit_data_buffers(journal_t *journal, spin_lock(&journal->j_list_lock); J_ASSERT(jinode->i_transaction == commit_transaction); clear_bit(__JI_COMMIT_RUNNING, &jinode->i_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING); } spin_unlock(&journal->j_list_lock); @@ -277,7 +277,7 @@ static int journal_finish_inode_data_buffers(journal_t *journal, } spin_lock(&journal->j_list_lock); clear_bit(__JI_COMMIT_RUNNING, &jinode->i_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING); } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index d9f3d067cd15..4a3d4ef76127 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2032,9 +2032,9 @@ static void nfs_access_free_entry(struct nfs_access_entry *entry) { put_rpccred(entry->cred); kfree(entry); - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_long_dec(&nfs_access_nr_entries); - smp_mb__after_atomic_dec(); + smp_mb__after_atomic(); } static void nfs_access_free_list(struct list_head *head) @@ -2082,9 +2082,9 @@ nfs_access_cache_scan(struct shrinker *shrink, struct shrink_control *sc) else { remove_lru_entry: list_del_init(&nfsi->access_cache_inode_lru); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } spin_unlock(&inode->i_lock); } @@ -2232,9 +2232,9 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) nfs_access_add_rbtree(inode, cache); /* Update accounting */ - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_long_inc(&nfs_access_nr_entries); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); /* Add inode to global LRU list */ if (!test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) { diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 0c438973f3c8..e6f7398d2b3c 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1085,7 +1085,7 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) trace_nfs_invalidate_mapping_exit(inode, ret); clear_bit_unlock(NFS_INO_INVALIDATING, bitlock); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(bitlock, NFS_INO_INVALIDATING); out: return ret; diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index efac602edb37..b9c61efe9660 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -789,9 +789,9 @@ static void nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds) static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(NFS4DS_CONNECTING, &ds->ds_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&ds->ds_state, NFS4DS_CONNECTING); } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 2349518eef2c..c0583b9bef71 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1140,9 +1140,9 @@ static int nfs4_run_state_manager(void *); static void nfs4_clear_state_manager_bit(struct nfs_client *clp) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING); rpc_wake_up(&clp->cl_rpcwaitq); } diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 2ffebf2081ce..03ed984ab4d8 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -95,7 +95,7 @@ nfs_iocounter_dec(struct nfs_io_counter *c) { if (atomic_dec_and_test(&c->io_count)) { clear_bit(NFS_IO_INPROGRESS, &c->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&c->flags, NFS_IO_INPROGRESS); } } @@ -193,9 +193,9 @@ void nfs_unlock_request(struct nfs_page *req) printk(KERN_ERR "NFS: Invalid unlock attempted\n"); BUG(); } - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(PG_BUSY, &req->wb_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&req->wb_flags, PG_BUSY); } diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index cb53d450ae32..fd9536e494bc 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1810,7 +1810,7 @@ static void pnfs_clear_layoutcommitting(struct inode *inode) unsigned long *bitlock = &NFS_I(inode)->flags; clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING); } diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 023793909778..c3058a076596 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -275,7 +275,7 @@ pnfs_get_lseg(struct pnfs_layout_segment *lseg) { if (lseg) { atomic_inc(&lseg->pls_refcount); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } return lseg; } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 9a3b6a4cd6b9..ffb9459f180b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -405,7 +405,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) nfs_pageio_complete(&pgio); clear_bit_unlock(NFS_INO_FLUSHING, bitlock); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(bitlock, NFS_INO_FLUSHING); if (err < 0) @@ -1458,7 +1458,7 @@ static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) static void nfs_commit_clear_lock(struct nfs_inode *nfsi) { clear_bit(NFS_INO_COMMIT, &nfsi->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&nfsi->flags, NFS_INO_COMMIT); } diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c index 4b826abb1528..45d4e96a6bac 100644 --- a/fs/ubifs/lpt_commit.c +++ b/fs/ubifs/lpt_commit.c @@ -460,9 +460,9 @@ static int write_cnodes(struct ubifs_info *c) * important. */ clear_bit(DIRTY_CNODE, &cnode->flags); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(COW_CNODE, &cnode->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); offs += len; dbg_chk_lpt_sz(c, 1, len); cnode = cnode->cnext; diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c index 52a6559275c4..3600994f8411 100644 --- a/fs/ubifs/tnc_commit.c +++ b/fs/ubifs/tnc_commit.c @@ -895,9 +895,9 @@ static int write_index(struct ubifs_info *c) * the reason for the second barrier. */ clear_bit(DIRTY_ZNODE, &znode->flags); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(COW_ZNODE, &znode->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* * We have marked the znode as clean but have not updated the diff --git a/include/asm-generic/bitops/atomic.h b/include/asm-generic/bitops/atomic.h index 9ae6c34dc191..49673510b484 100644 --- a/include/asm-generic/bitops/atomic.h +++ b/include/asm-generic/bitops/atomic.h @@ -80,7 +80,7 @@ static inline void set_bit(int nr, volatile unsigned long *addr) * * clear_bit() is atomic and may not be reordered. However, it does * not contain a memory barrier, so if it is used for locking purposes, - * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() + * you should call smp_mb__before_atomic() and/or smp_mb__after_atomic() * in order to ensure changes are visible on other processors. */ static inline void clear_bit(int nr, volatile unsigned long *addr) diff --git a/include/asm-generic/bitops/lock.h b/include/asm-generic/bitops/lock.h index 308a9e22c802..c30266e94806 100644 --- a/include/asm-generic/bitops/lock.h +++ b/include/asm-generic/bitops/lock.h @@ -20,7 +20,7 @@ */ #define clear_bit_unlock(nr, addr) \ do { \ - smp_mb__before_clear_bit(); \ + smp_mb__before_atomic(); \ clear_bit(nr, addr); \ } while (0) diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index c40302f909ce..7cbf837a279c 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -278,7 +278,7 @@ static inline void get_bh(struct buffer_head *bh) static inline void put_bh(struct buffer_head *bh) { - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&bh->b_count); } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 9f3c275e053e..ec274e0f4ed2 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -649,7 +649,7 @@ static inline void hd_ref_init(struct hd_struct *part) static inline void hd_struct_get(struct hd_struct *part) { atomic_inc(&part->ref); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } static inline int hd_struct_try_get(struct hd_struct *part) diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index c7bfac1c4a7b..157111043281 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -453,7 +453,7 @@ static inline int tasklet_trylock(struct tasklet_struct *t) static inline void tasklet_unlock(struct tasklet_struct *t) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(TASKLET_STATE_RUN, &(t)->state); } @@ -501,7 +501,7 @@ static inline void tasklet_hi_schedule_first(struct tasklet_struct *t) static inline void tasklet_disable_nosync(struct tasklet_struct *t) { atomic_inc(&t->count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } static inline void tasklet_disable(struct tasklet_struct *t) @@ -513,13 +513,13 @@ static inline void tasklet_disable(struct tasklet_struct *t) static inline void tasklet_enable(struct tasklet_struct *t) { - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&t->count); } static inline void tasklet_hi_enable(struct tasklet_struct *t) { - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&t->count); } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7ed3a3aa6604..616415a4fee4 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -493,7 +493,7 @@ static inline void napi_disable(struct napi_struct *n) static inline void napi_enable(struct napi_struct *n) { BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state)); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(NAPI_STATE_SCHED, &n->state); } diff --git a/include/linux/sched.h b/include/linux/sched.h index 25f54c79f757..010cde3b44cb 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2782,10 +2782,8 @@ static inline bool __must_check current_set_polling_and_test(void) /* * Polling state must be visible before we test NEED_RESCHED, * paired by resched_task() - * - * XXX: assumes set/clear bit are identical barrier wise. */ - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return unlikely(tif_need_resched()); } @@ -2803,7 +2801,7 @@ static inline bool __must_check current_clr_polling_and_test(void) * Polling state must be visible before we test NEED_RESCHED, * paired by resched_task() */ - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return unlikely(tif_need_resched()); } diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 3a847de83fab..ad7dbe2cfecd 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -142,18 +142,18 @@ struct rpc_task_setup { test_and_set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate) #define rpc_clear_running(t) \ do { \ - smp_mb__before_clear_bit(); \ + smp_mb__before_atomic(); \ clear_bit(RPC_TASK_RUNNING, &(t)->tk_runstate); \ - smp_mb__after_clear_bit(); \ + smp_mb__after_atomic(); \ } while (0) #define RPC_IS_QUEUED(t) test_bit(RPC_TASK_QUEUED, &(t)->tk_runstate) #define rpc_set_queued(t) set_bit(RPC_TASK_QUEUED, &(t)->tk_runstate) #define rpc_clear_queued(t) \ do { \ - smp_mb__before_clear_bit(); \ + smp_mb__before_atomic(); \ clear_bit(RPC_TASK_QUEUED, &(t)->tk_runstate); \ - smp_mb__after_clear_bit(); \ + smp_mb__after_atomic(); \ } while (0) #define RPC_IS_ACTIVATED(t) test_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 3e5efb2b236e..3876f0f1dfd3 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -379,9 +379,9 @@ static inline int xprt_test_and_clear_connected(struct rpc_xprt *xprt) static inline void xprt_clear_connecting(struct rpc_xprt *xprt) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(XPRT_CONNECTING, &xprt->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } static inline int xprt_connecting(struct rpc_xprt *xprt) @@ -411,9 +411,9 @@ static inline void xprt_clear_bound(struct rpc_xprt *xprt) static inline void xprt_clear_binding(struct rpc_xprt *xprt) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(XPRT_BINDING, &xprt->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } static inline int xprt_test_and_set_binding(struct rpc_xprt *xprt) diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 1e98b5530425..6f8ab7da27c4 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -191,7 +191,7 @@ static inline void tracehook_notify_resume(struct pt_regs *regs) * pairs with task_work_add()->set_notify_resume() after * hlist_add_head(task->task_works); */ - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (unlikely(current->task_works)) task_work_run(); } diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 5679d927562b..624a8a54806d 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1204,7 +1204,7 @@ static inline bool __ip_vs_conn_get(struct ip_vs_conn *cp) /* put back the conn without restarting its timer */ static inline void __ip_vs_conn_put(struct ip_vs_conn *cp) { - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&cp->refcnt); } void ip_vs_conn_put(struct ip_vs_conn *cp); @@ -1408,7 +1408,7 @@ static inline void ip_vs_dest_hold(struct ip_vs_dest *dest) static inline void ip_vs_dest_put(struct ip_vs_dest *dest) { - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&dest->refcnt); } diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 2956c8da1605..1adf62b39b96 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -534,7 +534,7 @@ return_normal: kgdb_info[cpu].exception_state &= ~(DCPU_WANT_MASTER | DCPU_IS_SLAVE); kgdb_info[cpu].enter_kgdb--; - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&slaves_in_kgdb); dbg_touch_watchdogs(); local_irq_restore(flags); @@ -662,7 +662,7 @@ kgdb_restore: kgdb_info[cpu].exception_state &= ~(DCPU_WANT_MASTER | DCPU_IS_SLAVE); kgdb_info[cpu].enter_kgdb--; - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&masters_in_kgdb); /* Free kgdb_active */ atomic_set(&kgdb_active, -1); diff --git a/kernel/futex.c b/kernel/futex.c index 5f589279e462..b991ec05b8f9 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -267,7 +267,7 @@ static inline void futex_get_mm(union futex_key *key) * get_futex_key() implies a full barrier. This is relied upon * as full barrier (B), see the ordering comment above. */ - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } /* @@ -280,7 +280,7 @@ static inline void hb_waiters_inc(struct futex_hash_bucket *hb) /* * Full barrier (A), see the ordering comment above. */ - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); #endif } diff --git a/kernel/kmod.c b/kernel/kmod.c index 6b375af4958d..0ac67a5861c5 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -498,7 +498,7 @@ int __usermodehelper_disable(enum umh_disable_depth depth) static void helper_lock(void) { atomic_inc(&running_helpers); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); } static void helper_unlock(void) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 0c47e300210a..88b4a1dcb58c 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -387,9 +387,9 @@ static void rcu_eqs_enter_common(struct rcu_dynticks *rdtp, long long oldval, } rcu_prepare_for_idle(smp_processor_id()); /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ - smp_mb__before_atomic_inc(); /* See above. */ + smp_mb__before_atomic(); /* See above. */ atomic_inc(&rdtp->dynticks); - smp_mb__after_atomic_inc(); /* Force ordering with next sojourn. */ + smp_mb__after_atomic(); /* Force ordering with next sojourn. */ WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); /* @@ -507,10 +507,10 @@ void rcu_irq_exit(void) static void rcu_eqs_exit_common(struct rcu_dynticks *rdtp, long long oldval, int user) { - smp_mb__before_atomic_inc(); /* Force ordering w/previous sojourn. */ + smp_mb__before_atomic(); /* Force ordering w/previous sojourn. */ atomic_inc(&rdtp->dynticks); /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ - smp_mb__after_atomic_inc(); /* See above. */ + smp_mb__after_atomic(); /* See above. */ WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); rcu_cleanup_after_idle(smp_processor_id()); trace_rcu_dyntick(TPS("End"), oldval, rdtp->dynticks_nesting); @@ -635,10 +635,10 @@ void rcu_nmi_enter(void) (atomic_read(&rdtp->dynticks) & 0x1)) return; rdtp->dynticks_nmi_nesting++; - smp_mb__before_atomic_inc(); /* Force delay from prior write. */ + smp_mb__before_atomic(); /* Force delay from prior write. */ atomic_inc(&rdtp->dynticks); /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ - smp_mb__after_atomic_inc(); /* See above. */ + smp_mb__after_atomic(); /* See above. */ WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1)); } @@ -657,9 +657,9 @@ void rcu_nmi_exit(void) --rdtp->dynticks_nmi_nesting != 0) return; /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ - smp_mb__before_atomic_inc(); /* See above. */ + smp_mb__before_atomic(); /* See above. */ atomic_inc(&rdtp->dynticks); - smp_mb__after_atomic_inc(); /* Force delay to next write. */ + smp_mb__after_atomic(); /* Force delay to next write. */ WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); } @@ -2790,7 +2790,7 @@ void synchronize_sched_expedited(void) s = atomic_long_read(&rsp->expedited_done); if (ULONG_CMP_GE((ulong)s, (ulong)firstsnap)) { /* ensure test happens before caller kfree */ - smp_mb__before_atomic_inc(); /* ^^^ */ + smp_mb__before_atomic(); /* ^^^ */ atomic_long_inc(&rsp->expedited_workdone1); return; } @@ -2808,7 +2808,7 @@ void synchronize_sched_expedited(void) s = atomic_long_read(&rsp->expedited_done); if (ULONG_CMP_GE((ulong)s, (ulong)firstsnap)) { /* ensure test happens before caller kfree */ - smp_mb__before_atomic_inc(); /* ^^^ */ + smp_mb__before_atomic(); /* ^^^ */ atomic_long_inc(&rsp->expedited_workdone2); return; } @@ -2837,7 +2837,7 @@ void synchronize_sched_expedited(void) s = atomic_long_read(&rsp->expedited_done); if (ULONG_CMP_GE((ulong)s, (ulong)snap)) { /* ensure test happens before caller kfree */ - smp_mb__before_atomic_inc(); /* ^^^ */ + smp_mb__before_atomic(); /* ^^^ */ atomic_long_inc(&rsp->expedited_done_lost); break; } diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 962d1d589929..56db2f853e43 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -2523,9 +2523,9 @@ static void rcu_sysidle_enter(struct rcu_dynticks *rdtp, int irq) /* Record start of fully idle period. */ j = jiffies; ACCESS_ONCE(rdtp->dynticks_idle_jiffies) = j; - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&rdtp->dynticks_idle); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); WARN_ON_ONCE(atomic_read(&rdtp->dynticks_idle) & 0x1); } @@ -2590,9 +2590,9 @@ static void rcu_sysidle_exit(struct rcu_dynticks *rdtp, int irq) } /* Record end of idle period. */ - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&rdtp->dynticks_idle); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks_idle) & 0x1)); /* diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c index 8b836b376d91..746bc9344969 100644 --- a/kernel/sched/cpupri.c +++ b/kernel/sched/cpupri.c @@ -165,7 +165,7 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri) * do a write memory barrier, and then update the count, to * make sure the vector is visible when count is set. */ - smp_mb__before_atomic_inc(); + smp_mb__before_atomic(); atomic_inc(&(vec)->count); do_mb = 1; } @@ -185,14 +185,14 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri) * the new priority vec. */ if (do_mb) - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); /* * When removing from the vector, we decrement the counter first * do a memory barrier and then clear the mask. */ atomic_dec(&(vec)->count); - smp_mb__after_atomic_inc(); + smp_mb__after_atomic(); cpumask_clear_cpu(cpu, vec->mask); } diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index 7d50f794e248..0ffa20ae657b 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -394,7 +394,7 @@ EXPORT_SYMBOL(__wake_up_bit); * * In order for this to function properly, as it uses waitqueue_active() * internally, some kind of memory barrier must be done prior to calling - * this. Typically, this will be smp_mb__after_clear_bit(), but in some + * this. Typically, this will be smp_mb__after_atomic(), but in some * cases where bitflags are manipulated non-atomically under a lock, one * may need to use a less regular barrier, such fs/inode.c's smp_mb(), * because spin_unlock() does not guarantee a memory barrier. diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 09d9591b7708..1706cbbdf5f0 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -557,7 +557,7 @@ void clear_bdi_congested(struct backing_dev_info *bdi, int sync) bit = sync ? BDI_sync_congested : BDI_async_congested; if (test_and_clear_bit(bit, &bdi->state)) atomic_dec(&nr_bdi_congested[sync]); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (waitqueue_active(wqh)) wake_up(wqh); } diff --git a/mm/filemap.c b/mm/filemap.c index a82fbe4c9e8e..c73535c914cc 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -740,7 +740,7 @@ void unlock_page(struct page *page) { VM_BUG_ON_PAGE(!PageLocked(page), page); clear_bit_unlock(PG_locked, &page->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_page(page, PG_locked); } EXPORT_SYMBOL(unlock_page); @@ -757,7 +757,7 @@ void end_page_writeback(struct page *page) if (!test_clear_page_writeback(page)) BUG(); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_page(page, PG_writeback); } EXPORT_SYMBOL(end_page_writeback); diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c index 8c93267ce969..c4e09846d1de 100644 --- a/net/atm/pppoatm.c +++ b/net/atm/pppoatm.c @@ -252,7 +252,7 @@ static int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size) * we need to ensure there's a memory barrier after it. The bit * *must* be set before we do the atomic_inc() on pvcc->inflight. * There's no smp_mb__after_set_bit(), so it's this or abuse - * smp_mb__after_clear_bit(). + * smp_mb__after_atomic(). */ test_and_set_bit(BLOCKED, &pvcc->blocked); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 49774912cb01..74014420b3c7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -45,7 +45,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) return; clear_bit(HCI_INQUIRY, &hdev->flags); - smp_mb__after_clear_bit(); /* wake_up_bit advises about this barrier */ + smp_mb__after_atomic(); /* wake_up_bit advises about this barrier */ wake_up_bit(&hdev->flags, HCI_INQUIRY); hci_conn_check_pending(hdev); @@ -1768,7 +1768,7 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) return; - smp_mb__after_clear_bit(); /* wake_up_bit advises about this barrier */ + smp_mb__after_atomic(); /* wake_up_bit advises about this barrier */ wake_up_bit(&hdev->flags, HCI_INQUIRY); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) diff --git a/net/core/dev.c b/net/core/dev.c index 5b3042e69f85..e14f1cba591a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1326,7 +1326,7 @@ static int __dev_close_many(struct list_head *head) * dev->stop() will invoke napi_disable() on all of it's * napi_struct instances on this device. */ - smp_mb__after_clear_bit(); /* Commit netif_running(). */ + smp_mb__after_atomic(); /* Commit netif_running(). */ } dev_deactivate_many(head); @@ -3343,7 +3343,7 @@ static void net_tx_action(struct softirq_action *h) root_lock = qdisc_lock(q); if (spin_trylock(root_lock)) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__QDISC_STATE_SCHED, &q->state); qdisc_run(q); @@ -3353,7 +3353,7 @@ static void net_tx_action(struct softirq_action *h) &q->state)) { __netif_reschedule(q); } else { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(__QDISC_STATE_SCHED, &q->state); } @@ -4244,7 +4244,7 @@ void __napi_complete(struct napi_struct *n) BUG_ON(n->gro_list); list_del(&n->poll_list); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(NAPI_STATE_SCHED, &n->state); } EXPORT_SYMBOL(__napi_complete); diff --git a/net/core/link_watch.c b/net/core/link_watch.c index 9c3a839322ba..bd0767e6b2b3 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -147,7 +147,7 @@ static void linkwatch_do_dev(struct net_device *dev) * Make sure the above read is complete since it can be * rewritten as soon as we clear the bit below. */ - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); /* We are about to handle this device, * so new events can be accepted diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 48f424465112..56cd458a1b8c 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -522,7 +522,7 @@ EXPORT_SYMBOL_GPL(inet_getpeer); void inet_putpeer(struct inet_peer *p) { p->dtime = (__u32)jiffies; - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&p->refcnt); } EXPORT_SYMBOL_GPL(inet_putpeer); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 025e25093984..366cf06587b8 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1930,10 +1930,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, /* It is possible TX completion already happened * before we set TSQ_THROTTLED, so we must * test again the condition. - * We abuse smp_mb__after_clear_bit() because - * there is no smp_mb__after_set_bit() yet */ - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (atomic_read(&sk->sk_wmem_alloc) > limit) break; } diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 75421f2ba8be..1f4f954c4b47 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -914,7 +914,7 @@ void nf_conntrack_free(struct nf_conn *ct) nf_ct_ext_destroy(ct); nf_ct_ext_free(ct); kmem_cache_free(net->ct.nf_conntrack_cachep, ct); - smp_mb__before_atomic_dec(); + smp_mb__before_atomic(); atomic_dec(&net->ct.count); } EXPORT_SYMBOL_GPL(nf_conntrack_free); diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c index b7ebe23cdedf..d67de453c35a 100644 --- a/net/rds/ib_recv.c +++ b/net/rds/ib_recv.c @@ -598,7 +598,7 @@ static void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, { atomic64_set(&ic->i_ack_next, seq); if (ack_required) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(IB_ACK_REQUESTED, &ic->i_ack_flags); } } @@ -606,7 +606,7 @@ static void rds_ib_set_ack(struct rds_ib_connection *ic, u64 seq, static u64 rds_ib_get_ack(struct rds_ib_connection *ic) { clear_bit(IB_ACK_REQUESTED, &ic->i_ack_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return atomic64_read(&ic->i_ack_next); } diff --git a/net/rds/iw_recv.c b/net/rds/iw_recv.c index 45033358358e..aa8bf6786008 100644 --- a/net/rds/iw_recv.c +++ b/net/rds/iw_recv.c @@ -429,7 +429,7 @@ static void rds_iw_set_ack(struct rds_iw_connection *ic, u64 seq, { atomic64_set(&ic->i_ack_next, seq); if (ack_required) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(IB_ACK_REQUESTED, &ic->i_ack_flags); } } @@ -437,7 +437,7 @@ static void rds_iw_set_ack(struct rds_iw_connection *ic, u64 seq, static u64 rds_iw_get_ack(struct rds_iw_connection *ic) { clear_bit(IB_ACK_REQUESTED, &ic->i_ack_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return atomic64_read(&ic->i_ack_next); } diff --git a/net/rds/send.c b/net/rds/send.c index a82fb660ec00..23718160d71e 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -107,7 +107,7 @@ static int acquire_in_xmit(struct rds_connection *conn) static void release_in_xmit(struct rds_connection *conn) { clear_bit(RDS_IN_XMIT, &conn->c_flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* * We don't use wait_on_bit()/wake_up_bit() because our waking is in a * hot path and finding waiters is very rare. We don't want to walk @@ -661,7 +661,7 @@ void rds_send_drop_acked(struct rds_connection *conn, u64 ack, /* order flag updates with spin locks */ if (!list_empty(&list)) - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); spin_unlock_irqrestore(&conn->c_lock, flags); @@ -691,7 +691,7 @@ void rds_send_drop_to(struct rds_sock *rs, struct sockaddr_in *dest) } /* order flag updates with the rs lock */ - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); spin_unlock_irqrestore(&rs->rs_lock, flags); diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c index 81cf5a4c5e40..53b17ca0dff5 100644 --- a/net/rds/tcp_send.c +++ b/net/rds/tcp_send.c @@ -93,7 +93,7 @@ int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm, rm->m_ack_seq = tc->t_last_sent_nxt + sizeof(struct rds_header) + be32_to_cpu(rm->m_inc.i_hdr.h_len) - 1; - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); set_bit(RDS_MSG_HAS_ACK_SEQ, &rm->m_flags); tc->t_last_expected_una = rm->m_ack_seq + 1; diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 5285ead196c0..247e973544bf 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -296,7 +296,7 @@ static void rpcauth_unhash_cred_locked(struct rpc_cred *cred) { hlist_del_rcu(&cred->cr_hash); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); } diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 36e431ee1c90..b6e440baccc3 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -143,7 +143,7 @@ gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) gss_get_ctx(ctx); rcu_assign_pointer(gss_cred->gc_ctx, ctx); set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags); } diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 3513d559bc45..9761a0da964d 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -244,10 +244,10 @@ void xprt_free_bc_request(struct rpc_rqst *req) dprintk("RPC: free backchannel req=%p\n", req); req->rq_connect_cookie = xprt->connect_cookie - 1; - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); WARN_ON_ONCE(!test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state)); clear_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); if (!xprt_need_to_requeue(xprt)) { /* diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index d173f79947c6..89d051de6b3e 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -230,9 +230,9 @@ static void xprt_clear_locked(struct rpc_xprt *xprt) { xprt->snd_task = NULL; if (!test_bit(XPRT_CLOSE_WAIT, &xprt->state)) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(XPRT_LOCKED, &xprt->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } else queue_work(rpciod_workqueue, &xprt->task_cleanup); } diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 25a3dcf15cae..402a7e9a16b7 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -893,11 +893,11 @@ static void xs_close(struct rpc_xprt *xprt) xs_reset_transport(transport); xprt->reestablish_timeout = 0; - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); clear_bit(XPRT_CLOSE_WAIT, &xprt->state); clear_bit(XPRT_CLOSING, &xprt->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); xprt_disconnect_done(xprt); } @@ -1497,12 +1497,12 @@ static void xs_tcp_cancel_linger_timeout(struct rpc_xprt *xprt) static void xs_sock_reset_connection_flags(struct rpc_xprt *xprt) { - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(XPRT_CONNECTION_ABORT, &xprt->state); clear_bit(XPRT_CONNECTION_CLOSE, &xprt->state); clear_bit(XPRT_CLOSE_WAIT, &xprt->state); clear_bit(XPRT_CLOSING, &xprt->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } static void xs_sock_mark_closed(struct rpc_xprt *xprt) @@ -1556,10 +1556,10 @@ static void xs_tcp_state_change(struct sock *sk) xprt->connect_cookie++; xprt->reestablish_timeout = 0; set_bit(XPRT_CLOSING, &xprt->state); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(XPRT_CONNECTED, &xprt->state); clear_bit(XPRT_CLOSE_WAIT, &xprt->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); xs_tcp_schedule_linger_timeout(xprt, xs_tcp_fin_timeout); break; case TCP_CLOSE_WAIT: @@ -1578,9 +1578,9 @@ static void xs_tcp_state_change(struct sock *sk) case TCP_LAST_ACK: set_bit(XPRT_CLOSING, &xprt->state); xs_tcp_schedule_linger_timeout(xprt, xs_tcp_fin_timeout); - smp_mb__before_clear_bit(); + smp_mb__before_atomic(); clear_bit(XPRT_CONNECTED, &xprt->state); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); break; case TCP_CLOSE: xs_tcp_cancel_linger_timeout(xprt); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index bb7e8ba821f4..749f80c21e22 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1207,7 +1207,7 @@ restart: sk->sk_state = TCP_ESTABLISHED; sock_hold(newsk); - smp_mb__after_atomic_inc(); /* sock_hold() does an atomic_inc() */ + smp_mb__after_atomic(); /* sock_hold() does an atomic_inc() */ unix_peer(sk) = newsk; unix_state_unlock(sk); diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 8546711d12f9..70951fd9b354 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -443,7 +443,7 @@ static int snd_bt87x_pcm_open(struct snd_pcm_substream *substream) _error: clear_bit(0, &chip->opened); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return err; } @@ -458,7 +458,7 @@ static int snd_bt87x_close(struct snd_pcm_substream *substream) chip->substream = NULL; clear_bit(0, &chip->opened); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); return 0; } -- cgit v1.2.3 From 073a77d03ee88ae3a5504b3f73632841a55d60a1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 15 Apr 2014 19:47:38 +0800 Subject: regulator: tps65217: Remove *rdev[] from struct tps65217 Now this driver uses devm_regulator_register() so we don't need to save rdev pointer to tps->rdev[i] for cleanup. Signed-off-by: Axel Lin Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 3 --- include/linux/mfd/tps65217.h | 1 - 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 10b78d2b766a..8482f6ba08a1 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -257,9 +257,6 @@ static int tps65217_regulator_probe(struct platform_device *pdev) pdev->name); return PTR_ERR(rdev); } - - /* Save regulator for cleanup */ - tps->rdev[i] = rdev; } return 0; } diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index 54b5458ec084..95d6938737fd 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -254,7 +254,6 @@ struct tps65217 { struct tps65217_board *pdata; unsigned long id; struct regulator_desc desc[TPS65217_NUM_REGULATOR]; - struct regulator_dev *rdev[TPS65217_NUM_REGULATOR]; struct regmap *regmap; }; -- cgit v1.2.3 From 290414499cf94284a97cc3c33214d13ccfcd896a Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 16 Apr 2014 16:12:28 -0700 Subject: regulator: tps65090: Allow setting the overcurrent wait time The tps65090 regulator allows you to specify how long you want it to wait before detecting an overcurrent condition. Allow specifying that through the device tree (or through platform data). Signed-off-by: Doug Anderson Signed-off-by: Simon Glass Signed-off-by: Michael Spang Signed-off-by: Sean Paul Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/tps65090.txt | 4 ++ drivers/regulator/tps65090-regulator.c | 56 ++++++++++++++++++++++ include/linux/mfd/tps65090.h | 5 ++ 3 files changed, 65 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/tps65090.txt b/Documentation/devicetree/bindings/regulator/tps65090.txt index 313a60ba61d8..340980239ea9 100644 --- a/Documentation/devicetree/bindings/regulator/tps65090.txt +++ b/Documentation/devicetree/bindings/regulator/tps65090.txt @@ -21,6 +21,10 @@ Optional properties: number should be provided. If it is externally controlled and no GPIO entry then driver will just configure this rails as external control and will not provide any enable/disable APIs. +- ti,overcurrent-wait: This is applicable to FET registers, which have a + poorly defined "overcurrent wait" field. If this property is present it + should be between 0 - 3. If this property isn't present we won't touch the + "overcurrent wait" field and we'll leave it to the BIOS/EC to deal with. Each regulator is defined using the standard binding for regulators. diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 2e92ef68574d..ca04e9f010e1 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -28,15 +28,58 @@ #include #include +#define CTRL_WT_BIT 2 /* Regulator wait time 0 bit */ + +#define MAX_OVERCURRENT_WAIT 3 /* Overcurrent wait must be <= this */ + +/** + * struct tps65090_regulator - Per-regulator data for a tps65090 regulator + * + * @dev: Pointer to our device. + * @desc: The struct regulator_desc for the regulator. + * @rdev: The struct regulator_dev for the regulator. + * @overcurrent_wait_valid: True if overcurrent_wait is valid. + * @overcurrent_wait: For FETs, the value to put in the WTFET bitfield. + */ + struct tps65090_regulator { struct device *dev; struct regulator_desc *desc; struct regulator_dev *rdev; + bool overcurrent_wait_valid; + int overcurrent_wait; }; static struct regulator_ops tps65090_ext_control_ops = { }; +/** + * tps65090_reg_set_overcurrent_wait - Setup overcurrent wait + * + * This will set the overcurrent wait time based on what's in the regulator + * info. + * + * @ri: Overall regulator data + * @rdev: Regulator device + * + * Return: 0 if no error, non-zero if there was an error writing the register. + */ +static int tps65090_reg_set_overcurrent_wait(struct tps65090_regulator *ri, + struct regulator_dev *rdev) +{ + int ret; + + ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + MAX_OVERCURRENT_WAIT << CTRL_WT_BIT, + ri->overcurrent_wait << CTRL_WT_BIT); + if (ret) { + dev_err(&rdev->dev, "Error updating overcurrent wait %#x\n", + rdev->desc->enable_reg); + } + + return ret; +} + static struct regulator_ops tps65090_reg_contol_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -209,6 +252,11 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( rpdata->gpio = of_get_named_gpio(np, "dcdc-ext-control-gpios", 0); + if (of_property_read_u32(tps65090_matches[idx].of_node, + "ti,overcurrent-wait", + &rpdata->overcurrent_wait) == 0) + rpdata->overcurrent_wait_valid = true; + tps65090_pdata->reg_pdata[idx] = rpdata; } return tps65090_pdata; @@ -258,6 +306,8 @@ static int tps65090_regulator_probe(struct platform_device *pdev) ri = &pmic[num]; ri->dev = &pdev->dev; ri->desc = &tps65090_regulator_desc[num]; + ri->overcurrent_wait_valid = tps_pdata->overcurrent_wait_valid; + ri->overcurrent_wait = tps_pdata->overcurrent_wait; /* * TPS5090 DCDC support the control from external digital input. @@ -299,6 +349,12 @@ static int tps65090_regulator_probe(struct platform_device *pdev) } ri->rdev = rdev; + if (ri->overcurrent_wait_valid) { + ret = tps65090_reg_set_overcurrent_wait(ri, rdev); + if (ret < 0) + return ret; + } + /* Enable external control if it is require */ if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data && tps_pdata->enable_ext_control) { diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 3f43069413e7..f25adfa97c73 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -78,11 +78,16 @@ struct tps65090 { * DCDC1, DCDC2 and DCDC3. * @gpio: Gpio number if external control is enabled and controlled through * gpio. + * @overcurrent_wait_valid: True if the overcurrent_wait should be applied. + * @overcurrent_wait: Value to set as the overcurrent wait time. This is the + * actual bitfield value, not a time in ms (valid value are 0 - 3). */ struct tps65090_regulator_plat_data { struct regulator_init_data *reg_init_data; bool enable_ext_control; int gpio; + bool overcurrent_wait_valid; + int overcurrent_wait; }; struct tps65090_platform_data { -- cgit v1.2.3 From 3ac170376f2c5123414e0267aa0f9cf218965e24 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 17 Apr 2014 11:40:11 +0200 Subject: regmap: add reg_read/reg_write callbacks to regmap_bus struct Some busses do not support sending/receiving multiple registers in one go. Such kind of busses just unpack the registers that have been previously packed by the regmap core or pack registers that will be later unpacked by the core code. Add reg_write and reg_read callbacks in order to optimize access through this kind of busses. Signed-off-by: Boris BREZILLON Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 26 ++++++++++++++++++++++++++ include/linux/regmap.h | 6 ++++++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 63e30ef096e2..2209de0ceabc 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -35,10 +35,14 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); +static int _regmap_bus_reg_read(void *context, unsigned int reg, + unsigned int *val); static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val); static int _regmap_bus_formatted_write(void *context, unsigned int reg, unsigned int val); +static int _regmap_bus_reg_write(void *context, unsigned int reg, + unsigned int val); static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); @@ -493,6 +497,12 @@ struct regmap *regmap_init(struct device *dev, map->reg_read = config->reg_read; map->reg_write = config->reg_write; + map->defer_caching = false; + goto skip_format_initialization; + } else if (!bus->read || !bus->write) { + map->reg_read = _regmap_bus_reg_read; + map->reg_write = _regmap_bus_reg_write; + map->defer_caching = false; goto skip_format_initialization; } else { @@ -1284,6 +1294,14 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, return ret; } +static int _regmap_bus_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct regmap *map = context; + + return map->bus->reg_write(map->bus_context, reg, val); +} + static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val) { @@ -1925,6 +1943,14 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, return ret; } +static int _regmap_bus_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct regmap *map = context; + + return map->bus->reg_read(map->bus_context, reg, val); +} + static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val) { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 85691b9b4fa7..7b0e4b425cdf 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -276,6 +276,10 @@ typedef int (*regmap_hw_async_write)(void *context, typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); +typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg, + unsigned int *val); +typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg, + unsigned int val); typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef void (*regmap_hw_free_context)(void *context); @@ -309,7 +313,9 @@ struct regmap_bus { regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_async_write async_write; + regmap_hw_reg_write reg_write; regmap_hw_read read; + regmap_hw_reg_read reg_read; regmap_hw_free_context free_context; regmap_hw_async_alloc async_alloc; u8 read_flag_mask; -- cgit v1.2.3 From 8931bf6208776292b1b888dd8534229f63e2eaa2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Apr 2014 15:46:11 +0300 Subject: ASoC: Add resource managed snd_soc_register_platform() Simplify error handling and remove repetitive (and rarely executed) code for unregistration by providing a devm_snd_soc_register_platform() platform. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-devres.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168d8ff4..34c34d6e095c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -377,6 +377,8 @@ int snd_soc_resume(struct device *dev); int snd_soc_poweroff(struct device *dev); int snd_soc_register_platform(struct device *dev, const struct snd_soc_platform_driver *platform_drv); +int devm_snd_soc_register_platform(struct device *dev, + const struct snd_soc_platform_driver *platform_drv); void snd_soc_unregister_platform(struct device *dev); int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, const struct snd_soc_platform_driver *platform_drv); diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c index 7ac745df1412..e94aa0277250 100644 --- a/sound/soc/soc-devres.c +++ b/sound/soc/soc-devres.c @@ -52,6 +52,40 @@ int devm_snd_soc_register_component(struct device *dev, } EXPORT_SYMBOL_GPL(devm_snd_soc_register_component); +static void devm_platform_release(struct device *dev, void *res) +{ + snd_soc_unregister_platform(*(struct device **)res); +} + +/** + * devm_snd_soc_register_platform - resource managed platform registration + * @dev: Device used to manage platform + * @platform: platform to register + * + * Register a platform driver with automatic unregistration when the device is + * unregistered. + */ +int devm_snd_soc_register_platform(struct device *dev, + const struct snd_soc_platform_driver *platform_drv) +{ + struct device **ptr; + int ret; + + ptr = devres_alloc(devm_platform_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = snd_soc_register_platform(dev, platform_drv); + if (ret == 0) { + *ptr = dev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} + static void devm_card_release(struct device *dev, void *res) { snd_soc_unregister_card(*(struct snd_soc_card **)res); -- cgit v1.2.3 From e4fcb1d6148284a10c314fce2a488cf19ce886f6 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Apr 2014 10:01:37 +0100 Subject: mfd: arizona: Factor out read of device tree GPIOs This patch factors out the reading of GPIOs for the Arizona devices into a helper function. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 31 ++++++++++++++++++++++--------- include/linux/mfd/arizona/core.h | 3 +++ 2 files changed, 25 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 1c3ae57082ed..37b5e1447d02 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -508,19 +508,32 @@ int arizona_of_get_type(struct device *dev) } EXPORT_SYMBOL_GPL(arizona_of_get_type); +int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, + bool mandatory) +{ + int gpio; + + gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0); + if (gpio < 0) { + if (mandatory) + dev_err(arizona->dev, + "Mandatory DT gpio %s missing/malformed: %d\n", + prop, gpio); + + gpio = 0; + } + + return gpio; +} +EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio); + static int arizona_of_get_core_pdata(struct arizona *arizona) { + struct arizona_pdata *pdata = &arizona->pdata; int ret, i; - arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node, - "wlf,reset", 0); - if (arizona->pdata.reset < 0) - arizona->pdata.reset = 0; - - arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node, - "wlf,ldoena", 0); - if (arizona->pdata.ldoena < 0) - arizona->pdata.ldoena = 0; + pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true); + pdata->ldoena = arizona_of_get_named_gpio(arizona, "wlf,ldoena", true); ret = of_property_read_u32_array(arizona->dev->of_node, "wlf,gpio-defaults", diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 5cf8b91ce996..6d9371f88875 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -124,4 +124,7 @@ int wm5102_patch(struct arizona *arizona); int wm5110_patch(struct arizona *arizona); int wm8997_patch(struct arizona *arizona); +extern int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, + bool mandatory); + #endif -- cgit v1.2.3 From 40bc18a2a2677150840eff7fa77835b07da214dd Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 16 Apr 2014 19:20:46 +0800 Subject: ASoC: add RT5651 CODEC driver This patch adds the Realtek ALC5651 codec driver. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- include/sound/rt5651.h | 21 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5651.c | 1898 +++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5651.h | 2081 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 4006 insertions(+) create mode 100644 include/sound/rt5651.h create mode 100644 sound/soc/codecs/rt5651.c create mode 100644 sound/soc/codecs/rt5651.h (limited to 'include') diff --git a/include/sound/rt5651.h b/include/sound/rt5651.h new file mode 100644 index 000000000000..d35de758dfb5 --- /dev/null +++ b/include/sound/rt5651.h @@ -0,0 +1,21 @@ +/* + * linux/sound/rt286.h -- Platform data for RT286 + * + * Copyright 2013 Realtek Microelectronics + * + * 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_SND_RT5651_H +#define __LINUX_SND_RT5651_H + +struct rt5651_platform_data { + /* IN2 can optionally be differential */ + bool in2_diff; + + bool dmic_en; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f0e840137887..deba71bad6e9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -71,6 +71,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C + select SND_SOC_RT5651 if I2C select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SIRF_AUDIO_CODEC @@ -396,6 +397,9 @@ config SND_SOC_RT5631 config SND_SOC_RT5640 tristate +config SND_SOC_RT5651 + tristate + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3c4d275d064b..e76938ee82f4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -60,6 +60,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o +snd-soc-rt5651-objs := rt5651.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -211,6 +212,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o +obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c new file mode 100644 index 000000000000..a1577369c49c --- /dev/null +++ b/sound/soc/codecs/rt5651.c @@ -0,0 +1,1898 @@ +/* + * rt5651.c -- RT5651 ALSA SoC audio codec driver + * + * Copyright 2014 Realtek Semiconductor Corp. + * Author: Bard Liao + * + * 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 +#include +#include +#include +#include +#include + +#include "rt5651.h" + +#define RT5651_DEVICE_ID_VALUE 0x6281 + +#define RT5651_PR_RANGE_BASE (0xff + 1) +#define RT5651_PR_SPACING 0x100 + +#define RT5651_PR_BASE (RT5651_PR_RANGE_BASE + (0 * RT5651_PR_SPACING)) + +static const struct regmap_range_cfg rt5651_ranges[] = { + { .name = "PR", .range_min = RT5651_PR_BASE, + .range_max = RT5651_PR_BASE + 0xb4, + .selector_reg = RT5651_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5651_PRIV_DATA, + .window_len = 0x1, }, +}; + +static struct reg_default init_list[] = { + {RT5651_PR_BASE + 0x3d, 0x3e00}, +}; + +static const struct reg_default rt5651_reg[] = { + { 0x00, 0x0000 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x05, 0x0000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x10, 0x0808 }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0c00 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x27, 0x7860 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5252 }, + { 0x2b, 0x5454 }, + { 0x2f, 0x0000 }, + { 0x30, 0x5000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x006f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x006f }, + { 0x45, 0x6000 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x0279 }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x0279 }, + { 0x53, 0xf000 }, + { 0x61, 0x0000 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c0 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x73, 0x1104 }, + { 0x74, 0x0c00 }, + { 0x75, 0x1400 }, + { 0x77, 0x0c00 }, + { 0x78, 0x4000 }, + { 0x79, 0x0123 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0800 }, + { 0x84, 0x0000 }, + { 0x85, 0x0008 }, + { 0x89, 0x0000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0000 }, + { 0x93, 0x2000 }, + { 0x94, 0x0200 }, + { 0xb0, 0x2080 }, + { 0xb1, 0x0000 }, + { 0xb4, 0x2206 }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0000 }, + { 0xc0, 0x0400 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xcf, 0x0013 }, + { 0xd0, 0x0680 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd9, 0x0809 }, + { 0xfa, 0x0010 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6281 }, +}; + +static bool rt5651_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5651_ranges); i++) { + if ((reg >= rt5651_ranges[i].window_start && + reg <= rt5651_ranges[i].window_start + + rt5651_ranges[i].window_len) || + (reg >= rt5651_ranges[i].range_min && + reg <= rt5651_ranges[i].range_max)) { + return true; + } + } + + switch (reg) { + case RT5651_RESET: + case RT5651_PRIV_DATA: + case RT5651_EQ_CTRL1: + case RT5651_ALC_1: + case RT5651_IRQ_CTRL2: + case RT5651_INT_IRQ_ST: + case RT5651_PGM_REG_ARR1: + case RT5651_PGM_REG_ARR3: + case RT5651_VENDOR_ID: + case RT5651_DEVICE_ID: + return true; + default: + return false; + } +} + +static bool rt5651_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5651_ranges); i++) { + if ((reg >= rt5651_ranges[i].window_start && + reg <= rt5651_ranges[i].window_start + + rt5651_ranges[i].window_len) || + (reg >= rt5651_ranges[i].range_min && + reg <= rt5651_ranges[i].range_max)) { + return true; + } + } + + switch (reg) { + case RT5651_RESET: + case RT5651_VERSION_ID: + case RT5651_VENDOR_ID: + case RT5651_DEVICE_ID: + case RT5651_HP_VOL: + case RT5651_LOUT_CTRL1: + case RT5651_LOUT_CTRL2: + case RT5651_IN1_IN2: + case RT5651_IN3: + case RT5651_INL1_INR1_VOL: + case RT5651_INL2_INR2_VOL: + case RT5651_DAC1_DIG_VOL: + case RT5651_DAC2_DIG_VOL: + case RT5651_DAC2_CTRL: + case RT5651_ADC_DIG_VOL: + case RT5651_ADC_DATA: + case RT5651_ADC_BST_VOL: + case RT5651_STO1_ADC_MIXER: + case RT5651_STO2_ADC_MIXER: + case RT5651_AD_DA_MIXER: + case RT5651_STO_DAC_MIXER: + case RT5651_DD_MIXER: + case RT5651_DIG_INF_DATA: + case RT5651_PDM_CTL: + case RT5651_REC_L1_MIXER: + case RT5651_REC_L2_MIXER: + case RT5651_REC_R1_MIXER: + case RT5651_REC_R2_MIXER: + case RT5651_HPO_MIXER: + case RT5651_OUT_L1_MIXER: + case RT5651_OUT_L2_MIXER: + case RT5651_OUT_L3_MIXER: + case RT5651_OUT_R1_MIXER: + case RT5651_OUT_R2_MIXER: + case RT5651_OUT_R3_MIXER: + case RT5651_LOUT_MIXER: + case RT5651_PWR_DIG1: + case RT5651_PWR_DIG2: + case RT5651_PWR_ANLG1: + case RT5651_PWR_ANLG2: + case RT5651_PWR_MIXER: + case RT5651_PWR_VOL: + case RT5651_PRIV_INDEX: + case RT5651_PRIV_DATA: + case RT5651_I2S1_SDP: + case RT5651_I2S2_SDP: + case RT5651_ADDA_CLK1: + case RT5651_ADDA_CLK2: + case RT5651_DMIC: + case RT5651_TDM_CTL_1: + case RT5651_TDM_CTL_2: + case RT5651_TDM_CTL_3: + case RT5651_GLB_CLK: + case RT5651_PLL_CTRL1: + case RT5651_PLL_CTRL2: + case RT5651_PLL_MODE_1: + case RT5651_PLL_MODE_2: + case RT5651_PLL_MODE_3: + case RT5651_PLL_MODE_4: + case RT5651_PLL_MODE_5: + case RT5651_PLL_MODE_6: + case RT5651_PLL_MODE_7: + case RT5651_DEPOP_M1: + case RT5651_DEPOP_M2: + case RT5651_DEPOP_M3: + case RT5651_CHARGE_PUMP: + case RT5651_MICBIAS: + case RT5651_A_JD_CTL1: + case RT5651_EQ_CTRL1: + case RT5651_EQ_CTRL2: + case RT5651_ALC_1: + case RT5651_ALC_2: + case RT5651_ALC_3: + case RT5651_JD_CTRL1: + case RT5651_JD_CTRL2: + case RT5651_IRQ_CTRL1: + case RT5651_IRQ_CTRL2: + case RT5651_INT_IRQ_ST: + case RT5651_GPIO_CTRL1: + case RT5651_GPIO_CTRL2: + case RT5651_GPIO_CTRL3: + case RT5651_PGM_REG_ARR1: + case RT5651_PGM_REG_ARR2: + case RT5651_PGM_REG_ARR3: + case RT5651_PGM_REG_ARR4: + case RT5651_PGM_REG_ARR5: + case RT5651_SCB_FUNC: + case RT5651_SCB_CTRL: + case RT5651_BASE_BACK: + case RT5651_MP3_PLUS1: + case RT5651_MP3_PLUS2: + case RT5651_ADJ_HPF_CTRL1: + case RT5651_ADJ_HPF_CTRL2: + case RT5651_HP_CALIB_AMP_DET: + case RT5651_HP_CALIB2: + case RT5651_SV_ZCD1: + case RT5651_SV_ZCD2: + case RT5651_D_MISC: + case RT5651_DUMMY2: + case RT5651_DUMMY3: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static unsigned int bst_tlv[] = { + TLV_DB_RANGE_HEAD(7), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), +}; + +/* Interface data select */ +static const char * const rt5651_data_select[] = { + "Normal", "Swap", "left copy to right", "right copy to left"}; + +static SOC_ENUM_SINGLE_DECL(rt5651_if2_dac_enum, RT5651_DIG_INF_DATA, + RT5651_IF2_DAC_SEL_SFT, rt5651_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_enum, RT5651_DIG_INF_DATA, + RT5651_IF2_ADC_SEL_SFT, rt5651_data_select); + +static const struct snd_kcontrol_new rt5651_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_TLV("HP Playback Volume", RT5651_HP_VOL, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv), + /* OUTPUT Control */ + SOC_DOUBLE_TLV("OUT Playback Volume", RT5651_LOUT_CTRL1, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE("DAC2 Playback Switch", RT5651_DAC2_CTRL, + RT5651_M_DAC_L2_VOL_SFT, RT5651_M_DAC_R2_VOL_SFT, 1, 1), + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5651_DAC1_DIG_VOL, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, + 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, + 175, 0, dac_vol_tlv), + /* IN1/IN2 Control */ + SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2, + RT5651_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2, + RT5651_BST_SFT2, 8, 0, bst_tlv), + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL, + RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT, + 31, 1, in_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5651_ADC_DIG_VOL, + RT5651_L_MUTE_SFT, RT5651_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5651_ADC_DIG_VOL, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, + 127, 0, adc_vol_tlv), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5651_ADC_DATA, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, + 127, 0, adc_vol_tlv), + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("ADC Boost Gain", RT5651_ADC_BST_VOL, + RT5651_ADC_L_BST_SFT, RT5651_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + /* ASRC */ + SOC_SINGLE("IF1 ASRC Switch", RT5651_PLL_MODE_1, + RT5651_STO1_T_SFT, 1, 0), + SOC_SINGLE("IF2 ASRC Switch", RT5651_PLL_MODE_1, + RT5651_STO2_T_SFT, 1, 0), + SOC_SINGLE("DMIC ASRC Switch", RT5651_PLL_MODE_1, + RT5651_DMIC_1_M_SFT, 1, 0), + + SOC_ENUM("ADC IF2 Data Switch", rt5651_if2_adc_enum), + SOC_ENUM("DAC IF2 Data Switch", rt5651_if2_dac_enum), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL; + int i, rate, red, bound, temp; + + rate = rt5651->sysclk; + red = 3000000 * 12; + for (i = 0; i < ARRAY_SIZE(div); i++) { + bound = div[i] * 3000000; + if (rate > bound) + continue; + temp = bound - rate; + if (temp < red) { + red = temp; + idx = i; + } + } + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else + snd_soc_update_bits(codec, RT5651_DMIC, RT5651_DMIC_CLK_MASK, + idx << RT5651_DMIC_CLK_SFT); + + return idx; +} + +static int is_sysclk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + + val = snd_soc_read(source->codec, RT5651_GLB_CLK); + val &= RT5651_SCLK_SRC_MASK; + if (val == RT5651_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5651_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER, + RT5651_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER, + RT5651_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER, + RT5651_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER, + RT5651_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO2_ADC_MIXER, + RT5651_M_STO2_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO2_ADC_MIXER, + RT5651_M_STO2_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO2_ADC_MIXER, + RT5651_M_STO2_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO2_ADC_MIXER, + RT5651_M_STO2_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5651_AD_DA_MIXER, + RT5651_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5651_AD_DA_MIXER, + RT5651_M_IF1_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5651_AD_DA_MIXER, + RT5651_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5651_AD_DA_MIXER, + RT5651_M_IF1_DAC_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_L1_MIXL_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_L2_MIXL_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_R1_MIXL_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_R1_MIXR_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_R2_MIXR_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_L1_MIXR_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_dd_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_L2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_R2_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_dd_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_R2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_L2_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5651_rec_l_mix[] = { + SOC_DAPM_SINGLE("INL1 Switch", RT5651_REC_L2_MIXER, + RT5651_M_IN1_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5651_REC_L2_MIXER, + RT5651_M_BST3_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5651_REC_L2_MIXER, + RT5651_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5651_REC_L2_MIXER, + RT5651_M_BST1_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_rec_r_mix[] = { + SOC_DAPM_SINGLE("INR1 Switch", RT5651_REC_R2_MIXER, + RT5651_M_IN1_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5651_REC_R2_MIXER, + RT5651_M_BST3_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5651_REC_R2_MIXER, + RT5651_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5651_REC_R2_MIXER, + RT5651_M_BST1_RM_R_SFT, 1, 1), +}; + +/* Analog Output Mixer */ + +static const struct snd_kcontrol_new rt5651_out_l_mix[] = { + SOC_DAPM_SINGLE("BST1 Switch", RT5651_OUT_L3_MIXER, + RT5651_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5651_OUT_L3_MIXER, + RT5651_M_BST2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL1 Switch", RT5651_OUT_L3_MIXER, + RT5651_M_IN1_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXL Switch", RT5651_OUT_L3_MIXER, + RT5651_M_RM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_OUT_L3_MIXER, + RT5651_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5651_OUT_R3_MIXER, + RT5651_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5651_OUT_R3_MIXER, + RT5651_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR1 Switch", RT5651_OUT_R3_MIXER, + RT5651_M_IN1_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXR Switch", RT5651_OUT_R3_MIXER, + RT5651_M_RM_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_OUT_R3_MIXER, + RT5651_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_hpo_mix[] = { + SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5651_HPO_MIXER, + RT5651_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5651_HPO_MIXER, + RT5651_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_LOUT_MIXER, + RT5651_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_LOUT_MIXER, + RT5651_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5651_LOUT_MIXER, + RT5651_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5651_LOUT_MIXER, + RT5651_M_OV_R_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new outvol_l_control = + SOC_DAPM_SINGLE("Switch", RT5651_LOUT_CTRL1, + RT5651_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new outvol_r_control = + SOC_DAPM_SINGLE("Switch", RT5651_LOUT_CTRL1, + RT5651_VOL_R_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_l_mute_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_LOUT_CTRL1, + RT5651_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_mute_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_LOUT_CTRL1, + RT5651_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpovol_l_control = + SOC_DAPM_SINGLE("Switch", RT5651_HP_VOL, + RT5651_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new hpovol_r_control = + SOC_DAPM_SINGLE("Switch", RT5651_HP_VOL, + RT5651_VOL_R_SFT, 1, 1); + +static const struct snd_kcontrol_new hpo_l_mute_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL, + RT5651_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpo_r_mute_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL, + RT5651_R_MUTE_SFT, 1, 1); + +/* INL/R source */ +static const char * const rt5651_inl_src[] = {"IN2P", "HPOVOLLP"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_inl_enum, RT5651_INL1_INR1_VOL, + RT5651_INL_SEL_SFT, rt5651_inl_src); + +static const struct snd_kcontrol_new rt5651_inl1_mux = + SOC_DAPM_ENUM("INL1 source", rt5651_inl_enum); + +static const char * const rt5651_inr1_src[] = {"IN2N", "HPOVOLRP"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_inr1_enum, RT5651_INL1_INR1_VOL, + RT5651_INR_SEL_SFT, rt5651_inr1_src); + +static const struct snd_kcontrol_new rt5651_inr1_mux = + SOC_DAPM_ENUM("INR1 source", rt5651_inr1_enum); + +static const char * const rt5651_inl2_src[] = {"IN3P", "OUTVOLLP"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_inl2_enum, RT5651_INL2_INR2_VOL, + RT5651_INL_SEL_SFT, rt5651_inl2_src); + +static const struct snd_kcontrol_new rt5651_inl2_mux = + SOC_DAPM_ENUM("INL2 source", rt5651_inl2_enum); + +static const char * const rt5651_inr2_src[] = {"IN3N", "OUTVOLRP"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_inr2_enum, RT5651_INL2_INR2_VOL, + RT5651_INR_SEL_SFT, rt5651_inr2_src); + +static const struct snd_kcontrol_new rt5651_inr2_mux = + SOC_DAPM_ENUM("INR2 source", rt5651_inr2_enum); + + +/* Stereo ADC source */ +static const char * const rt5651_stereo1_adc1_src[] = {"DD MIX", "ADC"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_stereo1_adc1_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO1_ADC_1_SRC_SFT, rt5651_stereo1_adc1_src); + +static const struct snd_kcontrol_new rt5651_sto1_adc_l1_mux = + SOC_DAPM_ENUM("Stereo1 ADC L1 source", rt5651_stereo1_adc1_enum); + +static const struct snd_kcontrol_new rt5651_sto1_adc_r1_mux = + SOC_DAPM_ENUM("Stereo1 ADC R1 source", rt5651_stereo1_adc1_enum); + +static const char * const rt5651_stereo1_adc2_src[] = {"DMIC", "DD MIX"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_stereo1_adc2_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO1_ADC_2_SRC_SFT, rt5651_stereo1_adc2_src); + +static const struct snd_kcontrol_new rt5651_sto1_adc_l2_mux = + SOC_DAPM_ENUM("Stereo1 ADC L2 source", rt5651_stereo1_adc2_enum); + +static const struct snd_kcontrol_new rt5651_sto1_adc_r2_mux = + SOC_DAPM_ENUM("Stereo1 ADC R2 source", rt5651_stereo1_adc2_enum); + +/* Mono ADC source */ +static const char * const rt5651_sto2_adc_l1_src[] = {"DD MIXL", "ADCL"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_sto2_adc_l1_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO2_ADC_L1_SRC_SFT, rt5651_sto2_adc_l1_src); + +static const struct snd_kcontrol_new rt5651_sto2_adc_l1_mux = + SOC_DAPM_ENUM("Stereo2 ADC1 left source", rt5651_sto2_adc_l1_enum); + +static const char * const rt5651_sto2_adc_l2_src[] = {"DMIC L", "DD MIXL"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_sto2_adc_l2_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO2_ADC_L2_SRC_SFT, rt5651_sto2_adc_l2_src); + +static const struct snd_kcontrol_new rt5651_sto2_adc_l2_mux = + SOC_DAPM_ENUM("Stereo2 ADC2 left source", rt5651_sto2_adc_l2_enum); + +static const char * const rt5651_sto2_adc_r1_src[] = {"DD MIXR", "ADCR"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_sto2_adc_r1_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO2_ADC_R1_SRC_SFT, rt5651_sto2_adc_r1_src); + +static const struct snd_kcontrol_new rt5651_sto2_adc_r1_mux = + SOC_DAPM_ENUM("Stereo2 ADC1 right source", rt5651_sto2_adc_r1_enum); + +static const char * const rt5651_sto2_adc_r2_src[] = {"DMIC R", "DD MIXR"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_sto2_adc_r2_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO2_ADC_R2_SRC_SFT, rt5651_sto2_adc_r2_src); + +static const struct snd_kcontrol_new rt5651_sto2_adc_r2_mux = + SOC_DAPM_ENUM("Stereo2 ADC2 right source", rt5651_sto2_adc_r2_enum); + +/* DAC2 channel source */ + +static const char * const rt5651_dac_src[] = {"IF1", "IF2"}; + +static SOC_ENUM_SINGLE_DECL(rt5651_dac_l2_enum, RT5651_DAC2_CTRL, + RT5651_SEL_DAC_L2_SFT, rt5651_dac_src); + +static const struct snd_kcontrol_new rt5651_dac_l2_mux = + SOC_DAPM_ENUM("DAC2 left channel source", rt5651_dac_l2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5651_dac_r2_enum, RT5651_DAC2_CTRL, + RT5651_SEL_DAC_R2_SFT, rt5651_dac_src); + +static const struct snd_kcontrol_new rt5651_dac_r2_mux = + SOC_DAPM_ENUM("DAC2 right channel source", rt5651_dac_r2_enum); + +/* IF2_ADC channel source */ + +static const char * const rt5651_adc_src[] = {"IF1 ADC1", "IF1 ADC2"}; + +static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_src_enum, RT5651_DIG_INF_DATA, + RT5651_IF2_ADC_SRC_SFT, rt5651_adc_src); + +static const struct snd_kcontrol_new rt5651_if2_adc_src_mux = + SOC_DAPM_ENUM("IF2 ADC channel source", rt5651_if2_adc_src_enum); + +/* PDM select */ +static const char * const rt5651_pdm_sel[] = {"DD MIX", "Stereo DAC MIX"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_pdm_l_sel_enum, RT5651_PDM_CTL, + RT5651_PDM_L_SEL_SFT, rt5651_pdm_sel); + +static SOC_ENUM_SINGLE_DECL( + rt5651_pdm_r_sel_enum, RT5651_PDM_CTL, + RT5651_PDM_R_SEL_SFT, rt5651_pdm_sel); + +static const struct snd_kcontrol_new rt5651_pdm_l_mux = + SOC_DAPM_ENUM("PDM L select", rt5651_pdm_l_sel_enum); + +static const struct snd_kcontrol_new rt5651_pdm_r_mux = + SOC_DAPM_ENUM("PDM R select", rt5651_pdm_r_sel_enum); + +static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* depop parameters */ + regmap_update_bits(rt5651->regmap, RT5651_PR_BASE + + RT5651_CHPUMP_INT_REG1, 0x0700, 0x0200); + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M2, + RT5651_DEPOP_MASK, RT5651_DEPOP_MAN); + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M1, + RT5651_HP_CP_MASK | RT5651_HP_SG_MASK | + RT5651_HP_CB_MASK, RT5651_HP_CP_PU | + RT5651_HP_SG_DIS | RT5651_HP_CB_PU); + regmap_write(rt5651->regmap, RT5651_PR_BASE + + RT5651_HP_DCC_INT1, 0x9f00); + /* headphone amp power on */ + regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1, + RT5651_PWR_FV1 | RT5651_PWR_FV2, 0); + regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1, + RT5651_PWR_HA, + RT5651_PWR_HA); + usleep_range(10000, 15000); + regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1, + RT5651_PWR_FV1 | RT5651_PWR_FV2 , + RT5651_PWR_FV1 | RT5651_PWR_FV2); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* headphone unmute sequence */ + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M2, + RT5651_DEPOP_MASK | RT5651_DIG_DP_MASK, + RT5651_DEPOP_AUTO | RT5651_DIG_DP_EN); + regmap_update_bits(rt5651->regmap, RT5651_CHARGE_PUMP, + RT5651_PM_HP_MASK, RT5651_PM_HP_HV); + + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M3, + RT5651_CP_FQ1_MASK | RT5651_CP_FQ2_MASK | + RT5651_CP_FQ3_MASK, + (RT5651_CP_FQ_192_KHZ << RT5651_CP_FQ1_SFT) | + (RT5651_CP_FQ_12_KHZ << RT5651_CP_FQ2_SFT) | + (RT5651_CP_FQ_192_KHZ << RT5651_CP_FQ3_SFT)); + + regmap_write(rt5651->regmap, RT5651_PR_BASE + + RT5651_MAMP_INT_REG2, 0x1c00); + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M1, + RT5651_HP_CP_MASK | RT5651_HP_SG_MASK, + RT5651_HP_CP_PD | RT5651_HP_SG_EN); + regmap_update_bits(rt5651->regmap, RT5651_PR_BASE + + RT5651_CHPUMP_INT_REG1, 0x0700, 0x0400); + rt5651->hp_mute = 0; + break; + + case SND_SOC_DAPM_PRE_PMD: + rt5651->hp_mute = 1; + usleep_range(70000, 75000); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!rt5651->hp_mute) + usleep_range(80000, 85000); + + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_bst1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5651_PWR_ANLG2, + RT5651_PWR_BST1_OP2, RT5651_PWR_BST1_OP2); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5651_PWR_ANLG2, + RT5651_PWR_BST1_OP2, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_bst2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5651_PWR_ANLG2, + RT5651_PWR_BST2_OP2, RT5651_PWR_BST2_OP2); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5651_PWR_ANLG2, + RT5651_PWR_BST2_OP2, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_bst3_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5651_PWR_ANLG2, + RT5651_PWR_BST3_OP2, RT5651_PWR_BST3_OP2); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5651_PWR_ANLG2, + RT5651_PWR_BST3_OP2, 0); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = { + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5651_PLL_MODE_2, + 15, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5651_PLL_MODE_2, + 14, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("STO1 DAC ASRC", 1, RT5651_PLL_MODE_2, + 13, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("STO2 DAC ASRC", 1, RT5651_PLL_MODE_2, + 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC ASRC", 1, RT5651_PLL_MODE_2, + 11, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL1", RT5651_PWR_ANLG2, + RT5651_PWR_PLL_BIT, 0, NULL, 0), + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1, + RT5651_PWR_LDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_MICBIAS("micbias1", RT5651_PWR_ANLG2, + RT5651_PWR_MB1_BIT, 0), + /* Input Lines */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("MIC3"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_SUPPLY("DMIC CLK", RT5651_DMIC, RT5651_DMIC_1_EN_SFT, + 0, set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + /* Boost */ + SND_SOC_DAPM_PGA_E("BST1", RT5651_PWR_ANLG2, + RT5651_PWR_BST1_BIT, 0, NULL, 0, rt5651_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("BST2", RT5651_PWR_ANLG2, + RT5651_PWR_BST2_BIT, 0, NULL, 0, rt5651_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("BST3", RT5651_PWR_ANLG2, + RT5651_PWR_BST3_BIT, 0, NULL, 0, rt5651_bst3_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL1 VOL", RT5651_PWR_VOL, + RT5651_PWR_IN1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR1 VOL", RT5651_PWR_VOL, + RT5651_PWR_IN1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INL2 VOL", RT5651_PWR_VOL, + RT5651_PWR_IN2_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR2 VOL", RT5651_PWR_VOL, + RT5651_PWR_IN2_R_BIT, 0, NULL, 0), + /* IN Mux */ + SND_SOC_DAPM_MUX("INL1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl1_mux), + SND_SOC_DAPM_MUX("INR1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr1_mux), + SND_SOC_DAPM_MUX("INL2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl2_mux), + SND_SOC_DAPM_MUX("INR2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr2_mux), + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5651_PWR_MIXER, RT5651_PWR_RM_L_BIT, 0, + rt5651_rec_l_mix, ARRAY_SIZE(rt5651_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5651_PWR_MIXER, RT5651_PWR_RM_R_BIT, 0, + rt5651_rec_r_mix, ARRAY_SIZE(rt5651_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC L Power", RT5651_PWR_DIG1, + RT5651_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC R Power", RT5651_PWR_DIG1, + RT5651_PWR_ADC_R_BIT, 0, NULL, 0), + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto1_adc_l2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto1_adc_r2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto1_adc_l1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto1_adc_r1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto2_adc_l2_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto2_adc_l1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto2_adc_r1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto2_adc_r2_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("Stereo1 Filter", RT5651_PWR_DIG2, + RT5651_PWR_ADC_STO1_F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Stereo2 Filter", RT5651_PWR_DIG2, + RT5651_PWR_ADC_STO2_F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5651_sto1_adc_l_mix, + ARRAY_SIZE(rt5651_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5651_sto1_adc_r_mix, + ARRAY_SIZE(rt5651_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5651_sto2_adc_l_mix, + ARRAY_SIZE(rt5651_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5651_sto2_adc_r_mix, + ARRAY_SIZE(rt5651_sto2_adc_r_mix)), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5651_PWR_DIG1, + RT5651_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5651_PWR_DIG1, + RT5651_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("IF2 ADC", SND_SOC_NOPM, 0, 0, + &rt5651_if2_adc_src_mux), + + /* Digital Interface Select */ + + SND_SOC_DAPM_MUX("PDM L Mux", RT5651_PDM_CTL, + RT5651_M_PDM_L_SFT, 1, &rt5651_pdm_l_mux), + SND_SOC_DAPM_MUX("PDM R Mux", RT5651_PDM_CTL, + RT5651_M_PDM_R_SFT, 1, &rt5651_pdm_r_mux), + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Audio DSP */ + SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5651_dac_l_mix, ARRAY_SIZE(rt5651_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5651_dac_r_mix, ARRAY_SIZE(rt5651_dac_r_mix)), + + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_dac_r2_mux), + SND_SOC_DAPM_PGA("DAC L2 Volume", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC R2 Volume", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5651_PWR_DIG2, + RT5651_PWR_DAC_STO1_F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Stero2 DAC Power", RT5651_PWR_DIG2, + RT5651_PWR_DAC_STO2_F_BIT, 0, NULL, 0), + /* DAC Mixer */ + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5651_sto_dac_l_mix, + ARRAY_SIZE(rt5651_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5651_sto_dac_r_mix, + ARRAY_SIZE(rt5651_sto_dac_r_mix)), + SND_SOC_DAPM_MIXER("DD MIXL", SND_SOC_NOPM, 0, 0, + rt5651_dd_dac_l_mix, + ARRAY_SIZE(rt5651_dd_dac_l_mix)), + SND_SOC_DAPM_MIXER("DD MIXR", SND_SOC_NOPM, 0, 0, + rt5651_dd_dac_r_mix, + ARRAY_SIZE(rt5651_dd_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5651_PWR_DIG1, + RT5651_PWR_DAC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5651_PWR_DIG1, + RT5651_PWR_DAC_R1_BIT, 0, NULL, 0), + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("OUT MIXL", RT5651_PWR_MIXER, RT5651_PWR_OM_L_BIT, + 0, rt5651_out_l_mix, ARRAY_SIZE(rt5651_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5651_PWR_MIXER, RT5651_PWR_OM_R_BIT, + 0, rt5651_out_r_mix, ARRAY_SIZE(rt5651_out_r_mix)), + /* Ouput Volume */ + SND_SOC_DAPM_SWITCH("OUTVOL L", RT5651_PWR_VOL, + RT5651_PWR_OV_L_BIT, 0, &outvol_l_control), + SND_SOC_DAPM_SWITCH("OUTVOL R", RT5651_PWR_VOL, + RT5651_PWR_OV_R_BIT, 0, &outvol_r_control), + SND_SOC_DAPM_SWITCH("HPOVOL L", RT5651_PWR_VOL, + RT5651_PWR_HV_L_BIT, 0, &hpovol_l_control), + SND_SOC_DAPM_SWITCH("HPOVOL R", RT5651_PWR_VOL, + RT5651_PWR_HV_R_BIT, 0, &hpovol_r_control), + SND_SOC_DAPM_PGA("INL1", RT5651_PWR_VOL, + RT5651_PWR_IN1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR1", RT5651_PWR_VOL, + RT5651_PWR_IN1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INL2", RT5651_PWR_VOL, + RT5651_PWR_IN2_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR2", RT5651_PWR_VOL, + RT5651_PWR_IN2_R_BIT, 0, NULL, 0), + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("HPOL MIX", SND_SOC_NOPM, 0, 0, + rt5651_hpo_mix, ARRAY_SIZE(rt5651_hpo_mix)), + SND_SOC_DAPM_MIXER("HPOR MIX", SND_SOC_NOPM, 0, 0, + rt5651_hpo_mix, ARRAY_SIZE(rt5651_hpo_mix)), + SND_SOC_DAPM_SUPPLY("HP L Amp", RT5651_PWR_ANLG1, + RT5651_PWR_HP_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP R Amp", RT5651_PWR_ANLG1, + RT5651_PWR_HP_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("LOUT MIX", RT5651_PWR_ANLG1, RT5651_PWR_LM_BIT, 0, + rt5651_lout_mix, ARRAY_SIZE(rt5651_lout_mix)), + + SND_SOC_DAPM_SUPPLY("Amp Power", RT5651_PWR_ANLG1, + RT5651_PWR_HA_BIT, 0, rt5651_amp_power_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5651_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("HPO L Playback", SND_SOC_NOPM, 0, 0, + &hpo_l_mute_control), + SND_SOC_DAPM_SWITCH("HPO R Playback", SND_SOC_NOPM, 0, 0, + &hpo_r_mute_control), + SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0, + &lout_l_mute_control), + SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0, + &lout_r_mute_control), + SND_SOC_DAPM_POST("HP Post", rt5651_hp_post_event), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("PDML"), + SND_SOC_DAPM_OUTPUT("PDMR"), +}; + +static const struct snd_soc_dapm_route rt5651_dapm_routes[] = { + {"Stero1 DAC Power", NULL, "STO1 DAC ASRC"}, + {"Stero2 DAC Power", NULL, "STO2 DAC ASRC"}, + {"I2S1", NULL, "I2S1 ASRC"}, + {"I2S2", NULL, "I2S2 ASRC"}, + + {"IN1P", NULL, "LDO"}, + {"IN2P", NULL, "LDO"}, + {"IN3P", NULL, "LDO"}, + + {"IN1P", NULL, "MIC1"}, + {"IN2P", NULL, "MIC2"}, + {"IN2N", NULL, "MIC2"}, + {"IN3P", NULL, "MIC3"}, + + {"BST1", NULL, "IN1P"}, + {"BST2", NULL, "IN2P"}, + {"BST2", NULL, "IN2N"}, + {"BST3", NULL, "IN3P"}, + + {"INL1 VOL", NULL, "IN2P"}, + {"INR1 VOL", NULL, "IN2N"}, + + {"RECMIXL", "INL1 Switch", "INL1 VOL"}, + {"RECMIXL", "BST3 Switch", "BST3"}, + {"RECMIXL", "BST2 Switch", "BST2"}, + {"RECMIXL", "BST1 Switch", "BST1"}, + + {"RECMIXR", "INR1 Switch", "INR1 VOL"}, + {"RECMIXR", "BST3 Switch", "BST3"}, + {"RECMIXR", "BST2 Switch", "BST2"}, + {"RECMIXR", "BST1 Switch", "BST1"}, + + {"ADC L", NULL, "RECMIXL"}, + {"ADC L", NULL, "ADC L Power"}, + {"ADC R", NULL, "RECMIXR"}, + {"ADC R", NULL, "ADC R Power"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC CLK"}, + + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DD MIX", "DD MIXL"}, + {"Stereo1 ADC L1 Mux", "ADC", "ADC L"}, + {"Stereo1 ADC L1 Mux", "DD MIX", "DD MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "ADC R"}, + {"Stereo1 ADC R1 Mux", "DD MIX", "DD MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DD MIX", "DD MIXR"}, + + {"Stereo2 ADC L2 Mux", "DMIC L", "DMIC L1"}, + {"Stereo2 ADC L2 Mux", "DD MIXL", "DD MIXL"}, + {"Stereo2 ADC L1 Mux", "DD MIXL", "DD MIXL"}, + {"Stereo2 ADC L1 Mux", "ADCL", "ADC L"}, + + {"Stereo2 ADC R1 Mux", "DD MIXR", "DD MIXR"}, + {"Stereo2 ADC R1 Mux", "ADCR", "ADC R"}, + {"Stereo2 ADC R2 Mux", "DMIC R", "DMIC R1"}, + {"Stereo2 ADC R2 Mux", "DD MIXR", "DD MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "Stereo1 Filter"}, + {"Stereo1 Filter", NULL, "PLL1", is_sysclk_from_pll}, + {"Stereo1 Filter", NULL, "ADC ASRC"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "Stereo1 Filter"}, + + {"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"}, + {"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"}, + {"Stereo2 ADC MIXL", NULL, "Stereo2 Filter"}, + {"Stereo2 Filter", NULL, "PLL1", is_sysclk_from_pll}, + {"Stereo2 Filter", NULL, "ADC ASRC"}, + + {"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"}, + {"Stereo2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux"}, + {"Stereo2 ADC MIXR", NULL, "Stereo2 Filter"}, + + {"IF1 ADC2", NULL, "Stereo2 ADC MIXL"}, + {"IF1 ADC2", NULL, "Stereo2 ADC MIXR"}, + {"IF1 ADC1", NULL, "Stereo1 ADC MIXL"}, + {"IF1 ADC1", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 ADC1", NULL, "I2S1"}, + + {"IF2 ADC", "IF1 ADC1", "IF1 ADC1"}, + {"IF2 ADC", "IF1 ADC2", "IF1 ADC2"}, + {"IF2 ADC", NULL, "I2S2"}, + + {"AIF1TX", NULL, "IF1 ADC1"}, + {"AIF1TX", NULL, "IF1 ADC2"}, + {"AIF2TX", NULL, "IF2 ADC"}, + + {"IF1 DAC", NULL, "AIF1RX"}, + {"IF1 DAC", NULL, "I2S1"}, + {"IF2 DAC", NULL, "AIF2RX"}, + {"IF2 DAC", NULL, "I2S2"}, + + {"IF1 DAC1 L", NULL, "IF1 DAC"}, + {"IF1 DAC1 R", NULL, "IF1 DAC"}, + {"IF1 DAC2 L", NULL, "IF1 DAC"}, + {"IF1 DAC2 R", NULL, "IF1 DAC"}, + {"IF2 DAC L", NULL, "IF2 DAC"}, + {"IF2 DAC R", NULL, "IF2 DAC"}, + + {"DAC MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC MIXL", "INF1 Switch", "IF1 DAC1 L"}, + {"DAC MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC MIXR", "INF1 Switch", "IF1 DAC1 R"}, + + {"Audio DSP", NULL, "DAC MIXL"}, + {"Audio DSP", NULL, "DAC MIXR"}, + + {"DAC L2 Mux", "IF1", "IF1 DAC2 L"}, + {"DAC L2 Mux", "IF2", "IF2 DAC L"}, + {"DAC L2 Volume", NULL, "DAC L2 Mux"}, + + {"DAC R2 Mux", "IF1", "IF1 DAC2 R"}, + {"DAC R2 Mux", "IF2", "IF2 DAC R"}, + {"DAC R2 Volume", NULL, "DAC R2 Mux"}, + + {"Stereo DAC MIXL", "DAC L1 Switch", "Audio DSP"}, + {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume"}, + {"Stereo DAC MIXL", "DAC R1 Switch", "DAC MIXR"}, + {"Stereo DAC MIXL", NULL, "Stero1 DAC Power"}, + {"Stereo DAC MIXL", NULL, "Stero2 DAC Power"}, + {"Stereo DAC MIXR", "DAC R1 Switch", "Audio DSP"}, + {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume"}, + {"Stereo DAC MIXR", "DAC L1 Switch", "DAC MIXL"}, + {"Stereo DAC MIXR", NULL, "Stero1 DAC Power"}, + {"Stereo DAC MIXR", NULL, "Stero2 DAC Power"}, + + {"PDM L Mux", "Stereo DAC MIX", "Stereo DAC MIXL"}, + {"PDM L Mux", "DD MIX", "DAC MIXL"}, + {"PDM R Mux", "Stereo DAC MIX", "Stereo DAC MIXR"}, + {"PDM R Mux", "DD MIX", "DAC MIXR"}, + + {"DAC L1", NULL, "Stereo DAC MIXL"}, + {"DAC L1", NULL, "PLL1", is_sysclk_from_pll}, + {"DAC L1", NULL, "DAC L1 Power"}, + {"DAC R1", NULL, "Stereo DAC MIXR"}, + {"DAC R1", NULL, "PLL1", is_sysclk_from_pll}, + {"DAC R1", NULL, "DAC R1 Power"}, + + {"DD MIXL", "DAC L1 Switch", "DAC MIXL"}, + {"DD MIXL", "DAC L2 Switch", "DAC L2 Volume"}, + {"DD MIXL", "DAC R2 Switch", "DAC R2 Volume"}, + {"DD MIXL", NULL, "Stero2 DAC Power"}, + + {"DD MIXR", "DAC R1 Switch", "DAC MIXR"}, + {"DD MIXR", "DAC R2 Switch", "DAC R2 Volume"}, + {"DD MIXR", "DAC L2 Switch", "DAC L2 Volume"}, + {"DD MIXR", NULL, "Stero2 DAC Power"}, + + {"OUT MIXL", "BST1 Switch", "BST1"}, + {"OUT MIXL", "BST2 Switch", "BST2"}, + {"OUT MIXL", "INL1 Switch", "INL1 VOL"}, + {"OUT MIXL", "REC MIXL Switch", "RECMIXL"}, + {"OUT MIXL", "DAC L1 Switch", "DAC L1"}, + + {"OUT MIXR", "BST2 Switch", "BST2"}, + {"OUT MIXR", "BST1 Switch", "BST1"}, + {"OUT MIXR", "INR1 Switch", "INR1 VOL"}, + {"OUT MIXR", "REC MIXR Switch", "RECMIXR"}, + {"OUT MIXR", "DAC R1 Switch", "DAC R1"}, + + {"HPOVOL L", "Switch", "OUT MIXL"}, + {"HPOVOL R", "Switch", "OUT MIXR"}, + {"OUTVOL L", "Switch", "OUT MIXL"}, + {"OUTVOL R", "Switch", "OUT MIXR"}, + + {"HPOL MIX", "HPO MIX DAC1 Switch", "DAC L1"}, + {"HPOL MIX", "HPO MIX HPVOL Switch", "HPOVOL L"}, + {"HPOL MIX", NULL, "HP L Amp"}, + {"HPOR MIX", "HPO MIX DAC1 Switch", "DAC R1"}, + {"HPOR MIX", "HPO MIX HPVOL Switch", "HPOVOL R"}, + {"HPOR MIX", NULL, "HP R Amp"}, + + {"LOUT MIX", "DAC L1 Switch", "DAC L1"}, + {"LOUT MIX", "DAC R1 Switch", "DAC R1"}, + {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"}, + + {"HP Amp", NULL, "HPOL MIX"}, + {"HP Amp", NULL, "HPOR MIX"}, + {"HP Amp", NULL, "Amp Power"}, + {"HPO L Playback", "Switch", "HP Amp"}, + {"HPO R Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPO L Playback"}, + {"HPOR", NULL, "HPO R Playback"}, + + {"LOUT L Playback", "Switch", "LOUT MIX"}, + {"LOUT R Playback", "Switch", "LOUT MIX"}, + {"LOUTL", NULL, "LOUT L Playback"}, + {"LOUTL", NULL, "Amp Power"}, + {"LOUTR", NULL, "LOUT R Playback"}, + {"LOUTR", NULL, "Amp Power"}, + + {"PDML", NULL, "PDM L Mux"}, + {"PDMR", NULL, "PDM R Mux"}, +}; + +static int get_clk_info(int sclk, int rate) +{ + int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt5651_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5651->lrck[dai->id] = params_rate(params); + pre_div = get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]); + + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + bclk_ms = frame_size > 32 ? 1 : 0; + rt5651->bclk[dai->id] = rt5651->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5651->bclk[dai->id], rt5651->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len |= RT5651_I2S_DL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len |= RT5651_I2S_DL_24; + break; + case SNDRV_PCM_FORMAT_S8: + val_len |= RT5651_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5651_AIF1: + mask_clk = RT5651_I2S_PD1_MASK; + val_clk = pre_div << RT5651_I2S_PD1_SFT; + snd_soc_update_bits(codec, RT5651_I2S1_SDP, + RT5651_I2S_DL_MASK, val_len); + snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk); + break; + case RT5651_AIF2: + mask_clk = RT5651_I2S_BCLK_MS2_MASK | RT5651_I2S_PD2_MASK; + val_clk = pre_div << RT5651_I2S_PD2_SFT; + snd_soc_update_bits(codec, RT5651_I2S2_SDP, + RT5651_I2S_DL_MASK, val_len); + snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk); + break; + default: + dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5651->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5651_I2S_MS_S; + rt5651->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5651_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5651_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5651_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5651_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5651_AIF1: + snd_soc_update_bits(codec, RT5651_I2S1_SDP, + RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK | + RT5651_I2S_DF_MASK, reg_val); + break; + case RT5651_AIF2: + snd_soc_update_bits(codec, RT5651_I2S2_SDP, + RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK | + RT5651_I2S_DF_MASK, reg_val); + break; + default: + dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5651->sysclk && clk_id == rt5651->sysclk_src) + return 0; + + switch (clk_id) { + case RT5651_SCLK_S_MCLK: + reg_val |= RT5651_SCLK_SRC_MCLK; + break; + case RT5651_SCLK_S_PLL1: + reg_val |= RT5651_SCLK_SRC_PLL1; + break; + case RT5651_SCLK_S_RCCLK: + reg_val |= RT5651_SCLK_SRC_RCCLK; + break; + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_update_bits(codec, RT5651_GLB_CLK, + RT5651_SCLK_SRC_MASK, reg_val); + rt5651->sysclk = freq; + rt5651->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +/** + * rt5651_pll_calc - Calcualte PLL M/N/K code. + * @freq_in: external clock provided to codec. + * @freq_out: target clock which codec works on. + * @pll_code: Pointer to structure with M, N, K and bypass flag. + * + * Calcualte M/N/K code to configure PLL for codec. And K is assigned to 2 + * which make calculation more efficiently. + * + * Returns 0 for success or negative error code. + */ +static int rt5651_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rt5651_pll_code *pll_code) +{ + int max_n = RT5651_PLL_N_MAX, max_m = RT5651_PLL_M_MAX; + int n = 0, m = 0, red, n_t, m_t, in_t, out_t; + int red_t = abs(freq_out - freq_in); + bool bypass = false; + + if (RT5651_PLL_INP_MAX < freq_in || RT5651_PLL_INP_MIN > freq_in) + return -EINVAL; + + for (n_t = 0; n_t <= max_n; n_t++) { + in_t = (freq_in >> 1) + (freq_in >> 2) * n_t; + if (in_t < 0) + continue; + if (in_t == freq_out) { + bypass = true; + n = n_t; + goto code_find; + } + for (m_t = 0; m_t <= max_m; m_t++) { + out_t = in_t / (m_t + 2); + red = abs(out_t - freq_out); + if (red < red_t) { + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + } + } + pr_debug("Only get approximation about PLL\n"); + +code_find: + pll_code->m_bp = bypass; + pll_code->m_code = m; + pll_code->n_code = n; + pll_code->k_code = 2; + return 0; +} + +static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + struct rt5651_pll_code *pll_code = &rt5651->pll_code; + int ret; + + if (source == rt5651->pll_src && freq_in == rt5651->pll_in && + freq_out == rt5651->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5651->pll_in = 0; + rt5651->pll_out = 0; + snd_soc_update_bits(codec, RT5651_GLB_CLK, + RT5651_SCLK_SRC_MASK, RT5651_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5651_PLL1_S_MCLK: + snd_soc_update_bits(codec, RT5651_GLB_CLK, + RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_MCLK); + break; + case RT5651_PLL1_S_BCLK1: + snd_soc_update_bits(codec, RT5651_GLB_CLK, + RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK1); + break; + case RT5651_PLL1_S_BCLK2: + snd_soc_update_bits(codec, RT5651_GLB_CLK, + RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK2); + break; + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rt5651_pll_calc(freq_in, freq_out, pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=2\n", pll_code->m_bp, + (pll_code->m_bp ? 0 : pll_code->m_code), pll_code->n_code); + + snd_soc_write(codec, RT5651_PLL_CTRL1, + pll_code->n_code << RT5651_PLL_N_SFT | pll_code->k_code); + snd_soc_write(codec, RT5651_PLL_CTRL2, + (pll_code->m_bp ? 0 : pll_code->m_code) << RT5651_PLL_M_SFT | + pll_code->m_bp << RT5651_PLL_M_BP_SFT); + + rt5651->pll_in = freq_in; + rt5651->pll_out = freq_out; + rt5651->pll_src = source; + + return 0; +} + +static int rt5651_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + snd_soc_update_bits(codec, RT5651_PWR_ANLG1, + RT5651_PWR_VREF1 | RT5651_PWR_MB | + RT5651_PWR_BG | RT5651_PWR_VREF2, + RT5651_PWR_VREF1 | RT5651_PWR_MB | + RT5651_PWR_BG | RT5651_PWR_VREF2); + usleep_range(10000, 15000); + snd_soc_update_bits(codec, RT5651_PWR_ANLG1, + RT5651_PWR_FV1 | RT5651_PWR_FV2, + RT5651_PWR_FV1 | RT5651_PWR_FV2); + snd_soc_update_bits(codec, RT5651_PWR_ANLG1, + RT5651_PWR_LDO_DVO_MASK, + RT5651_PWR_LDO_DVO_1_2V); + snd_soc_update_bits(codec, RT5651_D_MISC, 0x1, 0x1); + if (snd_soc_read(codec, RT5651_PLL_MODE_1) & 0x9200) + snd_soc_update_bits(codec, RT5651_D_MISC, + 0xc00, 0xc00); + } + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_write(codec, RT5651_D_MISC, 0x0010); + snd_soc_write(codec, RT5651_PWR_DIG1, 0x0000); + snd_soc_write(codec, RT5651_PWR_DIG2, 0x0000); + snd_soc_write(codec, RT5651_PWR_VOL, 0x0000); + snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000); + snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000); + snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000); + break; + + default: + break; + } + codec->dapm.bias_level = level; + + return 0; +} + +static int rt5651_probe(struct snd_soc_codec *codec) +{ + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + rt5651->codec = codec; + + snd_soc_update_bits(codec, RT5651_PWR_ANLG1, + RT5651_PWR_VREF1 | RT5651_PWR_MB | + RT5651_PWR_BG | RT5651_PWR_VREF2, + RT5651_PWR_VREF1 | RT5651_PWR_MB | + RT5651_PWR_BG | RT5651_PWR_VREF2); + usleep_range(10000, 15000); + snd_soc_update_bits(codec, RT5651_PWR_ANLG1, + RT5651_PWR_FV1 | RT5651_PWR_FV2, + RT5651_PWR_FV1 | RT5651_PWR_FV2); + + rt5651_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +#ifdef CONFIG_PM +static int rt5651_suspend(struct snd_soc_codec *codec) +{ + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5651->regmap, true); + regcache_mark_dirty(rt5651->regmap); + return 0; +} + +static int rt5651_resume(struct snd_soc_codec *codec) +{ + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5651->regmap, false); + snd_soc_cache_sync(codec); + + return 0; +} +#else +#define rt5651_suspend NULL +#define rt5651_resume NULL +#endif + +#define RT5651_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5651_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt5651_aif_dai_ops = { + .hw_params = rt5651_hw_params, + .set_fmt = rt5651_set_dai_fmt, + .set_sysclk = rt5651_set_dai_sysclk, + .set_pll = rt5651_set_dai_pll, +}; + +struct snd_soc_dai_driver rt5651_dai[] = { + { + .name = "rt5651-aif1", + .id = RT5651_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5651_STEREO_RATES, + .formats = RT5651_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5651_STEREO_RATES, + .formats = RT5651_FORMATS, + }, + .ops = &rt5651_aif_dai_ops, + }, + { + .name = "rt5651-aif2", + .id = RT5651_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5651_STEREO_RATES, + .formats = RT5651_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5651_STEREO_RATES, + .formats = RT5651_FORMATS, + }, + .ops = &rt5651_aif_dai_ops, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5651 = { + .probe = rt5651_probe, + .suspend = rt5651_suspend, + .resume = rt5651_resume, + .set_bias_level = rt5651_set_bias_level, + .idle_bias_off = true, + .controls = rt5651_snd_controls, + .num_controls = ARRAY_SIZE(rt5651_snd_controls), + .dapm_widgets = rt5651_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets), + .dapm_routes = rt5651_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes), +}; + +static const struct regmap_config rt5651_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = RT5651_DEVICE_ID + 1 + (ARRAY_SIZE(rt5651_ranges) * + RT5651_PR_SPACING), + .volatile_reg = rt5651_volatile_register, + .readable_reg = rt5651_readable_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5651_reg, + .num_reg_defaults = ARRAY_SIZE(rt5651_reg), + .ranges = rt5651_ranges, + .num_ranges = ARRAY_SIZE(rt5651_ranges), +}; + +static const struct i2c_device_id rt5651_i2c_id[] = { + { "rt5651", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id); + +static int rt5651_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5651_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5651_priv *rt5651; + int ret; + + rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651), + GFP_KERNEL); + if (NULL == rt5651) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5651); + + if (pdata) + rt5651->pdata = *pdata; + + rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap); + if (IS_ERR(rt5651->regmap)) { + ret = PTR_ERR(rt5651->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret); + if (ret != RT5651_DEVICE_ID_VALUE) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5651\n", ret); + return -ENODEV; + } + + regmap_write(rt5651->regmap, RT5651_RESET, 0); + + ret = regmap_register_patch(rt5651->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + if (rt5651->pdata.in2_diff) + regmap_update_bits(rt5651->regmap, RT5651_IN1_IN2, + RT5651_IN_DF2, RT5651_IN_DF2); + + if (rt5651->pdata.dmic_en) + regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1, + RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL); + + rt5651->hp_mute = 1; + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651, + rt5651_dai, ARRAY_SIZE(rt5651_dai)); + + return ret; +} + +static int rt5651_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +struct i2c_driver rt5651_i2c_driver = { + .driver = { + .name = "rt5651", + .owner = THIS_MODULE, + }, + .probe = rt5651_i2c_probe, + .remove = rt5651_i2c_remove, + .id_table = rt5651_i2c_id, +}; +module_i2c_driver(rt5651_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5651 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h new file mode 100644 index 000000000000..a28bd0c3d613 --- /dev/null +++ b/sound/soc/codecs/rt5651.h @@ -0,0 +1,2081 @@ +/* + * rt5651.h -- RT5651 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + * + * 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 __RT5651_H__ +#define __RT5651_H__ + +#include + +/* Info */ +#define RT5651_RESET 0x00 +#define RT5651_VERSION_ID 0xfd +#define RT5651_VENDOR_ID 0xfe +#define RT5651_DEVICE_ID 0xff +/* I/O - Output */ +#define RT5651_HP_VOL 0x02 +#define RT5651_LOUT_CTRL1 0x03 +#define RT5651_LOUT_CTRL2 0x05 +/* I/O - Input */ +#define RT5651_IN1_IN2 0x0d +#define RT5651_IN3 0x0e +#define RT5651_INL1_INR1_VOL 0x0f +#define RT5651_INL2_INR2_VOL 0x10 +/* I/O - ADC/DAC/DMIC */ +#define RT5651_DAC1_DIG_VOL 0x19 +#define RT5651_DAC2_DIG_VOL 0x1a +#define RT5651_DAC2_CTRL 0x1b +#define RT5651_ADC_DIG_VOL 0x1c +#define RT5651_ADC_DATA 0x1d +#define RT5651_ADC_BST_VOL 0x1e +/* Mixer - D-D */ +#define RT5651_STO1_ADC_MIXER 0x27 +#define RT5651_STO2_ADC_MIXER 0x28 +#define RT5651_AD_DA_MIXER 0x29 +#define RT5651_STO_DAC_MIXER 0x2a +#define RT5651_DD_MIXER 0x2b +#define RT5651_DIG_INF_DATA 0x2f +/* PDM */ +#define RT5651_PDM_CTL 0x30 +#define RT5651_PDM_I2C_CTL1 0x31 +#define RT5651_PDM_I2C_CTL2 0x32 +#define RT5651_PDM_I2C_DATA_W 0x33 +#define RT5651_PDM_I2C_DATA_R 0x34 +/* Mixer - ADC */ +#define RT5651_REC_L1_MIXER 0x3b +#define RT5651_REC_L2_MIXER 0x3c +#define RT5651_REC_R1_MIXER 0x3d +#define RT5651_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5651_HPO_MIXER 0x45 +#define RT5651_OUT_L1_MIXER 0x4d +#define RT5651_OUT_L2_MIXER 0x4e +#define RT5651_OUT_L3_MIXER 0x4f +#define RT5651_OUT_R1_MIXER 0x50 +#define RT5651_OUT_R2_MIXER 0x51 +#define RT5651_OUT_R3_MIXER 0x52 +#define RT5651_LOUT_MIXER 0x53 +/* Power */ +#define RT5651_PWR_DIG1 0x61 +#define RT5651_PWR_DIG2 0x62 +#define RT5651_PWR_ANLG1 0x63 +#define RT5651_PWR_ANLG2 0x64 +#define RT5651_PWR_MIXER 0x65 +#define RT5651_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5651_PRIV_INDEX 0x6a +#define RT5651_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5651_I2S1_SDP 0x70 +#define RT5651_I2S2_SDP 0x71 +#define RT5651_ADDA_CLK1 0x73 +#define RT5651_ADDA_CLK2 0x74 +#define RT5651_DMIC 0x75 +/* TDM Control */ +#define RT5651_TDM_CTL_1 0x77 +#define RT5651_TDM_CTL_2 0x78 +#define RT5651_TDM_CTL_3 0x79 +/* Function - Analog */ +#define RT5651_GLB_CLK 0x80 +#define RT5651_PLL_CTRL1 0x81 +#define RT5651_PLL_CTRL2 0x82 +#define RT5651_PLL_MODE_1 0x83 +#define RT5651_PLL_MODE_2 0x84 +#define RT5651_PLL_MODE_3 0x85 +#define RT5651_PLL_MODE_4 0x86 +#define RT5651_PLL_MODE_5 0x87 +#define RT5651_PLL_MODE_6 0x89 +#define RT5651_PLL_MODE_7 0x8a +#define RT5651_DEPOP_M1 0x8e +#define RT5651_DEPOP_M2 0x8f +#define RT5651_DEPOP_M3 0x90 +#define RT5651_CHARGE_PUMP 0x91 +#define RT5651_MICBIAS 0x93 +#define RT5651_A_JD_CTL1 0x94 +/* Function - Digital */ +#define RT5651_EQ_CTRL1 0xb0 +#define RT5651_EQ_CTRL2 0xb1 +#define RT5651_ALC_1 0xb4 +#define RT5651_ALC_2 0xb5 +#define RT5651_ALC_3 0xb6 +#define RT5651_JD_CTRL1 0xbb +#define RT5651_JD_CTRL2 0xbc +#define RT5651_IRQ_CTRL1 0xbd +#define RT5651_IRQ_CTRL2 0xbe +#define RT5651_INT_IRQ_ST 0xbf +#define RT5651_GPIO_CTRL1 0xc0 +#define RT5651_GPIO_CTRL2 0xc1 +#define RT5651_GPIO_CTRL3 0xc2 +#define RT5651_PGM_REG_ARR1 0xc8 +#define RT5651_PGM_REG_ARR2 0xc9 +#define RT5651_PGM_REG_ARR3 0xca +#define RT5651_PGM_REG_ARR4 0xcb +#define RT5651_PGM_REG_ARR5 0xcc +#define RT5651_SCB_FUNC 0xcd +#define RT5651_SCB_CTRL 0xce +#define RT5651_BASE_BACK 0xcf +#define RT5651_MP3_PLUS1 0xd0 +#define RT5651_MP3_PLUS2 0xd1 +#define RT5651_ADJ_HPF_CTRL1 0xd3 +#define RT5651_ADJ_HPF_CTRL2 0xd4 +#define RT5651_HP_CALIB_AMP_DET 0xd6 +#define RT5651_HP_CALIB2 0xd7 +#define RT5651_SV_ZCD1 0xd9 +#define RT5651_SV_ZCD2 0xda +#define RT5651_D_MISC 0xfa +/* Dummy Register */ +#define RT5651_DUMMY2 0xfb +#define RT5651_DUMMY3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5651_BIAS_CUR1 0x12 +#define RT5651_BIAS_CUR3 0x14 +#define RT5651_CLSD_INT_REG1 0x1c +#define RT5651_CHPUMP_INT_REG1 0x24 +#define RT5651_MAMP_INT_REG2 0x37 +#define RT5651_CHOP_DAC_ADC 0x3d +#define RT5651_3D_SPK 0x63 +#define RT5651_WND_1 0x6c +#define RT5651_WND_2 0x6d +#define RT5651_WND_3 0x6e +#define RT5651_WND_4 0x6f +#define RT5651_WND_5 0x70 +#define RT5651_WND_8 0x73 +#define RT5651_DIP_SPK_INF 0x75 +#define RT5651_HP_DCC_INT1 0x77 +#define RT5651_EQ_BW_LOP 0xa0 +#define RT5651_EQ_GN_LOP 0xa1 +#define RT5651_EQ_FC_BP1 0xa2 +#define RT5651_EQ_BW_BP1 0xa3 +#define RT5651_EQ_GN_BP1 0xa4 +#define RT5651_EQ_FC_BP2 0xa5 +#define RT5651_EQ_BW_BP2 0xa6 +#define RT5651_EQ_GN_BP2 0xa7 +#define RT5651_EQ_FC_BP3 0xa8 +#define RT5651_EQ_BW_BP3 0xa9 +#define RT5651_EQ_GN_BP3 0xaa +#define RT5651_EQ_FC_BP4 0xab +#define RT5651_EQ_BW_BP4 0xac +#define RT5651_EQ_GN_BP4 0xad +#define RT5651_EQ_FC_HIP1 0xae +#define RT5651_EQ_GN_HIP1 0xaf +#define RT5651_EQ_FC_HIP2 0xb0 +#define RT5651_EQ_BW_HIP2 0xb1 +#define RT5651_EQ_GN_HIP2 0xb2 +#define RT5651_EQ_PRE_VOL 0xb3 +#define RT5651_EQ_PST_VOL 0xb4 + + +/* global definition */ +#define RT5651_L_MUTE (0x1 << 15) +#define RT5651_L_MUTE_SFT 15 +#define RT5651_VOL_L_MUTE (0x1 << 14) +#define RT5651_VOL_L_SFT 14 +#define RT5651_R_MUTE (0x1 << 7) +#define RT5651_R_MUTE_SFT 7 +#define RT5651_VOL_R_MUTE (0x1 << 6) +#define RT5651_VOL_R_SFT 6 +#define RT5651_L_VOL_MASK (0x3f << 8) +#define RT5651_L_VOL_SFT 8 +#define RT5651_R_VOL_MASK (0x3f) +#define RT5651_R_VOL_SFT 0 + +/* LOUT Control 2(0x05) */ +#define RT5651_EN_DFO (0x1 << 15) + +/* IN1 and IN2 Control (0x0d) */ +/* IN3 and IN4 Control (0x0e) */ +#define RT5651_BST_MASK1 (0xf<<12) +#define RT5651_BST_SFT1 12 +#define RT5651_BST_MASK2 (0xf<<8) +#define RT5651_BST_SFT2 8 +#define RT5651_IN_DF1 (0x1 << 7) +#define RT5651_IN_SFT1 7 +#define RT5651_IN_DF2 (0x1 << 6) +#define RT5651_IN_SFT2 6 + +/* INL1 and INR1 Volume Control (0x0f) */ +/* INL2 and INR2 Volume Control (0x10) */ +#define RT5651_INL_SEL_MASK (0x1 << 15) +#define RT5651_INL_SEL_SFT 15 +#define RT5651_INL_SEL_IN4P (0x0 << 15) +#define RT5651_INL_SEL_MONOP (0x1 << 15) +#define RT5651_INL_VOL_MASK (0x1f << 8) +#define RT5651_INL_VOL_SFT 8 +#define RT5651_INR_SEL_MASK (0x1 << 7) +#define RT5651_INR_SEL_SFT 7 +#define RT5651_INR_SEL_IN4N (0x0 << 7) +#define RT5651_INR_SEL_MONON (0x1 << 7) +#define RT5651_INR_VOL_MASK (0x1f) +#define RT5651_INR_VOL_SFT 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5651_DAC_L1_VOL_MASK (0xff << 8) +#define RT5651_DAC_L1_VOL_SFT 8 +#define RT5651_DAC_R1_VOL_MASK (0xff) +#define RT5651_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5651_DAC_L2_VOL_MASK (0xff << 8) +#define RT5651_DAC_L2_VOL_SFT 8 +#define RT5651_DAC_R2_VOL_MASK (0xff) +#define RT5651_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x1b) */ +#define RT5651_M_DAC_L2_VOL (0x1 << 13) +#define RT5651_M_DAC_L2_VOL_SFT 13 +#define RT5651_M_DAC_R2_VOL (0x1 << 12) +#define RT5651_M_DAC_R2_VOL_SFT 12 +#define RT5651_SEL_DAC_L2 (0x1 << 11) +#define RT5651_IF2_DAC_L2 (0x1 << 11) +#define RT5651_IF1_DAC_L2 (0x0 << 11) +#define RT5651_SEL_DAC_L2_SFT 11 +#define RT5651_SEL_DAC_R2 (0x1 << 10) +#define RT5651_IF2_DAC_R2 (0x1 << 11) +#define RT5651_IF1_DAC_R2 (0x0 << 11) +#define RT5651_SEL_DAC_R2_SFT 10 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5651_ADC_L_VOL_MASK (0x7f << 8) +#define RT5651_ADC_L_VOL_SFT 8 +#define RT5651_ADC_R_VOL_MASK (0x7f) +#define RT5651_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5651_M_MONO_ADC_L (0x1 << 15) +#define RT5651_M_MONO_ADC_L_SFT 15 +#define RT5651_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5651_MONO_ADC_L_VOL_SFT 8 +#define RT5651_M_MONO_ADC_R (0x1 << 7) +#define RT5651_M_MONO_ADC_R_SFT 7 +#define RT5651_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5651_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5651_ADC_L_BST_MASK (0x3 << 14) +#define RT5651_ADC_L_BST_SFT 14 +#define RT5651_ADC_R_BST_MASK (0x3 << 12) +#define RT5651_ADC_R_BST_SFT 12 +#define RT5651_ADC_COMP_MASK (0x3 << 10) +#define RT5651_ADC_COMP_SFT 10 + +/* Stereo ADC1 Mixer Control (0x27) */ +#define RT5651_M_STO1_ADC_L1 (0x1 << 14) +#define RT5651_M_STO1_ADC_L1_SFT 14 +#define RT5651_M_STO1_ADC_L2 (0x1 << 13) +#define RT5651_M_STO1_ADC_L2_SFT 13 +#define RT5651_STO1_ADC_1_SRC_MASK (0x1 << 12) +#define RT5651_STO1_ADC_1_SRC_SFT 12 +#define RT5651_STO1_ADC_1_SRC_ADC (0x1 << 12) +#define RT5651_STO1_ADC_1_SRC_DACMIX (0x0 << 12) +#define RT5651_STO1_ADC_2_SRC_MASK (0x1 << 11) +#define RT5651_STO1_ADC_2_SRC_SFT 11 +#define RT5651_STO1_ADC_2_SRC_DMIC (0x0 << 11) +#define RT5651_STO1_ADC_2_SRC_DACMIXR (0x1 << 11) +#define RT5651_M_STO1_ADC_R1 (0x1 << 6) +#define RT5651_M_STO1_ADC_R1_SFT 6 +#define RT5651_M_STO1_ADC_R2 (0x1 << 5) +#define RT5651_M_STO1_ADC_R2_SFT 5 + +/* Stereo ADC2 Mixer Control (0x28) */ +#define RT5651_M_STO2_ADC_L1 (0x1 << 14) +#define RT5651_M_STO2_ADC_L1_SFT 14 +#define RT5651_M_STO2_ADC_L2 (0x1 << 13) +#define RT5651_M_STO2_ADC_L2_SFT 13 +#define RT5651_STO2_ADC_L1_SRC_MASK (0x1 << 12) +#define RT5651_STO2_ADC_L1_SRC_SFT 12 +#define RT5651_STO2_ADC_L1_SRC_DACMIXL (0x0 << 12) +#define RT5651_STO2_ADC_L1_SRC_ADCL (0x1 << 12) +#define RT5651_STO2_ADC_L2_SRC_MASK (0x1 << 11) +#define RT5651_STO2_ADC_L2_SRC_SFT 11 +#define RT5651_STO2_ADC_L2_SRC_DMIC (0x0 << 11) +#define RT5651_STO2_ADC_L2_SRC_DACMIXR (0x1 << 11) +#define RT5651_M_STO2_ADC_R1 (0x1 << 6) +#define RT5651_M_STO2_ADC_R1_SFT 6 +#define RT5651_M_STO2_ADC_R2 (0x1 << 5) +#define RT5651_M_STO2_ADC_R2_SFT 5 +#define RT5651_STO2_ADC_R1_SRC_MASK (0x1 << 4) +#define RT5651_STO2_ADC_R1_SRC_SFT 4 +#define RT5651_STO2_ADC_R1_SRC_ADCR (0x1 << 4) +#define RT5651_STO2_ADC_R1_SRC_DACMIXR (0x0 << 4) +#define RT5651_STO2_ADC_R2_SRC_MASK (0x1 << 3) +#define RT5651_STO2_ADC_R2_SRC_SFT 3 +#define RT5651_STO2_ADC_R2_SRC_DMIC (0x0 << 3) +#define RT5651_STO2_ADC_R2_SRC_DACMIXR (0x1 << 3) + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5651_M_ADCMIX_L (0x1 << 15) +#define RT5651_M_ADCMIX_L_SFT 15 +#define RT5651_M_IF1_DAC_L (0x1 << 14) +#define RT5651_M_IF1_DAC_L_SFT 14 +#define RT5651_M_ADCMIX_R (0x1 << 7) +#define RT5651_M_ADCMIX_R_SFT 7 +#define RT5651_M_IF1_DAC_R (0x1 << 6) +#define RT5651_M_IF1_DAC_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5651_M_DAC_L1_MIXL (0x1 << 14) +#define RT5651_M_DAC_L1_MIXL_SFT 14 +#define RT5651_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5651_DAC_L1_STO_L_VOL_SFT 13 +#define RT5651_M_DAC_L2_MIXL (0x1 << 12) +#define RT5651_M_DAC_L2_MIXL_SFT 12 +#define RT5651_DAC_L2_STO_L_VOL_MASK (0x1 << 11) +#define RT5651_DAC_L2_STO_L_VOL_SFT 11 +#define RT5651_M_DAC_R1_MIXL (0x1 << 9) +#define RT5651_M_DAC_R1_MIXL_SFT 9 +#define RT5651_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5651_DAC_R1_STO_L_VOL_SFT 8 +#define RT5651_M_DAC_R1_MIXR (0x1 << 6) +#define RT5651_M_DAC_R1_MIXR_SFT 6 +#define RT5651_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5651_DAC_R1_STO_R_VOL_SFT 5 +#define RT5651_M_DAC_R2_MIXR (0x1 << 4) +#define RT5651_M_DAC_R2_MIXR_SFT 4 +#define RT5651_DAC_R2_STO_R_VOL_MASK (0x1 << 3) +#define RT5651_DAC_R2_STO_R_VOL_SFT 3 +#define RT5651_M_DAC_L1_MIXR (0x1 << 1) +#define RT5651_M_DAC_L1_MIXR_SFT 1 +#define RT5651_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5651_DAC_L1_STO_R_VOL_SFT 0 + +/* DD Mixer Control (0x2b) */ +#define RT5651_M_STO_DD_L1 (0x1 << 14) +#define RT5651_M_STO_DD_L1_SFT 14 +#define RT5651_STO_DD_L1_VOL_MASK (0x1 << 13) +#define RT5651_DAC_DD_L1_VOL_SFT 13 +#define RT5651_M_STO_DD_L2 (0x1 << 12) +#define RT5651_M_STO_DD_L2_SFT 12 +#define RT5651_STO_DD_L2_VOL_MASK (0x1 << 11) +#define RT5651_STO_DD_L2_VOL_SFT 11 +#define RT5651_M_STO_DD_R2_L (0x1 << 10) +#define RT5651_M_STO_DD_R2_L_SFT 10 +#define RT5651_STO_DD_R2_L_VOL_MASK (0x1 << 9) +#define RT5651_STO_DD_R2_L_VOL_SFT 9 +#define RT5651_M_STO_DD_R1 (0x1 << 6) +#define RT5651_M_STO_DD_R1_SFT 6 +#define RT5651_STO_DD_R1_VOL_MASK (0x1 << 5) +#define RT5651_STO_DD_R1_VOL_SFT 5 +#define RT5651_M_STO_DD_R2 (0x1 << 4) +#define RT5651_M_STO_DD_R2_SFT 4 +#define RT5651_STO_DD_R2_VOL_MASK (0x1 << 3) +#define RT5651_STO_DD_R2_VOL_SFT 3 +#define RT5651_M_STO_DD_L2_R (0x1 << 2) +#define RT5651_M_STO_DD_L2_R_SFT 2 +#define RT5651_STO_DD_L2_R_VOL_MASK (0x1 << 1) +#define RT5651_STO_DD_L2_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5651_M_STO_L_DAC_L (0x1 << 15) +#define RT5651_M_STO_L_DAC_L_SFT 15 +#define RT5651_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5651_STO_L_DAC_L_VOL_SFT 14 +#define RT5651_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5651_M_DAC_L2_DAC_L_SFT 13 +#define RT5651_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5651_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5651_M_STO_R_DAC_R (0x1 << 11) +#define RT5651_M_STO_R_DAC_R_SFT 11 +#define RT5651_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5651_STO_R_DAC_R_VOL_SFT 10 +#define RT5651_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5651_M_DAC_R2_DAC_R_SFT 9 +#define RT5651_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5651_DAC_R2_DAC_R_VOL_SFT 8 + +/* DSP Path Control 1 (0x2d) */ +#define RT5651_RXDP_SRC_MASK (0x1 << 15) +#define RT5651_RXDP_SRC_SFT 15 +#define RT5651_RXDP_SRC_NOR (0x0 << 15) +#define RT5651_RXDP_SRC_DIV3 (0x1 << 15) +#define RT5651_TXDP_SRC_MASK (0x1 << 14) +#define RT5651_TXDP_SRC_SFT 14 +#define RT5651_TXDP_SRC_NOR (0x0 << 14) +#define RT5651_TXDP_SRC_DIV3 (0x1 << 14) + +/* DSP Path Control 2 (0x2e) */ +#define RT5651_DAC_L2_SEL_MASK (0x3 << 14) +#define RT5651_DAC_L2_SEL_SFT 14 +#define RT5651_DAC_L2_SEL_IF2 (0x0 << 14) +#define RT5651_DAC_L2_SEL_IF3 (0x1 << 14) +#define RT5651_DAC_L2_SEL_TXDC (0x2 << 14) +#define RT5651_DAC_L2_SEL_BASS (0x3 << 14) +#define RT5651_DAC_R2_SEL_MASK (0x3 << 12) +#define RT5651_DAC_R2_SEL_SFT 12 +#define RT5651_DAC_R2_SEL_IF2 (0x0 << 12) +#define RT5651_DAC_R2_SEL_IF3 (0x1 << 12) +#define RT5651_DAC_R2_SEL_TXDC (0x2 << 12) +#define RT5651_IF2_ADC_L_SEL_MASK (0x1 << 11) +#define RT5651_IF2_ADC_L_SEL_SFT 11 +#define RT5651_IF2_ADC_L_SEL_TXDP (0x0 << 11) +#define RT5651_IF2_ADC_L_SEL_PASS (0x1 << 11) +#define RT5651_IF2_ADC_R_SEL_MASK (0x1 << 10) +#define RT5651_IF2_ADC_R_SEL_SFT 10 +#define RT5651_IF2_ADC_R_SEL_TXDP (0x0 << 10) +#define RT5651_IF2_ADC_R_SEL_PASS (0x1 << 10) +#define RT5651_RXDC_SEL_MASK (0x3 << 8) +#define RT5651_RXDC_SEL_SFT 8 +#define RT5651_RXDC_SEL_NOR (0x0 << 8) +#define RT5651_RXDC_SEL_L2R (0x1 << 8) +#define RT5651_RXDC_SEL_R2L (0x2 << 8) +#define RT5651_RXDC_SEL_SWAP (0x3 << 8) +#define RT5651_RXDP_SEL_MASK (0x3 << 6) +#define RT5651_RXDP_SEL_SFT 6 +#define RT5651_RXDP_SEL_NOR (0x0 << 6) +#define RT5651_RXDP_SEL_L2R (0x1 << 6) +#define RT5651_RXDP_SEL_R2L (0x2 << 6) +#define RT5651_RXDP_SEL_SWAP (0x3 << 6) +#define RT5651_TXDC_SEL_MASK (0x3 << 4) +#define RT5651_TXDC_SEL_SFT 4 +#define RT5651_TXDC_SEL_NOR (0x0 << 4) +#define RT5651_TXDC_SEL_L2R (0x1 << 4) +#define RT5651_TXDC_SEL_R2L (0x2 << 4) +#define RT5651_TXDC_SEL_SWAP (0x3 << 4) +#define RT5651_TXDP_SEL_MASK (0x3 << 2) +#define RT5651_TXDP_SEL_SFT 2 +#define RT5651_TXDP_SEL_NOR (0x0 << 2) +#define RT5651_TXDP_SEL_L2R (0x1 << 2) +#define RT5651_TXDP_SEL_R2L (0x2 << 2) +#define RT5651_TRXDP_SEL_SWAP (0x3 << 2) + +/* Digital Interface Data Control (0x2f) */ +#define RT5651_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5651_IF2_DAC_SEL_SFT 10 +#define RT5651_IF2_DAC_SEL_NOR (0x0 << 10) +#define RT5651_IF2_DAC_SEL_SWAP (0x1 << 10) +#define RT5651_IF2_DAC_SEL_L2R (0x2 << 10) +#define RT5651_IF2_DAC_SEL_R2L (0x3 << 10) +#define RT5651_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5651_IF2_ADC_SEL_SFT 8 +#define RT5651_IF2_ADC_SEL_NOR (0x0 << 8) +#define RT5651_IF2_ADC_SEL_SWAP (0x1 << 8) +#define RT5651_IF2_ADC_SEL_L2R (0x2 << 8) +#define RT5651_IF2_ADC_SEL_R2L (0x3 << 8) +#define RT5651_IF2_ADC_SRC_MASK (0x1 << 7) +#define RT5651_IF2_ADC_SRC_SFT 7 +#define RT5651_IF1_ADC1 (0x0 << 7) +#define RT5651_IF1_ADC2 (0x1 << 7) + +/* PDM Output Control (0x30) */ +#define RT5651_PDM_L_SEL_MASK (0x1 << 15) +#define RT5651_PDM_L_SEL_SFT 15 +#define RT5651_PDM_L_SEL_DD_L (0x0 << 15) +#define RT5651_PDM_L_SEL_STO_L (0x1 << 15) +#define RT5651_M_PDM_L (0x1 << 14) +#define RT5651_M_PDM_L_SFT 14 +#define RT5651_PDM_R_SEL_MASK (0x1 << 13) +#define RT5651_PDM_R_SEL_SFT 13 +#define RT5651_PDM_R_SEL_DD_L (0x0 << 13) +#define RT5651_PDM_R_SEL_STO_L (0x1 << 13) +#define RT5651_M_PDM_R (0x1 << 12) +#define RT5651_M_PDM_R_SFT 12 +#define RT5651_PDM_BUSY (0x1 << 6) +#define RT5651_PDM_BUSY_SFT 6 +#define RT5651_PDM_PATTERN_SEL_MASK (0x1 << 5) +#define RT5651_PDM_PATTERN_SEL_64 (0x0 << 5) +#define RT5651_PDM_PATTERN_SEL_128 (0x1 << 5) +#define RT5651_PDM_VOL_MASK (0x1 << 4) +#define RT5651_PDM_VOL_SFT 4 +#define RT5651_PDM_DIV_MASK (0x3) +#define RT5651_PDM_DIV_SFT 0 +#define RT5651_PDM_DIV_1 0 +#define RT5651_PDM_DIV_2 1 +#define RT5651_PDM_DIV_3 2 +#define RT5651_PDM_DIV_4 3 + +/* PDM I2C/Data Control 1 (0x31) */ +#define RT5651_PDM_I2C_ID_MASK (0xf << 12) +#define PT5631_PDM_CMD_EXE (0x1 << 11) +#define RT5651_PDM_I2C_CMD_MASK (0x1 << 10) +#define RT5651_PDM_I2C_CMD_R (0x0 << 10) +#define RT5651_PDM_I2C_CMD_W (0x1 << 10) +#define RT5651_PDM_I2C_CMD_EXE (0x1 << 9) +#define RT5651_PDM_I2C_NORMAL (0x0 << 8) +#define RT5651_PDM_I2C_BUSY (0x1 << 8) + +/* PDM I2C/Data Control 2 (0x32) */ +#define RT5651_PDM_I2C_ADDR (0xff << 8) +#define RT5651_PDM_I2C_CMD_PATTERN (0xff) + + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5651_G_LN_L2_RM_L_MASK (0x7 << 13) +#define RT5651_G_IN_L2_RM_L_SFT 13 +#define RT5651_G_LN_L1_RM_L_MASK (0x7 << 10) +#define RT5651_G_IN_L1_RM_L_SFT 10 +#define RT5651_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5651_G_BST3_RM_L_SFT 4 +#define RT5651_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5651_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5651_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5651_G_BST1_RM_L_SFT 13 +#define RT5651_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5651_G_OM_L_RM_L_SFT 10 +#define RT5651_M_IN2_L_RM_L (0x1 << 6) +#define RT5651_M_IN2_L_RM_L_SFT 6 +#define RT5651_M_IN1_L_RM_L (0x1 << 5) +#define RT5651_M_IN1_L_RM_L_SFT 5 +#define RT5651_M_BST3_RM_L (0x1 << 3) +#define RT5651_M_BST3_RM_L_SFT 3 +#define RT5651_M_BST2_RM_L (0x1 << 2) +#define RT5651_M_BST2_RM_L_SFT 2 +#define RT5651_M_BST1_RM_L (0x1 << 1) +#define RT5651_M_BST1_RM_L_SFT 1 +#define RT5651_M_OM_L_RM_L (0x1) +#define RT5651_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5651_G_IN2_R_RM_R_MASK (0x7 << 13) +#define RT5651_G_IN2_R_RM_R_SFT 13 +#define RT5651_G_IN1_R_RM_R_MASK (0x7 << 10) +#define RT5651_G_IN1_R_RM_R_SFT 10 +#define RT5651_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5651_G_BST3_RM_R_SFT 4 +#define RT5651_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5651_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5651_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5651_G_BST1_RM_R_SFT 13 +#define RT5651_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5651_G_OM_R_RM_R_SFT 10 +#define RT5651_M_IN2_R_RM_R (0x1 << 6) +#define RT5651_M_IN2_R_RM_R_SFT 6 +#define RT5651_M_IN1_R_RM_R (0x1 << 5) +#define RT5651_M_IN1_R_RM_R_SFT 5 +#define RT5651_M_BST3_RM_R (0x1 << 3) +#define RT5651_M_BST3_RM_R_SFT 3 +#define RT5651_M_BST2_RM_R (0x1 << 2) +#define RT5651_M_BST2_RM_R_SFT 2 +#define RT5651_M_BST1_RM_R (0x1 << 1) +#define RT5651_M_BST1_RM_R_SFT 1 +#define RT5651_M_OM_R_RM_R (0x1) +#define RT5651_M_OM_R_RM_R_SFT 0 + +/* HPMIX Control (0x45) */ +#define RT5651_M_DAC1_HM (0x1 << 14) +#define RT5651_M_DAC1_HM_SFT 14 +#define RT5651_M_HPVOL_HM (0x1 << 13) +#define RT5651_M_HPVOL_HM_SFT 13 +#define RT5651_G_HPOMIX_MASK (0x1 << 12) +#define RT5651_G_HPOMIX_SFT 12 + +/* SPK Left Mixer Control (0x46) */ +#define RT5651_G_RM_L_SM_L_MASK (0x3 << 14) +#define RT5651_G_RM_L_SM_L_SFT 14 +#define RT5651_G_IN_L_SM_L_MASK (0x3 << 12) +#define RT5651_G_IN_L_SM_L_SFT 12 +#define RT5651_G_DAC_L1_SM_L_MASK (0x3 << 10) +#define RT5651_G_DAC_L1_SM_L_SFT 10 +#define RT5651_G_DAC_L2_SM_L_MASK (0x3 << 8) +#define RT5651_G_DAC_L2_SM_L_SFT 8 +#define RT5651_G_OM_L_SM_L_MASK (0x3 << 6) +#define RT5651_G_OM_L_SM_L_SFT 6 +#define RT5651_M_RM_L_SM_L (0x1 << 5) +#define RT5651_M_RM_L_SM_L_SFT 5 +#define RT5651_M_IN_L_SM_L (0x1 << 4) +#define RT5651_M_IN_L_SM_L_SFT 4 +#define RT5651_M_DAC_L1_SM_L (0x1 << 3) +#define RT5651_M_DAC_L1_SM_L_SFT 3 +#define RT5651_M_DAC_L2_SM_L (0x1 << 2) +#define RT5651_M_DAC_L2_SM_L_SFT 2 +#define RT5651_M_OM_L_SM_L (0x1 << 1) +#define RT5651_M_OM_L_SM_L_SFT 1 + +/* SPK Right Mixer Control (0x47) */ +#define RT5651_G_RM_R_SM_R_MASK (0x3 << 14) +#define RT5651_G_RM_R_SM_R_SFT 14 +#define RT5651_G_IN_R_SM_R_MASK (0x3 << 12) +#define RT5651_G_IN_R_SM_R_SFT 12 +#define RT5651_G_DAC_R1_SM_R_MASK (0x3 << 10) +#define RT5651_G_DAC_R1_SM_R_SFT 10 +#define RT5651_G_DAC_R2_SM_R_MASK (0x3 << 8) +#define RT5651_G_DAC_R2_SM_R_SFT 8 +#define RT5651_G_OM_R_SM_R_MASK (0x3 << 6) +#define RT5651_G_OM_R_SM_R_SFT 6 +#define RT5651_M_RM_R_SM_R (0x1 << 5) +#define RT5651_M_RM_R_SM_R_SFT 5 +#define RT5651_M_IN_R_SM_R (0x1 << 4) +#define RT5651_M_IN_R_SM_R_SFT 4 +#define RT5651_M_DAC_R1_SM_R (0x1 << 3) +#define RT5651_M_DAC_R1_SM_R_SFT 3 +#define RT5651_M_DAC_R2_SM_R (0x1 << 2) +#define RT5651_M_DAC_R2_SM_R_SFT 2 +#define RT5651_M_OM_R_SM_R (0x1 << 1) +#define RT5651_M_OM_R_SM_R_SFT 1 + +/* SPOLMIX Control (0x48) */ +#define RT5651_M_DAC_R1_SPM_L (0x1 << 15) +#define RT5651_M_DAC_R1_SPM_L_SFT 15 +#define RT5651_M_DAC_L1_SPM_L (0x1 << 14) +#define RT5651_M_DAC_L1_SPM_L_SFT 14 +#define RT5651_M_SV_R_SPM_L (0x1 << 13) +#define RT5651_M_SV_R_SPM_L_SFT 13 +#define RT5651_M_SV_L_SPM_L (0x1 << 12) +#define RT5651_M_SV_L_SPM_L_SFT 12 +#define RT5651_M_BST1_SPM_L (0x1 << 11) +#define RT5651_M_BST1_SPM_L_SFT 11 + +/* SPORMIX Control (0x49) */ +#define RT5651_M_DAC_R1_SPM_R (0x1 << 13) +#define RT5651_M_DAC_R1_SPM_R_SFT 13 +#define RT5651_M_SV_R_SPM_R (0x1 << 12) +#define RT5651_M_SV_R_SPM_R_SFT 12 +#define RT5651_M_BST1_SPM_R (0x1 << 11) +#define RT5651_M_BST1_SPM_R_SFT 11 + +/* SPOLMIX / SPORMIX Ratio Control (0x4a) */ +#define RT5651_SPO_CLSD_RATIO_MASK (0x7) +#define RT5651_SPO_CLSD_RATIO_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5651_M_DAC_R2_MM (0x1 << 15) +#define RT5651_M_DAC_R2_MM_SFT 15 +#define RT5651_M_DAC_L2_MM (0x1 << 14) +#define RT5651_M_DAC_L2_MM_SFT 14 +#define RT5651_M_OV_R_MM (0x1 << 13) +#define RT5651_M_OV_R_MM_SFT 13 +#define RT5651_M_OV_L_MM (0x1 << 12) +#define RT5651_M_OV_L_MM_SFT 12 +#define RT5651_M_BST1_MM (0x1 << 11) +#define RT5651_M_BST1_MM_SFT 11 +#define RT5651_G_MONOMIX_MASK (0x1 << 10) +#define RT5651_G_MONOMIX_SFT 10 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5651_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5651_G_BST2_OM_L_SFT 10 +#define RT5651_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5651_G_BST1_OM_L_SFT 7 +#define RT5651_G_IN1_L_OM_L_MASK (0x7 << 4) +#define RT5651_G_IN1_L_OM_L_SFT 4 +#define RT5651_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5651_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5651_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5651_G_DAC_L1_OM_L_SFT 7 +#define RT5651_G_IN2_L_OM_L_MASK (0x7 << 4) +#define RT5651_G_IN2_L_OM_L_SFT 4 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5651_M_IN2_L_OM_L (0x1 << 9) +#define RT5651_M_IN2_L_OM_L_SFT 9 +#define RT5651_M_BST2_OM_L (0x1 << 6) +#define RT5651_M_BST2_OM_L_SFT 6 +#define RT5651_M_BST1_OM_L (0x1 << 5) +#define RT5651_M_BST1_OM_L_SFT 5 +#define RT5651_M_IN1_L_OM_L (0x1 << 4) +#define RT5651_M_IN1_L_OM_L_SFT 4 +#define RT5651_M_RM_L_OM_L (0x1 << 3) +#define RT5651_M_RM_L_OM_L_SFT 3 +#define RT5651_M_DAC_L1_OM_L (0x1) +#define RT5651_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5651_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5651_G_BST2_OM_R_SFT 10 +#define RT5651_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5651_G_BST1_OM_R_SFT 7 +#define RT5651_G_IN1_R_OM_R_MASK (0x7 << 4) +#define RT5651_G_IN1_R_OM_R_SFT 4 +#define RT5651_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5651_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5651_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5651_G_DAC_R1_OM_R_SFT 7 +#define RT5651_G_IN2_R_OM_R_MASK (0x7 << 4) +#define RT5651_G_IN2_R_OM_R_SFT 4 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5651_M_IN2_R_OM_R (0x1 << 9) +#define RT5651_M_IN2_R_OM_R_SFT 9 +#define RT5651_M_BST2_OM_R (0x1 << 6) +#define RT5651_M_BST2_OM_R_SFT 6 +#define RT5651_M_BST1_OM_R (0x1 << 5) +#define RT5651_M_BST1_OM_R_SFT 5 +#define RT5651_M_IN1_R_OM_R (0x1 << 4) +#define RT5651_M_IN1_R_OM_R_SFT 4 +#define RT5651_M_RM_R_OM_R (0x1 << 3) +#define RT5651_M_RM_R_OM_R_SFT 3 +#define RT5651_M_DAC_R1_OM_R (0x1) +#define RT5651_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5651_M_DAC_L1_LM (0x1 << 15) +#define RT5651_M_DAC_L1_LM_SFT 15 +#define RT5651_M_DAC_R1_LM (0x1 << 14) +#define RT5651_M_DAC_R1_LM_SFT 14 +#define RT5651_M_OV_L_LM (0x1 << 13) +#define RT5651_M_OV_L_LM_SFT 13 +#define RT5651_M_OV_R_LM (0x1 << 12) +#define RT5651_M_OV_R_LM_SFT 12 +#define RT5651_G_LOUTMIX_MASK (0x1 << 11) +#define RT5651_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5651_PWR_I2S1 (0x1 << 15) +#define RT5651_PWR_I2S1_BIT 15 +#define RT5651_PWR_I2S2 (0x1 << 14) +#define RT5651_PWR_I2S2_BIT 14 +#define RT5651_PWR_DAC_L1 (0x1 << 12) +#define RT5651_PWR_DAC_L1_BIT 12 +#define RT5651_PWR_DAC_R1 (0x1 << 11) +#define RT5651_PWR_DAC_R1_BIT 11 +#define RT5651_PWR_ADC_L (0x1 << 2) +#define RT5651_PWR_ADC_L_BIT 2 +#define RT5651_PWR_ADC_R (0x1 << 1) +#define RT5651_PWR_ADC_R_BIT 1 + +/* Power Management for Digital 2 (0x62) */ +#define RT5651_PWR_ADC_STO1_F (0x1 << 15) +#define RT5651_PWR_ADC_STO1_F_BIT 15 +#define RT5651_PWR_ADC_STO2_F (0x1 << 14) +#define RT5651_PWR_ADC_STO2_F_BIT 14 +#define RT5651_PWR_DAC_STO1_F (0x1 << 11) +#define RT5651_PWR_DAC_STO1_F_BIT 11 +#define RT5651_PWR_DAC_STO2_F (0x1 << 10) +#define RT5651_PWR_DAC_STO2_F_BIT 10 +#define RT5651_PWR_PDM (0x1 << 9) +#define RT5651_PWR_PDM_BIT 9 + +/* Power Management for Analog 1 (0x63) */ +#define RT5651_PWR_VREF1 (0x1 << 15) +#define RT5651_PWR_VREF1_BIT 15 +#define RT5651_PWR_FV1 (0x1 << 14) +#define RT5651_PWR_FV1_BIT 14 +#define RT5651_PWR_MB (0x1 << 13) +#define RT5651_PWR_MB_BIT 13 +#define RT5651_PWR_LM (0x1 << 12) +#define RT5651_PWR_LM_BIT 12 +#define RT5651_PWR_BG (0x1 << 11) +#define RT5651_PWR_BG_BIT 11 +#define RT5651_PWR_HP_L (0x1 << 7) +#define RT5651_PWR_HP_L_BIT 7 +#define RT5651_PWR_HP_R (0x1 << 6) +#define RT5651_PWR_HP_R_BIT 6 +#define RT5651_PWR_HA (0x1 << 5) +#define RT5651_PWR_HA_BIT 5 +#define RT5651_PWR_VREF2 (0x1 << 4) +#define RT5651_PWR_VREF2_BIT 4 +#define RT5651_PWR_FV2 (0x1 << 3) +#define RT5651_PWR_FV2_BIT 3 +#define RT5651_PWR_LDO (0x1 << 2) +#define RT5651_PWR_LDO_BIT 2 +#define RT5651_PWR_LDO_DVO_MASK (0x3) +#define RT5651_PWR_LDO_DVO_1_0V 0 +#define RT5651_PWR_LDO_DVO_1_1V 1 +#define RT5651_PWR_LDO_DVO_1_2V 2 +#define RT5651_PWR_LDO_DVO_1_3V 3 + +/* Power Management for Analog 2 (0x64) */ +#define RT5651_PWR_BST1 (0x1 << 15) +#define RT5651_PWR_BST1_BIT 15 +#define RT5651_PWR_BST2 (0x1 << 14) +#define RT5651_PWR_BST2_BIT 14 +#define RT5651_PWR_BST3 (0x1 << 13) +#define RT5651_PWR_BST3_BIT 13 +#define RT5651_PWR_MB1 (0x1 << 11) +#define RT5651_PWR_MB1_BIT 11 +#define RT5651_PWR_PLL (0x1 << 9) +#define RT5651_PWR_PLL_BIT 9 +#define RT5651_PWR_BST1_OP2 (0x1 << 5) +#define RT5651_PWR_BST1_OP2_BIT 5 +#define RT5651_PWR_BST2_OP2 (0x1 << 4) +#define RT5651_PWR_BST2_OP2_BIT 4 +#define RT5651_PWR_BST3_OP2 (0x1 << 3) +#define RT5651_PWR_BST3_OP2_BIT 3 +#define RT5651_PWR_JD_M (0x1 << 2) +#define RT5651_PWM_JD_M_BIT 2 +#define RT5651_PWR_JD2 (0x1 << 1) +#define RT5651_PWM_JD2_BIT 1 +#define RT5651_PWR_JD3 (0x1) +#define RT5651_PWM_JD3_BIT 0 + +/* Power Management for Mixer (0x65) */ +#define RT5651_PWR_OM_L (0x1 << 15) +#define RT5651_PWR_OM_L_BIT 15 +#define RT5651_PWR_OM_R (0x1 << 14) +#define RT5651_PWR_OM_R_BIT 14 +#define RT5651_PWR_RM_L (0x1 << 11) +#define RT5651_PWR_RM_L_BIT 11 +#define RT5651_PWR_RM_R (0x1 << 10) +#define RT5651_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5651_PWR_OV_L (0x1 << 13) +#define RT5651_PWR_OV_L_BIT 13 +#define RT5651_PWR_OV_R (0x1 << 12) +#define RT5651_PWR_OV_R_BIT 12 +#define RT5651_PWR_HV_L (0x1 << 11) +#define RT5651_PWR_HV_L_BIT 11 +#define RT5651_PWR_HV_R (0x1 << 10) +#define RT5651_PWR_HV_R_BIT 10 +#define RT5651_PWR_IN1_L (0x1 << 9) +#define RT5651_PWR_IN1_L_BIT 9 +#define RT5651_PWR_IN1_R (0x1 << 8) +#define RT5651_PWR_IN1_R_BIT 8 +#define RT5651_PWR_IN2_L (0x1 << 7) +#define RT5651_PWR_IN2_L_BIT 7 +#define RT5651_PWR_IN2_R (0x1 << 6) +#define RT5651_PWR_IN2_R_BIT 6 + +/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71) */ +#define RT5651_I2S_MS_MASK (0x1 << 15) +#define RT5651_I2S_MS_SFT 15 +#define RT5651_I2S_MS_M (0x0 << 15) +#define RT5651_I2S_MS_S (0x1 << 15) +#define RT5651_I2S_O_CP_MASK (0x3 << 10) +#define RT5651_I2S_O_CP_SFT 10 +#define RT5651_I2S_O_CP_OFF (0x0 << 10) +#define RT5651_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5651_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5651_I2S_I_CP_MASK (0x3 << 8) +#define RT5651_I2S_I_CP_SFT 8 +#define RT5651_I2S_I_CP_OFF (0x0 << 8) +#define RT5651_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5651_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5651_I2S_BP_MASK (0x1 << 7) +#define RT5651_I2S_BP_SFT 7 +#define RT5651_I2S_BP_NOR (0x0 << 7) +#define RT5651_I2S_BP_INV (0x1 << 7) +#define RT5651_I2S_DL_MASK (0x3 << 2) +#define RT5651_I2S_DL_SFT 2 +#define RT5651_I2S_DL_16 (0x0 << 2) +#define RT5651_I2S_DL_20 (0x1 << 2) +#define RT5651_I2S_DL_24 (0x2 << 2) +#define RT5651_I2S_DL_8 (0x3 << 2) +#define RT5651_I2S_DF_MASK (0x3) +#define RT5651_I2S_DF_SFT 0 +#define RT5651_I2S_DF_I2S (0x0) +#define RT5651_I2S_DF_LEFT (0x1) +#define RT5651_I2S_DF_PCM_A (0x2) +#define RT5651_I2S_DF_PCM_B (0x3) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5651_I2S_PD1_MASK (0x7 << 12) +#define RT5651_I2S_PD1_SFT 12 +#define RT5651_I2S_PD1_1 (0x0 << 12) +#define RT5651_I2S_PD1_2 (0x1 << 12) +#define RT5651_I2S_PD1_3 (0x2 << 12) +#define RT5651_I2S_PD1_4 (0x3 << 12) +#define RT5651_I2S_PD1_6 (0x4 << 12) +#define RT5651_I2S_PD1_8 (0x5 << 12) +#define RT5651_I2S_PD1_12 (0x6 << 12) +#define RT5651_I2S_PD1_16 (0x7 << 12) +#define RT5651_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5651_I2S_BCLK_MS2_SFT 11 +#define RT5651_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5651_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5651_I2S_PD2_MASK (0x7 << 8) +#define RT5651_I2S_PD2_SFT 8 +#define RT5651_I2S_PD2_1 (0x0 << 8) +#define RT5651_I2S_PD2_2 (0x1 << 8) +#define RT5651_I2S_PD2_3 (0x2 << 8) +#define RT5651_I2S_PD2_4 (0x3 << 8) +#define RT5651_I2S_PD2_6 (0x4 << 8) +#define RT5651_I2S_PD2_8 (0x5 << 8) +#define RT5651_I2S_PD2_12 (0x6 << 8) +#define RT5651_I2S_PD2_16 (0x7 << 8) +#define RT5651_DAC_OSR_MASK (0x3 << 2) +#define RT5651_DAC_OSR_SFT 2 +#define RT5651_DAC_OSR_128 (0x0 << 2) +#define RT5651_DAC_OSR_64 (0x1 << 2) +#define RT5651_DAC_OSR_32 (0x2 << 2) +#define RT5651_DAC_OSR_128_3 (0x3 << 2) +#define RT5651_ADC_OSR_MASK (0x3) +#define RT5651_ADC_OSR_SFT 0 +#define RT5651_ADC_OSR_128 (0x0) +#define RT5651_ADC_OSR_64 (0x1) +#define RT5651_ADC_OSR_32 (0x2) +#define RT5651_ADC_OSR_128_3 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5651_DAHPF_EN (0x1 << 11) +#define RT5651_DAHPF_EN_SFT 11 +#define RT5651_ADHPF_EN (0x1 << 10) +#define RT5651_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5651_DMIC_1_EN_MASK (0x1 << 15) +#define RT5651_DMIC_1_EN_SFT 15 +#define RT5651_DMIC_1_DIS (0x0 << 15) +#define RT5651_DMIC_1_EN (0x1 << 15) +#define RT5651_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5651_DMIC_1L_LH_SFT 13 +#define RT5651_DMIC_1L_LH_FALLING (0x0 << 13) +#define RT5651_DMIC_1L_LH_RISING (0x1 << 13) +#define RT5651_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5651_DMIC_1R_LH_SFT 12 +#define RT5651_DMIC_1R_LH_FALLING (0x0 << 12) +#define RT5651_DMIC_1R_LH_RISING (0x1 << 12) +#define RT5651_DMIC_1_DP_MASK (0x3 << 10) +#define RT5651_DMIC_1_DP_SFT 10 +#define RT5651_DMIC_1_DP_GPIO6 (0x0 << 10) +#define RT5651_DMIC_1_DP_IN1P (0x1 << 10) +#define RT5651_DMIC_2_DP_GPIO8 (0x2 << 10) +#define RT5651_DMIC_CLK_MASK (0x7 << 5) +#define RT5651_DMIC_CLK_SFT 5 + +/* TDM Control 1 (0x77) */ +#define RT5651_TDM_INTEL_SEL_MASK (0x1 << 15) +#define RT5651_TDM_INTEL_SEL_SFT 15 +#define RT5651_TDM_INTEL_SEL_64 (0x0 << 15) +#define RT5651_TDM_INTEL_SEL_50 (0x1 << 15) +#define RT5651_TDM_MODE_SEL_MASK (0x1 << 14) +#define RT5651_TDM_MODE_SEL_SFT 14 +#define RT5651_TDM_MODE_SEL_NOR (0x0 << 14) +#define RT5651_TDM_MODE_SEL_TDM (0x1 << 14) +#define RT5651_TDM_CH_NUM_SEL_MASK (0x3 << 12) +#define RT5651_TDM_CH_NUM_SEL_SFT 12 +#define RT5651_TDM_CH_NUM_SEL_2 (0x0 << 12) +#define RT5651_TDM_CH_NUM_SEL_4 (0x1 << 12) +#define RT5651_TDM_CH_NUM_SEL_6 (0x2 << 12) +#define RT5651_TDM_CH_NUM_SEL_8 (0x3 << 12) +#define RT5651_TDM_CH_LEN_SEL_MASK (0x3 << 10) +#define RT5651_TDM_CH_LEN_SEL_SFT 10 +#define RT5651_TDM_CH_LEN_SEL_16 (0x0 << 10) +#define RT5651_TDM_CH_LEN_SEL_20 (0x1 << 10) +#define RT5651_TDM_CH_LEN_SEL_24 (0x2 << 10) +#define RT5651_TDM_CH_LEN_SEL_32 (0x3 << 10) +#define RT5651_TDM_ADC_SEL_MASK (0x1 << 9) +#define RT5651_TDM_ADC_SEL_SFT 9 +#define RT5651_TDM_ADC_SEL_NOR (0x0 << 9) +#define RT5651_TDM_ADC_SEL_SWAP (0x1 << 9) +#define RT5651_TDM_ADC_START_SEL_MASK (0x1 << 8) +#define RT5651_TDM_ADC_START_SEL_SFT 8 +#define RT5651_TDM_ADC_START_SEL_SL0 (0x0 << 8) +#define RT5651_TDM_ADC_START_SEL_SL4 (0x1 << 8) +#define RT5651_TDM_I2S_CH2_SEL_MASK (0x3 << 6) +#define RT5651_TDM_I2S_CH2_SEL_SFT 6 +#define RT5651_TDM_I2S_CH2_SEL_LR (0x0 << 6) +#define RT5651_TDM_I2S_CH2_SEL_RL (0x1 << 6) +#define RT5651_TDM_I2S_CH2_SEL_LL (0x2 << 6) +#define RT5651_TDM_I2S_CH2_SEL_RR (0x3 << 6) +#define RT5651_TDM_I2S_CH4_SEL_MASK (0x3 << 4) +#define RT5651_TDM_I2S_CH4_SEL_SFT 4 +#define RT5651_TDM_I2S_CH4_SEL_LR (0x0 << 4) +#define RT5651_TDM_I2S_CH4_SEL_RL (0x1 << 4) +#define RT5651_TDM_I2S_CH4_SEL_LL (0x2 << 4) +#define RT5651_TDM_I2S_CH4_SEL_RR (0x3 << 4) +#define RT5651_TDM_I2S_CH6_SEL_MASK (0x3 << 2) +#define RT5651_TDM_I2S_CH6_SEL_SFT 2 +#define RT5651_TDM_I2S_CH6_SEL_LR (0x0 << 2) +#define RT5651_TDM_I2S_CH6_SEL_RL (0x1 << 2) +#define RT5651_TDM_I2S_CH6_SEL_LL (0x2 << 2) +#define RT5651_TDM_I2S_CH6_SEL_RR (0x3 << 2) +#define RT5651_TDM_I2S_CH8_SEL_MASK (0x3) +#define RT5651_TDM_I2S_CH8_SEL_SFT 0 +#define RT5651_TDM_I2S_CH8_SEL_LR (0x0) +#define RT5651_TDM_I2S_CH8_SEL_RL (0x1) +#define RT5651_TDM_I2S_CH8_SEL_LL (0x2) +#define RT5651_TDM_I2S_CH8_SEL_RR (0x3) + +/* TDM Control 2 (0x78) */ +#define RT5651_TDM_LRCK_POL_SEL_MASK (0x1 << 15) +#define RT5651_TDM_LRCK_POL_SEL_SFT 15 +#define RT5651_TDM_LRCK_POL_SEL_NOR (0x0 << 15) +#define RT5651_TDM_LRCK_POL_SEL_INV (0x1 << 15) +#define RT5651_TDM_CH_VAL_SEL_MASK (0x1 << 14) +#define RT5651_TDM_CH_VAL_SEL_SFT 14 +#define RT5651_TDM_CH_VAL_SEL_CH01 (0x0 << 14) +#define RT5651_TDM_CH_VAL_SEL_CH0123 (0x1 << 14) +#define RT5651_TDM_CH_VAL_EN (0x1 << 13) +#define RT5651_TDM_CH_VAL_SFT 13 +#define RT5651_TDM_LPBK_EN (0x1 << 12) +#define RT5651_TDM_LPBK_SFT 12 +#define RT5651_TDM_LRCK_PULSE_SEL_MASK (0x1 << 11) +#define RT5651_TDM_LRCK_PULSE_SEL_SFT 11 +#define RT5651_TDM_LRCK_PULSE_SEL_BCLK (0x0 << 11) +#define RT5651_TDM_LRCK_PULSE_SEL_CH (0x1 << 11) +#define RT5651_TDM_END_EDGE_SEL_MASK (0x1 << 10) +#define RT5651_TDM_END_EDGE_SEL_SFT 10 +#define RT5651_TDM_END_EDGE_SEL_POS (0x0 << 10) +#define RT5651_TDM_END_EDGE_SEL_NEG (0x1 << 10) +#define RT5651_TDM_END_EDGE_EN (0x1 << 9) +#define RT5651_TDM_END_EDGE_EN_SFT 9 +#define RT5651_TDM_TRAN_EDGE_SEL_MASK (0x1 << 8) +#define RT5651_TDM_TRAN_EDGE_SEL_SFT 8 +#define RT5651_TDM_TRAN_EDGE_SEL_POS (0x0 << 8) +#define RT5651_TDM_TRAN_EDGE_SEL_NEG (0x1 << 8) +#define RT5651_M_TDM2_L (0x1 << 7) +#define RT5651_M_TDM2_L_SFT 7 +#define RT5651_M_TDM2_R (0x1 << 6) +#define RT5651_M_TDM2_R_SFT 6 +#define RT5651_M_TDM4_L (0x1 << 5) +#define RT5651_M_TDM4_L_SFT 5 +#define RT5651_M_TDM4_R (0x1 << 4) +#define RT5651_M_TDM4_R_SFT 4 + +/* TDM Control 3 (0x79) */ +#define RT5651_CH2_L_SEL_MASK (0x7 << 12) +#define RT5651_CH2_L_SEL_SFT 12 +#define RT5651_CH2_L_SEL_SL0 (0x0 << 12) +#define RT5651_CH2_L_SEL_SL1 (0x1 << 12) +#define RT5651_CH2_L_SEL_SL2 (0x2 << 12) +#define RT5651_CH2_L_SEL_SL3 (0x3 << 12) +#define RT5651_CH2_L_SEL_SL4 (0x4 << 12) +#define RT5651_CH2_L_SEL_SL5 (0x5 << 12) +#define RT5651_CH2_L_SEL_SL6 (0x6 << 12) +#define RT5651_CH2_L_SEL_SL7 (0x7 << 12) +#define RT5651_CH2_R_SEL_MASK (0x7 << 8) +#define RT5651_CH2_R_SEL_SFT 8 +#define RT5651_CH2_R_SEL_SL0 (0x0 << 8) +#define RT5651_CH2_R_SEL_SL1 (0x1 << 8) +#define RT5651_CH2_R_SEL_SL2 (0x2 << 8) +#define RT5651_CH2_R_SEL_SL3 (0x3 << 8) +#define RT5651_CH2_R_SEL_SL4 (0x4 << 8) +#define RT5651_CH2_R_SEL_SL5 (0x5 << 8) +#define RT5651_CH2_R_SEL_SL6 (0x6 << 8) +#define RT5651_CH2_R_SEL_SL7 (0x7 << 8) +#define RT5651_CH4_L_SEL_MASK (0x7 << 4) +#define RT5651_CH4_L_SEL_SFT 4 +#define RT5651_CH4_L_SEL_SL0 (0x0 << 4) +#define RT5651_CH4_L_SEL_SL1 (0x1 << 4) +#define RT5651_CH4_L_SEL_SL2 (0x2 << 4) +#define RT5651_CH4_L_SEL_SL3 (0x3 << 4) +#define RT5651_CH4_L_SEL_SL4 (0x4 << 4) +#define RT5651_CH4_L_SEL_SL5 (0x5 << 4) +#define RT5651_CH4_L_SEL_SL6 (0x6 << 4) +#define RT5651_CH4_L_SEL_SL7 (0x7 << 4) +#define RT5651_CH4_R_SEL_MASK (0x7) +#define RT5651_CH4_R_SEL_SFT 0 +#define RT5651_CH4_R_SEL_SL0 (0x0) +#define RT5651_CH4_R_SEL_SL1 (0x1) +#define RT5651_CH4_R_SEL_SL2 (0x2) +#define RT5651_CH4_R_SEL_SL3 (0x3) +#define RT5651_CH4_R_SEL_SL4 (0x4) +#define RT5651_CH4_R_SEL_SL5 (0x5) +#define RT5651_CH4_R_SEL_SL6 (0x6) +#define RT5651_CH4_R_SEL_SL7 (0x7) + +/* Global Clock Control (0x80) */ +#define RT5651_SCLK_SRC_MASK (0x3 << 14) +#define RT5651_SCLK_SRC_SFT 14 +#define RT5651_SCLK_SRC_MCLK (0x0 << 14) +#define RT5651_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5651_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5651_PLL1_SRC_MASK (0x3 << 12) +#define RT5651_PLL1_SRC_SFT 12 +#define RT5651_PLL1_SRC_MCLK (0x0 << 12) +#define RT5651_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5651_PLL1_SRC_BCLK2 (0x2 << 12) +#define RT5651_PLL1_PD_MASK (0x1 << 3) +#define RT5651_PLL1_PD_SFT 3 +#define RT5651_PLL1_PD_1 (0x0 << 3) +#define RT5651_PLL1_PD_2 (0x1 << 3) + +#define RT5651_PLL_INP_MAX 40000000 +#define RT5651_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5651_PLL_N_MAX 0x1ff +#define RT5651_PLL_N_MASK (RT5651_PLL_N_MAX << 7) +#define RT5651_PLL_N_SFT 7 +#define RT5651_PLL_K_MAX 0x1f +#define RT5651_PLL_K_MASK (RT5651_PLL_K_MAX) +#define RT5651_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5651_PLL_M_MAX 0xf +#define RT5651_PLL_M_MASK (RT5651_PLL_M_MAX << 12) +#define RT5651_PLL_M_SFT 12 +#define RT5651_PLL_M_BP (0x1 << 11) +#define RT5651_PLL_M_BP_SFT 11 + +/* PLL tracking mode 1 (0x83) */ +#define RT5651_STO1_T_MASK (0x1 << 15) +#define RT5651_STO1_T_SFT 15 +#define RT5651_STO1_T_SCLK (0x0 << 15) +#define RT5651_STO1_T_LRCK1 (0x1 << 15) +#define RT5651_STO2_T_MASK (0x1 << 12) +#define RT5651_STO2_T_SFT 12 +#define RT5651_STO2_T_I2S2 (0x0 << 12) +#define RT5651_STO2_T_LRCK2 (0x1 << 12) +#define RT5651_ASRC2_REF_MASK (0x1 << 11) +#define RT5651_ASRC2_REF_SFT 11 +#define RT5651_ASRC2_REF_LRCK2 (0x0 << 11) +#define RT5651_ASRC2_REF_LRCK1 (0x1 << 11) +#define RT5651_DMIC_1_M_MASK (0x1 << 9) +#define RT5651_DMIC_1_M_SFT 9 +#define RT5651_DMIC_1_M_NOR (0x0 << 9) +#define RT5651_DMIC_1_M_ASYN (0x1 << 9) + +/* PLL tracking mode 2 (0x84) */ +#define RT5651_STO1_ASRC_EN (0x1 << 15) +#define RT5651_STO1_ASRC_EN_SFT 15 +#define RT5651_STO2_ASRC_EN (0x1 << 14) +#define RT5651_STO2_ASRC_EN_SFT 14 +#define RT5651_STO1_DAC_M_MASK (0x1 << 13) +#define RT5651_STO1_DAC_M_SFT 13 +#define RT5651_STO1_DAC_M_NOR (0x0 << 13) +#define RT5651_STO1_DAC_M_ASRC (0x1 << 13) +#define RT5651_STO2_DAC_M_MASK (0x1 << 12) +#define RT5651_STO2_DAC_M_SFT 12 +#define RT5651_STO2_DAC_M_NOR (0x0 << 12) +#define RT5651_STO2_DAC_M_ASRC (0x1 << 12) +#define RT5651_ADC_M_MASK (0x1 << 11) +#define RT5651_ADC_M_SFT 11 +#define RT5651_ADC_M_NOR (0x0 << 11) +#define RT5651_ADC_M_ASRC (0x1 << 11) +#define RT5651_I2S1_R_D_MASK (0x1 << 4) +#define RT5651_I2S1_R_D_SFT 4 +#define RT5651_I2S1_R_D_DIS (0x0 << 4) +#define RT5651_I2S1_R_D_EN (0x1 << 4) +#define RT5651_I2S2_R_D_MASK (0x1 << 3) +#define RT5651_I2S2_R_D_SFT 3 +#define RT5651_I2S2_R_D_DIS (0x0 << 3) +#define RT5651_I2S2_R_D_EN (0x1 << 3) +#define RT5651_PRE_SCLK_MASK (0x3) +#define RT5651_PRE_SCLK_SFT 0 +#define RT5651_PRE_SCLK_512 (0x0) +#define RT5651_PRE_SCLK_1024 (0x1) +#define RT5651_PRE_SCLK_2048 (0x2) + +/* PLL tracking mode 3 (0x85) */ +#define RT5651_I2S1_RATE_MASK (0xf << 12) +#define RT5651_I2S1_RATE_SFT 12 +#define RT5651_I2S2_RATE_MASK (0xf << 8) +#define RT5651_I2S2_RATE_SFT 8 +#define RT5651_G_ASRC_LP_MASK (0x1 << 3) +#define RT5651_G_ASRC_LP_SFT 3 +#define RT5651_ASRC_LP_F_M (0x1 << 2) +#define RT5651_ASRC_LP_F_SFT 2 +#define RT5651_ASRC_LP_F_NOR (0x0 << 2) +#define RT5651_ASRC_LP_F_SB (0x1 << 2) +#define RT5651_FTK_PH_DET_MASK (0x3) +#define RT5651_FTK_PH_DET_SFT 0 +#define RT5651_FTK_PH_DET_DIV1 (0x0) +#define RT5651_FTK_PH_DET_DIV2 (0x1) +#define RT5651_FTK_PH_DET_DIV4 (0x2) +#define RT5651_FTK_PH_DET_DIV8 (0x3) + +/*PLL tracking mode 6 (0x89) */ +#define RT5651_I2S1_PD_MASK (0x7 << 12) +#define RT5651_I2S1_PD_SFT 12 +#define RT5651_I2S2_PD_MASK (0x7 << 8) +#define RT5651_I2S2_PD_SFT 8 + +/*PLL tracking mode 7 (0x8a) */ +#define RT5651_FSI1_RATE_MASK (0xf << 12) +#define RT5651_FSI1_RATE_SFT 12 +#define RT5651_FSI2_RATE_MASK (0xf << 8) +#define RT5651_FSI2_RATE_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5651_HP_OVCD_MASK (0x1 << 10) +#define RT5651_HP_OVCD_SFT 10 +#define RT5651_HP_OVCD_DIS (0x0 << 10) +#define RT5651_HP_OVCD_EN (0x1 << 10) +#define RT5651_HP_OC_TH_MASK (0x3 << 8) +#define RT5651_HP_OC_TH_SFT 8 +#define RT5651_HP_OC_TH_90 (0x0 << 8) +#define RT5651_HP_OC_TH_105 (0x1 << 8) +#define RT5651_HP_OC_TH_120 (0x2 << 8) +#define RT5651_HP_OC_TH_135 (0x3 << 8) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5651_SMT_TRIG_MASK (0x1 << 15) +#define RT5651_SMT_TRIG_SFT 15 +#define RT5651_SMT_TRIG_DIS (0x0 << 15) +#define RT5651_SMT_TRIG_EN (0x1 << 15) +#define RT5651_HP_L_SMT_MASK (0x1 << 9) +#define RT5651_HP_L_SMT_SFT 9 +#define RT5651_HP_L_SMT_DIS (0x0 << 9) +#define RT5651_HP_L_SMT_EN (0x1 << 9) +#define RT5651_HP_R_SMT_MASK (0x1 << 8) +#define RT5651_HP_R_SMT_SFT 8 +#define RT5651_HP_R_SMT_DIS (0x0 << 8) +#define RT5651_HP_R_SMT_EN (0x1 << 8) +#define RT5651_HP_CD_PD_MASK (0x1 << 7) +#define RT5651_HP_CD_PD_SFT 7 +#define RT5651_HP_CD_PD_DIS (0x0 << 7) +#define RT5651_HP_CD_PD_EN (0x1 << 7) +#define RT5651_RSTN_MASK (0x1 << 6) +#define RT5651_RSTN_SFT 6 +#define RT5651_RSTN_DIS (0x0 << 6) +#define RT5651_RSTN_EN (0x1 << 6) +#define RT5651_RSTP_MASK (0x1 << 5) +#define RT5651_RSTP_SFT 5 +#define RT5651_RSTP_DIS (0x0 << 5) +#define RT5651_RSTP_EN (0x1 << 5) +#define RT5651_HP_CO_MASK (0x1 << 4) +#define RT5651_HP_CO_SFT 4 +#define RT5651_HP_CO_DIS (0x0 << 4) +#define RT5651_HP_CO_EN (0x1 << 4) +#define RT5651_HP_CP_MASK (0x1 << 3) +#define RT5651_HP_CP_SFT 3 +#define RT5651_HP_CP_PD (0x0 << 3) +#define RT5651_HP_CP_PU (0x1 << 3) +#define RT5651_HP_SG_MASK (0x1 << 2) +#define RT5651_HP_SG_SFT 2 +#define RT5651_HP_SG_DIS (0x0 << 2) +#define RT5651_HP_SG_EN (0x1 << 2) +#define RT5651_HP_DP_MASK (0x1 << 1) +#define RT5651_HP_DP_SFT 1 +#define RT5651_HP_DP_PD (0x0 << 1) +#define RT5651_HP_DP_PU (0x1 << 1) +#define RT5651_HP_CB_MASK (0x1) +#define RT5651_HP_CB_SFT 0 +#define RT5651_HP_CB_PD (0x0) +#define RT5651_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5651_DEPOP_MASK (0x1 << 13) +#define RT5651_DEPOP_SFT 13 +#define RT5651_DEPOP_AUTO (0x0 << 13) +#define RT5651_DEPOP_MAN (0x1 << 13) +#define RT5651_RAMP_MASK (0x1 << 12) +#define RT5651_RAMP_SFT 12 +#define RT5651_RAMP_DIS (0x0 << 12) +#define RT5651_RAMP_EN (0x1 << 12) +#define RT5651_BPS_MASK (0x1 << 11) +#define RT5651_BPS_SFT 11 +#define RT5651_BPS_DIS (0x0 << 11) +#define RT5651_BPS_EN (0x1 << 11) +#define RT5651_FAST_UPDN_MASK (0x1 << 10) +#define RT5651_FAST_UPDN_SFT 10 +#define RT5651_FAST_UPDN_DIS (0x0 << 10) +#define RT5651_FAST_UPDN_EN (0x1 << 10) +#define RT5651_MRES_MASK (0x3 << 8) +#define RT5651_MRES_SFT 8 +#define RT5651_MRES_15MO (0x0 << 8) +#define RT5651_MRES_25MO (0x1 << 8) +#define RT5651_MRES_35MO (0x2 << 8) +#define RT5651_MRES_45MO (0x3 << 8) +#define RT5651_VLO_MASK (0x1 << 7) +#define RT5651_VLO_SFT 7 +#define RT5651_VLO_3V (0x0 << 7) +#define RT5651_VLO_32V (0x1 << 7) +#define RT5651_DIG_DP_MASK (0x1 << 6) +#define RT5651_DIG_DP_SFT 6 +#define RT5651_DIG_DP_DIS (0x0 << 6) +#define RT5651_DIG_DP_EN (0x1 << 6) +#define RT5651_DP_TH_MASK (0x3 << 4) +#define RT5651_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5651_CP_SYS_MASK (0x7 << 12) +#define RT5651_CP_SYS_SFT 12 +#define RT5651_CP_FQ1_MASK (0x7 << 8) +#define RT5651_CP_FQ1_SFT 8 +#define RT5651_CP_FQ2_MASK (0x7 << 4) +#define RT5651_CP_FQ2_SFT 4 +#define RT5651_CP_FQ3_MASK (0x7) +#define RT5651_CP_FQ3_SFT 0 +#define RT5651_CP_FQ_1_5_KHZ 0 +#define RT5651_CP_FQ_3_KHZ 1 +#define RT5651_CP_FQ_6_KHZ 2 +#define RT5651_CP_FQ_12_KHZ 3 +#define RT5651_CP_FQ_24_KHZ 4 +#define RT5651_CP_FQ_48_KHZ 5 +#define RT5651_CP_FQ_96_KHZ 6 +#define RT5651_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump (0x91) */ +#define RT5651_OSW_L_MASK (0x1 << 11) +#define RT5651_OSW_L_SFT 11 +#define RT5651_OSW_L_DIS (0x0 << 11) +#define RT5651_OSW_L_EN (0x1 << 11) +#define RT5651_OSW_R_MASK (0x1 << 10) +#define RT5651_OSW_R_SFT 10 +#define RT5651_OSW_R_DIS (0x0 << 10) +#define RT5651_OSW_R_EN (0x1 << 10) +#define RT5651_PM_HP_MASK (0x3 << 8) +#define RT5651_PM_HP_SFT 8 +#define RT5651_PM_HP_LV (0x0 << 8) +#define RT5651_PM_HP_MV (0x1 << 8) +#define RT5651_PM_HP_HV (0x2 << 8) +#define RT5651_IB_HP_MASK (0x3 << 6) +#define RT5651_IB_HP_SFT 6 +#define RT5651_IB_HP_125IL (0x0 << 6) +#define RT5651_IB_HP_25IL (0x1 << 6) +#define RT5651_IB_HP_5IL (0x2 << 6) +#define RT5651_IB_HP_1IL (0x3 << 6) + +/* Micbias Control (0x93) */ +#define RT5651_MIC1_BS_MASK (0x1 << 15) +#define RT5651_MIC1_BS_SFT 15 +#define RT5651_MIC1_BS_9AV (0x0 << 15) +#define RT5651_MIC1_BS_75AV (0x1 << 15) +#define RT5651_MIC1_CLK_MASK (0x1 << 13) +#define RT5651_MIC1_CLK_SFT 13 +#define RT5651_MIC1_CLK_DIS (0x0 << 13) +#define RT5651_MIC1_CLK_EN (0x1 << 13) +#define RT5651_MIC1_OVCD_MASK (0x1 << 11) +#define RT5651_MIC1_OVCD_SFT 11 +#define RT5651_MIC1_OVCD_DIS (0x0 << 11) +#define RT5651_MIC1_OVCD_EN (0x1 << 11) +#define RT5651_MIC1_OVTH_MASK (0x3 << 9) +#define RT5651_MIC1_OVTH_SFT 9 +#define RT5651_MIC1_OVTH_600UA (0x0 << 9) +#define RT5651_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5651_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5651_PWR_MB_MASK (0x1 << 5) +#define RT5651_PWR_MB_SFT 5 +#define RT5651_PWR_MB_PD (0x0 << 5) +#define RT5651_PWR_MB_PU (0x1 << 5) +#define RT5651_PWR_CLK12M_MASK (0x1 << 4) +#define RT5651_PWR_CLK12M_SFT 4 +#define RT5651_PWR_CLK12M_PD (0x0 << 4) +#define RT5651_PWR_CLK12M_PU (0x1 << 4) + +/* Analog JD Control 1 (0x94) */ +#define RT5651_JD2_CMP_MASK (0x7 << 12) +#define RT5651_JD2_CMP_SFT 12 +#define RT5651_JD_PU (0x1 << 11) +#define RT5651_JD_PU_SFT 11 +#define RT5651_JD_PD (0x1 << 10) +#define RT5651_JD_PD_SFT 10 +#define RT5651_JD_MODE_SEL_MASK (0x3 << 8) +#define RT5651_JD_MODE_SEL_SFT 8 +#define RT5651_JD_MODE_SEL_M0 (0x0 << 8) +#define RT5651_JD_MODE_SEL_M1 (0x1 << 8) +#define RT5651_JD_MODE_SEL_M2 (0x2 << 8) +#define RT5651_JD_M_CMP (0x7 << 4) +#define RT5651_JD_M_CMP_SFT 4 +#define RT5651_JD_M_PU (0x1 << 3) +#define RT5651_JD_M_PU_SFT 3 +#define RT5651_JD_M_PD (0x1 << 2) +#define RT5651_JD_M_PD_SFT 2 +#define RT5651_JD_M_MODE_SEL_MASK (0x3) +#define RT5651_JD_M_MODE_SEL_SFT 0 +#define RT5651_JD_M_MODE_SEL_M0 (0x0) +#define RT5651_JD_M_MODE_SEL_M1 (0x1) +#define RT5651_JD_M_MODE_SEL_M2 (0x2) + +/* Analog JD Control 2 (0x95) */ +#define RT5651_JD3_CMP_MASK (0x7 << 12) +#define RT5651_JD3_CMP_SFT 12 + +/* EQ Control 1 (0xb0) */ +#define RT5651_EQ_SRC_MASK (0x1 << 15) +#define RT5651_EQ_SRC_SFT 15 +#define RT5651_EQ_SRC_DAC (0x0 << 15) +#define RT5651_EQ_SRC_ADC (0x1 << 15) +#define RT5651_EQ_UPD (0x1 << 14) +#define RT5651_EQ_UPD_BIT 14 +#define RT5651_EQ_CD_MASK (0x1 << 13) +#define RT5651_EQ_CD_SFT 13 +#define RT5651_EQ_CD_DIS (0x0 << 13) +#define RT5651_EQ_CD_EN (0x1 << 13) +#define RT5651_EQ_DITH_MASK (0x3 << 8) +#define RT5651_EQ_DITH_SFT 8 +#define RT5651_EQ_DITH_NOR (0x0 << 8) +#define RT5651_EQ_DITH_LSB (0x1 << 8) +#define RT5651_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5651_EQ_DITH_LSB_2 (0x3 << 8) +#define RT5651_EQ_CD_F (0x1 << 7) +#define RT5651_EQ_CD_F_BIT 7 +#define RT5651_EQ_STA_HP2 (0x1 << 6) +#define RT5651_EQ_STA_HP2_BIT 6 +#define RT5651_EQ_STA_HP1 (0x1 << 5) +#define RT5651_EQ_STA_HP1_BIT 5 +#define RT5651_EQ_STA_BP4 (0x1 << 4) +#define RT5651_EQ_STA_BP4_BIT 4 +#define RT5651_EQ_STA_BP3 (0x1 << 3) +#define RT5651_EQ_STA_BP3_BIT 3 +#define RT5651_EQ_STA_BP2 (0x1 << 2) +#define RT5651_EQ_STA_BP2_BIT 2 +#define RT5651_EQ_STA_BP1 (0x1 << 1) +#define RT5651_EQ_STA_BP1_BIT 1 +#define RT5651_EQ_STA_LP (0x1) +#define RT5651_EQ_STA_LP_BIT 0 + +/* EQ Control 2 (0xb1) */ +#define RT5651_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5651_EQ_HPF1_M_SFT 8 +#define RT5651_EQ_HPF1_M_HI (0x0 << 8) +#define RT5651_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5651_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5651_EQ_LPF1_M_SFT 7 +#define RT5651_EQ_LPF1_M_LO (0x0 << 7) +#define RT5651_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5651_EQ_HPF2_MASK (0x1 << 6) +#define RT5651_EQ_HPF2_SFT 6 +#define RT5651_EQ_HPF2_DIS (0x0 << 6) +#define RT5651_EQ_HPF2_EN (0x1 << 6) +#define RT5651_EQ_HPF1_MASK (0x1 << 5) +#define RT5651_EQ_HPF1_SFT 5 +#define RT5651_EQ_HPF1_DIS (0x0 << 5) +#define RT5651_EQ_HPF1_EN (0x1 << 5) +#define RT5651_EQ_BPF4_MASK (0x1 << 4) +#define RT5651_EQ_BPF4_SFT 4 +#define RT5651_EQ_BPF4_DIS (0x0 << 4) +#define RT5651_EQ_BPF4_EN (0x1 << 4) +#define RT5651_EQ_BPF3_MASK (0x1 << 3) +#define RT5651_EQ_BPF3_SFT 3 +#define RT5651_EQ_BPF3_DIS (0x0 << 3) +#define RT5651_EQ_BPF3_EN (0x1 << 3) +#define RT5651_EQ_BPF2_MASK (0x1 << 2) +#define RT5651_EQ_BPF2_SFT 2 +#define RT5651_EQ_BPF2_DIS (0x0 << 2) +#define RT5651_EQ_BPF2_EN (0x1 << 2) +#define RT5651_EQ_BPF1_MASK (0x1 << 1) +#define RT5651_EQ_BPF1_SFT 1 +#define RT5651_EQ_BPF1_DIS (0x0 << 1) +#define RT5651_EQ_BPF1_EN (0x1 << 1) +#define RT5651_EQ_LPF_MASK (0x1) +#define RT5651_EQ_LPF_SFT 0 +#define RT5651_EQ_LPF_DIS (0x0) +#define RT5651_EQ_LPF_EN (0x1) +#define RT5651_EQ_CTRL_MASK (0x7f) + +/* Memory Test (0xb2) */ +#define RT5651_MT_MASK (0x1 << 15) +#define RT5651_MT_SFT 15 +#define RT5651_MT_DIS (0x0 << 15) +#define RT5651_MT_EN (0x1 << 15) + +/* ALC Control 1 (0xb4) */ +#define RT5651_ALC_P_MASK (0x1 << 15) +#define RT5651_ALC_P_SFT 15 +#define RT5651_ALC_P_DAC (0x0 << 15) +#define RT5651_ALC_P_ADC (0x1 << 15) +#define RT5651_ALC_MASK (0x1 << 14) +#define RT5651_ALC_SFT 14 +#define RT5651_ALC_DIS (0x0 << 14) +#define RT5651_ALC_EN (0x1 << 14) +#define RT5651_ALC_UPD (0x1 << 13) +#define RT5651_ALC_UPD_BIT 13 +#define RT5651_ALC_AR_MASK (0x1f << 8) +#define RT5651_ALC_AR_SFT 8 +#define RT5651_ALC_R_MASK (0x7 << 5) +#define RT5651_ALC_R_SFT 5 +#define RT5651_ALC_R_48K (0x1 << 5) +#define RT5651_ALC_R_96K (0x2 << 5) +#define RT5651_ALC_R_192K (0x3 << 5) +#define RT5651_ALC_R_441K (0x5 << 5) +#define RT5651_ALC_R_882K (0x6 << 5) +#define RT5651_ALC_R_1764K (0x7 << 5) +#define RT5651_ALC_RC_MASK (0x1f) +#define RT5651_ALC_RC_SFT 0 + +/* ALC Control 2 (0xb5) */ +#define RT5651_ALC_POB_MASK (0x3f << 8) +#define RT5651_ALC_POB_SFT 8 +#define RT5651_ALC_DRC_MASK (0x1 << 7) +#define RT5651_ALC_DRC_SFT 7 +#define RT5651_ALC_DRC_DIS (0x0 << 7) +#define RT5651_ALC_DRC_EN (0x1 << 7) +#define RT5651_ALC_CPR_MASK (0x3 << 5) +#define RT5651_ALC_CPR_SFT 5 +#define RT5651_ALC_CPR_1_1 (0x0 << 5) +#define RT5651_ALC_CPR_1_2 (0x1 << 5) +#define RT5651_ALC_CPR_1_4 (0x2 << 5) +#define RT5651_ALC_CPR_1_8 (0x3 << 5) +#define RT5651_ALC_PRB_MASK (0x1f) +#define RT5651_ALC_PRB_SFT 0 + +/* ALC Control 3 (0xb6) */ +#define RT5651_ALC_NGB_MASK (0xf << 12) +#define RT5651_ALC_NGB_SFT 12 +#define RT5651_ALC_TAR_MASK (0x1f << 7) +#define RT5651_ALC_TAR_SFT 7 +#define RT5651_ALC_NG_MASK (0x1 << 6) +#define RT5651_ALC_NG_SFT 6 +#define RT5651_ALC_NG_DIS (0x0 << 6) +#define RT5651_ALC_NG_EN (0x1 << 6) +#define RT5651_ALC_NGH_MASK (0x1 << 5) +#define RT5651_ALC_NGH_SFT 5 +#define RT5651_ALC_NGH_DIS (0x0 << 5) +#define RT5651_ALC_NGH_EN (0x1 << 5) +#define RT5651_ALC_NGT_MASK (0x1f) +#define RT5651_ALC_NGT_SFT 0 + +/* Jack Detect Control 1 (0xbb) */ +#define RT5651_JD_MASK (0x7 << 13) +#define RT5651_JD_SFT 13 +#define RT5651_JD_DIS (0x0 << 13) +#define RT5651_JD_GPIO1 (0x1 << 13) +#define RT5651_JD_GPIO2 (0x2 << 13) +#define RT5651_JD_GPIO3 (0x3 << 13) +#define RT5651_JD_GPIO4 (0x4 << 13) +#define RT5651_JD_GPIO5 (0x5 << 13) +#define RT5651_JD_GPIO6 (0x6 << 13) +#define RT5651_JD_HP_MASK (0x1 << 11) +#define RT5651_JD_HP_SFT 11 +#define RT5651_JD_HP_DIS (0x0 << 11) +#define RT5651_JD_HP_EN (0x1 << 11) +#define RT5651_JD_HP_TRG_MASK (0x1 << 10) +#define RT5651_JD_HP_TRG_SFT 10 +#define RT5651_JD_HP_TRG_LO (0x0 << 10) +#define RT5651_JD_HP_TRG_HI (0x1 << 10) +#define RT5651_JD_SPL_MASK (0x1 << 9) +#define RT5651_JD_SPL_SFT 9 +#define RT5651_JD_SPL_DIS (0x0 << 9) +#define RT5651_JD_SPL_EN (0x1 << 9) +#define RT5651_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5651_JD_SPL_TRG_SFT 8 +#define RT5651_JD_SPL_TRG_LO (0x0 << 8) +#define RT5651_JD_SPL_TRG_HI (0x1 << 8) +#define RT5651_JD_SPR_MASK (0x1 << 7) +#define RT5651_JD_SPR_SFT 7 +#define RT5651_JD_SPR_DIS (0x0 << 7) +#define RT5651_JD_SPR_EN (0x1 << 7) +#define RT5651_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5651_JD_SPR_TRG_SFT 6 +#define RT5651_JD_SPR_TRG_LO (0x0 << 6) +#define RT5651_JD_SPR_TRG_HI (0x1 << 6) +#define RT5651_JD_LO_MASK (0x1 << 3) +#define RT5651_JD_LO_SFT 3 +#define RT5651_JD_LO_DIS (0x0 << 3) +#define RT5651_JD_LO_EN (0x1 << 3) +#define RT5651_JD_LO_TRG_MASK (0x1 << 2) +#define RT5651_JD_LO_TRG_SFT 2 +#define RT5651_JD_LO_TRG_LO (0x0 << 2) +#define RT5651_JD_LO_TRG_HI (0x1 << 2) + +/* Jack Detect Control 2 (0xbc) */ +#define RT5651_JD_TRG_SEL_MASK (0x7 << 9) +#define RT5651_JD_TRG_SEL_SFT 9 +#define RT5651_JD_TRG_SEL_GPIO (0x0 << 9) +#define RT5651_JD_TRG_SEL_JD1_1 (0x1 << 9) +#define RT5651_JD_TRG_SEL_JD1_2 (0x2 << 9) +#define RT5651_JD_TRG_SEL_JD2 (0x3 << 9) +#define RT5651_JD_TRG_SEL_JD3 (0x4 << 9) +#define RT5651_JD3_IRQ_EN (0x1 << 8) +#define RT5651_JD3_IRQ_EN_SFT 8 +#define RT5651_JD3_EN_STKY (0x1 << 7) +#define RT5651_JD3_EN_STKY_SFT 7 +#define RT5651_JD3_INV (0x1 << 6) +#define RT5651_JD3_INV_SFT 6 + +/* IRQ Control 1 (0xbd) */ +#define RT5651_IRQ_JD_MASK (0x1 << 15) +#define RT5651_IRQ_JD_SFT 15 +#define RT5651_IRQ_JD_BP (0x0 << 15) +#define RT5651_IRQ_JD_NOR (0x1 << 15) +#define RT5651_JD_STKY_MASK (0x1 << 13) +#define RT5651_JD_STKY_SFT 13 +#define RT5651_JD_STKY_DIS (0x0 << 13) +#define RT5651_JD_STKY_EN (0x1 << 13) +#define RT5651_JD_P_MASK (0x1 << 11) +#define RT5651_JD_P_SFT 11 +#define RT5651_JD_P_NOR (0x0 << 11) +#define RT5651_JD_P_INV (0x1 << 11) +#define RT5651_JD1_1_IRQ_EN (0x1 << 9) +#define RT5651_JD1_1_IRQ_EN_SFT 9 +#define RT5651_JD1_1_EN_STKY (0x1 << 8) +#define RT5651_JD1_1_EN_STKY_SFT 8 +#define RT5651_JD1_1_INV (0x1 << 7) +#define RT5651_JD1_1_INV_SFT 7 +#define RT5651_JD1_2_IRQ_EN (0x1 << 6) +#define RT5651_JD1_2_IRQ_EN_SFT 6 +#define RT5651_JD1_2_EN_STKY (0x1 << 5) +#define RT5651_JD1_2_EN_STKY_SFT 5 +#define RT5651_JD1_2_INV (0x1 << 4) +#define RT5651_JD1_2_INV_SFT 4 +#define RT5651_JD2_IRQ_EN (0x1 << 3) +#define RT5651_JD2_IRQ_EN_SFT 3 +#define RT5651_JD2_EN_STKY (0x1 << 2) +#define RT5651_JD2_EN_STKY_SFT 2 +#define RT5651_JD2_INV (0x1 << 1) +#define RT5651_JD2_INV_SFT 1 + +/* IRQ Control 2 (0xbe) */ +#define RT5651_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5651_IRQ_MB1_OC_SFT 15 +#define RT5651_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5651_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5651_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5651_MB1_OC_STKY_SFT 11 +#define RT5651_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5651_MB1_OC_STKY_EN (0x1 << 11) +#define RT5651_MB1_OC_P_MASK (0x1 << 7) +#define RT5651_MB1_OC_P_SFT 7 +#define RT5651_MB1_OC_P_NOR (0x0 << 7) +#define RT5651_MB1_OC_P_INV (0x1 << 7) +#define RT5651_MB2_OC_P_MASK (0x1 << 6) +#define RT5651_MB1_OC_CLR (0x1 << 3) +#define RT5651_MB1_OC_CLR_SFT 3 +#define RT5651_STA_GPIO8 (0x1) +#define RT5651_STA_GPIO8_BIT 0 + +/* Internal Status and GPIO status (0xbf) */ +#define RT5651_STA_JD3 (0x1 << 15) +#define RT5651_STA_JD3_BIT 15 +#define RT5651_STA_JD2 (0x1 << 14) +#define RT5651_STA_JD2_BIT 14 +#define RT5651_STA_JD1_2 (0x1 << 13) +#define RT5651_STA_JD1_2_BIT 13 +#define RT5651_STA_JD1_1 (0x1 << 12) +#define RT5651_STA_JD1_1_BIT 12 +#define RT5651_STA_GP7 (0x1 << 11) +#define RT5651_STA_GP7_BIT 11 +#define RT5651_STA_GP6 (0x1 << 10) +#define RT5651_STA_GP6_BIT 10 +#define RT5651_STA_GP5 (0x1 << 9) +#define RT5651_STA_GP5_BIT 9 +#define RT5651_STA_GP1 (0x1 << 8) +#define RT5651_STA_GP1_BIT 8 +#define RT5651_STA_GP2 (0x1 << 7) +#define RT5651_STA_GP2_BIT 7 +#define RT5651_STA_GP3 (0x1 << 6) +#define RT5651_STA_GP3_BIT 6 +#define RT5651_STA_GP4 (0x1 << 5) +#define RT5651_STA_GP4_BIT 5 +#define RT5651_STA_GP_JD (0x1 << 4) +#define RT5651_STA_GP_JD_BIT 4 + +/* GPIO Control 1 (0xc0) */ +#define RT5651_GP1_PIN_MASK (0x1 << 15) +#define RT5651_GP1_PIN_SFT 15 +#define RT5651_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5651_GP1_PIN_IRQ (0x1 << 15) +#define RT5651_GP2_PIN_MASK (0x1 << 14) +#define RT5651_GP2_PIN_SFT 14 +#define RT5651_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5651_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5651_GPIO_M_MASK (0x1 << 9) +#define RT5651_GPIO_M_SFT 9 +#define RT5651_GPIO_M_FLT (0x0 << 9) +#define RT5651_GPIO_M_PH (0x1 << 9) +#define RT5651_I2S2_SEL_MASK (0x1 << 8) +#define RT5651_I2S2_SEL_SFT 8 +#define RT5651_I2S2_SEL_I2S (0x0 << 8) +#define RT5651_I2S2_SEL_GPIO (0x1 << 8) +#define RT5651_GP5_PIN_MASK (0x1 << 7) +#define RT5651_GP5_PIN_SFT 7 +#define RT5651_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5651_GP5_PIN_IRQ (0x1 << 7) +#define RT5651_GP6_PIN_MASK (0x1 << 6) +#define RT5651_GP6_PIN_SFT 6 +#define RT5651_GP6_PIN_GPIO6 (0x0 << 6) +#define RT5651_GP6_PIN_DMIC_SDA (0x1 << 6) +#define RT5651_GP7_PIN_MASK (0x1 << 5) +#define RT5651_GP7_PIN_SFT 5 +#define RT5651_GP7_PIN_GPIO7 (0x0 << 5) +#define RT5651_GP7_PIN_IRQ (0x1 << 5) +#define RT5651_GP8_PIN_MASK (0x1 << 4) +#define RT5651_GP8_PIN_SFT 4 +#define RT5651_GP8_PIN_GPIO8 (0x0 << 4) +#define RT5651_GP8_PIN_DMIC_SDA (0x1 << 4) +#define RT5651_GPIO_PDM_SEL_MASK (0x1 << 3) +#define RT5651_GPIO_PDM_SEL_SFT 3 +#define RT5651_GPIO_PDM_SEL_GPIO (0x0 << 3) +#define RT5651_GPIO_PDM_SEL_PDM (0x1 << 3) + +/* GPIO Control 2 (0xc1) */ +#define RT5651_GP5_DR_MASK (0x1 << 14) +#define RT5651_GP5_DR_SFT 14 +#define RT5651_GP5_DR_IN (0x0 << 14) +#define RT5651_GP5_DR_OUT (0x1 << 14) +#define RT5651_GP5_OUT_MASK (0x1 << 13) +#define RT5651_GP5_OUT_SFT 13 +#define RT5651_GP5_OUT_LO (0x0 << 13) +#define RT5651_GP5_OUT_HI (0x1 << 13) +#define RT5651_GP5_P_MASK (0x1 << 12) +#define RT5651_GP5_P_SFT 12 +#define RT5651_GP5_P_NOR (0x0 << 12) +#define RT5651_GP5_P_INV (0x1 << 12) +#define RT5651_GP4_DR_MASK (0x1 << 11) +#define RT5651_GP4_DR_SFT 11 +#define RT5651_GP4_DR_IN (0x0 << 11) +#define RT5651_GP4_DR_OUT (0x1 << 11) +#define RT5651_GP4_OUT_MASK (0x1 << 10) +#define RT5651_GP4_OUT_SFT 10 +#define RT5651_GP4_OUT_LO (0x0 << 10) +#define RT5651_GP4_OUT_HI (0x1 << 10) +#define RT5651_GP4_P_MASK (0x1 << 9) +#define RT5651_GP4_P_SFT 9 +#define RT5651_GP4_P_NOR (0x0 << 9) +#define RT5651_GP4_P_INV (0x1 << 9) +#define RT5651_GP3_DR_MASK (0x1 << 8) +#define RT5651_GP3_DR_SFT 8 +#define RT5651_GP3_DR_IN (0x0 << 8) +#define RT5651_GP3_DR_OUT (0x1 << 8) +#define RT5651_GP3_OUT_MASK (0x1 << 7) +#define RT5651_GP3_OUT_SFT 7 +#define RT5651_GP3_OUT_LO (0x0 << 7) +#define RT5651_GP3_OUT_HI (0x1 << 7) +#define RT5651_GP3_P_MASK (0x1 << 6) +#define RT5651_GP3_P_SFT 6 +#define RT5651_GP3_P_NOR (0x0 << 6) +#define RT5651_GP3_P_INV (0x1 << 6) +#define RT5651_GP2_DR_MASK (0x1 << 5) +#define RT5651_GP2_DR_SFT 5 +#define RT5651_GP2_DR_IN (0x0 << 5) +#define RT5651_GP2_DR_OUT (0x1 << 5) +#define RT5651_GP2_OUT_MASK (0x1 << 4) +#define RT5651_GP2_OUT_SFT 4 +#define RT5651_GP2_OUT_LO (0x0 << 4) +#define RT5651_GP2_OUT_HI (0x1 << 4) +#define RT5651_GP2_P_MASK (0x1 << 3) +#define RT5651_GP2_P_SFT 3 +#define RT5651_GP2_P_NOR (0x0 << 3) +#define RT5651_GP2_P_INV (0x1 << 3) +#define RT5651_GP1_DR_MASK (0x1 << 2) +#define RT5651_GP1_DR_SFT 2 +#define RT5651_GP1_DR_IN (0x0 << 2) +#define RT5651_GP1_DR_OUT (0x1 << 2) +#define RT5651_GP1_OUT_MASK (0x1 << 1) +#define RT5651_GP1_OUT_SFT 1 +#define RT5651_GP1_OUT_LO (0x0 << 1) +#define RT5651_GP1_OUT_HI (0x1 << 1) +#define RT5651_GP1_P_MASK (0x1) +#define RT5651_GP1_P_SFT 0 +#define RT5651_GP1_P_NOR (0x0) +#define RT5651_GP1_P_INV (0x1) + +/* GPIO Control 3 (0xc2) */ +#define RT5651_GP8_DR_MASK (0x1 << 8) +#define RT5651_GP8_DR_SFT 8 +#define RT5651_GP8_DR_IN (0x0 << 8) +#define RT5651_GP8_DR_OUT (0x1 << 8) +#define RT5651_GP8_OUT_MASK (0x1 << 7) +#define RT5651_GP8_OUT_SFT 7 +#define RT5651_GP8_OUT_LO (0x0 << 7) +#define RT5651_GP8_OUT_HI (0x1 << 7) +#define RT5651_GP8_P_MASK (0x1 << 6) +#define RT5651_GP8_P_SFT 6 +#define RT5651_GP8_P_NOR (0x0 << 6) +#define RT5651_GP8_P_INV (0x1 << 6) +#define RT5651_GP7_DR_MASK (0x1 << 5) +#define RT5651_GP7_DR_SFT 5 +#define RT5651_GP7_DR_IN (0x0 << 5) +#define RT5651_GP7_DR_OUT (0x1 << 5) +#define RT5651_GP7_OUT_MASK (0x1 << 4) +#define RT5651_GP7_OUT_SFT 4 +#define RT5651_GP7_OUT_LO (0x0 << 4) +#define RT5651_GP7_OUT_HI (0x1 << 4) +#define RT5651_GP7_P_MASK (0x1 << 3) +#define RT5651_GP7_P_SFT 3 +#define RT5651_GP7_P_NOR (0x0 << 3) +#define RT5651_GP7_P_INV (0x1 << 3) +#define RT5651_GP6_DR_MASK (0x1 << 2) +#define RT5651_GP6_DR_SFT 2 +#define RT5651_GP6_DR_IN (0x0 << 2) +#define RT5651_GP6_DR_OUT (0x1 << 2) +#define RT5651_GP6_OUT_MASK (0x1 << 1) +#define RT5651_GP6_OUT_SFT 1 +#define RT5651_GP6_OUT_LO (0x0 << 1) +#define RT5651_GP6_OUT_HI (0x1 << 1) +#define RT5651_GP6_P_MASK (0x1) +#define RT5651_GP6_P_SFT 0 +#define RT5651_GP6_P_NOR (0x0) +#define RT5651_GP6_P_INV (0x1) + +/* Scramble Control (0xce) */ +#define RT5651_SCB_SWAP_MASK (0x1 << 15) +#define RT5651_SCB_SWAP_SFT 15 +#define RT5651_SCB_SWAP_DIS (0x0 << 15) +#define RT5651_SCB_SWAP_EN (0x1 << 15) +#define RT5651_SCB_MASK (0x1 << 14) +#define RT5651_SCB_SFT 14 +#define RT5651_SCB_DIS (0x0 << 14) +#define RT5651_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5651_BB_MASK (0x1 << 15) +#define RT5651_BB_SFT 15 +#define RT5651_BB_DIS (0x0 << 15) +#define RT5651_BB_EN (0x1 << 15) +#define RT5651_BB_CT_MASK (0x7 << 12) +#define RT5651_BB_CT_SFT 12 +#define RT5651_BB_CT_A (0x0 << 12) +#define RT5651_BB_CT_B (0x1 << 12) +#define RT5651_BB_CT_C (0x2 << 12) +#define RT5651_BB_CT_D (0x3 << 12) +#define RT5651_M_BB_L_MASK (0x1 << 9) +#define RT5651_M_BB_L_SFT 9 +#define RT5651_M_BB_R_MASK (0x1 << 8) +#define RT5651_M_BB_R_SFT 8 +#define RT5651_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5651_M_BB_HPF_L_SFT 7 +#define RT5651_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5651_M_BB_HPF_R_SFT 6 +#define RT5651_G_BB_BST_MASK (0x3f) +#define RT5651_G_BB_BST_SFT 0 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5651_M_MP3_L_MASK (0x1 << 15) +#define RT5651_M_MP3_L_SFT 15 +#define RT5651_M_MP3_R_MASK (0x1 << 14) +#define RT5651_M_MP3_R_SFT 14 +#define RT5651_M_MP3_MASK (0x1 << 13) +#define RT5651_M_MP3_SFT 13 +#define RT5651_M_MP3_DIS (0x0 << 13) +#define RT5651_M_MP3_EN (0x1 << 13) +#define RT5651_EG_MP3_MASK (0x1f << 8) +#define RT5651_EG_MP3_SFT 8 +#define RT5651_MP3_HLP_MASK (0x1 << 7) +#define RT5651_MP3_HLP_SFT 7 +#define RT5651_MP3_HLP_DIS (0x0 << 7) +#define RT5651_MP3_HLP_EN (0x1 << 7) +#define RT5651_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5651_M_MP3_ORG_L_SFT 6 +#define RT5651_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5651_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5651_MP3_WT_MASK (0x1 << 13) +#define RT5651_MP3_WT_SFT 13 +#define RT5651_MP3_WT_1_4 (0x0 << 13) +#define RT5651_MP3_WT_1_2 (0x1 << 13) +#define RT5651_OG_MP3_MASK (0x1f << 8) +#define RT5651_OG_MP3_SFT 8 +#define RT5651_HG_MP3_MASK (0x3f) +#define RT5651_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5651_3D_CF_MASK (0x1 << 15) +#define RT5651_3D_CF_SFT 15 +#define RT5651_3D_CF_DIS (0x0 << 15) +#define RT5651_3D_CF_EN (0x1 << 15) +#define RT5651_3D_HP_MASK (0x1 << 14) +#define RT5651_3D_HP_SFT 14 +#define RT5651_3D_HP_DIS (0x0 << 14) +#define RT5651_3D_HP_EN (0x1 << 14) +#define RT5651_3D_BT_MASK (0x1 << 13) +#define RT5651_3D_BT_SFT 13 +#define RT5651_3D_BT_DIS (0x0 << 13) +#define RT5651_3D_BT_EN (0x1 << 13) +#define RT5651_3D_1F_MIX_MASK (0x3 << 11) +#define RT5651_3D_1F_MIX_SFT 11 +#define RT5651_3D_HP_M_MASK (0x1 << 10) +#define RT5651_3D_HP_M_SFT 10 +#define RT5651_3D_HP_M_SUR (0x0 << 10) +#define RT5651_3D_HP_M_FRO (0x1 << 10) +#define RT5651_M_3D_HRTF_MASK (0x1 << 9) +#define RT5651_M_3D_HRTF_SFT 9 +#define RT5651_M_3D_D2H_MASK (0x1 << 8) +#define RT5651_M_3D_D2H_SFT 8 +#define RT5651_M_3D_D2R_MASK (0x1 << 7) +#define RT5651_M_3D_D2R_SFT 7 +#define RT5651_M_3D_REVB_MASK (0x1 << 6) +#define RT5651_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5651_2ND_HPF_MASK (0x1 << 15) +#define RT5651_2ND_HPF_SFT 15 +#define RT5651_2ND_HPF_DIS (0x0 << 15) +#define RT5651_2ND_HPF_EN (0x1 << 15) +#define RT5651_HPF_CF_L_MASK (0x7 << 12) +#define RT5651_HPF_CF_L_SFT 12 +#define RT5651_HPF_CF_R_MASK (0x7 << 8) +#define RT5651_HPF_CF_R_SFT 8 +#define RT5651_ZD_T_MASK (0x3 << 6) +#define RT5651_ZD_T_SFT 6 +#define RT5651_ZD_F_MASK (0x3 << 4) +#define RT5651_ZD_F_SFT 4 +#define RT5651_ZD_F_IM (0x0 << 4) +#define RT5651_ZD_F_ZC_IM (0x1 << 4) +#define RT5651_ZD_F_ZC_IOD (0x2 << 4) +#define RT5651_ZD_F_UN (0x3 << 4) + +/* Adjustable high pass filter control 2 (0xd4) */ +#define RT5651_HPF_CF_L_NUM_MASK (0x3f << 8) +#define RT5651_HPF_CF_L_NUM_SFT 8 +#define RT5651_HPF_CF_R_NUM_MASK (0x3f) +#define RT5651_HPF_CF_R_NUM_SFT 0 + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5651_SI_DAC_MASK (0x1 << 11) +#define RT5651_SI_DAC_SFT 11 +#define RT5651_SI_DAC_AUTO (0x0 << 11) +#define RT5651_SI_DAC_TEST (0x1 << 11) +#define RT5651_DC_CAL_M_MASK (0x1 << 10) +#define RT5651_DC_CAL_M_SFT 10 +#define RT5651_DC_CAL_M_NOR (0x0 << 10) +#define RT5651_DC_CAL_M_CAL (0x1 << 10) +#define RT5651_DC_CAL_MASK (0x1 << 9) +#define RT5651_DC_CAL_SFT 9 +#define RT5651_DC_CAL_DIS (0x0 << 9) +#define RT5651_DC_CAL_EN (0x1 << 9) +#define RT5651_HPD_RCV_MASK (0x7 << 6) +#define RT5651_HPD_RCV_SFT 6 +#define RT5651_HPD_PS_MASK (0x1 << 5) +#define RT5651_HPD_PS_SFT 5 +#define RT5651_HPD_PS_DIS (0x0 << 5) +#define RT5651_HPD_PS_EN (0x1 << 5) +#define RT5651_CAL_M_MASK (0x1 << 4) +#define RT5651_CAL_M_SFT 4 +#define RT5651_CAL_M_DEP (0x0 << 4) +#define RT5651_CAL_M_CAL (0x1 << 4) +#define RT5651_CAL_MASK (0x1 << 3) +#define RT5651_CAL_SFT 3 +#define RT5651_CAL_DIS (0x0 << 3) +#define RT5651_CAL_EN (0x1 << 3) +#define RT5651_CAL_TEST_MASK (0x1 << 2) +#define RT5651_CAL_TEST_SFT 2 +#define RT5651_CAL_TEST_DIS (0x0 << 2) +#define RT5651_CAL_TEST_EN (0x1 << 2) +#define RT5651_CAL_P_MASK (0x3) +#define RT5651_CAL_P_SFT 0 +#define RT5651_CAL_P_NONE (0x0) +#define RT5651_CAL_P_CAL (0x1) +#define RT5651_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5651_SV_MASK (0x1 << 15) +#define RT5651_SV_SFT 15 +#define RT5651_SV_DIS (0x0 << 15) +#define RT5651_SV_EN (0x1 << 15) +#define RT5651_OUT_SV_MASK (0x1 << 13) +#define RT5651_OUT_SV_SFT 13 +#define RT5651_OUT_SV_DIS (0x0 << 13) +#define RT5651_OUT_SV_EN (0x1 << 13) +#define RT5651_HP_SV_MASK (0x1 << 12) +#define RT5651_HP_SV_SFT 12 +#define RT5651_HP_SV_DIS (0x0 << 12) +#define RT5651_HP_SV_EN (0x1 << 12) +#define RT5651_ZCD_DIG_MASK (0x1 << 11) +#define RT5651_ZCD_DIG_SFT 11 +#define RT5651_ZCD_DIG_DIS (0x0 << 11) +#define RT5651_ZCD_DIG_EN (0x1 << 11) +#define RT5651_ZCD_MASK (0x1 << 10) +#define RT5651_ZCD_SFT 10 +#define RT5651_ZCD_PD (0x0 << 10) +#define RT5651_ZCD_PU (0x1 << 10) +#define RT5651_M_ZCD_MASK (0x3f << 4) +#define RT5651_M_ZCD_SFT 4 +#define RT5651_M_ZCD_OM_L (0x1 << 7) +#define RT5651_M_ZCD_OM_R (0x1 << 6) +#define RT5651_M_ZCD_RM_L (0x1 << 5) +#define RT5651_M_ZCD_RM_R (0x1 << 4) +#define RT5651_SV_DLY_MASK (0xf) +#define RT5651_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5651_ZCD_HP_MASK (0x1 << 15) +#define RT5651_ZCD_HP_SFT 15 +#define RT5651_ZCD_HP_DIS (0x0 << 15) +#define RT5651_ZCD_HP_EN (0x1 << 15) + +/* Digital Misc Control (0xfa) */ +#define RT5651_I2S2_MS_SP_MASK (0x1 << 8) +#define RT5651_I2S2_MS_SP_SEL 8 +#define RT5651_I2S2_MS_SP_64 (0x0 << 8) +#define RT5651_I2S2_MS_SP_50 (0x1 << 8) +#define RT5651_CLK_DET_EN (0x1 << 3) +#define RT5651_CLK_DET_EN_SFT 3 +#define RT5651_AMP_DET_EN (0x1 << 1) +#define RT5651_AMP_DET_EN_SFT 1 +#define RT5651_D_GATE_EN (0x1) +#define RT5651_D_GATE_EN_SFT 0 + +/* Codec Private Register definition */ +/* 3D Speaker Control (0x63) */ +#define RT5651_3D_SPK_MASK (0x1 << 15) +#define RT5651_3D_SPK_SFT 15 +#define RT5651_3D_SPK_DIS (0x0 << 15) +#define RT5651_3D_SPK_EN (0x1 << 15) +#define RT5651_3D_SPK_M_MASK (0x3 << 13) +#define RT5651_3D_SPK_M_SFT 13 +#define RT5651_3D_SPK_CG_MASK (0x1f << 8) +#define RT5651_3D_SPK_CG_SFT 8 +#define RT5651_3D_SPK_SG_MASK (0x1f) +#define RT5651_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5651_WND_MASK (0x1 << 15) +#define RT5651_WND_SFT 15 +#define RT5651_WND_DIS (0x0 << 15) +#define RT5651_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5651_WND_FC_NW_MASK (0x3f << 10) +#define RT5651_WND_FC_NW_SFT 10 +#define RT5651_WND_FC_WK_MASK (0x3f << 4) +#define RT5651_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5651_HPF_FC_MASK (0x3f << 6) +#define RT5651_HPF_FC_SFT 6 +#define RT5651_WND_FC_ST_MASK (0x3f) +#define RT5651_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5651_WND_TH_LO_MASK (0x3ff) +#define RT5651_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5651_WND_TH_HI_MASK (0x3ff) +#define RT5651_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5651_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5651_WND_WIND_SFT 13 +#define RT5651_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5651_WND_STRONG_SFT 12 +enum { + RT5651_NO_WIND, + RT5651_BREEZE, + RT5651_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5651_DP_ATT_MASK (0x3 << 14) +#define RT5651_DP_ATT_SFT 14 +#define RT5651_DP_SPK_MASK (0x1 << 10) +#define RT5651_DP_SPK_SFT 10 +#define RT5651_DP_SPK_DIS (0x0 << 10) +#define RT5651_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5651_EQ_PRE_VOL_MASK (0xffff) +#define RT5651_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5651_EQ_PST_VOL_MASK (0xffff) +#define RT5651_EQ_PST_VOL_SFT 0 + +/* System Clock Source */ +enum { + RT5651_SCLK_S_MCLK, + RT5651_SCLK_S_PLL1, + RT5651_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5651_PLL1_S_MCLK, + RT5651_PLL1_S_BCLK1, + RT5651_PLL1_S_BCLK2, +}; + +enum { + RT5651_AIF1, + RT5651_AIF2, + RT5651_AIFS, +}; + +struct rt5651_pll_code { + bool m_bp; /* Indicates bypass m code or not. */ + int m_code; + int n_code; + int k_code; +}; + +struct rt5651_priv { + struct snd_soc_codec *codec; + struct rt5651_platform_data pdata; + struct regmap *regmap; + + int sysclk; + int sysclk_src; + int lrck[RT5651_AIFS]; + int bclk[RT5651_AIFS]; + int master[RT5651_AIFS]; + + struct rt5651_pll_code pll_code; + int pll_src; + int pll_in; + int pll_out; + + int dmic_en; + bool hp_mute; +}; + +#endif /* __RT5651_H__ */ -- cgit v1.2.3 From 79f7ae7c45a6ccf04e2908337461dee615f6afb0 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Fri, 14 Mar 2014 21:11:56 +0900 Subject: mmc: clarify DDR timing mode between SD-UHS and eMMC This change distinguishes DDR timing mode of current mixed usage to clarify device type. Signed-off-by: Seungwon Jeon Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/debugfs.c | 3 +++ drivers/mmc/core/mmc.c | 2 +- include/linux/mmc/host.h | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 54829c0ed000..509229b48b55 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -135,6 +135,9 @@ static int mmc_ios_show(struct seq_file *s, void *data) case MMC_TIMING_UHS_DDR50: str = "sd uhs DDR50"; break; + case MMC_TIMING_MMC_DDR52: + str = "mmc DDR52"; + break; case MMC_TIMING_MMC_HS200: str = "mmc high-speed SDR200"; break; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 1ab5f3a0af5b..e22d8515ff97 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1264,7 +1264,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, goto err; } mmc_card_set_ddr_mode(card); - mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50); + mmc_set_timing(card->host, MMC_TIMING_MMC_DDR52); mmc_set_bus_width(card->host, bus_width); } } diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index cb61ea4d6945..35354207e71f 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -58,7 +58,8 @@ struct mmc_ios { #define MMC_TIMING_UHS_SDR50 5 #define MMC_TIMING_UHS_SDR104 6 #define MMC_TIMING_UHS_DDR50 7 -#define MMC_TIMING_MMC_HS200 8 +#define MMC_TIMING_MMC_DDR52 8 +#define MMC_TIMING_MMC_HS200 9 #define MMC_SDR_MODE 0 #define MMC_1_2V_DDR_MODE 1 -- cgit v1.2.3 From 7ed823264845f7c2f14d3278405e593ddfdc50f6 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 4 Apr 2014 12:37:19 +0800 Subject: ACPICA: gcc-specific: Fix possible issue with the strchr function. Some versions of gcc implement strchr via a macro, which either contains bugs or can provoke a bug in the compiler. This change fixes a possible compile-time error when using this function. The problem is usually seen when compiling the getopt.c module. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/platform/acgcc.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/acpi/platform/acgcc.h b/include/acpi/platform/acgcc.h index a476b9118b49..384875da3713 100644 --- a/include/acpi/platform/acgcc.h +++ b/include/acpi/platform/acgcc.h @@ -64,4 +64,15 @@ */ #define ACPI_UNUSED_VAR __attribute__ ((unused)) +/* + * Some versions of gcc implement strchr() with a buggy macro. So, + * undef it here. Prevents error messages of this form (usually from the + * file getopt.c): + * + * error: logical '&&' with non-zero constant will always evaluate as true + */ +#ifdef strchr +#undef strchr +#endif + #endif /* __ACGCC_H__ */ -- cgit v1.2.3 From a3a80da3ef396c5c64ade0c834a8145d3f283a24 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 4 Apr 2014 12:37:59 +0800 Subject: ACPICA: Clean up comment divergences in aclinux.h When the following commmit is back ported to ACPICA, comments have been updated: Subject: ACPICA: Linux-specific header: Update support for Linux/acpi applications. This patch back ports the differences between the ACPICA upstream and Linux. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore [rjw: Subject] Signed-off-by: Rafael J. Wysocki --- include/acpi/platform/aclinux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 93c55ed7c53d..0d94be8531b5 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -91,7 +91,7 @@ #include #include -/* Disable kernel specific declarators */ +/* Define/disable kernel-specific declarators */ #ifndef __init #define __init -- cgit v1.2.3 From 72bdad969271b713e109fd747111d39a0b41d289 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 4 Apr 2014 12:38:05 +0800 Subject: ACPICA: Linux-specific header: Add support for PPC64 compilation. Adds PPC64 as a 64-bit architecture. Colin Ian King. Signed-off-by: Colin Ian King Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/platform/aclinux.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 0d94be8531b5..f2429094b9b9 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -106,7 +106,8 @@ #define ACPI_FLUSH_CPU_CACHE() #define ACPI_CAST_PTHREAD_T(pthread) ((acpi_thread_id) (pthread)) -#if defined(__ia64__) || defined(__x86_64__) || defined(__aarch64__) +#if defined(__ia64__) || defined(__x86_64__) ||\ + defined(__aarch64__) || defined(__PPC64__) #define ACPI_MACHINE_WIDTH 64 #define COMPILER_DEPENDENT_INT64 long #define COMPILER_DEPENDENT_UINT64 unsigned long -- cgit v1.2.3 From 8a216d7f6aa94c3e252bbfdb2c422e2d0380084e Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 4 Apr 2014 12:39:04 +0800 Subject: ACPICA: Tables: Cleanup ACPI_TABLE_ORIGIN_xxx flags. This patch refines ACPI_TABLE_ORIGIN_xxx flags. No functional changes. The previous commits have introduced the following internal APIs: 1. acpi_tb_acquire_table: Acquire struct acpi_table_header according to ACPI_TABLE_ORIGIN_xxx flags. 2. acpi_tb_release_table: Release struct acpi_table_header according to ACPI_TABLE_ORIGIN_xxx flags. 3. acpi_tb_install_table: Make struct acpi_table_desc.Address not NULL according to ACPI_TABLE_ORIGIN_xxx flags. 4. acpi_tb_uninstall_table: Make struct acpi_table_desc.Address NULL according to ACPI_TABLE_ORIGIN_xxx flags. 5. acpi_tb_validate_table: Make struct acpi_table_desc.Pointer not NULL according to ACPI_TABLE_ORIGIN_xxx flags. 6. acpi_tb_invalidate_table: Make struct acpi_table_desc.Pointer NULL according to ACPI_TABLE_ORIGIN_xxx flags. It thus detects that the ACPI_TABLE_ORIGIN_UNKNOWN is redundant to ACPI_TABLE_ORIGIN_OVERRIDE. The ACPI_TABLE_ORIGIN_xxTERN_VIRTUAL flags are named as VIRTUAL in order not to confuse with x86 logical address, this patch also renames all "logical override" into "virtual override". Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/exconfig.c | 2 +- drivers/acpi/acpica/tbinstal.c | 31 ++++++++++++++----------------- drivers/acpi/acpica/tbutils.c | 4 ++-- drivers/acpi/acpica/tbxface.c | 2 +- drivers/acpi/acpica/tbxfload.c | 2 +- include/acpi/actbl.h | 11 +++++------ 6 files changed, 24 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 52ea900c41a1..4dfe6c07b004 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -483,7 +483,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, ACPI_INFO((AE_INFO, "Dynamic OEM Table Load:")); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); status = acpi_tb_install_non_fixed_table(ACPI_PTR_TO_PHYSADDR(table), - ACPI_TABLE_ORIGIN_ALLOCATED, + ACPI_TABLE_ORIGIN_INTERN_VIRTUAL, TRUE, &table_index); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index 099e678edcb2..cf1ccc576629 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -86,15 +86,14 @@ acpi_tb_acquire_table(struct acpi_table_desc *table_desc, struct acpi_table_header *table = NULL; switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) { - case ACPI_TABLE_ORIGIN_MAPPED: + case ACPI_TABLE_ORIGIN_INTERN_PHYSICAL: table = acpi_os_map_memory(table_desc->address, table_desc->length); break; - case ACPI_TABLE_ORIGIN_ALLOCATED: - case ACPI_TABLE_ORIGIN_UNKNOWN: - case ACPI_TABLE_ORIGIN_OVERRIDE: + case ACPI_TABLE_ORIGIN_INTERN_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL: table = ACPI_CAST_PTR(struct acpi_table_header, @@ -140,14 +139,13 @@ acpi_tb_release_table(struct acpi_table_header *table, u32 table_length, u8 table_flags) { switch (table_flags & ACPI_TABLE_ORIGIN_MASK) { - case ACPI_TABLE_ORIGIN_MAPPED: + case ACPI_TABLE_ORIGIN_INTERN_PHYSICAL: acpi_os_unmap_memory(table, table_length); break; - case ACPI_TABLE_ORIGIN_ALLOCATED: - case ACPI_TABLE_ORIGIN_UNKNOWN: - case ACPI_TABLE_ORIGIN_OVERRIDE: + case ACPI_TABLE_ORIGIN_INTERN_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL: default: break; @@ -333,7 +331,7 @@ acpi_tb_acquire_temporal_table(struct acpi_table_desc *table_desc, struct acpi_table_header *table_header; switch (flags & ACPI_TABLE_ORIGIN_MASK) { - case ACPI_TABLE_ORIGIN_MAPPED: + case ACPI_TABLE_ORIGIN_INTERN_PHYSICAL: /* Try to obtain the length of the table */ @@ -348,9 +346,8 @@ acpi_tb_acquire_temporal_table(struct acpi_table_desc *table_desc, sizeof(struct acpi_table_header)); return (AE_OK); - case ACPI_TABLE_ORIGIN_ALLOCATED: - case ACPI_TABLE_ORIGIN_UNKNOWN: - case ACPI_TABLE_ORIGIN_OVERRIDE: + case ACPI_TABLE_ORIGIN_INTERN_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL: table_header = ACPI_CAST_PTR(struct acpi_table_header, address); if (!table_header) { @@ -473,7 +470,7 @@ acpi_tb_install_fixed_table(acpi_physical_address address, /* Fill a table descriptor for validation */ status = acpi_tb_acquire_temporal_table(&new_table_desc, address, - ACPI_TABLE_ORIGIN_MAPPED); + ACPI_TABLE_ORIGIN_INTERN_PHYSICAL); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, "Could not acquire table length at %p", ACPI_CAST_PTR(void, address))); @@ -546,7 +543,7 @@ acpi_tb_is_equivalent_table(struct acpi_table_desc *table_desc, u32 table_index) * * FUNCTION: acpi_tb_install_non_fixed_table * - * PARAMETERS: address - Address of the table (might be a logical + * PARAMETERS: address - Address of the table (might be a virtual * address depending on the table_flags) * flags - Flags for the table * reload - Whether reload should be performed @@ -720,7 +717,7 @@ void acpi_tb_override_table(struct acpi_table_desc *old_table_desc) if (ACPI_SUCCESS(status) && table) { acpi_tb_acquire_temporal_table(&new_table_desc, ACPI_PTR_TO_PHYSADDR(table), - ACPI_TABLE_ORIGIN_OVERRIDE); + ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL); override_type = "Logical"; goto finish_override; } @@ -731,7 +728,7 @@ void acpi_tb_override_table(struct acpi_table_desc *old_table_desc) &address, &length); if (ACPI_SUCCESS(status) && address && length) { acpi_tb_acquire_temporal_table(&new_table_desc, address, - ACPI_TABLE_ORIGIN_MAPPED); + ACPI_TABLE_ORIGIN_INTERN_PHYSICAL); override_type = "Physical"; goto finish_override; } @@ -928,7 +925,7 @@ void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc) acpi_tb_invalidate_table(table_desc); if ((table_desc->flags & ACPI_TABLE_ORIGIN_MASK) == - ACPI_TABLE_ORIGIN_ALLOCATED) { + ACPI_TABLE_ORIGIN_INTERN_VIRTUAL) { ACPI_FREE(ACPI_CAST_PTR(void, table_desc->address)); } diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index e58dfbf9dd3e..1bf9de7c6636 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -182,7 +182,7 @@ struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index) acpi_tb_install_table(&acpi_gbl_root_table_list. tables[ACPI_TABLE_INDEX_DSDT], ACPI_PTR_TO_PHYSADDR(new_table), - ACPI_TABLE_ORIGIN_ALLOCATED, new_table); + ACPI_TABLE_ORIGIN_INTERN_VIRTUAL, new_table); ACPI_INFO((AE_INFO, "Forced DSDT copy: length 0x%05X copied locally, original unmapped", @@ -473,7 +473,7 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) acpi_tb_install_non_fixed_table(acpi_tb_get_root_table_entry (table_entry, table_entry_size), - ACPI_TABLE_ORIGIN_MAPPED, + ACPI_TABLE_ORIGIN_INTERN_PHYSICAL, FALSE, &table_index); if (ACPI_SUCCESS(status) && diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index ae3fe4d41137..19c0b13ad4c2 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -233,7 +233,7 @@ acpi_get_table_header(char *signature, if (!acpi_gbl_root_table_list.tables[i].pointer) { if ((acpi_gbl_root_table_list.tables[i].flags & ACPI_TABLE_ORIGIN_MASK) == - ACPI_TABLE_ORIGIN_MAPPED) { + ACPI_TABLE_ORIGIN_INTERN_PHYSICAL) { header = acpi_os_map_memory(acpi_gbl_root_table_list. tables[i].address, diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 77e8d269e491..62bbd384ac49 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -248,7 +248,7 @@ acpi_status acpi_load_table(struct acpi_table_header *table) ACPI_INFO((AE_INFO, "Host-directed Dynamic ACPI Table Load:")); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); status = acpi_tb_install_non_fixed_table(ACPI_PTR_TO_PHYSADDR(table), - ACPI_TABLE_ORIGIN_UNKNOWN, + ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL, TRUE, &table_index); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 3b30e36b53b5..0cdf4cc10f18 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -367,12 +367,11 @@ struct acpi_table_desc { /* Masks for Flags field above */ -#define ACPI_TABLE_ORIGIN_UNKNOWN (0) -#define ACPI_TABLE_ORIGIN_MAPPED (1) -#define ACPI_TABLE_ORIGIN_ALLOCATED (2) -#define ACPI_TABLE_ORIGIN_OVERRIDE (4) -#define ACPI_TABLE_ORIGIN_MASK (7) -#define ACPI_TABLE_IS_LOADED (8) +#define ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL (0) /* Virtual address, external maintained */ +#define ACPI_TABLE_ORIGIN_INTERN_PHYSICAL (1) /* Physical address, internal mapped */ +#define ACPI_TABLE_ORIGIN_INTERN_VIRTUAL (2) /* Virtual address, internal allocated */ +#define ACPI_TABLE_ORIGIN_MASK (3) +#define ACPI_TABLE_IS_LOADED (8) /* * Get the remaining ACPI tables -- cgit v1.2.3 From a94e88cdd8057fe8ea84bbb6d9a89a823c7bc49b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 4 Apr 2014 12:39:11 +0800 Subject: ACPICA: Tables: Avoid SSDT installation with acpi_gbl_disable_ssdt_table_load. It is reported that when acpi_gbl_disable_ssdt_table_load is specified, user still can see it installed into /sys/firmware/acpi/tables on Linux boxes. This is because the option only stops table "loading", but doesn't stop table "installing", thus it is still in the acpi_gbl_root_table_list. With previous cleanups, it is possible to prevent SSDT installations to make it not such confusing. The global variable is also renamed. Lv Zheng. Signed-off-by: Lv Zheng [rjw: Subject] Signed-off-by: Rafael J. Wysocki --- Documentation/kernel-parameters.txt | 10 +++++++++- drivers/acpi/acpica/acglobal.h | 4 ++-- drivers/acpi/acpica/tbinstal.c | 12 ++++++++++++ drivers/acpi/acpica/tbxfload.c | 13 ------------- drivers/acpi/osl.c | 11 +++++------ include/acpi/acpixf.h | 2 +- 6 files changed, 29 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 03e50b4883a8..fbb58d790ec7 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -237,7 +237,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted. This feature is enabled by default. This option allows to turn off the feature. - acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT + acpi_no_static_ssdt [HW,ACPI] + Disable installation of static SSDTs at early boot time + By default, SSDTs contained in the RSDT/XSDT will be + installed automatically and they will appear under + /sys/firmware/acpi/tables. + This option turns off this feature. + Note that specifying this option does not affect + dynamic table installation which will install SSDT + tables to /sys/firmware/acpi/tables/dynamic. acpica_no_return_repair [HW, ACPI] Disable AML predefined validation mechanism diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 71bb5b50c656..0cac564ffe93 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -160,10 +160,10 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_truncate_io_addresses, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_auto_repair, FALSE); /* - * Optionally do not load any SSDTs from the RSDT/XSDT during initialization. + * Optionally do not install any SSDTs from the RSDT/XSDT during initialization. * This can be useful for debugging ACPI problems on some machines. */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_load, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_install, FALSE); /* * We keep track of the latest version of Windows that has been requested by diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index cf1ccc576629..de10d3245d9c 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -580,6 +580,18 @@ acpi_tb_install_non_fixed_table(acpi_physical_address address, return_ACPI_STATUS(status); } + /* + * Optionally do not load any SSDTs from the RSDT/XSDT. This can + * be useful for debugging ACPI problems on some machines. + */ + if (!reload && acpi_gbl_disable_ssdt_table_install && + ACPI_COMPARE_NAME(&new_table_desc.signature, ACPI_SIG_SSDT)) { + ACPI_INFO((AE_INFO, "Ignoring installation of %4.4s at %p", + new_table_desc.signature.ascii, ACPI_CAST_PTR(void, + address))); + goto release_and_exit; + } + /* Validate and verify a table before installation */ status = acpi_tb_verify_table(&new_table_desc, NULL); diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 62bbd384ac49..3f9eaf5c9fb7 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -179,19 +179,6 @@ static acpi_status acpi_tb_load_namespace(void) continue; } - /* - * Optionally do not load any SSDTs from the RSDT/XSDT. This can - * be useful for debugging ACPI problems on some machines. - */ - if (acpi_gbl_disable_ssdt_table_load) { - ACPI_INFO((AE_INFO, "Ignoring %4.4s at %p", - acpi_gbl_root_table_list.tables[i].signature. - ascii, ACPI_CAST_PTR(void, - acpi_gbl_root_table_list. - tables[i].address))); - continue; - } - /* Ignore errors while loading tables, get as many as possible */ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 6776c599816f..9aeae41e22fb 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1770,16 +1770,15 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) } #endif -static int __init acpi_no_auto_ssdt_setup(char *s) +static int __init acpi_no_static_ssdt_setup(char *s) { - printk(KERN_NOTICE PREFIX "SSDT auto-load disabled\n"); + acpi_gbl_disable_ssdt_table_install = TRUE; + pr_info("ACPI: static SSDT installation disabled\n"); - acpi_gbl_disable_ssdt_table_load = TRUE; - - return 1; + return 0; } -__setup("acpi_no_auto_ssdt", acpi_no_auto_ssdt_setup); +early_param("acpi_no_static_ssdt", acpi_no_static_ssdt_setup); static int __init acpi_disable_return_repair(char *s) { diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 44f5e9749601..2280c190536d 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -75,7 +75,7 @@ extern u8 acpi_gbl_auto_serialize_methods; extern u8 acpi_gbl_copy_dsdt_locally; extern u8 acpi_gbl_create_osi_method; extern u8 acpi_gbl_disable_auto_repair; -extern u8 acpi_gbl_disable_ssdt_table_load; +extern u8 acpi_gbl_disable_ssdt_table_install; extern u8 acpi_gbl_do_not_use_xsdt; extern u8 acpi_gbl_enable_aml_debug_object; extern u8 acpi_gbl_enable_interpreter_slack; -- cgit v1.2.3 From caf4a15c5f930aae41951b4916289e3e59dda8eb Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 4 Apr 2014 12:39:18 +0800 Subject: ACPICA: Tables: Add acpi_install_table() API for early table installation. This patch adds a new API - acpi_install_table(). OSPMs can use this API to install tables during early boot stage. Lv Zheng. References: https://lkml.org/lkml/2014/2/28/372 Cc: Thomas Renninger Signed-off-by: Lv Zheng Signed-off-by: Bob Moore [rjw: Subject] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/actables.h | 6 ++++-- drivers/acpi/acpica/exconfig.c | 2 +- drivers/acpi/acpica/tbinstal.c | 16 +++++++++++----- drivers/acpi/acpica/tbutils.c | 2 +- drivers/acpi/acpica/tbxfload.c | 42 ++++++++++++++++++++++++++++++++++++++++-- include/acpi/acpixf.h | 3 +++ 6 files changed, 60 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 32aec48eb2d8..3d20a96f6f09 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -92,7 +92,8 @@ acpi_tb_release_table(struct acpi_table_header *table, acpi_status acpi_tb_install_non_fixed_table(acpi_physical_address address, - u8 flags, u8 reload, u32 *table_index); + u8 flags, + u8 reload, u8 override, u32 *table_index); acpi_status acpi_tb_store_table(acpi_physical_address address, @@ -142,7 +143,8 @@ acpi_tb_install_table(struct acpi_table_desc *table_desc, void acpi_tb_install_and_override_table(u32 table_index, - struct acpi_table_desc *new_table_desc); + struct acpi_table_desc *new_table_desc, + u8 override); acpi_status acpi_tb_install_fixed_table(acpi_physical_address address, diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 4dfe6c07b004..815003d81b5c 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -484,7 +484,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); status = acpi_tb_install_non_fixed_table(ACPI_PTR_TO_PHYSADDR(table), ACPI_TABLE_ORIGIN_INTERN_VIRTUAL, - TRUE, &table_index); + TRUE, TRUE, &table_index); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index de10d3245d9c..9835213269e6 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -394,6 +394,7 @@ static void acpi_tb_release_temporal_table(struct acpi_table_desc *table_desc) * * PARAMETERS: table_index - Index into root table array * new_table_desc - New table descriptor to install + * override - Whether override should be performed * * RETURN: None * @@ -406,7 +407,8 @@ static void acpi_tb_release_temporal_table(struct acpi_table_desc *table_desc) void acpi_tb_install_and_override_table(u32 table_index, - struct acpi_table_desc *new_table_desc) + struct acpi_table_desc *new_table_desc, + u8 override) { if (table_index >= acpi_gbl_root_table_list.current_table_count) { return; @@ -419,7 +421,9 @@ acpi_tb_install_and_override_table(u32 table_index, * one if desired. Any table within the RSDT/XSDT can be replaced, * including the DSDT which is pointed to by the FADT. */ - acpi_tb_override_table(new_table_desc); + if (override) { + acpi_tb_override_table(new_table_desc); + } acpi_tb_install_table(&acpi_gbl_root_table_list.tables[table_index], new_table_desc->address, new_table_desc->flags, @@ -484,7 +488,7 @@ acpi_tb_install_fixed_table(acpi_physical_address address, goto release_and_exit; } - acpi_tb_install_and_override_table(table_index, &new_table_desc); + acpi_tb_install_and_override_table(table_index, &new_table_desc, TRUE); release_and_exit: @@ -547,6 +551,7 @@ acpi_tb_is_equivalent_table(struct acpi_table_desc *table_desc, u32 table_index) * address depending on the table_flags) * flags - Flags for the table * reload - Whether reload should be performed + * override - Whether override should be performed * table_index - Where the table index is returned * * RETURN: Status @@ -562,7 +567,8 @@ acpi_tb_is_equivalent_table(struct acpi_table_desc *table_desc, u32 table_index) acpi_status acpi_tb_install_non_fixed_table(acpi_physical_address address, - u8 flags, u8 reload, u32 *table_index) + u8 flags, + u8 reload, u8 override, u32 *table_index) { u32 i; acpi_status status = AE_OK; @@ -687,7 +693,7 @@ acpi_tb_install_non_fixed_table(acpi_physical_address address, goto release_and_exit; } *table_index = i; - acpi_tb_install_and_override_table(i, &new_table_desc); + acpi_tb_install_and_override_table(i, &new_table_desc, override); release_and_exit: diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index 1bf9de7c6636..aa11949815df 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -474,7 +474,7 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) (table_entry, table_entry_size), ACPI_TABLE_ORIGIN_INTERN_PHYSICAL, - FALSE, &table_index); + FALSE, TRUE, &table_index); if (ACPI_SUCCESS(status) && ACPI_COMPARE_NAME(&acpi_gbl_root_table_list. diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 3f9eaf5c9fb7..529f633efa55 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -193,6 +193,45 @@ unlock_and_exit: return_ACPI_STATUS(status); } +/******************************************************************************* + * + * FUNCTION: acpi_install_table + * + * PARAMETERS: address - Address of the ACPI table to be installed. + * physical - Whether the address is a physical table + * address or not + * + * RETURN: Status + * + * DESCRIPTION: Dynamically install an ACPI table. + * Note: This function should only be invoked after + * acpi_initialize_tables() and before acpi_load_tables(). + * + ******************************************************************************/ + +acpi_status __init +acpi_install_table(acpi_physical_address address, u8 physical) +{ + acpi_status status; + u8 flags; + u32 table_index; + + ACPI_FUNCTION_TRACE(acpi_install_table); + + if (physical) { + flags = ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL; + } else { + flags = ACPI_TABLE_ORIGIN_INTERN_PHYSICAL; + } + + status = acpi_tb_install_non_fixed_table(address, flags, + FALSE, FALSE, &table_index); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_install_table) + /******************************************************************************* * * FUNCTION: acpi_load_table @@ -209,7 +248,6 @@ unlock_and_exit: * to ensure that the table is not deleted or unmapped. * ******************************************************************************/ - acpi_status acpi_load_table(struct acpi_table_header *table) { acpi_status status; @@ -236,7 +274,7 @@ acpi_status acpi_load_table(struct acpi_table_header *table) (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); status = acpi_tb_install_non_fixed_table(ACPI_PTR_TO_PHYSADDR(table), ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL, - TRUE, &table_index); + TRUE, FALSE, &table_index); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { goto unlock_and_exit; diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 2280c190536d..8dc934073620 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -164,6 +164,9 @@ acpi_decode_pld_buffer(u8 *in_buffer, /* * ACPI table load/unload interfaces */ +acpi_status __init +acpi_install_table(acpi_physical_address address, u8 physical); + acpi_status acpi_load_table(struct acpi_table_header *table); acpi_status acpi_unload_parent_table(acpi_handle object); -- cgit v1.2.3 From ed6f1d44dae8a4eec42a74acae95cc177ee2e1ad Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 4 Apr 2014 12:39:26 +0800 Subject: ACPICA: Table Manager: Misc cleanup and renames, no functional change. Some various cleanups and renames. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/actables.h | 18 ++--- drivers/acpi/acpica/exconfig.c | 8 +- drivers/acpi/acpica/tbinstal.c | 179 ++++++++++++++++++++++------------------- drivers/acpi/acpica/tbutils.c | 20 ++--- drivers/acpi/acpica/tbxface.c | 2 +- drivers/acpi/acpica/tbxfload.c | 16 ++-- include/acpi/actbl.h | 6 +- 7 files changed, 133 insertions(+), 116 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 3d20a96f6f09..ade430c6004f 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -91,9 +91,9 @@ acpi_tb_release_table(struct acpi_table_header *table, u32 table_length, u8 table_flags); acpi_status -acpi_tb_install_non_fixed_table(acpi_physical_address address, - u8 flags, - u8 reload, u8 override, u32 *table_index); +acpi_tb_install_standard_table(acpi_physical_address address, + u8 flags, + u8 reload, u8 override, u32 *table_index); acpi_status acpi_tb_store_table(acpi_physical_address address, @@ -137,14 +137,14 @@ void acpi_tb_check_dsdt_header(void); struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index); void -acpi_tb_install_table(struct acpi_table_desc *table_desc, - acpi_physical_address address, - u8 flags, struct acpi_table_header *table); +acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc, + acpi_physical_address address, + u8 flags, struct acpi_table_header *table); void -acpi_tb_install_and_override_table(u32 table_index, - struct acpi_table_desc *new_table_desc, - u8 override); +acpi_tb_install_table_with_override(u32 table_index, + struct acpi_table_desc *new_table_desc, + u8 override); acpi_status acpi_tb_install_fixed_table(acpi_physical_address address, diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 815003d81b5c..7d2949420db7 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -482,9 +482,11 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, ACPI_INFO((AE_INFO, "Dynamic OEM Table Load:")); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - status = acpi_tb_install_non_fixed_table(ACPI_PTR_TO_PHYSADDR(table), - ACPI_TABLE_ORIGIN_INTERN_VIRTUAL, - TRUE, TRUE, &table_index); + + status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table), + ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, + TRUE, TRUE, &table_index); + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index 9835213269e6..50a6f229633c 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -51,12 +51,12 @@ ACPI_MODULE_NAME("tbinstal") /* Local prototypes */ static acpi_status -acpi_tb_acquire_temporal_table(struct acpi_table_desc *table_desc, - acpi_physical_address address, u8 flags); +acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc, + acpi_physical_address address, u8 flags); -static void acpi_tb_release_temporal_table(struct acpi_table_desc *table_desc); +static void acpi_tb_release_temp_table(struct acpi_table_desc *table_desc); -static acpi_status acpi_tb_acquire_root_table_entry(u32 *table_index); +static acpi_status acpi_tb_get_root_table_entry(u32 *table_index); static u8 acpi_tb_is_equivalent_table(struct acpi_table_desc *table_desc, @@ -73,8 +73,8 @@ acpi_tb_is_equivalent_table(struct acpi_table_desc *table_desc, * * RETURN: Status * - * DESCRIPTION: Acquire a table. It can be used for tables not maintained in - * acpi_gbl_root_table_list. + * DESCRIPTION: Acquire an ACPI table. It can be used for tables not + * maintained in the acpi_gbl_root_table_list. * ******************************************************************************/ @@ -86,14 +86,14 @@ acpi_tb_acquire_table(struct acpi_table_desc *table_desc, struct acpi_table_header *table = NULL; switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) { - case ACPI_TABLE_ORIGIN_INTERN_PHYSICAL: + case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: table = acpi_os_map_memory(table_desc->address, table_desc->length); break; - case ACPI_TABLE_ORIGIN_INTERN_VIRTUAL: - case ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL: + case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: table = ACPI_CAST_PTR(struct acpi_table_header, @@ -116,7 +116,6 @@ acpi_tb_acquire_table(struct acpi_table_desc *table_desc, *table_ptr = table; *table_length = table_desc->length; *table_flags = table_desc->flags; - return (AE_OK); } @@ -130,7 +129,7 @@ acpi_tb_acquire_table(struct acpi_table_desc *table_desc, * * RETURN: None * - * DESCRIPTION: Release a table. The reversal of acpi_tb_acquire_table(). + * DESCRIPTION: Release a table. The inverse of acpi_tb_acquire_table(). * ******************************************************************************/ @@ -138,14 +137,15 @@ void acpi_tb_release_table(struct acpi_table_header *table, u32 table_length, u8 table_flags) { + switch (table_flags & ACPI_TABLE_ORIGIN_MASK) { - case ACPI_TABLE_ORIGIN_INTERN_PHYSICAL: + case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: acpi_os_unmap_memory(table, table_length); break; - case ACPI_TABLE_ORIGIN_INTERN_VIRTUAL: - case ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL: + case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: default: break; @@ -193,7 +193,7 @@ acpi_status acpi_tb_validate_table(struct acpi_table_desc *table_desc) * * RETURN: None * - * DESCRIPTION: Invalidate one internal ACPI table, this is reversal of + * DESCRIPTION: Invalidate one internal ACPI table, this is the inverse of * acpi_tb_validate_table(). * ******************************************************************************/ @@ -278,7 +278,7 @@ invalidate_and_exit: /******************************************************************************* * - * FUNCTION: acpi_tb_install_table + * FUNCTION: acpi_tb_init_table_descriptor * * PARAMETERS: table_desc - Table descriptor * address - Physical address of the table @@ -287,17 +287,18 @@ invalidate_and_exit: * * RETURN: None * - * DESCRIPTION: Install an ACPI table into the global data structure. + * DESCRIPTION: Initialize a new table descriptor * ******************************************************************************/ void -acpi_tb_install_table(struct acpi_table_desc *table_desc, - acpi_physical_address address, - u8 flags, struct acpi_table_header *table) +acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc, + acpi_physical_address address, + u8 flags, struct acpi_table_header *table) { + /* - * Initialize the table entry. Set the pointer to NULL, since the + * Initialize the table descriptor. Set the pointer to NULL, since the * table is not fully mapped at this time. */ ACPI_MEMSET(table_desc, 0, sizeof(struct acpi_table_desc)); @@ -309,7 +310,7 @@ acpi_tb_install_table(struct acpi_table_desc *table_desc, /******************************************************************************* * - * FUNCTION: acpi_tb_acquire_temporal_table + * FUNCTION: acpi_tb_acquire_temp_table * * PARAMETERS: table_desc - Table descriptor to be acquired * address - Address of the table @@ -319,21 +320,21 @@ acpi_tb_install_table(struct acpi_table_desc *table_desc, * * DESCRIPTION: This function validates the table header to obtain the length * of a table and fills the table descriptor to make its state as - * "INSTALLED". Such table descriptor is only used for verified + * "INSTALLED". Such a table descriptor is only used for verified * installation. * ******************************************************************************/ static acpi_status -acpi_tb_acquire_temporal_table(struct acpi_table_desc *table_desc, - acpi_physical_address address, u8 flags) +acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc, + acpi_physical_address address, u8 flags) { struct acpi_table_header *table_header; switch (flags & ACPI_TABLE_ORIGIN_MASK) { - case ACPI_TABLE_ORIGIN_INTERN_PHYSICAL: + case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: - /* Try to obtain the length of the table */ + /* Get the length of the full table from the header */ table_header = acpi_os_map_memory(address, @@ -341,19 +342,23 @@ acpi_tb_acquire_temporal_table(struct acpi_table_desc *table_desc, if (!table_header) { return (AE_NO_MEMORY); } - acpi_tb_install_table(table_desc, address, flags, table_header); + + acpi_tb_init_table_descriptor(table_desc, address, flags, + table_header); acpi_os_unmap_memory(table_header, sizeof(struct acpi_table_header)); return (AE_OK); - case ACPI_TABLE_ORIGIN_INTERN_VIRTUAL: - case ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL: + case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: table_header = ACPI_CAST_PTR(struct acpi_table_header, address); if (!table_header) { return (AE_NO_MEMORY); } - acpi_tb_install_table(table_desc, address, flags, table_header); + + acpi_tb_init_table_descriptor(table_desc, address, flags, + table_header); return (AE_OK); default: @@ -368,21 +373,22 @@ acpi_tb_acquire_temporal_table(struct acpi_table_desc *table_desc, /******************************************************************************* * - * FUNCTION: acpi_tb_release_temporal_table + * FUNCTION: acpi_tb_release_temp_table * * PARAMETERS: table_desc - Table descriptor to be released * * RETURN: Status * - * DESCRIPTION: The reversal of acpi_tb_acquire_temporal_table(). + * DESCRIPTION: The inverse of acpi_tb_acquire_temp_table(). * ******************************************************************************/ -static void acpi_tb_release_temporal_table(struct acpi_table_desc *table_desc) +static void acpi_tb_release_temp_table(struct acpi_table_desc *table_desc) { + /* * Note that the .Address is maintained by the callers of - * acpi_tb_acquire_temporal_table(), thus do not invoke acpi_tb_uninstall_table() + * acpi_tb_acquire_temp_table(), thus do not invoke acpi_tb_uninstall_table() * where .Address will be freed. */ acpi_tb_invalidate_table(table_desc); @@ -390,7 +396,7 @@ static void acpi_tb_release_temporal_table(struct acpi_table_desc *table_desc) /******************************************************************************* * - * FUNCTION: acpi_tb_install_and_override_table + * FUNCTION: acpi_tb_install_table_with_override * * PARAMETERS: table_index - Index into root table array * new_table_desc - New table descriptor to install @@ -406,10 +412,11 @@ static void acpi_tb_release_temporal_table(struct acpi_table_desc *table_desc) ******************************************************************************/ void -acpi_tb_install_and_override_table(u32 table_index, - struct acpi_table_desc *new_table_desc, - u8 override) +acpi_tb_install_table_with_override(u32 table_index, + struct acpi_table_desc *new_table_desc, + u8 override) { + if (table_index >= acpi_gbl_root_table_list.current_table_count) { return; } @@ -425,9 +432,11 @@ acpi_tb_install_and_override_table(u32 table_index, acpi_tb_override_table(new_table_desc); } - acpi_tb_install_table(&acpi_gbl_root_table_list.tables[table_index], - new_table_desc->address, new_table_desc->flags, - new_table_desc->pointer); + acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list. + tables[table_index], + new_table_desc->address, + new_table_desc->flags, + new_table_desc->pointer); acpi_tb_print_table_header(new_table_desc->address, new_table_desc->pointer); @@ -473,8 +482,8 @@ acpi_tb_install_fixed_table(acpi_physical_address address, /* Fill a table descriptor for validation */ - status = acpi_tb_acquire_temporal_table(&new_table_desc, address, - ACPI_TABLE_ORIGIN_INTERN_PHYSICAL); + status = acpi_tb_acquire_temp_table(&new_table_desc, address, + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, "Could not acquire table length at %p", ACPI_CAST_PTR(void, address))); @@ -488,13 +497,13 @@ acpi_tb_install_fixed_table(acpi_physical_address address, goto release_and_exit; } - acpi_tb_install_and_override_table(table_index, &new_table_desc, TRUE); + acpi_tb_install_table_with_override(table_index, &new_table_desc, TRUE); release_and_exit: - /* Release the temporal table descriptor */ + /* Release the temporary table descriptor */ - acpi_tb_release_temporal_table(&new_table_desc); + acpi_tb_release_temp_table(&new_table_desc); return_ACPI_STATUS(status); } @@ -539,13 +548,12 @@ acpi_tb_is_equivalent_table(struct acpi_table_desc *table_desc, u32 table_index) /* Release the acquired table */ acpi_tb_release_table(table, table_length, table_flags); - return (is_equivalent); } /******************************************************************************* * - * FUNCTION: acpi_tb_install_non_fixed_table + * FUNCTION: acpi_tb_install_standard_table * * PARAMETERS: address - Address of the table (might be a virtual * address depending on the table_flags) @@ -557,7 +565,7 @@ acpi_tb_is_equivalent_table(struct acpi_table_desc *table_desc, u32 table_index) * RETURN: Status * * DESCRIPTION: This function is called to install an ACPI table that is - * neither DSDT nor FACS. + * neither DSDT nor FACS (a "standard" table.) * When this function is called by "Load" or "LoadTable" opcodes, * or by acpi_load_table() API, the "Reload" parameter is set. * After sucessfully returning from this function, table is @@ -566,20 +574,19 @@ acpi_tb_is_equivalent_table(struct acpi_table_desc *table_desc, u32 table_index) ******************************************************************************/ acpi_status -acpi_tb_install_non_fixed_table(acpi_physical_address address, - u8 flags, - u8 reload, u8 override, u32 *table_index) +acpi_tb_install_standard_table(acpi_physical_address address, + u8 flags, + u8 reload, u8 override, u32 *table_index) { u32 i; acpi_status status = AE_OK; struct acpi_table_desc new_table_desc; - ACPI_FUNCTION_TRACE(tb_install_non_fixed_table); + ACPI_FUNCTION_TRACE(tb_install_standard_table); - /* Acquire a temporal table descriptor for validation */ + /* Acquire a temporary table descriptor for validation */ - status = - acpi_tb_acquire_temporal_table(&new_table_desc, address, flags); + status = acpi_tb_acquire_temp_table(&new_table_desc, address, flags); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, "Could not acquire table length at %p", ACPI_CAST_PTR(void, address))); @@ -590,7 +597,8 @@ acpi_tb_install_non_fixed_table(acpi_physical_address address, * Optionally do not load any SSDTs from the RSDT/XSDT. This can * be useful for debugging ACPI problems on some machines. */ - if (!reload && acpi_gbl_disable_ssdt_table_install && + if (!reload && + acpi_gbl_disable_ssdt_table_install && ACPI_COMPARE_NAME(&new_table_desc.signature, ACPI_SIG_SSDT)) { ACPI_INFO((AE_INFO, "Ignoring installation of %4.4s at %p", new_table_desc.signature.ascii, ACPI_CAST_PTR(void, @@ -675,7 +683,7 @@ acpi_tb_install_non_fixed_table(acpi_physical_address address, * As we are going to return AE_OK to the caller, we should * take the responsibility of freeing the input descriptor. * Refill the input descriptor to ensure - * acpi_tb_install_and_override_table() can be called again to + * acpi_tb_install_table_with_override() can be called again to * indicate the re-installation. */ acpi_tb_uninstall_table(&new_table_desc); @@ -688,18 +696,19 @@ acpi_tb_install_non_fixed_table(acpi_physical_address address, /* Add the table to the global root table list */ - status = acpi_tb_acquire_root_table_entry(&i); + status = acpi_tb_get_root_table_entry(&i); if (ACPI_FAILURE(status)) { goto release_and_exit; } + *table_index = i; - acpi_tb_install_and_override_table(i, &new_table_desc, override); + acpi_tb_install_table_with_override(i, &new_table_desc, override); release_and_exit: - /* Release the temporal table descriptor */ + /* Release the temporary table descriptor */ - acpi_tb_release_temporal_table(&new_table_desc); + acpi_tb_release_temp_table(&new_table_desc); return_ACPI_STATUS(status); } @@ -733,9 +742,9 @@ void acpi_tb_override_table(struct acpi_table_desc *old_table_desc) status = acpi_os_table_override(old_table_desc->pointer, &table); if (ACPI_SUCCESS(status) && table) { - acpi_tb_acquire_temporal_table(&new_table_desc, - ACPI_PTR_TO_PHYSADDR(table), - ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL); + acpi_tb_acquire_temp_table(&new_table_desc, + ACPI_PTR_TO_PHYSADDR(table), + ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL); override_type = "Logical"; goto finish_override; } @@ -745,8 +754,8 @@ void acpi_tb_override_table(struct acpi_table_desc *old_table_desc) status = acpi_os_physical_table_override(old_table_desc->pointer, &address, &length); if (ACPI_SUCCESS(status) && address && length) { - acpi_tb_acquire_temporal_table(&new_table_desc, address, - ACPI_TABLE_ORIGIN_INTERN_PHYSICAL); + acpi_tb_acquire_temp_table(&new_table_desc, address, + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL); override_type = "Physical"; goto finish_override; } @@ -776,13 +785,14 @@ finish_override: * Replace the original table descriptor and keep its state as * "VALIDATED". */ - acpi_tb_install_table(old_table_desc, new_table_desc.address, - new_table_desc.flags, new_table_desc.pointer); + acpi_tb_init_table_descriptor(old_table_desc, new_table_desc.address, + new_table_desc.flags, + new_table_desc.pointer); acpi_tb_validate_table(old_table_desc); - /* Release the temporal table descriptor */ + /* Release the temporary table descriptor */ - acpi_tb_release_temporal_table(&new_table_desc); + acpi_tb_release_temp_table(&new_table_desc); } /******************************************************************************* @@ -851,7 +861,7 @@ acpi_status acpi_tb_resize_root_table_list(void) /******************************************************************************* * - * FUNCTION: acpi_tb_acquire_root_table_entry + * FUNCTION: acpi_tb_get_root_table_entry * * PARAMETERS: table_index - Where table index is returned * @@ -861,7 +871,7 @@ acpi_status acpi_tb_resize_root_table_list(void) * ******************************************************************************/ -static acpi_status acpi_tb_acquire_root_table_entry(u32 *table_index) +static acpi_status acpi_tb_get_root_table_entry(u32 *table_index) { acpi_status status; @@ -887,7 +897,8 @@ static acpi_status acpi_tb_acquire_root_table_entry(u32 *table_index) * PARAMETERS: address - Table address * table - Table header * length - Table length - * flags - flags + * flags - Install flags + * table_index - Where the table index is returned * * RETURN: Status and table index. * @@ -903,7 +914,7 @@ acpi_tb_store_table(acpi_physical_address address, acpi_status status; struct acpi_table_desc *table_desc; - status = acpi_tb_acquire_root_table_entry(table_index); + status = acpi_tb_get_root_table_entry(table_index); if (ACPI_FAILURE(status)) { return (status); } @@ -911,9 +922,8 @@ acpi_tb_store_table(acpi_physical_address address, /* Initialize added table */ table_desc = &acpi_gbl_root_table_list.tables[*table_index]; - acpi_tb_install_table(table_desc, address, flags, table); + acpi_tb_init_table_descriptor(table_desc, address, flags, table); table_desc->pointer = table; - return (AE_OK); } @@ -943,12 +953,11 @@ void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc) acpi_tb_invalidate_table(table_desc); if ((table_desc->flags & ACPI_TABLE_ORIGIN_MASK) == - ACPI_TABLE_ORIGIN_INTERN_VIRTUAL) { + ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL) { ACPI_FREE(ACPI_CAST_PTR(void, table_desc->address)); } table_desc->address = ACPI_PTR_TO_PHYSADDR(NULL); - return_VOID; } @@ -991,8 +1000,8 @@ void acpi_tb_terminate(void) acpi_gbl_root_table_list.current_table_count = 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ACPI Tables freed\n")); - (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); return_VOID; } @@ -1074,8 +1083,10 @@ acpi_status acpi_tb_allocate_owner_id(u32 table_index) (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); if (table_index < acpi_gbl_root_table_list.current_table_count) { - status = acpi_ut_allocate_owner_id - (&(acpi_gbl_root_table_list.tables[table_index].owner_id)); + status = + acpi_ut_allocate_owner_id(& + (acpi_gbl_root_table_list. + tables[table_index].owner_id)); } (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); @@ -1146,7 +1157,7 @@ acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id * owner_id) * * FUNCTION: acpi_tb_is_table_loaded * - * PARAMETERS: table_index - Table index + * PARAMETERS: table_index - Index into the root table * * RETURN: Table Loaded Flag * diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index aa11949815df..6c31d77cece0 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -179,10 +179,12 @@ struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index) ACPI_MEMCPY(new_table, table_desc->pointer, table_desc->length); acpi_tb_uninstall_table(table_desc); - acpi_tb_install_table(&acpi_gbl_root_table_list. - tables[ACPI_TABLE_INDEX_DSDT], - ACPI_PTR_TO_PHYSADDR(new_table), - ACPI_TABLE_ORIGIN_INTERN_VIRTUAL, new_table); + + acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list. + tables[ACPI_TABLE_INDEX_DSDT], + ACPI_PTR_TO_PHYSADDR(new_table), + ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, + new_table); ACPI_INFO((AE_INFO, "Forced DSDT copy: length 0x%05X copied locally, original unmapped", @@ -470,11 +472,11 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) /* Get the table physical address (32-bit for RSDT, 64-bit for XSDT) */ status = - acpi_tb_install_non_fixed_table(acpi_tb_get_root_table_entry - (table_entry, - table_entry_size), - ACPI_TABLE_ORIGIN_INTERN_PHYSICAL, - FALSE, TRUE, &table_index); + acpi_tb_install_standard_table(acpi_tb_get_root_table_entry + (table_entry, + table_entry_size), + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL, + FALSE, TRUE, &table_index); if (ACPI_SUCCESS(status) && ACPI_COMPARE_NAME(&acpi_gbl_root_table_list. diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index 19c0b13ad4c2..6482b0ded652 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -233,7 +233,7 @@ acpi_get_table_header(char *signature, if (!acpi_gbl_root_table_list.tables[i].pointer) { if ((acpi_gbl_root_table_list.tables[i].flags & ACPI_TABLE_ORIGIN_MASK) == - ACPI_TABLE_ORIGIN_INTERN_PHYSICAL) { + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL) { header = acpi_os_map_memory(acpi_gbl_root_table_list. tables[i].address, diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 529f633efa55..ab5308b81aa8 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -219,13 +219,13 @@ acpi_install_table(acpi_physical_address address, u8 physical) ACPI_FUNCTION_TRACE(acpi_install_table); if (physical) { - flags = ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL; + flags = ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL; } else { - flags = ACPI_TABLE_ORIGIN_INTERN_PHYSICAL; + flags = ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL; } - status = acpi_tb_install_non_fixed_table(address, flags, - FALSE, FALSE, &table_index); + status = acpi_tb_install_standard_table(address, flags, + FALSE, FALSE, &table_index); return_ACPI_STATUS(status); } @@ -272,9 +272,11 @@ acpi_status acpi_load_table(struct acpi_table_header *table) ACPI_INFO((AE_INFO, "Host-directed Dynamic ACPI Table Load:")); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - status = acpi_tb_install_non_fixed_table(ACPI_PTR_TO_PHYSADDR(table), - ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL, - TRUE, FALSE, &table_index); + + status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table), + ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL, + TRUE, FALSE, &table_index); + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { goto unlock_and_exit; diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 0cdf4cc10f18..1cc7ef13c01a 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -367,9 +367,9 @@ struct acpi_table_desc { /* Masks for Flags field above */ -#define ACPI_TABLE_ORIGIN_EXTERN_VIRTUAL (0) /* Virtual address, external maintained */ -#define ACPI_TABLE_ORIGIN_INTERN_PHYSICAL (1) /* Physical address, internal mapped */ -#define ACPI_TABLE_ORIGIN_INTERN_VIRTUAL (2) /* Virtual address, internal allocated */ +#define ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL (0) /* Virtual address, external maintained */ +#define ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL (1) /* Physical address, internally mapped */ +#define ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL (2) /* Virtual address, internallly allocated */ #define ACPI_TABLE_ORIGIN_MASK (3) #define ACPI_TABLE_IS_LOADED (8) -- cgit v1.2.3 From 35476c75efa04a5fdb01074e54135dcc126c25f7 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 4 Apr 2014 12:40:16 +0800 Subject: ACPICA: Update version to 20140325. Version 20140325. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 8dc934073620..913d0765adbe 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20140214 +#define ACPI_CA_VERSION 0x20140325 #include #include -- cgit v1.2.3 From af6b6967d6e17fe070c0fd1be364c34cbd31a523 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 16 Apr 2014 17:19:12 +0200 Subject: net: phy: export genphy_config_init() This enables other drivers to call this generic implementation, and then only do specific details on top of it. Signed-off-by: Daniel Mack Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 3 ++- include/linux/phy.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0ce606624296..466ae3e06322 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1067,7 +1067,7 @@ int genphy_soft_reset(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_soft_reset); -static int genphy_config_init(struct phy_device *phydev) +int genphy_config_init(struct phy_device *phydev) { int val; u32 features; @@ -1118,6 +1118,7 @@ static int gen10g_soft_reset(struct phy_device *phydev) /* Do nothing for now */ return 0; } +EXPORT_SYMBOL(genphy_config_init); static int gen10g_config_init(struct phy_device *phydev) { diff --git a/include/linux/phy.h b/include/linux/phy.h index 4d0221fd0688..51d15f684e7e 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -666,6 +666,7 @@ static inline int phy_read_status(struct phy_device *phydev) return phydev->drv->read_status(phydev); } +int genphy_config_init(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev); int genphy_config_aneg(struct phy_device *phydev); -- cgit v1.2.3 From a0265d28b3a5877b5b8edd14eb12a2ccb60ab1f3 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 17 Apr 2014 13:45:03 +0800 Subject: net: Add __dev_forward_skb This patch adds the helper __dev_forward_skb which is identical to dev_forward_skb except that it doesn't actually inject the skb into the stack. This is useful where we wish to have finer control over how the packet is injected, e.g., via netif_rx_ni or netif_receive_skb. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7ed3a3aa6604..a803d792df1e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2633,6 +2633,7 @@ int dev_get_phys_port_id(struct net_device *dev, struct netdev_phys_port_id *ppid); int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq); +int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb); int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index d2c8a06b3a98..11d70e3afefa 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1661,6 +1661,29 @@ bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(is_skb_forwardable); +int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb) +{ + if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { + if (skb_copy_ubufs(skb, GFP_ATOMIC)) { + atomic_long_inc(&dev->rx_dropped); + kfree_skb(skb); + return NET_RX_DROP; + } + } + + if (unlikely(!is_skb_forwardable(dev, skb))) { + atomic_long_inc(&dev->rx_dropped); + kfree_skb(skb); + return NET_RX_DROP; + } + + skb_scrub_packet(skb, true); + skb->protocol = eth_type_trans(skb, dev); + + return 0; +} +EXPORT_SYMBOL_GPL(__dev_forward_skb); + /** * dev_forward_skb - loopback an skb to another netif * @@ -1681,24 +1704,7 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable); */ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) { - if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { - if (skb_copy_ubufs(skb, GFP_ATOMIC)) { - atomic_long_inc(&dev->rx_dropped); - kfree_skb(skb); - return NET_RX_DROP; - } - } - - if (unlikely(!is_skb_forwardable(dev, skb))) { - atomic_long_inc(&dev->rx_dropped); - kfree_skb(skb); - return NET_RX_DROP; - } - - skb_scrub_packet(skb, true); - skb->protocol = eth_type_trans(skb, dev); - - return netif_rx_internal(skb); + return __dev_forward_skb(dev, skb) ?: netif_rx_internal(skb); } EXPORT_SYMBOL_GPL(dev_forward_skb); -- cgit v1.2.3 From 599018a71013386119c057a64183e49240c8b4e6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 17 Apr 2014 18:22:54 -0700 Subject: 6lowpan: add helper to get 6lowpan namespace This will simplify the new reassembly backport with no code changes being required. CC: Alexander Smirnov Cc: Dmitry Eremin-Solenikov Cc: linux-zigbee-devel@lists.sourceforge.net Cc: David S. Miller" Cc: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: David S. Miller --- include/net/net_namespace.h | 15 +++++++++++++++ net/ieee802154/reassembly.c | 46 +++++++++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 5f9eb260990f..bc4118ede5b5 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -373,6 +373,21 @@ static inline void rt_genid_bump_ipv6(struct net *net) } #endif +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN) +static inline struct netns_ieee802154_lowpan * +net_ieee802154_lowpan(struct net *net) +{ + return &net->ieee802154_lowpan; +} +#else +static inline struct netns_ieee802154_lowpan * +net_ieee802154_lowpan(struct net *net) +{ + return NULL; +} +#endif + + /* For callers who don't really care about whether it's IPv4 or IPv6 */ static inline void rt_genid_bump_all(struct net *net) { diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index ef2d54372b13..132b65b6d957 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -120,6 +120,8 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info, struct inet_frag_queue *q; struct lowpan_create_arg arg; unsigned int hash; + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); arg.tag = frag_info->d_tag; arg.d_size = frag_info->d_size; @@ -129,7 +131,7 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info, read_lock(&lowpan_frags.lock); hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst); - q = inet_frag_find(&net->ieee802154_lowpan.frags, + q = inet_frag_find(&ieee802154_lowpan->frags, &lowpan_frags, &arg, hash); if (IS_ERR_OR_NULL(q)) { inet_frag_maybe_warn_overflow(q, pr_fmt()); @@ -357,6 +359,8 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) struct net *net = dev_net(skb->dev); struct lowpan_frag_info *frag_info = lowpan_cb(skb); struct ieee802154_addr source, dest; + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); int err; source = mac_cb(skb)->source; @@ -366,10 +370,10 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) if (err < 0) goto err; - if (frag_info->d_size > net->ieee802154_lowpan.max_dsize) + if (frag_info->d_size > ieee802154_lowpan->max_dsize) goto err; - inet_frag_evictor(&net->ieee802154_lowpan.frags, &lowpan_frags, false); + inet_frag_evictor(&ieee802154_lowpan->frags, &lowpan_frags, false); fq = fq_find(net, frag_info, &source, &dest); if (fq != NULL) { @@ -436,6 +440,8 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) { struct ctl_table *table; struct ctl_table_header *hdr; + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); table = lowpan_frags_ns_ctl_table; if (!net_eq(net, &init_net)) { @@ -444,10 +450,10 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) if (table == NULL) goto err_alloc; - table[0].data = &net->ieee802154_lowpan.frags.high_thresh; - table[1].data = &net->ieee802154_lowpan.frags.low_thresh; - table[2].data = &net->ieee802154_lowpan.frags.timeout; - table[3].data = &net->ieee802154_lowpan.max_dsize; + table[0].data = &ieee802154_lowpan->frags.high_thresh; + table[1].data = &ieee802154_lowpan->frags.low_thresh; + table[2].data = &ieee802154_lowpan->frags.timeout; + table[3].data = &ieee802154_lowpan->max_dsize; /* Don't export sysctls to unprivileged users */ if (net->user_ns != &init_user_ns) @@ -458,7 +464,7 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) if (hdr == NULL) goto err_reg; - net->ieee802154_lowpan.sysctl.frags_hdr = hdr; + ieee802154_lowpan->sysctl.frags_hdr = hdr; return 0; err_reg: @@ -471,9 +477,11 @@ err_alloc: static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net) { struct ctl_table *table; + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); - table = net->ieee802154_lowpan.sysctl.frags_hdr->ctl_table_arg; - unregister_net_sysctl_table(net->ieee802154_lowpan.sysctl.frags_hdr); + table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg; + unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr); if (!net_eq(net, &init_net)) kfree(table); } @@ -514,20 +522,26 @@ static inline void lowpan_frags_sysctl_unregister(void) static int __net_init lowpan_frags_init_net(struct net *net) { - net->ieee802154_lowpan.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; - net->ieee802154_lowpan.frags.low_thresh = IPV6_FRAG_LOW_THRESH; - net->ieee802154_lowpan.frags.timeout = IPV6_FRAG_TIMEOUT; - net->ieee802154_lowpan.max_dsize = 0xFFFF; + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); - inet_frags_init_net(&net->ieee802154_lowpan.frags); + ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH; + ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH; + ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT; + ieee802154_lowpan->max_dsize = 0xFFFF; + + inet_frags_init_net(&ieee802154_lowpan->frags); return lowpan_frags_ns_sysctl_register(net); } static void __net_exit lowpan_frags_exit_net(struct net *net) { + struct netns_ieee802154_lowpan *ieee802154_lowpan = + net_ieee802154_lowpan(net); + lowpan_frags_ns_sysctl_unregister(net); - inet_frags_exit_net(&net->ieee802154_lowpan.frags, &lowpan_frags); + inet_frags_exit_net(&ieee802154_lowpan->frags, &lowpan_frags); } static struct pernet_operations lowpan_frags_ops = { -- cgit v1.2.3 From 17d8ecb8ff791359c9d9a44bc766c3d4b87f37f7 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 17 Apr 2014 18:22:56 -0700 Subject: 6lowpan: include net/net_namespace.h on 6lowpan namepsace header Don't rely on driver files or other headers having this file included. CC: Alexander Smirnov Cc: Dmitry Eremin-Solenikov Cc: linux-zigbee-devel@lists.sourceforge.net Signed-off-by: Luis R. Rodriguez Signed-off-by: David S. Miller --- include/net/6lowpan.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index f7d372b7d4ff..79b530fb2c4d 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -54,6 +54,7 @@ #define __6LOWPAN_H__ #include +#include #define UIP_802154_SHORTADDR_LEN 2 /* compressed ipv6 address length */ #define UIP_IPH_LEN 40 /* ipv6 fixed header size */ -- cgit v1.2.3 From 86fd14ad1e8c4b8f5e9a7a27b26bdade91dd4bd0 Mon Sep 17 00:00:00 2001 From: Weiping Pan Date: Fri, 18 Apr 2014 12:27:46 +0800 Subject: tcp: make tcp_cwnd_application_limited() static Make tcp_cwnd_application_limited() static and move it from tcp_input.c to tcp_output.c Signed-off-by: Weiping Pan Signed-off-by: David S. Miller --- include/net/tcp.h | 1 - net/ipv4/tcp_input.c | 22 ---------------------- net/ipv4/tcp_output.c | 22 ++++++++++++++++++++++ 3 files changed, 22 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 87d877408188..163d2b467d78 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -558,7 +558,6 @@ void tcp_send_loss_probe(struct sock *sk); bool tcp_schedule_loss_probe(struct sock *sk); /* tcp_input.c */ -void tcp_cwnd_application_limited(struct sock *sk); void tcp_resume_early_retransmit(struct sock *sk); void tcp_rearm_rto(struct sock *sk); void tcp_reset(struct sock *sk); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d6b46eb2f94c..6efed134ab63 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4703,28 +4703,6 @@ static int tcp_prune_queue(struct sock *sk) return -1; } -/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto. - * As additional protections, we do not touch cwnd in retransmission phases, - * and if application hit its sndbuf limit recently. - */ -void tcp_cwnd_application_limited(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - - if (inet_csk(sk)->icsk_ca_state == TCP_CA_Open && - sk->sk_socket && !test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) { - /* Limited by application or receiver window. */ - u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk)); - u32 win_used = max(tp->snd_cwnd_used, init_win); - if (win_used < tp->snd_cwnd) { - tp->snd_ssthresh = tcp_current_ssthresh(sk); - tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1; - } - tp->snd_cwnd_used = 0; - } - tp->snd_cwnd_stamp = tcp_time_stamp; -} - static bool tcp_should_expand_sndbuf(const struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 025e25093984..29dde97c3c41 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1387,6 +1387,28 @@ unsigned int tcp_current_mss(struct sock *sk) return mss_now; } +/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto. + * As additional protections, we do not touch cwnd in retransmission phases, + * and if application hit its sndbuf limit recently. + */ +static void tcp_cwnd_application_limited(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (inet_csk(sk)->icsk_ca_state == TCP_CA_Open && + sk->sk_socket && !test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) { + /* Limited by application or receiver window. */ + u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk)); + u32 win_used = max(tp->snd_cwnd_used, init_win); + if (win_used < tp->snd_cwnd) { + tp->snd_ssthresh = tcp_current_ssthresh(sk); + tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1; + } + tp->snd_cwnd_used = 0; + } + tp->snd_cwnd_stamp = tcp_time_stamp; +} + /* Congestion window validation. (RFC2861) */ static void tcp_cwnd_validate(struct sock *sk) { -- cgit v1.2.3 From 090f32ee4ef0a59c738963c6b0a6948cc5dee84c Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Sun, 20 Apr 2014 23:44:47 -0400 Subject: ext4: get rid of EXT4_MAP_UNINIT flag Currently EXT4_MAP_UNINIT is used in dioread_nolock case to mark the cases where we're using dioread_nolock and we're writing into either unallocated, or unwritten extent, because we need to make sure that any DIO write into that inode will wait for the extent conversion. However EXT4_MAP_UNINIT is not only entirely misleading name but also unnecessary because we can check for EXT4_MAP_UNWRITTEN in the dioread_nolock case instead. This commit removes EXT4_MAP_UNINIT flag. Signed-off-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 8 +++----- fs/ext4/extents.c | 4 ---- fs/ext4/inode.c | 7 ++++--- include/trace/events/ext4.h | 1 - 4 files changed, 7 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 66946aa62127..b681d90b1e87 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -158,7 +158,6 @@ struct ext4_allocation_request { #define EXT4_MAP_MAPPED (1 << BH_Mapped) #define EXT4_MAP_UNWRITTEN (1 << BH_Unwritten) #define EXT4_MAP_BOUNDARY (1 << BH_Boundary) -#define EXT4_MAP_UNINIT (1 << BH_Uninit) /* Sometimes (in the bigalloc case, from ext4_da_get_block_prep) the caller of * ext4_map_blocks wants to know whether or not the underlying cluster has * already been accounted for. EXT4_MAP_FROM_CLUSTER conveys to the caller that @@ -169,7 +168,7 @@ struct ext4_allocation_request { #define EXT4_MAP_FROM_CLUSTER (1 << BH_AllocFromCluster) #define EXT4_MAP_FLAGS (EXT4_MAP_NEW | EXT4_MAP_MAPPED |\ EXT4_MAP_UNWRITTEN | EXT4_MAP_BOUNDARY |\ - EXT4_MAP_UNINIT | EXT4_MAP_FROM_CLUSTER) + EXT4_MAP_FROM_CLUSTER) struct ext4_map_blocks { ext4_fsblk_t m_pblk; @@ -2784,10 +2783,9 @@ extern int ext4_mmp_csum_verify(struct super_block *sb, * See EXT4_MAP_... to see where this is used. */ enum ext4_state_bits { - BH_Uninit /* blocks are allocated but uninitialized on disk */ - = BH_JBDPrivateStart, - BH_AllocFromCluster, /* allocated blocks were part of already + BH_AllocFromCluster /* allocated blocks were part of already * allocated cluster. */ + = BH_JBDPrivateStart }; /* diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 01b0c208f625..83ed52538ae4 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4033,8 +4033,6 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, else ext4_set_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN); map->m_flags |= EXT4_MAP_UNWRITTEN; - if (ext4_should_dioread_nolock(inode)) - map->m_flags |= EXT4_MAP_UNINIT; goto out; } /* IO end_io complete, convert the filled extent to written */ @@ -4479,8 +4477,6 @@ got_allocated_blocks: */ if ((flags & EXT4_GET_BLOCKS_PRE_IO)) set_unwritten = 1; - if (ext4_should_dioread_nolock(inode)) - map->m_flags |= EXT4_MAP_UNINIT; } err = 0; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d7b7462a0e13..297465e07d44 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2126,7 +2126,7 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd) struct inode *inode = mpd->inode; struct ext4_map_blocks *map = &mpd->map; int get_blocks_flags; - int err; + int err, dioread_nolock; trace_ext4_da_write_pages_extent(inode, map); /* @@ -2148,7 +2148,8 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd) */ get_blocks_flags = EXT4_GET_BLOCKS_CREATE | EXT4_GET_BLOCKS_METADATA_NOFAIL; - if (ext4_should_dioread_nolock(inode)) + dioread_nolock = ext4_should_dioread_nolock(inode); + if (dioread_nolock) get_blocks_flags |= EXT4_GET_BLOCKS_IO_CREATE_EXT; if (map->m_flags & (1 << BH_Delay)) get_blocks_flags |= EXT4_GET_BLOCKS_DELALLOC_RESERVE; @@ -2156,7 +2157,7 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd) err = ext4_map_blocks(handle, inode, map, get_blocks_flags); if (err < 0) return err; - if (map->m_flags & EXT4_MAP_UNINIT) { + if (dioread_nolock && (map->m_flags & EXT4_MAP_UNWRITTEN)) { if (!mpd->io_submit.io_end->handle && ext4_handle_valid(handle)) { mpd->io_submit.io_end->handle = handle->h_rsv_handle; diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 6a1a0245474f..9995d3b588a8 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -51,7 +51,6 @@ struct extent_status; { EXT4_MAP_MAPPED, "M" }, \ { EXT4_MAP_UNWRITTEN, "U" }, \ { EXT4_MAP_BOUNDARY, "B" }, \ - { EXT4_MAP_UNINIT, "u" }, \ { EXT4_MAP_FROM_CLUSTER, "C" }) #define show_free_flags(flags) __print_flags(flags, "|", \ -- cgit v1.2.3 From 556615dcbf38b0a92a9e659f52c06686270dfc16 Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Sun, 20 Apr 2014 23:45:47 -0400 Subject: ext4: rename uninitialized extents to unwritten Currently in ext4 there is quite a mess when it comes to naming unwritten extents. Sometimes we call it uninitialized and sometimes we refer to it as unwritten. The right name for the extent which has been allocated but does not contain any written data is _unwritten_. Other file systems are using this name consistently, even the buffer head state refers to it as unwritten. We need to fix this confusion in ext4. This commit changes every reference to an uninitialized extent (meaning allocated but unwritten) to unwritten extent. This includes comments, function names and variable names. It even covers abbreviation of the word uninitialized (such as uninit) and some misspellings. This commit does not change any of the code paths at all. This has been confirmed by comparing md5sums of the assembly code of each object file after all the function names were stripped from it. Signed-off-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 16 ++-- fs/ext4/ext4_extents.h | 22 ++--- fs/ext4/extents.c | 218 ++++++++++++++++++++++---------------------- fs/ext4/extents_status.c | 2 +- fs/ext4/file.c | 2 +- fs/ext4/inode.c | 18 ++-- fs/ext4/move_extent.c | 38 ++++---- fs/ext4/super.c | 2 +- include/trace/events/ext4.h | 8 +- 9 files changed, 163 insertions(+), 163 deletions(-) (limited to 'include') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b681d90b1e87..86c2cda208ea 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -183,7 +183,7 @@ struct ext4_map_blocks { #define EXT4_IO_END_UNWRITTEN 0x0001 /* - * For converting uninitialized extents on a work queue. 'handle' is used for + * For converting unwritten extents on a work queue. 'handle' is used for * buffered writeback. */ typedef struct ext4_io_end { @@ -536,26 +536,26 @@ enum { /* * Flags used by ext4_map_blocks() */ - /* Allocate any needed blocks and/or convert an unitialized + /* Allocate any needed blocks and/or convert an unwritten extent to be an initialized ext4 */ #define EXT4_GET_BLOCKS_CREATE 0x0001 - /* Request the creation of an unitialized extent */ -#define EXT4_GET_BLOCKS_UNINIT_EXT 0x0002 -#define EXT4_GET_BLOCKS_CREATE_UNINIT_EXT (EXT4_GET_BLOCKS_UNINIT_EXT|\ + /* Request the creation of an unwritten extent */ +#define EXT4_GET_BLOCKS_UNWRIT_EXT 0x0002 +#define EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT (EXT4_GET_BLOCKS_UNWRIT_EXT|\ EXT4_GET_BLOCKS_CREATE) /* Caller is from the delayed allocation writeout path * finally doing the actual allocation of delayed blocks */ #define EXT4_GET_BLOCKS_DELALLOC_RESERVE 0x0004 /* caller is from the direct IO path, request to creation of an - unitialized extents if not allocated, split the uninitialized + unwritten extents if not allocated, split the unwritten extent if blocks has been preallocated already*/ #define EXT4_GET_BLOCKS_PRE_IO 0x0008 #define EXT4_GET_BLOCKS_CONVERT 0x0010 #define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_PRE_IO|\ - EXT4_GET_BLOCKS_CREATE_UNINIT_EXT) + EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT) /* Convert extent to initialized after IO complete */ #define EXT4_GET_BLOCKS_IO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|\ - EXT4_GET_BLOCKS_CREATE_UNINIT_EXT) + EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT) /* Eventual metadata allocation (due to growing extent tree) * should not fail, so try to use reserved blocks for that.*/ #define EXT4_GET_BLOCKS_METADATA_NOFAIL 0x0020 diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h index 5074fe23f19e..a867f5ca9991 100644 --- a/fs/ext4/ext4_extents.h +++ b/fs/ext4/ext4_extents.h @@ -137,21 +137,21 @@ struct ext4_ext_path { * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an * initialized extent. This is 2^15 and not (2^16 - 1), since we use the * MSB of ee_len field in the extent datastructure to signify if this - * particular extent is an initialized extent or an uninitialized (i.e. + * particular extent is an initialized extent or an unwritten (i.e. * preallocated). - * EXT_UNINIT_MAX_LEN is the maximum number of blocks we can have in an - * uninitialized extent. + * EXT_UNWRITTEN_MAX_LEN is the maximum number of blocks we can have in an + * unwritten extent. * If ee_len is <= 0x8000, it is an initialized extent. Otherwise, it is an - * uninitialized one. In other words, if MSB of ee_len is set, it is an - * uninitialized extent with only one special scenario when ee_len = 0x8000. - * In this case we can not have an uninitialized extent of zero length and + * unwritten one. In other words, if MSB of ee_len is set, it is an + * unwritten extent with only one special scenario when ee_len = 0x8000. + * In this case we can not have an unwritten extent of zero length and * thus we make it as a special case of initialized extent with 0x8000 length. * This way we get better extent-to-group alignment for initialized extents. * Hence, the maximum number of blocks we can have in an *initialized* - * extent is 2^15 (32768) and in an *uninitialized* extent is 2^15-1 (32767). + * extent is 2^15 (32768) and in an *unwritten* extent is 2^15-1 (32767). */ #define EXT_INIT_MAX_LEN (1UL << 15) -#define EXT_UNINIT_MAX_LEN (EXT_INIT_MAX_LEN - 1) +#define EXT_UNWRITTEN_MAX_LEN (EXT_INIT_MAX_LEN - 1) #define EXT_FIRST_EXTENT(__hdr__) \ @@ -187,14 +187,14 @@ static inline unsigned short ext_depth(struct inode *inode) return le16_to_cpu(ext_inode_hdr(inode)->eh_depth); } -static inline void ext4_ext_mark_uninitialized(struct ext4_extent *ext) +static inline void ext4_ext_mark_unwritten(struct ext4_extent *ext) { - /* We can not have an uninitialized extent of zero length! */ + /* We can not have an unwritten extent of zero length! */ BUG_ON((le16_to_cpu(ext->ee_len) & ~EXT_INIT_MAX_LEN) == 0); ext->ee_len |= cpu_to_le16(EXT_INIT_MAX_LEN); } -static inline int ext4_ext_is_uninitialized(struct ext4_extent *ext) +static inline int ext4_ext_is_unwritten(struct ext4_extent *ext) { /* Extent with ee_len of 0x8000 is treated as an initialized extent */ return (le16_to_cpu(ext->ee_len) > EXT_INIT_MAX_LEN); diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 83ed52538ae4..e305a31641f2 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -50,8 +50,8 @@ */ #define EXT4_EXT_MAY_ZEROOUT 0x1 /* safe to zeroout if split fails \ due to ENOSPC */ -#define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */ -#define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */ +#define EXT4_EXT_MARK_UNWRIT1 0x2 /* mark first half unwritten */ +#define EXT4_EXT_MARK_UNWRIT2 0x4 /* mark second half unwritten */ #define EXT4_EXT_DATA_VALID1 0x8 /* first half contains valid data */ #define EXT4_EXT_DATA_VALID2 0x10 /* second half contains valid data */ @@ -524,7 +524,7 @@ __read_extent_tree_block(const char *function, unsigned int line, lblk - prev, ~0, EXTENT_STATUS_HOLE); - if (ext4_ext_is_uninitialized(ex)) + if (ext4_ext_is_unwritten(ex)) status = EXTENT_STATUS_UNWRITTEN; ext4_es_cache_extent(inode, lblk, len, ext4_ext_pblock(ex), status); @@ -620,7 +620,7 @@ static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) } else if (path->p_ext) { ext_debug(" %d:[%d]%d:%llu ", le32_to_cpu(path->p_ext->ee_block), - ext4_ext_is_uninitialized(path->p_ext), + ext4_ext_is_unwritten(path->p_ext), ext4_ext_get_actual_len(path->p_ext), ext4_ext_pblock(path->p_ext)); } else @@ -646,7 +646,7 @@ static void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path) for (i = 0; i < le16_to_cpu(eh->eh_entries); i++, ex++) { ext_debug("%d:[%d]%d:%llu ", le32_to_cpu(ex->ee_block), - ext4_ext_is_uninitialized(ex), + ext4_ext_is_unwritten(ex), ext4_ext_get_actual_len(ex), ext4_ext_pblock(ex)); } ext_debug("\n"); @@ -677,7 +677,7 @@ static void ext4_ext_show_move(struct inode *inode, struct ext4_ext_path *path, ext_debug("move %d:%llu:[%d]%d in new leaf %llu\n", le32_to_cpu(ex->ee_block), ext4_ext_pblock(ex), - ext4_ext_is_uninitialized(ex), + ext4_ext_is_unwritten(ex), ext4_ext_get_actual_len(ex), newblock); ex++; @@ -802,7 +802,7 @@ ext4_ext_binsearch(struct inode *inode, ext_debug(" -> %d:%llu:[%d]%d ", le32_to_cpu(path->p_ext->ee_block), ext4_ext_pblock(path->p_ext), - ext4_ext_is_uninitialized(path->p_ext), + ext4_ext_is_unwritten(path->p_ext), ext4_ext_get_actual_len(path->p_ext)); #ifdef CHECK_BINSEARCH @@ -1686,11 +1686,11 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1, /* * Make sure that both extents are initialized. We don't merge - * uninitialized extents so that we can be sure that end_io code has + * unwritten extents so that we can be sure that end_io code has * the extent that was written properly split out and conversion to * initialized is trivial. */ - if (ext4_ext_is_uninitialized(ex1) != ext4_ext_is_uninitialized(ex2)) + if (ext4_ext_is_unwritten(ex1) != ext4_ext_is_unwritten(ex2)) return 0; ext1_ee_len = ext4_ext_get_actual_len(ex1); @@ -1707,10 +1707,10 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1, */ if (ext1_ee_len + ext2_ee_len > EXT_INIT_MAX_LEN) return 0; - if (ext4_ext_is_uninitialized(ex1) && + if (ext4_ext_is_unwritten(ex1) && (ext4_test_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN) || atomic_read(&EXT4_I(inode)->i_unwritten) || - (ext1_ee_len + ext2_ee_len > EXT_UNINIT_MAX_LEN))) + (ext1_ee_len + ext2_ee_len > EXT_UNWRITTEN_MAX_LEN))) return 0; #ifdef AGGRESSIVE_TEST if (ext1_ee_len >= 4) @@ -1735,7 +1735,7 @@ static int ext4_ext_try_to_merge_right(struct inode *inode, { struct ext4_extent_header *eh; unsigned int depth, len; - int merge_done = 0, uninit; + int merge_done = 0, unwritten; depth = ext_depth(inode); BUG_ON(path[depth].p_hdr == NULL); @@ -1745,11 +1745,11 @@ static int ext4_ext_try_to_merge_right(struct inode *inode, if (!ext4_can_extents_be_merged(inode, ex, ex + 1)) break; /* merge with next extent! */ - uninit = ext4_ext_is_uninitialized(ex); + unwritten = ext4_ext_is_unwritten(ex); ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex) + ext4_ext_get_actual_len(ex + 1)); - if (uninit) - ext4_ext_mark_uninitialized(ex); + if (unwritten) + ext4_ext_mark_unwritten(ex); if (ex + 1 < EXT_LAST_EXTENT(eh)) { len = (EXT_LAST_EXTENT(eh) - ex - 1) @@ -1903,7 +1903,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, struct ext4_ext_path *npath = NULL; int depth, len, err; ext4_lblk_t next; - int mb_flags = 0, uninit; + int mb_flags = 0, unwritten; if (unlikely(ext4_ext_get_actual_len(newext) == 0)) { EXT4_ERROR_INODE(inode, "ext4_ext_get_actual_len(newext) == 0"); @@ -1943,21 +1943,21 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, if (ext4_can_extents_be_merged(inode, ex, newext)) { ext_debug("append [%d]%d block to %u:[%d]%d" "(from %llu)\n", - ext4_ext_is_uninitialized(newext), + ext4_ext_is_unwritten(newext), ext4_ext_get_actual_len(newext), le32_to_cpu(ex->ee_block), - ext4_ext_is_uninitialized(ex), + ext4_ext_is_unwritten(ex), ext4_ext_get_actual_len(ex), ext4_ext_pblock(ex)); err = ext4_ext_get_access(handle, inode, path + depth); if (err) return err; - uninit = ext4_ext_is_uninitialized(ex); + unwritten = ext4_ext_is_unwritten(ex); ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex) + ext4_ext_get_actual_len(newext)); - if (uninit) - ext4_ext_mark_uninitialized(ex); + if (unwritten) + ext4_ext_mark_unwritten(ex); eh = path[depth].p_hdr; nearex = ex; goto merge; @@ -1969,10 +1969,10 @@ prepend: ext_debug("prepend %u[%d]%d block to %u:[%d]%d" "(from %llu)\n", le32_to_cpu(newext->ee_block), - ext4_ext_is_uninitialized(newext), + ext4_ext_is_unwritten(newext), ext4_ext_get_actual_len(newext), le32_to_cpu(ex->ee_block), - ext4_ext_is_uninitialized(ex), + ext4_ext_is_unwritten(ex), ext4_ext_get_actual_len(ex), ext4_ext_pblock(ex)); err = ext4_ext_get_access(handle, inode, @@ -1980,13 +1980,13 @@ prepend: if (err) return err; - uninit = ext4_ext_is_uninitialized(ex); + unwritten = ext4_ext_is_unwritten(ex); ex->ee_block = newext->ee_block; ext4_ext_store_pblock(ex, ext4_ext_pblock(newext)); ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex) + ext4_ext_get_actual_len(newext)); - if (uninit) - ext4_ext_mark_uninitialized(ex); + if (unwritten) + ext4_ext_mark_unwritten(ex); eh = path[depth].p_hdr; nearex = ex; goto merge; @@ -2046,7 +2046,7 @@ has_space: ext_debug("first extent in the leaf: %u:%llu:[%d]%d\n", le32_to_cpu(newext->ee_block), ext4_ext_pblock(newext), - ext4_ext_is_uninitialized(newext), + ext4_ext_is_unwritten(newext), ext4_ext_get_actual_len(newext)); nearex = EXT_FIRST_EXTENT(eh); } else { @@ -2057,7 +2057,7 @@ has_space: "nearest %p\n", le32_to_cpu(newext->ee_block), ext4_ext_pblock(newext), - ext4_ext_is_uninitialized(newext), + ext4_ext_is_unwritten(newext), ext4_ext_get_actual_len(newext), nearex); nearex++; @@ -2068,7 +2068,7 @@ has_space: "nearest %p\n", le32_to_cpu(newext->ee_block), ext4_ext_pblock(newext), - ext4_ext_is_uninitialized(newext), + ext4_ext_is_unwritten(newext), ext4_ext_get_actual_len(newext), nearex); } @@ -2078,7 +2078,7 @@ has_space: "move %d extents from 0x%p to 0x%p\n", le32_to_cpu(newext->ee_block), ext4_ext_pblock(newext), - ext4_ext_is_uninitialized(newext), + ext4_ext_is_unwritten(newext), ext4_ext_get_actual_len(newext), len, nearex, nearex + 1); memmove(nearex + 1, nearex, @@ -2200,7 +2200,7 @@ static int ext4_fill_fiemap_extents(struct inode *inode, es.es_lblk = le32_to_cpu(ex->ee_block); es.es_len = ext4_ext_get_actual_len(ex); es.es_pblk = ext4_ext_pblock(ex); - if (ext4_ext_is_uninitialized(ex)) + if (ext4_ext_is_unwritten(ex)) flags |= FIEMAP_EXTENT_UNWRITTEN; } @@ -2576,7 +2576,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, unsigned num; ext4_lblk_t ex_ee_block; unsigned short ex_ee_len; - unsigned uninitialized = 0; + unsigned unwritten = 0; struct ext4_extent *ex; ext4_fsblk_t pblk; @@ -2623,13 +2623,13 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, while (ex >= EXT_FIRST_EXTENT(eh) && ex_ee_block + ex_ee_len > start) { - if (ext4_ext_is_uninitialized(ex)) - uninitialized = 1; + if (ext4_ext_is_unwritten(ex)) + unwritten = 1; else - uninitialized = 0; + unwritten = 0; ext_debug("remove ext %u:[%d]%d\n", ex_ee_block, - uninitialized, ex_ee_len); + unwritten, ex_ee_len); path[depth].p_ext = ex; a = ex_ee_block > start ? ex_ee_block : start; @@ -2701,11 +2701,11 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ex->ee_len = cpu_to_le16(num); /* - * Do not mark uninitialized if all the blocks in the + * Do not mark unwritten if all the blocks in the * extent have been removed. */ - if (uninitialized && num) - ext4_ext_mark_uninitialized(ex); + if (unwritten && num) + ext4_ext_mark_unwritten(ex); /* * If the extent was completely released, * we need to remove it from the leaf @@ -2854,9 +2854,9 @@ again: end < ee_block + ext4_ext_get_actual_len(ex) - 1) { int split_flag = 0; - if (ext4_ext_is_uninitialized(ex)) - split_flag = EXT4_EXT_MARK_UNINIT1 | - EXT4_EXT_MARK_UNINIT2; + if (ext4_ext_is_unwritten(ex)) + split_flag = EXT4_EXT_MARK_UNWRIT1 | + EXT4_EXT_MARK_UNWRIT2; /* * Split the extent in two so that 'end' is the last @@ -3113,7 +3113,7 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) * @path: the path to the extent * @split: the logical block where the extent is splitted. * @split_flags: indicates if the extent could be zeroout if split fails, and - * the states(init or uninit) of new extents. + * the states(init or unwritten) of new extents. * @flags: flags used to insert new extent to extent tree. * * @@ -3155,10 +3155,10 @@ static int ext4_split_extent_at(handle_t *handle, newblock = split - ee_block + ext4_ext_pblock(ex); BUG_ON(split < ee_block || split >= (ee_block + ee_len)); - BUG_ON(!ext4_ext_is_uninitialized(ex) && + BUG_ON(!ext4_ext_is_unwritten(ex) && split_flag & (EXT4_EXT_MAY_ZEROOUT | - EXT4_EXT_MARK_UNINIT1 | - EXT4_EXT_MARK_UNINIT2)); + EXT4_EXT_MARK_UNWRIT1 | + EXT4_EXT_MARK_UNWRIT2)); err = ext4_ext_get_access(handle, inode, path + depth); if (err) @@ -3170,8 +3170,8 @@ static int ext4_split_extent_at(handle_t *handle, * then we just change the state of the extent, and splitting * is not needed. */ - if (split_flag & EXT4_EXT_MARK_UNINIT2) - ext4_ext_mark_uninitialized(ex); + if (split_flag & EXT4_EXT_MARK_UNWRIT2) + ext4_ext_mark_unwritten(ex); else ext4_ext_mark_initialized(ex); @@ -3185,8 +3185,8 @@ static int ext4_split_extent_at(handle_t *handle, /* case a */ memcpy(&orig_ex, ex, sizeof(orig_ex)); ex->ee_len = cpu_to_le16(split - ee_block); - if (split_flag & EXT4_EXT_MARK_UNINIT1) - ext4_ext_mark_uninitialized(ex); + if (split_flag & EXT4_EXT_MARK_UNWRIT1) + ext4_ext_mark_unwritten(ex); /* * path may lead to new leaf, not to original leaf any more @@ -3200,8 +3200,8 @@ static int ext4_split_extent_at(handle_t *handle, ex2->ee_block = cpu_to_le32(split); ex2->ee_len = cpu_to_le16(ee_len - (split - ee_block)); ext4_ext_store_pblock(ex2, newblock); - if (split_flag & EXT4_EXT_MARK_UNINIT2) - ext4_ext_mark_uninitialized(ex2); + if (split_flag & EXT4_EXT_MARK_UNWRIT2) + ext4_ext_mark_unwritten(ex2); err = ext4_ext_insert_extent(handle, inode, path, &newex, flags); if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) { @@ -3278,7 +3278,7 @@ static int ext4_split_extent(handle_t *handle, struct ext4_extent *ex; unsigned int ee_len, depth; int err = 0; - int uninitialized; + int unwritten; int split_flag1, flags1; int allocated = map->m_len; @@ -3286,14 +3286,14 @@ static int ext4_split_extent(handle_t *handle, ex = path[depth].p_ext; ee_block = le32_to_cpu(ex->ee_block); ee_len = ext4_ext_get_actual_len(ex); - uninitialized = ext4_ext_is_uninitialized(ex); + unwritten = ext4_ext_is_unwritten(ex); if (map->m_lblk + map->m_len < ee_block + ee_len) { split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT; flags1 = flags | EXT4_GET_BLOCKS_PRE_IO; - if (uninitialized) - split_flag1 |= EXT4_EXT_MARK_UNINIT1 | - EXT4_EXT_MARK_UNINIT2; + if (unwritten) + split_flag1 |= EXT4_EXT_MARK_UNWRIT1 | + EXT4_EXT_MARK_UNWRIT2; if (split_flag & EXT4_EXT_DATA_VALID2) split_flag1 |= EXT4_EXT_DATA_VALID1; err = ext4_split_extent_at(handle, inode, path, @@ -3318,15 +3318,15 @@ static int ext4_split_extent(handle_t *handle, (unsigned long) map->m_lblk); return -EIO; } - uninitialized = ext4_ext_is_uninitialized(ex); + unwritten = ext4_ext_is_unwritten(ex); split_flag1 = 0; if (map->m_lblk >= ee_block) { split_flag1 = split_flag & EXT4_EXT_DATA_VALID2; - if (uninitialized) { - split_flag1 |= EXT4_EXT_MARK_UNINIT1; + if (unwritten) { + split_flag1 |= EXT4_EXT_MARK_UNWRIT1; split_flag1 |= split_flag & (EXT4_EXT_MAY_ZEROOUT | - EXT4_EXT_MARK_UNINIT2); + EXT4_EXT_MARK_UNWRIT2); } err = ext4_split_extent_at(handle, inode, path, map->m_lblk, split_flag1, flags); @@ -3341,16 +3341,16 @@ out: /* * This function is called by ext4_ext_map_blocks() if someone tries to write - * to an uninitialized extent. It may result in splitting the uninitialized + * to an unwritten extent. It may result in splitting the unwritten * extent into multiple extents (up to three - one initialized and two - * uninitialized). + * unwritten). * There are three possibilities: * a> There is no split required: Entire extent should be initialized * b> Splits in two extents: Write is happening at either end of the extent * c> Splits in three extents: Somone is writing in middle of the extent * * Pre-conditions: - * - The extent pointed to by 'path' is uninitialized. + * - The extent pointed to by 'path' is unwritten. * - The extent pointed to by 'path' contains a superset * of the logical span [map->m_lblk, map->m_lblk + map->m_len). * @@ -3396,12 +3396,12 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, trace_ext4_ext_convert_to_initialized_enter(inode, map, ex); /* Pre-conditions */ - BUG_ON(!ext4_ext_is_uninitialized(ex)); + BUG_ON(!ext4_ext_is_unwritten(ex)); BUG_ON(!in_range(map->m_lblk, ee_block, ee_len)); /* * Attempt to transfer newly initialized blocks from the currently - * uninitialized extent to its neighbor. This is much cheaper + * unwritten extent to its neighbor. This is much cheaper * than an insertion followed by a merge as those involve costly * memmove() calls. Transferring to the left is the common case in * steady state for workloads doing fallocate(FALLOC_FL_KEEP_SIZE) @@ -3437,7 +3437,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, * - C4: abut_ex can receive the additional blocks without * overflowing the (initialized) length limit. */ - if ((!ext4_ext_is_uninitialized(abut_ex)) && /*C1*/ + if ((!ext4_ext_is_unwritten(abut_ex)) && /*C1*/ ((prev_lblk + prev_len) == ee_block) && /*C2*/ ((prev_pblk + prev_len) == ee_pblk) && /*C3*/ (prev_len < (EXT_INIT_MAX_LEN - map_len))) { /*C4*/ @@ -3452,7 +3452,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, ex->ee_block = cpu_to_le32(ee_block + map_len); ext4_ext_store_pblock(ex, ee_pblk + map_len); ex->ee_len = cpu_to_le16(ee_len - map_len); - ext4_ext_mark_uninitialized(ex); /* Restore the flag */ + ext4_ext_mark_unwritten(ex); /* Restore the flag */ /* Extend abut_ex by 'map_len' blocks */ abut_ex->ee_len = cpu_to_le16(prev_len + map_len); @@ -3483,7 +3483,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, * - C4: abut_ex can receive the additional blocks without * overflowing the (initialized) length limit. */ - if ((!ext4_ext_is_uninitialized(abut_ex)) && /*C1*/ + if ((!ext4_ext_is_unwritten(abut_ex)) && /*C1*/ ((map->m_lblk + map_len) == next_lblk) && /*C2*/ ((ee_pblk + ee_len) == next_pblk) && /*C3*/ (next_len < (EXT_INIT_MAX_LEN - map_len))) { /*C4*/ @@ -3498,7 +3498,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, abut_ex->ee_block = cpu_to_le32(next_lblk - map_len); ext4_ext_store_pblock(abut_ex, next_pblk - map_len); ex->ee_len = cpu_to_le16(ee_len - map_len); - ext4_ext_mark_uninitialized(ex); /* Restore the flag */ + ext4_ext_mark_unwritten(ex); /* Restore the flag */ /* Extend abut_ex by 'map_len' blocks */ abut_ex->ee_len = cpu_to_le16(next_len + map_len); @@ -3603,26 +3603,26 @@ out: /* * This function is called by ext4_ext_map_blocks() from * ext4_get_blocks_dio_write() when DIO to write - * to an uninitialized extent. + * to an unwritten extent. * - * Writing to an uninitialized extent may result in splitting the uninitialized - * extent into multiple initialized/uninitialized extents (up to three) + * Writing to an unwritten extent may result in splitting the unwritten + * extent into multiple initialized/unwritten extents (up to three) * There are three possibilities: - * a> There is no split required: Entire extent should be uninitialized + * a> There is no split required: Entire extent should be unwritten * b> Splits in two extents: Write is happening at either end of the extent * c> Splits in three extents: Somone is writing in middle of the extent * * This works the same way in the case of initialized -> unwritten conversion. * * One of more index blocks maybe needed if the extent tree grow after - * the uninitialized extent split. To prevent ENOSPC occur at the IO - * complete, we need to split the uninitialized extent before DIO submit - * the IO. The uninitialized extent called at this time will be split - * into three uninitialized extent(at most). After IO complete, the part + * the unwritten extent split. To prevent ENOSPC occur at the IO + * complete, we need to split the unwritten extent before DIO submit + * the IO. The unwritten extent called at this time will be split + * into three unwritten extent(at most). After IO complete, the part * being filled will be convert to initialized by the end_io callback function * via ext4_convert_unwritten_extents(). * - * Returns the size of uninitialized extent to be written on success. + * Returns the size of unwritten extent to be written on success. */ static int ext4_split_convert_extents(handle_t *handle, struct inode *inode, @@ -3660,7 +3660,7 @@ static int ext4_split_convert_extents(handle_t *handle, } else if (flags & EXT4_GET_BLOCKS_CONVERT) { split_flag |= ee_block + ee_len <= eof_block ? EXT4_EXT_MAY_ZEROOUT : 0; - split_flag |= (EXT4_EXT_MARK_UNINIT2 | EXT4_EXT_DATA_VALID2); + split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2); } flags |= EXT4_GET_BLOCKS_PRE_IO; return ext4_split_extent(handle, inode, path, map, split_flag, flags); @@ -3710,8 +3710,8 @@ static int ext4_convert_initialized_extents(handle_t *handle, err = ext4_ext_get_access(handle, inode, path + depth); if (err) goto out; - /* first mark the extent as uninitialized */ - ext4_ext_mark_uninitialized(ex); + /* first mark the extent as unwritten */ + ext4_ext_mark_unwritten(ex); /* note: ext4_ext_correct_indexes() isn't needed here because * borders are not changed @@ -3971,10 +3971,10 @@ ext4_ext_convert_initialized_extent(handle_t *handle, struct inode *inode, /* * Make sure that the extent is no bigger than we support with - * uninitialized extent + * unwritten extent */ - if (map->m_len > EXT_UNINIT_MAX_LEN) - map->m_len = EXT_UNINIT_MAX_LEN / 2; + if (map->m_len > EXT_UNWRITTEN_MAX_LEN) + map->m_len = EXT_UNWRITTEN_MAX_LEN / 2; ret = ext4_convert_initialized_extents(handle, inode, map, path); @@ -3993,7 +3993,7 @@ ext4_ext_convert_initialized_extent(handle_t *handle, struct inode *inode, } static int -ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, +ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, struct ext4_ext_path *path, int flags, unsigned int allocated, ext4_fsblk_t newblock) @@ -4002,19 +4002,19 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, int err = 0; ext4_io_end_t *io = ext4_inode_aio(inode); - ext_debug("ext4_ext_handle_uninitialized_extents: inode %lu, logical " + ext_debug("ext4_ext_handle_unwritten_extents: inode %lu, logical " "block %llu, max_blocks %u, flags %x, allocated %u\n", inode->i_ino, (unsigned long long)map->m_lblk, map->m_len, flags, allocated); ext4_ext_show_leaf(inode, path); /* - * When writing into uninitialized space, we should not fail to + * When writing into unwritten space, we should not fail to * allocate metadata blocks for the new extent block if needed. */ flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL; - trace_ext4_ext_handle_uninitialized_extents(inode, map, flags, + trace_ext4_ext_handle_unwritten_extents(inode, map, flags, allocated, newblock); /* get_block() before submit the IO, split the extent */ @@ -4057,7 +4057,7 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, * repeat fallocate creation request * we already have an unwritten extent */ - if (flags & EXT4_GET_BLOCKS_UNINIT_EXT) { + if (flags & EXT4_GET_BLOCKS_UNWRIT_EXT) { map->m_flags |= EXT4_MAP_UNWRITTEN; goto map_out; } @@ -4308,7 +4308,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, /* - * Uninitialized extents are treated as holes, except that + * unwritten extents are treated as holes, except that * we split out initialized portions during a write. */ ee_len = ext4_ext_get_actual_len(ex); @@ -4327,16 +4327,16 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, * If the extent is initialized check whether the * caller wants to convert it to unwritten. */ - if ((!ext4_ext_is_uninitialized(ex)) && + if ((!ext4_ext_is_unwritten(ex)) && (flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN)) { allocated = ext4_ext_convert_initialized_extent( handle, inode, map, path, flags, allocated, newblock); goto out2; - } else if (!ext4_ext_is_uninitialized(ex)) + } else if (!ext4_ext_is_unwritten(ex)) goto out; - ret = ext4_ext_handle_uninitialized_extents( + ret = ext4_ext_handle_unwritten_extents( handle, inode, map, path, flags, allocated, newblock); if (ret < 0) @@ -4408,15 +4408,15 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, /* * See if request is beyond maximum number of blocks we can have in * a single extent. For an initialized extent this limit is - * EXT_INIT_MAX_LEN and for an uninitialized extent this limit is - * EXT_UNINIT_MAX_LEN. + * EXT_INIT_MAX_LEN and for an unwritten extent this limit is + * EXT_UNWRITTEN_MAX_LEN. */ if (map->m_len > EXT_INIT_MAX_LEN && - !(flags & EXT4_GET_BLOCKS_UNINIT_EXT)) + !(flags & EXT4_GET_BLOCKS_UNWRIT_EXT)) map->m_len = EXT_INIT_MAX_LEN; - else if (map->m_len > EXT_UNINIT_MAX_LEN && - (flags & EXT4_GET_BLOCKS_UNINIT_EXT)) - map->m_len = EXT_UNINIT_MAX_LEN; + else if (map->m_len > EXT_UNWRITTEN_MAX_LEN && + (flags & EXT4_GET_BLOCKS_UNWRIT_EXT)) + map->m_len = EXT_UNWRITTEN_MAX_LEN; /* Check if we can really insert (m_lblk)::(m_lblk + m_len) extent */ newex.ee_len = cpu_to_le16(map->m_len); @@ -4464,13 +4464,13 @@ got_allocated_blocks: /* try to insert new extent into found leaf and return */ ext4_ext_store_pblock(&newex, newblock + offset); newex.ee_len = cpu_to_le16(ar.len); - /* Mark uninitialized */ - if (flags & EXT4_GET_BLOCKS_UNINIT_EXT){ - ext4_ext_mark_uninitialized(&newex); + /* Mark unwritten */ + if (flags & EXT4_GET_BLOCKS_UNWRIT_EXT){ + ext4_ext_mark_unwritten(&newex); map->m_flags |= EXT4_MAP_UNWRITTEN; /* * io_end structure was created for every IO write to an - * uninitialized extent. To avoid unnecessary conversion, + * unwritten extent. To avoid unnecessary conversion, * here we flag the IO that really needs the conversion. * For non asycn direct IO case, flag the inode state * that we need to perform conversion when IO is done. @@ -4603,9 +4603,9 @@ got_allocated_blocks: /* * Cache the extent and update transaction to commit on fdatasync only - * when it is _not_ an uninitialized extent. + * when it is _not_ an unwritten extent. */ - if ((flags & EXT4_GET_BLOCKS_UNINIT_EXT) == 0) + if ((flags & EXT4_GET_BLOCKS_UNWRIT_EXT) == 0) ext4_update_inode_fsync_trans(handle, inode, 1); else ext4_update_inode_fsync_trans(handle, inode, 0); @@ -4679,7 +4679,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, * that it doesn't get unnecessarily split into multiple * extents. */ - if (len <= EXT_UNINIT_MAX_LEN) + if (len <= EXT_UNWRITTEN_MAX_LEN) flags |= EXT4_GET_BLOCKS_NO_NORMALIZE; /* @@ -4771,7 +4771,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, else max_blocks -= lblk; - flags = EXT4_GET_BLOCKS_CREATE_UNINIT_EXT | + flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT | EXT4_GET_BLOCKS_CONVERT_UNWRITTEN; if (mode & FALLOC_FL_KEEP_SIZE) flags |= EXT4_GET_BLOCKS_KEEP_SIZE; @@ -4914,7 +4914,7 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) max_blocks = (EXT4_BLOCK_ALIGN(len + offset, blkbits) >> blkbits) - lblk; - flags = EXT4_GET_BLOCKS_CREATE_UNINIT_EXT; + flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT; if (mode & FALLOC_FL_KEEP_SIZE) flags |= EXT4_GET_BLOCKS_KEEP_SIZE; diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 0ebc21204b51..98c90f5834ee 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -433,7 +433,7 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode, ee_start = ext4_ext_pblock(ex); ee_len = ext4_ext_get_actual_len(ex); - ee_status = ext4_ext_is_uninitialized(ex) ? 1 : 0; + ee_status = ext4_ext_is_unwritten(ex) ? 1 : 0; es_status = ext4_es_is_unwritten(es) ? 1 : 0; /* diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 063fc1538355..bf0e772b6a03 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -136,7 +136,7 @@ ext4_file_dio_write(struct kiocb *iocb, const struct iovec *iov, /* * 'err==len' means that all of blocks has been preallocated no * matter they are initialized or not. For excluding - * uninitialized extents, we need to check m_flags. There are + * unwritten extents, we need to check m_flags. There are * two conditions that indicate for initialized extents. * 1) If we hit extent cache, EXT4_MAP_MAPPED flag is returned; * 2) If we do a real lookup, non-flags are returned. diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 297465e07d44..7dfbcbba67d5 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -489,8 +489,8 @@ static void ext4_map_blocks_es_recheck(handle_t *handle, * Otherwise, call with ext4_ind_map_blocks() to handle indirect mapping * based files * - * On success, it returns the number of blocks being mapped or allocate. - * if create==0 and the blocks are pre-allocated and uninitialized block, + * On success, it returns the number of blocks being mapped or allocated. + * if create==0 and the blocks are pre-allocated and unwritten block, * the result buffer head is unmapped. If the create ==1, it will make sure * the buffer head is mapped. * @@ -622,7 +622,7 @@ found: map->m_flags &= ~EXT4_MAP_FLAGS; /* - * New blocks allocate and/or writing to uninitialized extent + * New blocks allocate and/or writing to unwritten extent * will possibly result in updating i_data, so we take * the write lock of i_data_sem, and call get_blocks() * with create == 1 flag. @@ -2032,7 +2032,7 @@ static int mpage_process_page_bufs(struct mpage_da_data *mpd, * Scan buffers corresponding to changed extent (we expect corresponding pages * to be already locked) and update buffer state according to new extent state. * We map delalloc buffers to their physical location, clear unwritten bits, - * and mark buffers as uninit when we perform writes to uninitialized extents + * and mark buffers as uninit when we perform writes to unwritten extents * and do extent conversion after IO is finished. If the last page is not fully * mapped, we update @map to the next extent in the last page that needs * mapping. Otherwise we submit the page for IO. @@ -2131,7 +2131,7 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd) trace_ext4_da_write_pages_extent(inode, map); /* * Call ext4_map_blocks() to allocate any delayed allocation blocks, or - * to convert an uninitialized extent to be initialized (in the case + * to convert an unwritten extent to be initialized (in the case * where we have written into one or more preallocated blocks). It is * possible that we're going to need more metadata blocks than * previously reserved. However we must not fail because we're in @@ -3071,9 +3071,9 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, * preallocated extents, and those write extend the file, no need to * fall back to buffered IO. * - * For holes, we fallocate those blocks, mark them as uninitialized + * For holes, we fallocate those blocks, mark them as unwritten * If those blocks were preallocated, we mark sure they are split, but - * still keep the range to write as uninitialized. + * still keep the range to write as unwritten. * * The unwritten extents will be converted to written when DIO is completed. * For async direct IO, since the IO may still pending when return, we @@ -3125,12 +3125,12 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, * We could direct write to holes and fallocate. * * Allocated blocks to fill the hole are marked as - * uninitialized to prevent parallel buffered read to expose + * unwritten to prevent parallel buffered read to expose * the stale data before DIO complete the data IO. * * As to previously fallocated extents, ext4 get_block will * just simply mark the buffer mapped but still keep the - * extents uninitialized. + * extents unwritten. * * For non AIO case, we will convert those unwritten extents * to written after return back from blockdev_direct_IO. diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 58ee7dc87669..1b809fe51da1 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -57,8 +57,8 @@ get_ext_path(struct inode *inode, ext4_lblk_t lblock, static void copy_extent_status(struct ext4_extent *src, struct ext4_extent *dest) { - if (ext4_ext_is_uninitialized(src)) - ext4_ext_mark_uninitialized(dest); + if (ext4_ext_is_unwritten(src)) + ext4_ext_mark_unwritten(dest); else dest->ee_len = cpu_to_le16(ext4_ext_get_actual_len(dest)); } @@ -593,14 +593,14 @@ mext_calc_swap_extents(struct ext4_extent *tmp_dext, * @inode: inode in question * @from: block offset of inode * @count: block count to be checked - * @uninit: extents expected to be uninitialized + * @unwritten: extents expected to be unwritten * @err: pointer to save error value * * Return 1 if all extents in range has expected type, and zero otherwise. */ static int mext_check_coverage(struct inode *inode, ext4_lblk_t from, ext4_lblk_t count, - int uninit, int *err) + int unwritten, int *err) { struct ext4_ext_path *path = NULL; struct ext4_extent *ext; @@ -611,7 +611,7 @@ mext_check_coverage(struct inode *inode, ext4_lblk_t from, ext4_lblk_t count, if (*err) goto out; ext = path[ext_depth(inode)].p_ext; - if (uninit != ext4_ext_is_uninitialized(ext)) + if (unwritten != ext4_ext_is_unwritten(ext)) goto out; from += ext4_ext_get_actual_len(ext); ext4_ext_drop_refs(path); @@ -894,7 +894,7 @@ out: * @orig_page_offset: page index on original file * @data_offset_in_page: block index where data swapping starts * @block_len_in_page: the number of blocks to be swapped - * @uninit: orig extent is uninitialized or not + * @unwritten: orig extent is unwritten or not * @err: pointer to save return value * * Save the data in original inode blocks and replace original inode extents @@ -905,7 +905,7 @@ out: static int move_extent_per_page(struct file *o_filp, struct inode *donor_inode, pgoff_t orig_page_offset, int data_offset_in_page, - int block_len_in_page, int uninit, int *err) + int block_len_in_page, int unwritten, int *err) { struct inode *orig_inode = file_inode(o_filp); struct page *pagep[2] = {NULL, NULL}; @@ -962,27 +962,27 @@ again: if (unlikely(*err < 0)) goto stop_journal; /* - * If orig extent was uninitialized it can become initialized + * If orig extent was unwritten it can become initialized * at any time after i_data_sem was dropped, in order to * serialize with delalloc we have recheck extent while we * hold page's lock, if it is still the case data copy is not * necessary, just swap data blocks between orig and donor. */ - if (uninit) { + if (unwritten) { ext4_double_down_write_data_sem(orig_inode, donor_inode); /* If any of extents in range became initialized we have to * fallback to data copying */ - uninit = mext_check_coverage(orig_inode, orig_blk_offset, - block_len_in_page, 1, err); + unwritten = mext_check_coverage(orig_inode, orig_blk_offset, + block_len_in_page, 1, err); if (*err) goto drop_data_sem; - uninit &= mext_check_coverage(donor_inode, orig_blk_offset, - block_len_in_page, 1, err); + unwritten &= mext_check_coverage(donor_inode, orig_blk_offset, + block_len_in_page, 1, err); if (*err) goto drop_data_sem; - if (!uninit) { + if (!unwritten) { ext4_double_up_write_data_sem(orig_inode, donor_inode); goto data_copy; } @@ -1259,7 +1259,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits; int data_offset_in_page; int block_len_in_page; - int uninit; + int unwritten; if (orig_inode->i_sb != donor_inode->i_sb) { ext4_debug("ext4 move extent: The argument files " @@ -1391,8 +1391,8 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, !last_extent) continue; - /* Is original extent is uninitialized */ - uninit = ext4_ext_is_uninitialized(ext_prev); + /* Is original extent is unwritten */ + unwritten = ext4_ext_is_unwritten(ext_prev); data_offset_in_page = seq_start % blocks_per_page; @@ -1432,8 +1432,8 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, o_filp, donor_inode, orig_page_offset, data_offset_in_page, - block_len_in_page, uninit, - &ret); + block_len_in_page, + unwritten, &ret); /* Count how many blocks we have exchanged */ *moved_len += block_len_in_page; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 6f9e6fadac04..c4895c195e00 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3337,7 +3337,7 @@ static ext4_fsblk_t ext4_calculate_resv_clusters(struct super_block *sb) * By default we reserve 2% or 4096 clusters, whichever is smaller. * This should cover the situations where we can not afford to run * out of space like for example punch hole, or converting - * uninitialized extents in delalloc path. In most cases such + * unwritten extents in delalloc path. In most cases such * allocation would require 1, or 2 blocks, higher numbers are * very rare. */ diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 9995d3b588a8..d4f70a7fe876 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -36,7 +36,7 @@ struct extent_status; #define show_map_flags(flags) __print_flags(flags, "|", \ { EXT4_GET_BLOCKS_CREATE, "CREATE" }, \ - { EXT4_GET_BLOCKS_UNINIT_EXT, "UNINIT" }, \ + { EXT4_GET_BLOCKS_UNWRIT_EXT, "UNWRIT" }, \ { EXT4_GET_BLOCKS_DELALLOC_RESERVE, "DELALLOC" }, \ { EXT4_GET_BLOCKS_PRE_IO, "PRE_IO" }, \ { EXT4_GET_BLOCKS_CONVERT, "CONVERT" }, \ @@ -1496,7 +1496,7 @@ DEFINE_EVENT(ext4__truncate, ext4_truncate_exit, TP_ARGS(inode) ); -/* 'ux' is the uninitialized extent. */ +/* 'ux' is the unwritten extent. */ TRACE_EVENT(ext4_ext_convert_to_initialized_enter, TP_PROTO(struct inode *inode, struct ext4_map_blocks *map, struct ext4_extent *ux), @@ -1532,7 +1532,7 @@ TRACE_EVENT(ext4_ext_convert_to_initialized_enter, ); /* - * 'ux' is the uninitialized extent. + * 'ux' is the unwritten extent. * 'ix' is the initialized extent to which blocks are transferred. */ TRACE_EVENT(ext4_ext_convert_to_initialized_fastpath, @@ -1810,7 +1810,7 @@ DEFINE_EVENT(ext4__trim, ext4_trim_all_free, TP_ARGS(sb, group, start, len) ); -TRACE_EVENT(ext4_ext_handle_uninitialized_extents, +TRACE_EVENT(ext4_ext_handle_unwritten_extents, TP_PROTO(struct inode *inode, struct ext4_map_blocks *map, int flags, unsigned int allocated, ext4_fsblk_t newblock), -- cgit v1.2.3 From ab2874a8faac9db00eb03ec831b9a983627fb2d1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 19 Apr 2014 10:43:57 +0200 Subject: ASoC: Change return type of snd_soc_write() to int The CODEC's write callback can return a negative error code, make sure to pass that on correctly. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++-- sound/soc/soc-io.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 81bc331c520f..192ddc40ae0a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1125,8 +1125,8 @@ static inline struct snd_soc_platform *snd_soc_component_to_platform( /* codec IO */ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); -unsigned int snd_soc_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int val); +int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val); /* device driver data */ diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 31ddd52c72fc..5fb85783b044 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -31,8 +31,8 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) } EXPORT_SYMBOL_GPL(snd_soc_read); -unsigned int snd_soc_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int val) +int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) { dev_dbg(codec->dev, "write %x = %x\n", reg, val); trace_snd_soc_reg_write(codec, reg, val); -- cgit v1.2.3 From 4104d326b670c2b66f575d2004daa28b2d1b4c8d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 10 Jan 2014 17:01:58 -0500 Subject: ftrace: Remove global function list and call function directly Instead of having a list of global functions that are called, as only one global function is allow to be enabled at a time, there's no reason to have a list. Instead, simply have all the users of the global ops, use the global ops directly, instead of registering their own ftrace_ops. Just switch what function is used before enabling the function tracer. This removes a lot of code as well as the complexity involved with it. Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 20 ++--- kernel/trace/ftrace.c | 152 +++++++++++--------------------------- kernel/trace/trace.c | 2 + kernel/trace/trace.h | 19 +++-- kernel/trace/trace_functions.c | 55 +++++--------- kernel/trace/trace_irqsoff.c | 33 ++++----- kernel/trace/trace_sched_wakeup.c | 40 +++++----- kernel/trace/trace_selftest.c | 33 +++++---- 8 files changed, 133 insertions(+), 221 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 9212b017bc72..f0ff2c2453e7 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -62,9 +62,6 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip, * set in the flags member. * * ENABLED - set/unset when ftrace_ops is registered/unregistered - * GLOBAL - set manualy by ftrace_ops user to denote the ftrace_ops - * is part of the global tracers sharing the same filter - * via set_ftrace_* debugfs files. * DYNAMIC - set when ftrace_ops is registered to denote dynamically * allocated ftrace_ops which need special care * CONTROL - set manualy by ftrace_ops user to denote the ftrace_ops @@ -96,15 +93,14 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip, */ enum { FTRACE_OPS_FL_ENABLED = 1 << 0, - FTRACE_OPS_FL_GLOBAL = 1 << 1, - FTRACE_OPS_FL_DYNAMIC = 1 << 2, - FTRACE_OPS_FL_CONTROL = 1 << 3, - FTRACE_OPS_FL_SAVE_REGS = 1 << 4, - FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 5, - FTRACE_OPS_FL_RECURSION_SAFE = 1 << 6, - FTRACE_OPS_FL_STUB = 1 << 7, - FTRACE_OPS_FL_INITIALIZED = 1 << 8, - FTRACE_OPS_FL_DELETED = 1 << 9, + FTRACE_OPS_FL_DYNAMIC = 1 << 1, + FTRACE_OPS_FL_CONTROL = 1 << 2, + FTRACE_OPS_FL_SAVE_REGS = 1 << 3, + FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 4, + FTRACE_OPS_FL_RECURSION_SAFE = 1 << 5, + FTRACE_OPS_FL_STUB = 1 << 6, + FTRACE_OPS_FL_INITIALIZED = 1 << 7, + FTRACE_OPS_FL_DELETED = 1 << 8, }; /* diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1fd4b9479210..8f61ef70a297 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -62,7 +62,7 @@ #define FTRACE_HASH_DEFAULT_BITS 10 #define FTRACE_HASH_MAX_BITS 12 -#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL) +#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_CONTROL) #ifdef CONFIG_DYNAMIC_FTRACE #define INIT_REGEX_LOCK(opsname) \ @@ -103,7 +103,6 @@ static int ftrace_disabled __read_mostly; static DEFINE_MUTEX(ftrace_lock); -static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; @@ -171,23 +170,6 @@ int ftrace_nr_registered_ops(void) return cnt; } -static void -ftrace_global_list_func(unsigned long ip, unsigned long parent_ip, - struct ftrace_ops *op, struct pt_regs *regs) -{ - int bit; - - bit = trace_test_and_set_recursion(TRACE_GLOBAL_START, TRACE_GLOBAL_MAX); - if (bit < 0) - return; - - do_for_each_ftrace_op(op, ftrace_global_list) { - op->func(ip, parent_ip, op, regs); - } while_for_each_ftrace_op(op); - - trace_clear_recursion(bit); -} - static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs) { @@ -237,43 +219,6 @@ static int control_ops_alloc(struct ftrace_ops *ops) return 0; } -static void update_global_ops(void) -{ - ftrace_func_t func = ftrace_global_list_func; - void *private = NULL; - - /* The list has its own recursion protection. */ - global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE; - - /* - * If there's only one function registered, then call that - * function directly. Otherwise, we need to iterate over the - * registered callers. - */ - if (ftrace_global_list == &ftrace_list_end || - ftrace_global_list->next == &ftrace_list_end) { - func = ftrace_global_list->func; - private = ftrace_global_list->private; - /* - * As we are calling the function directly. - * If it does not have recursion protection, - * the function_trace_op needs to be updated - * accordingly. - */ - if (!(ftrace_global_list->flags & FTRACE_OPS_FL_RECURSION_SAFE)) - global_ops.flags &= ~FTRACE_OPS_FL_RECURSION_SAFE; - } - - /* If we filter on pids, update to use the pid function */ - if (!list_empty(&ftrace_pids)) { - set_ftrace_pid_function(func); - func = ftrace_pid_func; - } - - global_ops.func = func; - global_ops.private = private; -} - static void ftrace_sync(struct work_struct *work) { /* @@ -301,8 +246,6 @@ static void update_ftrace_function(void) { ftrace_func_t func; - update_global_ops(); - /* * If we are at the end of the list and this ops is * recursion safe and not dynamic and the arch supports passing ops, @@ -314,10 +257,7 @@ static void update_ftrace_function(void) (ftrace_ops_list->flags & FTRACE_OPS_FL_RECURSION_SAFE) && !FTRACE_FORCE_LIST_FUNC)) { /* Set the ftrace_ops that the arch callback uses */ - if (ftrace_ops_list == &global_ops) - set_function_trace_op = ftrace_global_list; - else - set_function_trace_op = ftrace_ops_list; + set_function_trace_op = ftrace_ops_list; func = ftrace_ops_list->func; } else { /* Just use the default ftrace_ops */ @@ -434,16 +374,9 @@ static int __register_ftrace_function(struct ftrace_ops *ops) if (ops->flags & FTRACE_OPS_FL_DELETED) return -EINVAL; - if (FTRACE_WARN_ON(ops == &global_ops)) - return -EINVAL; - if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) return -EBUSY; - /* We don't support both control and global flags set. */ - if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK) - return -EINVAL; - #ifndef CONFIG_DYNAMIC_FTRACE_WITH_REGS /* * If the ftrace_ops specifies SAVE_REGS, then it only can be used @@ -461,10 +394,7 @@ static int __register_ftrace_function(struct ftrace_ops *ops) if (!core_kernel_data((unsigned long)ops)) ops->flags |= FTRACE_OPS_FL_DYNAMIC; - if (ops->flags & FTRACE_OPS_FL_GLOBAL) { - add_ftrace_list_ops(&ftrace_global_list, &global_ops, ops); - ops->flags |= FTRACE_OPS_FL_ENABLED; - } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { + if (ops->flags & FTRACE_OPS_FL_CONTROL) { if (control_ops_alloc(ops)) return -ENOMEM; add_ftrace_list_ops(&ftrace_control_list, &control_ops, ops); @@ -484,15 +414,7 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) if (WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED))) return -EBUSY; - if (FTRACE_WARN_ON(ops == &global_ops)) - return -EINVAL; - - if (ops->flags & FTRACE_OPS_FL_GLOBAL) { - ret = remove_ftrace_list_ops(&ftrace_global_list, - &global_ops, ops); - if (!ret) - ops->flags &= ~FTRACE_OPS_FL_ENABLED; - } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { + if (ops->flags & FTRACE_OPS_FL_CONTROL) { ret = remove_ftrace_list_ops(&ftrace_control_list, &control_ops, ops); } else @@ -2128,15 +2050,6 @@ static int ftrace_startup(struct ftrace_ops *ops, int command) ftrace_start_up++; command |= FTRACE_UPDATE_CALLS; - /* ops marked global share the filter hashes */ - if (ops->flags & FTRACE_OPS_FL_GLOBAL) { - ops = &global_ops; - /* Don't update hash if global is already set */ - if (global_start_up) - hash_enable = false; - global_start_up++; - } - ops->flags |= FTRACE_OPS_FL_ENABLED; if (hash_enable) ftrace_hash_rec_enable(ops, 1); @@ -2166,21 +2079,10 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) */ WARN_ON_ONCE(ftrace_start_up < 0); - if (ops->flags & FTRACE_OPS_FL_GLOBAL) { - ops = &global_ops; - global_start_up--; - WARN_ON_ONCE(global_start_up < 0); - /* Don't update hash if global still has users */ - if (global_start_up) { - WARN_ON_ONCE(!ftrace_start_up); - hash_disable = false; - } - } - if (hash_disable) ftrace_hash_rec_disable(ops, 1); - if (ops != &global_ops || !global_start_up) + if (!global_start_up) ops->flags &= ~FTRACE_OPS_FL_ENABLED; command |= FTRACE_UPDATE_CALLS; @@ -3524,10 +3426,6 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, struct ftrace_hash *hash; int ret; - /* All global ops uses the global ops filters */ - if (ops->flags & FTRACE_OPS_FL_GLOBAL) - ops = &global_ops; - if (unlikely(ftrace_disabled)) return -ENODEV; @@ -4462,6 +4360,34 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) #endif /* CONFIG_DYNAMIC_FTRACE */ +__init void ftrace_init_global_array_ops(struct trace_array *tr) +{ + tr->ops = &global_ops; + tr->ops->private = tr; +} + +void ftrace_init_array_ops(struct trace_array *tr, ftrace_func_t func) +{ + /* If we filter on pids, update to use the pid function */ + if (tr->flags & TRACE_ARRAY_FL_GLOBAL) { + if (WARN_ON(tr->ops->func != ftrace_stub)) + printk("ftrace ops had %pS for function\n", + tr->ops->func); + /* Only the top level instance does pid tracing */ + if (!list_empty(&ftrace_pids)) { + set_ftrace_pid_function(func); + func = ftrace_pid_func; + } + } + tr->ops->func = func; + tr->ops->private = tr; +} + +void ftrace_reset_array_ops(struct trace_array *tr) +{ + tr->ops->func = ftrace_stub; +} + static void ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs) @@ -4520,9 +4446,16 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, */ preempt_disable_notrace(); do_for_each_ftrace_op(op, ftrace_ops_list) { - if (ftrace_ops_test(op, ip, regs)) + if (ftrace_ops_test(op, ip, regs)) { + if (WARN_ON(!op->func)) { + function_trace_stop = 1; + printk("op=%p %pS\n", op, op); + goto out; + } op->func(ip, parent_ip, op, regs); + } } while_for_each_ftrace_op(op); +out: preempt_enable_notrace(); trace_clear_recursion(bit); } @@ -5076,8 +5009,7 @@ ftrace_suspend_notifier_call(struct notifier_block *bl, unsigned long state, /* Just a place holder for function graph */ static struct ftrace_ops fgraph_ops __read_mostly = { .func = ftrace_stub, - .flags = FTRACE_OPS_FL_STUB | FTRACE_OPS_FL_GLOBAL | - FTRACE_OPS_FL_RECURSION_SAFE, + .flags = FTRACE_OPS_FL_STUB | FTRACE_OPS_FL_RECURSION_SAFE, }; static int ftrace_graph_entry_test(struct ftrace_graph_ent *trace) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 737b0efa1a62..fdd33aacdf05 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6629,6 +6629,8 @@ __init static int tracer_alloc_buffers(void) */ global_trace.current_trace = &nop_trace; + ftrace_init_global_array_ops(&global_trace); + register_tracer(&nop_trace); /* All seems OK, enable tracing */ diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 2e29d7ba5a52..df5256be64cd 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -416,13 +416,7 @@ enum { TRACE_FTRACE_IRQ_BIT, TRACE_FTRACE_SIRQ_BIT, - /* GLOBAL_BITs must be greater than FTRACE_BITs */ - TRACE_GLOBAL_BIT, - TRACE_GLOBAL_NMI_BIT, - TRACE_GLOBAL_IRQ_BIT, - TRACE_GLOBAL_SIRQ_BIT, - - /* INTERNAL_BITs must be greater than GLOBAL_BITs */ + /* INTERNAL_BITs must be greater than FTRACE_BITs */ TRACE_INTERNAL_BIT, TRACE_INTERNAL_NMI_BIT, TRACE_INTERNAL_IRQ_BIT, @@ -449,9 +443,6 @@ enum { #define TRACE_FTRACE_START TRACE_FTRACE_BIT #define TRACE_FTRACE_MAX ((1 << (TRACE_FTRACE_START + TRACE_CONTEXT_BITS)) - 1) -#define TRACE_GLOBAL_START TRACE_GLOBAL_BIT -#define TRACE_GLOBAL_MAX ((1 << (TRACE_GLOBAL_START + TRACE_CONTEXT_BITS)) - 1) - #define TRACE_LIST_START TRACE_INTERNAL_BIT #define TRACE_LIST_MAX ((1 << (TRACE_LIST_START + TRACE_CONTEXT_BITS)) - 1) @@ -823,6 +814,9 @@ extern int ftrace_is_dead(void); int ftrace_create_function_files(struct trace_array *tr, struct dentry *parent); void ftrace_destroy_function_files(struct trace_array *tr); +void ftrace_init_global_array_ops(struct trace_array *tr); +void ftrace_init_array_ops(struct trace_array *tr, ftrace_func_t func); +void ftrace_reset_array_ops(struct trace_array *tr); #else static inline int ftrace_trace_task(struct task_struct *task) { @@ -836,6 +830,11 @@ ftrace_create_function_files(struct trace_array *tr, return 0; } static inline void ftrace_destroy_function_files(struct trace_array *tr) { } +static inline __init void +ftrace_init_global_array_ops(struct trace_array *tr) { } +static inline void ftrace_reset_array_ops(struct trace_array *tr) { } +/* ftace_func_t type is not defined, use macro instead of static inline */ +#define ftrace_init_array_ops(tr, func) do { } while (0) #endif /* CONFIG_FUNCTION_TRACER */ #if defined(CONFIG_FUNCTION_TRACER) && defined(CONFIG_DYNAMIC_FTRACE) diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index ffd56351b521..2d9482b8f26a 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -26,8 +26,6 @@ function_trace_call(unsigned long ip, unsigned long parent_ip, static void function_stack_trace_call(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *pt_regs); -static struct ftrace_ops trace_ops; -static struct ftrace_ops trace_stack_ops; static struct tracer_flags func_flags; /* Our option */ @@ -83,28 +81,24 @@ void ftrace_destroy_function_files(struct trace_array *tr) static int function_trace_init(struct trace_array *tr) { - struct ftrace_ops *ops; - - if (tr->flags & TRACE_ARRAY_FL_GLOBAL) { - /* There's only one global tr */ - if (!trace_ops.private) { - trace_ops.private = tr; - trace_stack_ops.private = tr; - } + ftrace_func_t func; - if (func_flags.val & TRACE_FUNC_OPT_STACK) - ops = &trace_stack_ops; - else - ops = &trace_ops; - tr->ops = ops; - } else if (!tr->ops) { - /* - * Instance trace_arrays get their ops allocated - * at instance creation. Unless it failed - * the allocation. - */ + /* + * Instance trace_arrays get their ops allocated + * at instance creation. Unless it failed + * the allocation. + */ + if (!tr->ops) return -ENOMEM; - } + + /* Currently only the global instance can do stack tracing */ + if (tr->flags & TRACE_ARRAY_FL_GLOBAL && + func_flags.val & TRACE_FUNC_OPT_STACK) + func = function_stack_trace_call; + else + func = function_trace_call; + + ftrace_init_array_ops(tr, func); tr->trace_buffer.cpu = get_cpu(); put_cpu(); @@ -118,6 +112,7 @@ static void function_trace_reset(struct trace_array *tr) { tracing_stop_function_trace(tr); tracing_stop_cmdline_record(); + ftrace_reset_array_ops(tr); } static void function_trace_start(struct trace_array *tr) @@ -199,18 +194,6 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip, local_irq_restore(flags); } -static struct ftrace_ops trace_ops __read_mostly = -{ - .func = function_trace_call, - .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, -}; - -static struct ftrace_ops trace_stack_ops __read_mostly = -{ - .func = function_stack_trace_call, - .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, -}; - static struct tracer_opt func_opts[] = { #ifdef CONFIG_STACKTRACE { TRACER_OPT(func_stack_trace, TRACE_FUNC_OPT_STACK) }, @@ -248,10 +231,10 @@ func_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set) unregister_ftrace_function(tr->ops); if (set) { - tr->ops = &trace_stack_ops; + tr->ops->func = function_stack_trace_call; register_ftrace_function(tr->ops); } else { - tr->ops = &trace_ops; + tr->ops->func = function_trace_call; register_ftrace_function(tr->ops); } diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 8ff02cbb892f..b5cb047df3e9 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -151,12 +151,6 @@ irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip, atomic_dec(&data->disabled); } - -static struct ftrace_ops trace_ops __read_mostly = -{ - .func = irqsoff_tracer_call, - .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, -}; #endif /* CONFIG_FUNCTION_TRACER */ #ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -531,7 +525,7 @@ void trace_preempt_off(unsigned long a0, unsigned long a1) } #endif /* CONFIG_PREEMPT_TRACER */ -static int register_irqsoff_function(int graph, int set) +static int register_irqsoff_function(struct trace_array *tr, int graph, int set) { int ret; @@ -543,7 +537,7 @@ static int register_irqsoff_function(int graph, int set) ret = register_ftrace_graph(&irqsoff_graph_return, &irqsoff_graph_entry); else - ret = register_ftrace_function(&trace_ops); + ret = register_ftrace_function(tr->ops); if (!ret) function_enabled = true; @@ -551,7 +545,7 @@ static int register_irqsoff_function(int graph, int set) return ret; } -static void unregister_irqsoff_function(int graph) +static void unregister_irqsoff_function(struct trace_array *tr, int graph) { if (!function_enabled) return; @@ -559,17 +553,17 @@ static void unregister_irqsoff_function(int graph) if (graph) unregister_ftrace_graph(); else - unregister_ftrace_function(&trace_ops); + unregister_ftrace_function(tr->ops); function_enabled = false; } -static void irqsoff_function_set(int set) +static void irqsoff_function_set(struct trace_array *tr, int set) { if (set) - register_irqsoff_function(is_graph(), 1); + register_irqsoff_function(tr, is_graph(), 1); else - unregister_irqsoff_function(is_graph()); + unregister_irqsoff_function(tr, is_graph()); } static int irqsoff_flag_changed(struct trace_array *tr, u32 mask, int set) @@ -577,7 +571,7 @@ static int irqsoff_flag_changed(struct trace_array *tr, u32 mask, int set) struct tracer *tracer = tr->current_trace; if (mask & TRACE_ITER_FUNCTION) - irqsoff_function_set(set); + irqsoff_function_set(tr, set); return trace_keep_overwrite(tracer, mask, set); } @@ -586,7 +580,7 @@ static int start_irqsoff_tracer(struct trace_array *tr, int graph) { int ret; - ret = register_irqsoff_function(graph, 0); + ret = register_irqsoff_function(tr, graph, 0); if (!ret && tracing_is_enabled()) tracer_enabled = 1; @@ -600,7 +594,7 @@ static void stop_irqsoff_tracer(struct trace_array *tr, int graph) { tracer_enabled = 0; - unregister_irqsoff_function(graph); + unregister_irqsoff_function(tr, graph); } static void __irqsoff_tracer_init(struct trace_array *tr) @@ -617,7 +611,11 @@ static void __irqsoff_tracer_init(struct trace_array *tr) smp_wmb(); tracing_reset_online_cpus(&tr->trace_buffer); - if (start_irqsoff_tracer(tr, is_graph())) + ftrace_init_array_ops(tr, irqsoff_tracer_call); + + /* Only toplevel instance supports graph tracing */ + if (start_irqsoff_tracer(tr, (tr->flags & TRACE_ARRAY_FL_GLOBAL && + is_graph()))) printk(KERN_ERR "failed to start irqsoff tracer\n"); } @@ -630,6 +628,7 @@ static void irqsoff_tracer_reset(struct trace_array *tr) set_tracer_flag(tr, TRACE_ITER_LATENCY_FMT, lat_flag); set_tracer_flag(tr, TRACE_ITER_OVERWRITE, overwrite_flag); + ftrace_reset_array_ops(tr); } static void irqsoff_tracer_start(struct trace_array *tr) diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index e14da5e97a69..4dd986defa60 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -130,15 +130,9 @@ wakeup_tracer_call(unsigned long ip, unsigned long parent_ip, atomic_dec(&data->disabled); preempt_enable_notrace(); } - -static struct ftrace_ops trace_ops __read_mostly = -{ - .func = wakeup_tracer_call, - .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, -}; #endif /* CONFIG_FUNCTION_TRACER */ -static int register_wakeup_function(int graph, int set) +static int register_wakeup_function(struct trace_array *tr, int graph, int set) { int ret; @@ -150,7 +144,7 @@ static int register_wakeup_function(int graph, int set) ret = register_ftrace_graph(&wakeup_graph_return, &wakeup_graph_entry); else - ret = register_ftrace_function(&trace_ops); + ret = register_ftrace_function(tr->ops); if (!ret) function_enabled = true; @@ -158,7 +152,7 @@ static int register_wakeup_function(int graph, int set) return ret; } -static void unregister_wakeup_function(int graph) +static void unregister_wakeup_function(struct trace_array *tr, int graph) { if (!function_enabled) return; @@ -166,17 +160,17 @@ static void unregister_wakeup_function(int graph) if (graph) unregister_ftrace_graph(); else - unregister_ftrace_function(&trace_ops); + unregister_ftrace_function(tr->ops); function_enabled = false; } -static void wakeup_function_set(int set) +static void wakeup_function_set(struct trace_array *tr, int set) { if (set) - register_wakeup_function(is_graph(), 1); + register_wakeup_function(tr, is_graph(), 1); else - unregister_wakeup_function(is_graph()); + unregister_wakeup_function(tr, is_graph()); } static int wakeup_flag_changed(struct trace_array *tr, u32 mask, int set) @@ -184,16 +178,16 @@ static int wakeup_flag_changed(struct trace_array *tr, u32 mask, int set) struct tracer *tracer = tr->current_trace; if (mask & TRACE_ITER_FUNCTION) - wakeup_function_set(set); + wakeup_function_set(tr, set); return trace_keep_overwrite(tracer, mask, set); } -static int start_func_tracer(int graph) +static int start_func_tracer(struct trace_array *tr, int graph) { int ret; - ret = register_wakeup_function(graph, 0); + ret = register_wakeup_function(tr, graph, 0); if (!ret && tracing_is_enabled()) tracer_enabled = 1; @@ -203,11 +197,11 @@ static int start_func_tracer(int graph) return ret; } -static void stop_func_tracer(int graph) +static void stop_func_tracer(struct trace_array *tr, int graph) { tracer_enabled = 0; - unregister_wakeup_function(graph); + unregister_wakeup_function(tr, graph); } #ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -221,12 +215,12 @@ wakeup_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set) if (!(is_graph() ^ set)) return 0; - stop_func_tracer(!set); + stop_func_tracer(tr, !set); wakeup_reset(wakeup_trace); tracing_max_latency = 0; - return start_func_tracer(set); + return start_func_tracer(tr, set); } static int wakeup_graph_entry(struct ftrace_graph_ent *trace) @@ -587,7 +581,7 @@ static void start_wakeup_tracer(struct trace_array *tr) */ smp_wmb(); - if (start_func_tracer(is_graph())) + if (start_func_tracer(tr, is_graph())) printk(KERN_ERR "failed to start wakeup tracer\n"); return; @@ -600,7 +594,7 @@ fail_deprobe: static void stop_wakeup_tracer(struct trace_array *tr) { tracer_enabled = 0; - stop_func_tracer(is_graph()); + stop_func_tracer(tr, is_graph()); unregister_trace_sched_switch(probe_wakeup_sched_switch, NULL); unregister_trace_sched_wakeup_new(probe_wakeup, NULL); unregister_trace_sched_wakeup(probe_wakeup, NULL); @@ -617,6 +611,7 @@ static int __wakeup_tracer_init(struct trace_array *tr) tracing_max_latency = 0; wakeup_trace = tr; + ftrace_init_array_ops(tr, wakeup_tracer_call); start_wakeup_tracer(tr); return 0; } @@ -653,6 +648,7 @@ static void wakeup_tracer_reset(struct trace_array *tr) set_tracer_flag(tr, TRACE_ITER_LATENCY_FMT, lat_flag); set_tracer_flag(tr, TRACE_ITER_OVERWRITE, overwrite_flag); + ftrace_reset_array_ops(tr); } static void wakeup_tracer_start(struct trace_array *tr) diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index e98fca60974f..519d04affe38 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -161,11 +161,6 @@ static struct ftrace_ops test_probe3 = { .flags = FTRACE_OPS_FL_RECURSION_SAFE, }; -static struct ftrace_ops test_global = { - .func = trace_selftest_test_global_func, - .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE, -}; - static void print_counts(void) { printk("(%d %d %d %d %d) ", @@ -185,7 +180,7 @@ static void reset_counts(void) trace_selftest_test_dyn_cnt = 0; } -static int trace_selftest_ops(int cnt) +static int trace_selftest_ops(struct trace_array *tr, int cnt) { int save_ftrace_enabled = ftrace_enabled; struct ftrace_ops *dyn_ops; @@ -220,7 +215,11 @@ static int trace_selftest_ops(int cnt) register_ftrace_function(&test_probe1); register_ftrace_function(&test_probe2); register_ftrace_function(&test_probe3); - register_ftrace_function(&test_global); + /* First time we are running with main function */ + if (cnt > 1) { + ftrace_init_array_ops(tr, trace_selftest_test_global_func); + register_ftrace_function(tr->ops); + } DYN_FTRACE_TEST_NAME(); @@ -232,8 +231,10 @@ static int trace_selftest_ops(int cnt) goto out; if (trace_selftest_test_probe3_cnt != 1) goto out; - if (trace_selftest_test_global_cnt == 0) - goto out; + if (cnt > 1) { + if (trace_selftest_test_global_cnt == 0) + goto out; + } DYN_FTRACE_TEST_NAME2(); @@ -269,8 +270,10 @@ static int trace_selftest_ops(int cnt) goto out_free; if (trace_selftest_test_probe3_cnt != 3) goto out_free; - if (trace_selftest_test_global_cnt == 0) - goto out; + if (cnt > 1) { + if (trace_selftest_test_global_cnt == 0) + goto out; + } if (trace_selftest_test_dyn_cnt == 0) goto out_free; @@ -295,7 +298,9 @@ static int trace_selftest_ops(int cnt) unregister_ftrace_function(&test_probe1); unregister_ftrace_function(&test_probe2); unregister_ftrace_function(&test_probe3); - unregister_ftrace_function(&test_global); + if (cnt > 1) + unregister_ftrace_function(tr->ops); + ftrace_reset_array_ops(tr); /* Make sure everything is off */ reset_counts(); @@ -388,7 +393,7 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, } /* Test the ops with global tracing running */ - ret = trace_selftest_ops(1); + ret = trace_selftest_ops(tr, 1); trace->reset(tr); out: @@ -399,7 +404,7 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, /* Test the ops with global tracing off */ if (!ret) - ret = trace_selftest_ops(2); + ret = trace_selftest_ops(tr, 2); return ret; } -- cgit v1.2.3 From 4525beeb9aadbb9e1cb3e9e135f4371553f26a70 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 16 Apr 2014 15:20:44 -0500 Subject: usb: phy: rename usb_nop_xceiv to usb_phy_generic no functional changes, just renaming the function in order to make it slightly clearer what it should be used for, also matching the driver name. Signed-off-by: Felipe Balbi --- arch/arm/mach-omap2/usb-host.c | 8 +++--- drivers/usb/dwc3/dwc3-exynos.c | 6 ++--- drivers/usb/dwc3/dwc3-pci.c | 6 ++--- drivers/usb/musb/am35x.c | 4 +-- drivers/usb/musb/blackfin.c | 4 +-- drivers/usb/musb/da8xx.c | 4 +-- drivers/usb/musb/davinci.c | 6 ++--- drivers/usb/musb/tusb6010.c | 6 ++--- drivers/usb/phy/phy-am335x.c | 2 +- drivers/usb/phy/phy-generic.c | 50 +++++++++++++++++------------------ drivers/usb/phy/phy-generic.h | 6 ++--- drivers/usb/phy/phy-keystone.c | 2 +- include/linux/usb/usb_phy_gen_xceiv.h | 10 +++---- 13 files changed, 57 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c index 10855eb4ccc1..ab983cdd3edf 100644 --- a/arch/arm/mach-omap2/usb-host.c +++ b/arch/arm/mach-omap2/usb-host.c @@ -349,7 +349,7 @@ static struct fixed_voltage_config hsusb_reg_config = { /* .init_data filled later */ }; -static const char *nop_name = "usb_phy_gen_xceiv"; /* NOP PHY driver */ +static const char *nop_name = "usb_phy_generic"; /* NOP PHY driver */ static const char *reg_name = "reg-fixed-voltage"; /* Regulator driver */ /** @@ -435,7 +435,7 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys) struct platform_device *pdev; char *phy_id; struct platform_device_info pdevinfo; - struct usb_phy_gen_xceiv_platform_data nop_pdata; + struct usb_phy_generic_platform_data nop_pdata; for (i = 0; i < num_phys; i++) { @@ -469,8 +469,8 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys) pdevinfo.id = phy->port; pdevinfo.data = &nop_pdata; pdevinfo.size_data = - sizeof(struct usb_phy_gen_xceiv_platform_data); - scnprintf(phy_id, MAX_STR, "usb_phy_gen_xceiv.%d", + sizeof(struct usb_phy_generic_platform_data); + scnprintf(phy_id, MAX_STR, "usb_phy_generic.%d", phy->port); pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) { diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 28c8ad79f5e6..821cc59e6e1d 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -38,13 +38,13 @@ struct dwc3_exynos { static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) { - struct usb_phy_gen_xceiv_platform_data pdata; + struct usb_phy_generic_platform_data pdata; struct platform_device *pdev; int ret; memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO); + pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO); if (!pdev) return -ENOMEM; @@ -56,7 +56,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) if (ret) goto err1; - pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO); + pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index f393c183cc69..8b162f0e293c 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -40,13 +40,13 @@ struct dwc3_pci { static int dwc3_pci_register_phys(struct dwc3_pci *glue) { - struct usb_phy_gen_xceiv_platform_data pdata; + struct usb_phy_generic_platform_data pdata; struct platform_device *pdev; int ret; memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("usb_phy_gen_xceiv", 0); + pdev = platform_device_alloc("usb_phy_generic", 0); if (!pdev) return -ENOMEM; @@ -58,7 +58,7 @@ static int dwc3_pci_register_phys(struct dwc3_pci *glue) if (ret) goto err1; - pdev = platform_device_alloc("usb_phy_gen_xceiv", 1); + pdev = platform_device_alloc("usb_phy_generic", 1); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index b3aa0184af9a..77ed66427969 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -360,7 +360,7 @@ static int am35x_musb_init(struct musb *musb) if (!rev) return -ENODEV; - usb_nop_xceiv_register(); + usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(musb->xceiv)) return -EPROBE_DEFER; @@ -402,7 +402,7 @@ static int am35x_musb_exit(struct musb *musb) data->set_phy_power(0); usb_put_phy(musb->xceiv); - usb_nop_xceiv_unregister(); + usb_phy_generic_unregister(); return 0; } diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 796677fa9a15..607f3ae04591 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -401,7 +401,7 @@ static int bfin_musb_init(struct musb *musb) } gpio_direction_output(musb->config->gpio_vrsel, 0); - usb_nop_xceiv_register(); + usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(musb->xceiv)) { gpio_free(musb->config->gpio_vrsel); @@ -426,7 +426,7 @@ static int bfin_musb_exit(struct musb *musb) gpio_free(musb->config->gpio_vrsel); usb_put_phy(musb->xceiv); - usb_nop_xceiv_unregister(); + usb_phy_generic_unregister(); return 0; } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index e3486de71995..bcdce8e64670 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -418,7 +418,7 @@ static int da8xx_musb_init(struct musb *musb) if (!rev) goto fail; - usb_nop_xceiv_register(); + usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(musb->xceiv)) { ret = -EPROBE_DEFER; @@ -453,7 +453,7 @@ static int da8xx_musb_exit(struct musb *musb) phy_off(); usb_put_phy(musb->xceiv); - usb_nop_xceiv_unregister(); + usb_phy_generic_unregister(); return 0; } diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index c259dac9d056..c0e07eddb079 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -381,7 +381,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; int ret = -ENODEV; - usb_nop_xceiv_register(); + usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(musb->xceiv)) { ret = -EPROBE_DEFER; @@ -439,7 +439,7 @@ static int davinci_musb_init(struct musb *musb) fail: usb_put_phy(musb->xceiv); unregister: - usb_nop_xceiv_unregister(); + usb_phy_generic_unregister(); return ret; } @@ -487,7 +487,7 @@ static int davinci_musb_exit(struct musb *musb) phy_off(); usb_put_phy(musb->xceiv); - usb_nop_xceiv_unregister(); + usb_phy_generic_unregister(); return 0; } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 4e9fb1d08698..0c0f5ee1e3f1 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1065,7 +1065,7 @@ static int tusb_musb_init(struct musb *musb) void __iomem *sync = NULL; int ret; - usb_nop_xceiv_register(); + usb_phy_generic_register(); musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(musb->xceiv)) return -EPROBE_DEFER; @@ -1117,7 +1117,7 @@ done: iounmap(sync); usb_put_phy(musb->xceiv); - usb_nop_xceiv_unregister(); + usb_phy_generic_unregister(); } return ret; } @@ -1133,7 +1133,7 @@ static int tusb_musb_exit(struct musb *musb) iounmap(musb->sync_va); usb_put_phy(musb->xceiv); - usb_nop_xceiv_unregister(); + usb_phy_generic_unregister(); return 0; } diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 12fc3468a01e..bb866e466051 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -13,7 +13,7 @@ #include "phy-generic.h" struct am335x_phy { - struct usb_phy_gen_xceiv usb_phy_gen; + struct usb_phy_generic usb_phy_gen; struct phy_control *phy_ctrl; int id; }; diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 95e70bc384c2..e76ca4ca3a8a 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -43,32 +43,32 @@ static struct platform_device *pd; -void usb_nop_xceiv_register(void) +void usb_phy_generic_register(void) { if (pd) return; - pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0); + pd = platform_device_register_simple("usb_phy_generic", -1, NULL, 0); if (IS_ERR(pd)) { pr_err("Unable to register generic usb transceiver\n"); pd = NULL; return; } } -EXPORT_SYMBOL_GPL(usb_nop_xceiv_register); +EXPORT_SYMBOL_GPL(usb_phy_generic_register); -void usb_nop_xceiv_unregister(void) +void usb_phy_generic_unregister(void) { platform_device_unregister(pd); pd = NULL; } -EXPORT_SYMBOL_GPL(usb_nop_xceiv_unregister); +EXPORT_SYMBOL_GPL(usb_phy_generic_unregister); static int nop_set_suspend(struct usb_phy *x, int suspend) { return 0; } -static void nop_reset_set(struct usb_phy_gen_xceiv *nop, int asserted) +static void nop_reset_set(struct usb_phy_generic *nop, int asserted) { int value; @@ -87,7 +87,7 @@ static void nop_reset_set(struct usb_phy_gen_xceiv *nop, int asserted) int usb_gen_phy_init(struct usb_phy *phy) { - struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); + struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); if (!IS_ERR(nop->vcc)) { if (regulator_enable(nop->vcc)) @@ -106,7 +106,7 @@ EXPORT_SYMBOL_GPL(usb_gen_phy_init); void usb_gen_phy_shutdown(struct usb_phy *phy) { - struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); + struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); /* Assert RESET */ nop_reset_set(nop, 1); @@ -150,8 +150,8 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } -int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, - struct usb_phy_gen_xceiv_platform_data *pdata) +int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, + struct usb_phy_generic_platform_data *pdata) { enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; @@ -245,10 +245,10 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, } EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); -static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) +static int usb_phy_generic_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usb_phy_gen_xceiv *nop; + struct usb_phy_generic *nop; int err; nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); @@ -274,9 +274,9 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) return 0; } -static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) +static int usb_phy_generic_remove(struct platform_device *pdev) { - struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); + struct usb_phy_generic *nop = platform_get_drvdata(pdev); usb_remove_phy(&nop->phy); @@ -290,29 +290,29 @@ static const struct of_device_id nop_xceiv_dt_ids[] = { MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); -static struct platform_driver usb_phy_gen_xceiv_driver = { - .probe = usb_phy_gen_xceiv_probe, - .remove = usb_phy_gen_xceiv_remove, +static struct platform_driver usb_phy_generic_driver = { + .probe = usb_phy_generic_probe, + .remove = usb_phy_generic_remove, .driver = { - .name = "usb_phy_gen_xceiv", + .name = "usb_phy_generic", .owner = THIS_MODULE, .of_match_table = nop_xceiv_dt_ids, }, }; -static int __init usb_phy_gen_xceiv_init(void) +static int __init usb_phy_generic_init(void) { - return platform_driver_register(&usb_phy_gen_xceiv_driver); + return platform_driver_register(&usb_phy_generic_driver); } -subsys_initcall(usb_phy_gen_xceiv_init); +subsys_initcall(usb_phy_generic_init); -static void __exit usb_phy_gen_xceiv_exit(void) +static void __exit usb_phy_generic_exit(void) { - platform_driver_unregister(&usb_phy_gen_xceiv_driver); + platform_driver_unregister(&usb_phy_generic_driver); } -module_exit(usb_phy_gen_xceiv_exit); +module_exit(usb_phy_generic_exit); -MODULE_ALIAS("platform:usb_phy_gen_xceiv"); +MODULE_ALIAS("platform:usb_phy_generic"); MODULE_AUTHOR("Texas Instruments Inc"); MODULE_DESCRIPTION("NOP USB Transceiver driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h index 38a81f307b82..f32450ada12d 100644 --- a/drivers/usb/phy/phy-generic.h +++ b/drivers/usb/phy/phy-generic.h @@ -3,7 +3,7 @@ #include -struct usb_phy_gen_xceiv { +struct usb_phy_generic { struct usb_phy phy; struct device *dev; struct clk *clk; @@ -15,7 +15,7 @@ struct usb_phy_gen_xceiv { int usb_gen_phy_init(struct usb_phy *phy); void usb_gen_phy_shutdown(struct usb_phy *phy); -int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, - struct usb_phy_gen_xceiv_platform_data *pdata); +int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, + struct usb_phy_generic_platform_data *pdata); #endif diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index d762003896c0..2404c442c302 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -35,7 +35,7 @@ #define PHY_REF_SSP_EN BIT(29) struct keystone_usbphy { - struct usb_phy_gen_xceiv usb_phy_gen; + struct usb_phy_generic usb_phy_gen; void __iomem *phy_ctrl; }; diff --git a/include/linux/usb/usb_phy_gen_xceiv.h b/include/linux/usb/usb_phy_gen_xceiv.h index cc8d818a83be..c00176d48625 100644 --- a/include/linux/usb/usb_phy_gen_xceiv.h +++ b/include/linux/usb/usb_phy_gen_xceiv.h @@ -3,7 +3,7 @@ #include -struct usb_phy_gen_xceiv_platform_data { +struct usb_phy_generic_platform_data { enum usb_phy_type type; unsigned long clk_rate; @@ -15,14 +15,14 @@ struct usb_phy_gen_xceiv_platform_data { #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ -extern void usb_nop_xceiv_register(void); -extern void usb_nop_xceiv_unregister(void); +extern void usb_phy_generic_register(void); +extern void usb_phy_generic_unregister(void); #else -static inline void usb_nop_xceiv_register(void) +static inline void usb_phy_generic_register(void) { } -static inline void usb_nop_xceiv_unregister(void) +static inline void usb_phy_generic_unregister(void) { } #endif -- cgit v1.2.3 From d7078df6be6e9e5e3ac354859f5b8d60114391b4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 16 Apr 2014 15:28:32 -0500 Subject: usb: phy: rename to now that all functions match the driver name, the only missing piece is to rename the header file itself. Signed-off-by: Felipe Balbi --- arch/arm/mach-omap2/board-omap3beagle.c | 1 - arch/arm/mach-omap2/usb-host.c | 2 +- drivers/usb/dwc3/dwc3-exynos.c | 2 +- drivers/usb/dwc3/dwc3-pci.c | 2 +- drivers/usb/musb/am35x.c | 2 +- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/da8xx.c | 2 +- drivers/usb/musb/davinci.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/phy/phy-am335x.c | 2 +- drivers/usb/phy/phy-generic.c | 2 +- drivers/usb/phy/phy-generic.h | 2 +- drivers/usb/phy/phy-keystone.c | 2 +- include/linux/usb/usb_phy_gen_xceiv.h | 30 ------------------------------ include/linux/usb/usb_phy_generic.h | 30 ++++++++++++++++++++++++++++++ 16 files changed, 43 insertions(+), 44 deletions(-) delete mode 100644 include/linux/usb/usb_phy_gen_xceiv.h create mode 100644 include/linux/usb/usb_phy_generic.h (limited to 'include') diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index d6ed819ff15c..660bfc5a70d7 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c index ab983cdd3edf..745367c0c2bb 100644 --- a/arch/arm/mach-omap2/usb-host.c +++ b/arch/arm/mach-omap2/usb-host.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "soc.h" #include "omap_device.h" diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 821cc59e6e1d..ed22d722884e 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 8b162f0e293c..1ed95e0386eb 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -23,7 +23,7 @@ #include #include -#include +#include /* FIXME define these in */ #define PCI_VENDOR_ID_SYNOPSYS 0x16c3 diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 77ed66427969..044cd824c70d 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include "musb_core.h" diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 607f3ae04591..c9992a2eaaa8 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index bcdce8e64670..a0dabb05de76 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index c0e07eddb079..737035457858 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 3372ded5def7..18882924d9d5 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 0c0f5ee1e3f1..8d4a8194c8f2 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "musb_core.h" diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index bb866e466051..585e50cb1980 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index e76ca4ca3a8a..2c49cd8f6d25 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h index f32450ada12d..d8feacc0b7fb 100644 --- a/drivers/usb/phy/phy-generic.h +++ b/drivers/usb/phy/phy-generic.h @@ -1,7 +1,7 @@ #ifndef _PHY_GENERIC_H_ #define _PHY_GENERIC_H_ -#include +#include struct usb_phy_generic { struct usb_phy phy; diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index 2404c442c302..f4d722de912b 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/include/linux/usb/usb_phy_gen_xceiv.h b/include/linux/usb/usb_phy_gen_xceiv.h deleted file mode 100644 index c00176d48625..000000000000 --- a/include/linux/usb/usb_phy_gen_xceiv.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef __LINUX_USB_NOP_XCEIV_H -#define __LINUX_USB_NOP_XCEIV_H - -#include - -struct usb_phy_generic_platform_data { - enum usb_phy_type type; - unsigned long clk_rate; - - /* if set fails with -EPROBE_DEFER if can't get regulator */ - unsigned int needs_vcc:1; - unsigned int needs_reset:1; /* deprecated */ - int gpio_reset; -}; - -#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) -/* sometimes transceivers are accessed only through e.g. ULPI */ -extern void usb_phy_generic_register(void); -extern void usb_phy_generic_unregister(void); -#else -static inline void usb_phy_generic_register(void) -{ -} - -static inline void usb_phy_generic_unregister(void) -{ -} -#endif - -#endif /* __LINUX_USB_NOP_XCEIV_H */ diff --git a/include/linux/usb/usb_phy_generic.h b/include/linux/usb/usb_phy_generic.h new file mode 100644 index 000000000000..c00176d48625 --- /dev/null +++ b/include/linux/usb/usb_phy_generic.h @@ -0,0 +1,30 @@ +#ifndef __LINUX_USB_NOP_XCEIV_H +#define __LINUX_USB_NOP_XCEIV_H + +#include + +struct usb_phy_generic_platform_data { + enum usb_phy_type type; + unsigned long clk_rate; + + /* if set fails with -EPROBE_DEFER if can't get regulator */ + unsigned int needs_vcc:1; + unsigned int needs_reset:1; /* deprecated */ + int gpio_reset; +}; + +#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) +/* sometimes transceivers are accessed only through e.g. ULPI */ +extern void usb_phy_generic_register(void); +extern void usb_phy_generic_unregister(void); +#else +static inline void usb_phy_generic_register(void) +{ +} + +static inline void usb_phy_generic_unregister(void) +{ +} +#endif + +#endif /* __LINUX_USB_NOP_XCEIV_H */ -- cgit v1.2.3 From 2f36ff6915c6c00df8b9962d9c6c7992befcf8ce Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 16 Apr 2014 16:16:33 -0500 Subject: usb: phy: generic: allow multiples calls to usb_phy_generic_register() it's now very easy to return a platform_device pointer and have the caller pass it as argument when calling usb_phy_generic_unregister(). Signed-off-by: Felipe Balbi --- drivers/usb/musb/am35x.c | 12 +++++++++--- drivers/usb/musb/blackfin.c | 10 ++++++++-- drivers/usb/musb/da8xx.c | 14 +++++++++++--- drivers/usb/musb/tusb6010.c | 3 ++- drivers/usb/phy/phy-generic.c | 19 +++++-------------- include/linux/usb/usb_phy_generic.h | 9 +++++---- 6 files changed, 40 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 05459b56b2a8..0a34dd859555 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -85,6 +85,7 @@ struct am35x_glue { struct device *dev; struct platform_device *musb; + struct platform_device *phy; struct clk *phy_clk; struct clk *clk; }; @@ -503,7 +504,9 @@ static int am35x_probe(struct platform_device *pdev) pdata->platform_ops = &am35x_ops; - usb_phy_generic_register(); + glue->phy = usb_phy_generic_register(); + if (IS_ERR(glue->phy)) + goto err7; platform_set_drvdata(pdev, glue); pinfo = am35x_dev_info; @@ -517,11 +520,14 @@ static int am35x_probe(struct platform_device *pdev) if (IS_ERR(musb)) { ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err7; + goto err8; } return 0; +err8: + usb_phy_generic_unregister(glue->phy); + err7: clk_disable(clk); @@ -546,7 +552,7 @@ static int am35x_remove(struct platform_device *pdev) struct am35x_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); - usb_phy_generic_unregister(); + usb_phy_generic_unregister(glue->phy); clk_disable(glue->clk); clk_disable(glue->phy_clk); clk_put(glue->clk); diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 53acffe9a858..d40d5f0b5528 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -29,6 +29,7 @@ struct bfin_glue { struct device *dev; struct platform_device *musb; + struct platform_device *phy; }; #define glue_to_musb(g) platform_get_drvdata(g->musb) @@ -475,7 +476,9 @@ static int bfin_probe(struct platform_device *pdev) pdata->platform_ops = &bfin_ops; - usb_phy_generic_register(); + glue->phy = usb_phy_generic_register(); + if (IS_ERR(glue->phy)) + goto err2; platform_set_drvdata(pdev, glue); memset(musb_resources, 0x00, sizeof(*musb_resources) * @@ -513,6 +516,9 @@ static int bfin_probe(struct platform_device *pdev) return 0; err3: + usb_phy_generic_unregister(glue->phy); + +err2: platform_device_put(musb); err1: @@ -527,7 +533,7 @@ static int bfin_remove(struct platform_device *pdev) struct bfin_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); - usb_phy_generic_unregister(); + usb_phy_generic_unregister(glue->phy); kfree(glue); return 0; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 024751f9b31d..058775e647ad 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -85,6 +85,7 @@ struct da8xx_glue { struct device *dev; struct platform_device *musb; + struct platform_device *phy; struct clk *clk; }; @@ -510,7 +511,11 @@ static int da8xx_probe(struct platform_device *pdev) pdata->platform_ops = &da8xx_ops; - usb_phy_generic_register(); + glue->phy = usb_phy_generic_register(); + if (IS_ERR(glue->phy)) { + ret = PTR_ERR(glue->phy); + goto err5; + } platform_set_drvdata(pdev, glue); memset(musb_resources, 0x00, sizeof(*musb_resources) * @@ -537,11 +542,14 @@ static int da8xx_probe(struct platform_device *pdev) if (IS_ERR(musb)) { ret = PTR_ERR(musb); dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); - goto err5; + goto err6; } return 0; +err6: + usb_phy_generic_unregister(glue->phy); + err5: clk_disable(clk); @@ -560,7 +568,7 @@ static int da8xx_remove(struct platform_device *pdev) struct da8xx_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); - usb_phy_generic_unregister(); + usb_phy_generic_unregister(glue->phy); clk_disable(glue->clk); clk_put(glue->clk); kfree(glue); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index e1da199c6f21..f38a8dbd6075 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -31,6 +31,7 @@ struct tusb6010_glue { struct device *dev; struct platform_device *musb; + struct platform_device *phy; }; static void tusb_musb_set_vbus(struct musb *musb, int is_on); @@ -1222,7 +1223,7 @@ static int tusb_remove(struct platform_device *pdev) struct tusb6010_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); - usb_phy_generic_unregister(); + usb_phy_generic_unregister(glue->phy); kfree(glue); return 0; diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 2c49cd8f6d25..7594e5069ae5 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -41,25 +41,16 @@ #include "phy-generic.h" -static struct platform_device *pd; - -void usb_phy_generic_register(void) +struct platform_device *usb_phy_generic_register(void) { - if (pd) - return; - pd = platform_device_register_simple("usb_phy_generic", -1, NULL, 0); - if (IS_ERR(pd)) { - pr_err("Unable to register generic usb transceiver\n"); - pd = NULL; - return; - } + return platform_device_register_simple("usb_phy_generic", + PLATFORM_DEVID_AUTO, NULL, 0); } EXPORT_SYMBOL_GPL(usb_phy_generic_register); -void usb_phy_generic_unregister(void) +void usb_phy_generic_unregister(struct platform_device *pdev) { - platform_device_unregister(pd); - pd = NULL; + platform_device_unregister(pdev); } EXPORT_SYMBOL_GPL(usb_phy_generic_unregister); diff --git a/include/linux/usb/usb_phy_generic.h b/include/linux/usb/usb_phy_generic.h index c00176d48625..8346bcc50c2f 100644 --- a/include/linux/usb/usb_phy_generic.h +++ b/include/linux/usb/usb_phy_generic.h @@ -15,14 +15,15 @@ struct usb_phy_generic_platform_data { #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ -extern void usb_phy_generic_register(void); -extern void usb_phy_generic_unregister(void); +extern struct platform_device *usb_phy_generic_register(void); +extern void usb_phy_generic_unregister(struct platform_device *); #else -static inline void usb_phy_generic_register(void) +static inline struct platform_device *usb_phy_generic_register(void) { + return NULL; } -static inline void usb_phy_generic_unregister(void) +static inline void usb_phy_generic_unregister(struct platform_device *pdev) { } #endif -- cgit v1.2.3 From dca769bd5a76e9e634cc36987760306846153cac Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 21 Apr 2014 10:50:35 -0500 Subject: usb: phy: generic: switch over to IS_ENABLED() when checking if our generic PHY is enabled, it's a lot easier to use IS_ENABLED() instead of manually checking for it. While at that, also remove the bogus defined(MODULE) at the end of the line. Signed-off-by: Felipe Balbi --- include/linux/usb/usb_phy_generic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb/usb_phy_generic.h b/include/linux/usb/usb_phy_generic.h index 8346bcc50c2f..68adae83affc 100644 --- a/include/linux/usb/usb_phy_generic.h +++ b/include/linux/usb/usb_phy_generic.h @@ -13,7 +13,7 @@ struct usb_phy_generic_platform_data { int gpio_reset; }; -#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_NOP_USB_XCEIV) /* sometimes transceivers are accessed only through e.g. ULPI */ extern struct platform_device *usb_phy_generic_register(void); extern void usb_phy_generic_unregister(struct platform_device *); -- cgit v1.2.3 From 68957303f44a501af5cf37913208a2acaa6bcdf1 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 25 Mar 2014 06:51:47 +0100 Subject: NFC: ST21NFCA: Add driver for STMicroelectronics ST21NFCA NFC Chip Add driver for STMicroelectronics ST21NFCA NFC controller. ST21NFCA is using HCI protocol, shdlc as LLC layer & I2C as communication protocol. Adding support for Reader/Writer mode with Tag type 1/2/3/4 A & B. It is using proprietary gate 15 for ISO14443-3 such as type 1 & type 2 tags. It is using proprietary gate 14 for type F tags. ST21NFCA_DEVICE_MGNT_GATE gives access to proprietary CLF configuration. Standard gate for ISO14443-4 A (13) & B (11) are also used. ST21NFCA specific mecanism: One particular point to notice for the data handling is that frame does not contain any length value. Therefore the i2c part of this driver is managing the reception with a read length sequence until the end of frame (0x7e) is reached. In order to avoid conflict between sof & eof a mecanism called byte stuffing concist of an escape byte (0x7d) insertion before special byte (0x7e, 0x7d). The special byte is then xored with 0x20. In this driver, When data are available in the CLF, the interrupt gpio is driven to active state and triggered an interrupt. Once the i2c_master_recv start, the interrupt gpio is driven to idle state until its complete. If the frame is incomplete or data are still available, interrupts will be triggered again. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 1 + drivers/nfc/Makefile | 1 + drivers/nfc/st21nfca/Kconfig | 23 ++ drivers/nfc/st21nfca/Makefile | 8 + drivers/nfc/st21nfca/i2c.c | 595 +++++++++++++++++++++++++++++++++ drivers/nfc/st21nfca/st21nfca.c | 506 ++++++++++++++++++++++++++++ drivers/nfc/st21nfca/st21nfca.h | 87 +++++ include/linux/platform_data/st21nfca.h | 32 ++ 8 files changed, 1253 insertions(+) create mode 100644 drivers/nfc/st21nfca/Kconfig create mode 100644 drivers/nfc/st21nfca/Makefile create mode 100644 drivers/nfc/st21nfca/i2c.c create mode 100644 drivers/nfc/st21nfca/st21nfca.c create mode 100644 drivers/nfc/st21nfca/st21nfca.h create mode 100644 include/linux/platform_data/st21nfca.h (limited to 'include') diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 65d4ca19d132..26c66a126551 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -71,5 +71,6 @@ config NFC_PORT100 source "drivers/nfc/pn544/Kconfig" source "drivers/nfc/microread/Kconfig" source "drivers/nfc/nfcmrvl/Kconfig" +source "drivers/nfc/st21nfca/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index ae42a3fa60c9..23225b0287fd 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_NFC_SIM) += nfcsim.o obj-$(CONFIG_NFC_PORT100) += port100.o obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o +obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig new file mode 100644 index 000000000000..ee459f066ade --- /dev/null +++ b/drivers/nfc/st21nfca/Kconfig @@ -0,0 +1,23 @@ +config NFC_ST21NFCA + tristate "STMicroelectronics ST21NFCA NFC driver" + depends on NFC_HCI + select CRC_CCITT + default n + ---help--- + STMicroelectronics ST21NFCA core driver. It implements the chipset + HCI logic and hooks into the NFC kernel APIs. Physical layers will + register against it. + + To compile this driver as a module, choose m here. The module will + be called st21nfca. + Say N if unsure. + +config NFC_ST21NFCA_I2C + tristate "NFC ST21NFCA i2c support" + depends on NFC_ST21NFCA && I2C && NFC_SHDLC + ---help--- + This module adds support for the STMicroelectronics st21nfca i2c interface. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called st21nfca_i2c. + Say N if unsure. diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile new file mode 100644 index 000000000000..038ed093a119 --- /dev/null +++ b/drivers/nfc/st21nfca/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for ST21NFCA HCI based NFC driver +# + +st21nfca_i2c-objs = i2c.o + +obj-$(CONFIG_NFC_ST21NFCA) += st21nfca.o +obj-$(CONFIG_NFC_ST21NFCA_I2C) += st21nfca_i2c.o diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c new file mode 100644 index 000000000000..3b0fd0f76d1c --- /dev/null +++ b/drivers/nfc/st21nfca/i2c.c @@ -0,0 +1,595 @@ +/* + * I2C Link Layer for ST21NFCA HCI based Driver + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "st21nfca.h" + +/* + * Every frame starts with ST21NFCA_SOF_EOF and ends with ST21NFCA_SOF_EOF. + * Because ST21NFCA_SOF_EOF is a possible data value, there is a mecanism + * called byte stuffing has been introduced. + * + * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING + * - insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte) + * - xor byte with ST21NFCA_BYTE_STUFFING_MASK + */ +#define ST21NFCA_SOF_EOF 0x7e +#define ST21NFCA_BYTE_STUFFING_MASK 0x20 +#define ST21NFCA_ESCAPE_BYTE_STUFFING 0x7d + +/* SOF + 00 fill size */ +#define ST21NFCA_FRAME_HEADROOM 2 + +/* 4 bytes crc (worst case byte stuffing) + EOF */ +#define ST21NFCA_FRAME_TAILROOM 5 + +#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c" + +static struct i2c_device_id st21nfca_hci_i2c_id_table[] = { + {ST21NFCA_HCI_DRIVER_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); + +struct st21nfca_i2c_phy { + struct i2c_client *i2c_dev; + struct nfc_hci_dev *hdev; + + unsigned int gpio_ena; + unsigned int gpio_irq; + unsigned int irq_polarity; + + struct sk_buff *pending_skb; + int current_read_len; + /* + * crc might have fail because i2c macro + * is disable due to other interface activity + */ + int crc_trials; + + int powered; + int run_mode; + + /* + * < 0 if hardware error occured (e.g. i2c err) + * and prevents normal operation. + */ + int hard_fault; +}; +static u8 len_seq[] = { 13, 24, 15, 29 }; +static u16 wait_tab[] = { 2, 3, 5, 15, 20, 40}; + +#define I2C_DUMP_SKB(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \ + 16, 1, (skb)->data, (skb)->len, 0); \ +} while (0) + +static void st21nfca_hci_platform_init(struct st21nfca_i2c_phy *phy) +{ + u16 wait_tab[] = { 50, 300, 1000 }; + char reboot_cmd[] = { 0x7E, 0x66, 0x48, 0xF6, 0x7E }; + u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE]; + int i, r = -1; + + for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) + r = i2c_master_recv(phy->i2c_dev, tmp, + ST21NFCA_HCI_LLC_MAX_SIZE); + + r = -1; + for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) + r = i2c_master_send(phy->i2c_dev, reboot_cmd, + sizeof(reboot_cmd)); + usleep_range(1000, 1500); + +} + +static int st21nfca_hci_i2c_enable(void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + + gpio_set_value(phy->gpio_ena, 1); + phy->powered = 1; + phy->run_mode = ST21NFCA_HCI_MODE; + + usleep_range(10000, 15000); + + return 0; +} + +static void st21nfca_hci_i2c_disable(void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + + pr_info("\n"); + gpio_set_value(phy->gpio_ena, 0); + + phy->powered = 0; +} + +static int st21nfca_hci_add_len_crc(struct sk_buff *skb) +{ + int ret = 2; + u16 crc; + u8 tmp; + + *skb_push(skb, 1) = 0; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + + tmp = crc & 0x00ff; + *skb_put(skb, 1) = tmp; + + tmp = (crc >> 8) & 0x00ff; + *skb_put(skb, 1) = tmp; + + return ret; +} + +static void st21nfca_hci_remove_len_crc(struct sk_buff *skb, int crc_len) +{ + skb_pull(skb, ST21NFCA_FRAME_HEADROOM); + skb_trim(skb, crc_len); +} + +/* + * Writing a frame must not return the number of written bytes. + * It must return either zero for success, or <0 for error. + * In addition, it must not alter the skb + */ +static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r = -1, i, j, len; + struct st21nfca_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + u16 wait_tab[] = { 2, 3, 5, 15, 20, 40}; + u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE * 2]; + + I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb); + + + if (phy->hard_fault != 0) + return phy->hard_fault; + + /* + * Compute CRC before byte stuffing computation on frame + * Note st21nfca_hci_add_len_crc is doing a byte stuffing + * on its own value + */ + len = st21nfca_hci_add_len_crc(skb); + + /* add ST21NFCA_SOF_EOF on tail */ + *skb_put(skb, 1) = ST21NFCA_SOF_EOF; + /* add ST21NFCA_SOF_EOF on head */ + *skb_push(skb, 1) = ST21NFCA_SOF_EOF; + + /* + * Compute byte stuffing + * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING + * insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte) + * xor byte with ST21NFCA_BYTE_STUFFING_MASK + */ + tmp[0] = skb->data[0]; + for (i = 1, j = 1; i < skb->len - 1; i++, j++) { + if (skb->data[i] == ST21NFCA_SOF_EOF + || skb->data[i] == ST21NFCA_ESCAPE_BYTE_STUFFING) { + tmp[j] = ST21NFCA_ESCAPE_BYTE_STUFFING; + j++; + tmp[j] = skb->data[i] ^ ST21NFCA_BYTE_STUFFING_MASK; + } else { + tmp[j] = skb->data[i]; + } + } + tmp[j] = skb->data[i]; + j++; + + /* + * Manage sleep mode + * Try 3 times to send data with delay between each + */ + for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) { + r = i2c_master_send(client, tmp, j); + if (r < 0) + msleep(wait_tab[i]); + } + + if (r >= 0) { + if (r != j) + r = -EREMOTEIO; + else + r = 0; + } + + st21nfca_hci_remove_len_crc(skb, len); + + return r; +} + +static int get_frame_size(u8 *buf, int buflen) +{ + int len = 0; + if (buf[len + 1] == ST21NFCA_SOF_EOF) + return 0; + + for (len = 1; len < buflen && buf[len] != ST21NFCA_SOF_EOF; len++) + ; + + return len; +} + +static int check_crc(u8 *buf, int buflen) +{ + u16 crc; + + crc = crc_ccitt(0xffff, buf, buflen - 2); + crc = ~crc; + + if (buf[buflen - 2] != (crc & 0xff) || buf[buflen - 1] != (crc >> 8)) { + pr_err(ST21NFCA_HCI_DRIVER_NAME + ": CRC error 0x%x != 0x%x 0x%x\n", crc, buf[buflen - 1], + buf[buflen - 2]); + + pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__); + print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, + 16, 2, buf, buflen, false); + return -EPERM; + } + return 0; +} + +/* + * Prepare received data for upper layer. + * Received data include byte stuffing, crc and sof/eof + * which is not usable by hci part. + * returns: + * frame size without sof/eof, header and byte stuffing + * -EBADMSG : frame was incorrect and discarded + */ +static int st21nfca_hci_i2c_repack(struct sk_buff *skb) +{ + int i, j, r, size; + if (skb->len < 1 || (skb->len > 1 && skb->data[1] != 0)) + return -EBADMSG; + + size = get_frame_size(skb->data, skb->len); + if (size > 0) { + skb_trim(skb, size); + /* remove ST21NFCA byte stuffing for upper layer */ + for (i = 1, j = 0; i < skb->len; i++) { + if (skb->data[i] == + (u8) ST21NFCA_ESCAPE_BYTE_STUFFING) { + skb->data[i] = + skb->data[i + + 1] | ST21NFCA_BYTE_STUFFING_MASK; + i++; + j++; + } + skb->data[i] = skb->data[i + j]; + } + /* remove byte stuffing useless byte */ + skb_trim(skb, i - j); + /* remove ST21NFCA_SOF_EOF from head */ + skb_pull(skb, 1); + + r = check_crc(skb->data, skb->len); + if (r != 0) { + i = 0; + return -EBADMSG; + } + + /* remove headbyte */ + skb_pull(skb, 1); + /* remove crc. Byte Stuffing is already removed here */ + skb_trim(skb, skb->len - 2); + return skb->len; + } + return 0; +} + +/* + * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees + * that i2c bus will be flushed and that next read will start on a new frame. + * returned skb contains only LLC header and payload. + * returns: + * frame size : if received frame is complete (find ST21NFCA_SOF_EOF at + * end of read) + * -EAGAIN : if received frame is incomplete (not find ST21NFCA_SOF_EOF + * at end of read) + * -EREMOTEIO : i2c read error (fatal) + * -EBADMSG : frame was incorrect and discarded + * (value returned from st21nfca_hci_i2c_repack) + * -EIO : if no ST21NFCA_SOF_EOF is found after reaching + * the read length end sequence + */ +static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy, + struct sk_buff *skb) +{ + int r, i; + u8 len; + struct i2c_client *client = phy->i2c_dev; + + if (phy->current_read_len < ARRAY_SIZE(len_seq)) { + len = len_seq[phy->current_read_len]; + + /* + * Add retry mecanism + * Operation on I2C interface may fail in case of operation on + * RF or SWP interface + */ + r = 0; + for (i = 0; i < ARRAY_SIZE(wait_tab) && r <= 0; i++) { + r = i2c_master_recv(client, skb_put(skb, len), len); + if (r < 0) + msleep(wait_tab[i]); + } + + if (r != len) { + phy->current_read_len = 0; + return -EREMOTEIO; + } + + if (memchr(skb->data + 2, ST21NFCA_SOF_EOF, + skb->len - 2) != NULL) { + phy->current_read_len = 0; + return st21nfca_hci_i2c_repack(skb); + } + phy->current_read_len++; + return -EAGAIN; + } + return -EIO; +} + +/* + * Reads an shdlc frame from the chip. This is not as straightforward as it + * seems. The frame format is data-crc, and corruption can occur anywhere + * while transiting on i2c bus, such that we could read an invalid data. + * The tricky case is when we read a corrupted data or crc. We must detect + * this here in order to determine that data can be transmitted to the hci + * core. This is the reason why we check the crc here. + * The CLF will repeat a frame until we send a RR on that frame. + * + * On ST21NFCA, IRQ goes in idle when read starts. As no size information are + * available in the incoming data, other IRQ might come. Every IRQ will trigger + * a read sequence with different length and will fill the current frame. + * The reception is complete once we reach a ST21NFCA_SOF_EOF. + */ +static irqreturn_t st21nfca_hci_irq_thread_fn(int irq, void *phy_id) +{ + struct st21nfca_i2c_phy *phy = phy_id; + struct i2c_client *client; + + int r; + + if (!phy || irq != phy->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + + client = phy->i2c_dev; + dev_dbg(&client->dev, "IRQ\n"); + + if (phy->hard_fault != 0) + return IRQ_HANDLED; + + r = st21nfca_hci_i2c_read(phy, phy->pending_skb); + if (r == -EREMOTEIO) { + phy->hard_fault = r; + + nfc_hci_recv_frame(phy->hdev, NULL); + + return IRQ_HANDLED; + } else if (r == -EAGAIN || r == -EIO) { + return IRQ_HANDLED; + } else if (r == -EBADMSG && phy->crc_trials < ARRAY_SIZE(wait_tab)) { + /* + * With ST21NFCA, only one interface (I2C, RF or SWP) + * may be active at a time. + * Having incorrect crc is usually due to i2c macrocell + * deactivation in the middle of a transmission. + * It may generate corrupted data on i2c. + * We give sometime to get i2c back. + * The complete frame will be repeated. + */ + msleep(wait_tab[phy->crc_trials]); + phy->crc_trials++; + phy->current_read_len = 0; + } else if (r > 0) { + /* + * We succeeded to read data from the CLF and + * data is valid. + * Reset counter. + */ + nfc_hci_recv_frame(phy->hdev, phy->pending_skb); + phy->crc_trials = 0; + } + + phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL); + if (phy->pending_skb == NULL) { + phy->hard_fault = -ENOMEM; + nfc_hci_recv_frame(phy->hdev, NULL); + } + + return IRQ_HANDLED; +} + +static struct nfc_phy_ops i2c_phy_ops = { + .write = st21nfca_hci_i2c_write, + .enable = st21nfca_hci_i2c_enable, + .disable = st21nfca_hci_i2c_disable, +}; + +static int st21nfca_request_resources(struct st21nfca_i2c_phy *phy, + struct i2c_client *client) +{ + struct st21nfca_nfc_platform_data *pdata; + int r; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + /* store for later use */ + phy->gpio_irq = pdata->gpio_irq; + phy->gpio_ena = pdata->gpio_ena; + phy->irq_polarity = pdata->irq_polarity; + phy->i2c_dev = client; + + r = devm_gpio_request(&client->dev, phy->gpio_irq, "wake_up"); + if (r) { + pr_err("%s : gpio_request failed\n", __FILE__); + return -ENODEV; + } + + r = gpio_direction_input(phy->gpio_irq); + if (r) { + pr_err("%s : gpio_direction_input failed\n", __FILE__); + return -ENODEV; + } + + if (phy->gpio_ena != 0) { + r = devm_gpio_request(&client->dev, + phy->gpio_ena, "clf_enable"); + if (r) { + pr_err("%s : ena gpio_request failed\n", __FILE__); + return -ENODEV; + } + r = gpio_direction_output(phy->gpio_ena, 1); + + if (r) { + pr_err("%s : ena gpio_direction_output failed\n", + __FILE__); + return -ENODEV; + } + } + + phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL); + if (phy->pending_skb == NULL) + return -ENOMEM; + + phy->current_read_len = 0; + phy->crc_trials = 0; + return r; +} + +static int st21nfca_hci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct st21nfca_i2c_phy *phy; + struct st21nfca_nfc_platform_data *pdata; + int r = 0; + + dev_dbg(&client->dev, "%s\n", __func__); + dev_dbg(&client->dev, "IRQ: %d\n", client->irq); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); + return -ENODEV; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy), + GFP_KERNEL); + if (!phy) { + nfc_err(&client->dev, + "Cannot allocate memory for st21nfca i2c phy.\n"); + return -ENOMEM; + } + + phy->i2c_dev = client; + + i2c_set_clientdata(client, phy); + + pdata = client->dev.platform_data; + if (pdata == NULL) { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + r = st21nfca_request_resources(phy, client); + if (r) { + nfc_err(&client->dev, "Cannot get platform resources\n"); + return r; + } + + st21nfca_hci_platform_init(phy); + r = devm_request_threaded_irq(&client->dev, client->irq, NULL, + st21nfca_hci_irq_thread_fn, + phy->irq_polarity | IRQF_ONESHOT, + ST21NFCA_HCI_DRIVER_NAME, phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + return r; + } + + r = st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, + ST21NFCA_FRAME_HEADROOM, ST21NFCA_FRAME_TAILROOM, + ST21NFCA_HCI_LLC_MAX_PAYLOAD, &phy->hdev); + + if (r < 0) + return r; + + return 0; +} + +static int st21nfca_hci_i2c_remove(struct i2c_client *client) +{ + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __func__); + + st21nfca_hci_remove(phy->hdev); + + if (phy->powered) + st21nfca_hci_i2c_disable(phy); + + return 0; +} + +static struct i2c_driver st21nfca_hci_i2c_driver = { + .driver = { + .name = ST21NFCA_HCI_I2C_DRIVER_NAME, + }, + .probe = st21nfca_hci_i2c_probe, + .id_table = st21nfca_hci_i2c_id_table, + .remove = st21nfca_hci_i2c_remove, +}; + +module_i2c_driver(st21nfca_hci_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c new file mode 100644 index 000000000000..69213f37b7ba --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca.c @@ -0,0 +1,506 @@ +/* + * HCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "st21nfca.h" +#include + +#define DRIVER_DESC "HCI NFC driver for ST21NFCA" + +#define FULL_VERSION_LEN 3 + +/* Proprietary gates, events, commands and registers */ + +/* Commands that apply to all RF readers */ +#define ST21NFCA_RF_READER_CMD_PRESENCE_CHECK 0x30 + +#define ST21NFCA_RF_READER_ISO15693_GATE 0x12 + +/* + * Reader gate for communication with contact-less cards using Type A + * protocol ISO14443-3 but not compliant with ISO14443-4 + */ +#define ST21NFCA_RF_READER_14443_3_A_GATE 0x15 +#define ST21NFCA_RF_READER_14443_3_A_UID 0x02 +#define ST21NFCA_RF_READER_14443_3_A_ATQA 0x03 +#define ST21NFCA_RF_READER_14443_3_A_SAK 0x04 + +#define ST21NFCA_DEVICE_MGNT_GATE 0x01 +#define ST21NFCA_DEVICE_MGNT_PIPE 0x02 +#define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/ + + +static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES); + +static struct nfc_hci_gate st21nfca_gates[] = { + {NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE}, + {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE}, + {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE}, +}; +/* Largest headroom needed for outgoing custom commands */ +#define ST21NFCA_CMDS_HEADROOM 7 + +static int st21nfca_hci_open(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + int r; + + mutex_lock(&info->info_lock); + + if (info->state != ST21NFCA_ST_COLD) { + r = -EBUSY; + goto out; + } + + r = info->phy_ops->enable(info->phy_id); + + if (r == 0) + info->state = ST21NFCA_ST_READY; + +out: + mutex_unlock(&info->info_lock); + return r; +} + +static void st21nfca_hci_close(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + mutex_lock(&info->info_lock); + + if (info->state == ST21NFCA_ST_COLD) + goto out; + + info->phy_ops->disable(info->phy_id); + info->state = ST21NFCA_ST_COLD; + +out: + mutex_unlock(&info->info_lock); +} + +static int st21nfca_hci_ready(struct nfc_hci_dev *hdev) +{ + struct sk_buff *skb; + + u8 param; + int r; + + param = NFC_HCI_UICC_HOST_ID; + r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_WHITELIST, ¶m, 1); + if (r < 0) + return r; + + /* Set NFC_MODE in device management gate to enable */ + r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_NFC_MODE, &skb); + if (r < 0) + return r; + + if (skb->data[0] == 0) { + kfree_skb(skb); + param = 1; + + r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_NFC_MODE, ¶m, 1); + if (r < 0) + return r; + } + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + + r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, + NFC_HCI_ID_MGMT_VERSION_SW, &skb); + if (r < 0) + return r; + + if (skb->len != FULL_VERSION_LEN) { + kfree_skb(skb); + return -EINVAL; + } + + print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ", + DUMP_PREFIX_NONE, 16, 1, + skb->data, FULL_VERSION_LEN, false); + + kfree_skb(skb); + + return 0; +} + +static int st21nfca_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + return info->phy_ops->write(info->phy_id, skb); +} + +static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols) +{ + int r; + + pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n", + __func__, im_protocols, tm_protocols); + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (r < 0) + return r; + if (im_protocols) { + /* + * enable polling according to im_protocols & tm_protocols + * - CLOSE pipe according to im_protocols & tm_protocols + */ + if ((NFC_HCI_RF_READER_B_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + NFC_HCI_RF_READER_B_GATE); + if (r < 0) + return r; + } + + if ((NFC_HCI_RF_READER_A_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + NFC_HCI_RF_READER_A_GATE); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_F_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_F_GATE); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_14443_3_A_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_14443_3_A_GATE); + if (r < 0) + return r; + } + + if ((ST21NFCA_RF_READER_ISO15693_GATE & im_protocols) == 0) { + r = nfc_hci_disconnect_gate(hdev, + ST21NFCA_RF_READER_ISO15693_GATE); + if (r < 0) + return r; + } + + r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_READER_REQUESTED, NULL, 0); + if (r < 0) + nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); + } + return r; +} + +static int st21nfca_get_iso14443_3_atqa(struct nfc_hci_dev *hdev, u16 *atqa) +{ + int r; + struct sk_buff *atqa_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_ATQA, &atqa_skb); + if (r < 0) + goto exit; + + if (atqa_skb->len != 2) { + r = -EPROTO; + goto exit; + } + + *atqa = be16_to_cpu(*(u16 *) atqa_skb->data); + +exit: + kfree_skb(atqa_skb); + return r; +} + +static int st21nfca_get_iso14443_3_sak(struct nfc_hci_dev *hdev, u8 *sak) +{ + int r; + struct sk_buff *sak_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_SAK, &sak_skb); + if (r < 0) + goto exit; + + if (sak_skb->len != 1) { + r = -EPROTO; + goto exit; + } + + *sak = sak_skb->data[0]; + +exit: + kfree_skb(sak_skb); + return r; +} + +static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate, + int *len) +{ + int r; + struct sk_buff *uid_skb = NULL; + + r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE, + ST21NFCA_RF_READER_14443_3_A_UID, &uid_skb); + if (r < 0) + goto exit; + + if (uid_skb->len == 0 || uid_skb->len > NFC_NFCID1_MAXSIZE) { + r = -EPROTO; + goto exit; + } + + gate = uid_skb->data; + *len = uid_skb->len; +exit: + kfree_skb(uid_skb); + return r; +} + +static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, + struct nfc_target *target) +{ + int r, len; + u16 atqa; + u8 sak; + u8 uid[NFC_NFCID1_MAXSIZE]; + + switch (gate) { + case ST21NFCA_RF_READER_F_GATE: + target->supported_protocols = NFC_PROTO_FELICA_MASK; + break; + case ST21NFCA_RF_READER_14443_3_A_GATE: + /* ISO14443-3 type 1 or 2 tags */ + r = st21nfca_get_iso14443_3_atqa(hdev, &atqa); + if (r < 0) + return r; + if (atqa == 0x000c) { + target->supported_protocols = NFC_PROTO_JEWEL_MASK; + target->sens_res = 0x0c00; + } else { + r = st21nfca_get_iso14443_3_sak(hdev, &sak); + if (r < 0) + return r; + + r = st21nfca_get_iso14443_3_uid(hdev, uid, &len); + if (r < 0) + return r; + + target->supported_protocols = + nfc_hci_sak_to_protocol(sak); + if (target->supported_protocols == 0xffffffff) + return -EPROTO; + + target->sens_res = atqa; + target->sel_res = sak; + memcpy(target->nfcid1, uid, len); + target->nfcid1_len = len; + } + + break; + default: + return -EPROTO; + } + + return 0; +} + +/* + * Returns: + * <= 0: driver handled the data exchange + * 1: driver doesn't especially handle, please do standard processing + */ +static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev, + struct nfc_target *target, + struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) +{ + pr_info(DRIVER_DESC ": %s for gate=%d len=%d\n", __func__, + target->hci_reader_gate, skb->len); + + switch (target->hci_reader_gate) { + case ST21NFCA_RF_READER_F_GATE: + *skb_push(skb, 1) = 0x1a; + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, cb, cb_context); + case ST21NFCA_RF_READER_14443_3_A_GATE: + *skb_push(skb, 1) = 0x1a; /* CTR, see spec:10.2.2.1 */ + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, skb->data, + skb->len, cb, cb_context); + default: + return 1; + } +} + +static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev, + struct nfc_target *target) +{ + u8 fwi = 0x11; + switch (target->hci_reader_gate) { + case NFC_HCI_RF_READER_A_GATE: + case NFC_HCI_RF_READER_B_GATE: + /* + * PRESENCE_CHECK on those gates is available + * However, the answer to this command is taking 3 * fwi + * if the card is no present. + * Instead, we send an empty I-Frame with a very short + * configurable fwi ~604µs. + */ + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + ST21NFCA_WR_XCHG_DATA, &fwi, 1, NULL); + case ST21NFCA_RF_READER_14443_3_A_GATE: + return nfc_hci_send_cmd(hdev, target->hci_reader_gate, + ST21NFCA_RF_READER_CMD_PRESENCE_CHECK, + NULL, 0, NULL); + default: + return -EOPNOTSUPP; + } +} + +static struct nfc_hci_ops st21nfca_hci_ops = { + .open = st21nfca_hci_open, + .close = st21nfca_hci_close, + .hci_ready = st21nfca_hci_ready, + .xmit = st21nfca_hci_xmit, + .start_poll = st21nfca_hci_start_poll, + .target_from_gate = st21nfca_hci_target_from_gate, + .im_transceive = st21nfca_hci_im_transceive, + .check_presence = st21nfca_hci_check_presence, +}; + +int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, + char *llc_name, int phy_headroom, int phy_tailroom, + int phy_payload, struct nfc_hci_dev **hdev) +{ + struct st21nfca_hci_info *info; + int r = 0; + int dev_num; + u32 protocols; + struct nfc_hci_init_data init_data; + unsigned long quirks = 0; + + info = kzalloc(sizeof(struct st21nfca_hci_info), GFP_KERNEL); + if (!info) { + r = -ENOMEM; + goto err_alloc_hdev; + } + + info->phy_ops = phy_ops; + info->phy_id = phy_id; + info->state = ST21NFCA_ST_COLD; + mutex_init(&info->info_lock); + + init_data.gate_count = ARRAY_SIZE(st21nfca_gates); + + memcpy(init_data.gates, st21nfca_gates, sizeof(st21nfca_gates)); + + /* + * Session id must include the driver name + i2c bus addr + * persistent info to discriminate 2 identical chips + */ + dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES); + if (dev_num >= ST21NFCA_NUM_DEVICES) + goto err_alloc_hdev; + + scnprintf(init_data.session_id, sizeof(init_data.session_id), "%s%2x", + "ST21AH", dev_num); + + protocols = NFC_PROTO_JEWEL_MASK | + NFC_PROTO_MIFARE_MASK | + NFC_PROTO_FELICA_MASK | + NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK; + + set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks); + + info->hdev = + nfc_hci_allocate_device(&st21nfca_hci_ops, &init_data, quirks, + protocols, llc_name, + phy_headroom + ST21NFCA_CMDS_HEADROOM, + phy_tailroom, phy_payload); + + if (!info->hdev) { + pr_err("Cannot allocate nfc hdev.\n"); + r = -ENOMEM; + goto err_alloc_hdev; + } + + nfc_hci_set_clientdata(info->hdev, info); + + r = nfc_hci_register_device(info->hdev); + if (r) + goto err_regdev; + + *hdev = info->hdev; + + return 0; + +err_regdev: + nfc_hci_free_device(info->hdev); + +err_alloc_hdev: + kfree(info); + + return r; +} +EXPORT_SYMBOL(st21nfca_hci_probe); + +void st21nfca_hci_remove(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + nfc_hci_unregister_device(hdev); + nfc_hci_free_device(hdev); + kfree(info); +} +EXPORT_SYMBOL(st21nfca_hci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h new file mode 100644 index 000000000000..334cd90bcc8c --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef __LOCAL_ST21NFCA_H_ +#define __LOCAL_ST21NFCA_H_ + +#include + +#define HCI_MODE 0 + +/* framing in HCI mode */ +#define ST21NFCA_SOF_EOF_LEN 2 + +/* Almost every time value is 0 */ +#define ST21NFCA_HCI_LLC_LEN 1 + +/* Size in worst case : + * In normal case CRC len = 2 but byte stuffing + * may appear in case one CRC byte = ST21NFCA_SOF_EOF + */ +#define ST21NFCA_HCI_LLC_CRC 4 + +#define ST21NFCA_HCI_LLC_LEN_CRC (ST21NFCA_SOF_EOF_LEN + \ + ST21NFCA_HCI_LLC_LEN + \ + ST21NFCA_HCI_LLC_CRC) +#define ST21NFCA_HCI_LLC_MIN_SIZE (1 + ST21NFCA_HCI_LLC_LEN_CRC) + +/* Worst case when adding byte stuffing between each byte */ +#define ST21NFCA_HCI_LLC_MAX_PAYLOAD 29 +#define ST21NFCA_HCI_LLC_MAX_SIZE (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \ + ST21NFCA_HCI_LLC_MAX_PAYLOAD) + +#define DRIVER_DESC "HCI NFC driver for ST21NFCA" + +#define ST21NFCA_HCI_MODE 0 + +#define ST21NFCA_NUM_DEVICES 256 + +int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, + char *llc_name, int phy_headroom, int phy_tailroom, + int phy_payload, struct nfc_hci_dev **hdev); +void st21nfca_hci_remove(struct nfc_hci_dev *hdev); + +enum st21nfca_state { + ST21NFCA_ST_COLD, + ST21NFCA_ST_READY, +}; + +struct st21nfca_hci_info { + struct nfc_phy_ops *phy_ops; + void *phy_id; + + struct nfc_hci_dev *hdev; + + enum st21nfca_state state; + + struct mutex info_lock; + + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; + +} __packed; + +/* Reader RF commands */ +#define ST21NFCA_WR_XCHG_DATA 0x10 + +#define ST21NFCA_RF_READER_F_GATE 0x14 +#define ST21NFCA_RF_READER_F_DATARATE 0x01 +#define ST21NFCA_RF_READER_F_DATARATE_106 0x01 +#define ST21NFCA_RF_READER_F_DATARATE_212 0x02 +#define ST21NFCA_RF_READER_F_DATARATE_424 0x04 + +#endif /* __LOCAL_ST21NFCA_H_ */ diff --git a/include/linux/platform_data/st21nfca.h b/include/linux/platform_data/st21nfca.h new file mode 100644 index 000000000000..1730312398ff --- /dev/null +++ b/include/linux/platform_data/st21nfca.h @@ -0,0 +1,32 @@ +/* + * Driver include for the ST21NFCA NFC chip. + * + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _ST21NFCA_HCI_H_ +#define _ST21NFCA_HCI_H_ + +#include + +#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci" + +struct st21nfca_nfc_platform_data { + unsigned int gpio_irq; + unsigned int gpio_ena; + unsigned int irq_polarity; +}; + +#endif /* _ST21NFCA_HCI_H_ */ -- cgit v1.2.3 From e240bc36125691b0e18e70407c2d18ca6117c2f5 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 25 Mar 2014 06:51:49 +0100 Subject: NFC: hci: Add load_session HCI operand load_session allows a CLF to restore the gate <-> pipe table from some proprietary location. The main advantage to add this function is to reduce the memory wear by running pipe creation (and storing) only once. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 1 + net/nfc/hci/core.c | 45 +++++++++++++++++++++------------------------ 2 files changed, 22 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 03c4650b548c..61286db54388 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -27,6 +27,7 @@ struct nfc_hci_dev; struct nfc_hci_ops { int (*open) (struct nfc_hci_dev *hdev); void (*close) (struct nfc_hci_dev *hdev); + int (*load_session) (struct nfc_hci_dev *hdev); int (*hci_ready) (struct nfc_hci_dev *hdev); /* * xmit must always send the complete buffer before diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index d45b638e77c7..c4d251a6fd67 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -380,34 +380,31 @@ static int hci_dev_session_init(struct nfc_hci_dev *hdev) if (r < 0) goto disconnect_all; - if (skb->len && skb->len == strlen(hdev->init_data.session_id)) - if (memcmp(hdev->init_data.session_id, skb->data, - skb->len) == 0) { - /* TODO ELa: restore gate<->pipe table from - * some TBD location. - * note: it doesn't seem possible to get the chip - * currently open gate/pipe table. - * It is only possible to obtain the supported - * gate list. - */ + if (skb->len && skb->len == strlen(hdev->init_data.session_id) && + (memcmp(hdev->init_data.session_id, skb->data, + skb->len) == 0) && hdev->ops->load_session) { + /* Restore gate<->pipe table from some proprietary location. */ - /* goto exit - * For now, always do a full initialization */ - } + r = hdev->ops->load_session(hdev); - r = nfc_hci_disconnect_all_gates(hdev); - if (r < 0) - goto exit; + if (r < 0) + goto disconnect_all; + } else { - r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count, - hdev->init_data.gates); - if (r < 0) - goto disconnect_all; + r = nfc_hci_disconnect_all_gates(hdev); + if (r < 0) + goto exit; - r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, - NFC_HCI_ADMIN_SESSION_IDENTITY, - hdev->init_data.session_id, - strlen(hdev->init_data.session_id)); + r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count, + hdev->init_data.gates); + if (r < 0) + goto disconnect_all; + + r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_SESSION_IDENTITY, + hdev->init_data.session_id, + strlen(hdev->init_data.session_id)); + } if (r == 0) goto exit; -- cgit v1.2.3 From 51d98fa47c9c3f5d34cd4097ce08e8e8669a89b4 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Mon, 31 Mar 2014 17:36:37 -0700 Subject: NFC: digital: Add macros for the ISO/IEC 14443-B Protocol Add RF tech and framing macros for the ISO/IEC 14443-B Protocol. Cc: Thierry Escande Signed-off-by: Mark A. Greer Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 7655cfe27c34..bdf55c3b7a19 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -36,6 +36,7 @@ enum { NFC_DIGITAL_RF_TECH_212F, NFC_DIGITAL_RF_TECH_424F, NFC_DIGITAL_RF_TECH_ISO15693, + NFC_DIGITAL_RF_TECH_106B, NFC_DIGITAL_RF_TECH_LAST, }; @@ -62,6 +63,9 @@ enum { NFC_DIGITAL_FRAMING_ISO15693_INVENTORY, NFC_DIGITAL_FRAMING_ISO15693_T5T, + NFC_DIGITAL_FRAMING_NFCB, + NFC_DIGITAL_FRAMING_NFCB_T4T, + NFC_DIGITAL_FRAMING_LAST, }; -- cgit v1.2.3 From e6853aafd4339dbf2992957ff2616ef7164bc9d4 Mon Sep 17 00:00:00 2001 From: David Ung Date: Wed, 26 Mar 2014 15:35:37 -0700 Subject: video: Check EDID for HDMI connection Check EDID Vendor Specific Data Block bytes to see if the connection is HDMI and set FB_MISC_HDMI. Signed-off-by: David Ung Signed-off-by: Christopher Freeman Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/core/fbmon.c | 9 ++++++++- include/linux/fb.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index c204ebe6187e..5b0e313849bd 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -1012,13 +1012,20 @@ void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) while (pos < edid[2]) { u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7; pr_debug("Data block %u of %u bytes\n", type, len); - if (type == 2) + if (type == 2) { for (i = pos; i < pos + len; i++) { u8 idx = edid[pos + i] & 0x7f; svd[svd_n++] = idx; pr_debug("N%sative mode #%d\n", edid[pos + i] & 0x80 ? "" : "on-n", idx); } + } else if (type == 3 && len >= 3) { + /* Check Vendor Specific Data Block. For HDMI, + it is always 00-0C-03 for HDMI Licensing, LLC. */ + if (edid[pos + 1] == 3 && edid[pos + 2] == 0xc && + edid[pos + 3] == 0) + specs->misc |= FB_MISC_HDMI; + } pos += len + 1; } diff --git a/include/linux/fb.h b/include/linux/fb.h index fe6ac956550e..506242979eea 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -47,6 +47,7 @@ struct device_node; #define FB_MISC_PRIM_COLOR 1 #define FB_MISC_1ST_DETAIL 2 /* First Detailed Timing is preferred */ +#define FB_MISC_HDMI 4 struct fb_chroma { __u32 redx; /* in fraction of 1024 */ __u32 greenx; -- cgit v1.2.3 From f1370cc4a01e61007ab3020c761cef6b88ae3729 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 18 Apr 2014 16:23:46 +0900 Subject: xfrm: Remove useless secid field from xfrm_audit. It seems to me that commit ab5f5e8b "[XFRM]: xfrm audit calls" is doing something strange at xfrm_audit_helper_usrinfo(). If secid != 0 && security_secid_to_secctx(secid) != 0, the caller calls audit_log_task_context() which basically does secid != 0 && security_secid_to_secctx(secid) == 0 case except that secid is obtained from current thread's context. Oh, what happens if secid passed to xfrm_audit_helper_usrinfo() was obtained from other thread's context? It might audit current thread's context rather than other thread's context if security_secid_to_secctx() in xfrm_audit_helper_usrinfo() failed for some reason. Then, are all the caller of xfrm_audit_helper_usrinfo() passing either secid obtained from current thread's context or secid == 0? It seems to me that they are. If I didn't miss something, we don't need to pass secid to xfrm_audit_helper_usrinfo() because audit_log_task_context() will obtain secid from current thread's context. Signed-off-by: Tetsuo Handa Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 29 ++++++++++------------------- net/key/af_key.c | 12 +++++------- net/xfrm/xfrm_policy.c | 22 ++++++++-------------- net/xfrm/xfrm_state.c | 17 +++++++---------- net/xfrm/xfrm_user.c | 27 ++++++--------------------- 5 files changed, 36 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 116e9c7e19cb..882889eb156b 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -693,7 +693,6 @@ struct xfrm_spi_skb_cb { /* Audit Information */ struct xfrm_audit { - u32 secid; kuid_t loginuid; unsigned int sessionid; }; @@ -713,30 +712,22 @@ static inline struct audit_buffer *xfrm_audit_start(const char *op) return audit_buf; } -static inline void xfrm_audit_helper_usrinfo(kuid_t auid, unsigned int ses, u32 secid, +static inline void xfrm_audit_helper_usrinfo(kuid_t auid, unsigned int ses, struct audit_buffer *audit_buf) { - char *secctx; - u32 secctx_len; - audit_log_format(audit_buf, " auid=%u ses=%u", from_kuid(&init_user_ns, auid), ses); - if (secid != 0 && - security_secid_to_secctx(secid, &secctx, &secctx_len) == 0) { - audit_log_format(audit_buf, " subj=%s", secctx); - security_release_secctx(secctx, secctx_len); - } else - audit_log_task_context(audit_buf); + audit_log_task_context(audit_buf); } void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, kuid_t auid, - unsigned int ses, u32 secid); + unsigned int ses); void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, kuid_t auid, - unsigned int ses, u32 secid); + unsigned int ses); void xfrm_audit_state_add(struct xfrm_state *x, int result, kuid_t auid, - unsigned int ses, u32 secid); + unsigned int ses); void xfrm_audit_state_delete(struct xfrm_state *x, int result, kuid_t auid, - unsigned int ses, u32 secid); + unsigned int ses); void xfrm_audit_state_replay_overflow(struct xfrm_state *x, struct sk_buff *skb); void xfrm_audit_state_replay(struct xfrm_state *x, struct sk_buff *skb, @@ -749,22 +740,22 @@ void xfrm_audit_state_icvfail(struct xfrm_state *x, struct sk_buff *skb, #else static inline void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int ses, u32 secid) + kuid_t auid, unsigned int ses) { } static inline void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int ses, u32 secid) + kuid_t auid, unsigned int ses) { } static inline void xfrm_audit_state_add(struct xfrm_state *x, int result, - kuid_t auid, unsigned int ses, u32 secid) + kuid_t auid, unsigned int ses) { } static inline void xfrm_audit_state_delete(struct xfrm_state *x, int result, - kuid_t auid, unsigned int ses, u32 secid) + kuid_t auid, unsigned int ses) { } diff --git a/net/key/af_key.c b/net/key/af_key.c index f3c83073afc4..d66ff72adefb 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1478,7 +1478,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg xfrm_audit_state_add(x, err ? 0 : 1, audit_get_loginuid(current), - audit_get_sessionid(current), 0); + audit_get_sessionid(current)); if (err < 0) { x->km.state = XFRM_STATE_DEAD; @@ -1534,7 +1534,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_ out: xfrm_audit_state_delete(x, err ? 0 : 1, audit_get_loginuid(current), - audit_get_sessionid(current), 0); + audit_get_sessionid(current)); xfrm_state_put(x); return err; @@ -1735,7 +1735,6 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m audit_info.loginuid = audit_get_loginuid(current); audit_info.sessionid = audit_get_sessionid(current); - audit_info.secid = 0; err = xfrm_state_flush(net, proto, &audit_info); err2 = unicast_flush_resp(sk, hdr); if (err || err2) { @@ -2290,7 +2289,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_ xfrm_audit_policy_add(xp, err ? 0 : 1, audit_get_loginuid(current), - audit_get_sessionid(current), 0); + audit_get_sessionid(current)); if (err) goto out; @@ -2374,7 +2373,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa xfrm_audit_policy_delete(xp, err ? 0 : 1, audit_get_loginuid(current), - audit_get_sessionid(current), 0); + audit_get_sessionid(current)); if (err) goto out; @@ -2624,7 +2623,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_ if (delete) { xfrm_audit_policy_delete(xp, err ? 0 : 1, audit_get_loginuid(current), - audit_get_sessionid(current), 0); + audit_get_sessionid(current)); if (err) goto out; @@ -2738,7 +2737,6 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sad audit_info.loginuid = audit_get_loginuid(current); audit_info.sessionid = audit_get_sessionid(current); - audit_info.secid = 0; err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); err2 = unicast_flush_resp(sk, hdr); if (err || err2) { diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c08fbd11ceff..bd001b7062c0 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -785,8 +785,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi if (err) { xfrm_audit_policy_delete(pol, 0, audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + audit_info->sessionid); return err; } } @@ -801,8 +800,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi if (err) { xfrm_audit_policy_delete(pol, 0, audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + audit_info->sessionid); return err; } } @@ -842,8 +840,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) cnt++; xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + audit_info->sessionid); xfrm_policy_kill(pol); @@ -864,8 +861,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + audit_info->sessionid); xfrm_policy_kill(pol); write_lock_bh(&net->xfrm.xfrm_policy_lock); @@ -2870,12 +2866,10 @@ static void xfrm_policy_fini(struct net *net) #ifdef CONFIG_XFRM_SUB_POLICY audit_info.loginuid = INVALID_UID; audit_info.sessionid = (unsigned int)-1; - audit_info.secid = 0; xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info); #endif audit_info.loginuid = INVALID_UID; audit_info.sessionid = (unsigned int)-1; - audit_info.secid = 0; xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); WARN_ON(!list_empty(&net->xfrm.policy_all)); @@ -2992,14 +2986,14 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, } void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int sessionid, u32 secid) + kuid_t auid, unsigned int sessionid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SPD-add"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); + xfrm_audit_helper_usrinfo(auid, sessionid, audit_buf); audit_log_format(audit_buf, " res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf); @@ -3007,14 +3001,14 @@ void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, EXPORT_SYMBOL_GPL(xfrm_audit_policy_add); void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int sessionid, u32 secid) + kuid_t auid, unsigned int sessionid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SPD-delete"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); + xfrm_audit_helper_usrinfo(auid, sessionid, audit_buf); audit_log_format(audit_buf, " res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 8e9c781a6bba..d91312b5ceb0 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -465,7 +465,7 @@ expired: xfrm_audit_state_delete(x, err ? 0 : 1, audit_get_loginuid(current), - audit_get_sessionid(current), 0); + audit_get_sessionid(current)); out: spin_unlock(&x->lock); @@ -574,8 +574,7 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi (err = security_xfrm_state_delete(x)) != 0) { xfrm_audit_state_delete(x, 0, audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + audit_info->sessionid); return err; } } @@ -613,8 +612,7 @@ restart: err = xfrm_state_delete(x); xfrm_audit_state_delete(x, err ? 0 : 1, audit_info->loginuid, - audit_info->sessionid, - audit_info->secid); + audit_info->sessionid); xfrm_state_put(x); if (!err) cnt++; @@ -2134,7 +2132,6 @@ void xfrm_state_fini(struct net *net) flush_work(&net->xfrm.state_hash_work); audit_info.loginuid = INVALID_UID; audit_info.sessionid = (unsigned int)-1; - audit_info.secid = 0; xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info); flush_work(&net->xfrm.state_gc_work); @@ -2199,14 +2196,14 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family, } void xfrm_audit_state_add(struct xfrm_state *x, int result, - kuid_t auid, unsigned int sessionid, u32 secid) + kuid_t auid, unsigned int sessionid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SAD-add"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); + xfrm_audit_helper_usrinfo(auid, sessionid, audit_buf); xfrm_audit_helper_sainfo(x, audit_buf); audit_log_format(audit_buf, " res=%u", result); audit_log_end(audit_buf); @@ -2214,14 +2211,14 @@ void xfrm_audit_state_add(struct xfrm_state *x, int result, EXPORT_SYMBOL_GPL(xfrm_audit_state_add); void xfrm_audit_state_delete(struct xfrm_state *x, int result, - kuid_t auid, unsigned int sessionid, u32 secid) + kuid_t auid, unsigned int sessionid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SAD-delete"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf); + xfrm_audit_helper_usrinfo(auid, sessionid, audit_buf); xfrm_audit_helper_sainfo(x, audit_buf); audit_log_format(audit_buf, " res=%u", result); audit_log_end(audit_buf); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 8f131c10a6f3..d6409d927b82 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -599,7 +599,6 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct km_event c; kuid_t loginuid = audit_get_loginuid(current); unsigned int sessionid = audit_get_sessionid(current); - u32 sid; err = verify_newsa_info(p, attrs); if (err) @@ -615,8 +614,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, else err = xfrm_state_update(x); - security_task_getsecid(current, &sid); - xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); + xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid); if (err < 0) { x->km.state = XFRM_STATE_DEAD; @@ -678,7 +676,6 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_usersa_id *p = nlmsg_data(nlh); kuid_t loginuid = audit_get_loginuid(current); unsigned int sessionid = audit_get_sessionid(current); - u32 sid; x = xfrm_user_state_lookup(net, p, attrs, &err); if (x == NULL) @@ -703,8 +700,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_notify(x, &c); out: - security_task_getsecid(current, &sid); - xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); + xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid); xfrm_state_put(x); return err; } @@ -1416,7 +1412,6 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, int excl; kuid_t loginuid = audit_get_loginuid(current); unsigned int sessionid = audit_get_sessionid(current); - u32 sid; err = verify_newpolicy_info(p); if (err) @@ -1435,8 +1430,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, * a type XFRM_MSG_UPDPOLICY - JHS */ excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; err = xfrm_policy_insert(p->dir, xp, excl); - security_task_getsecid(current, &sid); - xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); + xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid); if (err) { security_xfrm_policy_free(xp->security); @@ -1675,11 +1669,8 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, } else { kuid_t loginuid = audit_get_loginuid(current); unsigned int sessionid = audit_get_sessionid(current); - u32 sid; - security_task_getsecid(current, &sid); - xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, - sid); + xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid); if (err != 0) goto out; @@ -1709,7 +1700,6 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, audit_info.loginuid = audit_get_loginuid(current); audit_info.sessionid = audit_get_sessionid(current); - security_task_getsecid(current, &audit_info.secid); err = xfrm_state_flush(net, p->proto, &audit_info); if (err) { if (err == -ESRCH) /* empty table */ @@ -1902,7 +1892,6 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, audit_info.loginuid = audit_get_loginuid(current); audit_info.sessionid = audit_get_sessionid(current); - security_task_getsecid(current, &audit_info.secid); err = xfrm_policy_flush(net, type, &audit_info); if (err) { if (err == -ESRCH) /* empty table */ @@ -1971,11 +1960,9 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, if (up->hard) { kuid_t loginuid = audit_get_loginuid(current); unsigned int sessionid = audit_get_sessionid(current); - u32 sid; - security_task_getsecid(current, &sid); xfrm_policy_delete(xp, p->dir); - xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); + xfrm_audit_policy_delete(xp, 1, loginuid, sessionid); } else { // reset the timers here? @@ -2014,11 +2001,9 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, if (ue->hard) { kuid_t loginuid = audit_get_loginuid(current); unsigned int sessionid = audit_get_sessionid(current); - u32 sid; - security_task_getsecid(current, &sid); __xfrm_state_delete(x); - xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); + xfrm_audit_state_delete(x, 1, loginuid, sessionid); } err = 0; out: -- cgit v1.2.3 From 0967e6a5070e336507ce52f09f7297413b966981 Mon Sep 17 00:00:00 2001 From: Vandana Kannan Date: Tue, 1 Apr 2014 16:26:59 +0530 Subject: drm/edid: Fill PAR in AVI infoframe based on CEA mode list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Populate PAR in infoframe structure. If there is a user setting for PAR, then that value is set. Else, value is taken from CEA mode list if VIC is found. Else, PAR is calculated from resolution. If none of these conditions are satisfied, PAR is NONE as per initialization. v2: Removed the part which sets PAR according to user input, based on Daniel's review comments. A separate patch will be submitted to create a property that would enable a user space app to set aspect ratio for AVI infoframe. v2: Removed the part which sets PAR according to user input, based on Daniel's review comments. v3: Removed calculation of PAR for non-CEA modes as per discussion with Ville. A separate patch will be submitted to create a property that would enable a user space app to set aspect ratio for AVI infoframe. Signed-off-by: Vandana Kannan Cc: Jesse Barnes Cc: Vijay Purushothaman Cc: Ville Syrjälä Cc: intel-gfx@lists.freedesktop.org Reviewed-by: Jesse Barnes [danvet: Squash in fixup for htmldocs.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_edid.c | 22 ++++++++++++++++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d4e3f9d9370f..b8d6c5163575 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2452,6 +2452,22 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match) } EXPORT_SYMBOL(drm_match_cea_mode); +/** + * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to + * the input VIC from the CEA mode list + * @video_code: ID given to each of the CEA modes + * + * Returns picture aspect ratio + */ +enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code) +{ + /* return picture aspect ratio for video_code - 1 to access the + * right array element + */ + return edid_cea_modes[video_code-1].picture_aspect_ratio; +} +EXPORT_SYMBOL(drm_get_cea_aspect_ratio); + /* * Calculate the alternate clock for HDMI modes (those from the HDMI vendor * specific block). @@ -3613,6 +3629,12 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, frame->video_code = drm_match_cea_mode(mode); frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; + + /* Populate picture aspect ratio from CEA mode list */ + if (frame->video_code > 0) + frame->picture_aspect = drm_get_cea_aspect_ratio( + frame->video_code); + frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e55fccbe7c42..c061bb372199 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1020,6 +1020,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern u8 drm_match_cea_mode(const struct drm_display_mode *to_match); +extern enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code); extern bool drm_detect_hdmi_monitor(struct edid *edid); extern bool drm_detect_monitor_audio(struct edid *edid); extern bool drm_rgb_quant_range_selectable(struct edid *edid); -- cgit v1.2.3 From f9b0e251dfbf2c4da642ec9210db29a7ac63b81a Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Wed, 2 Apr 2014 12:29:46 +0200 Subject: drm: make mode_valid callback optional Many drm connectors do not need mode validation. The patch makes this callback optional and removes dumb implementations. v2: Rebase: - imx move to a shared (but still dummy) ->mode_valid implementation. - probe helpers have been extracted to drm_probe_helper.c Signed-off-by: Andrzej Hajda (v1) Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 6 +++--- drivers/gpu/drm/ast/ast_mode.c | 7 ------- drivers/gpu/drm/bridge/ptn3460.c | 7 ------- drivers/gpu/drm/cirrus/cirrus_mode.c | 8 -------- drivers/gpu/drm/drm_probe_helper.c | 2 +- drivers/gpu/drm/exynos/exynos_dp_core.c | 7 ------- drivers/gpu/drm/exynos/exynos_drm_dpi.c | 7 ------- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 7 ------- drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 7 ------- drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 7 ------- drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 7 ------- drivers/staging/imx-drm/imx-drm-core.c | 7 ------- drivers/staging/imx-drm/imx-drm.h | 2 -- drivers/staging/imx-drm/imx-hdmi.c | 1 - drivers/staging/imx-drm/imx-ldb.c | 1 - drivers/staging/imx-drm/imx-tve.c | 4 ---- drivers/staging/imx-drm/parallel-display.c | 1 - include/drm/drm_crtc_helper.h | 2 +- 18 files changed, 5 insertions(+), 85 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 677a02553ec0..c72e146e58d3 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1903,8 +1903,8 @@ void intel_crt_init(struct drm_device *dev) The function filters out modes larger than max_width and max_height - if specified. It then calls the connector - mode_valid helper operation for each mode in + if specified. It then calls the optional connector + mode_valid helper operation for each mode in the probed list to check whether the mode is valid for the connector. @@ -2265,7 +2265,7 @@ void intel_crt_init(struct drm_device *dev) Verify whether a mode is valid for the connector. Return MODE_OK for supported modes and one of the enum drm_mode_status values (MODE_*) - for unsupported modes. This operation is mandatory. + for unsupported modes. This operation is optional. As the mode rejection reason is currently not used beside for diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index a4afdc8bb578..e599d64a2620 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -743,12 +743,6 @@ static int ast_get_modes(struct drm_connector *connector) return 0; } -static int ast_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static void ast_connector_destroy(struct drm_connector *connector) { struct ast_connector *ast_connector = to_ast_connector(connector); @@ -765,7 +759,6 @@ ast_connector_detect(struct drm_connector *connector, bool force) } static const struct drm_connector_helper_funcs ast_connector_helper_funcs = { - .mode_valid = ast_mode_valid, .get_modes = ast_get_modes, .best_encoder = ast_best_single_encoder, }; diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index b171901a3553..98fd17ae4916 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -225,12 +225,6 @@ out: return num_modes; } -static int ptn3460_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) { struct ptn3460_bridge *ptn_bridge; @@ -242,7 +236,6 @@ struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { .get_modes = ptn3460_get_modes, - .mode_valid = ptn3460_mode_valid, .best_encoder = ptn3460_best_encoder, }; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index f59433b7610c..49332c5fe35b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -505,13 +505,6 @@ static int cirrus_vga_get_modes(struct drm_connector *connector) return count; } -static int cirrus_vga_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - /* Any mode we've added is valid */ - return MODE_OK; -} - static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector *connector) { @@ -546,7 +539,6 @@ static void cirrus_connector_destroy(struct drm_connector *connector) struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = { .get_modes = cirrus_vga_get_modes, - .mode_valid = cirrus_vga_mode_valid, .best_encoder = cirrus_connector_best_encoder, }; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index e70f54d4a581..d06340985a72 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -169,7 +169,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, drm_mode_validate_flag(connector, mode_flags); list_for_each_entry(mode, &connector->modes, head) { - if (mode->status == MODE_OK) + if (mode->status == MODE_OK && connector_funcs->mode_valid) mode->status = connector_funcs->mode_valid(connector, mode); } diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index aed533bbfd31..bb74472b4e4b 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -949,12 +949,6 @@ static int exynos_dp_get_modes(struct drm_connector *connector) return 1; } -static int exynos_dp_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static struct drm_encoder *exynos_dp_best_encoder( struct drm_connector *connector) { @@ -965,7 +959,6 @@ static struct drm_encoder *exynos_dp_best_encoder( static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { .get_modes = exynos_dp_get_modes, - .mode_valid = exynos_dp_mode_valid, .best_encoder = exynos_dp_best_encoder, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 2b09c7c0bfcc..82e52c71bccc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -94,12 +94,6 @@ static int exynos_dpi_get_modes(struct drm_connector *connector) return 0; } -static int exynos_dpi_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static struct drm_encoder * exynos_dpi_best_encoder(struct drm_connector *connector) { @@ -110,7 +104,6 @@ exynos_dpi_best_encoder(struct drm_connector *connector) static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { .get_modes = exynos_dpi_get_modes, - .mode_valid = exynos_dpi_mode_valid, .best_encoder = exynos_dpi_best_encoder, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 7afead9c3f30..b6980865dd50 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -533,12 +533,6 @@ static int vidi_get_modes(struct drm_connector *connector) return drm_add_edid_modes(connector, edid); } -static int vidi_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector) { struct vidi_context *ctx = ctx_from_connector(connector); @@ -548,7 +542,6 @@ static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector) static struct drm_connector_helper_funcs vidi_connector_helper_funcs = { .get_modes = vidi_get_modes, - .mode_valid = vidi_mode_valid, .best_encoder = vidi_best_encoder, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 4f3ba93cd91d..289048d1c7b2 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -57,15 +57,8 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) return 1; } -static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = rcar_du_lvds_connector_get_modes, - .mode_valid = rcar_du_lvds_connector_mode_valid, .best_encoder = rcar_du_connector_best_encoder, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index 41d563adfeaa..ccfe64c7188f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -25,15 +25,8 @@ static int rcar_du_vga_connector_get_modes(struct drm_connector *connector) return 0; } -static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = rcar_du_vga_connector_get_modes, - .mode_valid = rcar_du_vga_connector_mode_valid, .best_encoder = rcar_du_connector_best_encoder, }; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index e9e5e6d368cc..faf176b2daf9 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -674,12 +674,6 @@ static int shmob_drm_connector_get_modes(struct drm_connector *connector) return 1; } -static int shmob_drm_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static struct drm_encoder * shmob_drm_connector_best_encoder(struct drm_connector *connector) { @@ -690,7 +684,6 @@ shmob_drm_connector_best_encoder(struct drm_connector *connector) static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = shmob_drm_connector_get_modes, - .mode_valid = shmob_drm_connector_mode_valid, .best_encoder = shmob_drm_connector_best_encoder, }; diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index 4144a75e5f71..53ff005245c5 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -200,13 +200,6 @@ static const struct file_operations imx_drm_driver_fops = { .llseek = noop_llseek, }; -int imx_drm_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} -EXPORT_SYMBOL(imx_drm_connector_mode_valid); - void imx_drm_connector_destroy(struct drm_connector *connector) { drm_sysfs_connector_remove(connector); diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h index a322bac55414..7453ae00c412 100644 --- a/drivers/staging/imx-drm/imx-drm.h +++ b/drivers/staging/imx-drm/imx-drm.h @@ -50,8 +50,6 @@ int imx_drm_encoder_get_mux_id(struct device_node *node, int imx_drm_encoder_parse_of(struct drm_device *drm, struct drm_encoder *encoder, struct device_node *np); -int imx_drm_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode); void imx_drm_connector_destroy(struct drm_connector *connector); void imx_drm_encoder_destroy(struct drm_encoder *encoder); diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c index d47dedd2cdb4..9fbe6d6a989d 100644 --- a/drivers/staging/imx-drm/imx-hdmi.c +++ b/drivers/staging/imx-drm/imx-hdmi.c @@ -1492,7 +1492,6 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = { static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { .get_modes = imx_hdmi_connector_get_modes, - .mode_valid = imx_drm_connector_mode_valid, .best_encoder = imx_hdmi_connector_best_encoder, }; diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c index fe4c1ef4e7a5..7e3f019d7e72 100644 --- a/drivers/staging/imx-drm/imx-ldb.c +++ b/drivers/staging/imx-drm/imx-ldb.c @@ -317,7 +317,6 @@ static struct drm_connector_funcs imx_ldb_connector_funcs = { static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { .get_modes = imx_ldb_connector_get_modes, .best_encoder = imx_ldb_connector_best_encoder, - .mode_valid = imx_drm_connector_mode_valid, }; static struct drm_encoder_funcs imx_ldb_encoder_funcs = { diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c index 575533f4fd64..5a5a5287a86a 100644 --- a/drivers/staging/imx-drm/imx-tve.c +++ b/drivers/staging/imx-drm/imx-tve.c @@ -251,10 +251,6 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector, unsigned long rate; int ret; - ret = imx_drm_connector_mode_valid(connector, mode); - if (ret != MODE_OK) - return ret; - /* pixel clock with 2x oversampling */ rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000; if (rate == mode->clock) diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c index c60b6c645f42..52598e489a4b 100644 --- a/drivers/staging/imx-drm/parallel-display.c +++ b/drivers/staging/imx-drm/parallel-display.c @@ -148,7 +148,6 @@ static struct drm_connector_funcs imx_pd_connector_funcs = { static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { .get_modes = imx_pd_connector_get_modes, .best_encoder = imx_pd_connector_best_encoder, - .mode_valid = imx_drm_connector_mode_valid, }; static struct drm_encoder_funcs imx_pd_encoder_funcs = { diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 36a5febac2a6..7ffb59232e84 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -114,7 +114,7 @@ struct drm_encoder_helper_funcs { /** * drm_connector_helper_funcs - helper operations for connectors * @get_modes: get mode list for this connector - * @mode_valid: is this mode valid on the given connector? + * @mode_valid (optional): is this mode valid on the given connector? * * The helper operations are called by the mid-layer CRTC helper. */ -- cgit v1.2.3 From eaaf8f0fc32468d4dedbb5b5f9c5bfb27744be4c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 28 Aug 2013 15:19:23 +0200 Subject: drm/pci: fold in irq_by_busid support This is a ums-only ioctl, and we've only ever supported ums (at least in upstream) on pci devices. So no point in keeping that piece of legacy logic abstracted within the drm bus driver. To keep things work without CONFIG_PCI also add a dummy ioctl. v2: Block the irq_by_busid ioctl for modeset drivers. v3: Spelling/whitespace polish (Thierry) Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 27 --------------------------- drivers/gpu/drm/drm_pci.c | 39 +++++++++++++++++++++++++++++++++++++-- include/drm/drmP.h | 1 - 3 files changed, 37 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index d4cc47690d65..e5920e7a4392 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -56,33 +56,6 @@ */ #define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000 -/** - * Get interrupt from bus id. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_irq_busid structure. - * \return zero on success or a negative number on failure. - * - * Finds the PCI device with the specified bus id and gets its IRQ number. - * This IOCTL is deprecated, and will now return EINVAL for any busid not equal - * to that of the device that this DRM instance attached to. - */ -int drm_irq_by_busid(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_irq_busid *p = data; - - if (!dev->driver->bus->irq_by_busid) - return -EINVAL; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) - return -EINVAL; - - return dev->driver->bus->irq_by_busid(dev, p); -} - /* * Clear vblank timestamp buffer for a crtc. */ diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 9c696a5ad74d..8d4221683af4 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -247,7 +247,6 @@ err: return ret; } - static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) { if ((p->busnum >> 8) != drm_get_pci_domain(dev) || @@ -262,6 +261,37 @@ static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) return 0; } +/** + * Get interrupt from bus id. + * + * \param inode device inode. + * \param file_priv DRM file private. + * \param cmd command. + * \param arg user argument, pointing to a drm_irq_busid structure. + * \return zero on success or a negative number on failure. + * + * Finds the PCI device with the specified bus id and gets its IRQ number. + * This IOCTL is deprecated, and will now return EINVAL for any busid not equal + * to that of the device that this DRM instance attached to. + */ +int drm_irq_by_busid(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_irq_busid *p = data; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + /* UMS was only ever support on PCI devices. */ + if (WARN_ON(!dev->pdev)) + return -EINVAL; + + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + return -EINVAL; + + return drm_pci_irq_by_busid(dev, p); +} + static void drm_pci_agp_init(struct drm_device *dev) { if (drm_core_check_feature(dev, DRIVER_USE_AGP)) { @@ -292,7 +322,6 @@ static struct drm_bus drm_pci_bus = { .get_name = drm_pci_get_name, .set_busid = drm_pci_set_busid, .set_unique = drm_pci_set_unique, - .irq_by_busid = drm_pci_irq_by_busid, }; /** @@ -453,6 +482,12 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) } void drm_pci_agp_destroy(struct drm_device *dev) {} + +int drm_irq_by_busid(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -EINVAL; +} #endif EXPORT_SYMBOL(drm_pci_init); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a7c2a862b4f4..6d7ca98d0143 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -737,7 +737,6 @@ struct drm_bus { int (*set_busid)(struct drm_device *dev, struct drm_master *master); int (*set_unique)(struct drm_device *dev, struct drm_master *master, struct drm_unique *unique); - int (*irq_by_busid)(struct drm_device *dev, struct drm_irq_busid *p); }; /** -- cgit v1.2.3 From ebfa4324930618e72645d2eb7db1c9773228a868 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 20:27:09 +0100 Subject: drm: remove drm_dev_to_irq from drivers Only used in some legacy pci drivers, and dereferencing the PCI irq is actually shorter ... Since this removes all users for drm_dev_to_irq from the tree except in drm_irq.c, move the inline helper in there. It'll disappear soon, too. v2: Polish commit message (Thierry) Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 5 +++++ drivers/gpu/drm/mga/mga_state.c | 2 +- drivers/gpu/drm/r128/r128_state.c | 2 +- drivers/gpu/drm/radeon/radeon_state.c | 2 +- include/drm/drmP.h | 5 ----- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 06a53f8b618c..589e865832cd 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -233,6 +233,11 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) } } +static inline int drm_dev_to_irq(struct drm_device *dev) +{ + return dev->driver->bus->get_irq(dev); +} + /** * Install IRQ handler. * diff --git a/drivers/gpu/drm/mga/mga_state.c b/drivers/gpu/drm/mga/mga_state.c index 314685b7f41f..3cb58df5237e 100644 --- a/drivers/gpu/drm/mga/mga_state.c +++ b/drivers/gpu/drm/mga/mga_state.c @@ -1020,7 +1020,7 @@ static int mga_getparam(struct drm_device *dev, void *data, struct drm_file *fil switch (param->param) { case MGA_PARAM_IRQ_NR: - value = drm_dev_to_irq(dev); + value = dev->pdev->irq; break; case MGA_PARAM_CARD_TYPE: value = dev_priv->chipset; diff --git a/drivers/gpu/drm/r128/r128_state.c b/drivers/gpu/drm/r128/r128_state.c index e806dacd452f..97064dd434c2 100644 --- a/drivers/gpu/drm/r128/r128_state.c +++ b/drivers/gpu/drm/r128/r128_state.c @@ -1594,7 +1594,7 @@ static int r128_getparam(struct drm_device *dev, void *data, struct drm_file *fi switch (param->param) { case R128_PARAM_IRQ_NR: - value = drm_dev_to_irq(dev); + value = dev->pdev->irq; break; default: return -EINVAL; diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c index 956ab7f14e16..b576549fc783 100644 --- a/drivers/gpu/drm/radeon/radeon_state.c +++ b/drivers/gpu/drm/radeon/radeon_state.c @@ -3054,7 +3054,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600) value = 0; else - value = drm_dev_to_irq(dev); + value = dev->pdev->irq; break; case RADEON_PARAM_GART_BASE: value = dev_priv->gart_vm_start; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 6d7ca98d0143..41839ea0c1ee 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1185,11 +1185,6 @@ static __inline__ int drm_core_check_feature(struct drm_device *dev, return ((dev->driver->driver_features & feature) ? 1 : 0); } -static inline int drm_dev_to_irq(struct drm_device *dev) -{ - return dev->driver->bus->get_irq(dev); -} - static inline void drm_device_set_unplugged(struct drm_device *dev) { smp_wmb(); -- cgit v1.2.3 From 42b21049fc26513ca8e732f47559b1525b04a992 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 20:30:25 +0100 Subject: drm: kill drm_bus->bus_type Completely unused. Hooray, midlayer mistakes that didn't cause work to undo! v2: Rebase on top of the recent tegra changes which added a host1x drm bus. Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_pci.c | 1 - drivers/gpu/drm/drm_platform.c | 1 - drivers/gpu/drm/drm_usb.c | 1 - drivers/gpu/drm/tegra/bus.c | 1 - include/drm/drmP.h | 6 ------ 5 files changed, 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 8d4221683af4..08c76af920d6 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -317,7 +317,6 @@ void drm_pci_agp_destroy(struct drm_device *dev) } static struct drm_bus drm_pci_bus = { - .bus_type = DRIVER_BUS_PCI, .get_irq = drm_pci_get_irq, .get_name = drm_pci_get_name, .set_busid = drm_pci_set_busid, diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 319ff5385601..5cd8c695824f 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -123,7 +123,6 @@ err: } static struct drm_bus drm_platform_bus = { - .bus_type = DRIVER_BUS_PLATFORM, .get_irq = drm_platform_get_irq, .get_name = drm_platform_get_name, .set_busid = drm_platform_set_busid, diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index c3406aad2944..ed60f887c805 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -53,7 +53,6 @@ static int drm_usb_set_busid(struct drm_device *dev, } static struct drm_bus drm_usb_bus = { - .bus_type = DRIVER_BUS_USB, .get_irq = drm_usb_get_irq, .get_name = drm_usb_get_name, .set_busid = drm_usb_set_busid, diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c index 71cef5c13dc8..6916fa51cfa4 100644 --- a/drivers/gpu/drm/tegra/bus.c +++ b/drivers/gpu/drm/tegra/bus.c @@ -37,7 +37,6 @@ static int drm_host1x_set_busid(struct drm_device *dev, } static struct drm_bus drm_host1x_bus = { - .bus_type = DRIVER_BUS_HOST1X, .set_busid = drm_host1x_set_busid, }; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 41839ea0c1ee..9f1fb8d36b67 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -143,11 +143,6 @@ int drm_err(const char *func, const char *format, ...); #define DRIVER_PRIME 0x4000 #define DRIVER_RENDER 0x8000 -#define DRIVER_BUS_PCI 0x1 -#define DRIVER_BUS_PLATFORM 0x2 -#define DRIVER_BUS_USB 0x3 -#define DRIVER_BUS_HOST1X 0x4 - /***********************************************************************/ /** \name Begin the DRM... */ /*@{*/ @@ -731,7 +726,6 @@ struct drm_master { #define DRM_SCANOUTPOS_ACCURATE (1 << 2) struct drm_bus { - int bus_type; int (*get_irq)(struct drm_device *dev); const char *(*get_name)(struct drm_device *dev); int (*set_busid)(struct drm_device *dev, struct drm_master *master); -- cgit v1.2.3 From fa372a51cb5f93800f711473e5a36e0e0c9a8f00 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Tue, 8 Apr 2014 15:19:43 -0700 Subject: mmc: Delay the card_event callback into the mmc_rescan worker This change removes the callback from atomic context which it doesn't need to be in, and puts it in line with the debounced rescan. This code is based on these e-mail threads with Christian Daudt: https://lkml.org/lkml/2013/8/19/539 https://lkml.org/lkml/2014/3/19/79 Signed-off-by: Markus Mayer Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 5 +++++ drivers/mmc/core/slot-gpio.c | 4 +--- include/linux/mmc/host.h | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index acbc3f2aaaf9..f396d1bb4ac4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2403,6 +2403,11 @@ void mmc_rescan(struct work_struct *work) container_of(work, struct mmc_host, detect.work); int i; + if (host->trigger_card_event && host->ops->card_event) { + host->ops->card_event(host); + host->trigger_card_event = false; + } + if (host->rescan_disable) return; diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index f7650b899e3d..5f89cb83d5f0 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -32,9 +32,7 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) /* Schedule a card detection after a debounce timeout */ struct mmc_host *host = dev_id; - if (host->ops->card_event) - host->ops->card_event(host); - + host->trigger_card_event = true; mmc_detect_change(host, msecs_to_jiffies(200)); return IRQ_HANDLED; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 35354207e71f..0cf705c83998 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -319,6 +319,8 @@ struct mmc_host { int rescan_disable; /* disable card detection */ int rescan_entered; /* used with nonremovable devices */ + bool trigger_card_event; /* card_event necessary */ + struct mmc_card *card; /* device attached to this host */ wait_queue_head_t wq; -- cgit v1.2.3 From 297d40560bc8f474adbb43178e3118321fa702ea Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 4 Apr 2014 22:42:48 -0300 Subject: mmc: card.h: Use NULL instead of 0 for END_FIXUP Fix the following sparse warnings: drivers/mmc/card/block.c:2421:9: warning: Using plain integer as NULL pointer drivers/mmc/core/quirks.c:69:9: warning: Using plain integer as NULL pointer Signed-off-by: Fabio Estevam Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- include/linux/mmc/card.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index b73027298b3a..aa7e57f60fb2 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -353,7 +353,7 @@ struct mmc_fixup { #define CID_OEMID_ANY ((unsigned short) -1) #define CID_NAME_ANY (NULL) -#define END_FIXUP { 0 } +#define END_FIXUP { NULL } #define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \ _cis_vendor, _cis_device, \ -- cgit v1.2.3 From f2061656209fb9a5d54bbb1999f0a633438504e7 Mon Sep 17 00:00:00 2001 From: Dominik Dingel Date: Wed, 9 Apr 2014 13:13:00 +0200 Subject: KVM: s390: Per-vm kvm device controls We sometimes need to get/set attributes specific to a virtual machine and so need something else than ONE_REG. Let's copy the KVM_DEVICE approach, and define the respective ioctls for the vm file descriptor. Signed-off-by: Dominik Dingel Reviewed-by: Cornelia Huck Acked-by: Alexander Graf Signed-off-by: Christian Borntraeger --- Documentation/virtual/kvm/api.txt | 8 ++--- Documentation/virtual/kvm/devices/vm.txt | 10 ++++++ arch/s390/kvm/kvm-s390.c | 54 ++++++++++++++++++++++++++++++++ include/uapi/linux/kvm.h | 1 + 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 Documentation/virtual/kvm/devices/vm.txt (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index a9380ba54c8e..2014ff12b492 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2314,8 +2314,8 @@ struct kvm_create_device { 4.80 KVM_SET_DEVICE_ATTR/KVM_GET_DEVICE_ATTR -Capability: KVM_CAP_DEVICE_CTRL -Type: device ioctl +Capability: KVM_CAP_DEVICE_CTRL, KVM_CAP_VM_ATTRIBUTES for vm device +Type: device ioctl, vm ioctl Parameters: struct kvm_device_attr Returns: 0 on success, -1 on error Errors: @@ -2340,8 +2340,8 @@ struct kvm_device_attr { 4.81 KVM_HAS_DEVICE_ATTR -Capability: KVM_CAP_DEVICE_CTRL -Type: device ioctl +Capability: KVM_CAP_DEVICE_CTRL, KVM_CAP_VM_ATTRIBUTES for vm device +Type: device ioctl, vm ioctl Parameters: struct kvm_device_attr Returns: 0 on success, -1 on error Errors: diff --git a/Documentation/virtual/kvm/devices/vm.txt b/Documentation/virtual/kvm/devices/vm.txt new file mode 100644 index 000000000000..562bee6e600b --- /dev/null +++ b/Documentation/virtual/kvm/devices/vm.txt @@ -0,0 +1,10 @@ +Generic vm interface +==================================== + +The virtual machine "device" also accepts the ioctls KVM_SET_DEVICE_ATTR, +KVM_GET_DEVICE_ATTR, and KVM_HAS_DEVICE_ATTR. The interface uses the same +struct kvm_device_attr as other devices, but targets VM-wide settings +and controls. + +The groups and attributes per virtual machine, if any, are architecture +specific. diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 346a3478dd00..c335a2efa5de 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -162,6 +162,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_IOEVENTFD: case KVM_CAP_DEVICE_CTRL: case KVM_CAP_ENABLE_CAP_VM: + case KVM_CAP_VM_ATTRIBUTES: r = 1; break; case KVM_CAP_NR_VCPUS: @@ -257,11 +258,43 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) return r; } +static int kvm_s390_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr) +{ + int ret; + + switch (attr->group) { + default: + ret = -ENXIO; + break; + } + + return ret; +} + +static int kvm_s390_vm_get_attr(struct kvm *kvm, struct kvm_device_attr *attr) +{ + return -ENXIO; +} + +static int kvm_s390_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr) +{ + int ret; + + switch (attr->group) { + default: + ret = -ENXIO; + break; + } + + return ret; +} + long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { struct kvm *kvm = filp->private_data; void __user *argp = (void __user *)arg; + struct kvm_device_attr attr; int r; switch (ioctl) { @@ -294,6 +327,27 @@ long kvm_arch_vm_ioctl(struct file *filp, } break; } + case KVM_SET_DEVICE_ATTR: { + r = -EFAULT; + if (copy_from_user(&attr, (void __user *)arg, sizeof(attr))) + break; + r = kvm_s390_vm_set_attr(kvm, &attr); + break; + } + case KVM_GET_DEVICE_ATTR: { + r = -EFAULT; + if (copy_from_user(&attr, (void __user *)arg, sizeof(attr))) + break; + r = kvm_s390_vm_get_attr(kvm, &attr); + break; + } + case KVM_HAS_DEVICE_ATTR: { + r = -EFAULT; + if (copy_from_user(&attr, (void __user *)arg, sizeof(attr))) + break; + r = kvm_s390_vm_has_attr(kvm, &attr); + break; + } default: r = -ENOTTY; } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index a8f4ee5d2e82..90acfe4966e7 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -743,6 +743,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97 #define KVM_CAP_ENABLE_CAP_VM 98 #define KVM_CAP_S390_IRQCHIP 99 +#define KVM_CAP_VM_ATTRIBUTES 100 #ifdef KVM_CAP_IRQ_ROUTING -- cgit v1.2.3 From dfeec843fb237d73947e818f961e8d6f0df22b01 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 1 Jan 2014 16:09:21 +0100 Subject: KVM: add kvm_is_error_gpa() helper It's quite common (in the s390 guest access code) to test if a guest physical address points to a valid guest memory area or not. So add a simple helper function in common code, since this might be of interest for other architectures as well. Signed-off-by: Heiko Carstens Reviewed-by: Thomas Huth Reviewed-by: Cornelia Huck Signed-off-by: Christian Borntraeger --- include/linux/kvm_host.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 7d21cf9f4380..471d1400c4ac 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -879,6 +879,13 @@ static inline hpa_t pfn_to_hpa(pfn_t pfn) return (hpa_t)pfn << PAGE_SHIFT; } +static inline bool kvm_is_error_gpa(struct kvm *kvm, gpa_t gpa) +{ + unsigned long hva = gfn_to_hva(kvm, gpa_to_gfn(gpa)); + + return kvm_is_error_hva(hva); +} + static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu) { set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests); -- cgit v1.2.3 From e2c330b9b5665006c99327c05bc22f7a8e471043 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 22 Apr 2014 13:23:13 +0200 Subject: ASoC: Move IO abstraction to the component level We currently have two very similar IO abstractions in ASoC, one for CODECs, the other for platforms. Moving this to the component level will allow us to unify those two. It will also enable us to move the standard kcontrol helpers as well as DAPM support to the component level. The new component level abstraction layer is primarily build around regmap. There is a per component pointer for the regmap instance for the underlying device. There are four new function snd_soc_component_read(), snd_soc_component_write(), snd_soc_component_update_bits() and snd_soc_component_update_bits_async(). They have the same signature as their regmap counter-part and will internally forward the call one-to-one to regmap. If the component it not using regmap it will fallback to using the custom IO callbacks. This is done to be able to support drivers that haven't been converted to regmap yet, but it is expected that this will eventually be removed in the future once all component drivers have been converted to regmap. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + include/sound/soc.h | 31 +++-- sound/soc/soc-core.c | 96 ++++++++++---- sound/soc/soc-dapm.c | 84 ++----------- sound/soc/soc-io.c | 322 ++++++++++++++++++++++++++++++----------------- 5 files changed, 317 insertions(+), 217 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ef78f562f4a8..75020f52acdd 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -606,6 +606,7 @@ struct snd_soc_dapm_context { enum snd_soc_dapm_type, int); struct device *dev; /* from parent - for debug */ + struct snd_soc_component *component; /* parent component */ struct snd_soc_codec *codec; /* parent codec */ struct snd_soc_platform *platform; /* parent platform */ struct snd_soc_card *card; /* parent card */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 8148c98b5f8f..a4179e73369a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -393,8 +393,6 @@ int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); -int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - struct regmap *regmap); int snd_soc_cache_sync(struct snd_soc_codec *codec); int snd_soc_cache_init(struct snd_soc_codec *codec); int snd_soc_cache_exit(struct snd_soc_codec *codec); @@ -672,6 +670,14 @@ struct snd_soc_component { const struct snd_soc_component_driver *driver; struct list_head dai_list; + + int (*read)(struct snd_soc_component *, unsigned int, unsigned int *); + int (*write)(struct snd_soc_component *, unsigned int, unsigned int); + + struct regmap *regmap; + int val_bytes; + + struct mutex io_mutex; }; /* SoC Audio Codec device */ @@ -696,18 +702,14 @@ struct snd_soc_codec { unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int cache_init:1; /* codec cache has been initialized */ - unsigned int using_regmap:1; /* using regmap access */ u32 cache_only; /* Suppress writes to hardware */ u32 cache_sync; /* Cache needs to be synced to hardware */ /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ hw_write_t hw_write; - unsigned int (*read)(struct snd_soc_codec *, unsigned int); - int (*write)(struct snd_soc_codec *, unsigned int, unsigned int); void *reg_cache; struct mutex cache_rw_mutex; - int val_bytes; /* component */ struct snd_soc_component component; @@ -824,7 +826,6 @@ struct snd_soc_platform { int id; struct device *dev; const struct snd_soc_platform_driver *driver; - struct mutex mutex; unsigned int suspended:1; /* platform is suspended */ unsigned int probed:1; @@ -1129,6 +1130,22 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val); +/* component IO */ +int snd_soc_component_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val); +int snd_soc_component_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val); +int snd_soc_component_update_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val); +int snd_soc_component_update_bits_async(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val); +void snd_soc_component_async_complete(struct snd_soc_component *component); +int snd_soc_component_test_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int value); + +int snd_soc_component_init_io(struct snd_soc_component *component, + struct regmap *regmap); + /* device driver data */ static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index db577e4cdf49..f7eb21e70611 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -656,8 +656,8 @@ int snd_soc_suspend(struct device *dev) codec->driver->suspend(codec); codec->suspended = 1; codec->cache_sync = 1; - if (codec->using_regmap) - regcache_mark_dirty(codec->control_data); + if (codec->component.regmap) + regcache_mark_dirty(codec->component.regmap); /* deactivate pins to sleep state */ pinctrl_pm_select_sleep_state(codec->dev); break; @@ -2971,7 +2971,7 @@ int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct soc_bytes *params = (void *)kcontrol->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = params->num_regs * codec->val_bytes; + uinfo->count = params->num_regs * codec->component.val_bytes; return 0; } @@ -2984,16 +2984,16 @@ int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); int ret; - if (codec->using_regmap) - ret = regmap_raw_read(codec->control_data, params->base, + if (codec->component.regmap) + ret = regmap_raw_read(codec->component.regmap, params->base, ucontrol->value.bytes.data, - params->num_regs * codec->val_bytes); + params->num_regs * codec->component.val_bytes); else ret = -EINVAL; /* Hide any masked bytes to ensure consistent data reporting */ if (ret == 0 && params->mask) { - switch (codec->val_bytes) { + switch (codec->component.val_bytes) { case 1: ucontrol->value.bytes.data[0] &= ~params->mask; break; @@ -3023,10 +3023,10 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, unsigned int val, mask; void *data; - if (!codec->using_regmap) + if (!codec->component.regmap) return -EINVAL; - len = params->num_regs * codec->val_bytes; + len = params->num_regs * codec->component.val_bytes; data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); if (!data) @@ -3038,27 +3038,27 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, * copy. */ if (params->mask) { - ret = regmap_read(codec->control_data, params->base, &val); + ret = regmap_read(codec->component.regmap, params->base, &val); if (ret != 0) goto out; val &= params->mask; - switch (codec->val_bytes) { + switch (codec->component.val_bytes) { case 1: ((u8 *)data)[0] &= ~params->mask; ((u8 *)data)[0] |= val; break; case 2: mask = ~params->mask; - ret = regmap_parse_val(codec->control_data, + ret = regmap_parse_val(codec->component.regmap, &mask, &mask); if (ret != 0) goto out; ((u16 *)data)[0] &= mask; - ret = regmap_parse_val(codec->control_data, + ret = regmap_parse_val(codec->component.regmap, &val, &val); if (ret != 0) goto out; @@ -3067,14 +3067,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, break; case 4: mask = ~params->mask; - ret = regmap_parse_val(codec->control_data, + ret = regmap_parse_val(codec->component.regmap, &mask, &mask); if (ret != 0) goto out; ((u32 *)data)[0] &= mask; - ret = regmap_parse_val(codec->control_data, + ret = regmap_parse_val(codec->component.regmap, &val, &val); if (ret != 0) goto out; @@ -3087,7 +3087,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, } } - ret = regmap_raw_write(codec->control_data, params->base, + ret = regmap_raw_write(codec->component.regmap, params->base, data, len); out: @@ -3143,7 +3143,7 @@ int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; - unsigned int regwshift = codec->val_bytes * BITS_PER_BYTE; + unsigned int regwshift = codec->component.val_bytes * BITS_PER_BYTE; unsigned int regwmask = (1<invert; unsigned long mask = (1UL<nbits)-1; @@ -3189,7 +3189,7 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; - unsigned int regwshift = codec->val_bytes * BITS_PER_BYTE; + unsigned int regwshift = codec->component.val_bytes * BITS_PER_BYTE; unsigned int regwmask = (1<invert; unsigned long mask = (1UL<nbits)-1; @@ -3837,6 +3837,8 @@ __snd_soc_register_component(struct device *dev, return -ENOMEM; } + mutex_init(&cmpnt->io_mutex); + cmpnt->name = fmt_single_name(dev, &cmpnt->id); if (!cmpnt->name) { dev_err(dev, "ASoC: Failed to simplifying name\n"); @@ -3917,6 +3919,24 @@ found: } EXPORT_SYMBOL_GPL(snd_soc_unregister_component); +static int snd_soc_platform_drv_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + return platform->driver->write(platform, reg, val); +} + +static int snd_soc_platform_drv_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + *val = platform->driver->read(platform, reg); + + return 0; +} + /** * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform @@ -3937,8 +3957,12 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->driver = platform_drv; platform->dapm.dev = dev; platform->dapm.platform = platform; + platform->dapm.component = &platform->component; platform->dapm.stream_event = platform_drv->stream_event; - mutex_init(&platform->mutex); + if (platform_drv->write) + platform->component.write = snd_soc_platform_drv_write; + if (platform_drv->read) + platform->component.read = snd_soc_platform_drv_read; /* register component */ ret = __snd_soc_register_component(dev, &platform->component, @@ -4067,6 +4091,24 @@ static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) stream->formats |= codec_format_map[i]; } +static int snd_soc_codec_drv_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + return codec->driver->write(codec, reg, val); +} + +static int snd_soc_codec_drv_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val) +{ + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + *val = codec->driver->read(codec, reg); + + return 0; +} + /** * snd_soc_register_codec - Register a codec with the ASoC core * @@ -4094,29 +4136,33 @@ int snd_soc_register_codec(struct device *dev, goto fail_codec; } - codec->write = codec_drv->write; - codec->read = codec_drv->read; + if (codec_drv->write) + codec->component.write = snd_soc_codec_drv_write; + if (codec_drv->read) + codec->component.read = snd_soc_codec_drv_read; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; + codec->dapm.component = &codec->component; codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dapm.stream_event = codec_drv->stream_event; codec->dev = dev; codec->driver = codec_drv; codec->num_dai = num_dai; - codec->val_bytes = codec_drv->reg_word_size; + codec->component.val_bytes = codec_drv->reg_word_size; mutex_init(&codec->mutex); - if (!codec->write) { + if (!codec->component.write) { if (codec_drv->get_regmap) regmap = codec_drv->get_regmap(dev); else regmap = dev_get_regmap(dev, NULL); if (regmap) { - ret = snd_soc_codec_set_cache_io(codec, regmap); - if (ret && ret != -ENOTSUPP) { + ret = snd_soc_component_init_io(&codec->component, + regmap); + if (ret) { dev_err(codec->dev, "Failed to set cache I/O:%d\n", ret); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f4ba7b40a6ab..da266e1e61b0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -379,86 +379,24 @@ static void dapm_reset(struct snd_soc_card *card) static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg, unsigned int *value) { - if (w->codec) { - *value = snd_soc_read(w->codec, reg); - return 0; - } else if (w->platform) { - *value = snd_soc_platform_read(w->platform, reg); - return 0; - } - - dev_err(w->dapm->dev, "ASoC: no valid widget read method\n"); - return -1; -} - -static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, - unsigned int val) -{ - if (w->codec) - return snd_soc_write(w->codec, reg, val); - else if (w->platform) - return snd_soc_platform_write(w->platform, reg, val); - - dev_err(w->dapm->dev, "ASoC: no valid widget write method\n"); - return -1; -} - -static inline void soc_widget_lock(struct snd_soc_dapm_widget *w) -{ - if (w->codec && !w->codec->using_regmap) - mutex_lock(&w->codec->mutex); - else if (w->platform) - mutex_lock(&w->platform->mutex); + if (!w->dapm->component) + return -EIO; + return snd_soc_component_read(w->dapm->component, reg, value); } -static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w) +static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, + int reg, unsigned int mask, unsigned int value) { - if (w->codec && !w->codec->using_regmap) - mutex_unlock(&w->codec->mutex); - else if (w->platform) - mutex_unlock(&w->platform->mutex); + if (!w->dapm->component) + return -EIO; + return snd_soc_component_update_bits_async(w->dapm->component, reg, + mask, value); } static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) { - if (dapm->codec && dapm->codec->using_regmap) - regmap_async_complete(dapm->codec->control_data); -} - -static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, - int reg, unsigned int mask, unsigned int value) -{ - bool change; - unsigned int old, new; - int ret; - - if (w->codec && w->codec->using_regmap) { - ret = regmap_update_bits_check_async(w->codec->control_data, - reg, mask, value, - &change); - if (ret != 0) - return ret; - } else { - soc_widget_lock(w); - ret = soc_widget_read(w, reg, &old); - if (ret < 0) { - soc_widget_unlock(w); - return ret; - } - - new = (old & ~mask) | (value & mask); - change = old != new; - if (change) { - ret = soc_widget_write(w, reg, new); - if (ret < 0) { - soc_widget_unlock(w); - return ret; - } - } - soc_widget_unlock(w); - } - - return change; + if (dapm->component) + snd_soc_component_async_complete(dapm->component); } /** diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index dc0c09d2682a..ac64fd7252b2 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -19,24 +19,205 @@ #include -unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) +/** + * snd_soc_component_read() - Read register value + * @component: Component to read from + * @reg: Register to read + * @val: Pointer to where the read value is stored + * + * Return: 0 on success, a negative error code otherwise. + */ +int snd_soc_component_read(struct snd_soc_component *component, + unsigned int reg, unsigned int *val) +{ + int ret; + + if (component->regmap) + ret = regmap_read(component->regmap, reg, val); + else if (component->read) + ret = component->read(component, reg, val); + else + ret = -EIO; + + dev_dbg(component->dev, "read %x => %x\n", reg, *val); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_component_read); + +/** + * snd_soc_component_write() - Write register value + * @component: Component to write to + * @reg: Register to write + * @val: Value to write to the register + * + * Return: 0 on success, a negative error code otherwise. + */ +int snd_soc_component_write(struct snd_soc_component *component, + unsigned int reg, unsigned int val) { - unsigned int ret; + dev_dbg(component->dev, "write %x = %x\n", reg, val); - ret = codec->read(codec, reg); - dev_dbg(codec->dev, "read %x => %x\n", reg, ret); - trace_snd_soc_reg_read(codec, reg, ret); + if (component->regmap) + return regmap_write(component->regmap, reg, val); + else if (component->write) + return component->write(component, reg, val); + else + return -EIO; +} +EXPORT_SYMBOL_GPL(snd_soc_component_write); + +static int snd_soc_component_update_bits_legacy( + struct snd_soc_component *component, unsigned int reg, + unsigned int mask, unsigned int val, bool *change) +{ + unsigned int old, new; + int ret; + + if (!component->read || !component->write) + return -EIO; + + mutex_lock(&component->io_mutex); + + ret = component->read(component, reg, &old); + if (ret < 0) + goto out_unlock; + + new = (old & ~mask) | (val & mask); + *change = old != new; + if (*change) + ret = component->write(component, reg, new); +out_unlock: + mutex_unlock(&component->io_mutex); return ret; } + +/** + * snd_soc_component_update_bits() - Perform read/modify/write cycle + * @component: Component to update + * @reg: Register to update + * @mask: Mask that specifies which bits to update + * @val: New value for the bits specified by mask + * + * Return: 1 if the operation was successful and the value of the register + * changed, 0 if the operation was successful, but the value did not change. + * Returns a negative error code otherwise. + */ +int snd_soc_component_update_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val) +{ + bool change; + int ret; + + if (component->regmap) + ret = regmap_update_bits_check(component->regmap, reg, mask, + val, &change); + else + ret = snd_soc_component_update_bits_legacy(component, reg, + mask, val, &change); + + if (ret < 0) + return ret; + return change; +} +EXPORT_SYMBOL_GPL(snd_soc_component_update_bits); + +/** + * snd_soc_component_update_bits_async() - Perform asynchronous + * read/modify/write cycle + * @component: Component to update + * @reg: Register to update + * @mask: Mask that specifies which bits to update + * @val: New value for the bits specified by mask + * + * This function is similar to snd_soc_component_update_bits(), but the update + * operation is scheduled asynchronously. This means it may not be completed + * when the function returns. To make sure that all scheduled updates have been + * completed snd_soc_component_async_complete() must be called. + * + * Return: 1 if the operation was successful and the value of the register + * changed, 0 if the operation was successful, but the value did not change. + * Returns a negative error code otherwise. + */ +int snd_soc_component_update_bits_async(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int val) +{ + bool change; + int ret; + + if (component->regmap) + ret = regmap_update_bits_check_async(component->regmap, reg, + mask, val, &change); + else + ret = snd_soc_component_update_bits_legacy(component, reg, + mask, val, &change); + + if (ret < 0) + return ret; + return change; +} +EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); + +/** + * snd_soc_component_async_complete() - Ensure asynchronous I/O has completed + * @component: Component for which to wait + * + * This function blocks until all asynchronous I/O which has previously been + * scheduled using snd_soc_component_update_bits_async() has completed. + */ +void snd_soc_component_async_complete(struct snd_soc_component *component) +{ + if (component->regmap) + regmap_async_complete(component->regmap); +} +EXPORT_SYMBOL_GPL(snd_soc_component_async_complete); + +/** + * snd_soc_component_test_bits - Test register for change + * @component: component + * @reg: Register to test + * @mask: Mask that specifies which bits to test + * @value: Value to test against + * + * Tests a register with a new value and checks if the new value is + * different from the old value. + * + * Return: 1 for change, otherwise 0. + */ +int snd_soc_component_test_bits(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int value) +{ + unsigned int old, new; + int ret; + + ret = snd_soc_component_read(component, reg, &old); + if (ret < 0) + return ret; + new = (old & ~mask) | value; + return old != new; +} +EXPORT_SYMBOL_GPL(snd_soc_component_test_bits); + +unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int val; + int ret; + + ret = snd_soc_component_read(&codec->component, reg, &val); + if (ret < 0) + return -1; + trace_snd_soc_reg_read(codec, reg, val); + + return val; +} EXPORT_SYMBOL_GPL(snd_soc_read); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { - dev_dbg(codec->dev, "write %x = %x\n", reg, val); trace_snd_soc_reg_write(codec, reg, val); - return codec->write(codec, reg, val); + return snd_soc_component_write(&codec->component, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_write); @@ -54,29 +235,8 @@ EXPORT_SYMBOL_GPL(snd_soc_write); int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value) { - bool change; - unsigned int old, new; - int ret; - - if (codec->using_regmap) { - ret = regmap_update_bits_check(codec->control_data, reg, - mask, value, &change); - } else { - ret = snd_soc_read(codec, reg); - if (ret < 0) - return ret; - - old = ret; - new = (old & ~mask) | (value & mask); - change = old != new; - if (change) - ret = snd_soc_write(codec, reg, new); - } - - if (ret < 0) - return ret; - - return change; + return snd_soc_component_update_bits(&codec->component, reg, mask, + value); } EXPORT_SYMBOL_GPL(snd_soc_update_bits); @@ -95,13 +255,8 @@ int snd_soc_update_bits_locked(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value) { - int change; - - mutex_lock(&codec->mutex); - change = snd_soc_update_bits(codec, reg, mask, value); - mutex_unlock(&codec->mutex); - - return change; + return snd_soc_component_update_bits(&codec->component, reg, mask, + value); } EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked); @@ -120,115 +275,58 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked); int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, unsigned int mask, unsigned int value) { - int change; - unsigned int old, new; - - old = snd_soc_read(codec, reg); - new = (old & ~mask) | value; - change = old != new; - - return change; + return snd_soc_component_test_bits(&codec->component, reg, mask, value); } EXPORT_SYMBOL_GPL(snd_soc_test_bits); int snd_soc_platform_read(struct snd_soc_platform *platform, unsigned int reg) { - unsigned int ret; + unsigned int val; + int ret; - if (!platform->driver->read) { - dev_err(platform->dev, "ASoC: platform has no read back\n"); + ret = snd_soc_component_read(&platform->component, reg, &val); + if (ret < 0) return -1; - } - ret = platform->driver->read(platform, reg); - dev_dbg(platform->dev, "read %x => %x\n", reg, ret); - trace_snd_soc_preg_read(platform, reg, ret); + trace_snd_soc_preg_read(platform, reg, val); - return ret; + return val; } EXPORT_SYMBOL_GPL(snd_soc_platform_read); int snd_soc_platform_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val) { - if (!platform->driver->write) { - dev_err(platform->dev, "ASoC: platform has no write back\n"); - return -1; - } - - dev_dbg(platform->dev, "write %x = %x\n", reg, val); trace_snd_soc_preg_write(platform, reg, val); - return platform->driver->write(platform, reg, val); + return snd_soc_component_write(&platform->component, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_platform_write); -#ifdef CONFIG_REGMAP -static int hw_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - return regmap_write(codec->control_data, reg, value); -} - -static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg) -{ - int ret; - unsigned int val; - - ret = regmap_read(codec->control_data, reg, &val); - if (ret == 0) - return val; - else - return -1; -} - /** - * snd_soc_codec_set_cache_io: Set up standard I/O functions. - * - * @codec: CODEC to configure. - * @map: Register map to write to + * snd_soc_component_init_io() - Initialize regmap IO * - * Register formats are frequently shared between many I2C and SPI - * devices. In order to promote code reuse the ASoC core provides - * some standard implementations of CODEC read and write operations - * which can be set up using this function. + * @component: component to initialize + * @regmap: regmap instance to use for IO operations * - * The caller is responsible for allocating and initialising the - * actual cache. - * - * Note that at present this code cannot be used by CODECs with - * volatile registers. + * Return: 0 on success, a negative error code otherwise */ -int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - struct regmap *regmap) +int snd_soc_component_init_io(struct snd_soc_component *component, + struct regmap *regmap) { int ret; if (!regmap) return -EINVAL; - /* Device has made its own regmap arrangements */ - codec->control_data = regmap; - - codec->write = hw_write; - codec->read = hw_read; - - ret = regmap_get_val_bytes(codec->control_data); + ret = regmap_get_val_bytes(regmap); /* Errors are legitimate for non-integer byte * multiples */ if (ret > 0) - codec->val_bytes = ret; + component->val_bytes = ret; - codec->using_regmap = true; + component->regmap = regmap; return 0; } -EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); -#else -int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - struct regmap *regmap) -{ - return -ENOTSUPP; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); -#endif +EXPORT_SYMBOL_GPL(snd_soc_component_init_io); -- cgit v1.2.3 From 111c0cf566777ebbe026228b72df95788e771831 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 22 Apr 2014 13:23:17 +0200 Subject: ASoC: Remove ASoC level IO tracing The ASoC framework is in the process of migrating all IO operations to regmap. regmap has its own more sophisticated tracing infrastructure for IO operations, which means that the ASoC level IO tracing becomes redundant, hence this patch removes them. There are still a handful of ASoC drivers left that do not use regmap yet, but hopefully the removal of the ASoC IO tracing will be an additional incentive to switch to regmap. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/trace/events/asoc.h | 92 --------------------------------------------- sound/soc/soc-io.c | 11 ------ 2 files changed, 103 deletions(-) (limited to 'include') diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 03996b2bb04f..c75c795a377b 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -11,102 +11,10 @@ struct snd_soc_jack; struct snd_soc_codec; -struct snd_soc_platform; struct snd_soc_card; struct snd_soc_dapm_widget; struct snd_soc_dapm_path; -/* - * Log register events - */ -DECLARE_EVENT_CLASS(snd_soc_reg, - - TP_PROTO(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val), - - TP_ARGS(codec, reg, val), - - TP_STRUCT__entry( - __string( name, codec->name ) - __field( int, id ) - __field( unsigned int, reg ) - __field( unsigned int, val ) - ), - - TP_fast_assign( - __assign_str(name, codec->name); - __entry->id = codec->id; - __entry->reg = reg; - __entry->val = val; - ), - - TP_printk("codec=%s.%d reg=%x val=%x", __get_str(name), - (int)__entry->id, (unsigned int)__entry->reg, - (unsigned int)__entry->val) -); - -DEFINE_EVENT(snd_soc_reg, snd_soc_reg_write, - - TP_PROTO(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val), - - TP_ARGS(codec, reg, val) - -); - -DEFINE_EVENT(snd_soc_reg, snd_soc_reg_read, - - TP_PROTO(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val), - - TP_ARGS(codec, reg, val) - -); - -DECLARE_EVENT_CLASS(snd_soc_preg, - - TP_PROTO(struct snd_soc_platform *platform, unsigned int reg, - unsigned int val), - - TP_ARGS(platform, reg, val), - - TP_STRUCT__entry( - __string( name, platform->name ) - __field( int, id ) - __field( unsigned int, reg ) - __field( unsigned int, val ) - ), - - TP_fast_assign( - __assign_str(name, platform->name); - __entry->id = platform->id; - __entry->reg = reg; - __entry->val = val; - ), - - TP_printk("platform=%s.%d reg=%x val=%x", __get_str(name), - (int)__entry->id, (unsigned int)__entry->reg, - (unsigned int)__entry->val) -); - -DEFINE_EVENT(snd_soc_preg, snd_soc_preg_write, - - TP_PROTO(struct snd_soc_platform *platform, unsigned int reg, - unsigned int val), - - TP_ARGS(platform, reg, val) - -); - -DEFINE_EVENT(snd_soc_preg, snd_soc_preg_read, - - TP_PROTO(struct snd_soc_platform *platform, unsigned int reg, - unsigned int val), - - TP_ARGS(platform, reg, val) - -); - DECLARE_EVENT_CLASS(snd_soc_card, TP_PROTO(struct snd_soc_card *card, int val), diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 2ead9edd0d63..7767fbd73eb7 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -17,8 +17,6 @@ #include #include -#include - /** * snd_soc_component_read() - Read register value * @component: Component to read from @@ -39,8 +37,6 @@ int snd_soc_component_read(struct snd_soc_component *component, else ret = -EIO; - dev_dbg(component->dev, "read %x => %x\n", reg, *val); - return ret; } EXPORT_SYMBOL_GPL(snd_soc_component_read); @@ -56,8 +52,6 @@ EXPORT_SYMBOL_GPL(snd_soc_component_read); int snd_soc_component_write(struct snd_soc_component *component, unsigned int reg, unsigned int val) { - dev_dbg(component->dev, "write %x = %x\n", reg, val); - if (component->regmap) return regmap_write(component->regmap, reg, val); else if (component->write) @@ -207,7 +201,6 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) ret = snd_soc_component_read(&codec->component, reg, &val); if (ret < 0) return -1; - trace_snd_soc_reg_read(codec, reg, val); return val; } @@ -216,7 +209,6 @@ EXPORT_SYMBOL_GPL(snd_soc_read); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { - trace_snd_soc_reg_write(codec, reg, val); return snd_soc_component_write(&codec->component, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_write); @@ -269,8 +261,6 @@ int snd_soc_platform_read(struct snd_soc_platform *platform, if (ret < 0) return -1; - trace_snd_soc_preg_read(platform, reg, val); - return val; } EXPORT_SYMBOL_GPL(snd_soc_platform_read); @@ -278,7 +268,6 @@ EXPORT_SYMBOL_GPL(snd_soc_platform_read); int snd_soc_platform_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val) { - trace_snd_soc_preg_write(platform, reg, val); return snd_soc_component_write(&platform->component, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_platform_write); -- cgit v1.2.3 From 907fe36a2cd572fe58d98be01457b945c47b996e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 22 Apr 2014 13:23:14 +0200 Subject: ASoC: Move standard kcontrol helpers to the component level After moving the IO layer inside ASoC to the component level we can now easily move the standard control helpers also to the component level. This allows to reuse the same standard helper control implementations for other components. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 20 ++++- sound/soc/soc-core.c | 221 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 156 insertions(+), 85 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index a4179e73369a..c0b65fc90783 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1249,6 +1249,22 @@ static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec) return snd_soc_component_is_active(&codec->component); } +/** + * snd_soc_kcontrol_component() - Returns the component that registered the + * control + * @kcontrol: The control for which to get the component + * + * Note: This function will work correctly if the control has been registered + * for a component. Either with snd_soc_add_codec_controls() or + * snd_soc_add_platform_controls() or via table based setup for either a + * CODEC, a platform or component driver. Otherwise the behavior is undefined. + */ +static inline struct snd_soc_component *snd_soc_kcontrol_component( + struct snd_kcontrol *kcontrol) +{ + return snd_kcontrol_chip(kcontrol); +} + /** * snd_soc_kcontrol_codec() - Returns the CODEC that registered the control * @kcontrol: The control for which to get the CODEC @@ -1260,7 +1276,7 @@ static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec) static inline struct snd_soc_codec *snd_soc_kcontrol_codec( struct snd_kcontrol *kcontrol) { - return snd_kcontrol_chip(kcontrol); + return snd_soc_component_to_codec(snd_soc_kcontrol_component(kcontrol)); } /** @@ -1274,7 +1290,7 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec( static inline struct snd_soc_platform *snd_soc_kcontrol_platform( struct snd_kcontrol *kcontrol) { - return snd_kcontrol_chip(kcontrol); + return snd_soc_component_to_platform(snd_soc_kcontrol_component(kcontrol)); } int snd_soc_util_init(void); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index df3e2931cc0d..d5cd80b6d320 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2353,7 +2353,7 @@ int snd_soc_add_codec_controls(struct snd_soc_codec *codec, struct snd_card *card = codec->card->snd_card; return snd_soc_add_controls(card, codec->dev, controls, num_controls, - codec->name_prefix, codec); + codec->name_prefix, &codec->component); } EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls); @@ -2373,7 +2373,7 @@ int snd_soc_add_platform_controls(struct snd_soc_platform *platform, struct snd_card *card = platform->card->snd_card; return snd_soc_add_controls(card, platform->dev, controls, num_controls, - NULL, platform); + NULL, &platform->component); } EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls); @@ -2457,12 +2457,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, item; unsigned int reg_val; + int ret; - reg_val = snd_soc_read(codec, e->reg); + ret = snd_soc_component_read(component, e->reg, ®_val); + if (ret) + return ret; val = (reg_val >> e->shift_l) & e->mask; item = snd_soc_enum_val_to_item(e, val); ucontrol->value.enumerated.item[0] = item; @@ -2488,7 +2491,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_enum_double); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; unsigned int val; @@ -2505,38 +2508,48 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - return snd_soc_update_bits_locked(codec, e->reg, mask, val); + return snd_soc_component_update_bits(component, e->reg, mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); /** * snd_soc_read_signed - Read a codec register and interprete as signed value - * @codec: codec + * @component: component * @reg: Register to read * @mask: Mask to use after shifting the register value * @shift: Right shift of register value * @sign_bit: Bit that describes if a number is negative or not. + * @signed_val: Pointer to where the read value should be stored * * This functions reads a codec register. The register value is shifted right * by 'shift' bits and masked with the given 'mask'. Afterwards it translates * the given registervalue into a signed integer if sign_bit is non-zero. * - * Returns the register value as signed int. + * Returns 0 on sucess, otherwise an error value */ -static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg, - unsigned int mask, unsigned int shift, unsigned int sign_bit) +static int snd_soc_read_signed(struct snd_soc_component *component, + unsigned int reg, unsigned int mask, unsigned int shift, + unsigned int sign_bit, int *signed_val) { int ret; unsigned int val; - val = (snd_soc_read(codec, reg) >> shift) & mask; + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return ret; + + val = (val >> shift) & mask; - if (!sign_bit) - return val; + if (!sign_bit) { + *signed_val = val; + return 0; + } /* non-negative number */ - if (!(val & BIT(sign_bit))) - return val; + if (!(val & BIT(sign_bit))) { + *signed_val = val; + return 0; + } ret = val; @@ -2548,7 +2561,9 @@ static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg, */ ret |= ~((int)(BIT(sign_bit) - 1)); - return ret; + *signed_val = ret; + + return 0; } /** @@ -2597,9 +2612,9 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw); int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -2609,25 +2624,32 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, int sign_bit = mc->sign_bit; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; + int val; + int ret; if (sign_bit) mask = BIT(sign_bit + 1) - 1; - ucontrol->value.integer.value[0] = snd_soc_read_signed(codec, reg, mask, - shift, sign_bit) - min; + ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = val - min; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; if (snd_soc_volsw_is_stereo(mc)) { if (reg == reg2) - ucontrol->value.integer.value[1] = - snd_soc_read_signed(codec, reg, mask, rshift, - sign_bit) - min; + ret = snd_soc_read_signed(component, reg, mask, rshift, + sign_bit, &val); else - ucontrol->value.integer.value[1] = - snd_soc_read_signed(codec, reg2, mask, shift, - sign_bit) - min; + ret = snd_soc_read_signed(component, reg2, mask, shift, + sign_bit, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[1] = val - min; if (invert) ucontrol->value.integer.value[1] = max - ucontrol->value.integer.value[1]; @@ -2650,9 +2672,9 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw); int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -2687,12 +2709,13 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, type_2r = true; } } - err = snd_soc_update_bits_locked(codec, reg, val_mask, val); + err = snd_soc_component_update_bits(component, reg, val_mask, val); if (err < 0) return err; if (type_2r) - err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2); + err = snd_soc_component_update_bits(component, reg2, val_mask, + val2); return err; } @@ -2711,10 +2734,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw); int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; @@ -2722,13 +2744,23 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, int max = mc->max; int min = mc->min; int mask = (1 << (fls(min + max) - 1)) - 1; + unsigned int val; + int ret; - ucontrol->value.integer.value[0] = - ((snd_soc_read(codec, reg) >> shift) - min) & mask; + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return ret; - if (snd_soc_volsw_is_stereo(mc)) - ucontrol->value.integer.value[1] = - ((snd_soc_read(codec, reg2) >> rshift) - min) & mask; + ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask; + + if (snd_soc_volsw_is_stereo(mc)) { + ret = snd_soc_component_read(component, reg2, &val); + if (ret < 0) + return ret; + + val = ((val >> rshift) - min) & mask; + ucontrol->value.integer.value[1] = val; + } return 0; } @@ -2746,7 +2778,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -2764,7 +2796,7 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, val = (ucontrol->value.integer.value[0] + min) & mask; val = val << shift; - err = snd_soc_update_bits_locked(codec, reg, val_mask, val); + err = snd_soc_component_update_bits(component, reg, val_mask, val); if (err < 0) return err; @@ -2773,10 +2805,10 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, val2 = (ucontrol->value.integer.value[1] + min) & mask; val2 = val2 << rshift; - if (snd_soc_update_bits_locked(codec, reg2, val_mask, val2)) - return err; + err = snd_soc_component_update_bits(component, reg2, val_mask, + val2); } - return 0; + return err; } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); @@ -2823,10 +2855,15 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; + unsigned int val; int min = mc->min; - int val = snd_soc_read(codec, reg); + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; ucontrol->value.integer.value[0] = ((signed char)(val & 0xff))-min; @@ -2850,7 +2887,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; int min = mc->min; unsigned int val; @@ -2858,7 +2895,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, val = (ucontrol->value.integer.value[0]+min) & 0xff; val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; - return snd_soc_update_bits_locked(codec, reg, 0xffff, val); + return snd_soc_component_update_bits(component, reg, 0xffff, val); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); @@ -2907,7 +2944,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int rreg = mc->rreg; unsigned int shift = mc->shift; @@ -2924,7 +2961,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, val_mask = mask << shift; val = val << shift; - ret = snd_soc_update_bits_locked(codec, reg, val_mask, val); + ret = snd_soc_component_update_bits(component, reg, val_mask, val); if (ret < 0) return ret; @@ -2935,7 +2972,8 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, val_mask = mask << shift; val = val << shift; - ret = snd_soc_update_bits_locked(codec, rreg, val_mask, val); + ret = snd_soc_component_update_bits(component, rreg, val_mask, + val); } return ret; @@ -2954,9 +2992,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int rreg = mc->rreg; unsigned int shift = mc->shift; @@ -2964,9 +3002,14 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; + unsigned int val; + int ret; - ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = (val >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -2974,8 +3017,11 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] - min; if (snd_soc_volsw_is_stereo(mc)) { - ucontrol->value.integer.value[1] = - (snd_soc_read(codec, rreg) >> shift) & mask; + ret = snd_soc_component_read(component, rreg, &val); + if (ret) + return ret; + + ucontrol->value.integer.value[1] = (val >> shift) & mask; if (invert) ucontrol->value.integer.value[1] = max - ucontrol->value.integer.value[1]; @@ -3029,11 +3075,11 @@ EXPORT_SYMBOL_GPL(snd_soc_limit_volume); int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = params->num_regs * codec->component.val_bytes; + uinfo->count = params->num_regs * component->val_bytes; return 0; } @@ -3042,20 +3088,20 @@ EXPORT_SYMBOL_GPL(snd_soc_bytes_info); int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); int ret; - if (codec->component.regmap) - ret = regmap_raw_read(codec->component.regmap, params->base, + if (component->regmap) + ret = regmap_raw_read(component->regmap, params->base, ucontrol->value.bytes.data, - params->num_regs * codec->component.val_bytes); + params->num_regs * component->val_bytes); else ret = -EINVAL; /* Hide any masked bytes to ensure consistent data reporting */ if (ret == 0 && params->mask) { - switch (codec->component.val_bytes) { + switch (component->val_bytes) { case 1: ucontrol->value.bytes.data[0] &= ~params->mask; break; @@ -3079,16 +3125,16 @@ EXPORT_SYMBOL_GPL(snd_soc_bytes_get); int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); int ret, len; unsigned int val, mask; void *data; - if (!codec->component.regmap) + if (!component->regmap) return -EINVAL; - len = params->num_regs * codec->component.val_bytes; + len = params->num_regs * component->val_bytes; data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); if (!data) @@ -3100,27 +3146,27 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, * copy. */ if (params->mask) { - ret = regmap_read(codec->component.regmap, params->base, &val); + ret = regmap_read(component->regmap, params->base, &val); if (ret != 0) goto out; val &= params->mask; - switch (codec->component.val_bytes) { + switch (component->val_bytes) { case 1: ((u8 *)data)[0] &= ~params->mask; ((u8 *)data)[0] |= val; break; case 2: mask = ~params->mask; - ret = regmap_parse_val(codec->component.regmap, + ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) goto out; ((u16 *)data)[0] &= mask; - ret = regmap_parse_val(codec->component.regmap, + ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) goto out; @@ -3129,14 +3175,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, break; case 4: mask = ~params->mask; - ret = regmap_parse_val(codec->component.regmap, + ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) goto out; ((u32 *)data)[0] &= mask; - ret = regmap_parse_val(codec->component.regmap, + ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) goto out; @@ -3149,7 +3195,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, } } - ret = regmap_raw_write(codec->component.regmap, params->base, + ret = regmap_raw_write(component->regmap, params->base, data, len); out: @@ -3200,24 +3246,27 @@ EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx); int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; - unsigned int regwshift = codec->component.val_bytes * BITS_PER_BYTE; + unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; unsigned int regwmask = (1<invert; unsigned long mask = (1UL<nbits)-1; long min = mc->min; long max = mc->max; long val = 0; - unsigned long regval; + unsigned int regval; unsigned int i; + int ret; for (i = 0; i < regcount; i++) { - regval = snd_soc_read(codec, regbase+i) & regwmask; - val |= regval << (regwshift*(regcount-i-1)); + ret = snd_soc_component_read(component, regbase+i, ®val); + if (ret) + return ret; + val |= (regval & regwmask) << (regwshift*(regcount-i-1)); } val &= mask; if (min < 0 && val > max) @@ -3246,12 +3295,12 @@ EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx); int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; - unsigned int regwshift = codec->component.val_bytes * BITS_PER_BYTE; + unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; unsigned int regwmask = (1<invert; unsigned long mask = (1UL<nbits)-1; @@ -3266,7 +3315,7 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, for (i = 0; i < regcount; i++) { regval = (val >> (regwshift*(regcount-i-1))) & regwmask; regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask; - err = snd_soc_update_bits_locked(codec, regbase+i, + err = snd_soc_component_update_bits(component, regbase+i, regmask, regval); if (err < 0) return err; @@ -3288,14 +3337,21 @@ EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx); int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int shift = mc->shift; unsigned int mask = 1 << shift; unsigned int invert = mc->invert != 0; - unsigned int val = snd_soc_read(codec, reg) & mask; + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret) + return ret; + + val &= mask; if (shift != 0 && val != 0) val = val >> shift; @@ -3318,9 +3374,9 @@ EXPORT_SYMBOL_GPL(snd_soc_get_strobe); int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int reg = mc->reg; unsigned int shift = mc->shift; unsigned int mask = 1 << shift; @@ -3330,12 +3386,11 @@ int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, unsigned int val2 = (strobe ^ invert) ? 0 : mask; int err; - err = snd_soc_update_bits_locked(codec, reg, mask, val1); + err = snd_soc_component_update_bits(component, reg, mask, val1); if (err < 0) return err; - err = snd_soc_update_bits_locked(codec, reg, mask, val2); - return err; + return snd_soc_component_update_bits(component, reg, mask, val2); } EXPORT_SYMBOL_GPL(snd_soc_put_strobe); -- cgit v1.2.3 From 8df4053f0532df8fe47d0434af51676b0fa65491 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:56 +0300 Subject: platform_data: edma: Be precise with the paRAM struct The edmacc_param struct should follow the layout of the paRAM area in the HW. Be explicit on the size of the fields (u32) and also mark the struct as packed to avoid any padding on non 32bit architectures. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- include/linux/platform_data/edma.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index f50821cb64be..923f8a3e4ce0 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -43,15 +43,15 @@ /* PaRAM slots are laid out like this */ struct edmacc_param { - unsigned int opt; - unsigned int src; - unsigned int a_b_cnt; - unsigned int dst; - unsigned int src_dst_bidx; - unsigned int link_bcntrld; - unsigned int src_dst_cidx; - unsigned int ccnt; -}; + u32 opt; + u32 src; + u32 a_b_cnt; + u32 dst; + u32 src_dst_bidx; + u32 link_bcntrld; + u32 src_dst_cidx; + u32 ccnt; +} __packed; /* fields in edmacc_param.opt */ #define SAM BIT(0) -- cgit v1.2.3 From c04ae71c9c264312a6f57d2665a79f7bbccf8758 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 8 Apr 2014 17:33:19 -0700 Subject: sched_clock: Remove deprecated setup_sched_clock() API Remove the 32-bit only setup_sched_clock() API now that all users have been converted to the 64-bit friendly sched_clock_register(). Signed-off-by: Stephen Boyd Signed-off-by: John Stultz --- include/linux/sched_clock.h | 1 - kernel/time/sched_clock.c | 13 ------------- 2 files changed, 14 deletions(-) (limited to 'include') diff --git a/include/linux/sched_clock.h b/include/linux/sched_clock.h index cddf0c2940b6..efa931c5cef1 100644 --- a/include/linux/sched_clock.h +++ b/include/linux/sched_clock.h @@ -14,7 +14,6 @@ extern void sched_clock_postinit(void); static inline void sched_clock_postinit(void) { } #endif -extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate); extern void sched_clock_register(u64 (*read)(void), int bits, unsigned long rate); diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c index 4d23dc4d8139..445106d2c729 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -49,13 +49,6 @@ static u64 notrace jiffy_sched_clock_read(void) return (u64)(jiffies - INITIAL_JIFFIES); } -static u32 __read_mostly (*read_sched_clock_32)(void); - -static u64 notrace read_sched_clock_32_wrapper(void) -{ - return read_sched_clock_32(); -} - static u64 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read; static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift) @@ -176,12 +169,6 @@ void __init sched_clock_register(u64 (*read)(void), int bits, pr_debug("Registered %pF as sched_clock source\n", read); } -void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) -{ - read_sched_clock_32 = read; - sched_clock_register(read_sched_clock_32_wrapper, bits, rate); -} - void __init sched_clock_postinit(void) { /* -- cgit v1.2.3 From a6c39cb4f71e61aff19d07e2d0b26bb6e3548fae Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 22 Apr 2014 15:09:05 -0600 Subject: fs/bio: remove bs paramater in biovec_create_pool bs is no longer used in biovec_create_pool since 9f060e2231ca96 ("block: Convert integrity to bvec_alloc_bs()") Signed-off-by: Fabian Frederick Cc: Jens Axboe Signed-off-by: Jens Axboe --- fs/bio-integrity.c | 2 +- fs/bio.c | 4 ++-- include/linux/bio.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c index 1c2ce0c87711..9e241063a616 100644 --- a/fs/bio-integrity.c +++ b/fs/bio-integrity.c @@ -617,7 +617,7 @@ int bioset_integrity_create(struct bio_set *bs, int pool_size) if (!bs->bio_integrity_pool) return -1; - bs->bvec_integrity_pool = biovec_create_pool(bs, pool_size); + bs->bvec_integrity_pool = biovec_create_pool(pool_size); if (!bs->bvec_integrity_pool) { mempool_destroy(bs->bio_integrity_pool); return -1; diff --git a/fs/bio.c b/fs/bio.c index 4c9c5095bacb..ca55d37436d6 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1861,7 +1861,7 @@ EXPORT_SYMBOL_GPL(bio_trim); * create memory pools for biovec's in a bio_set. * use the global biovec slabs created for general use. */ -mempool_t *biovec_create_pool(struct bio_set *bs, int pool_entries) +mempool_t *biovec_create_pool(int pool_entries) { struct biovec_slab *bp = bvec_slabs + BIOVEC_MAX_IDX; @@ -1924,7 +1924,7 @@ struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad) if (!bs->bio_pool) goto bad; - bs->bvec_pool = biovec_create_pool(bs, pool_size); + bs->bvec_pool = biovec_create_pool(pool_size); if (!bs->bvec_pool) goto bad; diff --git a/include/linux/bio.h b/include/linux/bio.h index bba550826921..5a645769f020 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -333,7 +333,7 @@ static inline struct bio *bio_next_split(struct bio *bio, int sectors, extern struct bio_set *bioset_create(unsigned int, unsigned int); extern void bioset_free(struct bio_set *); -extern mempool_t *biovec_create_pool(struct bio_set *bs, int pool_entries); +extern mempool_t *biovec_create_pool(int pool_entries); extern struct bio *bio_alloc_bioset(gfp_t, int, struct bio_set *); extern void bio_put(struct bio *); -- cgit v1.2.3 From 603fb42a66499ab353466c7afa3d38beea20a8a9 Mon Sep 17 00:00:00 2001 From: Sebastian Capella Date: Tue, 25 Mar 2014 01:20:29 +0100 Subject: ARM: 8011/1: ARM hibernation / suspend-to-disk Enable hibernation for ARM architectures and provide ARM architecture specific calls used during hibernation. The swsusp hibernation framework depends on the platform first having functional suspend/resume. Then, in order to enable hibernation on a given platform, a platform_hibernation_ops structure may need to be registered with the system in order to save/restore any SoC-specific / cpu specific state needing (re)init over a suspend-to-disk/resume-from-disk cycle. For example: - "secure" SoCs that have different sets of control registers and/or different CR reg access patterns. - SoCs with L2 caches as the activation sequence there is SoC-dependent; a full off-on cycle for L2 is not done by the hibernation support code. - SoCs requiring steps on wakeup _before_ the "generic" parts done by cpu_suspend / cpu_resume can work correctly. - SoCs having persistent state which is maintained during suspend and resume, but will be lost during the power off cycle after suspend-to-disk. This is a rebase/rework of Frank Hofmann's v5 hibernation patchset. Acked-by: Russ Dill Cc: "Rafael J. Wysocki" Signed-off-by: Sebastian Capella Acked-by: Pavel Machek Reviewed-by: Lorenzo Pieralisi [fixed duplicate virt_to_pfn() definition --rmk] Signed-off-by: Russell King --- arch/arm/Kconfig | 5 +++ arch/arm/kernel/Makefile | 1 + arch/arm/kernel/hibernate.c | 107 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/suspend.h | 2 + 4 files changed, 115 insertions(+) create mode 100644 arch/arm/kernel/hibernate.c (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ab438cb5af55..58506175a3ea 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2294,6 +2294,11 @@ config ARCH_SUSPEND_POSSIBLE config ARM_CPU_SUSPEND def_bool PM_SLEEP +config ARCH_HIBERNATION_POSSIBLE + bool + depends on MMU + default y if ARCH_SUSPEND_POSSIBLE + endmenu source "net/Kconfig" diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index a766bcbaf8ad..10f0464206a2 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o +obj-$(CONFIG_HIBERNATION) += hibernate.o obj-$(CONFIG_SMP) += smp.o ifdef CONFIG_MMU obj-$(CONFIG_SMP) += smp_tlb.o diff --git a/arch/arm/kernel/hibernate.c b/arch/arm/kernel/hibernate.c new file mode 100644 index 000000000000..bb8b79648643 --- /dev/null +++ b/arch/arm/kernel/hibernate.c @@ -0,0 +1,107 @@ +/* + * Hibernation support specific for ARM + * + * Derived from work on ARM hibernation support by: + * + * Ubuntu project, hibernation support for mach-dove + * Copyright (C) 2010 Nokia Corporation (Hiroshi Doyu) + * Copyright (C) 2010 Texas Instruments, Inc. (Teerth Reddy et al.) + * https://lkml.org/lkml/2010/6/18/4 + * https://lists.linux-foundation.org/pipermail/linux-pm/2010-June/027422.html + * https://patchwork.kernel.org/patch/96442/ + * + * Copyright (C) 2006 Rafael J. Wysocki + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include + +extern const void __nosave_begin, __nosave_end; + +int pfn_is_nosave(unsigned long pfn) +{ + unsigned long nosave_begin_pfn = virt_to_pfn(&__nosave_begin); + unsigned long nosave_end_pfn = virt_to_pfn(&__nosave_end - 1); + + return (pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn); +} + +void notrace save_processor_state(void) +{ + WARN_ON(num_online_cpus() != 1); + local_fiq_disable(); +} + +void notrace restore_processor_state(void) +{ + local_fiq_enable(); +} + +/* + * Snapshot kernel memory and reset the system. + * + * swsusp_save() is executed in the suspend finisher so that the CPU + * context pointer and memory are part of the saved image, which is + * required by the resume kernel image to restart execution from + * swsusp_arch_suspend(). + * + * soft_restart is not technically needed, but is used to get success + * returned from cpu_suspend. + * + * When soft reboot completes, the hibernation snapshot is written out. + */ +static int notrace arch_save_image(unsigned long unused) +{ + int ret; + + ret = swsusp_save(); + if (ret == 0) + soft_restart(virt_to_phys(cpu_resume)); + return ret; +} + +/* + * Save the current CPU state before suspend / poweroff. + */ +int notrace swsusp_arch_suspend(void) +{ + return cpu_suspend(0, arch_save_image); +} + +/* + * Restore page contents for physical pages that were in use during loading + * hibernation image. Switch to idmap_pgd so the physical page tables + * are overwritten with the same contents. + */ +static void notrace arch_restore_image(void *unused) +{ + struct pbe *pbe; + + cpu_switch_mm(idmap_pgd, &init_mm); + for (pbe = restore_pblist; pbe; pbe = pbe->next) + copy_page(pbe->orig_address, pbe->address); + + soft_restart(virt_to_phys(cpu_resume)); +} + +static u64 resume_stack[PAGE_SIZE/2/sizeof(u64)] __nosavedata; + +/* + * Resume from the hibernation image. + * Due to the kernel heap / data restore, stack contents change underneath + * and that would make function calls impossible; switch to a temporary + * stack within the nosave region to avoid that problem. + */ +int swsusp_arch_resume(void) +{ + extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); + call_with_stack(arch_restore_image, 0, + resume_stack + ARRAY_SIZE(resume_stack)); + return 0; +} diff --git a/include/linux/suspend.h b/include/linux/suspend.h index f73cabf59012..38bbf95109da 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -320,6 +320,8 @@ extern unsigned long get_safe_page(gfp_t gfp_mask); extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); extern int hibernate(void); extern bool system_entering_hibernation(void); +asmlinkage int swsusp_save(void); +extern struct pbe *restore_pblist; #else /* CONFIG_HIBERNATION */ static inline void register_nosave_region(unsigned long b, unsigned long e) {} static inline void register_nosave_region_late(unsigned long b, unsigned long e) {} -- cgit v1.2.3 From 3de0b592394d17b2c41a261a6a493a521213f299 Mon Sep 17 00:00:00 2001 From: Venkata Duvvuru Date: Mon, 21 Apr 2014 15:37:59 +0530 Subject: ethtool: Support for configurable RSS hash key This ethtool patch primarily copies the ioctl command data structures from/to the User space and invokes the driver hook. Signed-off-by: Venkat Duvvuru Signed-off-by: David S. Miller --- include/linux/ethtool.h | 13 +++ include/uapi/linux/ethtool.h | 32 +++++++ net/core/ethtool.c | 221 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 252 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 0a114d05f68d..212f537fc686 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -154,13 +154,23 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings) * @reset: Reset (part of) the device, as specified by a bitmask of * flags from &enum ethtool_reset_flags. Returns a negative * error code or zero. + * @get_rxfh_key_size: Get the size of the RX flow hash key. + * Returns zero if not supported for this specific device. * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table. * Returns zero if not supported for this specific device. * @get_rxfh_indir: Get the contents of the RX flow hash indirection table. * Will not be called if @get_rxfh_indir_size returns zero. + * @get_rxfh: Get the contents of the RX flow hash indirection table and hash + * key. + * Will not be called if @get_rxfh_indir_size and @get_rxfh_key_size + * returns zero. * Returns a negative error code or zero. * @set_rxfh_indir: Set the contents of the RX flow hash indirection table. * Will not be called if @get_rxfh_indir_size returns zero. + * @set_rxfh: Set the contents of the RX flow hash indirection table and + * hash key. + * Will not be called if @get_rxfh_indir_size and @get_rxfh_key_size + * returns zero. * Returns a negative error code or zero. * @get_channels: Get number of channels. * @set_channels: Set number of channels. Returns a negative error code or @@ -232,7 +242,10 @@ struct ethtool_ops { int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); int (*flash_device)(struct net_device *, struct ethtool_flash *); int (*reset)(struct net_device *, u32 *); + u32 (*get_rxfh_key_size)(struct net_device *); u32 (*get_rxfh_indir_size)(struct net_device *); + int (*get_rxfh)(struct net_device *, u32 *, u8 *); + int (*set_rxfh)(struct net_device *, u32 *, u8 *); int (*get_rxfh_indir)(struct net_device *, u32 *); int (*set_rxfh_indir)(struct net_device *, const u32 *); void (*get_channels)(struct net_device *, struct ethtool_channels *); diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index fd161e91b6d7..d47d31d6fa0e 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -846,6 +846,35 @@ struct ethtool_rxfh_indir { __u32 ring_index[0]; }; +/** + * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key. + * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH + * @rss_context: RSS context identifier. + * @indir_size: On entry, the array size of the user buffer, which may be zero. + * On return from %ETHTOOL_GRSSH, the array size of the hardware + * indirection table. + * @key_size: On entry, the array size of the user buffer in bytes, + * which may be zero. + * On return from %ETHTOOL_GRSSH, the size of the RSS hash key. + * @rsvd: Reserved for future extensions. + * @rss_config: RX ring/queue index for each hash value i.e., indirection table + * of size @indir_size followed by hash key of size @key_size. + * + * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the + * size should be returned. For %ETHTOOL_SRSSH, a @indir_size of 0xDEADBEEF + * means that indir table setting is not requested and a @indir_size of zero + * means the indir table should be reset to default values. This last feature + * is not supported by the original implementations. + */ +struct ethtool_rxfh { + __u32 cmd; + __u32 rss_context; + __u32 indir_size; + __u32 key_size; + __u32 rsvd[2]; + __u32 rss_config[0]; +}; + /** * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW @@ -1118,6 +1147,9 @@ enum ethtool_sfeatures_retval_bits { #define ETHTOOL_GEEE 0x00000044 /* Get EEE settings */ #define ETHTOOL_SEEE 0x00000045 /* Set EEE settings */ +#define ETHTOOL_GRSSH 0x00000046 /* Get RX flow hash configuration */ +#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */ + /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET #define SPARC_ETH_SSET ETHTOOL_SSET diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 640ba0e5831c..1d72786ef866 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -557,6 +557,23 @@ err_out: return ret; } +static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr, + struct ethtool_rxnfc *rx_rings, + u32 size) +{ + int ret = 0, i; + + if (copy_from_user(indir, useraddr, size * sizeof(indir[0]))) + ret = -EFAULT; + + /* Validate ring indices */ + for (i = 0; i < size; i++) { + if (indir[i] >= rx_rings->data) + ret = -EINVAL; + } + return ret; +} + static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, void __user *useraddr) { @@ -613,6 +630,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, u32 *indir; const struct ethtool_ops *ops = dev->ethtool_ops; int ret; + u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]); if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir || !ops->get_rxnfc) @@ -643,28 +661,196 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, for (i = 0; i < dev_size; i++) indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); } else { - if (copy_from_user(indir, - useraddr + - offsetof(struct ethtool_rxfh_indir, - ring_index[0]), - dev_size * sizeof(indir[0]))) { + ret = ethtool_copy_validate_indir(indir, + useraddr + ringidx_offset, + &rx_rings, + dev_size); + if (ret) + goto out; + } + + ret = ops->set_rxfh_indir(dev, indir); + +out: + kfree(indir); + return ret; +} + +static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, + void __user *useraddr) +{ + int ret; + const struct ethtool_ops *ops = dev->ethtool_ops; + u32 user_indir_size = 0, user_key_size = 0; + u32 dev_indir_size = 0, dev_key_size = 0; + u32 total_size; + u32 indir_offset, indir_bytes; + u32 key_offset; + u32 *indir = NULL; + u8 *hkey = NULL; + u8 *rss_config; + + if (!(dev->ethtool_ops->get_rxfh_indir_size || + dev->ethtool_ops->get_rxfh_key_size) || + !dev->ethtool_ops->get_rxfh) + return -EOPNOTSUPP; + + if (ops->get_rxfh_indir_size) + dev_indir_size = ops->get_rxfh_indir_size(dev); + + indir_offset = offsetof(struct ethtool_rxfh, indir_size); + + if (copy_from_user(&user_indir_size, + useraddr + indir_offset, + sizeof(user_indir_size))) + return -EFAULT; + + if (copy_to_user(useraddr + indir_offset, + &dev_indir_size, sizeof(dev_indir_size))) + return -EFAULT; + + if (ops->get_rxfh_key_size) + dev_key_size = ops->get_rxfh_key_size(dev); + + if ((dev_key_size + dev_indir_size) == 0) + return -EOPNOTSUPP; + + key_offset = offsetof(struct ethtool_rxfh, key_size); + + if (copy_from_user(&user_key_size, + useraddr + key_offset, + sizeof(user_key_size))) + return -EFAULT; + + if (copy_to_user(useraddr + key_offset, + &dev_key_size, sizeof(dev_key_size))) + return -EFAULT; + + /* If the user buffer size is 0, this is just a query for the + * device table size and key size. Otherwise, if the User size is + * not equal to device table size or key size it's an error. + */ + if (!user_indir_size && !user_key_size) + return 0; + + if ((user_indir_size && (user_indir_size != dev_indir_size)) || + (user_key_size && (user_key_size != dev_key_size))) + return -EINVAL; + + indir_bytes = user_indir_size * sizeof(indir[0]); + total_size = indir_bytes + user_key_size; + rss_config = kzalloc(total_size, GFP_USER); + if (!rss_config) + return -ENOMEM; + + if (user_indir_size) + indir = (u32 *)rss_config; + + if (user_key_size) + hkey = rss_config + indir_bytes; + + ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey); + if (!ret) { + if (copy_to_user(useraddr + + offsetof(struct ethtool_rxfh, rss_config[0]), + rss_config, total_size)) ret = -EFAULT; + } + + kfree(rss_config); + + return ret; +} + +static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, + void __user *useraddr) +{ + int ret; + const struct ethtool_ops *ops = dev->ethtool_ops; + struct ethtool_rxnfc rx_rings; + u32 user_indir_size = 0, dev_indir_size = 0, i; + u32 user_key_size = 0, dev_key_size = 0; + u32 *indir = NULL, indir_bytes = 0; + u8 *hkey = NULL; + u8 *rss_config; + u32 indir_offset, key_offset; + u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); + + if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) || + !ops->get_rxnfc || !ops->set_rxfh) + return -EOPNOTSUPP; + + if (ops->get_rxfh_indir_size) + dev_indir_size = ops->get_rxfh_indir_size(dev); + + indir_offset = offsetof(struct ethtool_rxfh, indir_size); + if (copy_from_user(&user_indir_size, + useraddr + indir_offset, + sizeof(user_indir_size))) + return -EFAULT; + + if (ops->get_rxfh_key_size) + dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev); + + if ((dev_key_size + dev_indir_size) == 0) + return -EOPNOTSUPP; + + key_offset = offsetof(struct ethtool_rxfh, key_size); + if (copy_from_user(&user_key_size, + useraddr + key_offset, + sizeof(user_key_size))) + return -EFAULT; + + /* If either indir or hash key is valid, proceed further. + */ + if ((user_indir_size && ((user_indir_size != 0xDEADBEEF) && + user_indir_size != dev_indir_size)) || + (user_key_size && (user_key_size != dev_key_size))) + return -EINVAL; + + if (user_indir_size != 0xDEADBEEF) + indir_bytes = dev_indir_size * sizeof(indir[0]); + + rss_config = kzalloc(indir_bytes + user_key_size, GFP_USER); + if (!rss_config) + return -ENOMEM; + + rx_rings.cmd = ETHTOOL_GRXRINGS; + ret = ops->get_rxnfc(dev, &rx_rings, NULL); + if (ret) + goto out; + + /* user_indir_size == 0 means reset the indir table to default. + * user_indir_size == 0xDEADBEEF means indir setting is not requested. + */ + if (user_indir_size && user_indir_size != 0xDEADBEEF) { + indir = (u32 *)rss_config; + ret = ethtool_copy_validate_indir(indir, + useraddr + rss_cfg_offset, + &rx_rings, + user_indir_size); + if (ret) goto out; - } + } else if (user_indir_size == 0) { + indir = (u32 *)rss_config; + for (i = 0; i < dev_indir_size; i++) + indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); + } - /* Validate ring indices */ - for (i = 0; i < dev_size; i++) { - if (indir[i] >= rx_rings.data) { - ret = -EINVAL; - goto out; - } + if (user_key_size) { + hkey = rss_config + indir_bytes; + if (copy_from_user(hkey, + useraddr + rss_cfg_offset + indir_bytes, + user_key_size)) { + ret = -EFAULT; + goto out; } } - ret = ops->set_rxfh_indir(dev, indir); + ret = ops->set_rxfh(dev, indir, hkey); out: - kfree(indir); + kfree(rss_config); return ret; } @@ -1491,6 +1677,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GRXCLSRULE: case ETHTOOL_GRXCLSRLALL: case ETHTOOL_GRXFHINDIR: + case ETHTOOL_GRSSH: case ETHTOOL_GFEATURES: case ETHTOOL_GCHANNELS: case ETHTOOL_GET_TS_INFO: @@ -1628,6 +1815,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SRXFHINDIR: rc = ethtool_set_rxfh_indir(dev, useraddr); break; + case ETHTOOL_GRSSH: + rc = ethtool_get_rxfh(dev, useraddr); + break; + case ETHTOOL_SRSSH: + rc = ethtool_set_rxfh(dev, useraddr); + break; case ETHTOOL_GFEATURES: rc = ethtool_get_features(dev, useraddr); break; -- cgit v1.2.3 From 4cd3675ebf74d7f559038ded6aa8088e4099a83d Mon Sep 17 00:00:00 2001 From: Chema Gonzalez Date: Mon, 21 Apr 2014 09:21:24 -0700 Subject: filter: added BPF random opcode Added a new ancillary load (bpf call in eBPF parlance) that produces a 32-bit random number. We are implementing it as an ancillary load (instead of an ISA opcode) because (a) it is simpler, (b) allows easy JITing, and (c) seems more in line with generic ISAs that do not have "get a random number" as a instruction, but as an OS call. The main use for this ancillary load is to perform random packet sampling. Signed-off-by: Chema Gonzalez Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- Documentation/networking/filter.txt | 13 +++++++++++++ include/linux/filter.h | 1 + include/uapi/linux/filter.h | 3 ++- net/core/filter.c | 12 ++++++++++++ tools/net/bpf_exp.l | 1 + tools/net/bpf_exp.y | 11 ++++++++++- 6 files changed, 39 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index 81f940f4e884..82e1cb0b3da8 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -281,6 +281,7 @@ Possible BPF extensions are shown in the following table: cpu raw_smp_processor_id() vlan_tci vlan_tx_tag_get(skb) vlan_pr vlan_tx_tag_present(skb) + rand prandom_u32() These extensions can also be prefixed with '#'. Examples for low-level BPF: @@ -308,6 +309,18 @@ Examples for low-level BPF: ret #-1 drop: ret #0 +** icmp random packet sampling, 1 in 4 + ldh [12] + jne #0x800, drop + ldb [23] + jneq #1, drop + # get a random uint32 number + ld rand + mod #4 + jneq #1, drop + ret #-1 + drop: ret #0 + ** SECCOMP filter example: ld [4] /* offsetof(struct seccomp_data, arch) */ diff --git a/include/linux/filter.h b/include/linux/filter.h index 024fd03e5d18..759abf78dd61 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -223,6 +223,7 @@ enum { BPF_S_ANC_VLAN_TAG, BPF_S_ANC_VLAN_TAG_PRESENT, BPF_S_ANC_PAY_OFFSET, + BPF_S_ANC_RANDOM, }; #endif /* __LINUX_FILTER_H__ */ diff --git a/include/uapi/linux/filter.h b/include/uapi/linux/filter.h index 8eb9ccaa5b48..253b4d42cf2b 100644 --- a/include/uapi/linux/filter.h +++ b/include/uapi/linux/filter.h @@ -130,7 +130,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ #define SKF_AD_VLAN_TAG 44 #define SKF_AD_VLAN_TAG_PRESENT 48 #define SKF_AD_PAY_OFFSET 52 -#define SKF_AD_MAX 56 +#define SKF_AD_RANDOM 56 +#define SKF_AD_MAX 60 #define SKF_NET_OFF (-0x100000) #define SKF_LL_OFF (-0x200000) diff --git a/net/core/filter.c b/net/core/filter.c index cd58614660cf..78a636e60a0b 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -643,6 +643,12 @@ static u64 __get_raw_cpu_id(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) return raw_smp_processor_id(); } +/* note that this only generates 32-bit random numbers */ +static u64 __get_random_u32(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) +{ + return (u64)prandom_u32(); +} + /* Register mappings for user programs. */ #define A_REG 0 #define X_REG 7 @@ -779,6 +785,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_NLATTR: case SKF_AD_OFF + SKF_AD_NLATTR_NEST: case SKF_AD_OFF + SKF_AD_CPU: + case SKF_AD_OFF + SKF_AD_RANDOM: /* arg1 = ctx */ insn->code = BPF_ALU64 | BPF_MOV | BPF_X; insn->a_reg = ARG1_REG; @@ -812,6 +819,9 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_CPU: insn->imm = __get_raw_cpu_id - __bpf_call_base; break; + case SKF_AD_OFF + SKF_AD_RANDOM: + insn->imm = __get_random_u32 - __bpf_call_base; + break; } break; @@ -1362,6 +1372,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) ANCILLARY(VLAN_TAG); ANCILLARY(VLAN_TAG_PRESENT); ANCILLARY(PAY_OFFSET); + ANCILLARY(RANDOM); } /* ancillary operation unknown or unsupported */ @@ -1746,6 +1757,7 @@ void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to) [BPF_S_ANC_VLAN_TAG] = BPF_LD|BPF_B|BPF_ABS, [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS, [BPF_S_ANC_PAY_OFFSET] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_RANDOM] = BPF_LD|BPF_B|BPF_ABS, [BPF_S_LD_W_LEN] = BPF_LD|BPF_W|BPF_LEN, [BPF_S_LD_W_IND] = BPF_LD|BPF_W|BPF_IND, [BPF_S_LD_H_IND] = BPF_LD|BPF_H|BPF_IND, diff --git a/tools/net/bpf_exp.l b/tools/net/bpf_exp.l index bf7be77ddd62..833a96611da6 100644 --- a/tools/net/bpf_exp.l +++ b/tools/net/bpf_exp.l @@ -92,6 +92,7 @@ extern void yyerror(const char *str); "#"?("cpu") { return K_CPU; } "#"?("vlan_tci") { return K_VLANT; } "#"?("vlan_pr") { return K_VLANP; } +"#"?("rand") { return K_RAND; } ":" { return ':'; } "," { return ','; } diff --git a/tools/net/bpf_exp.y b/tools/net/bpf_exp.y index d15efc989ef5..e6306c51c26f 100644 --- a/tools/net/bpf_exp.y +++ b/tools/net/bpf_exp.y @@ -56,7 +56,7 @@ static void bpf_set_jmp_label(char *label, enum jmp_type type); %token OP_LDXI %token K_PKT_LEN K_PROTO K_TYPE K_NLATTR K_NLATTR_NEST K_MARK K_QUEUE K_HATYPE -%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF +%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF K_RAND %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%' @@ -164,6 +164,9 @@ ldb | OP_LDB K_POFF { bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_PAY_OFFSET); } + | OP_LDB K_RAND { + bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, + SKF_AD_OFF + SKF_AD_RANDOM); } ; ldh @@ -212,6 +215,9 @@ ldh | OP_LDH K_POFF { bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_PAY_OFFSET); } + | OP_LDH K_RAND { + bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, + SKF_AD_OFF + SKF_AD_RANDOM); } ; ldi @@ -265,6 +271,9 @@ ld | OP_LD K_POFF { bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_PAY_OFFSET); } + | OP_LD K_RAND { + bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, + SKF_AD_OFF + SKF_AD_RANDOM); } | OP_LD 'M' '[' number ']' { bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); } | OP_LD '[' 'x' '+' number ']' { -- cgit v1.2.3 From 4f520900522fd596e336c07e9aafd5b7a9564235 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Tue, 22 Apr 2014 21:31:54 -0400 Subject: netlink: have netlink per-protocol bind function return an error code. Have the netlink per-protocol optional bind function return an int error code rather than void to signal a failure. This will enable netlink protocols to perform extra checks including capabilities and permissions verifications when updating memberships in multicast groups. In netlink_bind() and netlink_setsockopt() the call to the per-protocol bind function was moved above the multicast group update to prevent any access to the multicast socket groups before checking with the per-protocol bind function. This will enable the per-protocol bind function to be used to check permissions which could be denied before making them available, and to avoid the messy job of undoing the addition should the per-protocol bind function fail. The netfilter subsystem seems to be the only one currently using the per-protocol bind function. Signed-off-by: Richard Guy Briggs Signed-off-by: David S. Miller --- include/linux/netlink.h | 3 ++- net/netfilter/nfnetlink.c | 3 ++- net/netlink/af_netlink.c | 68 +++++++++++++++++++++++++++++++++-------------- net/netlink/af_netlink.h | 6 +++-- 4 files changed, 56 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index aad8eeaf416d..5146ce066498 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -45,7 +45,8 @@ struct netlink_kernel_cfg { unsigned int flags; void (*input)(struct sk_buff *skb); struct mutex *cb_mutex; - void (*bind)(int group); + int (*bind)(int group); + void (*unbind)(int group); bool (*compare)(struct net *net, struct sock *sk); }; diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 0df800a454ec..6e42dcfad40a 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -400,7 +400,7 @@ static void nfnetlink_rcv(struct sk_buff *skb) } #ifdef CONFIG_MODULES -static void nfnetlink_bind(int group) +static int nfnetlink_bind(int group) { const struct nfnetlink_subsystem *ss; int type = nfnl_group2type[group]; @@ -410,6 +410,7 @@ static void nfnetlink_bind(int group) rcu_read_unlock(); if (!ss) request_module("nfnetlink-subsys-%d", type); + return 0; } #endif diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 894cda0206bb..7e8d229bc010 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1206,7 +1206,8 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, struct module *module = NULL; struct mutex *cb_mutex; struct netlink_sock *nlk; - void (*bind)(int group); + int (*bind)(int group); + void (*unbind)(int group); int err = 0; sock->state = SS_UNCONNECTED; @@ -1232,6 +1233,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, err = -EPROTONOSUPPORT; cb_mutex = nl_table[protocol].cb_mutex; bind = nl_table[protocol].bind; + unbind = nl_table[protocol].unbind; netlink_unlock_table(); if (err < 0) @@ -1248,6 +1250,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, nlk = nlk_sk(sock->sk); nlk->module = module; nlk->netlink_bind = bind; + nlk->netlink_unbind = unbind; out: return err; @@ -1301,6 +1304,7 @@ static int netlink_release(struct socket *sock) kfree_rcu(old, rcu); nl_table[sk->sk_protocol].module = NULL; nl_table[sk->sk_protocol].bind = NULL; + nl_table[sk->sk_protocol].unbind = NULL; nl_table[sk->sk_protocol].flags = 0; nl_table[sk->sk_protocol].registered = 0; } @@ -1411,6 +1415,19 @@ static int netlink_realloc_groups(struct sock *sk) return err; } +static void netlink_unbind(int group, long unsigned int groups, + struct netlink_sock *nlk) +{ + int undo; + + if (!nlk->netlink_unbind) + return; + + for (undo = 0; undo < group; undo++) + if (test_bit(group, &groups)) + nlk->netlink_unbind(undo); +} + static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { @@ -1419,6 +1436,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, struct netlink_sock *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; int err; + long unsigned int groups = nladdr->nl_groups; if (addr_len < sizeof(struct sockaddr_nl)) return -EINVAL; @@ -1427,7 +1445,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, return -EINVAL; /* Only superuser is allowed to listen multicasts */ - if (nladdr->nl_groups) { + if (groups) { if (!netlink_capable(sock, NL_CFG_F_NONROOT_RECV)) return -EPERM; err = netlink_realloc_groups(sk); @@ -1435,37 +1453,45 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, return err; } - if (nlk->portid) { + if (nlk->portid) if (nladdr->nl_pid != nlk->portid) return -EINVAL; - } else { + + if (nlk->netlink_bind && groups) { + int group; + + for (group = 0; group < nlk->ngroups; group++) { + if (!test_bit(group, &groups)) + continue; + err = nlk->netlink_bind(group); + if (!err) + continue; + netlink_unbind(group, groups, nlk); + return err; + } + } + + if (!nlk->portid) { err = nladdr->nl_pid ? netlink_insert(sk, net, nladdr->nl_pid) : netlink_autobind(sock); - if (err) + if (err) { + netlink_unbind(nlk->ngroups - 1, groups, nlk); return err; + } } - if (!nladdr->nl_groups && (nlk->groups == NULL || !(u32)nlk->groups[0])) + if (!groups && (nlk->groups == NULL || !(u32)nlk->groups[0])) return 0; netlink_table_grab(); netlink_update_subscriptions(sk, nlk->subscriptions + - hweight32(nladdr->nl_groups) - + hweight32(groups) - hweight32(nlk->groups[0])); - nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; + nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | groups; netlink_update_listeners(sk); netlink_table_ungrab(); - if (nlk->netlink_bind && nlk->groups[0]) { - int i; - - for (i = 0; i < nlk->ngroups; i++) { - if (test_bit(i, nlk->groups)) - nlk->netlink_bind(i); - } - } - return 0; } @@ -2103,14 +2129,16 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, return err; if (!val || val - 1 >= nlk->ngroups) return -EINVAL; + if (nlk->netlink_bind) { + err = nlk->netlink_bind(val); + if (err) + return err; + } netlink_table_grab(); netlink_update_socket_mc(nlk, val, optname == NETLINK_ADD_MEMBERSHIP); netlink_table_ungrab(); - if (nlk->netlink_bind) - nlk->netlink_bind(val); - err = 0; break; } diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index ed13a790b00e..0b59d441f5b6 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -38,7 +38,8 @@ struct netlink_sock { struct mutex *cb_mutex; struct mutex cb_def_mutex; void (*netlink_rcv)(struct sk_buff *skb); - void (*netlink_bind)(int group); + int (*netlink_bind)(int group); + void (*netlink_unbind)(int group); struct module *module; #ifdef CONFIG_NETLINK_MMAP struct mutex pg_vec_lock; @@ -74,7 +75,8 @@ struct netlink_table { unsigned int groups; struct mutex *cb_mutex; struct module *module; - void (*bind)(int group); + int (*bind)(int group); + void (*unbind)(int group); bool (*compare)(struct net *net, struct sock *sock); int registered; }; -- cgit v1.2.3 From 3a101b8de0d39403b2c7e5c23fd0b005668acf48 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Tue, 22 Apr 2014 21:31:56 -0400 Subject: audit: add netlink audit protocol bind to check capabilities on multicast join Register a netlink per-protocol bind fuction for audit to check userspace process capabilities before allowing a multicast group connection. Signed-off-by: Richard Guy Briggs Signed-off-by: David S. Miller --- include/uapi/linux/capability.h | 7 ++++++- kernel/audit.c | 10 ++++++++++ security/selinux/include/classmap.h | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/capability.h b/include/uapi/linux/capability.h index 154dd6d3c8fe..12c37a197d24 100644 --- a/include/uapi/linux/capability.h +++ b/include/uapi/linux/capability.h @@ -347,7 +347,12 @@ struct vfs_cap_data { #define CAP_BLOCK_SUSPEND 36 -#define CAP_LAST_CAP CAP_BLOCK_SUSPEND +/* Allow reading the audit log via multicast netlink socket */ + +#define CAP_AUDIT_READ 37 + + +#define CAP_LAST_CAP CAP_AUDIT_READ #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) diff --git a/kernel/audit.c b/kernel/audit.c index 7c2893602d06..223cb746f141 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1076,10 +1076,20 @@ static void audit_receive(struct sk_buff *skb) mutex_unlock(&audit_cmd_mutex); } +/* Run custom bind function on netlink socket group connect or bind requests. */ +static int audit_bind(int group) +{ + if (!capable(CAP_AUDIT_READ)) + return -EPERM; + + return 0; +} + static int __net_init audit_net_init(struct net *net) { struct netlink_kernel_cfg cfg = { .input = audit_receive, + .bind = audit_bind, }; struct audit_net *aunet = net_generic(net, audit_net_id); diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 14d04e63b1f0..be491a74c1ed 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -147,7 +147,7 @@ struct security_class_mapping secclass_map[] = { { "peer", { "recv", NULL } }, { "capability2", { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", - NULL } }, + "audit_read", NULL } }, { "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "tun_socket", { COMMON_SOCK_PERMS, "attach_queue", NULL } }, -- cgit v1.2.3 From 451f921639fea4600dfb9ab2889332bdcc7b48d3 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Tue, 22 Apr 2014 21:31:57 -0400 Subject: audit: add netlink multicast group for log read Add a netlink multicast socket with one group to kaudit for "best-effort" delivery to read-only userspace clients such as systemd, in addition to the existing bidirectional unicast auditd userspace client. Currently, auditd is intended to use the CAP_AUDIT_CONTROL and CAP_AUDIT_WRITE capabilities, but actually uses CAP_NET_ADMIN. The CAP_AUDIT_READ capability is added for use by read-only AUDIT_NLGRP_READLOG netlink multicast group clients to the kaudit subsystem. This will safely give access to services such as systemd to consume audit logs while ensuring write access remains restricted for integrity. Signed-off-by: Richard Guy Briggs Signed-off-by: David S. Miller --- include/uapi/linux/audit.h | 8 ++++++++ kernel/audit.c | 51 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 11917f747cb4..dfa4c860ccef 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -373,6 +373,14 @@ enum { */ #define AUDIT_MESSAGE_TEXT_MAX 8560 +/* Multicast Netlink socket groups (default up to 32) */ +enum audit_nlgrps { + AUDIT_NLGRP_NONE, /* Group 0 not used */ + AUDIT_NLGRP_READLOG, /* "best effort" read only socket */ + __AUDIT_NLGRP_MAX +}; +#define AUDIT_NLGRP_MAX (__AUDIT_NLGRP_MAX - 1) + struct audit_status { __u32 mask; /* Bit mask for valid entries */ __u32 enabled; /* 1 = enabled, 0 = disabled */ diff --git a/kernel/audit.c b/kernel/audit.c index 223cb746f141..d272cc11dff4 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -423,6 +423,35 @@ static void kauditd_send_skb(struct sk_buff *skb) consume_skb(skb); } +/* + * kauditd_send_multicast_skb - send the skb to multicast userspace listeners + * + * This function doesn't consume an skb as might be expected since it has to + * copy it anyways. + */ +static void kauditd_send_multicast_skb(struct sk_buff *skb) +{ + struct sk_buff *copy; + struct audit_net *aunet = net_generic(&init_net, audit_net_id); + struct sock *sock = aunet->nlsk; + + /* + * The seemingly wasteful skb_copy() rather than bumping the refcount + * using skb_get() is necessary because non-standard mods are made to + * the skb by the original kaudit unicast socket send routine. The + * existing auditd daemon assumes this breakage. Fixing this would + * require co-ordinating a change in the established protocol between + * the kaudit kernel subsystem and the auditd userspace code. There is + * no reason for new multicast clients to continue with this + * non-compliance. + */ + copy = skb_copy(skb, GFP_KERNEL); + if (!copy) + return; + + nlmsg_multicast(sock, copy, 0, AUDIT_NLGRP_READLOG, GFP_KERNEL); +} + /* * flush_hold_queue - empty the hold queue if auditd appears * @@ -1090,6 +1119,8 @@ static int __net_init audit_net_init(struct net *net) struct netlink_kernel_cfg cfg = { .input = audit_receive, .bind = audit_bind, + .flags = NL_CFG_F_NONROOT_RECV, + .groups = AUDIT_NLGRP_MAX, }; struct audit_net *aunet = net_generic(net, audit_net_id); @@ -1911,10 +1942,10 @@ out: * audit_log_end - end one audit record * @ab: the audit_buffer * - * The netlink_* functions cannot be called inside an irq context, so - * the audit buffer is placed on a queue and a tasklet is scheduled to - * remove them from the queue outside the irq context. May be called in - * any context. + * netlink_unicast() cannot be called inside an irq context because it blocks + * (last arg, flags, is not set to MSG_DONTWAIT), so the audit buffer is placed + * on a queue and a tasklet is scheduled to remove them from the queue outside + * the irq context. May be called in any context. */ void audit_log_end(struct audit_buffer *ab) { @@ -1924,6 +1955,18 @@ void audit_log_end(struct audit_buffer *ab) audit_log_lost("rate limit exceeded"); } else { struct nlmsghdr *nlh = nlmsg_hdr(ab->skb); + + kauditd_send_multicast_skb(ab->skb); + + /* + * The original kaudit unicast socket sends up messages with + * nlmsg_len set to the payload length rather than the entire + * message length. This breaks the standard set by netlink. + * The existing auditd daemon assumes this breakage. Fixing + * this would require co-ordinating a change in the established + * protocol between the kaudit kernel subsystem and the auditd + * userspace code. + */ nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN; if (audit_pid) { -- cgit v1.2.3 From 2e71029e2c32ecd59a2e8f351517bfbbad42ac11 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 22 Apr 2014 21:48:30 +0900 Subject: xfrm: Remove useless xfrm_audit struct. Commit f1370cc4 "xfrm: Remove useless secid field from xfrm_audit." changed "struct xfrm_audit" to have either { audit_get_loginuid(current) / audit_get_sessionid(current) } or { INVALID_UID / -1 } pair. This means that we can represent "struct xfrm_audit" as "bool". This patch replaces "struct xfrm_audit" argument with "bool". Signed-off-by: Tetsuo Handa Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 42 +++++++++++++++++++----------------------- net/key/af_key.c | 30 +++++++----------------------- net/xfrm/xfrm_policy.c | 40 ++++++++++++++-------------------------- net/xfrm/xfrm_state.c | 34 ++++++++++++---------------------- net/xfrm/xfrm_user.c | 38 ++++++++------------------------------ 5 files changed, 60 insertions(+), 124 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 882889eb156b..721e9c3b11bd 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -691,12 +691,6 @@ struct xfrm_spi_skb_cb { #define XFRM_SPI_SKB_CB(__skb) ((struct xfrm_spi_skb_cb *)&((__skb)->cb[0])) -/* Audit Information */ -struct xfrm_audit { - kuid_t loginuid; - unsigned int sessionid; -}; - #ifdef CONFIG_AUDITSYSCALL static inline struct audit_buffer *xfrm_audit_start(const char *op) { @@ -712,22 +706,24 @@ static inline struct audit_buffer *xfrm_audit_start(const char *op) return audit_buf; } -static inline void xfrm_audit_helper_usrinfo(kuid_t auid, unsigned int ses, +static inline void xfrm_audit_helper_usrinfo(bool task_valid, struct audit_buffer *audit_buf) { - audit_log_format(audit_buf, " auid=%u ses=%u", - from_kuid(&init_user_ns, auid), ses); + const unsigned int auid = from_kuid(&init_user_ns, task_valid ? + audit_get_loginuid(current) : + INVALID_UID); + const unsigned int ses = task_valid ? audit_get_sessionid(current) : + (unsigned int) -1; + + audit_log_format(audit_buf, " auid=%u ses=%u", auid, ses); audit_log_task_context(audit_buf); } -void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, kuid_t auid, - unsigned int ses); -void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, kuid_t auid, - unsigned int ses); -void xfrm_audit_state_add(struct xfrm_state *x, int result, kuid_t auid, - unsigned int ses); -void xfrm_audit_state_delete(struct xfrm_state *x, int result, kuid_t auid, - unsigned int ses); +void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid); +void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, + bool task_valid); +void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid); +void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid); void xfrm_audit_state_replay_overflow(struct xfrm_state *x, struct sk_buff *skb); void xfrm_audit_state_replay(struct xfrm_state *x, struct sk_buff *skb, @@ -740,22 +736,22 @@ void xfrm_audit_state_icvfail(struct xfrm_state *x, struct sk_buff *skb, #else static inline void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int ses) + bool task_valid) { } static inline void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int ses) + bool task_valid) { } static inline void xfrm_audit_state_add(struct xfrm_state *x, int result, - kuid_t auid, unsigned int ses) + bool task_valid) { } static inline void xfrm_audit_state_delete(struct xfrm_state *x, int result, - kuid_t auid, unsigned int ses) + bool task_valid) { } @@ -1499,7 +1495,7 @@ struct xfrmk_spdinfo { struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq); int xfrm_state_delete(struct xfrm_state *x); -int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info); +int xfrm_state_flush(struct net *net, u8 proto, bool task_valid); void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); @@ -1594,7 +1590,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, int *err); struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, u32 id, int delete, int *err); -int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info); +int xfrm_policy_flush(struct net *net, u8 type, bool task_valid); u32 xfrm_get_acqseq(void); int verify_spi_info(u8 proto, u32 min, u32 max); int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); diff --git a/net/key/af_key.c b/net/key/af_key.c index d66ff72adefb..b47f8e542aae 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1476,9 +1476,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg else err = xfrm_state_update(x); - xfrm_audit_state_add(x, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current)); + xfrm_audit_state_add(x, err ? 0 : 1, true); if (err < 0) { x->km.state = XFRM_STATE_DEAD; @@ -1532,9 +1530,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_ c.event = XFRM_MSG_DELSA; km_state_notify(x, &c); out: - xfrm_audit_state_delete(x, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current)); + xfrm_audit_state_delete(x, err ? 0 : 1, true); xfrm_state_put(x); return err; @@ -1726,16 +1722,13 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m struct net *net = sock_net(sk); unsigned int proto; struct km_event c; - struct xfrm_audit audit_info; int err, err2; proto = pfkey_satype2proto(hdr->sadb_msg_satype); if (proto == 0) return -EINVAL; - audit_info.loginuid = audit_get_loginuid(current); - audit_info.sessionid = audit_get_sessionid(current); - err = xfrm_state_flush(net, proto, &audit_info); + err = xfrm_state_flush(net, proto, true); err2 = unicast_flush_resp(sk, hdr); if (err || err2) { if (err == -ESRCH) /* empty table - go quietly */ @@ -2287,9 +2280,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_ err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp, hdr->sadb_msg_type != SADB_X_SPDUPDATE); - xfrm_audit_policy_add(xp, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current)); + xfrm_audit_policy_add(xp, err ? 0 : 1, true); if (err) goto out; @@ -2371,9 +2362,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa if (xp == NULL) return -ENOENT; - xfrm_audit_policy_delete(xp, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current)); + xfrm_audit_policy_delete(xp, err ? 0 : 1, true); if (err) goto out; @@ -2621,9 +2610,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_ return -ENOENT; if (delete) { - xfrm_audit_policy_delete(xp, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current)); + xfrm_audit_policy_delete(xp, err ? 0 : 1, true); if (err) goto out; @@ -2732,12 +2719,9 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sad { struct net *net = sock_net(sk); struct km_event c; - struct xfrm_audit audit_info; int err, err2; - audit_info.loginuid = audit_get_loginuid(current); - audit_info.sessionid = audit_get_sessionid(current); - err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); + err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, true); err2 = unicast_flush_resp(sk, hdr); if (err || err2) { if (err == -ESRCH) /* empty table - old silent behavior */ diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index bd001b7062c0..375267d15c8f 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -769,7 +769,7 @@ EXPORT_SYMBOL(xfrm_policy_byid); #ifdef CONFIG_SECURITY_NETWORK_XFRM static inline int -xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info) +xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) { int dir, err = 0; @@ -783,9 +783,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi continue; err = security_xfrm_policy_delete(pol->security); if (err) { - xfrm_audit_policy_delete(pol, 0, - audit_info->loginuid, - audit_info->sessionid); + xfrm_audit_policy_delete(pol, 0, task_valid); return err; } } @@ -799,8 +797,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi pol->security); if (err) { xfrm_audit_policy_delete(pol, 0, - audit_info->loginuid, - audit_info->sessionid); + task_valid); return err; } } @@ -810,19 +807,19 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi } #else static inline int -xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info) +xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid) { return 0; } #endif -int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) +int xfrm_policy_flush(struct net *net, u8 type, bool task_valid) { int dir, err = 0, cnt = 0; write_lock_bh(&net->xfrm.xfrm_policy_lock); - err = xfrm_policy_flush_secctx_check(net, type, audit_info); + err = xfrm_policy_flush_secctx_check(net, type, task_valid); if (err) goto out; @@ -839,8 +836,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) write_unlock_bh(&net->xfrm.xfrm_policy_lock); cnt++; - xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, - audit_info->sessionid); + xfrm_audit_policy_delete(pol, 1, task_valid); xfrm_policy_kill(pol); @@ -859,9 +855,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) write_unlock_bh(&net->xfrm.xfrm_policy_lock); cnt++; - xfrm_audit_policy_delete(pol, 1, - audit_info->loginuid, - audit_info->sessionid); + xfrm_audit_policy_delete(pol, 1, task_valid); xfrm_policy_kill(pol); write_lock_bh(&net->xfrm.xfrm_policy_lock); @@ -2858,19 +2852,14 @@ out_byidx: static void xfrm_policy_fini(struct net *net) { - struct xfrm_audit audit_info; unsigned int sz; int dir; flush_work(&net->xfrm.policy_hash_work); #ifdef CONFIG_XFRM_SUB_POLICY - audit_info.loginuid = INVALID_UID; - audit_info.sessionid = (unsigned int)-1; - xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info); + xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false); #endif - audit_info.loginuid = INVALID_UID; - audit_info.sessionid = (unsigned int)-1; - xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); + xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false); WARN_ON(!list_empty(&net->xfrm.policy_all)); @@ -2985,15 +2974,14 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, } } -void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int sessionid) +void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SPD-add"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, audit_buf); + xfrm_audit_helper_usrinfo(task_valid, audit_buf); audit_log_format(audit_buf, " res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf); @@ -3001,14 +2989,14 @@ void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, EXPORT_SYMBOL_GPL(xfrm_audit_policy_add); void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - kuid_t auid, unsigned int sessionid) + bool task_valid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SPD-delete"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, audit_buf); + xfrm_audit_helper_usrinfo(task_valid, audit_buf); audit_log_format(audit_buf, " res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index d91312b5ceb0..0ab54134bb40 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -463,9 +463,7 @@ expired: if (!err) km_state_expired(x, 1, 0); - xfrm_audit_state_delete(x, err ? 0 : 1, - audit_get_loginuid(current), - audit_get_sessionid(current)); + xfrm_audit_state_delete(x, err ? 0 : 1, true); out: spin_unlock(&x->lock); @@ -562,7 +560,7 @@ EXPORT_SYMBOL(xfrm_state_delete); #ifdef CONFIG_SECURITY_NETWORK_XFRM static inline int -xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info) +xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid) { int i, err = 0; @@ -572,9 +570,7 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { if (xfrm_id_proto_match(x->id.proto, proto) && (err = security_xfrm_state_delete(x)) != 0) { - xfrm_audit_state_delete(x, 0, - audit_info->loginuid, - audit_info->sessionid); + xfrm_audit_state_delete(x, 0, task_valid); return err; } } @@ -584,18 +580,18 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi } #else static inline int -xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info) +xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid) { return 0; } #endif -int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info) +int xfrm_state_flush(struct net *net, u8 proto, bool task_valid) { int i, err = 0, cnt = 0; spin_lock_bh(&net->xfrm.xfrm_state_lock); - err = xfrm_state_flush_secctx_check(net, proto, audit_info); + err = xfrm_state_flush_secctx_check(net, proto, task_valid); if (err) goto out; @@ -611,8 +607,7 @@ restart: err = xfrm_state_delete(x); xfrm_audit_state_delete(x, err ? 0 : 1, - audit_info->loginuid, - audit_info->sessionid); + task_valid); xfrm_state_put(x); if (!err) cnt++; @@ -2126,13 +2121,10 @@ out_bydst: void xfrm_state_fini(struct net *net) { - struct xfrm_audit audit_info; unsigned int sz; flush_work(&net->xfrm.state_hash_work); - audit_info.loginuid = INVALID_UID; - audit_info.sessionid = (unsigned int)-1; - xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info); + xfrm_state_flush(net, IPSEC_PROTO_ANY, false); flush_work(&net->xfrm.state_gc_work); WARN_ON(!list_empty(&net->xfrm.state_all)); @@ -2195,30 +2187,28 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family, } } -void xfrm_audit_state_add(struct xfrm_state *x, int result, - kuid_t auid, unsigned int sessionid) +void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SAD-add"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, audit_buf); + xfrm_audit_helper_usrinfo(task_valid, audit_buf); xfrm_audit_helper_sainfo(x, audit_buf); audit_log_format(audit_buf, " res=%u", result); audit_log_end(audit_buf); } EXPORT_SYMBOL_GPL(xfrm_audit_state_add); -void xfrm_audit_state_delete(struct xfrm_state *x, int result, - kuid_t auid, unsigned int sessionid) +void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid) { struct audit_buffer *audit_buf; audit_buf = xfrm_audit_start("SAD-delete"); if (audit_buf == NULL) return; - xfrm_audit_helper_usrinfo(auid, sessionid, audit_buf); + xfrm_audit_helper_usrinfo(task_valid, audit_buf); xfrm_audit_helper_sainfo(x, audit_buf); audit_log_format(audit_buf, " res=%u", result); audit_log_end(audit_buf); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index d6409d927b82..3d4b4c464091 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -597,8 +597,6 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_state *x; int err; struct km_event c; - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); err = verify_newsa_info(p, attrs); if (err) @@ -614,7 +612,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, else err = xfrm_state_update(x); - xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid); + xfrm_audit_state_add(x, err ? 0 : 1, true); if (err < 0) { x->km.state = XFRM_STATE_DEAD; @@ -674,8 +672,6 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, int err = -ESRCH; struct km_event c; struct xfrm_usersa_id *p = nlmsg_data(nlh); - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); x = xfrm_user_state_lookup(net, p, attrs, &err); if (x == NULL) @@ -700,7 +696,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_notify(x, &c); out: - xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid); + xfrm_audit_state_delete(x, err ? 0 : 1, true); xfrm_state_put(x); return err; } @@ -1410,8 +1406,6 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, struct km_event c; int err; int excl; - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); err = verify_newpolicy_info(p); if (err) @@ -1430,7 +1424,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, * a type XFRM_MSG_UPDPOLICY - JHS */ excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; err = xfrm_policy_insert(p->dir, xp, excl); - xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid); + xfrm_audit_policy_add(xp, err ? 0 : 1, true); if (err) { security_xfrm_policy_free(xp->security); @@ -1667,10 +1661,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, NETLINK_CB(skb).portid); } } else { - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - - xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid); + xfrm_audit_policy_delete(xp, err ? 0 : 1, true); if (err != 0) goto out; @@ -1695,12 +1686,9 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct net *net = sock_net(skb->sk); struct km_event c; struct xfrm_usersa_flush *p = nlmsg_data(nlh); - struct xfrm_audit audit_info; int err; - audit_info.loginuid = audit_get_loginuid(current); - audit_info.sessionid = audit_get_sessionid(current); - err = xfrm_state_flush(net, p->proto, &audit_info); + err = xfrm_state_flush(net, p->proto, true); if (err) { if (err == -ESRCH) /* empty table */ return 0; @@ -1884,15 +1872,12 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, struct km_event c; u8 type = XFRM_POLICY_TYPE_MAIN; int err; - struct xfrm_audit audit_info; err = copy_from_user_policy_type(&type, attrs); if (err) return err; - audit_info.loginuid = audit_get_loginuid(current); - audit_info.sessionid = audit_get_sessionid(current); - err = xfrm_policy_flush(net, type, &audit_info); + err = xfrm_policy_flush(net, type, true); if (err) { if (err == -ESRCH) /* empty table */ return 0; @@ -1958,12 +1943,8 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, err = 0; if (up->hard) { - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - xfrm_policy_delete(xp, p->dir); - xfrm_audit_policy_delete(xp, 1, loginuid, sessionid); - + xfrm_audit_policy_delete(xp, 1, true); } else { // reset the timers here? WARN(1, "Dont know what to do with soft policy expire\n"); @@ -1999,11 +1980,8 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_expired(x, ue->hard, nlh->nlmsg_pid); if (ue->hard) { - kuid_t loginuid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - __xfrm_state_delete(x); - xfrm_audit_state_delete(x, 1, loginuid, sessionid); + xfrm_audit_state_delete(x, 1, true); } err = 0; out: -- cgit v1.2.3 From fc8fd40eb29a936cc689d0008863d39a67741c67 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 20:46:34 +0100 Subject: drm: Rip out totally bogus vga_switcheroo->can_switch locking So I just wanted to add a new field to struct drm_device and accidentally stumbled over something. According to comments dev->open_count is protected by dev->count_lock, but that's totally not the case. It's protected by drm_global_mutex. Unfortunately the vga switcheroo callbacks took this comment at face value. The problem is that we can't just take the drm_global_mutex because: - It would lead to a locking inversion with the driver load/unload paths. - It wouldn't actually protect anything, for that we'd need to wrap the entire vga switcheroo code in the drm_global_mutex. And I'm not sure whether that would actually solve anything. What we probably want is a try_to_grab_switcheroo reference kind of thing which is used in the driver's ->open callback. Then we could move all that ->can_switch madness into the vga switcheroo core where it really belongs. But since that would amount to real work take the easy way out and just add a comment. It's definitely not going to make anything worse since doing switcheroo state changes while restarting X just isn't recommended. Even though the delayed switching code does exactly that. v2: - Simplify the ->can_switch implementations more (Thierry) - Fix comment about the dev->open_count locking (Thierry) Cc: Thierry Reding Reviewed-by: Laurent Pinchart (v1) Reviewed-by: Thierry Reding Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 11 ++++++----- drivers/gpu/drm/nouveau/nouveau_vga.c | 11 ++++++----- drivers/gpu/drm/radeon/radeon_device.c | 11 ++++++----- include/drm/drmP.h | 2 +- 4 files changed, 19 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 96177eec0a0e..283ff06001bc 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1277,12 +1277,13 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - bool can_switch; - spin_lock(&dev->count_lock); - can_switch = (dev->open_count == 0); - spin_unlock(&dev->count_lock); - return can_switch; + /* + * FIXME: open_count is protected by drm_global_mutex but that would lead to + * locking inversion with the driver load path. And the access here is + * completely racy anyway. So don't bother with locking for now. + */ + return dev->open_count == 0; } static const struct vga_switcheroo_client_ops i915_switcheroo_ops = { diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index fb84da3cb50d..4f4c3fec6916 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -64,12 +64,13 @@ static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - bool can_switch; - spin_lock(&dev->count_lock); - can_switch = (dev->open_count == 0); - spin_unlock(&dev->count_lock); - return can_switch; + /* + * FIXME: open_count is protected by drm_global_mutex but that would lead to + * locking inversion with the driver load path. And the access here is + * completely racy anyway. So don't bother with locking for now. + */ + return dev->open_count == 0; } static const struct vga_switcheroo_client_ops diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 511fe26198e4..9aa1afd1786e 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1125,12 +1125,13 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero static bool radeon_switcheroo_can_switch(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); - bool can_switch; - spin_lock(&dev->count_lock); - can_switch = (dev->open_count == 0); - spin_unlock(&dev->count_lock); - return can_switch; + /* + * FIXME: open_count is protected by drm_global_mutex but that would lead to + * locking inversion with the driver load path. And the access here is + * completely racy anyway. So don't bother with locking for now. + */ + return dev->open_count == 0; } static const struct vga_switcheroo_client_ops radeon_switcheroo_ops = { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 9f1fb8d36b67..a20d882ca265 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1076,7 +1076,7 @@ struct drm_device { /** \name Usage Counters */ /*@{ */ - int open_count; /**< Outstanding files open */ + int open_count; /**< Outstanding files open, protected by drm_global_mutex. */ int buf_use; /**< Buffers in use -- cannot alloc */ atomic_t buf_alloc; /**< Buffer allocation in progress */ /*@} */ -- cgit v1.2.3 From 2177a2182f3f375f64d9938dd884895c3872c380 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 16 Dec 2013 11:21:06 +0100 Subject: drm: rename dev->count_lock to dev->buf_lock Since really that's all it protects - legacy horror stories in drm_bufs.c. Since I don't want to waste any more time on this I didn't bother to actually look at what it protects in there, but it's at least contained now. v2: Move the spurious hunk to the right patch (Thierry). Cc: Thierry Reding Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_bufs.c | 32 ++++++++++++++++---------------- drivers/gpu/drm/drm_stub.c | 2 +- include/drm/drmP.h | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index edec31fe3fed..ef7f0199a0f1 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -656,13 +656,13 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) DRM_DEBUG("zone invalid\n"); return -EINVAL; } - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (dev->buf_use) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); mutex_lock(&dev->struct_mutex); entry = &dma->bufs[order]; @@ -805,13 +805,13 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; total = PAGE_SIZE << page_order; - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (dev->buf_use) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); mutex_lock(&dev->struct_mutex); entry = &dma->bufs[order]; @@ -1015,13 +1015,13 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (dev->buf_use) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); mutex_lock(&dev->struct_mutex); entry = &dma->bufs[order]; @@ -1175,7 +1175,7 @@ int drm_addbufs(struct drm_device *dev, void *data, * \param arg pointer to a drm_buf_info structure. * \return zero on success or a negative number on failure. * - * Increments drm_device::buf_use while holding the drm_device::count_lock + * Increments drm_device::buf_use while holding the drm_device::buf_lock * lock, preventing of allocating more buffers after this call. Information * about each requested buffer is then copied into user space. */ @@ -1196,13 +1196,13 @@ int drm_infobufs(struct drm_device *dev, void *data, if (!dma) return -EINVAL; - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (atomic_read(&dev->buf_alloc)) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } ++dev->buf_use; /* Can't allocate more after this call */ - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { if (dma->bufs[i].buf_count) @@ -1381,13 +1381,13 @@ int drm_mapbufs(struct drm_device *dev, void *data, if (!dma) return -EINVAL; - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (atomic_read(&dev->buf_alloc)) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } dev->buf_use++; /* Can't allocate more after this call */ - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); if (request->count >= dma->buf_count) { if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP)) diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 4c24c3ac1efa..5394b201c3d0 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -569,7 +569,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, INIT_LIST_HEAD(&dev->maplist); INIT_LIST_HEAD(&dev->vblank_event_list); - spin_lock_init(&dev->count_lock); + spin_lock_init(&dev->buf_lock); spin_lock_init(&dev->event_lock); mutex_init(&dev->struct_mutex); mutex_init(&dev->ctxlist_mutex); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a20d882ca265..85682d959c7e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1069,7 +1069,6 @@ struct drm_device { /** \name Locks */ /*@{ */ - spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */ struct mutex struct_mutex; /**< For others */ struct mutex master_mutex; /**< For drm_minor::master and drm_file::is_master */ /*@} */ @@ -1077,6 +1076,7 @@ struct drm_device { /** \name Usage Counters */ /*@{ */ int open_count; /**< Outstanding files open, protected by drm_global_mutex. */ + spinlock_t buf_lock; /**< For drm_device::buf_use and a few other things. */ int buf_use; /**< Buffers in use -- cannot alloc */ atomic_t buf_alloc; /**< Buffer allocation in progress */ /*@} */ -- cgit v1.2.3 From 7c1a38e391745cca72627979e5e02924bed5b43d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 16 Dec 2013 11:21:15 +0100 Subject: drm/irq: track the irq installed in drm_irq_install in dev->irq To get rid of the dev->bus->get_irq callback we need to pass in the desired irq explicitly into drm_irq_install. To avoid having to do the same for drm_irq_unistall just track it internally. That leaves drivers with less room to botch things up. v2: Add the hunk lost in an earlier patch to this one (Thierry). v3: Fix up the totally fumbled logic in drm_irq_install and use the local variable consistently. Spotted by both Thierry and Laurent. Shame on me for failing to properly test the rebase version of this patch ... Cc: Thierry Reding Cc: Laurent Pinchart Reviewed-by: Thierry Reding Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 18 +++++++++++------- include/drm/drmP.h | 2 ++ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 589e865832cd..7cf407bbfed5 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -249,14 +249,16 @@ static inline int drm_dev_to_irq(struct drm_device *dev) */ int drm_irq_install(struct drm_device *dev) { - int ret; + int ret, irq; unsigned long sh_flags = 0; char *irqname; + irq = drm_dev_to_irq(dev); + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return -EINVAL; - if (drm_dev_to_irq(dev) == 0) + if (irq == 0) return -EINVAL; /* Driver must have been initialized */ @@ -267,7 +269,7 @@ int drm_irq_install(struct drm_device *dev) return -EBUSY; dev->irq_enabled = true; - DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev)); + DRM_DEBUG("irq=%d\n", irq); /* Before installing handler */ if (dev->driver->irq_preinstall) @@ -282,7 +284,7 @@ int drm_irq_install(struct drm_device *dev) else irqname = dev->driver->name; - ret = request_irq(drm_dev_to_irq(dev), dev->driver->irq_handler, + ret = request_irq(irq, dev->driver->irq_handler, sh_flags, irqname, dev); if (ret < 0) { @@ -301,7 +303,9 @@ int drm_irq_install(struct drm_device *dev) dev->irq_enabled = false; if (!drm_core_check_feature(dev, DRIVER_MODESET)) vga_client_register(dev->pdev, NULL, NULL, NULL); - free_irq(drm_dev_to_irq(dev), dev); + free_irq(irq, dev); + } else { + dev->irq = irq; } return ret; @@ -344,7 +348,7 @@ int drm_irq_uninstall(struct drm_device *dev) if (!irq_enabled) return -EINVAL; - DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev)); + DRM_DEBUG("irq=%d\n", dev->irq); if (!drm_core_check_feature(dev, DRIVER_MODESET)) vga_client_register(dev->pdev, NULL, NULL, NULL); @@ -352,7 +356,7 @@ int drm_irq_uninstall(struct drm_device *dev) if (dev->driver->irq_uninstall) dev->driver->irq_uninstall(dev); - free_irq(drm_dev_to_irq(dev), dev); + free_irq(dev->irq, dev); return 0; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 85682d959c7e..8e8c392d6fa8 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1107,6 +1107,8 @@ struct drm_device { /** \name Context support */ /*@{ */ bool irq_enabled; /**< True if irq handler is enabled */ + int irq; + __volatile__ long context_flag; /**< Context swapping flag */ int last_context; /**< Last current context */ /*@} */ -- cgit v1.2.3 From bb0f1b5c1695b4399cfd2359c114ae63edbb3ad8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 21:09:27 +0100 Subject: drm: pass the irq explicitly to drm_irq_install Unfortunately this requires a drm-wide change, and I didn't see a sane way around that. Luckily it's fairly simple, we just need to inline the respective get_irq implementation from either drm_pci.c or drm_platform.c. With that we can now also remove drm_dev_to_irq from drm_irq.c. Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 10 +--------- drivers/gpu/drm/armada/armada_drv.c | 2 +- drivers/gpu/drm/drm_irq.c | 13 +++---------- drivers/gpu/drm/gma500/psb_drv.c | 2 +- drivers/gpu/drm/i915/i915_dma.c | 2 +- drivers/gpu/drm/i915/i915_drv.c | 4 ++-- drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/msm/msm_drv.c | 2 +- drivers/gpu/drm/qxl/qxl_irq.c | 2 +- drivers/gpu/drm/radeon/radeon_irq_kms.c | 2 +- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 2 +- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 +- include/drm/drmP.h | 2 +- 14 files changed, 17 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 677a02553ec0..83dd0b043c28 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -341,14 +341,6 @@ char *date; Managed IRQ Registration - - Both the drm_irq_install and - drm_irq_uninstall functions get the device IRQ by - calling drm_dev_to_irq. This inline function will - call a bus-specific operation to retrieve the IRQ number. For platform - devices, platform_get_irq(..., 0) is used to - retrieve the IRQ number. - drm_irq_install starts by calling the irq_preinstall driver operation. The operation @@ -356,7 +348,7 @@ char *date; clearing all pending interrupt flags or disabling the interrupt. - The IRQ will then be requested by a call to + The passed-in IRQ will then be requested by a call to request_irq. If the DRIVER_IRQ_SHARED driver feature flag is set, a shared (IRQF_SHARED) IRQ handler will be requested. diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 32982da82694..567cfbde0883 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -173,7 +173,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (ret) goto err_kms; - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); if (ret) goto err_kms; diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index cbf6f259bfe4..de38bc9b6581 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -233,11 +233,6 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) } } -static inline int drm_dev_to_irq(struct drm_device *dev) -{ - return dev->driver->bus->get_irq(dev); -} - /** * Install IRQ handler. * @@ -247,14 +242,12 @@ static inline int drm_dev_to_irq(struct drm_device *dev) * \c irq_preinstall() and \c irq_postinstall() functions * before and after the installation. */ -int drm_irq_install(struct drm_device *dev) +int drm_irq_install(struct drm_device *dev, int irq) { - int ret, irq; + int ret; unsigned long sh_flags = 0; char *irqname; - irq = drm_dev_to_irq(dev); - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return -EINVAL; @@ -399,7 +392,7 @@ int drm_control(struct drm_device *dev, void *data, ctl->irq != irq) return -EINVAL; mutex_lock(&dev->struct_mutex); - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, irq); mutex_unlock(&dev->struct_mutex); return ret; diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index b686e56646eb..0a3101a3db19 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -354,7 +354,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R); spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - drm_irq_install(dev); + drm_irq_install(dev, dev->pdev->irq); dev->vblank_disable_allowed = true; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 283ff06001bc..42de2808e53d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1327,7 +1327,7 @@ static int i915_load_modeset_init(struct drm_device *dev) intel_power_domains_init_hw(dev_priv); - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, dev->pdev->irq); if (ret) goto cleanup_gem_stolen; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 87ce105910f2..6124b491a19e 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -574,7 +574,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) mutex_unlock(&dev->struct_mutex); /* We need working interrupts for modeset enabling ... */ - drm_irq_install(dev); + drm_irq_install(dev, dev->pdev->irq); intel_modeset_init_hw(dev); @@ -752,7 +752,7 @@ int i915_reset(struct drm_device *dev) * being false when they shouldn't be able to. */ drm_irq_uninstall(dev); - drm_irq_install(dev); + drm_irq_install(dev, dev->pdev->irq); /* rps/rc6 re-init is necessary to restore state lost after the * reset and the re-install of drm irq. Skip for ironlake per diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d2cc19a7023f..a94938f08698 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4523,7 +4523,7 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, BUG_ON(!list_empty(&dev_priv->gtt.base.active_list)); - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, dev->pdev->irq); if (ret) goto cleanup_ringbuffer; mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index f9de156b9e65..50ec1bed5820 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -288,7 +288,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) } pm_runtime_get_sync(dev->dev); - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); pm_runtime_put_sync(dev->dev); if (ret < 0) { dev_err(dev->dev, "failed to install IRQ handler\n"); diff --git a/drivers/gpu/drm/qxl/qxl_irq.c b/drivers/gpu/drm/qxl/qxl_irq.c index 28f84b4fce32..34d6a85e9023 100644 --- a/drivers/gpu/drm/qxl/qxl_irq.c +++ b/drivers/gpu/drm/qxl/qxl_irq.c @@ -87,7 +87,7 @@ int qxl_irq_init(struct qxl_device *qdev) atomic_set(&qdev->irq_received_cursor, 0); atomic_set(&qdev->irq_received_io_cmd, 0); qdev->irq_received_error = 0; - ret = drm_irq_install(qdev->ddev); + ret = drm_irq_install(qdev->ddev, qdev->ddev->pdev->irq); qdev->ram_header->int_mask = QXL_INTERRUPT_MASK; if (unlikely(ret != 0)) { DRM_ERROR("Failed installing irq: %d\n", ret); diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 089c9ffb0aa9..16807afab362 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -287,7 +287,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev) INIT_WORK(&rdev->reset_work, radeon_irq_reset_work_func); rdev->irq.installed = true; - r = drm_irq_install(rdev->ddev); + r = drm_irq_install(rdev->ddev, rdev->ddev->pdev->irq); if (r) { rdev->irq.installed = false; flush_work(&rdev->hotplug_work); diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index c839c9c89efb..82c84c7fd4f6 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -185,7 +185,7 @@ static int shmob_drm_load(struct drm_device *dev, unsigned long flags) goto done; } - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); if (ret < 0) { dev_err(&pdev->dev, "failed to install IRQ handler\n"); goto done; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 171a8203892c..b20b69488dc9 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -268,7 +268,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) } pm_runtime_get_sync(dev->dev); - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); pm_runtime_put_sync(dev->dev); if (ret < 0) { dev_err(dev->dev, "failed to install IRQ handler\n"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 4a223bbea3b3..6bdd15eea7e8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -806,7 +806,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) } if (dev_priv->capabilities & SVGA_CAP_IRQMASK) { - ret = drm_irq_install(dev); + ret = drm_irq_install(dev, dev->pdev->irq); if (ret != 0) { DRM_ERROR("Failed installing irq: %d\n", ret); goto out_no_irq; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8e8c392d6fa8..7a7cfe88b9bf 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1353,7 +1353,7 @@ extern void drm_core_reclaim_buffers(struct drm_device *dev, /* IRQ support (drm_irq.h) */ extern int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_irq_install(struct drm_device *dev); +extern int drm_irq_install(struct drm_device *dev, int irq); extern int drm_irq_uninstall(struct drm_device *dev); extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); -- cgit v1.2.3 From b2a21aa25a39837d06eb24a7f0fef1733f9843eb Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 21:13:09 +0100 Subject: drm: remove bus->get_irq implementations Now that they're all unused we can get rid of them, including the dummy version in drm_usb.c. Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_pci.c | 6 ------ drivers/gpu/drm/drm_platform.c | 6 ------ drivers/gpu/drm/drm_usb.c | 6 ------ include/drm/drmP.h | 1 - 4 files changed, 19 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 08c76af920d6..eff29e311f70 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -137,11 +137,6 @@ static int drm_get_pci_domain(struct drm_device *dev) return pci_domain_nr(dev->pdev->bus); } -static int drm_pci_get_irq(struct drm_device *dev) -{ - return dev->pdev->irq; -} - static const char *drm_pci_get_name(struct drm_device *dev) { struct pci_driver *pdriver = dev->driver->kdriver.pci; @@ -317,7 +312,6 @@ void drm_pci_agp_destroy(struct drm_device *dev) } static struct drm_bus drm_pci_bus = { - .get_irq = drm_pci_get_irq, .get_name = drm_pci_get_name, .set_busid = drm_pci_set_busid, .set_unique = drm_pci_set_unique, diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 5cd8c695824f..5b918212b1c3 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -68,11 +68,6 @@ err_free: return ret; } -static int drm_platform_get_irq(struct drm_device *dev) -{ - return platform_get_irq(dev->platformdev, 0); -} - static const char *drm_platform_get_name(struct drm_device *dev) { return dev->platformdev->name; @@ -123,7 +118,6 @@ err: } static struct drm_bus drm_platform_bus = { - .get_irq = drm_platform_get_irq, .get_name = drm_platform_get_name, .set_busid = drm_platform_set_busid, }; diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index ed60f887c805..abdc265c9cc8 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -36,11 +36,6 @@ err_free: } EXPORT_SYMBOL(drm_get_usb_dev); -static int drm_usb_get_irq(struct drm_device *dev) -{ - return 0; -} - static const char *drm_usb_get_name(struct drm_device *dev) { return "USB"; @@ -53,7 +48,6 @@ static int drm_usb_set_busid(struct drm_device *dev, } static struct drm_bus drm_usb_bus = { - .get_irq = drm_usb_get_irq, .get_name = drm_usb_get_name, .set_busid = drm_usb_set_busid, }; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 7a7cfe88b9bf..f4d0eaa3da46 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -726,7 +726,6 @@ struct drm_master { #define DRM_SCANOUTPOS_ACCURATE (1 << 2) struct drm_bus { - int (*get_irq)(struct drm_device *dev); const char *(*get_name)(struct drm_device *dev); int (*set_busid)(struct drm_device *dev, struct drm_master *master); int (*set_unique)(struct drm_device *dev, struct drm_master *master, -- cgit v1.2.3 From 53bf2a2bca9b56e23fa8862159c75868672d7f1e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 21:47:18 +0100 Subject: drm: inline drm_pci_set_unique This is only used for drm versions 1.0, and kms drivers have never been there. So we can appropriately restrict this to legacy and hence pci devices and inline everything. v2: Make the dummy function actually return something, caught by Wu Fengguang's 0-day tester. v3: Fix spelling in comment (Thierry) Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_ioctl.c | 10 +++++++--- drivers/gpu/drm/drm_pci.c | 14 ++++++++++---- include/drm/drmP.h | 5 +++-- 3 files changed, 20 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 93a42040bedb..53560ef53f99 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -93,7 +93,8 @@ drm_unset_busid(struct drm_device *dev, * Copies the bus id from userspace into drm_device::unique, and verifies that * it matches the device this DRM is attached to (EINVAL otherwise). Deprecated * in interface version 1.1 and will return EBUSY when setversion has requested - * version 1.1 or greater. + * version 1.1 or greater. Also note that KMS is all version 1.1 and later and + * UMS was only ever supported on pci devices. */ int drm_setunique(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -108,10 +109,13 @@ int drm_setunique(struct drm_device *dev, void *data, if (!u->unique_len || u->unique_len > 1024) return -EINVAL; - if (!dev->driver->bus->set_unique) + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + + if (WARN_ON(!dev->pdev)) return -EINVAL; - ret = dev->driver->bus->set_unique(dev, master, u); + ret = drm_pci_set_unique(dev, master, u); if (ret) goto err; diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index eff29e311f70..c550b349f202 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -185,9 +185,9 @@ err: return ret; } -static int drm_pci_set_unique(struct drm_device *dev, - struct drm_master *master, - struct drm_unique *u) +int drm_pci_set_unique(struct drm_device *dev, + struct drm_master *master, + struct drm_unique *u) { int domain, bus, slot, func, ret; const char *bus_name; @@ -314,7 +314,6 @@ void drm_pci_agp_destroy(struct drm_device *dev) static struct drm_bus drm_pci_bus = { .get_name = drm_pci_get_name, .set_busid = drm_pci_set_busid, - .set_unique = drm_pci_set_unique, }; /** @@ -481,6 +480,13 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, { return -EINVAL; } + +int drm_pci_set_unique(struct drm_device *dev, + struct drm_master *master, + struct drm_unique *u) +{ + return -EINVAL; +} #endif EXPORT_SYMBOL(drm_pci_init); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index f4d0eaa3da46..1a9d509bef66 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -728,8 +728,6 @@ struct drm_master { struct drm_bus { const char *(*get_name)(struct drm_device *dev); int (*set_busid)(struct drm_device *dev, struct drm_master *master); - int (*set_unique)(struct drm_device *dev, struct drm_master *master, - struct drm_unique *unique); }; /** @@ -1511,6 +1509,9 @@ extern drm_dma_handle_t *drm_pci_alloc(struct drm_device *dev, size_t size, size_t align); extern void __drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); extern void drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); +extern int drm_pci_set_unique(struct drm_device *dev, + struct drm_master *master, + struct drm_unique *u); /* sysfs support (drm_sysfs.c) */ struct drm_sysfs_class; -- cgit v1.2.3 From 5829d1834e5486e83547f36576b160023c9609c2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 21:48:48 +0100 Subject: drm: rip out dev->devname This was only ever used to pretty-print the irq driver name. And on kms systems due to set_version bonghits we never set up the prettier name, ever. Which make this a bit pointless. Also, we can always dig out the driver-instance/irq relationship through other means, so this isn't that useful. So just rip it out to simplify the set_version/set_busid insanity a bit. Also delete the temporary busname from drm_pci_set_busid, it's now unused. v2: Rebase on top of the new host1x drm_bus for tegra. Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_ioctl.c | 3 --- drivers/gpu/drm/drm_irq.c | 8 +------- drivers/gpu/drm/drm_pci.c | 25 ------------------------- drivers/gpu/drm/drm_platform.c | 11 ----------- drivers/gpu/drm/drm_stub.c | 5 ----- drivers/gpu/drm/tegra/bus.c | 10 ---------- include/drm/drmP.h | 1 - 7 files changed, 1 insertion(+), 62 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 53560ef53f99..38269d5aa333 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -72,9 +72,6 @@ static void drm_unset_busid(struct drm_device *dev, struct drm_master *master) { - kfree(dev->devname); - dev->devname = NULL; - kfree(master->unique); master->unique = NULL; master->unique_len = 0; diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index de38bc9b6581..7583767ec619 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -246,7 +246,6 @@ int drm_irq_install(struct drm_device *dev, int irq) { int ret; unsigned long sh_flags = 0; - char *irqname; if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return -EINVAL; @@ -272,13 +271,8 @@ int drm_irq_install(struct drm_device *dev, int irq) if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED)) sh_flags = IRQF_SHARED; - if (dev->devname) - irqname = dev->devname; - else - irqname = dev->driver->name; - ret = request_irq(irq, dev->driver->irq_handler, - sh_flags, irqname, dev); + sh_flags, dev->driver->name, dev); if (ret < 0) { dev->irq_enabled = false; diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index c550b349f202..047c51046d94 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -146,7 +146,6 @@ static const char *drm_pci_get_name(struct drm_device *dev) static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) { int len, ret; - struct pci_driver *pdriver = dev->driver->kdriver.pci; master->unique_len = 40; master->unique_size = master->unique_len; master->unique = kmalloc(master->unique_size, GFP_KERNEL); @@ -168,18 +167,6 @@ static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) } else master->unique_len = len; - dev->devname = - kmalloc(strlen(pdriver->name) + - master->unique_len + 2, GFP_KERNEL); - - if (dev->devname == NULL) { - ret = -ENOMEM; - goto err; - } - - sprintf(dev->devname, "%s@%s", pdriver->name, - master->unique); - return 0; err: return ret; @@ -190,7 +177,6 @@ int drm_pci_set_unique(struct drm_device *dev, struct drm_unique *u) { int domain, bus, slot, func, ret; - const char *bus_name; master->unique_len = u->unique_len; master->unique_size = u->unique_len + 1; @@ -207,17 +193,6 @@ int drm_pci_set_unique(struct drm_device *dev, master->unique[master->unique_len] = '\0'; - bus_name = dev->driver->bus->get_name(dev); - dev->devname = kmalloc(strlen(bus_name) + - strlen(master->unique) + 2, GFP_KERNEL); - if (!dev->devname) { - ret = -ENOMEM; - goto err; - } - - sprintf(dev->devname, "%s@%s", bus_name, - master->unique); - /* Return error if the busid submitted doesn't match the device's actual * busid. */ diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 5b918212b1c3..b97b11ea2dc4 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -101,17 +101,6 @@ static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *mas goto err; } - dev->devname = - kmalloc(strlen(dev->platformdev->name) + - master->unique_len + 2, GFP_KERNEL); - - if (dev->devname == NULL) { - ret = -ENOMEM; - goto err; - } - - sprintf(dev->devname, "%s@%s", dev->platformdev->name, - master->unique); return 0; err: return ret; diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 5394b201c3d0..3a8e832ad151 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -166,9 +166,6 @@ static void drm_master_destroy(struct kref *kref) master->unique_len = 0; } - kfree(dev->devname); - dev->devname = NULL; - list_for_each_entry_safe(pt, next, &master->magicfree, head) { list_del(&pt->head); drm_ht_remove_item(&master->magiclist, &pt->hash_item); @@ -648,8 +645,6 @@ static void drm_dev_release(struct kref *ref) drm_minor_free(dev, DRM_MINOR_RENDER); drm_minor_free(dev, DRM_MINOR_CONTROL); - kfree(dev->devname); - mutex_destroy(&dev->master_mutex); kfree(dev); } diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c index 6916fa51cfa4..b3a66d65cb53 100644 --- a/drivers/gpu/drm/tegra/bus.c +++ b/drivers/gpu/drm/tegra/bus.c @@ -12,9 +12,7 @@ static int drm_host1x_set_busid(struct drm_device *dev, struct drm_master *master) { const char *device = dev_name(dev->dev); - const char *driver = dev->driver->name; const char *bus = dev->dev->bus->name; - int length; master->unique_len = strlen(bus) + 1 + strlen(device); master->unique_size = master->unique_len; @@ -25,14 +23,6 @@ static int drm_host1x_set_busid(struct drm_device *dev, snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device); - length = strlen(driver) + 1 + master->unique_len; - - dev->devname = kmalloc(length + 1, GFP_KERNEL); - if (!dev->devname) - return -ENOMEM; - - snprintf(dev->devname, length + 1, "%s@%s", driver, master->unique); - return 0; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 1a9d509bef66..56b028d6bc98 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1048,7 +1048,6 @@ struct drm_vblank_crtc { */ struct drm_device { struct list_head legacy_dev_list;/**< list of devices per driver for stealth attach cleanup */ - char *devname; /**< For /proc/interrupts */ int if_version; /**< Highest interface version set */ /** \name Lifetime Management */ -- cgit v1.2.3 From 9de1b51f1fae6476155350a0670dc637c762e718 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 22:24:37 +0100 Subject: drm: remove drm_bus->get_name The only user is the info debugfs file, so we only need something human readable. Now for both pci and platform devices we've used the name of the underlying device driver, which matches the name of the drm driver in all cases. So we can just use that instead. The exception is usb, which used a generic "USB". Not to harmful with just one usb driver, but better to use "udl", too. With that converted we can rip out all the ->get_name implementations. Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_info.c | 6 ++---- drivers/gpu/drm/drm_pci.c | 7 ------- drivers/gpu/drm/drm_platform.c | 6 ------ drivers/gpu/drm/drm_usb.c | 6 ------ include/drm/drmP.h | 1 - 5 files changed, 2 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 7473035dd28b..86feedd5e6f6 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -47,18 +47,16 @@ int drm_name_info(struct seq_file *m, void *data) struct drm_minor *minor = node->minor; struct drm_device *dev = minor->dev; struct drm_master *master = minor->master; - const char *bus_name; if (!master) return 0; - bus_name = dev->driver->bus->get_name(dev); if (master->unique) { seq_printf(m, "%s %s %s\n", - bus_name, + dev->driver->name, dev_name(dev->dev), master->unique); } else { seq_printf(m, "%s %s\n", - bus_name, dev_name(dev->dev)); + dev->driver->name, dev_name(dev->dev)); } return 0; } diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 047c51046d94..1fbe22a254e1 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -137,12 +137,6 @@ static int drm_get_pci_domain(struct drm_device *dev) return pci_domain_nr(dev->pdev->bus); } -static const char *drm_pci_get_name(struct drm_device *dev) -{ - struct pci_driver *pdriver = dev->driver->kdriver.pci; - return pdriver->name; -} - static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) { int len, ret; @@ -287,7 +281,6 @@ void drm_pci_agp_destroy(struct drm_device *dev) } static struct drm_bus drm_pci_bus = { - .get_name = drm_pci_get_name, .set_busid = drm_pci_set_busid, }; diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index b97b11ea2dc4..c7ec27bbe7c6 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -68,11 +68,6 @@ err_free: return ret; } -static const char *drm_platform_get_name(struct drm_device *dev) -{ - return dev->platformdev->name; -} - static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master) { int len, ret, id; @@ -107,7 +102,6 @@ err: } static struct drm_bus drm_platform_bus = { - .get_name = drm_platform_get_name, .set_busid = drm_platform_set_busid, }; diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index abdc265c9cc8..fae4aa4e1426 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -36,11 +36,6 @@ err_free: } EXPORT_SYMBOL(drm_get_usb_dev); -static const char *drm_usb_get_name(struct drm_device *dev) -{ - return "USB"; -} - static int drm_usb_set_busid(struct drm_device *dev, struct drm_master *master) { @@ -48,7 +43,6 @@ static int drm_usb_set_busid(struct drm_device *dev, } static struct drm_bus drm_usb_bus = { - .get_name = drm_usb_get_name, .set_busid = drm_usb_set_busid, }; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 56b028d6bc98..493bbbb300e6 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -726,7 +726,6 @@ struct drm_master { #define DRM_SCANOUTPOS_ACCURATE (1 << 2) struct drm_bus { - const char *(*get_name)(struct drm_device *dev); int (*set_busid)(struct drm_device *dev, struct drm_master *master); }; -- cgit v1.2.3 From f93227759d9bd3b299c288a6bd448161f849cdfd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 3 Nov 2013 22:32:41 +0100 Subject: drm: Remove dev->kdriver With the last patch to ditch the ->get_name callbacks the last user is now gone. Reviewed-by: Thierry Reding Reviewed-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_pci.c | 1 - drivers/gpu/drm/drm_platform.c | 1 - drivers/gpu/drm/drm_usb.c | 1 - include/drm/drmP.h | 5 ----- 4 files changed, 8 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 1fbe22a254e1..d237de36a07a 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -364,7 +364,6 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) DRM_DEBUG("\n"); - driver->kdriver.pci = pdriver; driver->bus = &drm_pci_bus; if (driver->driver_features & DRIVER_MODESET) diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index c7ec27bbe7c6..234e0bc1ae51 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -121,7 +121,6 @@ int drm_platform_init(struct drm_driver *driver, struct platform_device *platfor { DRM_DEBUG("\n"); - driver->kdriver.platform_device = platform_device; driver->bus = &drm_platform_bus; return drm_get_platform_dev(platform_device, driver); } diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index fae4aa4e1426..c6c7c29ad46f 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -51,7 +51,6 @@ int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver) int res; DRM_DEBUG("\n"); - driver->kdriver.usb = udriver; driver->bus = &drm_usb_bus; res = usb_register(udriver); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 493bbbb300e6..19daabeeffbe 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -963,11 +963,6 @@ struct drm_driver { const struct drm_ioctl_desc *ioctls; int num_ioctls; const struct file_operations *fops; - union { - struct pci_driver *pci; - struct platform_device *platform_device; - struct usb_driver *usb; - } kdriver; struct drm_bus *bus; /* List of devices hanging off this driver with stealth attach. */ -- cgit v1.2.3 From c42ba72ec3a7a1b6aa30122931f1f4b91b601c31 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 16 Apr 2014 16:12:27 -0700 Subject: mfd: tps65090: Stop caching most registers Nearly all of the registers in tps65090 combine control bits and status bits. Turn off caching of all registers except the select few that can be cached. In order to avoid adding more duplicate #defines, we also move some register offset definitions to the mfd driver (and resolve inconsistent names). Signed-off-by: Doug Anderson Acked-by: Mark Brown Signed-off-by: Lee Jones --- drivers/mfd/tps65090.c | 27 ++++++++++++++------------- drivers/power/tps65090-charger.c | 11 ----------- include/linux/mfd/tps65090.h | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index c3cddb4c3a1a..1c3e6e2efe41 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -32,14 +32,6 @@ #define NUM_INT_REG 2 #define TOTAL_NUM_REG 0x18 -/* interrupt status registers */ -#define TPS65090_INT_STS 0x0 -#define TPS65090_INT_STS2 0x1 - -/* interrupt mask registers */ -#define TPS65090_INT_MSK 0x2 -#define TPS65090_INT_MSK2 0x3 - #define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1 #define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2 #define TPS65090_INT1_MASK_BAT_STATUS_CHANGE 3 @@ -144,17 +136,26 @@ static struct regmap_irq_chip tps65090_irq_chip = { .irqs = tps65090_irqs, .num_irqs = ARRAY_SIZE(tps65090_irqs), .num_regs = NUM_INT_REG, - .status_base = TPS65090_INT_STS, - .mask_base = TPS65090_INT_MSK, + .status_base = TPS65090_REG_INTR_STS, + .mask_base = TPS65090_REG_INTR_MASK, .mask_invert = true, }; static bool is_volatile_reg(struct device *dev, unsigned int reg) { - if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS2)) - return true; - else + /* Nearly all registers have status bits mixed in, except a few */ + switch (reg) { + case TPS65090_REG_INTR_MASK: + case TPS65090_REG_INTR_MASK2: + case TPS65090_REG_CG_CTRL0: + case TPS65090_REG_CG_CTRL1: + case TPS65090_REG_CG_CTRL2: + case TPS65090_REG_CG_CTRL3: + case TPS65090_REG_CG_CTRL4: + case TPS65090_REG_CG_CTRL5: return false; + } + return true; } static const struct regmap_config tps65090_regmap_config = { diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index 8fc9d6df87f6..1685f63b9e5d 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -28,17 +28,6 @@ #include -#define TPS65090_REG_INTR_STS 0x00 -#define TPS65090_REG_INTR_MASK 0x02 -#define TPS65090_REG_CG_CTRL0 0x04 -#define TPS65090_REG_CG_CTRL1 0x05 -#define TPS65090_REG_CG_CTRL2 0x06 -#define TPS65090_REG_CG_CTRL3 0x07 -#define TPS65090_REG_CG_CTRL4 0x08 -#define TPS65090_REG_CG_CTRL5 0x09 -#define TPS65090_REG_CG_STATUS1 0x0a -#define TPS65090_REG_CG_STATUS2 0x0b - #define TPS65090_CHARGER_ENABLE BIT(0) #define TPS65090_VACG BIT(1) #define TPS65090_NOITERM BIT(5) diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 3f43069413e7..45f0f9d2ed25 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -64,6 +64,20 @@ enum { TPS65090_REGULATOR_MAX, }; +/* Register addresses */ +#define TPS65090_REG_INTR_STS 0x00 +#define TPS65090_REG_INTR_STS2 0x01 +#define TPS65090_REG_INTR_MASK 0x02 +#define TPS65090_REG_INTR_MASK2 0x03 +#define TPS65090_REG_CG_CTRL0 0x04 +#define TPS65090_REG_CG_CTRL1 0x05 +#define TPS65090_REG_CG_CTRL2 0x06 +#define TPS65090_REG_CG_CTRL3 0x07 +#define TPS65090_REG_CG_CTRL4 0x08 +#define TPS65090_REG_CG_CTRL5 0x09 +#define TPS65090_REG_CG_STATUS1 0x0a +#define TPS65090_REG_CG_STATUS2 0x0b + struct tps65090 { struct device *dev; struct regmap *rmap; -- cgit v1.2.3 From aa45660c6b59388fac3995a8c2998d710ef28fd4 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Mon, 14 Apr 2014 15:41:27 +0300 Subject: netfilter: nf_tables: Make meta expression core functions public This will be useful to create network family dedicated META expression as for NFPROTO_BRIDGE for instance. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nft_meta.h | 36 +++++++++++++++++++++++++++++ net/netfilter/nft_meta.c | 50 ++++++++++++++++++++-------------------- 2 files changed, 61 insertions(+), 25 deletions(-) create mode 100644 include/net/netfilter/nft_meta.h (limited to 'include') diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h new file mode 100644 index 000000000000..0ee47c3e2e31 --- /dev/null +++ b/include/net/netfilter/nft_meta.h @@ -0,0 +1,36 @@ +#ifndef _NFT_META_H_ +#define _NFT_META_H_ + +struct nft_meta { + enum nft_meta_keys key:8; + union { + enum nft_registers dreg:8; + enum nft_registers sreg:8; + }; +}; + +extern const struct nla_policy nft_meta_policy[]; + +int nft_meta_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]); + +int nft_meta_set_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]); + +int nft_meta_get_dump(struct sk_buff *skb, + const struct nft_expr *expr); + +int nft_meta_set_dump(struct sk_buff *skb, + const struct nft_expr *expr); + +void nft_meta_get_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt); + +void nft_meta_set_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt); + +#endif diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 6d0b8cc27f2a..852b178c6ae7 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -18,18 +18,11 @@ #include #include /* for TCP_TIME_WAIT */ #include +#include -struct nft_meta { - enum nft_meta_keys key:8; - union { - enum nft_registers dreg:8; - enum nft_registers sreg:8; - }; -}; - -static void nft_meta_get_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) +void nft_meta_get_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) { const struct nft_meta *priv = nft_expr_priv(expr); const struct sk_buff *skb = pkt->skb; @@ -140,10 +133,11 @@ static void nft_meta_get_eval(const struct nft_expr *expr, err: data[NFT_REG_VERDICT].verdict = NFT_BREAK; } +EXPORT_SYMBOL_GPL(nft_meta_get_eval); -static void nft_meta_set_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) +void nft_meta_set_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) { const struct nft_meta *meta = nft_expr_priv(expr); struct sk_buff *skb = pkt->skb; @@ -163,16 +157,18 @@ static void nft_meta_set_eval(const struct nft_expr *expr, WARN_ON(1); } } +EXPORT_SYMBOL_GPL(nft_meta_set_eval); -static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { +const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { [NFTA_META_DREG] = { .type = NLA_U32 }, [NFTA_META_KEY] = { .type = NLA_U32 }, [NFTA_META_SREG] = { .type = NLA_U32 }, }; +EXPORT_SYMBOL_GPL(nft_meta_policy); -static int nft_meta_get_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) +int nft_meta_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { struct nft_meta *priv = nft_expr_priv(expr); int err; @@ -215,10 +211,11 @@ static int nft_meta_get_init(const struct nft_ctx *ctx, return 0; } +EXPORT_SYMBOL_GPL(nft_meta_get_init); -static int nft_meta_set_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) +int nft_meta_set_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { struct nft_meta *priv = nft_expr_priv(expr); int err; @@ -240,9 +237,10 @@ static int nft_meta_set_init(const struct nft_ctx *ctx, return 0; } +EXPORT_SYMBOL_GPL(nft_meta_set_init); -static int nft_meta_get_dump(struct sk_buff *skb, - const struct nft_expr *expr) +int nft_meta_get_dump(struct sk_buff *skb, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -255,9 +253,10 @@ static int nft_meta_get_dump(struct sk_buff *skb, nla_put_failure: return -1; } +EXPORT_SYMBOL_GPL(nft_meta_get_dump); -static int nft_meta_set_dump(struct sk_buff *skb, - const struct nft_expr *expr) +int nft_meta_set_dump(struct sk_buff *skb, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -271,6 +270,7 @@ static int nft_meta_set_dump(struct sk_buff *skb, nla_put_failure: return -1; } +EXPORT_SYMBOL_GPL(nft_meta_set_dump); static struct nft_expr_type nft_meta_type; static const struct nft_expr_ops nft_meta_get_ops = { -- cgit v1.2.3 From 389cb8348cf5ac4a702c71bf13673c4c8bf01e34 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 24 Mar 2014 12:15:24 +0200 Subject: ASoC: core: Update snd_soc_of_parse_daifmt() interface Adds struct device_node **bitclkmaster and struct device_node **framemaster function parameters. With the new syntax bitclock-master and frame-master properties can explicitly indicate the dai-link bit-clock and frame masters with a phandle. This patch also makes the minimal changes to simple-card for it to work with the updated snd_soc_of_parse_daifmt(). Simple-card appears to be the only user of snd_soc_of_parse_daifmt() for now. Signed-off-by: Jyri Sarha Acked-by: Jean-Francois Moine Signed-off-by: Mark Brown --- include/sound/soc.h | 4 +++- sound/soc/generic/simple-card.c | 5 +++-- sound/soc/soc-core.c | 8 +++++++- 3 files changed, 13 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168d8ff4..58784105289a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1241,7 +1241,9 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, - const char *prefix); + const char *prefix, + struct device_node **bitclkmaster, + struct device_node **framemaster); int snd_soc_of_get_dai_name(struct device_node *of_node, const char **dai_name); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 21f1ccbdf582..835fd0258243 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -121,7 +121,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np, * bitclock-master, frame-master * and specific "format" if it has */ - dai->fmt = snd_soc_of_parse_daifmt(np, NULL); + dai->fmt = snd_soc_of_parse_daifmt(np, NULL, NULL, NULL); dai->fmt |= daifmt; /* @@ -201,7 +201,8 @@ static int asoc_simple_card_parse_of(struct device_node *node, snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name"); /* get CPU/CODEC common format via simple-audio-card,format */ - daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") & + daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,", NULL, + NULL) & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK); /* off-codec widgets */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 051c006281f5..3487a55c9a06 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4554,7 +4554,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, - const char *prefix) + const char *prefix, + struct device_node **bitclkmaster, + struct device_node **framemaster) { int ret, i; char prop[128]; @@ -4637,9 +4639,13 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, */ snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); bit = !!of_get_property(np, prop, NULL); + if (bit && bitclkmaster) + *bitclkmaster = of_parse_phandle(np, prop, 0); snprintf(prop, sizeof(prop), "%sframe-master", prefix); frame = !!of_get_property(np, prop, NULL); + if (frame && framemaster) + *framemaster = of_parse_phandle(np, prop, 0); switch ((bit << 4) + frame) { case 0x11: -- cgit v1.2.3 From 575343d161d75dc1516f53436b9eb47d04eda938 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 11:17:13 +0200 Subject: mfd: max14577: Add muic prefix to regmap config Add muic prefix to regmap config to differentiate between another regmap config for MAX77836 PMIC node. Additionally remove unused symbols: MAX14577_REG_INVALID and max14577_irq_source. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max14577.c | 9 +++++---- include/linux/mfd/max14577-private.h | 4 +--- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 5f13cefe8def..d180fae8e317 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -37,7 +37,7 @@ static struct mfd_cell max14577_devs[] = { { .name = "max14577-charger", }, }; -static bool max14577_volatile_reg(struct device *dev, unsigned int reg) +static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3: @@ -48,10 +48,10 @@ static bool max14577_volatile_reg(struct device *dev, unsigned int reg) return false; } -static const struct regmap_config max14577_regmap_config = { +static const struct regmap_config max14577_muic_regmap_config = { .reg_bits = 8, .val_bits = 8, - .volatile_reg = max14577_volatile_reg, + .volatile_reg = max14577_muic_volatile_reg, .max_register = MAX14577_REG_END, }; @@ -113,7 +113,8 @@ static int max14577_i2c_probe(struct i2c_client *i2c, max14577->i2c = i2c; max14577->irq = i2c->irq; - max14577->regmap = devm_regmap_init_i2c(i2c, &max14577_regmap_config); + max14577->regmap = devm_regmap_init_i2c(i2c, + &max14577_muic_regmap_config); if (IS_ERR(max14577->regmap)) { ret = PTR_ERR(max14577->regmap); dev_err(max14577->dev, "Failed to allocate register map: %d\n", diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index c9b332fb0d5d..97b78d94f92f 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -22,9 +22,7 @@ #include #include -#define MAX14577_REG_INVALID (0xff) - -/* Slave addr = 0x4A: Interrupt */ +/* Slave addr = 0x4A: MUIC and Charger */ enum max14577_reg { MAX14577_REG_DEVICEID = 0x00, MAX14577_REG_INT1 = 0x01, -- cgit v1.2.3 From eccb80cc22354a12255c2579247a92a30a4c881b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 11:17:14 +0200 Subject: mfd: max14577: Add detection of device type This patch continues the preparation for adding support for MAX77836 device to existing max14577 driver. Add enum for types of devices supported by this driver. The device type will be detected by matching of_device_id, or i2c_device_id as a fallback. The patch also moves to separate function the code related to displaying DeviceID register values. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max14577.c | 64 +++++++++++++++++++++++++----------- include/linux/mfd/max14577-private.h | 12 ++++--- 2 files changed, 53 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index d180fae8e317..0e07ed74ab41 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,14 @@ static struct mfd_cell max14577_devs[] = { { .name = "max14577-charger", }, }; +static struct of_device_id max14577_dt_match[] = { + { + .compatible = "maxim,max14577", + .data = (void *)MAXIM_DEVICE_TYPE_MAX14577, + }, + {}, +}; + static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -83,13 +92,34 @@ static const struct regmap_irq_chip max14577_irq_chip = { .num_irqs = ARRAY_SIZE(max14577_irqs), }; +static void max14577_print_dev_type(struct max14577 *max14577) +{ + u8 reg_data, vendor_id, device_id; + int ret; + + ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID, + ®_data); + if (ret) { + dev_err(max14577->dev, + "Failed to read DEVICEID register: %d\n", ret); + return; + } + + vendor_id = ((reg_data & DEVID_VENDORID_MASK) >> + DEVID_VENDORID_SHIFT); + device_id = ((reg_data & DEVID_DEVICEID_MASK) >> + DEVID_DEVICEID_SHIFT); + + dev_info(max14577->dev, "Device type: %u (ID: 0x%x, vendor: 0x%x)\n", + max14577->dev_type, device_id, vendor_id); +} + static int max14577_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max14577 *max14577; struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev); struct device_node *np = i2c->dev.of_node; - u8 reg_data; int ret = 0; if (np) { @@ -122,19 +152,17 @@ static int max14577_i2c_probe(struct i2c_client *i2c, return ret; } - ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID, - ®_data); - if (ret) { - dev_err(max14577->dev, "Device not found on this channel: %d\n", - ret); - return ret; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(max14577_dt_match, &i2c->dev); + if (of_id) + max14577->dev_type = (unsigned int)of_id->data; + } else { + max14577->dev_type = id->driver_data; } - max14577->vendor_id = ((reg_data & DEVID_VENDORID_MASK) >> - DEVID_VENDORID_SHIFT); - max14577->device_id = ((reg_data & DEVID_DEVICEID_MASK) >> - DEVID_DEVICEID_SHIFT); - dev_info(max14577->dev, "Device ID: 0x%x, vendor: 0x%x\n", - max14577->device_id, max14577->vendor_id); + + max14577_print_dev_type(max14577); ret = regmap_add_irq_chip(max14577->regmap, max14577->irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0, @@ -173,7 +201,7 @@ static int max14577_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id max14577_i2c_id[] = { - { "max14577", 0 }, + { "max14577", MAXIM_DEVICE_TYPE_MAX14577, }, { } }; MODULE_DEVICE_TABLE(i2c, max14577_i2c_id); @@ -216,11 +244,6 @@ static int max14577_resume(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -static struct of_device_id max14577_dt_match[] = { - { .compatible = "maxim,max14577", }, - {}, -}; - static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume); static struct i2c_driver max14577_i2c_driver = { @@ -237,6 +260,9 @@ static struct i2c_driver max14577_i2c_driver = { static int __init max14577_i2c_init(void) { + BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM); + BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM); + return i2c_add_driver(&max14577_i2c_driver); } subsys_initcall(max14577_i2c_init); diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index 97b78d94f92f..1ce6f2952cc9 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -22,6 +22,13 @@ #include #include +enum maxim_device_type { + MAXIM_DEVICE_TYPE_UNKNOWN = 0, + MAXIM_DEVICE_TYPE_MAX14577, + + MAXIM_DEVICE_TYPE_NUM, +}; + /* Slave addr = 0x4A: MUIC and Charger */ enum max14577_reg { MAX14577_REG_DEVICEID = 0x00, @@ -271,15 +278,12 @@ enum max14577_irq { struct max14577 { struct device *dev; struct i2c_client *i2c; /* Slave addr = 0x4A */ + enum maxim_device_type dev_type; struct regmap *regmap; struct regmap_irq_chip_data *irq_data; int irq; - - /* Device ID */ - u8 vendor_id; /* Vendor Identification */ - u8 device_id; /* Chip Version */ }; /* MAX14577 shared regmap API function */ -- cgit v1.2.3 From c7846852ec8f304c629963202fa565452e8fe34c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 11:17:17 +0200 Subject: mfd: max14577: Add MAX14577 prefix to IRQ defines This patch prepares for adding support for MAX77836 device to existing max14577 driver by adding MAX14577 prefix to defines of interrupts. This is only a rename-like patch, new code is not added. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max14577.c | 24 ++++++++++++------------ include/linux/mfd/max14577-private.h | 28 ++++++++++++++-------------- 2 files changed, 26 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 0e07ed74ab41..6f39dec9dfdf 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -66,20 +66,20 @@ static const struct regmap_config max14577_muic_regmap_config = { static const struct regmap_irq max14577_irqs[] = { /* INT1 interrupts */ - { .reg_offset = 0, .mask = INT1_ADC_MASK, }, - { .reg_offset = 0, .mask = INT1_ADCLOW_MASK, }, - { .reg_offset = 0, .mask = INT1_ADCERR_MASK, }, + { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, }, + { .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, }, + { .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, }, /* INT2 interrupts */ - { .reg_offset = 1, .mask = INT2_CHGTYP_MASK, }, - { .reg_offset = 1, .mask = INT2_CHGDETRUN_MASK, }, - { .reg_offset = 1, .mask = INT2_DCDTMR_MASK, }, - { .reg_offset = 1, .mask = INT2_DBCHG_MASK, }, - { .reg_offset = 1, .mask = INT2_VBVOLT_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, }, /* INT3 interrupts */ - { .reg_offset = 2, .mask = INT3_EOC_MASK, }, - { .reg_offset = 2, .mask = INT3_CGMBC_MASK, }, - { .reg_offset = 2, .mask = INT3_OVP_MASK, }, - { .reg_offset = 2, .mask = INT3_MBCCHGERR_MASK, }, + { .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, }, + { .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, }, + { .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, }, + { .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, }, }; static const struct regmap_irq_chip max14577_irq_chip = { diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index 1ce6f2952cc9..989183d232cd 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -79,20 +79,20 @@ enum max14577_muic_charger_type { }; /* MAX14577 interrupts */ -#define INT1_ADC_MASK (0x1 << 0) -#define INT1_ADCLOW_MASK (0x1 << 1) -#define INT1_ADCERR_MASK (0x1 << 2) - -#define INT2_CHGTYP_MASK (0x1 << 0) -#define INT2_CHGDETRUN_MASK (0x1 << 1) -#define INT2_DCDTMR_MASK (0x1 << 2) -#define INT2_DBCHG_MASK (0x1 << 3) -#define INT2_VBVOLT_MASK (0x1 << 4) - -#define INT3_EOC_MASK (0x1 << 0) -#define INT3_CGMBC_MASK (0x1 << 1) -#define INT3_OVP_MASK (0x1 << 2) -#define INT3_MBCCHGERR_MASK (0x1 << 3) +#define MAX14577_INT1_ADC_MASK BIT(0) +#define MAX14577_INT1_ADCLOW_MASK BIT(1) +#define MAX14577_INT1_ADCERR_MASK BIT(2) + +#define MAX14577_INT2_CHGTYP_MASK BIT(0) +#define MAX14577_INT2_CHGDETRUN_MASK BIT(1) +#define MAX14577_INT2_DCDTMR_MASK BIT(2) +#define MAX14577_INT2_DBCHG_MASK BIT(3) +#define MAX14577_INT2_VBVOLT_MASK BIT(4) + +#define MAX14577_INT3_EOC_MASK BIT(0) +#define MAX14577_INT3_CGMBC_MASK BIT(1) +#define MAX14577_INT3_OVP_MASK BIT(2) +#define MAX14577_INT3_MBCCHGERR_MASK BIT(3) /* MAX14577 DEVICE ID register */ #define DEVID_VENDORID_SHIFT 0 -- cgit v1.2.3 From aee2a57c7482c712052b877218aa2c5bc0fe8626 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 11:17:18 +0200 Subject: mfd: max77836: Add MAX77836 support to max14577 driver Add Maxim 77836 support to max14577 driver. The chipsets have same MUIC component so the extcon, charger and regulators are almost the same. The MAX77836 however has also PMIC and Fuel Gauge. The MAX77836 uses three I2C slave addresses and has additional interrupts (related to PMIC and Fuel Gauge). It has also Interrupt Source register, just like MAX77686 and MAX77693. The MAX77836 PMIC's TOPSYS and INTSRC interrupts are reported in the PMIC block. The PMIC block has different I2C slave address and uses own regmap so another regmap_irq_chip is needed. Since we have two regmap_irq_chip, use shared interrupts on MAX77836. This patch adds additional defines and functions to the max14577 MFD core driver so the driver will handle both chipsets. Also this patch replaces "0x1 << N" with BIT(N) in defines for register masks. Signed-off-by: Krzysztof Kozlowski Acked-by: Chanwoo Choi Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 6 +- drivers/mfd/max14577.c | 217 +++++++++++++++++++++++++++++++++-- include/linux/mfd/max14577-private.h | 145 +++++++++++++++++------ include/linux/mfd/max14577.h | 7 +- 4 files changed, 330 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 33834120d057..5bdefe72625e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -331,15 +331,15 @@ config MFD_88PM860X battery-charger under the corresponding menus. config MFD_MAX14577 - bool "Maxim Semiconductor MAX14577 MUIC + Charger Support" + bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support" depends on I2C=y select MFD_CORE select REGMAP_I2C select REGMAP_IRQ select IRQ_DOMAIN help - Say yes here to add support for Maxim Semiconductor MAX14577. - This is a Micro-USB IC with Charger controls on chip. + Say yes here to add support for Maxim Semiconductor MAX14577 and + MAX77836 Micro-USB ICs with battery charger. This driver provides common support for accessing the device; additional drivers must be enabled in order to use the functionality of the device. diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 6f39dec9dfdf..20e3b2d81bf0 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -1,7 +1,7 @@ /* - * max14577.c - mfd core driver for the Maxim 14577 + * max14577.c - mfd core driver for the Maxim 14577/77836 * - * Copyright (C) 2013 Samsung Electrnoics + * Copyright (C) 2014 Samsung Electrnoics * Chanwoo Choi * Krzysztof Kozlowski * @@ -38,11 +38,34 @@ static struct mfd_cell max14577_devs[] = { { .name = "max14577-charger", }, }; +static struct mfd_cell max77836_devs[] = { + { + .name = "max77836-muic", + .of_compatible = "maxim,max77836-muic", + }, + { + .name = "max77836-regulator", + .of_compatible = "maxim,max77836-regulator", + }, + { + .name = "max77836-charger", + .of_compatible = "maxim,max77836-charger", + }, + { + .name = "max77836-battery", + .of_compatible = "maxim,max77836-battery", + }, +}; + static struct of_device_id max14577_dt_match[] = { { .compatible = "maxim,max14577", .data = (void *)MAXIM_DEVICE_TYPE_MAX14577, }, + { + .compatible = "maxim,max77836", + .data = (void *)MAXIM_DEVICE_TYPE_MAX77836, + }, {}, }; @@ -57,6 +80,26 @@ static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg) return false; } +static bool max77836_muic_volatile_reg(struct device *dev, unsigned int reg) +{ + /* Any max14577 volatile registers are also max77836 volatile. */ + if (max14577_muic_volatile_reg(dev, reg)) + return true; + + switch (reg) { + case MAX77836_FG_REG_VCELL_MSB ... MAX77836_FG_REG_SOC_LSB: + case MAX77836_FG_REG_CRATE_MSB ... MAX77836_FG_REG_CRATE_LSB: + case MAX77836_FG_REG_STATUS_H ... MAX77836_FG_REG_STATUS_L: + case MAX77836_PMIC_REG_INTSRC: + case MAX77836_PMIC_REG_TOPSYS_INT: + case MAX77836_PMIC_REG_TOPSYS_STAT: + return true; + default: + break; + } + return false; +} + static const struct regmap_config max14577_muic_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -64,6 +107,13 @@ static const struct regmap_config max14577_muic_regmap_config = { .max_register = MAX14577_REG_END, }; +static const struct regmap_config max77836_pmic_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = max77836_muic_volatile_reg, + .max_register = MAX77836_PMIC_REG_END, +}; + static const struct regmap_irq max14577_irqs[] = { /* INT1 interrupts */ { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, }, @@ -86,12 +136,56 @@ static const struct regmap_irq_chip max14577_irq_chip = { .name = "max14577", .status_base = MAX14577_REG_INT1, .mask_base = MAX14577_REG_INTMASK1, - .mask_invert = 1, + .mask_invert = true, .num_regs = 3, .irqs = max14577_irqs, .num_irqs = ARRAY_SIZE(max14577_irqs), }; +static const struct regmap_irq max77836_muic_irqs[] = { + /* INT1 interrupts */ + { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, }, + { .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, }, + { .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, }, + /* INT2 interrupts */ + { .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, }, + { .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, }, + { .reg_offset = 1, .mask = MAX77836_INT2_VIDRM_MASK, }, + /* INT3 interrupts */ + { .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, }, + { .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, }, + { .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, }, + { .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, }, +}; + +static const struct regmap_irq_chip max77836_muic_irq_chip = { + .name = "max77836-muic", + .status_base = MAX14577_REG_INT1, + .mask_base = MAX14577_REG_INTMASK1, + .mask_invert = true, + .num_regs = 3, + .irqs = max77836_muic_irqs, + .num_irqs = ARRAY_SIZE(max77836_muic_irqs), +}; + +static const struct regmap_irq max77836_pmic_irqs[] = { + { .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T120C_MASK, }, + { .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T140C_MASK, }, +}; + +static const struct regmap_irq_chip max77836_pmic_irq_chip = { + .name = "max77836-pmic", + .status_base = MAX77836_PMIC_REG_TOPSYS_INT, + .mask_base = MAX77836_PMIC_REG_TOPSYS_INT_MASK, + .mask_invert = false, + .num_regs = 1, + .irqs = max77836_pmic_irqs, + .num_irqs = ARRAY_SIZE(max77836_pmic_irqs), +}; + static void max14577_print_dev_type(struct max14577 *max14577) { u8 reg_data, vendor_id, device_id; @@ -114,6 +208,81 @@ static void max14577_print_dev_type(struct max14577 *max14577) max14577->dev_type, device_id, vendor_id); } +/* + * Max77836 specific initialization code for driver probe. + * Adds new I2C dummy device, regmap and regmap IRQ chip. + * Unmasks Interrupt Source register. + * + * On success returns 0. + * On failure returns errno and reverts any changes done so far (e.g. remove + * I2C dummy device), except masking the INT SRC register. + */ +static int max77836_init(struct max14577 *max14577) +{ + int ret; + u8 intsrc_mask; + + max14577->i2c_pmic = i2c_new_dummy(max14577->i2c->adapter, + I2C_ADDR_PMIC); + if (!max14577->i2c_pmic) { + dev_err(max14577->dev, "Failed to register PMIC I2C device\n"); + return -ENODEV; + } + i2c_set_clientdata(max14577->i2c_pmic, max14577); + + max14577->regmap_pmic = devm_regmap_init_i2c(max14577->i2c_pmic, + &max77836_pmic_regmap_config); + if (IS_ERR(max14577->regmap_pmic)) { + ret = PTR_ERR(max14577->regmap_pmic); + dev_err(max14577->dev, "Failed to allocate PMIC register map: %d\n", + ret); + goto err; + } + + /* Un-mask MAX77836 Interrupt Source register */ + ret = max14577_read_reg(max14577->regmap_pmic, + MAX77836_PMIC_REG_INTSRC_MASK, &intsrc_mask); + if (ret < 0) { + dev_err(max14577->dev, "Failed to read PMIC register\n"); + goto err; + } + + intsrc_mask &= ~(MAX77836_INTSRC_MASK_TOP_INT_MASK); + intsrc_mask &= ~(MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK); + ret = max14577_write_reg(max14577->regmap_pmic, + MAX77836_PMIC_REG_INTSRC_MASK, intsrc_mask); + if (ret < 0) { + dev_err(max14577->dev, "Failed to write PMIC register\n"); + goto err; + } + + ret = regmap_add_irq_chip(max14577->regmap_pmic, max14577->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED, + 0, &max77836_pmic_irq_chip, + &max14577->irq_data_pmic); + if (ret != 0) { + dev_err(max14577->dev, "Failed to request PMIC IRQ %d: %d\n", + max14577->irq, ret); + goto err; + } + + return 0; + +err: + i2c_unregister_device(max14577->i2c_pmic); + + return ret; +} + +/* + * Max77836 specific de-initialization code for driver remove. + */ +static void max77836_remove(struct max14577 *max14577) +{ + regmap_del_irq_chip(max14577->irq, max14577->irq_data_pmic); + i2c_unregister_device(max14577->i2c_pmic); +} + static int max14577_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -121,6 +290,10 @@ static int max14577_i2c_probe(struct i2c_client *i2c, struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev); struct device_node *np = i2c->dev.of_node; int ret = 0; + const struct regmap_irq_chip *irq_chip; + struct mfd_cell *mfd_devs; + unsigned int mfd_devs_size; + int irq_flags; if (np) { pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); @@ -164,9 +337,24 @@ static int max14577_i2c_probe(struct i2c_client *i2c, max14577_print_dev_type(max14577); + switch (max14577->dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + irq_chip = &max77836_muic_irq_chip; + mfd_devs = max77836_devs; + mfd_devs_size = ARRAY_SIZE(max77836_devs); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; + break; + case MAXIM_DEVICE_TYPE_MAX14577: + default: + irq_chip = &max14577_irq_chip; + mfd_devs = max14577_devs; + mfd_devs_size = ARRAY_SIZE(max14577_devs); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + break; + } + ret = regmap_add_irq_chip(max14577->regmap, max14577->irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0, - &max14577_irq_chip, + irq_flags, 0, irq_chip, &max14577->irq_data); if (ret != 0) { dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", @@ -174,8 +362,15 @@ static int max14577_i2c_probe(struct i2c_client *i2c, return ret; } - ret = mfd_add_devices(max14577->dev, -1, max14577_devs, - ARRAY_SIZE(max14577_devs), NULL, 0, + /* Max77836 specific initialization code (additional regmap) */ + if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) { + ret = max77836_init(max14577); + if (ret < 0) + goto err_max77836; + } + + ret = mfd_add_devices(max14577->dev, -1, mfd_devs, + mfd_devs_size, NULL, 0, regmap_irq_get_domain(max14577->irq_data)); if (ret < 0) goto err_mfd; @@ -185,6 +380,9 @@ static int max14577_i2c_probe(struct i2c_client *i2c, return 0; err_mfd: + if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) + max77836_remove(max14577); +err_max77836: regmap_del_irq_chip(max14577->irq, max14577->irq_data); return ret; @@ -196,12 +394,15 @@ static int max14577_i2c_remove(struct i2c_client *i2c) mfd_remove_devices(max14577->dev); regmap_del_irq_chip(max14577->irq, max14577->irq_data); + if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) + max77836_remove(max14577); return 0; } static const struct i2c_device_id max14577_i2c_id[] = { { "max14577", MAXIM_DEVICE_TYPE_MAX14577, }, + { "max77836", MAXIM_DEVICE_TYPE_MAX77836, }, { } }; MODULE_DEVICE_TABLE(i2c, max14577_i2c_id); @@ -274,5 +475,5 @@ static void __exit max14577_i2c_exit(void) module_exit(max14577_i2c_exit); MODULE_AUTHOR("Chanwoo Choi , Krzysztof Kozlowski "); -MODULE_DESCRIPTION("MAXIM 14577 multi-function core driver"); +MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver"); MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index 989183d232cd..e301bd19b067 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -1,7 +1,7 @@ /* - * max14577-private.h - Common API for the Maxim 14577 internal sub chip + * max14577-private.h - Common API for the Maxim 14577/77836 internal sub chip * - * Copyright (C) 2013 Samsung Electrnoics + * Copyright (C) 2014 Samsung Electrnoics * Chanwoo Choi * Krzysztof Kozlowski * @@ -22,9 +22,14 @@ #include #include +#define I2C_ADDR_PMIC (0x46 >> 1) +#define I2C_ADDR_MUIC (0x4A >> 1) +#define I2C_ADDR_FG (0x6C >> 1) + enum maxim_device_type { MAXIM_DEVICE_TYPE_UNKNOWN = 0, MAXIM_DEVICE_TYPE_MAX14577, + MAXIM_DEVICE_TYPE_MAX77836, MAXIM_DEVICE_TYPE_NUM, }; @@ -88,6 +93,7 @@ enum max14577_muic_charger_type { #define MAX14577_INT2_DCDTMR_MASK BIT(2) #define MAX14577_INT2_DBCHG_MASK BIT(3) #define MAX14577_INT2_VBVOLT_MASK BIT(4) +#define MAX77836_INT2_VIDRM_MASK BIT(5) #define MAX14577_INT3_EOC_MASK BIT(0) #define MAX14577_INT3_CGMBC_MASK BIT(1) @@ -104,9 +110,11 @@ enum max14577_muic_charger_type { #define STATUS1_ADC_SHIFT 0 #define STATUS1_ADCLOW_SHIFT 5 #define STATUS1_ADCERR_SHIFT 6 +#define MAX77836_STATUS1_ADC1K_SHIFT 7 #define STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT) -#define STATUS1_ADCLOW_MASK (0x1 << STATUS1_ADCLOW_SHIFT) -#define STATUS1_ADCERR_MASK (0x1 << STATUS1_ADCERR_SHIFT) +#define STATUS1_ADCLOW_MASK BIT(STATUS1_ADCLOW_SHIFT) +#define STATUS1_ADCERR_MASK BIT(STATUS1_ADCERR_SHIFT) +#define MAX77836_STATUS1_ADC1K_MASK BIT(MAX77836_STATUS1_ADC1K_SHIFT) /* MAX14577 STATUS2 register */ #define STATUS2_CHGTYP_SHIFT 0 @@ -114,11 +122,13 @@ enum max14577_muic_charger_type { #define STATUS2_DCDTMR_SHIFT 4 #define STATUS2_DBCHG_SHIFT 5 #define STATUS2_VBVOLT_SHIFT 6 +#define MAX77836_STATUS2_VIDRM_SHIFT 7 #define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) -#define STATUS2_CHGDETRUN_MASK (0x1 << STATUS2_CHGDETRUN_SHIFT) -#define STATUS2_DCDTMR_MASK (0x1 << STATUS2_DCDTMR_SHIFT) -#define STATUS2_DBCHG_MASK (0x1 << STATUS2_DBCHG_SHIFT) -#define STATUS2_VBVOLT_MASK (0x1 << STATUS2_VBVOLT_SHIFT) +#define STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT) +#define STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT) +#define STATUS2_DBCHG_MASK BIT(STATUS2_DBCHG_SHIFT) +#define STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT) +#define MAX77836_STATUS2_VIDRM_MASK BIT(MAX77836_STATUS2_VIDRM_SHIFT) /* MAX14577 CONTROL1 register */ #define COMN1SW_SHIFT 0 @@ -127,8 +137,8 @@ enum max14577_muic_charger_type { #define IDBEN_SHIFT 7 #define COMN1SW_MASK (0x7 << COMN1SW_SHIFT) #define COMP2SW_MASK (0x7 << COMP2SW_SHIFT) -#define MICEN_MASK (0x1 << MICEN_SHIFT) -#define IDBEN_MASK (0x1 << IDBEN_SHIFT) +#define MICEN_MASK BIT(MICEN_SHIFT) +#define IDBEN_MASK BIT(IDBEN_SHIFT) #define CLEAR_IDBEN_MICEN_MASK (COMN1SW_MASK | COMP2SW_MASK) #define CTRL1_SW_USB ((1 << COMP2SW_SHIFT) \ | (1 << COMN1SW_SHIFT)) @@ -148,14 +158,14 @@ enum max14577_muic_charger_type { #define CTRL2_ACCDET_SHIFT (5) #define CTRL2_USBCPINT_SHIFT (6) #define CTRL2_RCPS_SHIFT (7) -#define CTRL2_LOWPWR_MASK (0x1 << CTRL2_LOWPWR_SHIFT) -#define CTRL2_ADCEN_MASK (0x1 << CTRL2_ADCEN_SHIFT) -#define CTRL2_CPEN_MASK (0x1 << CTRL2_CPEN_SHIFT) -#define CTRL2_SFOUTASRT_MASK (0x1 << CTRL2_SFOUTASRT_SHIFT) -#define CTRL2_SFOUTORD_MASK (0x1 << CTRL2_SFOUTORD_SHIFT) -#define CTRL2_ACCDET_MASK (0x1 << CTRL2_ACCDET_SHIFT) -#define CTRL2_USBCPINT_MASK (0x1 << CTRL2_USBCPINT_SHIFT) -#define CTRL2_RCPS_MASK (0x1 << CTR2_RCPS_SHIFT) +#define CTRL2_LOWPWR_MASK BIT(CTRL2_LOWPWR_SHIFT) +#define CTRL2_ADCEN_MASK BIT(CTRL2_ADCEN_SHIFT) +#define CTRL2_CPEN_MASK BIT(CTRL2_CPEN_SHIFT) +#define CTRL2_SFOUTASRT_MASK BIT(CTRL2_SFOUTASRT_SHIFT) +#define CTRL2_SFOUTORD_MASK BIT(CTRL2_SFOUTORD_SHIFT) +#define CTRL2_ACCDET_MASK BIT(CTRL2_ACCDET_SHIFT) +#define CTRL2_USBCPINT_MASK BIT(CTRL2_USBCPINT_SHIFT) +#define CTRL2_RCPS_MASK BIT(CTRL2_RCPS_SHIFT) #define CTRL2_CPEN1_LOWPWR0 ((1 << CTRL2_CPEN_SHIFT) | \ (0 << CTRL2_LOWPWR_SHIFT)) @@ -203,14 +213,14 @@ enum max14577_charger_reg { #define CDETCTRL1_DBEXIT_SHIFT 5 #define CDETCTRL1_DBIDLE_SHIFT 6 #define CDETCTRL1_CDPDET_SHIFT 7 -#define CDETCTRL1_CHGDETEN_MASK (0x1 << CDETCTRL1_CHGDETEN_SHIFT) -#define CDETCTRL1_CHGTYPMAN_MASK (0x1 << CDETCTRL1_CHGTYPMAN_SHIFT) -#define CDETCTRL1_DCDEN_MASK (0x1 << CDETCTRL1_DCDEN_SHIFT) -#define CDETCTRL1_DCD2SCT_MASK (0x1 << CDETCTRL1_DCD2SCT_SHIFT) -#define CDETCTRL1_DCHKTM_MASK (0x1 << CDETCTRL1_DCHKTM_SHIFT) -#define CDETCTRL1_DBEXIT_MASK (0x1 << CDETCTRL1_DBEXIT_SHIFT) -#define CDETCTRL1_DBIDLE_MASK (0x1 << CDETCTRL1_DBIDLE_SHIFT) -#define CDETCTRL1_CDPDET_MASK (0x1 << CDETCTRL1_CDPDET_SHIFT) +#define CDETCTRL1_CHGDETEN_MASK BIT(CDETCTRL1_CHGDETEN_SHIFT) +#define CDETCTRL1_CHGTYPMAN_MASK BIT(CDETCTRL1_CHGTYPMAN_SHIFT) +#define CDETCTRL1_DCDEN_MASK BIT(CDETCTRL1_DCDEN_SHIFT) +#define CDETCTRL1_DCD2SCT_MASK BIT(CDETCTRL1_DCD2SCT_SHIFT) +#define CDETCTRL1_DCHKTM_MASK BIT(CDETCTRL1_DCHKTM_SHIFT) +#define CDETCTRL1_DBEXIT_MASK BIT(CDETCTRL1_DBEXIT_SHIFT) +#define CDETCTRL1_DBIDLE_MASK BIT(CDETCTRL1_DBIDLE_SHIFT) +#define CDETCTRL1_CDPDET_MASK BIT(CDETCTRL1_CDPDET_SHIFT) /* MAX14577 CHGCTRL1 register */ #define CHGCTRL1_TCHW_SHIFT 4 @@ -218,9 +228,9 @@ enum max14577_charger_reg { /* MAX14577 CHGCTRL2 register */ #define CHGCTRL2_MBCHOSTEN_SHIFT 6 -#define CHGCTRL2_MBCHOSTEN_MASK (0x1 << CHGCTRL2_MBCHOSTEN_SHIFT) +#define CHGCTRL2_MBCHOSTEN_MASK BIT(CHGCTRL2_MBCHOSTEN_SHIFT) #define CHGCTRL2_VCHGR_RC_SHIFT 7 -#define CHGCTRL2_VCHGR_RC_MASK (0x1 << CHGCTRL2_VCHGR_RC_SHIFT) +#define CHGCTRL2_VCHGR_RC_MASK BIT(CHGCTRL2_VCHGR_RC_SHIFT) /* MAX14577 CHGCTRL3 register */ #define CHGCTRL3_MBCCVWRC_SHIFT 0 @@ -230,7 +240,7 @@ enum max14577_charger_reg { #define CHGCTRL4_MBCICHWRCH_SHIFT 0 #define CHGCTRL4_MBCICHWRCH_MASK (0xf << CHGCTRL4_MBCICHWRCH_SHIFT) #define CHGCTRL4_MBCICHWRCL_SHIFT 4 -#define CHGCTRL4_MBCICHWRCL_MASK (0x1 << CHGCTRL4_MBCICHWRCL_SHIFT) +#define CHGCTRL4_MBCICHWRCL_MASK BIT(CHGCTRL4_MBCICHWRCL_SHIFT) /* MAX14577 CHGCTRL5 register */ #define CHGCTRL5_EOCS_SHIFT 0 @@ -238,7 +248,7 @@ enum max14577_charger_reg { /* MAX14577 CHGCTRL6 register */ #define CHGCTRL6_AUTOSTOP_SHIFT 5 -#define CHGCTRL6_AUTOSTOP_MASK (0x1 << CHGCTRL6_AUTOSTOP_SHIFT) +#define CHGCTRL6_AUTOSTOP_MASK BIT(CHGCTRL6_AUTOSTOP_SHIFT) /* MAX14577 CHGCTRL7 register */ #define CHGCTRL7_OTPCGHCVS_SHIFT 0 @@ -253,6 +263,70 @@ enum max14577_charger_reg { /* MAX14577 regulator SFOUT LDO voltage, fixed, uV */ #define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000 +/* Slave addr = 0x46: PMIC */ +enum max77836_pmic_reg { + MAX77836_PMIC_REG_PMIC_ID = 0x20, + MAX77836_PMIC_REG_PMIC_REV = 0x21, + MAX77836_PMIC_REG_INTSRC = 0x22, + MAX77836_PMIC_REG_INTSRC_MASK = 0x23, + MAX77836_PMIC_REG_TOPSYS_INT = 0x24, + MAX77836_PMIC_REG_TOPSYS_INT_MASK = 0x26, + MAX77836_PMIC_REG_TOPSYS_STAT = 0x28, + MAX77836_PMIC_REG_MRSTB_CNTL = 0x2A, + MAX77836_PMIC_REG_LSCNFG = 0x2B, + + MAX77836_LDO_REG_CNFG1_LDO1 = 0x51, + MAX77836_LDO_REG_CNFG2_LDO1 = 0x52, + MAX77836_LDO_REG_CNFG1_LDO2 = 0x53, + MAX77836_LDO_REG_CNFG2_LDO2 = 0x54, + MAX77836_LDO_REG_CNFG_LDO_BIAS = 0x55, + + MAX77836_COMP_REG_COMP1 = 0x60, + + MAX77836_PMIC_REG_END, +}; + +#define MAX77836_INTSRC_MASK_TOP_INT_SHIFT 1 +#define MAX77836_INTSRC_MASK_MUIC_CHG_INT_SHIFT 3 +#define MAX77836_INTSRC_MASK_TOP_INT_MASK BIT(MAX77836_INTSRC_MASK_TOP_INT_SHIFT) +#define MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK BIT(MAX77836_INTSRC_MASK_MUIC_CHG_INT_SHIFT) + +/* MAX77836 PMIC interrupts */ +#define MAX77836_TOPSYS_INT_T120C_SHIFT 0 +#define MAX77836_TOPSYS_INT_T140C_SHIFT 1 +#define MAX77836_TOPSYS_INT_T120C_MASK BIT(MAX77836_TOPSYS_INT_T120C_SHIFT) +#define MAX77836_TOPSYS_INT_T140C_MASK BIT(MAX77836_TOPSYS_INT_T140C_SHIFT) + +/* Slave addr = 0x6C: Fuel-Gauge/Battery */ +enum max77836_fg_reg { + MAX77836_FG_REG_VCELL_MSB = 0x02, + MAX77836_FG_REG_VCELL_LSB = 0x03, + MAX77836_FG_REG_SOC_MSB = 0x04, + MAX77836_FG_REG_SOC_LSB = 0x05, + MAX77836_FG_REG_MODE_H = 0x06, + MAX77836_FG_REG_MODE_L = 0x07, + MAX77836_FG_REG_VERSION_MSB = 0x08, + MAX77836_FG_REG_VERSION_LSB = 0x09, + MAX77836_FG_REG_HIBRT_H = 0x0A, + MAX77836_FG_REG_HIBRT_L = 0x0B, + MAX77836_FG_REG_CONFIG_H = 0x0C, + MAX77836_FG_REG_CONFIG_L = 0x0D, + MAX77836_FG_REG_VALRT_MIN = 0x14, + MAX77836_FG_REG_VALRT_MAX = 0x15, + MAX77836_FG_REG_CRATE_MSB = 0x16, + MAX77836_FG_REG_CRATE_LSB = 0x17, + MAX77836_FG_REG_VRESET = 0x18, + MAX77836_FG_REG_FGID = 0x19, + MAX77836_FG_REG_STATUS_H = 0x1A, + MAX77836_FG_REG_STATUS_L = 0x1B, + /* + * TODO: TABLE registers + * TODO: CMD register + */ + + MAX77836_FG_REG_END, +}; + enum max14577_irq { /* INT1 */ MAX14577_IRQ_INT1_ADC, @@ -272,17 +346,24 @@ enum max14577_irq { MAX14577_IRQ_INT3_OVP, MAX14577_IRQ_INT3_MBCCHGERR, + /* TOPSYS_INT, only MAX77836 */ + MAX77836_IRQ_TOPSYS_T140C, + MAX77836_IRQ_TOPSYS_T120C, + MAX14577_IRQ_NUM, }; struct max14577 { struct device *dev; struct i2c_client *i2c; /* Slave addr = 0x4A */ + struct i2c_client *i2c_pmic; /* Slave addr = 0x46 */ enum maxim_device_type dev_type; - struct regmap *regmap; + struct regmap *regmap; /* For MUIC and Charger */ + struct regmap *regmap_pmic; - struct regmap_irq_chip_data *irq_data; + struct regmap_irq_chip_data *irq_data; /* For MUIC and Charger */ + struct regmap_irq_chip_data *irq_data_pmic; int irq; }; diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h index 736d39c3ec0d..08b449159fd1 100644 --- a/include/linux/mfd/max14577.h +++ b/include/linux/mfd/max14577.h @@ -1,7 +1,7 @@ /* - * max14577.h - Driver for the Maxim 14577 + * max14577.h - Driver for the Maxim 14577/77836 * - * Copyright (C) 2013 Samsung Electrnoics + * Copyright (C) 2014 Samsung Electrnoics * Chanwoo Choi * Krzysztof Kozlowski * @@ -20,6 +20,9 @@ * MAX14577 has MUIC, Charger devices. * The devices share the same I2C bus and interrupt line * included in this mfd driver. + * + * MAX77836 has additional PMIC and Fuel-Gauge on different I2C slave + * addresses. */ #ifndef __MAX14577_H__ -- cgit v1.2.3 From 4706a5253bcc502a5889feb98392ea7b15dd936e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 11:17:19 +0200 Subject: extcon: max14577: Add support for MAX77836 Add support for MAX77836 chipset to the max14577 extcon driver. The MAX77836 MUIC has additional interrupts (VIDRM, ADC1K) so IRQ handling is split up into two functions: max14577_parse_irq() and max77836_parse_irq(). Signed-off-by: Krzysztof Kozlowski Acked-by: Chanwoo Choi Tested-by: Chanwoo Choi Signed-off-by: Lee Jones --- drivers/extcon/Kconfig | 4 +- drivers/extcon/extcon-max14577.c | 109 +++++++++++++++++++++++++++++------ drivers/mfd/max14577.c | 1 + include/linux/mfd/max14577-private.h | 3 + 4 files changed, 96 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index be56e8ac95e6..aebde489c291 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -28,13 +28,13 @@ config EXTCON_ADC_JACK Say Y here to enable extcon device driver based on ADC values. config EXTCON_MAX14577 - tristate "MAX14577 EXTCON Support" + tristate "MAX14577/77836 EXTCON Support" depends on MFD_MAX14577 select IRQ_DOMAIN select REGMAP_I2C help If you say yes here you get support for the MUIC device of - Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory + Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory detector and switch. config EXTCON_MAX77693 diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index 1513013a92f1..c76734a70171 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -1,8 +1,9 @@ /* - * extcon-max14577.c - MAX14577 extcon driver to support MAX14577 MUIC + * extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC * - * Copyright (C) 2013 Samsung Electrnoics + * Copyright (C) 2013,2014 Samsung Electrnoics * Chanwoo Choi + * Krzysztof Kozlowski * * 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 @@ -62,6 +63,19 @@ static struct max14577_muic_irq max14577_muic_irqs[] = { { MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" }, }; +static struct max14577_muic_irq max77836_muic_irqs[] = { + { MAX14577_IRQ_INT1_ADC, "muic-ADC" }, + { MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" }, + { MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" }, + { MAX77836_IRQ_INT1_ADC1K, "muic-ADC1K" }, + { MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" }, + { MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" }, + { MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" }, + { MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" }, + { MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" }, + { MAX77836_IRQ_INT2_VIDRM, "muic-VIDRM" }, +}; + struct max14577_muic_info { struct device *dev; struct max14577 *max14577; @@ -529,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work) return; } -static irqreturn_t max14577_muic_irq_handler(int irq, void *data) +/* + * Sets irq_adc or irq_chg in max14577_muic_info and returns 1. + * Returns 0 if irq_type does not match registered IRQ for this device type. + */ +static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type) { - struct max14577_muic_info *info = data; - int i, irq_type = -1; - - /* - * We may be called multiple times for different nested IRQ-s. - * Including changes in INT1_ADC and INT2_CGHTYP at once. - * However we only need to know whether it was ADC, charger - * or both interrupts so decode IRQ and turn on proper flags. - */ - for (i = 0; i < info->muic_irqs_num; i++) - if (irq == info->muic_irqs[i].virq) - irq_type = info->muic_irqs[i].irq; - switch (irq_type) { case MAX14577_IRQ_INT1_ADC: case MAX14577_IRQ_INT1_ADCLOW: @@ -551,7 +556,7 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data) /* Handle all of accessory except for type of charger accessory */ info->irq_adc = true; - break; + return 1; case MAX14577_IRQ_INT2_CHGTYP: case MAX14577_IRQ_INT2_CHGDETRUN: case MAX14577_IRQ_INT2_DCDTMR: @@ -559,8 +564,62 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data) case MAX14577_IRQ_INT2_VBVOLT: /* Handle charger accessory */ info->irq_chg = true; + return 1; + default: + return 0; + } +} + +/* + * Sets irq_adc or irq_chg in max14577_muic_info and returns 1. + * Returns 0 if irq_type does not match registered IRQ for this device type. + */ +static int max77836_parse_irq(struct max14577_muic_info *info, int irq_type) +{ + /* First check common max14577 interrupts */ + if (max14577_parse_irq(info, irq_type)) + return 1; + + switch (irq_type) { + case MAX77836_IRQ_INT1_ADC1K: + info->irq_adc = true; + return 1; + case MAX77836_IRQ_INT2_VIDRM: + /* Handle charger accessory */ + info->irq_chg = true; + return 1; + default: + return 0; + } +} + +static irqreturn_t max14577_muic_irq_handler(int irq, void *data) +{ + struct max14577_muic_info *info = data; + int i, irq_type = -1; + bool irq_parsed; + + /* + * We may be called multiple times for different nested IRQ-s. + * Including changes in INT1_ADC and INT2_CGHTYP at once. + * However we only need to know whether it was ADC, charger + * or both interrupts so decode IRQ and turn on proper flags. + */ + for (i = 0; i < info->muic_irqs_num; i++) + if (irq == info->muic_irqs[i].virq) + irq_type = info->muic_irqs[i].irq; + + switch (info->max14577->dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + irq_parsed = max77836_parse_irq(info, irq_type); break; + case MAXIM_DEVICE_TYPE_MAX14577: default: + irq_parsed = max14577_parse_irq(info, irq_type); + break; + } + + if (!irq_parsed) { dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n", irq_type); return IRQ_HANDLED; @@ -646,6 +705,10 @@ static int max14577_muic_probe(struct platform_device *pdev) INIT_WORK(&info->irq_work, max14577_muic_irq_work); switch (max14577->dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + info->muic_irqs = max77836_muic_irqs; + info->muic_irqs_num = ARRAY_SIZE(max77836_muic_irqs); + break; case MAXIM_DEVICE_TYPE_MAX14577: default: info->muic_irqs = max14577_muic_irqs; @@ -744,6 +807,13 @@ static int max14577_muic_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id max14577_muic_id[] = { + { "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, }, + { "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, }, + { } +}; +MODULE_DEVICE_TABLE(platform, max14577_muic_id); + static struct platform_driver max14577_muic_driver = { .driver = { .name = "max14577-muic", @@ -751,11 +821,12 @@ static struct platform_driver max14577_muic_driver = { }, .probe = max14577_muic_probe, .remove = max14577_muic_remove, + .id_table = max14577_muic_id, }; module_platform_driver(max14577_muic_driver); -MODULE_DESCRIPTION("MAXIM 14577 Extcon driver"); -MODULE_AUTHOR("Chanwoo Choi "); +MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver"); +MODULE_AUTHOR("Chanwoo Choi , Krzysztof Kozlowski "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:extcon-max14577"); diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 20e3b2d81bf0..484d372a4892 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -147,6 +147,7 @@ static const struct regmap_irq max77836_muic_irqs[] = { { .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, }, { .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, }, { .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, }, + { .reg_offset = 0, .mask = MAX77836_INT1_ADC1K_MASK, }, /* INT2 interrupts */ { .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, }, { .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, }, diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index e301bd19b067..a557ae27d8a8 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -87,6 +87,7 @@ enum max14577_muic_charger_type { #define MAX14577_INT1_ADC_MASK BIT(0) #define MAX14577_INT1_ADCLOW_MASK BIT(1) #define MAX14577_INT1_ADCERR_MASK BIT(2) +#define MAX77836_INT1_ADC1K_MASK BIT(3) #define MAX14577_INT2_CHGTYP_MASK BIT(0) #define MAX14577_INT2_CHGDETRUN_MASK BIT(1) @@ -332,6 +333,7 @@ enum max14577_irq { MAX14577_IRQ_INT1_ADC, MAX14577_IRQ_INT1_ADCLOW, MAX14577_IRQ_INT1_ADCERR, + MAX77836_IRQ_INT1_ADC1K, /* INT2 */ MAX14577_IRQ_INT2_CHGTYP, @@ -339,6 +341,7 @@ enum max14577_irq { MAX14577_IRQ_INT2_DCDTMR, MAX14577_IRQ_INT2_DBCHG, MAX14577_IRQ_INT2_VBVOLT, + MAX77836_IRQ_INT2_VIDRM, /* INT3 */ MAX14577_IRQ_INT3_EOC, -- cgit v1.2.3 From 8a82b408acad29161c43072727151d373e68116a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 11:17:20 +0200 Subject: regulator: max14577: Add support for MAX77836 regulators Add support for MAX77836 chipset and its additional two LDO regulators. These LDO regulators are controlled by the PMIC block with additional regmap (different I2C slave address). The MAX77836 charger and safeout regulators are almost identical to MAX14577. The registers layout is the same, except values for charger's current. The patch adds simple mapping between device type and supported current by the charger regulator. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Mark Brown Signed-off-by: Lee Jones --- drivers/regulator/Kconfig | 7 +- drivers/regulator/max14577.c | 277 ++++++++++++++++++++++++++++++----- include/linux/mfd/max14577-private.h | 32 ++++ include/linux/mfd/max14577.h | 12 +- 4 files changed, 289 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 903eb37f047a..f0cc9e6dac3a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -266,11 +266,12 @@ config REGULATOR_LP8788 This driver supports LP8788 voltage regulator chip. config REGULATOR_MAX14577 - tristate "Maxim 14577 regulator" + tristate "Maxim 14577/77836 regulator" depends on MFD_MAX14577 help - This driver controls a Maxim 14577 regulator via I2C bus. - The regulators include safeout LDO and current regulator 'CHARGER'. + This driver controls a Maxim MAX14577/77836 regulator via I2C bus. + The MAX14577 regulators include safeout LDO and charger current + regulator. The MAX77836 has two additional LDOs. config REGULATOR_MAX1586 tristate "Maxim 1586/1587 voltage regulator" diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c index ed60baaeceec..5d9c605cf534 100644 --- a/drivers/regulator/max14577.c +++ b/drivers/regulator/max14577.c @@ -1,5 +1,5 @@ /* - * max14577.c - Regulator driver for the Maxim 14577 + * max14577.c - Regulator driver for the Maxim 14577/77836 * * Copyright (C) 2013,2014 Samsung Electronics * Krzysztof Kozlowski @@ -22,6 +22,42 @@ #include #include +/* + * Valid limits of current for max14577 and max77836 chargers. + * They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4 + * register for given chipset. + */ +struct maxim_charger_current { + /* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */ + unsigned int min; + /* + * Minimal current when high setting is active, + * set in CHGCTRL4/MBCICHWRCH, uA + */ + unsigned int high_start; + /* Value of one step in high setting, uA */ + unsigned int high_step; + /* Maximum current of high setting, uA */ + unsigned int max; +}; + +/* Table of valid charger currents for different Maxim chipsets */ +static const struct maxim_charger_current maxim_charger_currents[] = { + [MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 }, + [MAXIM_DEVICE_TYPE_MAX14577] = { + .min = MAX14577_REGULATOR_CURRENT_LIMIT_MIN, + .high_start = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START, + .high_step = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP, + .max = MAX14577_REGULATOR_CURRENT_LIMIT_MAX, + }, + [MAXIM_DEVICE_TYPE_MAX77836] = { + .min = MAX77836_REGULATOR_CURRENT_LIMIT_MIN, + .high_start = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START, + .high_step = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP, + .max = MAX77836_REGULATOR_CURRENT_LIMIT_MAX, + }, +}; + static int max14577_reg_is_enabled(struct regulator_dev *rdev) { int rid = rdev_get_id(rdev); @@ -47,6 +83,9 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev) { u8 reg_data; struct regmap *rmap = rdev->regmap; + struct max14577 *max14577 = rdev_get_drvdata(rdev); + const struct maxim_charger_current *limits = + &maxim_charger_currents[max14577->dev_type]; if (rdev_get_id(rdev) != MAX14577_CHARGER) return -EINVAL; @@ -54,12 +93,11 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev) max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL4, ®_data); if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0) - return MAX14577_REGULATOR_CURRENT_LIMIT_MIN; + return limits->min; reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >> CHGCTRL4_MBCICHWRCH_SHIFT); - return MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START + - reg_data * MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP; + return limits->high_start + reg_data * limits->high_step; } static int max14577_reg_set_current_limit(struct regulator_dev *rdev, @@ -67,33 +105,39 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev, { int i, current_bits = 0xf; u8 reg_data; + struct max14577 *max14577 = rdev_get_drvdata(rdev); + const struct maxim_charger_current *limits = + &maxim_charger_currents[max14577->dev_type]; if (rdev_get_id(rdev) != MAX14577_CHARGER) return -EINVAL; - if (min_uA > MAX14577_REGULATOR_CURRENT_LIMIT_MAX || - max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_MIN) + if (min_uA > limits->max || max_uA < limits->min) return -EINVAL; - if (max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START) { - /* Less than 200 mA, so set 90mA (turn only Low Bit off) */ + if (max_uA < limits->high_start) { + /* + * Less than high_start, + * so set the minimal current (turn only Low Bit off) + */ u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT; return max14577_update_reg(rdev->regmap, MAX14577_CHG_REG_CHG_CTRL4, CHGCTRL4_MBCICHWRCL_MASK, reg_data); } - /* max_uA is in range: , so search for - * valid current starting from LIMIT_MAX. */ - for (i = MAX14577_REGULATOR_CURRENT_LIMIT_MAX; - i >= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START; - i -= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP) { + /* + * max_uA is in range: , so search for + * valid current starting from maximum current. + */ + for (i = limits->max; i >= limits->high_start; i -= limits->high_step) { if (i <= max_uA) break; current_bits--; } BUG_ON(current_bits < 0); /* Cannot happen */ - /* Turn Low Bit on (use range 200mA-950 mA) */ + + /* Turn Low Bit on (use range high_start-max)... */ reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT; /* and set proper High Bits */ reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT; @@ -118,7 +162,7 @@ static struct regulator_ops max14577_charger_ops = { .set_current_limit = max14577_reg_set_current_limit, }; -static const struct regulator_desc supported_regulators[] = { +static const struct regulator_desc max14577_supported_regulators[] = { [MAX14577_SAFEOUT] = { .name = "SAFEOUT", .id = MAX14577_SAFEOUT, @@ -141,16 +185,88 @@ static const struct regulator_desc supported_regulators[] = { }, }; +static struct regulator_ops max77836_ldo_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + /* TODO: add .set_suspend_mode */ +}; + +static const struct regulator_desc max77836_supported_regulators[] = { + [MAX14577_SAFEOUT] = { + .name = "SAFEOUT", + .id = MAX14577_SAFEOUT, + .ops = &max14577_safeout_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = 1, + .min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE, + .enable_reg = MAX14577_REG_CONTROL2, + .enable_mask = CTRL2_SFOUTORD_MASK, + }, + [MAX14577_CHARGER] = { + .name = "CHARGER", + .id = MAX14577_CHARGER, + .ops = &max14577_charger_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + .enable_reg = MAX14577_CHG_REG_CHG_CTRL2, + .enable_mask = CHGCTRL2_MBCHOSTEN_MASK, + }, + [MAX77836_LDO1] = { + .name = "LDO1", + .id = MAX77836_LDO1, + .ops = &max77836_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM, + .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN, + .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP, + .enable_reg = MAX77836_LDO_REG_CNFG1_LDO1, + .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK, + .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO1, + .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK, + }, + [MAX77836_LDO2] = { + .name = "LDO2", + .id = MAX77836_LDO2, + .ops = &max77836_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM, + .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN, + .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP, + .enable_reg = MAX77836_LDO_REG_CNFG1_LDO2, + .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK, + .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO2, + .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK, + }, +}; + #ifdef CONFIG_OF static struct of_regulator_match max14577_regulator_matches[] = { { .name = "SAFEOUT", }, { .name = "CHARGER", }, }; -static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev) +static struct of_regulator_match max77836_regulator_matches[] = { + { .name = "SAFEOUT", }, + { .name = "CHARGER", }, + { .name = "LDO1", }, + { .name = "LDO2", }, +}; + +static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev, + enum maxim_device_type dev_type) { int ret; struct device_node *np; + struct of_regulator_match *regulator_matches; + unsigned int regulator_matches_size; np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); if (!np) { @@ -158,8 +274,19 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev) return -EINVAL; } - ret = of_regulator_match(&pdev->dev, np, max14577_regulator_matches, - MAX14577_REG_MAX); + switch (dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + regulator_matches = max77836_regulator_matches; + regulator_matches_size = ARRAY_SIZE(max77836_regulator_matches); + break; + case MAXIM_DEVICE_TYPE_MAX14577: + default: + regulator_matches = max14577_regulator_matches; + regulator_matches_size = ARRAY_SIZE(max14577_regulator_matches); + } + + ret = of_regulator_match(&pdev->dev, np, regulator_matches, + regulator_matches_size); if (ret < 0) dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); else @@ -170,31 +297,74 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev) return ret; } -static inline struct regulator_init_data *match_init_data(int index) +static inline struct regulator_init_data *match_init_data(int index, + enum maxim_device_type dev_type) { - return max14577_regulator_matches[index].init_data; + switch (dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + return max77836_regulator_matches[index].init_data; + + case MAXIM_DEVICE_TYPE_MAX14577: + default: + return max14577_regulator_matches[index].init_data; + } } -static inline struct device_node *match_of_node(int index) +static inline struct device_node *match_of_node(int index, + enum maxim_device_type dev_type) { - return max14577_regulator_matches[index].of_node; + switch (dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + return max77836_regulator_matches[index].of_node; + + case MAXIM_DEVICE_TYPE_MAX14577: + default: + return max14577_regulator_matches[index].of_node; + } } #else /* CONFIG_OF */ -static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev) +static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev, + enum maxim_device_type dev_type) { return 0; } -static inline struct regulator_init_data *match_init_data(int index) +static inline struct regulator_init_data *match_init_data(int index, + enum maxim_device_type dev_type) { return NULL; } -static inline struct device_node *match_of_node(int index) +static inline struct device_node *match_of_node(int index, + enum maxim_device_type dev_type) { return NULL; } #endif /* CONFIG_OF */ +/** + * Registers for regulators of max77836 use different I2C slave addresses so + * different regmaps must be used for them. + * + * Returns proper regmap for accessing regulator passed by id. + */ +static struct regmap *max14577_get_regmap(struct max14577 *max14577, + int reg_id) +{ + switch (max14577->dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + switch (reg_id) { + case MAX77836_SAFEOUT ... MAX77836_CHARGER: + return max14577->regmap; + default: + /* MAX77836_LDO1 ... MAX77836_LDO2 */ + return max14577->regmap_pmic; + } + + case MAXIM_DEVICE_TYPE_MAX14577: + default: + return max14577->regmap; + } +} static int max14577_regulator_probe(struct platform_device *pdev) { @@ -202,15 +372,29 @@ static int max14577_regulator_probe(struct platform_device *pdev) struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev); int i, ret; struct regulator_config config = {}; + const struct regulator_desc *supported_regulators; + unsigned int supported_regulators_size; + enum maxim_device_type dev_type = max14577->dev_type; - ret = max14577_regulator_dt_parse_pdata(pdev); + ret = max14577_regulator_dt_parse_pdata(pdev, dev_type); if (ret) return ret; + switch (dev_type) { + case MAXIM_DEVICE_TYPE_MAX77836: + supported_regulators = max77836_supported_regulators; + supported_regulators_size = ARRAY_SIZE(max77836_supported_regulators); + break; + case MAXIM_DEVICE_TYPE_MAX14577: + default: + supported_regulators = max14577_supported_regulators; + supported_regulators_size = ARRAY_SIZE(max14577_supported_regulators); + } + config.dev = &pdev->dev; - config.regmap = max14577->regmap; + config.driver_data = max14577; - for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) { + for (i = 0; i < supported_regulators_size; i++) { struct regulator_dev *regulator; /* * Index of supported_regulators[] is also the id and must @@ -220,17 +404,19 @@ static int max14577_regulator_probe(struct platform_device *pdev) config.init_data = pdata->regulators[i].initdata; config.of_node = pdata->regulators[i].of_node; } else { - config.init_data = match_init_data(i); - config.of_node = match_of_node(i); + config.init_data = match_init_data(i, dev_type); + config.of_node = match_of_node(i, dev_type); } + config.regmap = max14577_get_regmap(max14577, + supported_regulators[i].id); regulator = devm_regulator_register(&pdev->dev, &supported_regulators[i], &config); if (IS_ERR(regulator)) { ret = PTR_ERR(regulator); dev_err(&pdev->dev, - "Regulator init failed for ID %d with error: %d\n", - i, ret); + "Regulator init failed for %d/%s with error: %d\n", + i, supported_regulators[i].name, ret); return ret; } } @@ -238,20 +424,41 @@ static int max14577_regulator_probe(struct platform_device *pdev) return ret; } +static const struct platform_device_id max14577_regulator_id[] = { + { "max14577-regulator", MAXIM_DEVICE_TYPE_MAX14577, }, + { "max77836-regulator", MAXIM_DEVICE_TYPE_MAX77836, }, + { } +}; +MODULE_DEVICE_TABLE(platform, max14577_regulator_id); + static struct platform_driver max14577_regulator_driver = { .driver = { .owner = THIS_MODULE, .name = "max14577-regulator", }, - .probe = max14577_regulator_probe, + .probe = max14577_regulator_probe, + .id_table = max14577_regulator_id, }; static int __init max14577_regulator_init(void) { + /* Check for valid values for charger */ BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START + MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf != MAX14577_REGULATOR_CURRENT_LIMIT_MAX); - BUILD_BUG_ON(ARRAY_SIZE(supported_regulators) != MAX14577_REG_MAX); + BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START + + MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf != + MAX77836_REGULATOR_CURRENT_LIMIT_MAX); + /* Valid charger current values must be provided for each chipset */ + BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM); + + BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM); + BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM); + + BUILD_BUG_ON(MAX77836_REGULATOR_LDO_VOLTAGE_MIN + + (MAX77836_REGULATOR_LDO_VOLTAGE_STEP * + (MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM - 1)) != + MAX77836_REGULATOR_LDO_VOLTAGE_MAX); return platform_driver_register(&max14577_regulator_driver); } @@ -264,6 +471,6 @@ static void __exit max14577_regulator_exit(void) module_exit(max14577_regulator_exit); MODULE_AUTHOR("Krzysztof Kozlowski "); -MODULE_DESCRIPTION("MAXIM 14577 regulator driver"); +MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:max14577-regulator"); diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index a557ae27d8a8..499253604026 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -261,9 +261,21 @@ enum max14577_charger_reg { #define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP 50000 #define MAX14577_REGULATOR_CURRENT_LIMIT_MAX 950000 +/* MAX77836 regulator current limits (as in CHGCTRL4 register), uA */ +#define MAX77836_REGULATOR_CURRENT_LIMIT_MIN 45000 +#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START 100000 +#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP 25000 +#define MAX77836_REGULATOR_CURRENT_LIMIT_MAX 475000 + /* MAX14577 regulator SFOUT LDO voltage, fixed, uV */ #define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000 +/* MAX77836 regulator LDOx voltage, uV */ +#define MAX77836_REGULATOR_LDO_VOLTAGE_MIN 800000 +#define MAX77836_REGULATOR_LDO_VOLTAGE_MAX 3950000 +#define MAX77836_REGULATOR_LDO_VOLTAGE_STEP 50000 +#define MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM 64 + /* Slave addr = 0x46: PMIC */ enum max77836_pmic_reg { MAX77836_PMIC_REG_PMIC_ID = 0x20, @@ -298,6 +310,26 @@ enum max77836_pmic_reg { #define MAX77836_TOPSYS_INT_T120C_MASK BIT(MAX77836_TOPSYS_INT_T120C_SHIFT) #define MAX77836_TOPSYS_INT_T140C_MASK BIT(MAX77836_TOPSYS_INT_T140C_SHIFT) +/* LDO1/LDO2 CONFIG1 register */ +#define MAX77836_CNFG1_LDO_PWRMD_SHIFT 6 +#define MAX77836_CNFG1_LDO_TV_SHIFT 0 +#define MAX77836_CNFG1_LDO_PWRMD_MASK (0x3 << MAX77836_CNFG1_LDO_PWRMD_SHIFT) +#define MAX77836_CNFG1_LDO_TV_MASK (0x3f << MAX77836_CNFG1_LDO_TV_SHIFT) + +/* LDO1/LDO2 CONFIG2 register */ +#define MAX77836_CNFG2_LDO_OVCLMPEN_SHIFT 7 +#define MAX77836_CNFG2_LDO_ALPMEN_SHIFT 6 +#define MAX77836_CNFG2_LDO_COMP_SHIFT 4 +#define MAX77836_CNFG2_LDO_POK_SHIFT 3 +#define MAX77836_CNFG2_LDO_ADE_SHIFT 1 +#define MAX77836_CNFG2_LDO_SS_SHIFT 0 +#define MAX77836_CNFG2_LDO_OVCLMPEN_MASK BIT(MAX77836_CNFG2_LDO_OVCLMPEN_SHIFT) +#define MAX77836_CNFG2_LDO_ALPMEN_MASK BIT(MAX77836_CNFG2_LDO_ALPMEN_SHIFT) +#define MAX77836_CNFG2_LDO_COMP_MASK (0x3 << MAX77836_CNFG2_LDO_COMP_SHIFT) +#define MAX77836_CNFG2_LDO_POK_MASK BIT(MAX77836_CNFG2_LDO_POK_SHIFT) +#define MAX77836_CNFG2_LDO_ADE_MASK BIT(MAX77836_CNFG2_LDO_ADE_SHIFT) +#define MAX77836_CNFG2_LDO_SS_MASK BIT(MAX77836_CNFG2_LDO_SS_SHIFT) + /* Slave addr = 0x6C: Fuel-Gauge/Battery */ enum max77836_fg_reg { MAX77836_FG_REG_VCELL_MSB = 0x02, diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h index 08b449159fd1..c83fbed1c7b6 100644 --- a/include/linux/mfd/max14577.h +++ b/include/linux/mfd/max14577.h @@ -35,7 +35,17 @@ enum max14577_regulators { MAX14577_SAFEOUT = 0, MAX14577_CHARGER, - MAX14577_REG_MAX, + MAX14577_REGULATOR_NUM, +}; + +/* MAX77836 regulator IDs */ +enum max77836_regulators { + MAX77836_SAFEOUT = 0, + MAX77836_CHARGER, + MAX77836_LDO1, + MAX77836_LDO2, + + MAX77836_REGULATOR_NUM, }; struct max14577_regulator_platform_data { -- cgit v1.2.3 From e37559b22c63b557d242bfa1a07ab1b8f7d5d9f1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Apr 2014 02:47:21 -0300 Subject: [media] vb2: stop_streaming should return void The vb2 core ignores any return code from the stop_streaming op. And there really isn't anything it can do anyway in case of an error. So change the return type to void and update any drivers that implement it. The int return gave drivers the idea that this operation could actually fail, but that's really not the case. The pwc amd sdr-msi3101 drivers both had this construction: if (mutex_lock_interruptible(&s->v4l2_lock)) return -ERESTARTSYS; This has been updated to just call mutex_lock(). The stop_streaming op expects this to really stop streaming and I very much doubt this will work reliably if stop_streaming just returns without really stopping the DMA. Signed-off-by: Hans Verkuil Acked-by: Pawel Osciak Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-pci-skeleton.c | 3 +-- drivers/media/pci/sta2x11/sta2x11_vip.c | 3 +-- drivers/media/platform/blackfin/bfin_capture.c | 3 +-- drivers/media/platform/coda.c | 4 +--- drivers/media/platform/davinci/vpbe_display.c | 5 ++--- drivers/media/platform/davinci/vpif_capture.c | 6 ++---- drivers/media/platform/davinci/vpif_display.c | 6 ++---- drivers/media/platform/exynos-gsc/gsc-m2m.c | 4 +--- drivers/media/platform/exynos4-is/fimc-capture.c | 6 +++--- drivers/media/platform/exynos4-is/fimc-isp-video.c | 5 ++--- drivers/media/platform/exynos4-is/fimc-lite.c | 6 +++--- drivers/media/platform/exynos4-is/fimc-m2m.c | 3 +-- drivers/media/platform/marvell-ccic/mcam-core.c | 7 +++---- drivers/media/platform/mem2mem_testdev.c | 5 ++--- drivers/media/platform/s3c-camif/camif-capture.c | 4 ++-- drivers/media/platform/s5p-jpeg/jpeg-core.c | 4 +--- drivers/media/platform/s5p-mfc/s5p_mfc_dec.c | 3 +-- drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 3 +-- drivers/media/platform/s5p-tv/mixer_video.c | 3 +-- drivers/media/platform/soc_camera/atmel-isi.c | 6 ++---- drivers/media/platform/soc_camera/mx2_camera.c | 4 +--- drivers/media/platform/soc_camera/mx3_camera.c | 4 +--- drivers/media/platform/soc_camera/rcar_vin.c | 4 +--- .../platform/soc_camera/sh_mobile_ceu_camera.c | 4 ++-- drivers/media/platform/vivi.c | 3 +-- drivers/media/platform/vsp1/vsp1_video.c | 4 +--- drivers/media/usb/em28xx/em28xx-v4l.h | 2 +- drivers/media/usb/em28xx/em28xx-video.c | 8 ++------ drivers/media/usb/pwc/pwc-if.c | 7 ++----- drivers/media/usb/s2255/s2255drv.c | 5 ++--- drivers/media/usb/stk1160/stk1160-v4l.c | 4 ++-- drivers/media/usb/usbtv/usbtv-video.c | 9 +++----- drivers/media/v4l2-core/videobuf2-core.c | 2 +- drivers/staging/media/davinci_vpfe/vpfe_video.c | 3 +-- drivers/staging/media/dt3155v4l/dt3155v4l.c | 3 +-- drivers/staging/media/go7007/go7007-v4l2.c | 3 +-- drivers/staging/media/msi3101/sdr-msi3101.c | 24 ++++++++-------------- drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c | 7 ++----- drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c | 3 +-- drivers/staging/media/solo6x10/solo6x10-v4l2.c | 3 +-- include/media/videobuf2-core.h | 2 +- 41 files changed, 69 insertions(+), 128 deletions(-) (limited to 'include') diff --git a/Documentation/video4linux/v4l2-pci-skeleton.c b/Documentation/video4linux/v4l2-pci-skeleton.c index 80251dc2c6cf..53dd346586f6 100644 --- a/Documentation/video4linux/v4l2-pci-skeleton.c +++ b/Documentation/video4linux/v4l2-pci-skeleton.c @@ -269,7 +269,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued * and passed on to the vb2 framework marked as STATE_ERROR. */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct skeleton *skel = vb2_get_drv_priv(vq); @@ -277,7 +277,6 @@ static int stop_streaming(struct vb2_queue *vq) /* Release all active buffers */ return_all_buffers(skel, VB2_BUF_STATE_ERROR); - return 0; } /* diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index bb11443ed63e..7559951b2ea4 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -357,7 +357,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct sta2x11_vip *vip = vb2_get_drv_priv(vq); struct vip_buffer *vip_buf, *node; @@ -374,7 +374,6 @@ static int stop_streaming(struct vb2_queue *vq) list_del(&vip_buf->list); } spin_unlock(&vip->lock); - return 0; } static struct vb2_ops vip_video_qops = { diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 200bec91182e..dfb09d4ee6d1 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -427,7 +427,7 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; } -static int bcap_stop_streaming(struct vb2_queue *vq) +static void bcap_stop_streaming(struct vb2_queue *vq) { struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); struct ppi_if *ppi = bcap_dev->ppi; @@ -452,7 +452,6 @@ static int bcap_stop_streaming(struct vb2_queue *vq) list_del(&bcap_dev->cur_frm->list); vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR); } - return 0; } static struct vb2_ops bcap_video_qops = { diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 3e5199ee5d25..d9b1a0409076 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -2269,7 +2269,7 @@ out: return ret; } -static int coda_stop_streaming(struct vb2_queue *q) +static void coda_stop_streaming(struct vb2_queue *q) { struct coda_ctx *ctx = vb2_get_drv_priv(q); struct coda_dev *dev = ctx->dev; @@ -2295,8 +2295,6 @@ static int coda_stop_streaming(struct vb2_queue *q) ctx->bitstream.vaddr, ctx->bitstream.size); ctx->runcounter = 0; } - - return 0; } static struct vb2_ops coda_qops = { diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index a9ad949d0c19..4025b1b8aff4 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -368,7 +368,7 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) return ret; } -static int vpbe_stop_streaming(struct vb2_queue *vq) +static void vpbe_stop_streaming(struct vb2_queue *vq) { struct vpbe_fh *fh = vb2_get_drv_priv(vq); struct vpbe_layer *layer = fh->layer; @@ -376,7 +376,7 @@ static int vpbe_stop_streaming(struct vb2_queue *vq) unsigned long flags; if (!vb2_is_streaming(vq)) - return 0; + return; /* release all active buffers */ spin_lock_irqsave(&disp->dma_queue_lock, flags); @@ -398,7 +398,6 @@ static int vpbe_stop_streaming(struct vb2_queue *vq) vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&disp->dma_queue_lock, flags); - return 0; } static struct vb2_ops video_qops = { diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 8dea0b84a3ad..d09a27a23c5d 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -346,7 +346,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int vpif_stop_streaming(struct vb2_queue *vq) +static void vpif_stop_streaming(struct vb2_queue *vq) { struct vpif_fh *fh = vb2_get_drv_priv(vq); struct channel_obj *ch = fh->channel; @@ -354,7 +354,7 @@ static int vpif_stop_streaming(struct vb2_queue *vq) unsigned long flags; if (!vb2_is_streaming(vq)) - return 0; + return; common = &ch->common[VPIF_VIDEO_INDEX]; @@ -390,8 +390,6 @@ static int vpif_stop_streaming(struct vb2_queue *vq) vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&common->irqlock, flags); - - return 0; } static struct vb2_ops video_qops = { diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index aed41edd0501..d03487fb1888 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -308,7 +308,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int vpif_stop_streaming(struct vb2_queue *vq) +static void vpif_stop_streaming(struct vb2_queue *vq) { struct vpif_fh *fh = vb2_get_drv_priv(vq); struct channel_obj *ch = fh->channel; @@ -316,7 +316,7 @@ static int vpif_stop_streaming(struct vb2_queue *vq) unsigned long flags; if (!vb2_is_streaming(vq)) - return 0; + return; common = &ch->common[VPIF_VIDEO_INDEX]; @@ -352,8 +352,6 @@ static int vpif_stop_streaming(struct vb2_queue *vq) vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&common->irqlock, flags); - - return 0; } static struct vb2_ops video_qops = { diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index d0ea94f58d6f..e434f1f03d7b 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -66,15 +66,13 @@ static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count) return ret > 0 ? 0 : ret; } -static int gsc_m2m_stop_streaming(struct vb2_queue *q) +static void gsc_m2m_stop_streaming(struct vb2_queue *q) { struct gsc_ctx *ctx = q->drv_priv; __gsc_m2m_job_abort(ctx); pm_runtime_put(&ctx->gsc_dev->pdev->dev); - - return 0; } void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 92ae812abce2..3d2babd5067a 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -294,15 +294,15 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; struct fimc_dev *fimc = ctx->fimc_dev; if (!fimc_capture_active(fimc)) - return -EINVAL; + return; - return fimc_stop_capture(fimc, false); + fimc_stop_capture(fimc, false); } int fimc_capture_suspend(struct fimc_dev *fimc) diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index e92b4e115adb..ced46600e343 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -125,7 +125,7 @@ static int isp_video_capture_start_streaming(struct vb2_queue *q, return ret; } -static int isp_video_capture_stop_streaming(struct vb2_queue *q) +static void isp_video_capture_stop_streaming(struct vb2_queue *q) { struct fimc_isp *isp = vb2_get_drv_priv(q); struct fimc_is *is = fimc_isp_to_is(isp); @@ -134,7 +134,7 @@ static int isp_video_capture_stop_streaming(struct vb2_queue *q) ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0); if (ret < 0) - return ret; + return; dma->cmd = DMA_OUTPUT_COMMAND_DISABLE; dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE; @@ -155,7 +155,6 @@ static int isp_video_capture_stop_streaming(struct vb2_queue *q) clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); isp->video_capture.buf_count = 0; - return 0; } static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb) diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 3ad660b55b6b..630aef52dbb8 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -350,14 +350,14 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q) { struct fimc_lite *fimc = q->drv_priv; if (!fimc_lite_active(fimc)) - return -EINVAL; + return; - return fimc_lite_stop_capture(fimc, false); + fimc_lite_stop_capture(fimc, false); } static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 36971d915b53..d314155da9e4 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -85,7 +85,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) return ret > 0 ? 0 : ret; } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; int ret; @@ -95,7 +95,6 @@ static int stop_streaming(struct vb2_queue *q) fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); pm_runtime_put(&ctx->fimc_dev->pdev->dev); - return 0; } static void fimc_device_run(void *priv) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 8b34c485be79..be4b51212106 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1156,7 +1156,7 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count) return mcam_read_setup(cam); } -static int mcam_vb_stop_streaming(struct vb2_queue *vq) +static void mcam_vb_stop_streaming(struct vb2_queue *vq) { struct mcam_camera *cam = vb2_get_drv_priv(vq); unsigned long flags; @@ -1164,10 +1164,10 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq) if (cam->state == S_BUFWAIT) { /* They never gave us buffers */ cam->state = S_IDLE; - return 0; + return; } if (cam->state != S_STREAMING) - return -EINVAL; + return; mcam_ctlr_stop_dma(cam); /* * Reset the CCIC PHY after stopping streaming, @@ -1182,7 +1182,6 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq) spin_lock_irqsave(&cam->dev_lock, flags); INIT_LIST_HEAD(&cam->buffers); spin_unlock_irqrestore(&cam->dev_lock, flags); - return 0; } diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 4f3096b17066..0714070ed7fa 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -787,7 +787,7 @@ static int m2mtest_start_streaming(struct vb2_queue *q, unsigned count) return 0; } -static int m2mtest_stop_streaming(struct vb2_queue *q) +static void m2mtest_stop_streaming(struct vb2_queue *q) { struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); struct vb2_buffer *vb; @@ -799,12 +799,11 @@ static int m2mtest_stop_streaming(struct vb2_queue *q) else vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); if (vb == NULL) - return 0; + return; spin_lock_irqsave(&ctx->dev->irqlock, flags); v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&ctx->dev->irqlock, flags); } - return 0; } static struct vb2_ops m2mtest_qops = { diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 4e4d1631e042..deba425e3d8f 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -435,10 +435,10 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) return 0; } -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct camif_vp *vp = vb2_get_drv_priv(vq); - return camif_stop_capture(vp); + camif_stop_capture(vp); } static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 8a18972012f7..368b3f6df24b 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1670,13 +1670,11 @@ static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) return ret > 0 ? 0 : ret; } -static int s5p_jpeg_stop_streaming(struct vb2_queue *q) +static void s5p_jpeg_stop_streaming(struct vb2_queue *q) { struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q); pm_runtime_put(ctx->jpeg->dev); - - return 0; } static struct vb2_ops s5p_jpeg_qops = { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 8faf9691712d..58b7bbaa2da0 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -1027,7 +1027,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int s5p_mfc_stop_streaming(struct vb2_queue *q) +static void s5p_mfc_stop_streaming(struct vb2_queue *q) { unsigned long flags; struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); @@ -1071,7 +1071,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q) } if (aborted) ctx->state = MFCINST_RUNNING; - return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index df83cd157bab..458279e5dc80 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1954,7 +1954,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int s5p_mfc_stop_streaming(struct vb2_queue *q) +static void s5p_mfc_stop_streaming(struct vb2_queue *q) { unsigned long flags; struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); @@ -1983,7 +1983,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q) ctx->src_queue_cnt = 0; } spin_unlock_irqrestore(&dev->irqlock, flags); - return 0; } static void s5p_mfc_buf_queue(struct vb2_buffer *vb) diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index a1ce55fd30f3..9f1e52f0bd09 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -985,7 +985,7 @@ static void mxr_watchdog(unsigned long arg) spin_unlock_irqrestore(&layer->enq_slock, flags); } -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct mxr_layer *layer = vb2_get_drv_priv(vq); struct mxr_device *mdev = layer->mdev; @@ -1031,7 +1031,6 @@ static int stop_streaming(struct vb2_queue *vq) mxr_streamer_put(mdev); /* allow changes in output configuration */ mxr_output_put(mdev); - return 0; } static struct vb2_ops mxr_video_qops = { diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index f0b6c900034d..38c723aca438 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -406,7 +406,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -433,7 +433,7 @@ static int stop_streaming(struct vb2_queue *vq) if (time_after(jiffies, timeout)) { dev_err(icd->parent, "Timeout waiting for finishing codec request\n"); - return -ETIMEDOUT; + return; } /* Disable interrupts */ @@ -444,8 +444,6 @@ static int stop_streaming(struct vb2_queue *vq) ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); if (ret < 0) dev_err(icd->parent, "Disable ISI timed out\n"); - - return ret; } static struct vb2_ops isi_video_qops = { diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 3e844803bdca..b40bc2e5ba47 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -741,7 +741,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int mx2_stop_streaming(struct vb2_queue *q) +static void mx2_stop_streaming(struct vb2_queue *q) { struct soc_camera_device *icd = soc_camera_from_vb2q(q); struct soc_camera_host *ici = @@ -773,8 +773,6 @@ static int mx2_stop_streaming(struct vb2_queue *q) dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size, b, pcdev->discard_buffer_dma); - - return 0; } static struct vb2_ops mx2_videobuf_ops = { diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 9ed81ac6881c..83315dfeef62 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -406,7 +406,7 @@ static int mx3_videobuf_init(struct vb2_buffer *vb) return 0; } -static int mx3_stop_streaming(struct vb2_queue *q) +static void mx3_stop_streaming(struct vb2_queue *q) { struct soc_camera_device *icd = soc_camera_from_vb2q(q); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -430,8 +430,6 @@ static int mx3_stop_streaming(struct vb2_queue *q) } spin_unlock_irqrestore(&mx3_cam->lock, flags); - - return 0; } static struct vb2_ops mx3_videobuf_ops = { diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 704eee766487..e594230e84d3 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -513,7 +513,7 @@ static int rcar_vin_videobuf_init(struct vb2_buffer *vb) return 0; } -static int rcar_vin_stop_streaming(struct vb2_queue *vq) +static void rcar_vin_stop_streaming(struct vb2_queue *vq) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -524,8 +524,6 @@ static int rcar_vin_stop_streaming(struct vb2_queue *vq) list_for_each_safe(buf_head, tmp, &priv->capture) list_del_init(buf_head); spin_unlock_irq(&priv->lock); - - return 0; } static struct vb2_ops rcar_vin_vb2_ops = { diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 3e75a469cd49..20ad4a571d37 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -471,7 +471,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) return 0; } -static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) +static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q) { struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -487,7 +487,7 @@ static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) spin_unlock_irq(&pcdev->lock); - return sh_mobile_ceu_soft_reset(pcdev); + sh_mobile_ceu_soft_reset(pcdev); } static struct vb2_ops sh_mobile_ceu_videobuf_ops = { diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 3890f4f42a78..d00bf3df0f8a 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -906,12 +906,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct vivi_dev *dev = vb2_get_drv_priv(vq); dprintk(dev, 1, "%s\n", __func__); vivi_stop_generating(dev); - return 0; } static void vivi_lock(struct vb2_queue *vq) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index b48f135ffc01..a0595c17700f 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -720,7 +720,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; } -static int vsp1_video_stop_streaming(struct vb2_queue *vq) +static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); @@ -743,8 +743,6 @@ static int vsp1_video_stop_streaming(struct vb2_queue *vq) spin_lock_irqsave(&video->irqlock, flags); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); - - return 0; } static struct vb2_ops vsp1_video_queue_qops = { diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h index bce438691e0e..432862c20bbf 100644 --- a/drivers/media/usb/em28xx/em28xx-v4l.h +++ b/drivers/media/usb/em28xx/em28xx-v4l.h @@ -16,5 +16,5 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); -int em28xx_stop_vbi_streaming(struct vb2_queue *vq); +void em28xx_stop_vbi_streaming(struct vb2_queue *vq); extern struct vb2_ops em28xx_vbi_qops; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 0856e5d367b6..cdcd7513e6ec 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -937,7 +937,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) return rc; } -static int em28xx_stop_streaming(struct vb2_queue *vq) +static void em28xx_stop_streaming(struct vb2_queue *vq) { struct em28xx *dev = vb2_get_drv_priv(vq); struct em28xx_dmaqueue *vidq = &dev->vidq; @@ -961,11 +961,9 @@ static int em28xx_stop_streaming(struct vb2_queue *vq) } dev->usb_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); - - return 0; } -int em28xx_stop_vbi_streaming(struct vb2_queue *vq) +void em28xx_stop_vbi_streaming(struct vb2_queue *vq) { struct em28xx *dev = vb2_get_drv_priv(vq); struct em28xx_dmaqueue *vbiq = &dev->vbiq; @@ -989,8 +987,6 @@ int em28xx_stop_vbi_streaming(struct vb2_queue *vq) } dev->usb_ctl.vbi_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); - - return 0; } static void diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 84a6720b1d00..a73b0bced96f 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -681,12 +681,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) return r; } -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); - if (mutex_lock_interruptible(&pdev->v4l2_lock)) - return -ERESTARTSYS; + mutex_lock(&pdev->v4l2_lock); if (pdev->udev) { pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); @@ -695,8 +694,6 @@ static int stop_streaming(struct vb2_queue *vq) pwc_cleanup_queued_bufs(pdev); mutex_unlock(&pdev->v4l2_lock); - - return 0; } static struct vb2_ops pwc_vb_queue_ops = { diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 1d4ba2b80490..e019dd63ed42 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -714,7 +714,7 @@ static void buffer_queue(struct vb2_buffer *vb) } static int start_streaming(struct vb2_queue *vq, unsigned int count); -static int stop_streaming(struct vb2_queue *vq); +static void stop_streaming(struct vb2_queue *vq); static struct vb2_ops s2255_video_qops = { .queue_setup = queue_setup, @@ -1109,7 +1109,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct s2255_vc *vc = vb2_get_drv_priv(vq); struct s2255_buffer *buf, *node; @@ -1123,7 +1123,6 @@ static int stop_streaming(struct vb2_queue *vq) buf, buf->vb.v4l2_buf.index); } spin_unlock_irqrestore(&vc->qlock, flags); - return 0; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 37bc00f418f1..46e8a5069b37 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -583,10 +583,10 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct stk1160 *dev = vb2_get_drv_priv(vq); - return stk1160_stop_streaming(dev); + stk1160_stop_streaming(dev); } static struct vb2_ops stk1160_video_qops = { diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 20365bd69d05..2967e808408b 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -634,15 +634,12 @@ static int usbtv_start_streaming(struct vb2_queue *vq, unsigned int count) return usbtv_start(usbtv); } -static int usbtv_stop_streaming(struct vb2_queue *vq) +static void usbtv_stop_streaming(struct vb2_queue *vq) { struct usbtv *usbtv = vb2_get_drv_priv(vq); - if (usbtv->udev == NULL) - return -ENODEV; - - usbtv_stop(usbtv); - return 0; + if (usbtv->udev) + usbtv_stop(usbtv); } static struct vb2_ops usbtv_vb2_ops = { diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 3f0cdb150dfd..f8f694a239f8 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2086,7 +2086,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) * buffers. */ if (q->start_streaming_called) - call_qop(q, stop_streaming, q); + call_void_qop(q, stop_streaming, q); q->streaming = 0; q->start_streaming_called = 0; q->queued_count = 0; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index 9337d92c5939..7b213a7f9623 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -1249,7 +1249,7 @@ static int vpfe_buffer_init(struct vb2_buffer *vb) } /* abort streaming and wait for last buffer */ -static int vpfe_stop_streaming(struct vb2_queue *vq) +static void vpfe_stop_streaming(struct vb2_queue *vq) { struct vpfe_fh *fh = vb2_get_drv_priv(vq); struct vpfe_video_device *video = fh->video; @@ -1272,7 +1272,6 @@ static int vpfe_stop_streaming(struct vb2_queue *vq) list_del(&video->next_frm->list); vb2_buffer_done(&video->next_frm->vb, VB2_BUF_STATE_ERROR); } - return 0; } static void vpfe_buf_cleanup(struct vb2_buffer *vb) diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c index afbc2e519606..14bdc19ec48f 100644 --- a/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -262,7 +262,7 @@ dt3155_buf_prepare(struct vb2_buffer *vb) return 0; } -static int +static void dt3155_stop_streaming(struct vb2_queue *q) { struct dt3155_priv *pd = vb2_get_drv_priv(q); @@ -276,7 +276,6 @@ dt3155_stop_streaming(struct vb2_queue *q) } spin_unlock_irq(&pd->lock); msleep(45); /* irq hendler will stop the hardware */ - return 0; } static void diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index b397aa3c0f44..090b3e6e852a 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -516,7 +516,7 @@ static int go7007_start_streaming(struct vb2_queue *q, unsigned int count) return ret; } -static int go7007_stop_streaming(struct vb2_queue *q) +static void go7007_stop_streaming(struct vb2_queue *q) { struct go7007 *go = vb2_get_drv_priv(q); unsigned long flags; @@ -538,7 +538,6 @@ static int go7007_stop_streaming(struct vb2_queue *q) /* Turn on Capture LED */ if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) go7007_write_addr(go, 0x3c82, 0x000d); - return 0; } static struct vb2_ops go7007_video_qops = { diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 65d351f99da2..08d0d096b881 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -1074,14 +1074,13 @@ static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count) return ret; } -static int msi3101_stop_streaming(struct vb2_queue *vq) +static void msi3101_stop_streaming(struct vb2_queue *vq) { struct msi3101_state *s = vb2_get_drv_priv(vq); - int ret; + dev_dbg(&s->udev->dev, "%s:\n", __func__); - if (mutex_lock_interruptible(&s->v4l2_lock)) - return -ERESTARTSYS; + mutex_lock(&s->v4l2_lock); if (s->udev) msi3101_isoc_cleanup(s); @@ -1090,22 +1089,15 @@ static int msi3101_stop_streaming(struct vb2_queue *vq) /* according to tests, at least 700us delay is required */ msleep(20); - ret = msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0); - if (ret) - goto err_sleep_tuner; - - /* sleep USB IF / ADC */ - ret = msi3101_ctrl_msg(s, CMD_WREG, 0x01000003); - if (ret) - goto err_sleep_tuner; + if (!msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0)) { + /* sleep USB IF / ADC */ + msi3101_ctrl_msg(s, CMD_WREG, 0x01000003); + } -err_sleep_tuner: /* sleep tuner */ - ret = v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0); + v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0); mutex_unlock(&s->v4l2_lock); - - return ret; } static struct vb2_ops msi3101_vb2_ops = { diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c index 104ee8af79af..093df6b6ae35 100644 --- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c +++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c @@ -1032,13 +1032,12 @@ err: return ret; } -static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq) +static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) { struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); dev_dbg(&s->udev->dev, "%s:\n", __func__); - if (mutex_lock_interruptible(&s->v4l2_lock)) - return -ERESTARTSYS; + mutex_lock(&s->v4l2_lock); rtl2832_sdr_kill_urbs(s); rtl2832_sdr_free_urbs(s); @@ -1053,8 +1052,6 @@ static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq) s->d->props->power_ctrl(s->d, 0); mutex_unlock(&s->v4l2_lock); - - return 0; } static struct vb2_ops rtl2832_sdr_vb2_ops = { diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index 2cbe088f1697..b8ff113c20f4 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -745,14 +745,13 @@ static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count) return solo_ring_start(solo_enc->solo_dev); } -static int solo_enc_stop_streaming(struct vb2_queue *q) +static void solo_enc_stop_streaming(struct vb2_queue *q) { struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q); solo_enc_off(solo_enc); INIT_LIST_HEAD(&solo_enc->vidq_active); solo_ring_stop(solo_enc->solo_dev); - return 0; } static struct vb2_ops solo_enc_video_qops = { diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2.c b/drivers/staging/media/solo6x10/solo6x10-v4l2.c index 1815f765d033..5d0100eb38e6 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2.c @@ -336,13 +336,12 @@ static int solo_start_streaming(struct vb2_queue *q, unsigned int count) return solo_start_thread(solo_dev); } -static int solo_stop_streaming(struct vb2_queue *q) +static void solo_stop_streaming(struct vb2_queue *q) { struct solo_dev *solo_dev = vb2_get_drv_priv(q); solo_stop_thread(solo_dev); INIT_LIST_HEAD(&solo_dev->vidq_active); - return 0; } static void solo_buf_queue(struct vb2_buffer *vb) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 46e76096c22a..bca25dc53f9d 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -324,7 +324,7 @@ struct vb2_ops { void (*buf_cleanup)(struct vb2_buffer *vb); int (*start_streaming)(struct vb2_queue *q, unsigned int count); - int (*stop_streaming)(struct vb2_queue *q); + void (*stop_streaming)(struct vb2_queue *q); void (*buf_queue)(struct vb2_buffer *vb); }; -- cgit v1.2.3 From f392e51cd6ae6f6ee5b9b6d611cdc282b4c1711e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 23 Apr 2014 11:13:14 -0400 Subject: cgroup: update cgroup->subsys_mask to ->child_subsys_mask and restore cgroup_root->subsys_mask 944196278d3d ("cgroup: move ->subsys_mask from cgroupfs_root to cgroup") moved ->subsys_mask from cgroup_root to cgroup to prepare for the unified hierarhcy; however, it turns out that carrying the subsys_mask of the children in the parent, instead of itself, is a lot more natural. This patch restores cgroup_root->subsys_mask and morphs cgroup->subsys_mask into cgroup->child_subsys_mask. * Uses of root->cgrp.subsys_mask are restored to root->subsys_mask. * Remove automatic setting and clearing of cgrp->subsys_mask and instead just inherit ->child_subsys_mask from the parent during cgroup creation. Note that this doesn't affect any current behaviors. * Undo __kill_css() separation. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 7 ++++-- kernel/cgroup.c | 64 +++++++++++++++++++++----------------------------- 2 files changed, 32 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c2515851c1aa..1b5b2fe1b228 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -173,8 +173,8 @@ struct cgroup { */ u64 serial_nr; - /* The bitmask of subsystems attached to this cgroup */ - unsigned long subsys_mask; + /* the bitmask of subsystems enabled on the child cgroups */ + unsigned long child_subsys_mask; /* Private pointers for each registered subsystem */ struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT]; @@ -282,6 +282,9 @@ enum { struct cgroup_root { struct kernfs_root *kf_root; + /* The bitmask of subsystems attached to this hierarchy */ + unsigned long subsys_mask; + /* Unique id for this hierarchy. */ int hierarchy_id; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a6894272353b..f944619077f4 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -529,7 +529,7 @@ static struct css_set *find_existing_css_set(struct css_set *old_cset, * won't change, so no need for locking. */ for_each_subsys(ss, i) { - if (root->cgrp.subsys_mask & (1UL << i)) { + if (root->subsys_mask & (1UL << i)) { /* Subsystem is in this hierarchy. So we want * the subsystem state from the new * cgroup */ @@ -742,7 +742,7 @@ static void cgroup_destroy_root(struct cgroup_root *root) BUG_ON(!list_empty(&cgrp->children)); /* Rebind all subsystems back to the default hierarchy */ - rebind_subsystems(&cgrp_dfl_root, cgrp->subsys_mask); + rebind_subsystems(&cgrp_dfl_root, root->subsys_mask); /* * Release all the links from cset_links to this hierarchy's @@ -1050,8 +1050,11 @@ static int rebind_subsystems(struct cgroup_root *dst_root, ss->root = dst_root; css->cgroup = &dst_root->cgrp; - src_root->cgrp.subsys_mask &= ~(1 << ssid); - dst_root->cgrp.subsys_mask |= 1 << ssid; + src_root->subsys_mask &= ~(1 << ssid); + src_root->cgrp.child_subsys_mask &= ~(1 << ssid); + + dst_root->subsys_mask |= 1 << ssid; + dst_root->cgrp.child_subsys_mask |= 1 << ssid; if (ss->bind) ss->bind(css); @@ -1069,7 +1072,7 @@ static int cgroup_show_options(struct seq_file *seq, int ssid; for_each_subsys(ss, ssid) - if (root->cgrp.subsys_mask & (1 << ssid)) + if (root->subsys_mask & (1 << ssid)) seq_printf(seq, ",%s", ss->name); if (root->flags & CGRP_ROOT_SANE_BEHAVIOR) seq_puts(seq, ",sane_behavior"); @@ -1273,12 +1276,12 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data) if (ret) goto out_unlock; - if (opts.subsys_mask != root->cgrp.subsys_mask || opts.release_agent) + if (opts.subsys_mask != root->subsys_mask || opts.release_agent) pr_warning("cgroup: option changes via remount are deprecated (pid=%d comm=%s)\n", task_tgid_nr(current), current->comm); - added_mask = opts.subsys_mask & ~root->cgrp.subsys_mask; - removed_mask = root->cgrp.subsys_mask & ~opts.subsys_mask; + added_mask = opts.subsys_mask & ~root->subsys_mask; + removed_mask = root->subsys_mask & ~opts.subsys_mask; /* Don't allow flags or name to change at remount */ if (((opts.flags ^ root->flags) & CGRP_ROOT_OPTION_MASK) || @@ -1535,7 +1538,7 @@ retry: * subsystems) then they must match. */ if ((opts.subsys_mask || opts.none) && - (opts.subsys_mask != root->cgrp.subsys_mask)) { + (opts.subsys_mask != root->subsys_mask)) { if (!name_match) continue; ret = -EBUSY; @@ -3658,8 +3661,6 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss) cgroup_get(cgrp); css_get(css->parent); - cgrp->subsys_mask |= 1 << ss->id; - if (ss->broken_hierarchy && !ss->warned_broken_hierarchy && parent->parent) { pr_warning("cgroup: %s (%d) created nested cgroup for controller \"%s\" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.\n", @@ -3780,13 +3781,15 @@ static long cgroup_create(struct cgroup *parent, const char *name, /* let's create and online css's */ for_each_subsys(ss, ssid) { - if (root->cgrp.subsys_mask & (1 << ssid)) { + if (parent->child_subsys_mask & (1 << ssid)) { err = create_css(cgrp, ss); if (err) goto err_destroy; } } + cgrp->child_subsys_mask = parent->child_subsys_mask; + kernfs_activate(kn); mutex_unlock(&cgroup_mutex); @@ -3882,7 +3885,16 @@ static void css_killed_ref_fn(struct percpu_ref *ref) queue_work(cgroup_destroy_wq, &css->destroy_work); } -static void __kill_css(struct cgroup_subsys_state *css) +/** + * kill_css - destroy a css + * @css: css to destroy + * + * This function initiates destruction of @css by removing cgroup interface + * files and putting its base reference. ->css_offline() will be invoked + * asynchronously once css_tryget() is guaranteed to fail and when the + * reference count reaches zero, @css will be released. + */ +static void kill_css(struct cgroup_subsys_state *css) { lockdep_assert_held(&cgroup_tree_mutex); @@ -3911,28 +3923,6 @@ static void __kill_css(struct cgroup_subsys_state *css) percpu_ref_kill_and_confirm(&css->refcnt, css_killed_ref_fn); } -/** - * kill_css - destroy a css - * @css: css to destroy - * - * This function initiates destruction of @css by removing cgroup interface - * files and putting its base reference. ->css_offline() will be invoked - * asynchronously once css_tryget() is guaranteed to fail and when the - * reference count reaches zero, @css will be released. - */ -static void kill_css(struct cgroup_subsys_state *css) -{ - struct cgroup *cgrp = css->cgroup; - - lockdep_assert_held(&cgroup_tree_mutex); - - /* if already killed, noop */ - if (cgrp->subsys_mask & (1 << css->ss->id)) { - cgrp->subsys_mask &= ~(1 << css->ss->id); - __kill_css(css); - } -} - /** * cgroup_destroy_locked - the first stage of cgroup destruction * @cgrp: cgroup to be destroyed @@ -4145,7 +4135,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss) BUG_ON(online_css(css)); - cgrp_dfl_root.cgrp.subsys_mask |= 1 << ss->id; + cgrp_dfl_root.subsys_mask |= 1 << ss->id; mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_tree_mutex); @@ -4302,7 +4292,7 @@ int proc_cgroup_show(struct seq_file *m, void *v) seq_printf(m, "%d:", root->hierarchy_id); for_each_subsys(ss, ssid) - if (root->cgrp.subsys_mask & (1 << ssid)) + if (root->subsys_mask & (1 << ssid)) seq_printf(m, "%s%s", count++ ? "," : "", ss->name); if (strlen(root->name)) seq_printf(m, "%sname=%s", count ? "," : "", -- cgit v1.2.3 From 2d8f243a5e6efa57fb7c46fe83fafa45b33d0ec2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 23 Apr 2014 11:13:15 -0400 Subject: cgroup: implement cgroup->e_csets[] On the default unified hierarchy, a cgroup may be associated with csses of its ancestors, which means that a css of a given cgroup may be associated with css_sets of descendant cgroups. This means that we can't walk all tasks associated with a css by iterating the css_sets associated with the cgroup as there are css_sets which are pointing to the css but linked on the descendants. This patch adds per-subsystem list heads cgroup->e_csets[]. Any css_set which is pointing to a css is linked to css->cgroup->e_csets[$SUBSYS_ID] through css_set->e_cset_node[$SUBSYS_ID]. The lists are protected by css_set_rwsem and will allow us to walk all css_sets associated with a given css so that we can find out all associated tasks. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 18 ++++++++++++++++++ kernel/cgroup.c | 30 ++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 1b5b2fe1b228..33a0043ef454 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -187,6 +187,15 @@ struct cgroup { */ struct list_head cset_links; + /* + * On the default hierarchy, a css_set for a cgroup with some + * susbsys disabled will point to css's which are associated with + * the closest ancestor which has the subsys enabled. The + * following lists all css_sets which point to this cgroup's css + * for the given subsystem. + */ + struct list_head e_csets[CGROUP_SUBSYS_COUNT]; + /* * Linked list running through all cgroups that can * potentially be reaped by the release agent. Protected by @@ -369,6 +378,15 @@ struct css_set { struct cgroup *mg_src_cgrp; struct css_set *mg_dst_cset; + /* + * On the default hierarhcy, ->subsys[ssid] may point to a css + * attached to an ancestor instead of the cgroup this css_set is + * associated with. The following node is anchored at + * ->subsys[ssid]->cgroup->e_csets[ssid] and provides a way to + * iterate through all css's attached to a given cgroup. + */ + struct list_head e_cset_node[CGROUP_SUBSYS_COUNT]; + /* For RCU-protected deletion */ struct rcu_head rcu_head; }; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 4eb2dd1bb5b1..37d966289978 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -425,6 +425,8 @@ static unsigned long css_set_hash(struct cgroup_subsys_state *css[]) static void put_css_set_locked(struct css_set *cset, bool taskexit) { struct cgrp_cset_link *link, *tmp_link; + struct cgroup_subsys *ss; + int ssid; lockdep_assert_held(&css_set_rwsem); @@ -432,6 +434,8 @@ static void put_css_set_locked(struct css_set *cset, bool taskexit) return; /* This css_set is dead. unlink it and release cgroup refcounts */ + for_each_subsys(ss, ssid) + list_del(&cset->e_cset_node[ssid]); hash_del(&cset->hlist); css_set_count--; @@ -673,7 +677,9 @@ static struct css_set *find_css_set(struct css_set *old_cset, struct css_set *cset; struct list_head tmp_links; struct cgrp_cset_link *link; + struct cgroup_subsys *ss; unsigned long key; + int ssid; lockdep_assert_held(&cgroup_mutex); @@ -724,10 +730,14 @@ static struct css_set *find_css_set(struct css_set *old_cset, css_set_count++; - /* Add this cgroup group to the hash table */ + /* Add @cset to the hash table */ key = css_set_hash(cset->subsys); hash_add(css_set_table, &cset->hlist, key); + for_each_subsys(ss, ssid) + list_add_tail(&cset->e_cset_node[ssid], + &cset->subsys[ssid]->cgroup->e_csets[ssid]); + up_write(&css_set_rwsem); return cset; @@ -1028,7 +1038,7 @@ static int rebind_subsystems(struct cgroup_root *dst_root, unsigned long ss_mask) { struct cgroup_subsys *ss; - int ssid, ret; + int ssid, i, ret; lockdep_assert_held(&cgroup_tree_mutex); lockdep_assert_held(&cgroup_mutex); @@ -1081,6 +1091,7 @@ static int rebind_subsystems(struct cgroup_root *dst_root, for_each_subsys(ss, ssid) { struct cgroup_root *src_root; struct cgroup_subsys_state *css; + struct css_set *cset; if (!(ss_mask & (1 << ssid))) continue; @@ -1095,6 +1106,12 @@ static int rebind_subsystems(struct cgroup_root *dst_root, ss->root = dst_root; css->cgroup = &dst_root->cgrp; + down_write(&css_set_rwsem); + hash_for_each(css_set_table, i, cset, hlist) + list_move_tail(&cset->e_cset_node[ss->id], + &dst_root->cgrp.e_csets[ss->id]); + up_write(&css_set_rwsem); + src_root->subsys_mask &= ~(1 << ssid); src_root->cgrp.child_subsys_mask &= ~(1 << ssid); @@ -1417,6 +1434,9 @@ out_unlock: static void init_cgroup_housekeeping(struct cgroup *cgrp) { + struct cgroup_subsys *ss; + int ssid; + atomic_set(&cgrp->refcnt, 1); INIT_LIST_HEAD(&cgrp->sibling); INIT_LIST_HEAD(&cgrp->children); @@ -1425,6 +1445,9 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) INIT_LIST_HEAD(&cgrp->pidlists); mutex_init(&cgrp->pidlist_mutex); cgrp->dummy_css.cgroup = cgrp; + + for_each_subsys(ss, ssid) + INIT_LIST_HEAD(&cgrp->e_csets[ssid]); } static void init_cgroup_root(struct cgroup_root *root, @@ -4249,6 +4272,9 @@ int __init cgroup_init(void) if (!ss->early_init) cgroup_init_subsys(ss); + list_add_tail(&init_css_set.e_cset_node[ssid], + &cgrp_dfl_root.cgrp.e_csets[ssid]); + /* * cftype registration needs kmalloc and can't be done * during early_init. Register base cftypes separately. -- cgit v1.2.3 From 0f0a2b4fa6210147131082999f1f16d7fb79abf8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 23 Apr 2014 11:13:15 -0400 Subject: cgroup: reorganize css_task_iter This patch reorganizes css_task_iter so that adding effective css support is easier. * s/->cset_link/->cset_pos/ and s/->task/->task_pos/ for consistency * ->origin_css is used to determine whether the iteration reached the last css_set. Replace it with explicit ->cset_head so that css_advance_task_iter() doesn't have to know the termination condition directly. * css_task_iter_next() currently assumes that it's walking list of cgrp_cset_link and reaches into the current cset through the current link to determine the termination conditions for task walking. As this won't always be true for effective css walking, add ->tasks_head and ->mg_tasks_head and use them to control task walking so that css_task_iter_next() doesn't have to know how css_sets are being walked. This patch doesn't make any behavior changes. The iteration logic stays unchanged after the patch. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 9 ++++++--- kernel/cgroup.c | 33 +++++++++++++++++---------------- 2 files changed, 23 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 33a0043ef454..bee390586120 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -842,9 +842,12 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, /* A css_task_iter should be treated as an opaque object */ struct css_task_iter { - struct cgroup_subsys_state *origin_css; - struct list_head *cset_link; - struct list_head *task; + struct list_head *cset_pos; + struct list_head *cset_head; + + struct list_head *task_pos; + struct list_head *tasks_head; + struct list_head *mg_tasks_head; }; void css_task_iter_start(struct cgroup_subsys_state *css, diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 0edc186cd545..d48163b26196 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2857,27 +2857,30 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, */ static void css_advance_task_iter(struct css_task_iter *it) { - struct list_head *l = it->cset_link; + struct list_head *l = it->cset_pos; struct cgrp_cset_link *link; struct css_set *cset; /* Advance to the next non-empty css_set */ do { l = l->next; - if (l == &it->origin_css->cgroup->cset_links) { - it->cset_link = NULL; + if (l == it->cset_head) { + it->cset_pos = NULL; return; } link = list_entry(l, struct cgrp_cset_link, cset_link); cset = link->cset; } while (list_empty(&cset->tasks) && list_empty(&cset->mg_tasks)); - it->cset_link = l; + it->cset_pos = l; if (!list_empty(&cset->tasks)) - it->task = cset->tasks.next; + it->task_pos = cset->tasks.next; else - it->task = cset->mg_tasks.next; + it->task_pos = cset->mg_tasks.next; + + it->tasks_head = &cset->tasks; + it->mg_tasks_head = &cset->mg_tasks; } /** @@ -2903,8 +2906,8 @@ void css_task_iter_start(struct cgroup_subsys_state *css, down_read(&css_set_rwsem); - it->origin_css = css; - it->cset_link = &css->cgroup->cset_links; + it->cset_pos = &css->cgroup->cset_links; + it->cset_head = it->cset_pos; css_advance_task_iter(it); } @@ -2920,12 +2923,10 @@ void css_task_iter_start(struct cgroup_subsys_state *css, struct task_struct *css_task_iter_next(struct css_task_iter *it) { struct task_struct *res; - struct list_head *l = it->task; - struct cgrp_cset_link *link = list_entry(it->cset_link, - struct cgrp_cset_link, cset_link); + struct list_head *l = it->task_pos; /* If the iterator cg is NULL, we have no tasks */ - if (!it->cset_link) + if (!it->cset_pos) return NULL; res = list_entry(l, struct task_struct, cg_list); @@ -2936,13 +2937,13 @@ struct task_struct *css_task_iter_next(struct css_task_iter *it) */ l = l->next; - if (l == &link->cset->tasks) - l = link->cset->mg_tasks.next; + if (l == it->tasks_head) + l = it->mg_tasks_head->next; - if (l == &link->cset->mg_tasks) + if (l == it->mg_tasks_head) css_advance_task_iter(it); else - it->task = l; + it->task_pos = l; return res; } -- cgit v1.2.3 From 3ebb2b6ef38875b866ec0118bfae7bc52afd0166 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 23 Apr 2014 11:13:15 -0400 Subject: cgroup: teach css_task_iter about effective csses Currently, css_task_iter iterates tasks associated with a css by visiting each css_set associated with the owning cgroup and walking tasks of each of them. This works fine for !unified hierarchies as each cgroup has its own css for each associated subsystem on the hierarchy; however, on the planned unified hierarchy, a cgroup may not have csses associated and its tasks would be considered associated with the matching css of the nearest ancestor which has the subsystem enabled. This means that on the default unified hierarchy, just walking all tasks associated with a cgroup isn't enough to walk all tasks which are associated with the specified css. If any of its children doesn't have the matching css enabled, task iteration should also include all tasks from the subtree. We already added cgroup->e_csets[] to list all css_sets effectively associated with a given css and walk css_sets on that list instead to achieve such iteration. This patch updates css_task_iter iteration such that it walks css_sets on cgroup->e_csets[] instead of cgroup->cset_links if iteration is requested on an non-dummy css. Thanks to the previous iteration update, this change can be achieved with the addition of css_task_iter->ss and minimal updates to css_advance_task_iter() and css_task_iter_start(). Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 2 ++ kernel/cgroup.c | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index bee390586120..18fcae39e63e 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -842,6 +842,8 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, /* A css_task_iter should be treated as an opaque object */ struct css_task_iter { + struct cgroup_subsys *ss; + struct list_head *cset_pos; struct list_head *cset_head; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index d48163b26196..ad28866ed44c 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2868,8 +2868,14 @@ static void css_advance_task_iter(struct css_task_iter *it) it->cset_pos = NULL; return; } - link = list_entry(l, struct cgrp_cset_link, cset_link); - cset = link->cset; + + if (it->ss) { + cset = container_of(l, struct css_set, + e_cset_node[it->ss->id]); + } else { + link = list_entry(l, struct cgrp_cset_link, cset_link); + cset = link->cset; + } } while (list_empty(&cset->tasks) && list_empty(&cset->mg_tasks)); it->cset_pos = l; @@ -2906,7 +2912,13 @@ void css_task_iter_start(struct cgroup_subsys_state *css, down_read(&css_set_rwsem); - it->cset_pos = &css->cgroup->cset_links; + it->ss = css->ss; + + if (it->ss) + it->cset_pos = &css->cgroup->e_csets[css->ss->id]; + else + it->cset_pos = &css->cgroup->cset_links; + it->cset_head = it->cset_pos; css_advance_task_iter(it); -- cgit v1.2.3 From 6803c006282768ec850760766a6e4eb1a6ff87df Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 23 Apr 2014 11:13:16 -0400 Subject: cgroup: add css_set->dfl_cgrp To implement the unified hierarchy behavior, we'll need to be able to determine the associated cgroup on the default hierarchy from css_set. Let's add css_set->dfl_cgrp so that it can be accessed conveniently and efficiently. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 3 +++ kernel/cgroup.c | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 18fcae39e63e..c49d161a71cd 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -354,6 +354,9 @@ struct css_set { */ struct list_head cgrp_links; + /* the default cgroup associated with this css_set */ + struct cgroup *dfl_cgrp; + /* * Set of subsystem states, one for each subsystem. This array is * immutable after creation apart from the init_css_set during diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 2a4f88db3205..c66bfc8ee8a7 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -651,6 +651,10 @@ static void link_css_set(struct list_head *tmp_links, struct css_set *cset, struct cgrp_cset_link *link; BUG_ON(list_empty(tmp_links)); + + if (cgroup_on_dfl(cgrp)) + cset->dfl_cgrp = cgrp; + link = list_first_entry(tmp_links, struct cgrp_cset_link, cset_link); link->cset = cset; link->cgrp = cgrp; -- cgit v1.2.3 From f8f22e53a262ebee37fc98004f16b066cf5bc125 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 23 Apr 2014 11:13:16 -0400 Subject: cgroup: implement dynamic subtree controller enable/disable on the default hierarchy cgroup is switching away from multiple hierarchies and will use one unified default hierarchy where controllers can be dynamically enabled and disabled per subtree. The default hierarchy will serve as the unified hierarchy to which all controllers are attached and a css on the default hierarchy would need to also serve the tasks of descendant cgroups which don't have the controller enabled - ie. the tree may be collapsed from leaf towards root when viewed from specific controllers. This has been implemented through effective css in the previous patches. This patch finally implements dynamic subtree controller enable/disable on the default hierarchy via a new knob - "cgroup.subtree_control" which controls which controllers are enabled on the child cgroups. Let's assume a hierarchy like the following. root - A - B - C \ D root's "cgroup.subtree_control" determines which controllers are enabled on A. A's on B. B's on C and D. This coincides with the fact that controllers on the immediate sub-level are used to distribute the resources of the parent. In fact, it's natural to assume that resource control knobs of a child belong to its parent. Enabling a controller in "cgroup.subtree_control" declares that distribution of the respective resources of the cgroup will be controlled. Note that this means that controller enable states are shared among siblings. The default hierarchy has an extra restriction - only cgroups which don't contain any task may have controllers enabled in "cgroup.subtree_control". Combined with the other properties of the default hierarchy, this guarantees that, from the view point of controllers, tasks are only on the leaf cgroups. In other words, only leaf csses may contain tasks. This rules out situations where child cgroups compete against internal tasks of the parent, which is a competition between two different types of entities without any clear way to determine resource distribution between the two. Different controllers handle it differently and all the implemented behaviors are ambiguous, ad-hoc, cumbersome and/or just wrong. Having this structural constraints imposed from cgroup core removes the burden from controller implementations and enables showing one consistent behavior across all controllers. When a controller is enabled or disabled, css associations for the controller in the subtrees of each child should be updated. After enabling, the whole subtree of a child should point to the new css of the child. After disabling, the whole subtree of a child should point to the cgroup's css. This is implemented by first updating cgroup states such that cgroup_e_css() result points to the appropriate css and then invoking cgroup_update_dfl_csses() which migrates all tasks in the affected subtrees to the self cgroup on the default hierarchy. * When read, "cgroup.subtree_control" lists all the currently enabled controllers on the children of the cgroup. * White-space separated list of controller names prefixed with either '+' or '-' can be written to "cgroup.subtree_control". The ones prefixed with '+' are enabled on the controller and '-' disabled. * A controller can be enabled iff the parent's "cgroup.subtree_control" enables it and disabled iff no child's "cgroup.subtree_control" has it enabled. * If a cgroup has tasks, no controller can be enabled via "cgroup.subtree_control". Likewise, if "cgroup.subtree_control" has some controllers enabled, tasks can't be migrated into the cgroup. * All controllers which aren't bound on other hierarchies are automatically associated with the root cgroup of the default hierarchy. All the controllers which are bound to the default hierarchy are listed in the read-only file "cgroup.controllers" in the root directory. * "cgroup.controllers" in all non-root cgroups is read-only file whose content is equal to that of "cgroup.subtree_control" of the parent. This indicates which controllers can be used in the cgroup's "cgroup.subtree_control". This is still experimental and there are some holes, one of which is that ->can_attach() failure during cgroup_update_dfl_csses() may leave the cgroups in an undefined state. The issues will be addressed by future patches. v2: Non-root cgroups now also have "cgroup.controllers". Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 5 + kernel/cgroup.c | 367 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 370 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c49d161a71cd..ada239253ec7 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_CGROUPS @@ -164,6 +165,7 @@ struct cgroup { struct cgroup *parent; /* my parent */ struct kernfs_node *kn; /* cgroup kernfs entry */ + struct kernfs_node *control_kn; /* kn for "cgroup.subtree_control" */ /* * Monotonically increasing unique serial number which defines a @@ -216,6 +218,9 @@ struct cgroup { /* For css percpu_ref killing and RCU-protected deletion */ struct rcu_head rcu_head; struct work_struct destroy_work; + + /* used to wait for offlining of csses */ + wait_queue_head_t offline_waitq; }; #define MAX_CGROUP_ROOT_NAMELEN 64 diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 8c2835a9e192..809dd903ceb8 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -182,6 +182,8 @@ static int rebind_subsystems(struct cgroup_root *dst_root, unsigned long ss_mask); static void cgroup_destroy_css_killed(struct cgroup *cgrp); static int cgroup_destroy_locked(struct cgroup *cgrp); +static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss); +static void kill_css(struct cgroup_subsys_state *css); static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], bool is_add); static void cgroup_pidlist_destroy_all(struct cgroup *cgrp); @@ -338,6 +340,14 @@ static int notify_on_release(const struct cgroup *cgrp) #define for_each_root(root) \ list_for_each_entry((root), &cgroup_roots, root_list) +/* iterate over child cgrps, lock should be held throughout iteration */ +#define cgroup_for_each_live_child(child, cgrp) \ + list_for_each_entry((child), &(cgrp)->children, sibling) \ + if (({ lockdep_assert_held(&cgroup_tree_mutex); \ + cgroup_is_dead(child); })) \ + ; \ + else + /** * cgroup_lock_live_group - take cgroup_mutex and check that cgrp is alive. * @cgrp: the cgroup to be checked for liveness @@ -1450,6 +1460,8 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) for_each_subsys(ss, ssid) INIT_LIST_HEAD(&cgrp->e_csets[ssid]); + + init_waitqueue_head(&cgrp->offline_waitq); } static void init_cgroup_root(struct cgroup_root *root, @@ -1938,6 +1950,14 @@ static int cgroup_migrate_prepare_dst(struct cgroup *dst_cgrp, lockdep_assert_held(&cgroup_mutex); + /* + * Except for the root, child_subsys_mask must be zero for a cgroup + * with tasks so that child cgroups don't compete against tasks. + */ + if (dst_cgrp && cgroup_on_dfl(dst_cgrp) && dst_cgrp->parent && + dst_cgrp->child_subsys_mask) + return -EBUSY; + /* look up the dst cset for each src cset and link it to src */ list_for_each_entry_safe(src_cset, tmp_cset, preloaded_csets, mg_preload_node) { struct css_set *dst_cset; @@ -2303,6 +2323,326 @@ static int cgroup_sane_behavior_show(struct seq_file *seq, void *v) return 0; } +static void cgroup_print_ss_mask(struct seq_file *seq, unsigned int ss_mask) +{ + struct cgroup_subsys *ss; + bool printed = false; + int ssid; + + for_each_subsys(ss, ssid) { + if (ss_mask & (1 << ssid)) { + if (printed) + seq_putc(seq, ' '); + seq_printf(seq, "%s", ss->name); + printed = true; + } + } + if (printed) + seq_putc(seq, '\n'); +} + +/* show controllers which are currently attached to the default hierarchy */ +static int cgroup_root_controllers_show(struct seq_file *seq, void *v) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; + + cgroup_print_ss_mask(seq, cgrp->root->subsys_mask); + return 0; +} + +/* show controllers which are enabled from the parent */ +static int cgroup_controllers_show(struct seq_file *seq, void *v) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; + + cgroup_print_ss_mask(seq, cgrp->parent->child_subsys_mask); + return 0; +} + +/* show controllers which are enabled for a given cgroup's children */ +static int cgroup_subtree_control_show(struct seq_file *seq, void *v) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; + + cgroup_print_ss_mask(seq, cgrp->child_subsys_mask); + return 0; +} + +/** + * cgroup_update_dfl_csses - update css assoc of a subtree in default hierarchy + * @cgrp: root of the subtree to update csses for + * + * @cgrp's child_subsys_mask has changed and its subtree's (self excluded) + * css associations need to be updated accordingly. This function looks up + * all css_sets which are attached to the subtree, creates the matching + * updated css_sets and migrates the tasks to the new ones. + */ +static int cgroup_update_dfl_csses(struct cgroup *cgrp) +{ + LIST_HEAD(preloaded_csets); + struct cgroup_subsys_state *css; + struct css_set *src_cset; + int ret; + + lockdep_assert_held(&cgroup_tree_mutex); + lockdep_assert_held(&cgroup_mutex); + + /* look up all csses currently attached to @cgrp's subtree */ + down_read(&css_set_rwsem); + css_for_each_descendant_pre(css, cgroup_css(cgrp, NULL)) { + struct cgrp_cset_link *link; + + /* self is not affected by child_subsys_mask change */ + if (css->cgroup == cgrp) + continue; + + list_for_each_entry(link, &css->cgroup->cset_links, cset_link) + cgroup_migrate_add_src(link->cset, cgrp, + &preloaded_csets); + } + up_read(&css_set_rwsem); + + /* NULL dst indicates self on default hierarchy */ + ret = cgroup_migrate_prepare_dst(NULL, &preloaded_csets); + if (ret) + goto out_finish; + + list_for_each_entry(src_cset, &preloaded_csets, mg_preload_node) { + struct task_struct *last_task = NULL, *task; + + /* src_csets precede dst_csets, break on the first dst_cset */ + if (!src_cset->mg_src_cgrp) + break; + + /* + * All tasks in src_cset need to be migrated to the + * matching dst_cset. Empty it process by process. We + * walk tasks but migrate processes. The leader might even + * belong to a different cset but such src_cset would also + * be among the target src_csets because the default + * hierarchy enforces per-process membership. + */ + while (true) { + down_read(&css_set_rwsem); + task = list_first_entry_or_null(&src_cset->tasks, + struct task_struct, cg_list); + if (task) { + task = task->group_leader; + WARN_ON_ONCE(!task_css_set(task)->mg_src_cgrp); + get_task_struct(task); + } + up_read(&css_set_rwsem); + + if (!task) + break; + + /* guard against possible infinite loop */ + if (WARN(last_task == task, + "cgroup: update_dfl_csses failed to make progress, aborting in inconsistent state\n")) + goto out_finish; + last_task = task; + + threadgroup_lock(task); + /* raced against de_thread() from another thread? */ + if (!thread_group_leader(task)) { + threadgroup_unlock(task); + put_task_struct(task); + continue; + } + + ret = cgroup_migrate(src_cset->dfl_cgrp, task, true); + + threadgroup_unlock(task); + put_task_struct(task); + + if (WARN(ret, "cgroup: failed to update controllers for the default hierarchy (%d), further operations may crash or hang\n", ret)) + goto out_finish; + } + } + +out_finish: + cgroup_migrate_finish(&preloaded_csets); + return ret; +} + +/* change the enabled child controllers for a cgroup in the default hierarchy */ +static int cgroup_subtree_control_write(struct cgroup_subsys_state *dummy_css, + struct cftype *cft, char *buffer) +{ + unsigned long enable_req = 0, disable_req = 0, enable, disable; + struct cgroup *cgrp = dummy_css->cgroup, *child; + struct cgroup_subsys *ss; + char *tok, *p; + int ssid, ret; + + /* + * Parse input - white space separated list of subsystem names + * prefixed with either + or -. + */ + p = buffer; + while ((tok = strsep(&p, " \t\n"))) { + for_each_subsys(ss, ssid) { + if (ss->disabled || strcmp(tok + 1, ss->name)) + continue; + + if (*tok == '+') { + enable_req |= 1 << ssid; + disable_req &= ~(1 << ssid); + } else if (*tok == '-') { + disable_req |= 1 << ssid; + enable_req &= ~(1 << ssid); + } else { + return -EINVAL; + } + break; + } + if (ssid == CGROUP_SUBSYS_COUNT) + return -EINVAL; + } + + /* + * We're gonna grab cgroup_tree_mutex which nests outside kernfs + * active_ref. cgroup_lock_live_group() already provides enough + * protection. Ensure @cgrp stays accessible and break the + * active_ref protection. + */ + cgroup_get(cgrp); + kernfs_break_active_protection(cgrp->control_kn); +retry: + enable = enable_req; + disable = disable_req; + + mutex_lock(&cgroup_tree_mutex); + + for_each_subsys(ss, ssid) { + if (enable & (1 << ssid)) { + if (cgrp->child_subsys_mask & (1 << ssid)) { + enable &= ~(1 << ssid); + continue; + } + + /* + * Because css offlining is asynchronous, userland + * might try to re-enable the same controller while + * the previous instance is still around. In such + * cases, wait till it's gone using offline_waitq. + */ + cgroup_for_each_live_child(child, cgrp) { + wait_queue_t wait; + + if (!cgroup_css(child, ss)) + continue; + + prepare_to_wait(&child->offline_waitq, &wait, + TASK_UNINTERRUPTIBLE); + mutex_unlock(&cgroup_tree_mutex); + schedule(); + finish_wait(&child->offline_waitq, &wait); + goto retry; + } + + /* unavailable or not enabled on the parent? */ + if (!(cgrp_dfl_root.subsys_mask & (1 << ssid)) || + (cgrp->parent && + !(cgrp->parent->child_subsys_mask & (1 << ssid)))) { + ret = -ENOENT; + goto out_unlock_tree; + } + } else if (disable & (1 << ssid)) { + if (!(cgrp->child_subsys_mask & (1 << ssid))) { + disable &= ~(1 << ssid); + continue; + } + + /* a child has it enabled? */ + cgroup_for_each_live_child(child, cgrp) { + if (child->child_subsys_mask & (1 << ssid)) { + ret = -EBUSY; + goto out_unlock_tree; + } + } + } + } + + if (!enable && !disable) { + ret = 0; + goto out_unlock_tree; + } + + if (!cgroup_lock_live_group(cgrp)) { + ret = -ENODEV; + goto out_unlock_tree; + } + + /* + * Except for the root, child_subsys_mask must be zero for a cgroup + * with tasks so that child cgroups don't compete against tasks. + */ + if (enable && cgrp->parent && !list_empty(&cgrp->cset_links)) { + ret = -EBUSY; + goto out_unlock; + } + + /* + * Create csses for enables and update child_subsys_mask. This + * changes cgroup_e_css() results which in turn makes the + * subsequent cgroup_update_dfl_csses() associate all tasks in the + * subtree to the updated csses. + */ + for_each_subsys(ss, ssid) { + if (!(enable & (1 << ssid))) + continue; + + cgroup_for_each_live_child(child, cgrp) { + ret = create_css(child, ss); + if (ret) + goto err_undo_css; + } + } + + cgrp->child_subsys_mask |= enable; + cgrp->child_subsys_mask &= ~disable; + + ret = cgroup_update_dfl_csses(cgrp); + if (ret) + goto err_undo_css; + + /* all tasks are now migrated away from the old csses, kill them */ + for_each_subsys(ss, ssid) { + if (!(disable & (1 << ssid))) + continue; + + cgroup_for_each_live_child(child, cgrp) + kill_css(cgroup_css(child, ss)); + } + + kernfs_activate(cgrp->kn); + ret = 0; +out_unlock: + mutex_unlock(&cgroup_mutex); +out_unlock_tree: + mutex_unlock(&cgroup_tree_mutex); + kernfs_unbreak_active_protection(cgrp->control_kn); + cgroup_put(cgrp); + return ret; + +err_undo_css: + cgrp->child_subsys_mask &= ~enable; + cgrp->child_subsys_mask |= disable; + + for_each_subsys(ss, ssid) { + if (!(enable & (1 << ssid))) + continue; + + cgroup_for_each_live_child(child, cgrp) { + struct cgroup_subsys_state *css = cgroup_css(child, ss); + if (css) + kill_css(css); + } + } + goto out_unlock; +} + static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { @@ -2462,9 +2802,14 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft) return PTR_ERR(kn); ret = cgroup_kn_set_ugid(kn); - if (ret) + if (ret) { kernfs_remove(kn); - return ret; + return ret; + } + + if (cft->seq_show == cgroup_subtree_control_show) + cgrp->control_kn = kn; + return 0; } /** @@ -3557,6 +3902,22 @@ static struct cftype cgroup_base_files[] = { .flags = CFTYPE_ONLY_ON_ROOT, .seq_show = cgroup_sane_behavior_show, }, + { + .name = "cgroup.controllers", + .flags = CFTYPE_ONLY_ON_DFL | CFTYPE_ONLY_ON_ROOT, + .seq_show = cgroup_root_controllers_show, + }, + { + .name = "cgroup.controllers", + .flags = CFTYPE_ONLY_ON_DFL | CFTYPE_NOT_ON_ROOT, + .seq_show = cgroup_controllers_show, + }, + { + .name = "cgroup.subtree_control", + .flags = CFTYPE_ONLY_ON_DFL, + .seq_show = cgroup_subtree_control_show, + .write_string = cgroup_subtree_control_write, + }, /* * Historical crazy stuff. These don't have "cgroup." prefix and @@ -3725,6 +4086,8 @@ static void offline_css(struct cgroup_subsys_state *css) css->flags &= ~CSS_ONLINE; css->cgroup->nr_css--; RCU_INIT_POINTER(css->cgroup->subsys[ss->id], NULL); + + wake_up_all(&css->cgroup->offline_waitq); } /** -- cgit v1.2.3 From a086f6a1ebc9d8d2d028b99e779ce0dbd9691dea Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Thu, 17 Apr 2014 17:06:12 +0800 Subject: Revert "KVM: Simplify kvm->tlbs_dirty handling" This reverts commit 5befdc385ddb2d5ae8995ad89004529a3acf58fc. Since we will allow flush tlb out of mmu-lock in the later patch Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/paging_tmpl.h | 7 +++---- include/linux/kvm_host.h | 4 +--- virt/kvm/kvm_main.c | 5 ++++- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 123efd3ec29f..410776528265 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -913,8 +913,7 @@ static gpa_t FNAME(gva_to_gpa_nested)(struct kvm_vcpu *vcpu, gva_t vaddr, * and kvm_mmu_notifier_invalidate_range_start detect the mapping page isn't * used by guest then tlbs are not flushed, so guest is allowed to access the * freed pages. - * We set tlbs_dirty to let the notifier know this change and delay the flush - * until such a case actually happens. + * And we increase kvm->tlbs_dirty to delay tlbs flush in this case. */ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) { @@ -943,7 +942,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) return -EINVAL; if (FNAME(prefetch_invalid_gpte)(vcpu, sp, &sp->spt[i], gpte)) { - vcpu->kvm->tlbs_dirty = true; + vcpu->kvm->tlbs_dirty++; continue; } @@ -958,7 +957,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) if (gfn != sp->gfns[i]) { drop_spte(vcpu->kvm, &sp->spt[i]); - vcpu->kvm->tlbs_dirty = true; + vcpu->kvm->tlbs_dirty++; continue; } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 32d263f683dc..820fc2e1d9df 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -411,9 +411,7 @@ struct kvm { unsigned long mmu_notifier_seq; long mmu_notifier_count; #endif - /* Protected by mmu_lock */ - bool tlbs_dirty; - + long tlbs_dirty; struct list_head devices; }; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index ea46d64c8e75..fa70c6e642b4 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -186,9 +186,12 @@ static bool make_all_cpus_request(struct kvm *kvm, unsigned int req) void kvm_flush_remote_tlbs(struct kvm *kvm) { + long dirty_count = kvm->tlbs_dirty; + + smp_mb(); if (make_all_cpus_request(kvm, KVM_REQ_TLB_FLUSH)) ++kvm->stat.remote_tlb_flush; - kvm->tlbs_dirty = false; + cmpxchg(&kvm->tlbs_dirty, dirty_count, 0); } EXPORT_SYMBOL_GPL(kvm_flush_remote_tlbs); -- cgit v1.2.3 From 5686a1e5aa436c49187a60052d5885fb1f541ce6 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:47:01 +0200 Subject: bus: mvebu: pass the coherency availability information at init time Until now, the mvebu-mbus was guessing by itself whether hardware I/O coherency was available or not by poking into the Device Tree to see if the coherency fabric Device Tree node was present or not. However, on some upcoming SoCs, the presence or absence of the coherency fabric DT node isn't sufficient: in CONFIG_SMP, the coherency can be enabled, but not in !CONFIG_SMP. In order to clean this up, the mvebu_mbus_dt_init() function is extended to get a boolean argument telling whether coherency is enabled or not. Therefore, the logic to decide whether coherency is available or not now belongs to the core SoC code instead of the mvebu-mbus driver itself, which is much better. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-4-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-kirkwood/board-dt.c | 2 +- arch/arm/mach-mvebu/board-v7.c | 2 +- arch/arm/mach-mvebu/dove.c | 2 +- arch/arm/mach-mvebu/kirkwood.c | 2 +- drivers/bus/mvebu-mbus.c | 11 +++-------- include/linux/mbus.h | 2 +- 6 files changed, 8 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-kirkwood/board-dt.c b/arch/arm/mach-kirkwood/board-dt.c index 2801da49e2a3..ff18ff20f71f 100644 --- a/arch/arm/mach-kirkwood/board-dt.c +++ b/arch/arm/mach-kirkwood/board-dt.c @@ -195,7 +195,7 @@ static void __init kirkwood_dt_init(void) { kirkwood_disable_mbus_error_propagation(); - BUG_ON(mvebu_mbus_dt_init()); + BUG_ON(mvebu_mbus_dt_init(false)); #ifdef CONFIG_CACHE_FEROCEON_L2 feroceon_of_init(); diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index 333fca8fdc41..1730e0cdb6f6 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c @@ -58,7 +58,7 @@ static void __init mvebu_timer_and_clk_init(void) of_clk_init(NULL); clocksource_of_init(); coherency_init(); - BUG_ON(mvebu_mbus_dt_init()); + BUG_ON(mvebu_mbus_dt_init(coherency_available())); #ifdef CONFIG_CACHE_L2X0 l2x0_of_init(0, ~0UL); #endif diff --git a/arch/arm/mach-mvebu/dove.c b/arch/arm/mach-mvebu/dove.c index 5e5a43624237..b50464ec1130 100644 --- a/arch/arm/mach-mvebu/dove.c +++ b/arch/arm/mach-mvebu/dove.c @@ -23,7 +23,7 @@ static void __init dove_init(void) #ifdef CONFIG_CACHE_TAUROS2 tauros2_init(0); #endif - BUG_ON(mvebu_mbus_dt_init()); + BUG_ON(mvebu_mbus_dt_init(false)); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c index 120207fc36f1..a77e0bae9c55 100644 --- a/arch/arm/mach-mvebu/kirkwood.c +++ b/arch/arm/mach-mvebu/kirkwood.c @@ -169,7 +169,7 @@ static void __init kirkwood_dt_init(void) { kirkwood_disable_mbus_error_propagation(); - BUG_ON(mvebu_mbus_dt_init()); + BUG_ON(mvebu_mbus_dt_init(false)); #ifdef CONFIG_CACHE_FEROCEON_L2 feroceon_of_init(); diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 293e2e0a0a87..ff02fc90fc21 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -694,7 +694,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, phys_addr_t sdramwins_phys_base, size_t sdramwins_size) { - struct device_node *np; int win; mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size); @@ -707,12 +706,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, return -ENOMEM; } - np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); - if (np) { - mbus->hw_io_coherency = 1; - of_node_put(np); - } - for (win = 0; win < mbus->soc->num_wins; win++) mvebu_mbus_disable_window(mbus, win); @@ -882,7 +875,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, } } -int __init mvebu_mbus_dt_init(void) +int __init mvebu_mbus_dt_init(bool is_coherent) { struct resource mbuswins_res, sdramwins_res; struct device_node *np, *controller; @@ -920,6 +913,8 @@ int __init mvebu_mbus_dt_init(void) return -EINVAL; } + mbus_state.hw_io_coherency = is_coherent; + /* Get optional pcie-{mem,io}-aperture properties */ mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, &mbus_state.pcie_io_aperture); diff --git a/include/linux/mbus.h b/include/linux/mbus.h index 345b8c53b897..550c88fb0267 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h @@ -73,6 +73,6 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size); int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base, size_t mbus_size, phys_addr_t sdram_phys_base, size_t sdram_size); -int mvebu_mbus_dt_init(void); +int mvebu_mbus_dt_init(bool is_coherent); #endif /* __LINUX_MBUS_H */ -- cgit v1.2.3 From be8f274323c26ddc7e6fd6c44254b7abcdbe6389 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 17 Apr 2014 17:16:58 +0900 Subject: kprobes: Prohibit probing on .entry.text code .entry.text is a code area which is used for interrupt/syscall entries, which includes many sensitive code. Thus, it is better to prohibit probing on all of such code instead of a part of that. Since some symbols are already registered on kprobe blacklist, this also removes them from the blacklist. Signed-off-by: Masami Hiramatsu Reviewed-by: Steven Rostedt Cc: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Cc: Borislav Petkov Cc: David S. Miller Cc: Frederic Weisbecker Cc: Jan Kiszka Cc: Jiri Kosina Cc: Jonathan Lebon Cc: Seiji Aguchi Link: http://lkml.kernel.org/r/20140417081658.26341.57354.stgit@ltc230.yrl.intra.hitachi.co.jp Signed-off-by: Ingo Molnar --- arch/x86/kernel/entry_32.S | 33 --------------------------------- arch/x86/kernel/entry_64.S | 20 -------------------- arch/x86/kernel/kprobes/core.c | 8 ++++++++ include/linux/kprobes.h | 1 + kernel/kprobes.c | 13 ++++++++----- 5 files changed, 17 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index a2a4f4697889..0ca5bf1697bb 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -314,10 +314,6 @@ ENTRY(ret_from_kernel_thread) CFI_ENDPROC ENDPROC(ret_from_kernel_thread) -/* - * Interrupt exit functions should be protected against kprobes - */ - .pushsection .kprobes.text, "ax" /* * Return to user mode is not as complex as all this looks, * but we want the default path for a system call return to @@ -372,10 +368,6 @@ need_resched: END(resume_kernel) #endif CFI_ENDPROC -/* - * End of kprobes section - */ - .popsection /* SYSENTER_RETURN points to after the "sysenter" instruction in the vsyscall page. See vsyscall-sysentry.S, which defines the symbol. */ @@ -495,10 +487,6 @@ sysexit_audit: PTGS_TO_GS_EX ENDPROC(ia32_sysenter_target) -/* - * syscall stub including irq exit should be protected against kprobes - */ - .pushsection .kprobes.text, "ax" # system call handler stub ENTRY(system_call) RING0_INT_FRAME # can't unwind into user space anyway @@ -691,10 +679,6 @@ syscall_badsys: jmp resume_userspace END(syscall_badsys) CFI_ENDPROC -/* - * End of kprobes section - */ - .popsection .macro FIXUP_ESPFIX_STACK /* @@ -781,10 +765,6 @@ common_interrupt: ENDPROC(common_interrupt) CFI_ENDPROC -/* - * Irq entries should be protected against kprobes - */ - .pushsection .kprobes.text, "ax" #define BUILD_INTERRUPT3(name, nr, fn) \ ENTRY(name) \ RING0_INT_FRAME; \ @@ -961,10 +941,6 @@ ENTRY(spurious_interrupt_bug) jmp error_code CFI_ENDPROC END(spurious_interrupt_bug) -/* - * End of kprobes section - */ - .popsection #ifdef CONFIG_XEN /* Xen doesn't set %esp to be precisely what the normal sysenter @@ -1239,11 +1215,6 @@ return_to_handler: jmp *%ecx #endif -/* - * Some functions should be protected against kprobes - */ - .pushsection .kprobes.text, "ax" - #ifdef CONFIG_TRACING ENTRY(trace_page_fault) RING0_EC_FRAME @@ -1453,7 +1424,3 @@ ENTRY(async_page_fault) END(async_page_fault) #endif -/* - * End of kprobes section - */ - .popsection diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 1e96c3628bf2..43bb38951660 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -487,8 +487,6 @@ ENDPROC(native_usergs_sysret64) TRACE_IRQS_OFF .endm -/* save complete stack frame */ - .pushsection .kprobes.text, "ax" ENTRY(save_paranoid) XCPT_FRAME 1 RDI+8 cld @@ -517,7 +515,6 @@ ENTRY(save_paranoid) 1: ret CFI_ENDPROC END(save_paranoid) - .popsection /* * A newly forked process directly context switches into this address. @@ -975,10 +972,6 @@ END(interrupt) call \func .endm -/* - * Interrupt entry/exit should be protected against kprobes - */ - .pushsection .kprobes.text, "ax" /* * The interrupt stubs push (~vector+0x80) onto the stack and * then jump to common_interrupt. @@ -1113,10 +1106,6 @@ ENTRY(retint_kernel) CFI_ENDPROC END(common_interrupt) -/* - * End of kprobes section - */ - .popsection /* * APIC interrupts. @@ -1477,11 +1466,6 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ hyperv_callback_vector hyperv_vector_handler #endif /* CONFIG_HYPERV */ -/* - * Some functions should be protected against kprobes - */ - .pushsection .kprobes.text, "ax" - paranoidzeroentry_ist debug do_debug DEBUG_STACK paranoidzeroentry_ist int3 do_int3 DEBUG_STACK paranoiderrorentry stack_segment do_stack_segment @@ -1898,7 +1882,3 @@ ENTRY(ignore_sysret) CFI_ENDPROC END(ignore_sysret) -/* - * End of kprobes section - */ - .popsection diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c index da7bdaa3ce15..7751b3dee53a 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c @@ -1065,6 +1065,14 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) return 0; } +bool arch_within_kprobe_blacklist(unsigned long addr) +{ + return (addr >= (unsigned long)__kprobes_text_start && + addr < (unsigned long)__kprobes_text_end) || + (addr >= (unsigned long)__entry_text_start && + addr < (unsigned long)__entry_text_end); +} + int __init arch_init_kprobes(void) { return 0; diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 925eaf28fca9..cdf9251f8249 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -265,6 +265,7 @@ extern void arch_disarm_kprobe(struct kprobe *p); extern int arch_init_kprobes(void); extern void show_registers(struct pt_regs *regs); extern void kprobes_inc_nmissed_count(struct kprobe *p); +extern bool arch_within_kprobe_blacklist(unsigned long addr); struct kprobe_insn_cache { struct mutex mutex; diff --git a/kernel/kprobes.c b/kernel/kprobes.c index ceeadfcabb76..5b5ac76671e7 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -96,9 +96,6 @@ static raw_spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) static struct kprobe_blackpoint kprobe_blacklist[] = { {"preempt_schedule",}, {"native_get_debugreg",}, - {"irq_entries_start",}, - {"common_interrupt",}, - {"mcount",}, /* mcount can be called from everywhere */ {NULL} /* Terminator */ }; @@ -1324,12 +1321,18 @@ out: return ret; } +bool __weak arch_within_kprobe_blacklist(unsigned long addr) +{ + /* The __kprobes marked functions and entry code must not be probed */ + return addr >= (unsigned long)__kprobes_text_start && + addr < (unsigned long)__kprobes_text_end; +} + static int __kprobes in_kprobes_functions(unsigned long addr) { struct kprobe_blackpoint *kb; - if (addr >= (unsigned long)__kprobes_text_start && - addr < (unsigned long)__kprobes_text_end) + if (arch_within_kprobe_blacklist(addr)) return -EINVAL; /* * If there exists a kprobe_blacklist, verify and -- cgit v1.2.3 From 376e242429bf8539ef39a080ac113c8799840b13 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 17 Apr 2014 17:17:05 +0900 Subject: kprobes: Introduce NOKPROBE_SYMBOL() macro to maintain kprobes blacklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce NOKPROBE_SYMBOL() macro which builds a kprobes blacklist at kernel build time. The usage of this macro is similar to EXPORT_SYMBOL(), placed after the function definition: NOKPROBE_SYMBOL(function); Since this macro will inhibit inlining of static/inline functions, this patch also introduces a nokprobe_inline macro for static/inline functions. In this case, we must use NOKPROBE_SYMBOL() for the inline function caller. When CONFIG_KPROBES=y, the macro stores the given function address in the "_kprobe_blacklist" section. Since the data structures are not fully initialized by the macro (because there is no "size" information), those are re-initialized at boot time by using kallsyms. Signed-off-by: Masami Hiramatsu Link: http://lkml.kernel.org/r/20140417081705.26341.96719.stgit@ltc230.yrl.intra.hitachi.co.jp Cc: Alok Kataria Cc: Ananth N Mavinakayanahalli Cc: Andrew Morton Cc: Anil S Keshavamurthy Cc: Arnd Bergmann Cc: Christopher Li Cc: Chris Wright Cc: David S. Miller Cc: Jan-Simon Möller Cc: Jeremy Fitzhardinge Cc: Linus Torvalds Cc: Randy Dunlap Cc: Rusty Russell Cc: linux-arch@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: linux-sparse@vger.kernel.org Cc: virtualization@lists.linux-foundation.org Signed-off-by: Ingo Molnar --- Documentation/kprobes.txt | 16 +++++- arch/x86/include/asm/asm.h | 7 +++ arch/x86/kernel/paravirt.c | 4 ++ include/asm-generic/vmlinux.lds.h | 9 ++++ include/linux/compiler.h | 2 + include/linux/kprobes.h | 20 ++++++-- kernel/kprobes.c | 100 ++++++++++++++++++++------------------ kernel/sched/core.c | 1 + 8 files changed, 107 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/Documentation/kprobes.txt b/Documentation/kprobes.txt index 0cfb00fd86ff..4bbeca8483ed 100644 --- a/Documentation/kprobes.txt +++ b/Documentation/kprobes.txt @@ -22,8 +22,9 @@ Appendix B: The kprobes sysctl interface Kprobes enables you to dynamically break into any kernel routine and collect debugging and performance information non-disruptively. You -can trap at almost any kernel code address, specifying a handler +can trap at almost any kernel code address(*), specifying a handler routine to be invoked when the breakpoint is hit. +(*: some parts of the kernel code can not be trapped, see 1.5 Blacklist) There are currently three types of probes: kprobes, jprobes, and kretprobes (also called return probes). A kprobe can be inserted @@ -273,6 +274,19 @@ using one of the following techniques: or - Execute 'sysctl -w debug.kprobes_optimization=n' +1.5 Blacklist + +Kprobes can probe most of the kernel except itself. This means +that there are some functions where kprobes cannot probe. Probing +(trapping) such functions can cause a recursive trap (e.g. double +fault) or the nested probe handler may never be called. +Kprobes manages such functions as a blacklist. +If you want to add a function into the blacklist, you just need +to (1) include linux/kprobes.h and (2) use NOKPROBE_SYMBOL() macro +to specify a blacklisted function. +Kprobes checks the given probe address against the blacklist and +rejects registering it, if the given address is in the blacklist. + 2. Architectures Supported Kprobes, jprobes, and return probes are implemented on the following diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h index 4582e8e1cd1a..7730c1c5c83a 100644 --- a/arch/x86/include/asm/asm.h +++ b/arch/x86/include/asm/asm.h @@ -57,6 +57,12 @@ .long (from) - . ; \ .long (to) - . + 0x7ffffff0 ; \ .popsection + +# define _ASM_NOKPROBE(entry) \ + .pushsection "_kprobe_blacklist","aw" ; \ + _ASM_ALIGN ; \ + _ASM_PTR (entry); \ + .popsection #else # define _ASM_EXTABLE(from,to) \ " .pushsection \"__ex_table\",\"a\"\n" \ @@ -71,6 +77,7 @@ " .long (" #from ") - .\n" \ " .long (" #to ") - . + 0x7ffffff0\n" \ " .popsection\n" +/* For C file, we already have NOKPROBE_SYMBOL macro */ #endif #endif /* _ASM_X86_ASM_H */ diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index 1b10af835c31..e136869ae42e 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -389,6 +390,9 @@ __visible struct pv_cpu_ops pv_cpu_ops = { .end_context_switch = paravirt_nop, }; +/* At this point, native_get_debugreg has a real function entry */ +NOKPROBE_SYMBOL(native_get_debugreg); + struct pv_apic_ops pv_apic_ops = { #ifdef CONFIG_X86_LOCAL_APIC .startup_ipi_hook = paravirt_nop, diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 146e4fffd710..40ceb3ceba79 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -109,6 +109,14 @@ #define BRANCH_PROFILE() #endif +#ifdef CONFIG_KPROBES +#define KPROBE_BLACKLIST() VMLINUX_SYMBOL(__start_kprobe_blacklist) = .; \ + *(_kprobe_blacklist) \ + VMLINUX_SYMBOL(__stop_kprobe_blacklist) = .; +#else +#define KPROBE_BLACKLIST() +#endif + #ifdef CONFIG_EVENT_TRACING #define FTRACE_EVENTS() . = ALIGN(8); \ VMLINUX_SYMBOL(__start_ftrace_events) = .; \ @@ -507,6 +515,7 @@ *(.init.rodata) \ FTRACE_EVENTS() \ TRACE_SYSCALLS() \ + KPROBE_BLACKLIST() \ MEM_DISCARD(init.rodata) \ CLK_OF_TABLES() \ RESERVEDMEM_OF_TABLES() \ diff --git a/include/linux/compiler.h b/include/linux/compiler.h index ee7239ea1583..0300c0f5c88b 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -374,7 +374,9 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); /* Ignore/forbid kprobes attach on very low level functions marked by this attribute: */ #ifdef CONFIG_KPROBES # define __kprobes __attribute__((__section__(".kprobes.text"))) +# define nokprobe_inline __always_inline #else # define __kprobes +# define nokprobe_inline inline #endif #endif /* __LINUX_COMPILER_H */ diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index cdf9251f8249..e059507c465d 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -205,10 +205,10 @@ struct kretprobe_blackpoint { void *addr; }; -struct kprobe_blackpoint { - const char *name; +struct kprobe_blacklist_entry { + struct list_head list; unsigned long start_addr; - unsigned long range; + unsigned long end_addr; }; #ifdef CONFIG_KPROBES @@ -477,4 +477,18 @@ static inline int enable_jprobe(struct jprobe *jp) return enable_kprobe(&jp->kp); } +#ifdef CONFIG_KPROBES +/* + * Blacklist ganerating macro. Specify functions which is not probed + * by using this macro. + */ +#define __NOKPROBE_SYMBOL(fname) \ +static unsigned long __used \ + __attribute__((section("_kprobe_blacklist"))) \ + _kbl_addr_##fname = (unsigned long)fname; +#define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname) +#else +#define NOKPROBE_SYMBOL(fname) +#endif + #endif /* _LINUX_KPROBES_H */ diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 5b5ac76671e7..5ffc6875d2a7 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -86,18 +86,8 @@ static raw_spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) return &(kretprobe_table_locks[hash].lock); } -/* - * Normally, functions that we'd want to prohibit kprobes in, are marked - * __kprobes. But, there are cases where such functions already belong to - * a different section (__sched for preempt_schedule) - * - * For such cases, we now have a blacklist - */ -static struct kprobe_blackpoint kprobe_blacklist[] = { - {"preempt_schedule",}, - {"native_get_debugreg",}, - {NULL} /* Terminator */ -}; +/* Blacklist -- list of struct kprobe_blacklist_entry */ +static LIST_HEAD(kprobe_blacklist); #ifdef __ARCH_WANT_KPROBES_INSN_SLOT /* @@ -1328,24 +1318,22 @@ bool __weak arch_within_kprobe_blacklist(unsigned long addr) addr < (unsigned long)__kprobes_text_end; } -static int __kprobes in_kprobes_functions(unsigned long addr) +static bool __kprobes within_kprobe_blacklist(unsigned long addr) { - struct kprobe_blackpoint *kb; + struct kprobe_blacklist_entry *ent; if (arch_within_kprobe_blacklist(addr)) - return -EINVAL; + return true; /* * If there exists a kprobe_blacklist, verify and * fail any probe registration in the prohibited area */ - for (kb = kprobe_blacklist; kb->name != NULL; kb++) { - if (kb->start_addr) { - if (addr >= kb->start_addr && - addr < (kb->start_addr + kb->range)) - return -EINVAL; - } + list_for_each_entry(ent, &kprobe_blacklist, list) { + if (addr >= ent->start_addr && addr < ent->end_addr) + return true; } - return 0; + + return false; } /* @@ -1436,7 +1424,7 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p, /* Ensure it is not in reserved area nor out of text */ if (!kernel_text_address((unsigned long) p->addr) || - in_kprobes_functions((unsigned long) p->addr) || + within_kprobe_blacklist((unsigned long) p->addr) || jump_label_text_reserved(p->addr, p->addr)) { ret = -EINVAL; goto out; @@ -2022,6 +2010,38 @@ void __kprobes dump_kprobe(struct kprobe *kp) kp->symbol_name, kp->addr, kp->offset); } +/* + * Lookup and populate the kprobe_blacklist. + * + * Unlike the kretprobe blacklist, we'll need to determine + * the range of addresses that belong to the said functions, + * since a kprobe need not necessarily be at the beginning + * of a function. + */ +static int __init populate_kprobe_blacklist(unsigned long *start, + unsigned long *end) +{ + unsigned long *iter; + struct kprobe_blacklist_entry *ent; + unsigned long offset = 0, size = 0; + + for (iter = start; iter < end; iter++) { + if (!kallsyms_lookup_size_offset(*iter, &size, &offset)) { + pr_err("Failed to find blacklist %p\n", (void *)*iter); + continue; + } + + ent = kmalloc(sizeof(*ent), GFP_KERNEL); + if (!ent) + return -ENOMEM; + ent->start_addr = *iter; + ent->end_addr = *iter + size; + INIT_LIST_HEAD(&ent->list); + list_add_tail(&ent->list, &kprobe_blacklist); + } + return 0; +} + /* Module notifier call back, checking kprobes on the module */ static int __kprobes kprobes_module_callback(struct notifier_block *nb, unsigned long val, void *data) @@ -2065,14 +2085,13 @@ static struct notifier_block kprobe_module_nb = { .priority = 0 }; +/* Markers of _kprobe_blacklist section */ +extern unsigned long __start_kprobe_blacklist[]; +extern unsigned long __stop_kprobe_blacklist[]; + static int __init init_kprobes(void) { int i, err = 0; - unsigned long offset = 0, size = 0; - char *modname, namebuf[KSYM_NAME_LEN]; - const char *symbol_name; - void *addr; - struct kprobe_blackpoint *kb; /* FIXME allocate the probe table, currently defined statically */ /* initialize all list heads */ @@ -2082,26 +2101,11 @@ static int __init init_kprobes(void) raw_spin_lock_init(&(kretprobe_table_locks[i].lock)); } - /* - * Lookup and populate the kprobe_blacklist. - * - * Unlike the kretprobe blacklist, we'll need to determine - * the range of addresses that belong to the said functions, - * since a kprobe need not necessarily be at the beginning - * of a function. - */ - for (kb = kprobe_blacklist; kb->name != NULL; kb++) { - kprobe_lookup_name(kb->name, addr); - if (!addr) - continue; - - kb->start_addr = (unsigned long)addr; - symbol_name = kallsyms_lookup(kb->start_addr, - &size, &offset, &modname, namebuf); - if (!symbol_name) - kb->range = 0; - else - kb->range = size; + err = populate_kprobe_blacklist(__start_kprobe_blacklist, + __stop_kprobe_blacklist); + if (err) { + pr_err("kprobes: failed to populate blacklist: %d\n", err); + pr_err("Please take care of using kprobes.\n"); } if (kretprobe_blacklist_size) { diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 268a45ea238c..6863631e8cd0 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2804,6 +2804,7 @@ asmlinkage void __sched notrace preempt_schedule(void) barrier(); } while (need_resched()); } +NOKPROBE_SYMBOL(preempt_schedule); EXPORT_SYMBOL(preempt_schedule); #endif /* CONFIG_PREEMPT */ -- cgit v1.2.3 From f5efc696cc711021cc73e7543cc3038e58459707 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Mon, 14 Apr 2014 15:41:28 +0300 Subject: netfilter: nf_tables: Add meta expression key for bridge interface name NFT_META_BRI_IIFNAME to get packet input bridge interface name NFT_META_BRI_OIFNAME to get packet output bridge interface name Such meta key are accessible only through NFPROTO_BRIDGE family, on a dedicated nft meta module: nft_meta_bridge. Suggested-by: Pablo Neira Ayuso Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 4 + net/bridge/Makefile | 2 +- net/bridge/netfilter/Kconfig | 14 +++- net/bridge/netfilter/Makefile | 1 + net/bridge/netfilter/nft_meta_bridge.c | 139 +++++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 net/bridge/netfilter/nft_meta_bridge.c (limited to 'include') diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 160159274cab..7d6433f66bf8 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -563,6 +563,8 @@ enum nft_exthdr_attributes { * @NFT_META_SECMARK: packet secmark (skb->secmark) * @NFT_META_NFPROTO: netfilter protocol * @NFT_META_L4PROTO: layer 4 protocol number + * @NFT_META_BRI_IIFNAME: packet input bridge interface name + * @NFT_META_BRI_OIFNAME: packet output bridge interface name */ enum nft_meta_keys { NFT_META_LEN, @@ -582,6 +584,8 @@ enum nft_meta_keys { NFT_META_SECMARK, NFT_META_NFPROTO, NFT_META_L4PROTO, + NFT_META_BRI_IIFNAME, + NFT_META_BRI_OIFNAME, }; /** diff --git a/net/bridge/Makefile b/net/bridge/Makefile index e85498b2f166..906a18b4e74a 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -16,4 +16,4 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o -obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ +obj-$(CONFIG_BRIDGE_NETFILTER) += netfilter/ diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index 5ca74a0e595f..3baf29d34e62 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -2,13 +2,25 @@ # Bridge netfilter configuration # # -config NF_TABLES_BRIDGE +menuconfig NF_TABLES_BRIDGE depends on NF_TABLES + select BRIDGE_NETFILTER tristate "Ethernet Bridge nf_tables support" +if NF_TABLES_BRIDGE + +config NFT_BRIDGE_META + tristate "Netfilter nf_table bridge meta support" + depends on NFT_META + help + Add support for bridge dedicated meta key. + +endif # NF_TABLES_BRIDGE + menuconfig BRIDGE_NF_EBTABLES tristate "Ethernet Bridge tables (ebtables) support" depends on BRIDGE && NETFILTER + select BRIDGE_NETFILTER select NETFILTER_XTABLES help ebtables is a general, extensible frame/packet identification diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index ea7629f58b3d..6f2f3943d66f 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o +obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c new file mode 100644 index 000000000000..4f02109d708f --- /dev/null +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014 Intel Corporation + * + * 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 "../br_private.h" + +static void nft_meta_bridge_get_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_meta *priv = nft_expr_priv(expr); + const struct net_device *in = pkt->in, *out = pkt->out; + struct nft_data *dest = &data[priv->dreg]; + const struct net_bridge_port *p; + + switch (priv->key) { + case NFT_META_BRI_IIFNAME: + if (in == NULL || (p = br_port_get_rcu(in)) == NULL) + goto err; + break; + case NFT_META_BRI_OIFNAME: + if (out == NULL || (p = br_port_get_rcu(out)) == NULL) + goto err; + break; + default: + goto out; + } + + strncpy((char *)dest->data, p->br->dev->name, sizeof(dest->data)); + return; +out: + return nft_meta_get_eval(expr, data, pkt); +err: + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_meta *priv = nft_expr_priv(expr); + int err; + + priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); + switch (priv->key) { + case NFT_META_BRI_IIFNAME: + case NFT_META_BRI_OIFNAME: + break; + default: + return nft_meta_get_init(ctx, expr, tb); + } + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + + err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); + if (err < 0) + return err; + + return 0; +} + +static struct nft_expr_type nft_meta_bridge_type; +static const struct nft_expr_ops nft_meta_bridge_get_ops = { + .type = &nft_meta_bridge_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .eval = nft_meta_bridge_get_eval, + .init = nft_meta_bridge_get_init, + .dump = nft_meta_get_dump, +}; + +static const struct nft_expr_ops nft_meta_bridge_set_ops = { + .type = &nft_meta_bridge_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .eval = nft_meta_set_eval, + .init = nft_meta_set_init, + .dump = nft_meta_set_dump, +}; + +static const struct nft_expr_ops * +nft_meta_bridge_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + if (tb[NFTA_META_KEY] == NULL) + return ERR_PTR(-EINVAL); + + if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) + return ERR_PTR(-EINVAL); + + if (tb[NFTA_META_DREG]) + return &nft_meta_bridge_get_ops; + + if (tb[NFTA_META_SREG]) + return &nft_meta_bridge_set_ops; + + return ERR_PTR(-EINVAL); +} + +static struct nft_expr_type nft_meta_bridge_type __read_mostly = { + .family = NFPROTO_BRIDGE, + .name = "meta", + .select_ops = &nft_meta_bridge_select_ops, + .policy = nft_meta_policy, + .maxattr = NFTA_META_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_meta_bridge_module_init(void) +{ + return nft_register_expr(&nft_meta_bridge_type); +} + +static void __exit nft_meta_bridge_module_exit(void) +{ + nft_unregister_expr(&nft_meta_bridge_type); +} + +module_init(nft_meta_bridge_module_init); +module_exit(nft_meta_bridge_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Bursztyka "); +MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta"); -- cgit v1.2.3 From 1111244ff4493448c0ee66e814e20c6e81d3b93d Mon Sep 17 00:00:00 2001 From: Sangjung Woo Date: Mon, 21 Apr 2014 19:10:08 +0900 Subject: extcon: Add resource-managed extcon register function Add resource-managed extcon device register function for convenience. For example, if a extcon device is attached with new devm_extcon_dev_register(), that extcon device is automatically unregistered on driver detach. Signed-off-by: Sangjung Woo [Fix bug about devm_extcon_dev_match/release() and code clean by Chanwoo Choi] Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-class.c | 69 +++++++++++++++++++++++++++++++++++++++++++ include/linux/extcon.h | 13 ++++++++ 2 files changed, 82 insertions(+) (limited to 'include') diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 7ab21aa6eaa1..f6df68989651 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -819,6 +819,75 @@ void extcon_dev_unregister(struct extcon_dev *edev) } EXPORT_SYMBOL_GPL(extcon_dev_unregister); +static void devm_extcon_dev_unreg(struct device *dev, void *res) +{ + extcon_dev_unregister(*(struct extcon_dev **)res); +} + +static int devm_extcon_dev_match(struct device *dev, void *res, void *data) +{ + struct extcon_dev **r = res; + + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + + return *r == data; +} + +/** + * devm_extcon_dev_register() - Resource-managed extcon_dev_register() + * @dev: device to allocate extcon device + * @edev: the new extcon device to register + * + * Managed extcon_dev_register() function. If extcon device is attached with + * this function, that extcon device is automatically unregistered on driver + * detach. Internally this function calls extcon_dev_register() function. + * To get more information, refer that function. + * + * If extcon device is registered with this function and the device needs to be + * unregistered separately, devm_extcon_dev_unregister() should be used. + * + * Returns 0 if success or negaive error number if failure. + */ +int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev) +{ + struct extcon_dev **ptr; + int ret; + + ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = extcon_dev_register(edev); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = edev; + devres_add(dev, ptr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_extcon_dev_register); + +/** + * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister() + * @dev: device the extcon belongs to + * @edev: the extcon device to unregister + * + * Unregister extcon device that is registered with devm_extcon_dev_register() + * function. + */ +void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev) +{ + WARN_ON(devres_release(dev, devm_extcon_dev_unreg, + devm_extcon_dev_match, edev)); +} +EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister); + #ifdef CONFIG_OF /* * extcon_get_edev_by_phandle - Get the extcon device from devicetree diff --git a/include/linux/extcon.h b/include/linux/extcon.h index f488145bb2d4..548447be2d8f 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -185,6 +185,10 @@ struct extcon_specific_cable_nb { */ extern int extcon_dev_register(struct extcon_dev *edev); extern void extcon_dev_unregister(struct extcon_dev *edev); +extern int devm_extcon_dev_register(struct device *dev, + struct extcon_dev *edev); +extern void devm_extcon_dev_unregister(struct device *dev, + struct extcon_dev *edev); extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); /* @@ -254,6 +258,15 @@ static inline int extcon_dev_register(struct extcon_dev *edev) static inline void extcon_dev_unregister(struct extcon_dev *edev) { } +static inline int devm_extcon_dev_register(struct device *dev, + struct extcon_dev *edev) +{ + return -EINVAL; +} + +static inline void devm_extcon_dev_unregister(struct device *dev, + struct extcon_dev *edev) { } + static inline u32 extcon_get_state(struct extcon_dev *edev) { return 0; -- cgit v1.2.3 From 5e40704ed2c69425bcb076fb1890417ef137e6c8 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 17 Apr 2014 13:57:37 +0100 Subject: arm: xen: implement multicall hypercall support. As part of this make the usual change to xen_ulong_t in place of unsigned long. This change has no impact on x86. The Linux definition of struct multicall_entry.result differs from the Xen definition, I think for good reasons, and used a long rather than an unsigned long. Therefore introduce a xen_long_t, which is a long on x86 architectures and a signed 64-bit integer on ARM. Use uint32_t nr_calls on x86 for consistency with the ARM definition. Build tested on amd64 and i386 builds. Runtime tested on ARM. Signed-off-by: Ian Campbell Cc: Stefano Stabellini Cc: Konrad Rzeszutek Wilk Cc: Boris Ostrovsky Signed-off-by: David Vrabel --- arch/arm/include/asm/xen/hypercall.h | 6 +----- arch/arm/include/asm/xen/interface.h | 2 ++ arch/arm/xen/hypercall.S | 1 + arch/arm64/xen/hypercall.S | 1 + arch/x86/include/asm/xen/hypercall.h | 2 +- arch/x86/include/asm/xen/interface.h | 3 +++ include/xen/interface/xen.h | 6 +++--- 7 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/xen/hypercall.h b/arch/arm/include/asm/xen/hypercall.h index 7704e28c3483..7658150488d6 100644 --- a/arch/arm/include/asm/xen/hypercall.h +++ b/arch/arm/include/asm/xen/hypercall.h @@ -48,6 +48,7 @@ int HYPERVISOR_memory_op(unsigned int cmd, void *arg); int HYPERVISOR_physdev_op(int cmd, void *arg); int HYPERVISOR_vcpu_op(int cmd, int vcpuid, void *extra_args); int HYPERVISOR_tmem_op(void *arg); +int HYPERVISOR_multicall(struct multicall_entry *calls, uint32_t nr); static inline void MULTI_update_va_mapping(struct multicall_entry *mcl, unsigned long va, @@ -63,9 +64,4 @@ MULTI_mmu_update(struct multicall_entry *mcl, struct mmu_update *req, BUG(); } -static inline int -HYPERVISOR_multicall(void *call_list, int nr_calls) -{ - BUG(); -} #endif /* _ASM_ARM_XEN_HYPERCALL_H */ diff --git a/arch/arm/include/asm/xen/interface.h b/arch/arm/include/asm/xen/interface.h index 1151188bcd83..50066006e6bd 100644 --- a/arch/arm/include/asm/xen/interface.h +++ b/arch/arm/include/asm/xen/interface.h @@ -40,6 +40,8 @@ typedef uint64_t xen_pfn_t; #define PRI_xen_pfn "llx" typedef uint64_t xen_ulong_t; #define PRI_xen_ulong "llx" +typedef int64_t xen_long_t; +#define PRI_xen_long "llx" /* Guest handles for primitive C types. */ __DEFINE_GUEST_HANDLE(uchar, unsigned char); __DEFINE_GUEST_HANDLE(uint, unsigned int); diff --git a/arch/arm/xen/hypercall.S b/arch/arm/xen/hypercall.S index d1cf7b7c2200..44e3a5f10c4c 100644 --- a/arch/arm/xen/hypercall.S +++ b/arch/arm/xen/hypercall.S @@ -89,6 +89,7 @@ HYPERCALL2(memory_op); HYPERCALL2(physdev_op); HYPERCALL3(vcpu_op); HYPERCALL1(tmem_op); +HYPERCALL2(multicall); ENTRY(privcmd_call) stmdb sp!, {r4} diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S index 531342ec4bcf..8bbe9401f4f0 100644 --- a/arch/arm64/xen/hypercall.S +++ b/arch/arm64/xen/hypercall.S @@ -80,6 +80,7 @@ HYPERCALL2(memory_op); HYPERCALL2(physdev_op); HYPERCALL3(vcpu_op); HYPERCALL1(tmem_op); +HYPERCALL2(multicall); ENTRY(privcmd_call) mov x16, x0 diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h index e709884d0ef9..ca08a27b90b3 100644 --- a/arch/x86/include/asm/xen/hypercall.h +++ b/arch/x86/include/asm/xen/hypercall.h @@ -343,7 +343,7 @@ HYPERVISOR_memory_op(unsigned int cmd, void *arg) } static inline int -HYPERVISOR_multicall(void *call_list, int nr_calls) +HYPERVISOR_multicall(void *call_list, uint32_t nr_calls) { return _hypercall2(int, multicall, call_list, nr_calls); } diff --git a/arch/x86/include/asm/xen/interface.h b/arch/x86/include/asm/xen/interface.h index fd9cb7695b5f..3400dbaec3c3 100644 --- a/arch/x86/include/asm/xen/interface.h +++ b/arch/x86/include/asm/xen/interface.h @@ -54,6 +54,9 @@ typedef unsigned long xen_pfn_t; #define PRI_xen_pfn "lx" typedef unsigned long xen_ulong_t; #define PRI_xen_ulong "lx" +typedef long xen_long_t; +#define PRI_xen_long "lx" + /* Guest handles for primitive C types. */ __DEFINE_GUEST_HANDLE(uchar, unsigned char); __DEFINE_GUEST_HANDLE(uint, unsigned int); diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index 0cd5ca333fac..de082130ba4b 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -275,9 +275,9 @@ DEFINE_GUEST_HANDLE_STRUCT(mmu_update); * NB. The fields are natural register size for this architecture. */ struct multicall_entry { - unsigned long op; - long result; - unsigned long args[6]; + xen_ulong_t op; + xen_long_t result; + xen_ulong_t args[6]; }; DEFINE_GUEST_HANDLE_STRUCT(multicall_entry); -- cgit v1.2.3 From 7e65eac8e36f3f4e2553e83249e3d9bdf055456d Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 22 Apr 2014 12:03:58 -0700 Subject: 6lowpan: nuke net_ieee802154_lowpan() accessor when 6lowpan is disabled Johannes noted this is not needed, all of the fragment accessors don't need CONFIG_NET_NS. This goes test compiled with CONFIG_BT_6LOWPAN=y and a disabled CONFIG_NET_NS. CC: Alexander Smirnov Cc: Dmitry Eremin-Solenikov Cc: linux-zigbee-devel@lists.sourceforge.net Cc: David S. Miller" Cc: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: David S. Miller --- include/net/net_namespace.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include') diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index bc4118ede5b5..361d26077196 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -379,15 +379,8 @@ net_ieee802154_lowpan(struct net *net) { return &net->ieee802154_lowpan; } -#else -static inline struct netns_ieee802154_lowpan * -net_ieee802154_lowpan(struct net *net) -{ - return NULL; -} #endif - /* For callers who don't really care about whether it's IPv4 or IPv6 */ static inline void rt_genid_bump_all(struct net *net) { -- cgit v1.2.3 From f01ec1c017dead42092997a2b8684fcab4cbf126 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 24 Apr 2014 10:02:49 +0200 Subject: vxlan: add x-netns support This patch allows to switch the netns when packet is encapsulated or decapsulated. The vxlan socket is openned into the i/o netns, ie into the netns where encapsulated packets are received. The socket lookup is done into this netns to find the corresponding vxlan tunnel. After decapsulation, the packet is injecting into the corresponding interface which may stand to another netns. When one of the two netns is removed, the tunnel is destroyed. Configuration example: ip netns add netns1 ip netns exec netns1 ip link set lo up ip link add vxlan10 type vxlan id 10 group 239.0.0.10 dev eth0 dstport 0 ip link set vxlan10 netns netns1 ip netns exec netns1 ip addr add 192.168.0.249/24 broadcast 192.168.0.255 dev vxlan10 ip netns exec netns1 ip link set vxlan10 up Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 63 ++++++++++++++++++++++++++++++++----------- include/net/vxlan.h | 2 +- net/openvswitch/vport-vxlan.c | 3 ++- 3 files changed, 50 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 4dbb2ed85b97..1dfee9a7fbf7 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -127,6 +127,7 @@ struct vxlan_dev { struct list_head next; /* vxlan's per namespace list */ struct vxlan_sock *vn_sock; /* listening socket */ struct net_device *dev; + struct net *net; /* netns for packet i/o */ struct vxlan_rdst default_dst; /* default destination */ union vxlan_addr saddr; /* source address */ __be16 dst_port; @@ -1203,6 +1204,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, remote_ip = &vxlan->default_dst.remote_ip; skb_reset_mac_header(skb); + skb_scrub_packet(skb, !net_eq(vxlan->net, dev_net(vxlan->dev))); skb->protocol = eth_type_trans(skb, vxlan->dev); /* Ignore packet loops (and multicast echo) */ @@ -1618,7 +1620,8 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, struct dst_entry *dst, struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, - __be16 src_port, __be16 dst_port, __be32 vni) + __be16 src_port, __be16 dst_port, __be32 vni, + bool xnet) { struct ipv6hdr *ip6h; struct vxlanhdr *vxh; @@ -1631,7 +1634,7 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, skb->encapsulation = 1; } - skb_scrub_packet(skb, false); + skb_scrub_packet(skb, xnet); min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len + VXLAN_HLEN + sizeof(struct ipv6hdr) @@ -1711,7 +1714,7 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, int vxlan_xmit_skb(struct vxlan_sock *vs, struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, - __be16 src_port, __be16 dst_port, __be32 vni) + __be16 src_port, __be16 dst_port, __be32 vni, bool xnet) { struct vxlanhdr *vxh; struct udphdr *uh; @@ -1760,7 +1763,7 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, return err; return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP, - tos, ttl, df, false); + tos, ttl, df, xnet); } EXPORT_SYMBOL_GPL(vxlan_xmit_skb); @@ -1853,7 +1856,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, fl4.daddr = dst->sin.sin_addr.s_addr; fl4.saddr = vxlan->saddr.sin.sin_addr.s_addr; - rt = ip_route_output_key(dev_net(dev), &fl4); + rt = ip_route_output_key(vxlan->net, &fl4); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to %pI4\n", &dst->sin.sin_addr.s_addr); @@ -1874,7 +1877,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_dev *dst_vxlan; ip_rt_put(rt); - dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port); + dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port); if (!dst_vxlan) goto tx_error; vxlan_encap_bypass(skb, vxlan, dst_vxlan); @@ -1887,7 +1890,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb, fl4.saddr, dst->sin.sin_addr.s_addr, tos, ttl, df, src_port, dst_port, - htonl(vni << 8)); + htonl(vni << 8), + !net_eq(vxlan->net, dev_net(vxlan->dev))); if (err < 0) goto rt_tx_error; @@ -1927,7 +1931,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_dev *dst_vxlan; dst_release(ndst); - dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port); + dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port); if (!dst_vxlan) goto tx_error; vxlan_encap_bypass(skb, vxlan, dst_vxlan); @@ -1938,7 +1942,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, err = vxlan6_xmit_skb(vxlan->vn_sock, ndst, skb, dev, &fl6.saddr, &fl6.daddr, 0, ttl, - src_port, dst_port, htonl(vni << 8)); + src_port, dst_port, htonl(vni << 8), + !net_eq(vxlan->net, dev_net(vxlan->dev))); #endif } @@ -2082,7 +2087,7 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan) static int vxlan_init(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); struct vxlan_sock *vs; dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); @@ -2090,7 +2095,7 @@ static int vxlan_init(struct net_device *dev) return -ENOMEM; spin_lock(&vn->sock_lock); - vs = vxlan_find_sock(dev_net(dev), vxlan->dst_port); + vs = vxlan_find_sock(vxlan->net, vxlan->dst_port); if (vs) { /* If we have a socket with same port already, reuse it */ atomic_inc(&vs->refcnt); @@ -2172,8 +2177,8 @@ static void vxlan_flush(struct vxlan_dev *vxlan) /* Cleanup timer and forwarding table on shutdown */ static int vxlan_stop(struct net_device *dev) { - struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); struct vxlan_sock *vs = vxlan->vn_sock; if (vs && vxlan_addr_multicast(&vxlan->default_dst.remote_ip) && @@ -2202,7 +2207,7 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu) struct net_device *lowerdev; int max_mtu; - lowerdev = __dev_get_by_index(dev_net(dev), dst->remote_ifindex); + lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex); if (lowerdev == NULL) return eth_change_mtu(dev, new_mtu); @@ -2285,7 +2290,6 @@ static void vxlan_setup(struct net_device *dev) dev->tx_queue_len = 0; dev->features |= NETIF_F_LLTX; - dev->features |= NETIF_F_NETNS_LOCAL; dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; @@ -2578,7 +2582,7 @@ EXPORT_SYMBOL_GPL(vxlan_sock_add); static void vxlan_sock_work(struct work_struct *work) { struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, sock_work); - struct net *net = dev_net(vxlan->dev); + struct net *net = vxlan->net; struct vxlan_net *vn = net_generic(net, vxlan_net_id); __be16 port = vxlan->dst_port; struct vxlan_sock *nvs; @@ -2605,6 +2609,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, if (!data[IFLA_VXLAN_ID]) return -EINVAL; + vxlan->net = dev_net(dev); + vni = nla_get_u32(data[IFLA_VXLAN_ID]); dst->remote_vni = vni; @@ -2739,8 +2745,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, static void vxlan_dellink(struct net_device *dev, struct list_head *head) { - struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); spin_lock(&vn->sock_lock); if (!hlist_unhashed(&vxlan->hlist)) @@ -2905,8 +2911,33 @@ static __net_init int vxlan_init_net(struct net *net) return 0; } +static void __net_exit vxlan_exit_net(struct net *net) +{ + struct vxlan_net *vn = net_generic(net, vxlan_net_id); + struct vxlan_dev *vxlan, *next; + struct net_device *dev, *aux; + LIST_HEAD(list); + + rtnl_lock(); + for_each_netdev_safe(net, dev, aux) + if (dev->rtnl_link_ops == &vxlan_link_ops) + unregister_netdevice_queue(dev, &list); + + list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) { + /* If vxlan->dev is in the same netns, it has already been added + * to the list by the previous loop. + */ + if (!net_eq(dev_net(vxlan->dev), net)) + unregister_netdevice_queue(dev, &list); + } + + unregister_netdevice_many(&list); + rtnl_unlock(); +} + static struct pernet_operations vxlan_net_ops = { .init = vxlan_init_net, + .exit = vxlan_exit_net, .id = &vxlan_net_id, .size = sizeof(struct vxlan_net), }; diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 5deef1ae78c9..7bb4084b1bd0 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -33,7 +33,7 @@ void vxlan_sock_release(struct vxlan_sock *vs); int vxlan_xmit_skb(struct vxlan_sock *vs, struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, - __be16 src_port, __be16 dst_port, __be32 vni); + __be16 src_port, __be16 dst_port, __be32 vni, bool xnet); __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb); diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index e797a50ac2be..21cceb3bdf78 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -180,7 +180,8 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) OVS_CB(skb)->tun_key->ipv4_tos, OVS_CB(skb)->tun_key->ipv4_ttl, df, src_port, dst_port, - htonl(be64_to_cpu(OVS_CB(skb)->tun_key->tun_id) << 8)); + htonl(be64_to_cpu(OVS_CB(skb)->tun_key->tun_id) << 8), + false); if (err < 0) ip_rt_put(rt); error: -- cgit v1.2.3 From 7ee4910ab31c4b1fafb7e4f273cbe9340ac953aa Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 31 Mar 2014 15:19:29 +0200 Subject: PCI: Remove old serial device IDs These IDs are no longer referenced since kernel 3.1 so I suppose we can remove them from pci_ids.h. Signed-off-by: Jean Delvare Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman --- include/linux/pci_ids.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index d4de24b4d4c6..7fa31731c854 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1631,8 +1631,6 @@ #define PCI_DEVICE_ID_ATT_VENUS_MODEM 0x480 #define PCI_VENDOR_ID_SPECIALIX 0x11cb -#define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000 -#define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 #define PCI_SUBDEVICE_ID_SPECIALIX_SPEED4 0xa004 #define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 @@ -2874,7 +2872,6 @@ #define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 #define PCI_VENDOR_ID_COMPUTONE 0x8e0e -#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 #define PCI_DEVICE_ID_COMPUTONE_PG 0x0302 #define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e #define PCI_SUBDEVICE_ID_COMPUTONE_PG4 0x0001 -- cgit v1.2.3 From 879eb9c3f9b854394c5a2014b9243c00eaa329f0 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 23 Apr 2014 09:58:25 -0500 Subject: tty_ldisc: add more limits to the @write_wakeup In the uart_handle_cts_change(), uart_write_wakeup() is called after we call @uart_port->ops->start_tx(). The Documentation/serial/driver tells us: ----------------------------------------------- start_tx(port) Start transmitting characters. Locking: port->lock taken. Interrupts: locally disabled. ----------------------------------------------- So when the uart_write_wakeup() is called, the port->lock is taken by the upper. See the following callstack: |_ uart_write_wakeup |_ tty_wakeup |_ ld->ops->write_wakeup With the port->lock held, we call the @write_wakeup. Some implemetation of the @write_wakeup does not notice that the port->lock is held, and it still tries to send data with uart_write() which will try to grab the prot->lock. A dead lock occurs, see the following log caught in the Bluetooth by uart: -------------------------------------------------------------------- BUG: spinlock lockup suspected on CPU#0, swapper/0/0 lock: 0xdc3f4410, .magic: dead4ead, .owner: swapper/0/0, .owner_cpu: 0 CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 3.10.17-16839-ge4a1bef #1320 [<80014cbc>] (unwind_backtrace+0x0/0x138) from [<8001251c>] (show_stack+0x10/0x14) [<8001251c>] (show_stack+0x10/0x14) from [<802816ac>] (do_raw_spin_lock+0x108/0x184) [<802816ac>] (do_raw_spin_lock+0x108/0x184) from [<806a22b0>] (_raw_spin_lock_irqsave+0x54/0x60) [<806a22b0>] (_raw_spin_lock_irqsave+0x54/0x60) from [<802f5754>] (uart_write+0x38/0xe0) [<802f5754>] (uart_write+0x38/0xe0) from [<80455270>] (hci_uart_tx_wakeup+0xa4/0x168) [<80455270>] (hci_uart_tx_wakeup+0xa4/0x168) from [<802dab18>] (tty_wakeup+0x50/0x5c) [<802dab18>] (tty_wakeup+0x50/0x5c) from [<802f81a4>] (imx_rtsint+0x50/0x80) [<802f81a4>] (imx_rtsint+0x50/0x80) from [<802f88f4>] (imx_int+0x158/0x17c) [<802f88f4>] (imx_int+0x158/0x17c) from [<8007abe0>] (handle_irq_event_percpu+0x50/0x194) [<8007abe0>] (handle_irq_event_percpu+0x50/0x194) from [<8007ad60>] (handle_irq_event+0x3c/0x5c) -------------------------------------------------------------------- This patch adds more limits to the @write_wakeup, the one who wants to implemet the @write_wakeup should follow the limits which avoid the deadlock. Signed-off-by: Huang Shijie Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- include/linux/tty_ldisc.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index add26da2faeb..00c9d688d7b7 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -92,7 +92,10 @@ * This function is called by the low-level tty driver to signal * that line discpline should try to send more characters to the * low-level driver for transmission. If the line discpline does - * not have any more data to send, it can just return. + * not have any more data to send, it can just return. If the line + * discipline does have some data to send, please arise a tasklet + * or workqueue to do the real data transfer. Do not send data in + * this hook, it may leads to a deadlock. * * int (*hangup)(struct tty_struct *) * -- cgit v1.2.3 From bd5dc09f557547399cd44d0a1224df7ff64e4a6b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 23 Apr 2014 09:58:28 -0500 Subject: serial: fix UART_IIR_ID UART IRQ Identification bitfield is 3 bits long (bits 3:1) but current mask only masks 2 bits. Fix it. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/serial_reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index e6322605b138..99b47058816a 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -32,7 +32,7 @@ #define UART_IIR 2 /* In: Interrupt ID Register */ #define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ +#define UART_IIR_ID 0x0e /* Mask for the interrupt ID */ #define UART_IIR_MSI 0x00 /* Modem status interrupt */ #define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ #define UART_IIR_RDI 0x04 /* Receiver data interrupt */ -- cgit v1.2.3 From d9bb3fb12685209765fd838bec69d701d7b479e5 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Fri, 4 Apr 2014 17:23:43 -0700 Subject: tty: xuartps: Rebrand driver as Cadence UART Zynq's UART is Cadence IP. Make this visible in the prompt in kconfig and additional comments in the driver. This also renames functions and symbols, as far as possible without breaking user space API, to reflect the Cadence origin. This is achieved through simple search and replace: - s/XUARTPS/CDNS_UART/g - s/xuartps/cdns_uart/g The only exceptions are PORT_XUARTPS and the driver name, which stay as is, due to their exposure to user space. As well as the - no legacy - compatibility string 'xlnx,xuartps' Signed-off-by: Soren Brinkmann Tested-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 9 +- drivers/tty/serial/xilinx_uartps.c | 908 +++++++++++++++++++------------------ include/uapi/linux/serial_core.h | 2 +- 3 files changed, 471 insertions(+), 448 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5d9b01aa54f4..396cf8499947 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1369,18 +1369,19 @@ config SERIAL_MXS_AUART_CONSOLE Enable a MXS AUART port to be the system console. config SERIAL_XILINX_PS_UART - tristate "Xilinx PS UART support" + tristate "Cadence (Xilinx Zynq) UART support" depends on OF select SERIAL_CORE help - This driver supports the Xilinx PS UART port. + This driver supports the Cadence UART. It is found e.g. in Xilinx + Zynq. config SERIAL_XILINX_PS_UART_CONSOLE - bool "Xilinx PS UART console support" + bool "Cadence UART console support" depends on SERIAL_XILINX_PS_UART=y select SERIAL_CORE_CONSOLE help - Enable a Xilinx PS UART port to be the system console. + Enable a Cadence UART port to be the system console. config SERIAL_AR933X tristate "AR933X serial port support" diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index f9a2c2fc03c4..8809775e2ba3 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1,5 +1,5 @@ /* - * Xilinx PS UART driver + * Cadence UART driver (found in Xilinx Zynq) * * 2011 - 2014 (C) Xilinx Inc. * @@ -8,6 +8,10 @@ * License as published by the Free Software Foundation; * either version 2 of the License, or (at your option) any * later version. + * + * This driver has originally been pushed by Xilinx using a Zynq-branding. This + * still shows in the naming of this file, the kconfig symbols and some symbols + * in the code. */ #if defined(CONFIG_SERIAL_XILINX_PS_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -27,16 +31,16 @@ #include #include -#define XUARTPS_TTY_NAME "ttyPS" -#define XUARTPS_NAME "xuartps" -#define XUARTPS_MAJOR 0 /* use dynamic node allocation */ -#define XUARTPS_MINOR 0 /* works best with devtmpfs */ -#define XUARTPS_NR_PORTS 2 -#define XUARTPS_FIFO_SIZE 64 /* FIFO size */ -#define XUARTPS_REGISTER_SPACE 0xFFF +#define CDNS_UART_TTY_NAME "ttyPS" +#define CDNS_UART_NAME "xuartps" +#define CDNS_UART_MAJOR 0 /* use dynamic node allocation */ +#define CDNS_UART_MINOR 0 /* works best with devtmpfs */ +#define CDNS_UART_NR_PORTS 2 +#define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ +#define CDNS_UART_REGISTER_SPACE 0xFFF -#define xuartps_readl(offset) ioread32(port->membase + offset) -#define xuartps_writel(val, offset) iowrite32(val, port->membase + offset) +#define cdns_uart_readl(offset) ioread32(port->membase + offset) +#define cdns_uart_writel(val, offset) iowrite32(val, port->membase + offset) /* Rx Trigger level */ static int rx_trigger_level = 56; @@ -49,35 +53,35 @@ module_param(rx_timeout, uint, S_IRUGO); MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); /* Register offsets for the UART. */ -#define XUARTPS_CR_OFFSET 0x00 /* Control Register */ -#define XUARTPS_MR_OFFSET 0x04 /* Mode Register */ -#define XUARTPS_IER_OFFSET 0x08 /* Interrupt Enable */ -#define XUARTPS_IDR_OFFSET 0x0C /* Interrupt Disable */ -#define XUARTPS_IMR_OFFSET 0x10 /* Interrupt Mask */ -#define XUARTPS_ISR_OFFSET 0x14 /* Interrupt Status */ -#define XUARTPS_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator */ -#define XUARTPS_RXTOUT_OFFSET 0x1C /* RX Timeout */ -#define XUARTPS_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level */ -#define XUARTPS_MODEMCR_OFFSET 0x24 /* Modem Control */ -#define XUARTPS_MODEMSR_OFFSET 0x28 /* Modem Status */ -#define XUARTPS_SR_OFFSET 0x2C /* Channel Status */ -#define XUARTPS_FIFO_OFFSET 0x30 /* FIFO */ -#define XUARTPS_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider */ -#define XUARTPS_FLOWDEL_OFFSET 0x38 /* Flow Delay */ -#define XUARTPS_IRRX_PWIDTH_OFFSET 0x3C /* IR Minimum Received Pulse Width */ -#define XUARTPS_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse Width */ -#define XUARTPS_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level */ +#define CDNS_UART_CR_OFFSET 0x00 /* Control Register */ +#define CDNS_UART_MR_OFFSET 0x04 /* Mode Register */ +#define CDNS_UART_IER_OFFSET 0x08 /* Interrupt Enable */ +#define CDNS_UART_IDR_OFFSET 0x0C /* Interrupt Disable */ +#define CDNS_UART_IMR_OFFSET 0x10 /* Interrupt Mask */ +#define CDNS_UART_ISR_OFFSET 0x14 /* Interrupt Status */ +#define CDNS_UART_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator */ +#define CDNS_UART_RXTOUT_OFFSET 0x1C /* RX Timeout */ +#define CDNS_UART_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level */ +#define CDNS_UART_MODEMCR_OFFSET 0x24 /* Modem Control */ +#define CDNS_UART_MODEMSR_OFFSET 0x28 /* Modem Status */ +#define CDNS_UART_SR_OFFSET 0x2C /* Channel Status */ +#define CDNS_UART_FIFO_OFFSET 0x30 /* FIFO */ +#define CDNS_UART_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider */ +#define CDNS_UART_FLOWDEL_OFFSET 0x38 /* Flow Delay */ +#define CDNS_UART_IRRX_PWIDTH_OFFSET 0x3C /* IR Min Received Pulse Width */ +#define CDNS_UART_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse Width */ +#define CDNS_UART_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level */ /* Control Register Bit Definitions */ -#define XUARTPS_CR_STOPBRK 0x00000100 /* Stop TX break */ -#define XUARTPS_CR_STARTBRK 0x00000080 /* Set TX break */ -#define XUARTPS_CR_TX_DIS 0x00000020 /* TX disabled. */ -#define XUARTPS_CR_TX_EN 0x00000010 /* TX enabled */ -#define XUARTPS_CR_RX_DIS 0x00000008 /* RX disabled. */ -#define XUARTPS_CR_RX_EN 0x00000004 /* RX enabled */ -#define XUARTPS_CR_TXRST 0x00000002 /* TX logic reset */ -#define XUARTPS_CR_RXRST 0x00000001 /* RX logic reset */ -#define XUARTPS_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ +#define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */ +#define CDNS_UART_CR_STARTBRK 0x00000080 /* Set TX break */ +#define CDNS_UART_CR_TX_DIS 0x00000020 /* TX disabled. */ +#define CDNS_UART_CR_TX_EN 0x00000010 /* TX enabled */ +#define CDNS_UART_CR_RX_DIS 0x00000008 /* RX disabled. */ +#define CDNS_UART_CR_RX_EN 0x00000004 /* RX enabled */ +#define CDNS_UART_CR_TXRST 0x00000002 /* TX logic reset */ +#define CDNS_UART_CR_RXRST 0x00000001 /* RX logic reset */ +#define CDNS_UART_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ /* * Mode Register: @@ -85,22 +89,22 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * format. If this register is modified during transmission or reception, * data validity cannot be guaranteed. */ -#define XUARTPS_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ -#define XUARTPS_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ -#define XUARTPS_MR_CHMODE_NORM 0x00000000 /* Normal mode */ +#define CDNS_UART_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ +#define CDNS_UART_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ +#define CDNS_UART_MR_CHMODE_NORM 0x00000000 /* Normal mode */ -#define XUARTPS_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ -#define XUARTPS_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ +#define CDNS_UART_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ +#define CDNS_UART_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ -#define XUARTPS_MR_PARITY_NONE 0x00000020 /* No parity mode */ -#define XUARTPS_MR_PARITY_MARK 0x00000018 /* Mark parity mode */ -#define XUARTPS_MR_PARITY_SPACE 0x00000010 /* Space parity mode */ -#define XUARTPS_MR_PARITY_ODD 0x00000008 /* Odd parity mode */ -#define XUARTPS_MR_PARITY_EVEN 0x00000000 /* Even parity mode */ +#define CDNS_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */ +#define CDNS_UART_MR_PARITY_MARK 0x00000018 /* Mark parity mode */ +#define CDNS_UART_MR_PARITY_SPACE 0x00000010 /* Space parity mode */ +#define CDNS_UART_MR_PARITY_ODD 0x00000008 /* Odd parity mode */ +#define CDNS_UART_MR_PARITY_EVEN 0x00000000 /* Even parity mode */ -#define XUARTPS_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */ -#define XUARTPS_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */ -#define XUARTPS_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */ +#define CDNS_UART_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */ +#define CDNS_UART_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */ +#define CDNS_UART_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */ /* * Interrupt Registers: @@ -113,20 +117,20 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * Reading either IER or IDR returns 0x00. * All four registers have the same bit definitions. */ -#define XUARTPS_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */ -#define XUARTPS_IXR_PARITY 0x00000080 /* Parity error interrupt */ -#define XUARTPS_IXR_FRAMING 0x00000040 /* Framing error interrupt */ -#define XUARTPS_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */ -#define XUARTPS_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */ -#define XUARTPS_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */ -#define XUARTPS_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */ -#define XUARTPS_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ -#define XUARTPS_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ -#define XUARTPS_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ -#define XUARTPS_IXR_MASK 0x00001FFF /* Valid bit mask */ +#define CDNS_UART_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */ +#define CDNS_UART_IXR_PARITY 0x00000080 /* Parity error interrupt */ +#define CDNS_UART_IXR_FRAMING 0x00000040 /* Framing error interrupt */ +#define CDNS_UART_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */ +#define CDNS_UART_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */ +#define CDNS_UART_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */ +#define CDNS_UART_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */ +#define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ +#define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ +#define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ +#define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */ /* Goes in read_status_mask for break detection as the HW doesn't do it*/ -#define XUARTPS_IXR_BRK 0x80000000 +#define CDNS_UART_IXR_BRK 0x80000000 /* * Channel Status Register: @@ -134,41 +138,42 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * to monitor the status of bits in the channel interrupt status register, * even if these are masked out by the interrupt mask register. */ -#define XUARTPS_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ -#define XUARTPS_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ -#define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */ -#define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */ +#define CDNS_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ +#define CDNS_UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ +#define CDNS_UART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define CDNS_UART_SR_RXTRIG 0x00000001 /* Rx Trigger */ /* baud dividers min/max values */ -#define XUARTPS_BDIV_MIN 4 -#define XUARTPS_BDIV_MAX 255 -#define XUARTPS_CD_MAX 65535 +#define CDNS_UART_BDIV_MIN 4 +#define CDNS_UART_BDIV_MAX 255 +#define CDNS_UART_CD_MAX 65535 /** - * struct xuartps - device data + * struct cdns_uart - device data * @port: Pointer to the UART port - * @refclk: Reference clock - * @aperclk: APB clock + * @uartclk: Reference clock + * @pclk: APB clock * @baud: Current baud rate * @clk_rate_change_nb: Notifier block for clock changes */ -struct xuartps { +struct cdns_uart { struct uart_port *port; - struct clk *refclk; - struct clk *aperclk; + struct clk *uartclk; + struct clk *pclk; unsigned int baud; struct notifier_block clk_rate_change_nb; }; -#define to_xuartps(_nb) container_of(_nb, struct xuartps, clk_rate_change_nb); +#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ + clk_rate_change_nb); /** - * xuartps_isr - Interrupt handler + * cdns_uart_isr - Interrupt handler * @irq: Irq number * @dev_id: Id of the port * * Return: IRQHANDLED */ -static irqreturn_t xuartps_isr(int irq, void *dev_id) +static irqreturn_t cdns_uart_isr(int irq, void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; unsigned long flags; @@ -181,42 +186,42 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) /* Read the interrupt status register to determine which * interrupt(s) is/are active. */ - isrstatus = xuartps_readl(XUARTPS_ISR_OFFSET); + isrstatus = cdns_uart_readl(CDNS_UART_ISR_OFFSET); /* * There is no hardware break detection, so we interpret framing * error with all-zeros data as a break sequence. Most of the time, * there's another non-zero byte at the end of the sequence. */ - if (isrstatus & XUARTPS_IXR_FRAMING) { - while (!(xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_RXEMPTY)) { - if (!xuartps_readl(XUARTPS_FIFO_OFFSET)) { - port->read_status_mask |= XUARTPS_IXR_BRK; - isrstatus &= ~XUARTPS_IXR_FRAMING; + if (isrstatus & CDNS_UART_IXR_FRAMING) { + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY)) { + if (!cdns_uart_readl(CDNS_UART_FIFO_OFFSET)) { + port->read_status_mask |= CDNS_UART_IXR_BRK; + isrstatus &= ~CDNS_UART_IXR_FRAMING; } } - xuartps_writel(XUARTPS_IXR_FRAMING, XUARTPS_ISR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_FRAMING, CDNS_UART_ISR_OFFSET); } /* drop byte with parity error if IGNPAR specified */ - if (isrstatus & port->ignore_status_mask & XUARTPS_IXR_PARITY) - isrstatus &= ~(XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT); + if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY) + isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT); isrstatus &= port->read_status_mask; isrstatus &= ~port->ignore_status_mask; - if ((isrstatus & XUARTPS_IXR_TOUT) || - (isrstatus & XUARTPS_IXR_RXTRIG)) { + if ((isrstatus & CDNS_UART_IXR_TOUT) || + (isrstatus & CDNS_UART_IXR_RXTRIG)) { /* Receive Timeout Interrupt */ - while ((xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) { - data = xuartps_readl(XUARTPS_FIFO_OFFSET); + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { + data = cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* Non-NULL byte after BREAK is garbage (99%) */ if (data && (port->read_status_mask & - XUARTPS_IXR_BRK)) { - port->read_status_mask &= ~XUARTPS_IXR_BRK; + CDNS_UART_IXR_BRK)) { + port->read_status_mask &= ~CDNS_UART_IXR_BRK; port->icount.brk++; if (uart_handle_break(port)) continue; @@ -240,17 +245,17 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) port->icount.rx++; - if (isrstatus & XUARTPS_IXR_PARITY) { + if (isrstatus & CDNS_UART_IXR_PARITY) { port->icount.parity++; status = TTY_PARITY; - } else if (isrstatus & XUARTPS_IXR_FRAMING) { + } else if (isrstatus & CDNS_UART_IXR_FRAMING) { port->icount.frame++; status = TTY_FRAME; - } else if (isrstatus & XUARTPS_IXR_OVERRUN) { + } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { port->icount.overrun++; } - uart_insert_char(port, isrstatus, XUARTPS_IXR_OVERRUN, + uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, data, status); } spin_unlock(&port->lock); @@ -259,10 +264,10 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) } /* Dispatch an appropriate handler */ - if ((isrstatus & XUARTPS_IXR_TXEMPTY) == XUARTPS_IXR_TXEMPTY) { + if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) { if (uart_circ_empty(&port->state->xmit)) { - xuartps_writel(XUARTPS_IXR_TXEMPTY, - XUARTPS_IDR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, + CDNS_UART_IDR_OFFSET); } else { numbytes = port->fifosize; /* Break if no more data available in the UART buffer */ @@ -270,12 +275,12 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) if (uart_circ_empty(&port->state->xmit)) break; /* Get the data from the UART circular buffer - * and write it to the xuartps's TX_FIFO + * and write it to the cdns_uart's TX_FIFO * register. */ - xuartps_writel( + cdns_uart_writel( port->state->xmit.buf[port->state->xmit. - tail], XUARTPS_FIFO_OFFSET); + tail], CDNS_UART_FIFO_OFFSET); port->icount.tx++; @@ -293,7 +298,7 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) } } - xuartps_writel(isrstatus, XUARTPS_ISR_OFFSET); + cdns_uart_writel(isrstatus, CDNS_UART_ISR_OFFSET); /* be sure to release the lock and tty before leaving */ spin_unlock_irqrestore(&port->lock, flags); @@ -302,7 +307,7 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) } /** - * xuartps_calc_baud_divs - Calculate baud rate divisors + * cdns_uart_calc_baud_divs - Calculate baud rate divisors * @clk: UART module input clock * @baud: Desired baud rate * @rbdiv: BDIV value (return value) @@ -321,8 +326,8 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) * baud rate generate register * baud rate clock divisor register */ -static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud, - u32 *rbdiv, u32 *rcd, int *div8) +static unsigned int cdns_uart_calc_baud_divs(unsigned int clk, + unsigned int baud, u32 *rbdiv, u32 *rcd, int *div8) { u32 cd, bdiv; unsigned int calc_baud; @@ -330,16 +335,16 @@ static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud, unsigned int bauderror; unsigned int besterror = ~0; - if (baud < clk / ((XUARTPS_BDIV_MAX + 1) * XUARTPS_CD_MAX)) { + if (baud < clk / ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX)) { *div8 = 1; clk /= 8; } else { *div8 = 0; } - for (bdiv = XUARTPS_BDIV_MIN; bdiv <= XUARTPS_BDIV_MAX; bdiv++) { + for (bdiv = CDNS_UART_BDIV_MIN; bdiv <= CDNS_UART_BDIV_MAX; bdiv++) { cd = DIV_ROUND_CLOSEST(clk, baud * (bdiv + 1)); - if (cd < 1 || cd > XUARTPS_CD_MAX) + if (cd < 1 || cd > CDNS_UART_CD_MAX) continue; calc_baud = clk / (cd * (bdiv + 1)); @@ -364,47 +369,47 @@ static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud, } /** - * xuartps_set_baud_rate - Calculate and set the baud rate + * cdns_uart_set_baud_rate - Calculate and set the baud rate * @port: Handle to the uart port structure * @baud: Baud rate to set * Return: baud rate, requested baud when possible, or actual baud when there * was too much error, zero if no valid divisors are found. */ -static unsigned int xuartps_set_baud_rate(struct uart_port *port, +static unsigned int cdns_uart_set_baud_rate(struct uart_port *port, unsigned int baud) { unsigned int calc_baud; u32 cd = 0, bdiv = 0; u32 mreg; int div8; - struct xuartps *xuartps = port->private_data; + struct cdns_uart *cdns_uart = port->private_data; - calc_baud = xuartps_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, + calc_baud = cdns_uart_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, &div8); /* Write new divisors to hardware */ - mreg = xuartps_readl(XUARTPS_MR_OFFSET); + mreg = cdns_uart_readl(CDNS_UART_MR_OFFSET); if (div8) - mreg |= XUARTPS_MR_CLKSEL; + mreg |= CDNS_UART_MR_CLKSEL; else - mreg &= ~XUARTPS_MR_CLKSEL; - xuartps_writel(mreg, XUARTPS_MR_OFFSET); - xuartps_writel(cd, XUARTPS_BAUDGEN_OFFSET); - xuartps_writel(bdiv, XUARTPS_BAUDDIV_OFFSET); - xuartps->baud = baud; + mreg &= ~CDNS_UART_MR_CLKSEL; + cdns_uart_writel(mreg, CDNS_UART_MR_OFFSET); + cdns_uart_writel(cd, CDNS_UART_BAUDGEN_OFFSET); + cdns_uart_writel(bdiv, CDNS_UART_BAUDDIV_OFFSET); + cdns_uart->baud = baud; return calc_baud; } #ifdef CONFIG_COMMON_CLK /** - * xuartps_clk_notitifer_cb - Clock notifier callback + * cdns_uart_clk_notitifer_cb - Clock notifier callback * @nb: Notifier block * @event: Notify event * @data: Notifier data * Return: NOTIFY_OK or NOTIFY_DONE on success, NOTIFY_BAD on error. */ -static int xuartps_clk_notifier_cb(struct notifier_block *nb, +static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, unsigned long event, void *data) { u32 ctrl_reg; @@ -412,9 +417,9 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, int locked = 0; struct clk_notifier_data *ndata = data; unsigned long flags = 0; - struct xuartps *xuartps = to_xuartps(nb); + struct cdns_uart *cdns_uart = to_cdns_uart(nb); - port = xuartps->port; + port = cdns_uart->port; if (port->suspended) return NOTIFY_OK; @@ -428,20 +433,20 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, * Find out if current baud-rate can be achieved with new clock * frequency. */ - if (!xuartps_calc_baud_divs(ndata->new_rate, xuartps->baud, + if (!cdns_uart_calc_baud_divs(ndata->new_rate, cdns_uart->baud, &bdiv, &cd, &div8)) { dev_warn(port->dev, "clock rate change rejected\n"); return NOTIFY_BAD; } - spin_lock_irqsave(&xuartps->port->lock, flags); + spin_lock_irqsave(&cdns_uart->port->lock, flags); /* Disable the TX and RX to set baud rate */ - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - ctrl_reg |= XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS; - xuartps_writel(ctrl_reg, XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - spin_unlock_irqrestore(&xuartps->port->lock, flags); + spin_unlock_irqrestore(&cdns_uart->port->lock, flags); return NOTIFY_OK; } @@ -451,25 +456,25 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, * frequency. */ - spin_lock_irqsave(&xuartps->port->lock, flags); + spin_lock_irqsave(&cdns_uart->port->lock, flags); locked = 1; port->uartclk = ndata->new_rate; - xuartps->baud = xuartps_set_baud_rate(xuartps->port, - xuartps->baud); + cdns_uart->baud = cdns_uart_set_baud_rate(cdns_uart->port, + cdns_uart->baud); /* fall through */ case ABORT_RATE_CHANGE: if (!locked) - spin_lock_irqsave(&xuartps->port->lock, flags); + spin_lock_irqsave(&cdns_uart->port->lock, flags); /* Set TX/RX Reset */ - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - ctrl_reg |= XUARTPS_CR_TXRST | XUARTPS_CR_RXRST; - xuartps_writel(ctrl_reg, XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - while (xuartps_readl(XUARTPS_CR_OFFSET) & - (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST)) + while (cdns_uart_readl(CDNS_UART_CR_OFFSET) & + (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); /* @@ -477,13 +482,13 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, * enable bit and RX enable bit to enable the transmitter and * receiver. */ - xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - ctrl_reg &= ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS); - ctrl_reg |= XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN; - xuartps_writel(ctrl_reg, XUARTPS_CR_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - spin_unlock_irqrestore(&xuartps->port->lock, flags); + spin_unlock_irqrestore(&cdns_uart->port->lock, flags); return NOTIFY_OK; default: @@ -493,35 +498,35 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb, #endif /** - * xuartps_start_tx - Start transmitting bytes + * cdns_uart_start_tx - Start transmitting bytes * @port: Handle to the uart port structure */ -static void xuartps_start_tx(struct uart_port *port) +static void cdns_uart_start_tx(struct uart_port *port) { unsigned int status, numbytes = port->fifosize; if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port)) return; - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); /* Set the TX enable bit and clear the TX disable bit to enable the * transmitter. */ - xuartps_writel((status & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN, - XUARTPS_CR_OFFSET); + cdns_uart_writel((status & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, + CDNS_UART_CR_OFFSET); - while (numbytes-- && ((xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_TXFULL)) != XUARTPS_SR_TXFULL) { + while (numbytes-- && ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_TXFULL)) != CDNS_UART_SR_TXFULL) { /* Break if no more data available in the UART buffer */ if (uart_circ_empty(&port->state->xmit)) break; /* Get the data from the UART circular buffer and - * write it to the xuartps's TX_FIFO register. + * write it to the cdns_uart's TX_FIFO register. */ - xuartps_writel( + cdns_uart_writel( port->state->xmit.buf[port->state->xmit.tail], - XUARTPS_FIFO_OFFSET); + CDNS_UART_FIFO_OFFSET); port->icount.tx++; /* Adjust the tail of the UART buffer and wrap @@ -530,90 +535,90 @@ static void xuartps_start_tx(struct uart_port *port) port->state->xmit.tail = (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); } - xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_ISR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_ISR_OFFSET); /* Enable the TX Empty interrupt */ - xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_IER_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_IER_OFFSET); if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) uart_write_wakeup(port); } /** - * xuartps_stop_tx - Stop TX + * cdns_uart_stop_tx - Stop TX * @port: Handle to the uart port structure */ -static void xuartps_stop_tx(struct uart_port *port) +static void cdns_uart_stop_tx(struct uart_port *port) { unsigned int regval; - regval = xuartps_readl(XUARTPS_CR_OFFSET); - regval |= XUARTPS_CR_TX_DIS; + regval = cdns_uart_readl(CDNS_UART_CR_OFFSET); + regval |= CDNS_UART_CR_TX_DIS; /* Disable the transmitter */ - xuartps_writel(regval, XUARTPS_CR_OFFSET); + cdns_uart_writel(regval, CDNS_UART_CR_OFFSET); } /** - * xuartps_stop_rx - Stop RX + * cdns_uart_stop_rx - Stop RX * @port: Handle to the uart port structure */ -static void xuartps_stop_rx(struct uart_port *port) +static void cdns_uart_stop_rx(struct uart_port *port) { unsigned int regval; - regval = xuartps_readl(XUARTPS_CR_OFFSET); - regval |= XUARTPS_CR_RX_DIS; + regval = cdns_uart_readl(CDNS_UART_CR_OFFSET); + regval |= CDNS_UART_CR_RX_DIS; /* Disable the receiver */ - xuartps_writel(regval, XUARTPS_CR_OFFSET); + cdns_uart_writel(regval, CDNS_UART_CR_OFFSET); } /** - * xuartps_tx_empty - Check whether TX is empty + * cdns_uart_tx_empty - Check whether TX is empty * @port: Handle to the uart port structure * * Return: TIOCSER_TEMT on success, 0 otherwise */ -static unsigned int xuartps_tx_empty(struct uart_port *port) +static unsigned int cdns_uart_tx_empty(struct uart_port *port) { unsigned int status; - status = xuartps_readl(XUARTPS_ISR_OFFSET) & XUARTPS_IXR_TXEMPTY; + status = cdns_uart_readl(CDNS_UART_ISR_OFFSET) & CDNS_UART_IXR_TXEMPTY; return status ? TIOCSER_TEMT : 0; } /** - * xuartps_break_ctl - Based on the input ctl we have to start or stop + * cdns_uart_break_ctl - Based on the input ctl we have to start or stop * transmitting char breaks * @port: Handle to the uart port structure * @ctl: Value based on which start or stop decision is taken */ -static void xuartps_break_ctl(struct uart_port *port, int ctl) +static void cdns_uart_break_ctl(struct uart_port *port, int ctl) { unsigned int status; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); if (ctl == -1) - xuartps_writel(XUARTPS_CR_STARTBRK | status, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_STARTBRK | status, + CDNS_UART_CR_OFFSET); else { - if ((status & XUARTPS_CR_STOPBRK) == 0) - xuartps_writel(XUARTPS_CR_STOPBRK | status, - XUARTPS_CR_OFFSET); + if ((status & CDNS_UART_CR_STOPBRK) == 0) + cdns_uart_writel(CDNS_UART_CR_STOPBRK | status, + CDNS_UART_CR_OFFSET); } spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_set_termios - termios operations, handling data length, parity, + * cdns_uart_set_termios - termios operations, handling data length, parity, * stop bits, flow control, baud rate * @port: Handle to the uart port structure * @termios: Handle to the input termios structure * @old: Values of the previously saved termios structure */ -static void xuartps_set_termios(struct uart_port *port, +static void cdns_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { unsigned int cval = 0; @@ -624,25 +629,26 @@ static void xuartps_set_termios(struct uart_port *port, spin_lock_irqsave(&port->lock, flags); /* Empty the receive FIFO 1st before making changes */ - while ((xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) { - xuartps_readl(XUARTPS_FIFO_OFFSET); + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { + cdns_uart_readl(CDNS_UART_FIFO_OFFSET); } /* Disable the TX and RX to set baud rate */ - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - ctrl_reg |= XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS; - xuartps_writel(ctrl_reg, XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); /* * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk * min and max baud should be calculated here based on port->uartclk. * this way we get a valid baud and can safely call set_baud() */ - minbaud = port->uartclk / ((XUARTPS_BDIV_MAX + 1) * XUARTPS_CD_MAX * 8); - maxbaud = port->uartclk / (XUARTPS_BDIV_MIN + 1); + minbaud = port->uartclk / + ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX * 8); + maxbaud = port->uartclk / (CDNS_UART_BDIV_MIN + 1); baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud); - baud = xuartps_set_baud_rate(port, baud); + baud = cdns_uart_set_baud_rate(port, baud); if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); @@ -650,52 +656,52 @@ static void xuartps_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); /* Set TX/RX Reset */ - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - ctrl_reg |= XUARTPS_CR_TXRST | XUARTPS_CR_RXRST; - xuartps_writel(ctrl_reg, XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); /* * Clear the RX disable and TX disable bits and then set the TX enable * bit and RX enable bit to enable the transmitter and receiver. */ - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - ctrl_reg &= ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS); - ctrl_reg |= XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN; - xuartps_writel(ctrl_reg, XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); - port->read_status_mask = XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXTRIG | - XUARTPS_IXR_OVERRUN | XUARTPS_IXR_TOUT; + port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG | + CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT; port->ignore_status_mask = 0; if (termios->c_iflag & INPCK) - port->read_status_mask |= XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING; + port->read_status_mask |= CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING; if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN; + port->ignore_status_mask |= CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; /* ignore all characters if CREAD is not set */ if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= XUARTPS_IXR_RXTRIG | - XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN; + port->ignore_status_mask |= CDNS_UART_IXR_RXTRIG | + CDNS_UART_IXR_TOUT | CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; - mode_reg = xuartps_readl(XUARTPS_MR_OFFSET); + mode_reg = cdns_uart_readl(CDNS_UART_MR_OFFSET); /* Handling Data Size */ switch (termios->c_cflag & CSIZE) { case CS6: - cval |= XUARTPS_MR_CHARLEN_6_BIT; + cval |= CDNS_UART_MR_CHARLEN_6_BIT; break; case CS7: - cval |= XUARTPS_MR_CHARLEN_7_BIT; + cval |= CDNS_UART_MR_CHARLEN_7_BIT; break; default: case CS8: - cval |= XUARTPS_MR_CHARLEN_8_BIT; + cval |= CDNS_UART_MR_CHARLEN_8_BIT; termios->c_cflag &= ~CSIZE; termios->c_cflag |= CS8; break; @@ -703,133 +709,135 @@ static void xuartps_set_termios(struct uart_port *port, /* Handling Parity and Stop Bits length */ if (termios->c_cflag & CSTOPB) - cval |= XUARTPS_MR_STOPMODE_2_BIT; /* 2 STOP bits */ + cval |= CDNS_UART_MR_STOPMODE_2_BIT; /* 2 STOP bits */ else - cval |= XUARTPS_MR_STOPMODE_1_BIT; /* 1 STOP bit */ + cval |= CDNS_UART_MR_STOPMODE_1_BIT; /* 1 STOP bit */ if (termios->c_cflag & PARENB) { /* Mark or Space parity */ if (termios->c_cflag & CMSPAR) { if (termios->c_cflag & PARODD) - cval |= XUARTPS_MR_PARITY_MARK; + cval |= CDNS_UART_MR_PARITY_MARK; else - cval |= XUARTPS_MR_PARITY_SPACE; + cval |= CDNS_UART_MR_PARITY_SPACE; } else { if (termios->c_cflag & PARODD) - cval |= XUARTPS_MR_PARITY_ODD; + cval |= CDNS_UART_MR_PARITY_ODD; else - cval |= XUARTPS_MR_PARITY_EVEN; + cval |= CDNS_UART_MR_PARITY_EVEN; } } else { - cval |= XUARTPS_MR_PARITY_NONE; + cval |= CDNS_UART_MR_PARITY_NONE; } cval |= mode_reg & 1; - xuartps_writel(cval, XUARTPS_MR_OFFSET); + cdns_uart_writel(cval, CDNS_UART_MR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_startup - Called when an application opens a xuartps port + * cdns_uart_startup - Called when an application opens a cdns_uart port * @port: Handle to the uart port structure * * Return: 0 on success, negative errno otherwise */ -static int xuartps_startup(struct uart_port *port) +static int cdns_uart_startup(struct uart_port *port) { unsigned int retval = 0, status = 0; - retval = request_irq(port->irq, xuartps_isr, 0, XUARTPS_NAME, + retval = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, (void *)port); if (retval) return retval; /* Disable the TX and RX */ - xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, + CDNS_UART_CR_OFFSET); /* Set the Control Register with TX/RX Enable, TX/RX Reset, * no break chars. */ - xuartps_writel(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST, + CDNS_UART_CR_OFFSET); - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); /* Clear the RX disable and TX disable bits and then set the TX enable * bit and RX enable bit to enable the transmitter and receiver. */ - xuartps_writel((status & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) - | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN | - XUARTPS_CR_STOPBRK), XUARTPS_CR_OFFSET); + cdns_uart_writel((status & ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS)) + | (CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN | + CDNS_UART_CR_STOPBRK), CDNS_UART_CR_OFFSET); /* Set the Mode Register with normal mode,8 data bits,1 stop bit, * no parity. */ - xuartps_writel(XUARTPS_MR_CHMODE_NORM | XUARTPS_MR_STOPMODE_1_BIT - | XUARTPS_MR_PARITY_NONE | XUARTPS_MR_CHARLEN_8_BIT, - XUARTPS_MR_OFFSET); + cdns_uart_writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT + | CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT, + CDNS_UART_MR_OFFSET); /* * Set the RX FIFO Trigger level to use most of the FIFO, but it * can be tuned with a module parameter */ - xuartps_writel(rx_trigger_level, XUARTPS_RXWM_OFFSET); + cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET); /* * Receive Timeout register is enabled but it * can be tuned with a module parameter */ - xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); /* Clear out any pending interrupts before enabling them */ - xuartps_writel(xuartps_readl(XUARTPS_ISR_OFFSET), XUARTPS_ISR_OFFSET); + cdns_uart_writel(cdns_uart_readl(CDNS_UART_ISR_OFFSET), + CDNS_UART_ISR_OFFSET); /* Set the Interrupt Registers with desired interrupts */ - xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | - XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN | + CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT, + CDNS_UART_IER_OFFSET); return retval; } /** - * xuartps_shutdown - Called when an application closes a xuartps port + * cdns_uart_shutdown - Called when an application closes a cdns_uart port * @port: Handle to the uart port structure */ -static void xuartps_shutdown(struct uart_port *port) +static void cdns_uart_shutdown(struct uart_port *port) { int status; /* Disable interrupts */ - status = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(status, XUARTPS_IDR_OFFSET); + status = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(status, CDNS_UART_IDR_OFFSET); /* Disable the TX and RX */ - xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, + CDNS_UART_CR_OFFSET); free_irq(port->irq, port); } /** - * xuartps_type - Set UART type to xuartps port + * cdns_uart_type - Set UART type to cdns_uart port * @port: Handle to the uart port structure * * Return: string on success, NULL otherwise */ -static const char *xuartps_type(struct uart_port *port) +static const char *cdns_uart_type(struct uart_port *port) { - return port->type == PORT_XUARTPS ? XUARTPS_NAME : NULL; + return port->type == PORT_XUARTPS ? CDNS_UART_NAME : NULL; } /** - * xuartps_verify_port - Verify the port params + * cdns_uart_verify_port - Verify the port params * @port: Handle to the uart port structure * @ser: Handle to the structure whose members are compared * * Return: 0 on success, negative errno otherwise. */ -static int xuartps_verify_port(struct uart_port *port, +static int cdns_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { if (ser->type != PORT_UNKNOWN && ser->type != PORT_XUARTPS) @@ -846,170 +854,170 @@ static int xuartps_verify_port(struct uart_port *port, } /** - * xuartps_request_port - Claim the memory region attached to xuartps port, - * called when the driver adds a xuartps port via + * cdns_uart_request_port - Claim the memory region attached to cdns_uart port, + * called when the driver adds a cdns_uart port via * uart_add_one_port() * @port: Handle to the uart port structure * * Return: 0 on success, negative errno otherwise. */ -static int xuartps_request_port(struct uart_port *port) +static int cdns_uart_request_port(struct uart_port *port) { - if (!request_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE, - XUARTPS_NAME)) { + if (!request_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE, + CDNS_UART_NAME)) { return -ENOMEM; } - port->membase = ioremap(port->mapbase, XUARTPS_REGISTER_SPACE); + port->membase = ioremap(port->mapbase, CDNS_UART_REGISTER_SPACE); if (!port->membase) { dev_err(port->dev, "Unable to map registers\n"); - release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE); + release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); return -ENOMEM; } return 0; } /** - * xuartps_release_port - Release UART port + * cdns_uart_release_port - Release UART port * @port: Handle to the uart port structure * - * Release the memory region attached to a xuartps port. Called when the - * driver removes a xuartps port via uart_remove_one_port(). + * Release the memory region attached to a cdns_uart port. Called when the + * driver removes a cdns_uart port via uart_remove_one_port(). */ -static void xuartps_release_port(struct uart_port *port) +static void cdns_uart_release_port(struct uart_port *port) { - release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE); + release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); iounmap(port->membase); port->membase = NULL; } /** - * xuartps_config_port - Configure UART port + * cdns_uart_config_port - Configure UART port * @port: Handle to the uart port structure * @flags: If any */ -static void xuartps_config_port(struct uart_port *port, int flags) +static void cdns_uart_config_port(struct uart_port *port, int flags) { - if (flags & UART_CONFIG_TYPE && xuartps_request_port(port) == 0) + if (flags & UART_CONFIG_TYPE && cdns_uart_request_port(port) == 0) port->type = PORT_XUARTPS; } /** - * xuartps_get_mctrl - Get the modem control state + * cdns_uart_get_mctrl - Get the modem control state * @port: Handle to the uart port structure * * Return: the modem control state */ -static unsigned int xuartps_get_mctrl(struct uart_port *port) +static unsigned int cdns_uart_get_mctrl(struct uart_port *port) { return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; } -static void xuartps_set_mctrl(struct uart_port *port, unsigned int mctrl) +static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { /* N/A */ } -static void xuartps_enable_ms(struct uart_port *port) +static void cdns_uart_enable_ms(struct uart_port *port) { /* N/A */ } #ifdef CONFIG_CONSOLE_POLL -static int xuartps_poll_get_char(struct uart_port *port) +static int cdns_uart_poll_get_char(struct uart_port *port) { u32 imr; int c; /* Disable all interrupts */ - imr = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(imr, XUARTPS_IDR_OFFSET); + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* Check if FIFO is empty */ - if (xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY) + if (cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY) c = NO_POLL_CHAR; else /* Read a character */ - c = (unsigned char) xuartps_readl(XUARTPS_FIFO_OFFSET); + c = (unsigned char) cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* Enable interrupts */ - xuartps_writel(imr, XUARTPS_IER_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); return c; } -static void xuartps_poll_put_char(struct uart_port *port, unsigned char c) +static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) { u32 imr; /* Disable all interrupts */ - imr = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(imr, XUARTPS_IDR_OFFSET); + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* Wait until FIFO is empty */ - while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY)) + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); /* Write a character */ - xuartps_writel(c, XUARTPS_FIFO_OFFSET); + cdns_uart_writel(c, CDNS_UART_FIFO_OFFSET); /* Wait until FIFO is empty */ - while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY)) + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); /* Enable interrupts */ - xuartps_writel(imr, XUARTPS_IER_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); return; } #endif -static struct uart_ops xuartps_ops = { - .set_mctrl = xuartps_set_mctrl, - .get_mctrl = xuartps_get_mctrl, - .enable_ms = xuartps_enable_ms, - .start_tx = xuartps_start_tx, - .stop_tx = xuartps_stop_tx, - .stop_rx = xuartps_stop_rx, - .tx_empty = xuartps_tx_empty, - .break_ctl = xuartps_break_ctl, - .set_termios = xuartps_set_termios, - .startup = xuartps_startup, - .shutdown = xuartps_shutdown, - .type = xuartps_type, - .verify_port = xuartps_verify_port, - .request_port = xuartps_request_port, - .release_port = xuartps_release_port, - .config_port = xuartps_config_port, +static struct uart_ops cdns_uart_ops = { + .set_mctrl = cdns_uart_set_mctrl, + .get_mctrl = cdns_uart_get_mctrl, + .enable_ms = cdns_uart_enable_ms, + .start_tx = cdns_uart_start_tx, + .stop_tx = cdns_uart_stop_tx, + .stop_rx = cdns_uart_stop_rx, + .tx_empty = cdns_uart_tx_empty, + .break_ctl = cdns_uart_break_ctl, + .set_termios = cdns_uart_set_termios, + .startup = cdns_uart_startup, + .shutdown = cdns_uart_shutdown, + .type = cdns_uart_type, + .verify_port = cdns_uart_verify_port, + .request_port = cdns_uart_request_port, + .release_port = cdns_uart_release_port, + .config_port = cdns_uart_config_port, #ifdef CONFIG_CONSOLE_POLL - .poll_get_char = xuartps_poll_get_char, - .poll_put_char = xuartps_poll_put_char, + .poll_get_char = cdns_uart_poll_get_char, + .poll_put_char = cdns_uart_poll_put_char, #endif }; -static struct uart_port xuartps_port[2]; +static struct uart_port cdns_uart_port[2]; /** - * xuartps_get_port - Configure the port from the platform device resource info + * cdns_uart_get_port - Configure the port from platform device resource info * @id: Port id * * Return: a pointer to a uart_port or NULL for failure */ -static struct uart_port *xuartps_get_port(int id) +static struct uart_port *cdns_uart_get_port(int id) { struct uart_port *port; /* Try the given port id if failed use default method */ - if (xuartps_port[id].mapbase != 0) { + if (cdns_uart_port[id].mapbase != 0) { /* Find the next unused port */ - for (id = 0; id < XUARTPS_NR_PORTS; id++) - if (xuartps_port[id].mapbase == 0) + for (id = 0; id < CDNS_UART_NR_PORTS; id++) + if (cdns_uart_port[id].mapbase == 0) break; } - if (id >= XUARTPS_NR_PORTS) + if (id >= CDNS_UART_NR_PORTS) return NULL; - port = &xuartps_port[id]; + port = &cdns_uart_port[id]; /* At this point, we've got an empty uart_port struct, initialize it */ spin_lock_init(&port->lock); @@ -1019,8 +1027,8 @@ static struct uart_port *xuartps_get_port(int id) port->type = PORT_UNKNOWN; port->iotype = UPIO_MEM32; port->flags = UPF_BOOT_AUTOCONF; - port->ops = &xuartps_ops; - port->fifosize = XUARTPS_FIFO_SIZE; + port->ops = &cdns_uart_ops; + port->fifosize = CDNS_UART_FIFO_SIZE; port->line = id; port->dev = NULL; return port; @@ -1028,37 +1036,37 @@ static struct uart_port *xuartps_get_port(int id) #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /** - * xuartps_console_wait_tx - Wait for the TX to be full + * cdns_uart_console_wait_tx - Wait for the TX to be full * @port: Handle to the uart port structure */ -static void xuartps_console_wait_tx(struct uart_port *port) +static void cdns_uart_console_wait_tx(struct uart_port *port) { - while ((xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY) - != XUARTPS_SR_TXEMPTY) + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY) + != CDNS_UART_SR_TXEMPTY) barrier(); } /** - * xuartps_console_putchar - write the character to the FIFO buffer + * cdns_uart_console_putchar - write the character to the FIFO buffer * @port: Handle to the uart port structure * @ch: Character to be written */ -static void xuartps_console_putchar(struct uart_port *port, int ch) +static void cdns_uart_console_putchar(struct uart_port *port, int ch) { - xuartps_console_wait_tx(port); - xuartps_writel(ch, XUARTPS_FIFO_OFFSET); + cdns_uart_console_wait_tx(port); + cdns_uart_writel(ch, CDNS_UART_FIFO_OFFSET); } /** - * xuartps_console_write - perform write operation + * cdns_uart_console_write - perform write operation * @co: Console handle * @s: Pointer to character array * @count: No of characters */ -static void xuartps_console_write(struct console *co, const char *s, +static void cdns_uart_console_write(struct console *co, const char *s, unsigned int count) { - struct uart_port *port = &xuartps_port[co->index]; + struct uart_port *port = &cdns_uart_port[co->index]; unsigned long flags; unsigned int imr, ctrl; int locked = 1; @@ -1069,45 +1077,45 @@ static void xuartps_console_write(struct console *co, const char *s, spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - imr = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(imr, XUARTPS_IDR_OFFSET); + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); /* * Make sure that the tx part is enabled. Set the TX enable bit and * clear the TX disable bit to enable the transmitter. */ - ctrl = xuartps_readl(XUARTPS_CR_OFFSET); - xuartps_writel((ctrl & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN, - XUARTPS_CR_OFFSET); + ctrl = cdns_uart_readl(CDNS_UART_CR_OFFSET); + cdns_uart_writel((ctrl & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, + CDNS_UART_CR_OFFSET); - uart_console_write(port, s, count, xuartps_console_putchar); - xuartps_console_wait_tx(port); + uart_console_write(port, s, count, cdns_uart_console_putchar); + cdns_uart_console_wait_tx(port); - xuartps_writel(ctrl, XUARTPS_CR_OFFSET); + cdns_uart_writel(ctrl, CDNS_UART_CR_OFFSET); /* restore interrupt state */ - xuartps_writel(imr, XUARTPS_IER_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); if (locked) spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_console_setup - Initialize the uart to default config + * cdns_uart_console_setup - Initialize the uart to default config * @co: Console handle * @options: Initial settings of uart * * Return: 0 on success, negative errno otherwise. */ -static int __init xuartps_console_setup(struct console *co, char *options) +static int __init cdns_uart_console_setup(struct console *co, char *options) { - struct uart_port *port = &xuartps_port[co->index]; + struct uart_port *port = &cdns_uart_port[co->index]; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; - if (co->index < 0 || co->index >= XUARTPS_NR_PORTS) + if (co->index < 0 || co->index >= CDNS_UART_NR_PORTS) return -EINVAL; if (!port->mapbase) { @@ -1121,53 +1129,53 @@ static int __init xuartps_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } -static struct uart_driver xuartps_uart_driver; +static struct uart_driver cdns_uart_uart_driver; -static struct console xuartps_console = { - .name = XUARTPS_TTY_NAME, - .write = xuartps_console_write, +static struct console cdns_uart_console = { + .name = CDNS_UART_TTY_NAME, + .write = cdns_uart_console_write, .device = uart_console_device, - .setup = xuartps_console_setup, + .setup = cdns_uart_console_setup, .flags = CON_PRINTBUFFER, .index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */ - .data = &xuartps_uart_driver, + .data = &cdns_uart_uart_driver, }; /** - * xuartps_console_init - Initialization call + * cdns_uart_console_init - Initialization call * * Return: 0 on success, negative errno otherwise */ -static int __init xuartps_console_init(void) +static int __init cdns_uart_console_init(void) { - register_console(&xuartps_console); + register_console(&cdns_uart_console); return 0; } -console_initcall(xuartps_console_init); +console_initcall(cdns_uart_console_init); #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ -static struct uart_driver xuartps_uart_driver = { +static struct uart_driver cdns_uart_uart_driver = { .owner = THIS_MODULE, - .driver_name = XUARTPS_NAME, - .dev_name = XUARTPS_TTY_NAME, - .major = XUARTPS_MAJOR, - .minor = XUARTPS_MINOR, - .nr = XUARTPS_NR_PORTS, + .driver_name = CDNS_UART_NAME, + .dev_name = CDNS_UART_TTY_NAME, + .major = CDNS_UART_MAJOR, + .minor = CDNS_UART_MINOR, + .nr = CDNS_UART_NR_PORTS, #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE - .cons = &xuartps_console, + .cons = &cdns_uart_console, #endif }; #ifdef CONFIG_PM_SLEEP /** - * xuartps_suspend - suspend event + * cdns_uart_suspend - suspend event * @device: Pointer to the device structure * * Return: 0 */ -static int xuartps_suspend(struct device *device) +static int cdns_uart_suspend(struct device *device) { struct uart_port *port = dev_get_drvdata(device); struct tty_struct *tty; @@ -1186,23 +1194,24 @@ static int xuartps_suspend(struct device *device) * Call the API provided in serial_core.c file which handles * the suspend. */ - uart_suspend_port(&xuartps_uart_driver, port); + uart_suspend_port(&cdns_uart_uart_driver, port); if (console_suspend_enabled && !may_wake) { - struct xuartps *xuartps = port->private_data; + struct cdns_uart *cdns_uart = port->private_data; - clk_disable(xuartps->refclk); - clk_disable(xuartps->aperclk); + clk_disable(cdns_uart->uartclk); + clk_disable(cdns_uart->pclk); } else { unsigned long flags = 0; spin_lock_irqsave(&port->lock, flags); /* Empty the receive FIFO 1st before making changes */ - while (!(xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_RXEMPTY)) - xuartps_readl(XUARTPS_FIFO_OFFSET); + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY)) + cdns_uart_readl(CDNS_UART_FIFO_OFFSET); /* set RX trigger level to 1 */ - xuartps_writel(1, XUARTPS_RXWM_OFFSET); + cdns_uart_writel(1, CDNS_UART_RXWM_OFFSET); /* disable RX timeout interrups */ - xuartps_writel(XUARTPS_IXR_TOUT, XUARTPS_IDR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IDR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } @@ -1210,12 +1219,12 @@ static int xuartps_suspend(struct device *device) } /** - * xuartps_resume - Resume after a previous suspend + * cdns_uart_resume - Resume after a previous suspend * @device: Pointer to the device structure * * Return: 0 */ -static int xuartps_resume(struct device *device) +static int cdns_uart_resume(struct device *device) { struct uart_port *port = dev_get_drvdata(device); unsigned long flags = 0; @@ -1233,83 +1242,95 @@ static int xuartps_resume(struct device *device) } if (console_suspend_enabled && !may_wake) { - struct xuartps *xuartps = port->private_data; + struct cdns_uart *cdns_uart = port->private_data; - clk_enable(xuartps->aperclk); - clk_enable(xuartps->refclk); + clk_enable(cdns_uart->pclk); + clk_enable(cdns_uart->uartclk); spin_lock_irqsave(&port->lock, flags); /* Set TX/RX Reset */ - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - ctrl_reg |= XUARTPS_CR_TXRST | XUARTPS_CR_RXRST; - xuartps_writel(ctrl_reg, XUARTPS_CR_OFFSET); - while (xuartps_readl(XUARTPS_CR_OFFSET) & - (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST)) + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); + while (cdns_uart_readl(CDNS_UART_CR_OFFSET) & + (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); /* restore rx timeout value */ - xuartps_writel(rx_timeout, XUARTPS_RXTOUT_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); /* Enable Tx/Rx */ - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); - ctrl_reg &= ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS); - ctrl_reg |= XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN; - xuartps_writel(ctrl_reg, XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } else { spin_lock_irqsave(&port->lock, flags); /* restore original rx trigger level */ - xuartps_writel(rx_trigger_level, XUARTPS_RXWM_OFFSET); + cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET); /* enable RX timeout interrupt */ - xuartps_writel(XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IER_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } - return uart_resume_port(&xuartps_uart_driver, port); + return uart_resume_port(&cdns_uart_uart_driver, port); } #endif /* ! CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(xuartps_dev_pm_ops, xuartps_suspend, xuartps_resume); +static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend, + cdns_uart_resume); /** - * xuartps_probe - Platform driver probe + * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure * * Return: 0 on success, negative errno otherwise */ -static int xuartps_probe(struct platform_device *pdev) +static int cdns_uart_probe(struct platform_device *pdev) { int rc, id; struct uart_port *port; struct resource *res, *res2; - struct xuartps *xuartps_data; + struct cdns_uart *cdns_uart_data; - xuartps_data = devm_kzalloc(&pdev->dev, sizeof(*xuartps_data), + cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data), GFP_KERNEL); - if (!xuartps_data) + if (!cdns_uart_data) return -ENOMEM; - xuartps_data->aperclk = devm_clk_get(&pdev->dev, "aper_clk"); - if (IS_ERR(xuartps_data->aperclk)) { - dev_err(&pdev->dev, "aper_clk clock not found.\n"); - return PTR_ERR(xuartps_data->aperclk); + cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(cdns_uart_data->pclk)) { + cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk"); + if (!IS_ERR(cdns_uart_data->pclk)) + dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n"); + } + if (IS_ERR(cdns_uart_data->pclk)) { + dev_err(&pdev->dev, "pclk clock not found.\n"); + return PTR_ERR(cdns_uart_data->pclk); + } + + cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk"); + if (IS_ERR(cdns_uart_data->uartclk)) { + cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "ref_clk"); + if (!IS_ERR(cdns_uart_data->uartclk)) + dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n"); } - xuartps_data->refclk = devm_clk_get(&pdev->dev, "ref_clk"); - if (IS_ERR(xuartps_data->refclk)) { - dev_err(&pdev->dev, "ref_clk clock not found.\n"); - return PTR_ERR(xuartps_data->refclk); + if (IS_ERR(cdns_uart_data->uartclk)) { + dev_err(&pdev->dev, "uart_clk clock not found.\n"); + return PTR_ERR(cdns_uart_data->uartclk); } - rc = clk_prepare_enable(xuartps_data->aperclk); + rc = clk_prepare_enable(cdns_uart_data->pclk); if (rc) { - dev_err(&pdev->dev, "Unable to enable APER clock.\n"); + dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); return rc; } - rc = clk_prepare_enable(xuartps_data->refclk); + rc = clk_prepare_enable(cdns_uart_data->uartclk); if (rc) { dev_err(&pdev->dev, "Unable to enable device clock.\n"); - goto err_out_clk_dis_aper; + goto err_out_clk_dis_pclk; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1325,10 +1346,10 @@ static int xuartps_probe(struct platform_device *pdev) } #ifdef CONFIG_COMMON_CLK - xuartps_data->clk_rate_change_nb.notifier_call = - xuartps_clk_notifier_cb; - if (clk_notifier_register(xuartps_data->refclk, - &xuartps_data->clk_rate_change_nb)) + cdns_uart_data->clk_rate_change_nb.notifier_call = + cdns_uart_clk_notifier_cb; + if (clk_notifier_register(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb)) dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); #endif /* Look for a serialN alias */ @@ -1337,7 +1358,7 @@ static int xuartps_probe(struct platform_device *pdev) id = 0; /* Initialize the port structure */ - port = xuartps_get_port(id); + port = cdns_uart_get_port(id); if (!port) { dev_err(&pdev->dev, "Cannot get uart_port structure\n"); @@ -1351,11 +1372,11 @@ static int xuartps_probe(struct platform_device *pdev) port->mapbase = res->start; port->irq = res2->start; port->dev = &pdev->dev; - port->uartclk = clk_get_rate(xuartps_data->refclk); - port->private_data = xuartps_data; - xuartps_data->port = port; + port->uartclk = clk_get_rate(cdns_uart_data->uartclk); + port->private_data = cdns_uart_data; + cdns_uart_data->port = port; platform_set_drvdata(pdev, port); - rc = uart_add_one_port(&xuartps_uart_driver, port); + rc = uart_add_one_port(&cdns_uart_uart_driver, port); if (rc) { dev_err(&pdev->dev, "uart_add_one_port() failed; err=%i\n", rc); @@ -1366,88 +1387,89 @@ static int xuartps_probe(struct platform_device *pdev) err_out_notif_unreg: #ifdef CONFIG_COMMON_CLK - clk_notifier_unregister(xuartps_data->refclk, - &xuartps_data->clk_rate_change_nb); + clk_notifier_unregister(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb); #endif err_out_clk_disable: - clk_disable_unprepare(xuartps_data->refclk); -err_out_clk_dis_aper: - clk_disable_unprepare(xuartps_data->aperclk); + clk_disable_unprepare(cdns_uart_data->uartclk); +err_out_clk_dis_pclk: + clk_disable_unprepare(cdns_uart_data->pclk); return rc; } /** - * xuartps_remove - called when the platform driver is unregistered + * cdns_uart_remove - called when the platform driver is unregistered * @pdev: Pointer to the platform device structure * * Return: 0 on success, negative errno otherwise */ -static int xuartps_remove(struct platform_device *pdev) +static int cdns_uart_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); - struct xuartps *xuartps_data = port->private_data; + struct cdns_uart *cdns_uart_data = port->private_data; int rc; - /* Remove the xuartps port from the serial core */ + /* Remove the cdns_uart port from the serial core */ #ifdef CONFIG_COMMON_CLK - clk_notifier_unregister(xuartps_data->refclk, - &xuartps_data->clk_rate_change_nb); + clk_notifier_unregister(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb); #endif - rc = uart_remove_one_port(&xuartps_uart_driver, port); + rc = uart_remove_one_port(&cdns_uart_uart_driver, port); port->mapbase = 0; - clk_disable_unprepare(xuartps_data->refclk); - clk_disable_unprepare(xuartps_data->aperclk); + clk_disable_unprepare(cdns_uart_data->uartclk); + clk_disable_unprepare(cdns_uart_data->pclk); return rc; } /* Match table for of_platform binding */ -static struct of_device_id xuartps_of_match[] = { +static struct of_device_id cdns_uart_of_match[] = { { .compatible = "xlnx,xuartps", }, + { .compatible = "cdns,uart-r1p8", }, {} }; -MODULE_DEVICE_TABLE(of, xuartps_of_match); +MODULE_DEVICE_TABLE(of, cdns_uart_of_match); -static struct platform_driver xuartps_platform_driver = { - .probe = xuartps_probe, - .remove = xuartps_remove, +static struct platform_driver cdns_uart_platform_driver = { + .probe = cdns_uart_probe, + .remove = cdns_uart_remove, .driver = { .owner = THIS_MODULE, - .name = XUARTPS_NAME, - .of_match_table = xuartps_of_match, - .pm = &xuartps_dev_pm_ops, + .name = CDNS_UART_NAME, + .of_match_table = cdns_uart_of_match, + .pm = &cdns_uart_dev_pm_ops, }, }; -static int __init xuartps_init(void) +static int __init cdns_uart_init(void) { int retval = 0; - /* Register the xuartps driver with the serial core */ - retval = uart_register_driver(&xuartps_uart_driver); + /* Register the cdns_uart driver with the serial core */ + retval = uart_register_driver(&cdns_uart_uart_driver); if (retval) return retval; /* Register the platform driver */ - retval = platform_driver_register(&xuartps_platform_driver); + retval = platform_driver_register(&cdns_uart_platform_driver); if (retval) - uart_unregister_driver(&xuartps_uart_driver); + uart_unregister_driver(&cdns_uart_uart_driver); return retval; } -static void __exit xuartps_exit(void) +static void __exit cdns_uart_exit(void) { /* Unregister the platform driver */ - platform_driver_unregister(&xuartps_platform_driver); + platform_driver_unregister(&cdns_uart_platform_driver); - /* Unregister the xuartps driver */ - uart_unregister_driver(&xuartps_uart_driver); + /* Unregister the cdns_uart driver */ + uart_unregister_driver(&cdns_uart_uart_driver); } -module_init(xuartps_init); -module_exit(xuartps_exit); +module_init(cdns_uart_init); +module_exit(cdns_uart_exit); -MODULE_DESCRIPTION("Driver for PS UART"); +MODULE_DESCRIPTION("Driver for Cadence UART"); MODULE_AUTHOR("Xilinx Inc."); MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index b47dba2c1e6f..22aaf8ed7735 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -211,7 +211,7 @@ /* VIA VT8500 SoC */ #define PORT_VT8500 97 -/* Xilinx PSS UART */ +/* Cadence (Xilinx Zynq) UART */ #define PORT_XUARTPS 98 /* Atheros AR933X SoC */ -- cgit v1.2.3 From e264ebf4c81ac733642ed03ee3f0e26914ed3714 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Thu, 17 Apr 2014 15:47:58 +0200 Subject: tty: serial: Add driver for MEN's 16z135 High Speed UART. Add driver for MEN's 16z135 High Speed UART. The 16z135 is a memory mapped UART Core on an MCB FPGA and has 1024 byte deep FIFO buffers for the RX and TX path. It also has configurable FIFO fill level IRQs and data copied to and from the hardware has to be acknowledged. Signed-off-by: Johannes Thumshirn Reviewed-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 10 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/men_z135_uart.c | 866 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 880 insertions(+) create mode 100644 drivers/tty/serial/men_z135_uart.c (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 396cf8499947..30530e47cdf0 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1509,6 +1509,16 @@ config SERIAL_ST_ASC_CONSOLE depends on SERIAL_ST_ASC=y select SERIAL_CORE_CONSOLE +config SERIAL_MEN_Z135 + tristate "MEN 16z135 Support" + depends on MCB + help + Say yes here to enable support for the MEN 16z135 High Speed UART IP-Core + on a MCB carrier. + + This driver can also be build as a module. If so, the module will be called + men_z135_uart.ko + endmenu endif # TTY diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 3680854fef41..5f2a3f493ab9 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -87,3 +87,4 @@ obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o obj-$(CONFIG_SERIAL_RP2) += rp2.o obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o +obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c new file mode 100644 index 000000000000..d08eb5d8330d --- /dev/null +++ b/drivers/tty/serial/men_z135_uart.c @@ -0,0 +1,866 @@ +/* + * MEN 16z135 High Speed UART + * + * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) + * Author: Johannes Thumshirn + * + * 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; version 2 of the License. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MEN_Z135_MAX_PORTS 12 +#define MEN_Z135_BASECLK 29491200 +#define MEN_Z135_FIFO_SIZE 1024 +#define MEN_Z135_NUM_MSI_VECTORS 2 +#define MEN_Z135_FIFO_WATERMARK 1020 + +#define MEN_Z135_STAT_REG 0x0 +#define MEN_Z135_RX_RAM 0x4 +#define MEN_Z135_TX_RAM 0x400 +#define MEN_Z135_RX_CTRL 0x800 +#define MEN_Z135_TX_CTRL 0x804 +#define MEN_Z135_CONF_REG 0x808 +#define MEN_Z135_UART_FREQ 0x80c +#define MEN_Z135_BAUD_REG 0x810 +#define MENZ135_TIMEOUT 0x814 + +#define MEN_Z135_MEM_SIZE 0x818 + +#define IS_IRQ(x) ((x) & 1) +#define IRQ_ID(x) (((x) >> 1) & 7) + +#define MEN_Z135_IER_RXCIEN BIT(0) /* TX Space IRQ */ +#define MEN_Z135_IER_TXCIEN BIT(1) /* RX Space IRQ */ +#define MEN_Z135_IER_RLSIEN BIT(2) /* Receiver Line Status IRQ */ +#define MEN_Z135_IER_MSIEN BIT(3) /* Modem Status IRQ */ +#define MEN_Z135_ALL_IRQS (MEN_Z135_IER_RXCIEN \ + | MEN_Z135_IER_RLSIEN \ + | MEN_Z135_IER_MSIEN \ + | MEN_Z135_IER_TXCIEN) + +#define MEN_Z135_MCR_DTR BIT(24) +#define MEN_Z135_MCR_RTS BIT(25) +#define MEN_Z135_MCR_OUT1 BIT(26) +#define MEN_Z135_MCR_OUT2 BIT(27) +#define MEN_Z135_MCR_LOOP BIT(28) +#define MEN_Z135_MCR_RCFC BIT(29) + +#define MEN_Z135_MSR_DCTS BIT(0) +#define MEN_Z135_MSR_DDSR BIT(1) +#define MEN_Z135_MSR_DRI BIT(2) +#define MEN_Z135_MSR_DDCD BIT(3) +#define MEN_Z135_MSR_CTS BIT(4) +#define MEN_Z135_MSR_DSR BIT(5) +#define MEN_Z135_MSR_RI BIT(6) +#define MEN_Z135_MSR_DCD BIT(7) + +#define MEN_Z135_LCR_SHIFT 8 /* LCR shift mask */ + +#define MEN_Z135_WL5 0 /* CS5 */ +#define MEN_Z135_WL6 1 /* CS6 */ +#define MEN_Z135_WL7 2 /* CS7 */ +#define MEN_Z135_WL8 3 /* CS8 */ + +#define MEN_Z135_STB_SHIFT 2 /* Stopbits */ +#define MEN_Z135_NSTB1 0 +#define MEN_Z135_NSTB2 1 + +#define MEN_Z135_PEN_SHIFT 3 /* Parity enable */ +#define MEN_Z135_PAR_DIS 0 +#define MEN_Z135_PAR_ENA 1 + +#define MEN_Z135_PTY_SHIFT 4 /* Parity type */ +#define MEN_Z135_PTY_ODD 0 +#define MEN_Z135_PTY_EVN 1 + +#define MEN_Z135_LSR_DR BIT(0) +#define MEN_Z135_LSR_OE BIT(1) +#define MEN_Z135_LSR_PE BIT(2) +#define MEN_Z135_LSR_FE BIT(3) +#define MEN_Z135_LSR_BI BIT(4) +#define MEN_Z135_LSR_THEP BIT(5) +#define MEN_Z135_LSR_TEXP BIT(6) +#define MEN_Z135_LSR_RXFIFOERR BIT(7) + +#define MEN_Z135_IRQ_ID_MST 0 +#define MEN_Z135_IRQ_ID_TSA 1 +#define MEN_Z135_IRQ_ID_RDA 2 +#define MEN_Z135_IRQ_ID_RLS 3 +#define MEN_Z135_IRQ_ID_CTI 6 + +#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff) + +#define BYTES_TO_ALIGN(x) ((x) & 0x3) + +static int line; + +static int txlvl = 5; +module_param(txlvl, int, S_IRUGO); +MODULE_PARM_DESC(txlvl, "TX IRQ trigger level 0-7, default 5 (128 byte)"); + +static int rxlvl = 6; +module_param(rxlvl, int, S_IRUGO); +MODULE_PARM_DESC(rxlvl, "RX IRQ trigger level 0-7, default 6 (256 byte)"); + +static int align; +module_param(align, int, S_IRUGO); +MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0"); + +struct men_z135_port { + struct uart_port port; + struct mcb_device *mdev; + unsigned char *rxbuf; + u32 stat_reg; + spinlock_t lock; +}; +#define to_men_z135(port) container_of((port), struct men_z135_port, port) + +/** + * men_z135_reg_set() - Set value in register + * @uart: The UART port + * @addr: Register address + * @val: value to set + */ +static inline void men_z135_reg_set(struct men_z135_port *uart, + u32 addr, u32 val) +{ + struct uart_port *port = &uart->port; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&uart->lock, flags); + + reg = ioread32(port->membase + addr); + reg |= val; + iowrite32(reg, port->membase + addr); + + spin_unlock_irqrestore(&uart->lock, flags); +} + +/** + * men_z135_reg_clr() - Unset value in register + * @uart: The UART port + * @addr: Register address + * @val: value to clear + */ +static inline void men_z135_reg_clr(struct men_z135_port *uart, + u32 addr, u32 val) +{ + struct uart_port *port = &uart->port; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&uart->lock, flags); + + reg = ioread32(port->membase + addr); + reg &= ~val; + iowrite32(reg, port->membase + addr); + + spin_unlock_irqrestore(&uart->lock, flags); +} + +/** + * men_z135_handle_modem_status() - Handle change of modem status + * @port: The UART port + * + * Handle change of modem status register. This is done by reading the "delta" + * versions of DCD (Data Carrier Detect) and CTS (Clear To Send). + */ +static void men_z135_handle_modem_status(struct men_z135_port *uart) +{ + if (uart->stat_reg & MEN_Z135_MSR_DDCD) + uart_handle_dcd_change(&uart->port, + uart->stat_reg & ~MEN_Z135_MSR_DCD); + if (uart->stat_reg & MEN_Z135_MSR_DCTS) + uart_handle_cts_change(&uart->port, + uart->stat_reg & ~MEN_Z135_MSR_CTS); +} + +static void men_z135_handle_lsr(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + u8 lsr; + + lsr = (uart->stat_reg >> 16) & 0xff; + + if (lsr & MEN_Z135_LSR_OE) + port->icount.overrun++; + if (lsr & MEN_Z135_LSR_PE) + port->icount.parity++; + if (lsr & MEN_Z135_LSR_FE) + port->icount.frame++; + if (lsr & MEN_Z135_LSR_BI) { + port->icount.brk++; + uart_handle_break(port); + } +} + +/** + * get_rx_fifo_content() - Get the number of bytes in RX FIFO + * @uart: The UART port + * + * Read RXC register from hardware and return current FIFO fill size. + */ +static u16 get_rx_fifo_content(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + u32 stat_reg; + u16 rxc; + u8 rxc_lo; + u8 rxc_hi; + + stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + rxc_lo = stat_reg >> 24; + rxc_hi = (stat_reg & 0xC0) >> 6; + + rxc = rxc_lo | (rxc_hi << 8); + + return rxc; +} + +/** + * men_z135_handle_rx() - RX tasklet routine + * @arg: Pointer to struct men_z135_port + * + * Copy from RX FIFO and acknowledge number of bytes copied. + */ +static void men_z135_handle_rx(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + struct tty_port *tport = &port->state->port; + int copied; + u16 size; + int room; + + size = get_rx_fifo_content(uart); + + if (size == 0) + return; + + /* Avoid accidently accessing TX FIFO instead of RX FIFO. Last + * longword in RX FIFO cannot be read.(0x004-0x3FF) + */ + if (size > MEN_Z135_FIFO_WATERMARK) + size = MEN_Z135_FIFO_WATERMARK; + + room = tty_buffer_request_room(tport, size); + if (room != size) + dev_warn(&uart->mdev->dev, + "Not enough room in flip buffer, truncating to %d\n", + room); + + if (room == 0) + return; + + memcpy_fromio(uart->rxbuf, port->membase + MEN_Z135_RX_RAM, room); + /* Be sure to first copy all data and then acknowledge it */ + mb(); + iowrite32(room, port->membase + MEN_Z135_RX_CTRL); + + copied = tty_insert_flip_string(tport, uart->rxbuf, room); + if (copied != room) + dev_warn(&uart->mdev->dev, + "Only copied %d instead of %d bytes\n", + copied, room); + + port->icount.rx += copied; + + tty_flip_buffer_push(tport); + +} + +/** + * men_z135_handle_tx() - TX tasklet routine + * @arg: Pointer to struct men_z135_port + * + */ +static void men_z135_handle_tx(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + struct circ_buf *xmit = &port->state->xmit; + u32 txc; + u32 wptr; + int qlen; + int n; + int txfree; + int head; + int tail; + int s; + + if (uart_circ_empty(xmit)) + goto out; + + if (uart_tx_stopped(port)) + goto out; + + if (port->x_char) + goto out; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* calculate bytes to copy */ + qlen = uart_circ_chars_pending(xmit); + if (qlen <= 0) + goto out; + + wptr = ioread32(port->membase + MEN_Z135_TX_CTRL); + txc = (wptr >> 16) & 0x3ff; + wptr &= 0x3ff; + + if (txc > MEN_Z135_FIFO_WATERMARK) + txc = MEN_Z135_FIFO_WATERMARK; + + txfree = MEN_Z135_FIFO_WATERMARK - txc; + if (txfree <= 0) { + pr_err("Not enough room in TX FIFO have %d, need %d\n", + txfree, qlen); + goto irq_en; + } + + /* if we're not aligned, it's better to copy only 1 or 2 bytes and + * then the rest. + */ + if (align && qlen >= 3 && BYTES_TO_ALIGN(wptr)) + n = 4 - BYTES_TO_ALIGN(wptr); + else if (qlen > txfree) + n = txfree; + else + n = qlen; + + if (n <= 0) + goto irq_en; + + head = xmit->head & (UART_XMIT_SIZE - 1); + tail = xmit->tail & (UART_XMIT_SIZE - 1); + + s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; + n = min(n, s); + + memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n); + xmit->tail = (xmit->tail + n) & (UART_XMIT_SIZE - 1); + mmiowb(); + + iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL); + + port->icount.tx += n; + +irq_en: + if (!uart_circ_empty(xmit)) + men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); + else + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); + +out: + return; + +} + +/** + * men_z135_intr() - Handle legacy IRQs + * @irq: The IRQ number + * @data: Pointer to UART port + * + * Check IIR register to see which tasklet to start. + */ +static irqreturn_t men_z135_intr(int irq, void *data) +{ + struct men_z135_port *uart = (struct men_z135_port *)data; + struct uart_port *port = &uart->port; + int irq_id; + + uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + /* IRQ pending is low active */ + if (IS_IRQ(uart->stat_reg)) + return IRQ_NONE; + + irq_id = IRQ_ID(uart->stat_reg); + switch (irq_id) { + case MEN_Z135_IRQ_ID_MST: + men_z135_handle_modem_status(uart); + break; + case MEN_Z135_IRQ_ID_TSA: + men_z135_handle_tx(uart); + break; + case MEN_Z135_IRQ_ID_CTI: + dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n"); + /* Fallthrough */ + case MEN_Z135_IRQ_ID_RDA: + /* Reading data clears RX IRQ */ + men_z135_handle_rx(uart); + break; + case MEN_Z135_IRQ_ID_RLS: + men_z135_handle_lsr(uart); + break; + default: + dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +/** + * men_z135_request_irq() - Request IRQ for 16z135 core + * @uart: z135 private uart port structure + * + * Request an IRQ for 16z135 to use. First try using MSI, if it fails + * fall back to using legacy interrupts. + */ +static int men_z135_request_irq(struct men_z135_port *uart) +{ + struct device *dev = &uart->mdev->dev; + struct uart_port *port = &uart->port; + int err = 0; + + err = request_irq(port->irq, men_z135_intr, IRQF_SHARED, + "men_z135_intr", uart); + if (err) + dev_err(dev, "Error %d getting interrupt\n", err); + + return err; +} + +/** + * men_z135_tx_empty() - Handle tx_empty call + * @port: The UART port + * + * This function tests whether the TX FIFO and shifter for the port + * described by @port is empty. + */ +static unsigned int men_z135_tx_empty(struct uart_port *port) +{ + u32 wptr; + u16 txc; + + wptr = ioread32(port->membase + MEN_Z135_TX_CTRL); + txc = (wptr >> 16) & 0x3ff; + + if (txc == 0) + return TIOCSER_TEMT; + else + return 0; +} + +/** + * men_z135_set_mctrl() - Set modem control lines + * @port: The UART port + * @mctrl: The modem control lines + * + * This function sets the modem control lines for a port described by @port + * to the state described by @mctrl + */ +static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct men_z135_port *uart = to_men_z135(port); + u32 conf_reg = 0; + + if (mctrl & TIOCM_RTS) + conf_reg |= MEN_Z135_MCR_RTS; + if (mctrl & TIOCM_DTR) + conf_reg |= MEN_Z135_MCR_DTR; + if (mctrl & TIOCM_OUT1) + conf_reg |= MEN_Z135_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + conf_reg |= MEN_Z135_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + conf_reg |= MEN_Z135_MCR_LOOP; + + men_z135_reg_set(uart, MEN_Z135_CONF_REG, conf_reg); +} + +/** + * men_z135_get_mctrl() - Get modem control lines + * @port: The UART port + * + * Retruns the current state of modem control inputs. + */ +static unsigned int men_z135_get_mctrl(struct uart_port *port) +{ + unsigned int mctrl = 0; + u32 stat_reg; + u8 msr; + + stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + + msr = ~((stat_reg >> 8) & 0xff); + + if (msr & MEN_Z135_MSR_CTS) + mctrl |= TIOCM_CTS; + if (msr & MEN_Z135_MSR_DSR) + mctrl |= TIOCM_DSR; + if (msr & MEN_Z135_MSR_RI) + mctrl |= TIOCM_RI; + if (msr & MEN_Z135_MSR_DCD) + mctrl |= TIOCM_CAR; + + return mctrl; +} + +/** + * men_z135_stop_tx() - Stop transmitting characters + * @port: The UART port + * + * Stop transmitting characters. This might be due to CTS line becomming + * inactive or the tty layer indicating we want to stop transmission due to + * an XOFF character. + */ +static void men_z135_stop_tx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); +} + +/** + * men_z135_start_tx() - Start transmitting characters + * @port: The UART port + * + * Start transmitting character. This actually doesn't transmit anything, but + * fires off the TX tasklet. + */ +static void men_z135_start_tx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_handle_tx(uart); +} + +/** + * men_z135_stop_rx() - Stop receiving characters + * @port: The UART port + * + * Stop receiving characters; the port is in the process of being closed. + */ +static void men_z135_stop_rx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_RXCIEN); +} + +/** + * men_z135_enable_ms() - Enable Modem Status + * port: + * + * Enable Modem Status IRQ. + */ +static void men_z135_enable_ms(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_MSIEN); +} + +static int men_z135_startup(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + int err; + u32 conf_reg = 0; + + err = men_z135_request_irq(uart); + if (err) + return -ENODEV; + + conf_reg = ioread32(port->membase + MEN_Z135_CONF_REG); + + conf_reg |= MEN_Z135_ALL_IRQS; + conf_reg &= ~(0xff << 16); + conf_reg |= (txlvl << 16); + conf_reg |= (rxlvl << 20); + + iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG); + + return 0; +} + +static void men_z135_shutdown(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + u32 conf_reg = 0; + + conf_reg |= MEN_Z135_ALL_IRQS; + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, conf_reg); + + free_irq(uart->port.irq, uart); +} + +static void men_z135_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud; + u32 conf_reg; + u32 bd_reg; + u32 uart_freq; + u8 lcr; + + conf_reg = ioread32(port->membase + MEN_Z135_CONF_REG); + lcr = LCR(conf_reg); + + /* byte size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= MEN_Z135_WL5; + break; + case CS6: + lcr |= MEN_Z135_WL6; + break; + case CS7: + lcr |= MEN_Z135_WL7; + break; + case CS8: + lcr |= MEN_Z135_WL8; + break; + } + + /* stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= MEN_Z135_NSTB2 << MEN_Z135_STB_SHIFT; + + /* parity */ + if (termios->c_cflag & PARENB) { + lcr |= MEN_Z135_PAR_ENA << MEN_Z135_PEN_SHIFT; + + if (termios->c_cflag & PARODD) + lcr |= MEN_Z135_PTY_ODD << MEN_Z135_PTY_SHIFT; + else + lcr |= MEN_Z135_PTY_EVN << MEN_Z135_PTY_SHIFT; + } else + lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT; + + termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */ + + conf_reg |= lcr << MEN_Z135_LCR_SHIFT; + iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG); + + uart_freq = ioread32(port->membase + MEN_Z135_UART_FREQ); + if (uart_freq == 0) + uart_freq = MEN_Z135_BASECLK; + + baud = uart_get_baud_rate(port, termios, old, 0, uart_freq / 16); + + spin_lock(&port->lock); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + bd_reg = uart_freq / (4 * baud); + iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG); + + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock(&port->lock); +} + +static const char *men_z135_type(struct uart_port *port) +{ + return KBUILD_MODNAME; +} + +static void men_z135_release_port(struct uart_port *port) +{ + iounmap(port->membase); + port->membase = NULL; + + release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE); +} + +static int men_z135_request_port(struct uart_port *port) +{ + int size = MEN_Z135_MEM_SIZE; + + if (!request_mem_region(port->mapbase, size, "men_z135_port")) + return -EBUSY; + + port->membase = ioremap(port->mapbase, MEN_Z135_MEM_SIZE); + if (port->membase == NULL) { + release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE); + return -ENOMEM; + } + + return 0; +} + +static void men_z135_config_port(struct uart_port *port, int type) +{ + port->type = PORT_MEN_Z135; + men_z135_request_port(port); +} + +static int men_z135_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + return -EINVAL; +} + +static struct uart_ops men_z135_ops = { + .tx_empty = men_z135_tx_empty, + .set_mctrl = men_z135_set_mctrl, + .get_mctrl = men_z135_get_mctrl, + .stop_tx = men_z135_stop_tx, + .start_tx = men_z135_start_tx, + .stop_rx = men_z135_stop_rx, + .enable_ms = men_z135_enable_ms, + .startup = men_z135_startup, + .shutdown = men_z135_shutdown, + .set_termios = men_z135_set_termios, + .type = men_z135_type, + .release_port = men_z135_release_port, + .request_port = men_z135_request_port, + .config_port = men_z135_config_port, + .verify_port = men_z135_verify_port, +}; + +static struct uart_driver men_z135_driver = { + .owner = THIS_MODULE, + .driver_name = KBUILD_MODNAME, + .dev_name = "ttyHSU", + .major = 0, + .minor = 0, + .nr = MEN_Z135_MAX_PORTS, +}; + +/** + * men_z135_probe() - Probe a z135 instance + * @mdev: The MCB device + * @id: The MCB device ID + * + * men_z135_probe does the basic setup of hardware resources and registers the + * new uart port to the tty layer. + */ +static int men_z135_probe(struct mcb_device *mdev, + const struct mcb_device_id *id) +{ + struct men_z135_port *uart; + struct resource *mem; + struct device *dev; + int err; + + dev = &mdev->dev; + + uart = devm_kzalloc(dev, sizeof(struct men_z135_port), GFP_KERNEL); + if (!uart) + return -ENOMEM; + + uart->rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!uart->rxbuf) + return -ENOMEM; + + mem = &mdev->mem; + + mcb_set_drvdata(mdev, uart); + + uart->port.uartclk = MEN_Z135_BASECLK * 16; + uart->port.fifosize = MEN_Z135_FIFO_SIZE; + uart->port.iotype = UPIO_MEM; + uart->port.ops = &men_z135_ops; + uart->port.irq = mcb_get_irq(mdev); + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + uart->port.line = line++; + uart->port.dev = dev; + uart->port.type = PORT_MEN_Z135; + uart->port.mapbase = mem->start; + uart->port.membase = NULL; + uart->mdev = mdev; + + spin_lock_init(&uart->port.lock); + spin_lock_init(&uart->lock); + + err = uart_add_one_port(&men_z135_driver, &uart->port); + if (err) + goto err; + + return 0; + +err: + free_page((unsigned long) uart->rxbuf); + dev_err(dev, "Failed to add UART: %d\n", err); + + return err; +} + +/** + * men_z135_remove() - Remove a z135 instance from the system + * + * @mdev: The MCB device + */ +static void men_z135_remove(struct mcb_device *mdev) +{ + struct men_z135_port *uart = mcb_get_drvdata(mdev); + + line--; + uart_remove_one_port(&men_z135_driver, &uart->port); + free_page((unsigned long) uart->rxbuf); +} + +static const struct mcb_device_id men_z135_ids[] = { + { .device = 0x87 }, +}; +MODULE_DEVICE_TABLE(mcb, men_z135_ids); + +static struct mcb_driver mcb_driver = { + .driver = { + .name = "z135-uart", + .owner = THIS_MODULE, + }, + .probe = men_z135_probe, + .remove = men_z135_remove, + .id_table = men_z135_ids, +}; + +/** + * men_z135_init() - Driver Registration Routine + * + * men_z135_init is the first routine called when the driver is loaded. All it + * does is register with the legacy MEN Chameleon subsystem. + */ +static int __init men_z135_init(void) +{ + int err; + + err = uart_register_driver(&men_z135_driver); + if (err) { + pr_err("Failed to register UART: %d\n", err); + return err; + } + + err = mcb_register_driver(&mcb_driver); + if (err) { + pr_err("Failed to register MCB driver: %d\n", err); + uart_unregister_driver(&men_z135_driver); + return err; + } + + return 0; +} +module_init(men_z135_init); + +/** + * men_z135_exit() - Driver Exit Routine + * + * men_z135_exit is called just before the driver is removed from memory. + */ +static void __exit men_z135_exit(void) +{ + mcb_unregister_driver(&mcb_driver); + uart_unregister_driver(&men_z135_driver); +} +module_exit(men_z135_exit); + +MODULE_AUTHOR("Johannes Thumshirn "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MEN 16z135 High Speed UART"); +MODULE_ALIAS("mcb:16z135"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 22aaf8ed7735..6e293622851a 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -238,4 +238,7 @@ /* Tilera TILE-Gx UART */ #define PORT_TILEGX 106 +/* MEN 16z135 UART */ +#define PORT_MEN_Z135 107 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3 From c1309040967e200d3ea6415ae54cf6a69d7ad996 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Mon, 31 Mar 2014 14:58:39 -0700 Subject: PCI: Use designated initialization in PCI_VDEVICE By using designated initialization in PCI_VDEVICE, like other similar macros, many "missing initializer" warnings that appear when compiling with W=2 can be silenced. Tested-by: Phil Schmitt Tested-by: Aaron Brown Tested-by: Kavindya Deegala Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4abe7f..a95aac7ad37f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -680,8 +680,8 @@ struct pci_driver { /** * PCI_VDEVICE - macro used to describe a specific pci device in short form - * @vendor: the vendor name - * @device: the 16 bit PCI Device ID + * @vend: the vendor name + * @dev: the 16 bit PCI Device ID * * This macro is used to create a struct pci_device_id that matches a * specific PCI device. The subvendor, and subdevice fields will be set @@ -689,9 +689,9 @@ struct pci_driver { * private data. */ -#define PCI_VDEVICE(vendor, device) \ - PCI_VENDOR_ID_##vendor, (device), \ - PCI_ANY_ID, PCI_ANY_ID, 0, 0 +#define PCI_VDEVICE(vend, dev) \ + .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0 /* these external functions are only available when PCI support is enabled */ #ifdef CONFIG_PCI -- cgit v1.2.3 From 9aac5887595b765b6f64b2af08b785e82e095b57 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 18 Apr 2014 17:19:55 -0500 Subject: tty/serial: add generic serial earlycon This introduces generic earlycon infrastructure for serial devices based on the 8250 earlycon. This allows for supporting earlycon option with other serial devices. The earlycon output is enabled at the time early_params are processed. Only architectures that have fixmap support or have functional ioremap when early_params are processed are supported. This is the same restriction that the 8250 driver had. Signed-off-by: Rob Herring Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 7 ++ drivers/tty/serial/Makefile | 2 + drivers/tty/serial/earlycon.c | 152 ++++++++++++++++++++++++++++++++++++++++++ include/linux/serial_core.h | 16 +++++ 4 files changed, 177 insertions(+) create mode 100644 drivers/tty/serial/earlycon.c (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 30530e47cdf0..9fb6028ad900 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -7,6 +7,13 @@ if TTY menu "Serial drivers" depends on HAS_IOMEM +config SERIAL_EARLYCON + bool + help + Support for early consoles with the earlycon parameter. This enables + the console before standard serial driver is probed. The console is + enabled when early_param is processed. + source "drivers/tty/serial/8250/Kconfig" comment "Non-8250 serial port support" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 5f2a3f493ab9..28048178f308 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -5,6 +5,8 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o +obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o + # These Sparc drivers have to appear before others such as 8250 # which share ttySx minor node space. Otherwise console device # names change and other unplesantries. diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c new file mode 100644 index 000000000000..73bf1e21aae0 --- /dev/null +++ b/drivers/tty/serial/earlycon.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Rob Herring + * + * Based on 8250 earlycon: + * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas + * + * 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 + +#ifdef CONFIG_FIX_EARLYCON_MEM +#include +#endif + +#include + +static struct console early_con = { + .name = "earlycon", + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +static struct earlycon_device early_console_dev = { + .con = &early_con, +}; + +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) +{ + void __iomem *base; +#ifdef CONFIG_FIX_EARLYCON_MEM + set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); + base += paddr & ~PAGE_MASK; +#else + base = ioremap(paddr, size); +#endif + if (!base) + pr_err("%s: Couldn't map 0x%llx\n", __func__, + (unsigned long long)paddr); + + return base; +} + +static int __init parse_options(struct earlycon_device *device, + char *options) +{ + struct uart_port *port = &device->port; + int mmio, mmio32, length, ret; + unsigned long addr; + + if (!options) + return -ENODEV; + + mmio = !strncmp(options, "mmio,", 5); + mmio32 = !strncmp(options, "mmio32,", 7); + if (mmio || mmio32) { + port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); + options += mmio ? 5 : 7; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->mapbase = addr; + if (mmio32) + port->regshift = 2; + } else if (!strncmp(options, "io,", 3)) { + port->iotype = UPIO_PORT; + options += 3; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->iobase = addr; + mmio = 0; + } else if (!strncmp(options, "0x", 2)) { + port->iotype = UPIO_MEM; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->mapbase = addr; + } else { + return -EINVAL; + } + + port->uartclk = BASE_BAUD * 16; + + options = strchr(options, ','); + if (options) { + options++; + ret = kstrtouint(options, 0, &device->baud); + if (ret) + return ret; + length = min(strcspn(options, " ") + 1, + (size_t)(sizeof(device->options))); + strlcpy(device->options, options, length); + } + + if (mmio || mmio32) + pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", + mmio32 ? "32" : "", + (unsigned long long)port->mapbase, + device->options); + else + pr_info("Early serial console at I/O port 0x%lx (options '%s')\n", + port->iobase, + device->options); + + return 0; +} + +int __init setup_earlycon(char *buf, const char *match, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + size_t len; + struct uart_port *port = &early_console_dev.port; + + if (!buf || !match || !setup) + return 0; + + len = strlen(match); + if (strncmp(buf, match, len)) + return 0; + if (buf[len] && (buf[len] != ',')) + return 0; + + buf += len + 1; + + err = parse_options(&early_console_dev, buf); + /* On parsing error, pass the options buf to the setup function */ + if (!err) + buf = NULL; + + if (port->mapbase) + port->membase = earlycon_map(port->mapbase, 64); + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, buf); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + register_console(early_console_dev.con); + return 0; +} diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f729be981da0..7a15b5b24c0b 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -285,6 +285,22 @@ static inline int uart_poll_timeout(struct uart_port *port) /* * Console helpers. */ +struct earlycon_device { + struct console *con; + struct uart_port port; + char options[16]; /* e.g., 115200n8 */ + unsigned int baud; +}; +int setup_earlycon(char *buf, const char *match, + int (*setup)(struct earlycon_device *, const char *)); + +#define EARLYCON_DECLARE(name, func) \ +static int __init name ## _setup_earlycon(char *buf) \ +{ \ + return setup_earlycon(buf, __stringify(name), func); \ +} \ +early_param("earlycon", name ## _setup_earlycon); + struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *c); void uart_parse_options(char *options, int *baud, int *parity, int *bits, -- cgit v1.2.3 From 38535201633077cbaf8b32886b5e3005b36c9024 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 25 Apr 2014 02:32:53 -0700 Subject: blk-mq: respect rq_affinity The blk-mq code is using it's own version of the I/O completion affinity tunables, which causes a few issues: - the rq_affinity sysfs file doesn't work for blk-mq devices, even if it still is present, thus breaking existing tuning setups. - the rq_affinity = 1 mode, which is the defauly for legacy request based drivers isn't implemented at all. - blk-mq drivers don't implement any completion affinity with the default flag settings. This patches removes the blk-mq ipi_redirect flag and sysfs file, as well as the internal BLK_MQ_F_SHOULD_IPI flag and replaces it with code that respects the queue-wide rq_affinity flags and also implements the rq_affinity = 1 mode. This means I/O completion affinity can now only be tuned block-queue wide instead of per context, which seems more sensible to me anyway. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-mq-sysfs.c | 42 ------------------------------------------ block/blk-mq.c | 8 ++++++-- block/blk-mq.h | 1 - include/linux/blk-mq.h | 1 - 4 files changed, 6 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/block/blk-mq-sysfs.c b/block/blk-mq-sysfs.c index 9176a6984857..8145b5b25b4b 100644 --- a/block/blk-mq-sysfs.c +++ b/block/blk-mq-sysfs.c @@ -203,42 +203,6 @@ static ssize_t blk_mq_hw_sysfs_rq_list_show(struct blk_mq_hw_ctx *hctx, return ret; } -static ssize_t blk_mq_hw_sysfs_ipi_show(struct blk_mq_hw_ctx *hctx, char *page) -{ - ssize_t ret; - - spin_lock(&hctx->lock); - ret = sprintf(page, "%u\n", !!(hctx->flags & BLK_MQ_F_SHOULD_IPI)); - spin_unlock(&hctx->lock); - - return ret; -} - -static ssize_t blk_mq_hw_sysfs_ipi_store(struct blk_mq_hw_ctx *hctx, - const char *page, size_t len) -{ - struct blk_mq_ctx *ctx; - unsigned long ret; - unsigned int i; - - if (kstrtoul(page, 10, &ret)) { - pr_err("blk-mq-sysfs: invalid input '%s'\n", page); - return -EINVAL; - } - - spin_lock(&hctx->lock); - if (ret) - hctx->flags |= BLK_MQ_F_SHOULD_IPI; - else - hctx->flags &= ~BLK_MQ_F_SHOULD_IPI; - spin_unlock(&hctx->lock); - - hctx_for_each_ctx(hctx, ctx, i) - ctx->ipi_redirect = !!ret; - - return len; -} - static ssize_t blk_mq_hw_sysfs_tags_show(struct blk_mq_hw_ctx *hctx, char *page) { return blk_mq_tag_sysfs_show(hctx->tags, page); @@ -307,11 +271,6 @@ static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_pending = { .attr = {.name = "pending", .mode = S_IRUGO }, .show = blk_mq_hw_sysfs_rq_list_show, }; -static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_ipi = { - .attr = {.name = "ipi_redirect", .mode = S_IRUGO | S_IWUSR}, - .show = blk_mq_hw_sysfs_ipi_show, - .store = blk_mq_hw_sysfs_ipi_store, -}; static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_tags = { .attr = {.name = "tags", .mode = S_IRUGO }, .show = blk_mq_hw_sysfs_tags_show, @@ -326,7 +285,6 @@ static struct attribute *default_hw_ctx_attrs[] = { &blk_mq_hw_sysfs_run.attr, &blk_mq_hw_sysfs_dispatched.attr, &blk_mq_hw_sysfs_pending.attr, - &blk_mq_hw_sysfs_ipi.attr, &blk_mq_hw_sysfs_tags.attr, &blk_mq_hw_sysfs_cpus.attr, NULL, diff --git a/block/blk-mq.c b/block/blk-mq.c index a84112c94e74..f2e92eb92803 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -326,15 +326,19 @@ static void __blk_mq_complete_request_remote(void *data) void __blk_mq_complete_request(struct request *rq) { struct blk_mq_ctx *ctx = rq->mq_ctx; + bool shared = false; int cpu; - if (!ctx->ipi_redirect) { + if (!test_bit(QUEUE_FLAG_SAME_COMP, &rq->q->queue_flags)) { rq->q->softirq_done_fn(rq); return; } cpu = get_cpu(); - if (cpu != ctx->cpu && cpu_online(ctx->cpu)) { + if (!test_bit(QUEUE_FLAG_SAME_FORCE, &rq->q->queue_flags)) + shared = cpus_share_cache(cpu, ctx->cpu); + + if (cpu != ctx->cpu && !shared && cpu_online(ctx->cpu)) { rq->csd.func = __blk_mq_complete_request_remote; rq->csd.info = rq; rq->csd.flags = 0; diff --git a/block/blk-mq.h b/block/blk-mq.h index b41a784de50d..1ae364ceaf8b 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -11,7 +11,6 @@ struct blk_mq_ctx { unsigned int cpu; unsigned int index_hw; - unsigned int ipi_redirect; /* incremented at dispatch time */ unsigned long rq_dispatched[2]; diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index ab469d525894..3b561d651a02 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -122,7 +122,6 @@ enum { BLK_MQ_F_SHOULD_MERGE = 1 << 0, BLK_MQ_F_SHOULD_SORT = 1 << 1, - BLK_MQ_F_SHOULD_IPI = 1 << 2, BLK_MQ_S_STOPPED = 0, -- cgit v1.2.3 From 65a124dd719d6e90591e4756bb04e1719489705e Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 9 Apr 2014 15:29:22 +0200 Subject: cfg80211: allow drivers to iterate over matching combinations The patch splits cfg80211_check_combinations() into an iterator function and a simple iteration user. This makes it possible for drivers to asses how many channels can use given iftype setup. This in turn can be used for future multi-interface/multi-channel channel switching. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 27 +++++++++++++++++++++++++++ net/wireless/util.c | 44 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9496fe5ea6b4..3dd2cb465540 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4715,6 +4715,33 @@ int cfg80211_check_combinations(struct wiphy *wiphy, const u8 radar_detect, const int iftype_num[NUM_NL80211_IFTYPES]); +/** + * cfg80211_iter_combinations - iterate over matching combinations + * + * @wiphy: the wiphy + * @num_different_channels: the number of different channels we want + * to use for verification + * @radar_detect: a bitmap where each bit corresponds to a channel + * width where radar detection is needed, as in the definition of + * &struct ieee80211_iface_combination.@radar_detect_widths + * @iftype_num: array with the numbers of interfaces of each interface + * type. The index is the interface type as specified in &enum + * nl80211_iftype. + * @iter: function to call for each matching combination + * @data: pointer to pass to iter function + * + * This function can be called by the driver to check what possible + * combinations it fits in at a given moment, e.g. for channel switching + * purposes. + */ +int cfg80211_iter_combinations(struct wiphy *wiphy, + const int num_different_channels, + const u8 radar_detect, + const int iftype_num[NUM_NL80211_IFTYPES], + void (*iter)(const struct ieee80211_iface_combination *c, + void *data), + void *data); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/util.c b/net/wireless/util.c index d032a31828f1..d8b599575a5e 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1263,10 +1263,13 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, return res; } -int cfg80211_check_combinations(struct wiphy *wiphy, - const int num_different_channels, - const u8 radar_detect, - const int iftype_num[NUM_NL80211_IFTYPES]) +int cfg80211_iter_combinations(struct wiphy *wiphy, + const int num_different_channels, + const u8 radar_detect, + const int iftype_num[NUM_NL80211_IFTYPES], + void (*iter)(const struct ieee80211_iface_combination *c, + void *data), + void *data) { int i, j, iftype; int num_interfaces = 0; @@ -1323,13 +1326,40 @@ int cfg80211_check_combinations(struct wiphy *wiphy, /* This combination covered all interface types and * supported the requested numbers, so we're good. */ - kfree(limits); - return 0; + + (*iter)(c, data); cont: kfree(limits); } - return -EBUSY; + return 0; +} +EXPORT_SYMBOL(cfg80211_iter_combinations); + +static void +cfg80211_iter_sum_ifcombs(const struct ieee80211_iface_combination *c, + void *data) +{ + int *num = data; + (*num)++; +} + +int cfg80211_check_combinations(struct wiphy *wiphy, + const int num_different_channels, + const u8 radar_detect, + const int iftype_num[NUM_NL80211_IFTYPES]) +{ + int err, num = 0; + + err = cfg80211_iter_combinations(wiphy, num_different_channels, + radar_detect, iftype_num, + cfg80211_iter_sum_ifcombs, &num); + if (err) + return err; + if (num == 0) + return -EBUSY; + + return 0; } EXPORT_SYMBOL(cfg80211_check_combinations); -- cgit v1.2.3 From 17d38fa8c20a9c3ec76943da46264ce657ac56d0 Mon Sep 17 00:00:00 2001 From: Marek Kwaczynski Date: Mon, 14 Apr 2014 11:27:21 +0200 Subject: mac80211: add option to generate CCMP IVs only for mgmt frames Some chips can encrypt managment frames in HW, but require generated IV in the frame. Add a key flag that allows us to achieve this. Signed-off-by: Marek Kwaczynski [use BIT(0) to fill that spot, fix indentation] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 16 ++++++++++------ net/mac80211/wpa.c | 5 ++++- 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a3044e124229..451c1bf00df9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1202,14 +1202,18 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); * fall back to software crypto. Note that this flag deals only with * RX, if your crypto engine can't deal with TX you can also set the * %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW. + * @IEEE80211_KEY_FLAG_GENERATE_IV_MGMT: This flag should be set by the + * driver for a CCMP key to indicate that is requires IV generation + * only for managment frames (MFP). */ enum ieee80211_key_flags { - IEEE80211_KEY_FLAG_GENERATE_IV = 1<<1, - IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2, - IEEE80211_KEY_FLAG_PAIRWISE = 1<<3, - IEEE80211_KEY_FLAG_SW_MGMT_TX = 1<<4, - IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5, - IEEE80211_KEY_FLAG_RX_MGMT = 1<<6, + IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0), + IEEE80211_KEY_FLAG_GENERATE_IV = BIT(1), + IEEE80211_KEY_FLAG_GENERATE_MMIC = BIT(2), + IEEE80211_KEY_FLAG_PAIRWISE = BIT(3), + IEEE80211_KEY_FLAG_SW_MGMT_TX = BIT(4), + IEEE80211_KEY_FLAG_PUT_IV_SPACE = BIT(5), + IEEE80211_KEY_FLAG_RX_MGMT = BIT(6), }; /** diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index b8600e3c29c8..9b3dcc201145 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -406,7 +406,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) if (info->control.hw_key && !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && - !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { + !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && + !((info->control.hw_key->flags & + IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) && + ieee80211_is_mgmt(hdr->frame_control))) { /* * hwaccel has no need for preallocated room for CCMP * header or MIC fields -- cgit v1.2.3 From ea077c1cea36a6b5ded1256dcd56c72ff2a22c62 Mon Sep 17 00:00:00 2001 From: Rostislav Lisovy Date: Tue, 15 Apr 2014 14:37:55 +0200 Subject: cfg80211: Add attributes describing prohibited channel bandwidth Since there are frequency bands (e.g. 5.9GHz) allowing channels with only 10 or 5 MHz bandwidth, this patch adds attributes that allow keeping track about this information. When channel attributes are reported to user-space, make sure to not break old tools, i.e. if the 'split wiphy dump' is enabled, report the extra attributes (if present) describing the bandwidth restrictions. If the 'split wiphy dump' is not enabled, completely omit those channels that have flags set to either IEEE80211_CHAN_NO_10MHZ or IEEE80211_CHAN_NO_20MHZ. Add the check for new bandwidth restriction flags in cfg80211_chandef_usable() to comply with the restrictions. Signed-off-by: Rostislav Lisovy Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 ++++++ include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/chan.c | 2 ++ net/wireless/nl80211.c | 13 +++++++++++++ 4 files changed, 27 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3dd2cb465540..c98cf08538b9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -111,6 +111,10 @@ enum ieee80211_band { * restrictions. * @IEEE80211_CHAN_INDOOR_ONLY: see %NL80211_FREQUENCY_ATTR_INDOOR_ONLY * @IEEE80211_CHAN_GO_CONCURRENT: see %NL80211_FREQUENCY_ATTR_GO_CONCURRENT + * @IEEE80211_CHAN_NO_20MHZ: 20 MHz bandwidth is not permitted + * on this channel. + * @IEEE80211_CHAN_NO_10MHZ: 10 MHz bandwidth is not permitted + * on this channel. * */ enum ieee80211_channel_flags { @@ -125,6 +129,8 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_NO_160MHZ = 1<<8, IEEE80211_CHAN_INDOOR_ONLY = 1<<9, IEEE80211_CHAN_GO_CONCURRENT = 1<<10, + IEEE80211_CHAN_NO_20MHZ = 1<<11, + IEEE80211_CHAN_NO_10MHZ = 1<<12, }; #define IEEE80211_CHAN_NO_HT40 \ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 513bfd7b2e5f..0592032ff160 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2358,6 +2358,10 @@ enum nl80211_band_attr { * connected to an AP with DFS and radar detection on the UNII band (it is * up to user-space, i.e., wpa_supplicant to perform the required * verifications) + * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed + * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -2384,6 +2388,8 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, NL80211_FREQUENCY_ATTR_INDOOR_ONLY, NL80211_FREQUENCY_ATTR_GO_CONCURRENT, + NL80211_FREQUENCY_ATTR_NO_20MHZ, + NL80211_FREQUENCY_ATTR_NO_10MHZ, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 2adf7b2eccbc..84d686e2dbd0 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -616,12 +616,14 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, width = 5; break; case NL80211_CHAN_WIDTH_10: + prohibited_flags |= IEEE80211_CHAN_NO_10MHZ; width = 10; break; case NL80211_CHAN_WIDTH_20: if (!ht_cap->ht_supported) return false; case NL80211_CHAN_WIDTH_20_NOHT: + prohibited_flags |= IEEE80211_CHAN_NO_20MHZ; width = 20; break; case NL80211_CHAN_WIDTH_40: diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fce423a4e96a..ca75f60041d2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -567,6 +567,13 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct ieee80211_channel *chan, bool large) { + /* Some channels must be completely excluded from the + * list to protect old user-space tools from breaking + */ + if (!large && chan->flags & + (IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ)) + return 0; + if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ, chan->center_freq)) goto nla_put_failure; @@ -620,6 +627,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, if ((chan->flags & IEEE80211_CHAN_GO_CONCURRENT) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_GO_CONCURRENT)) goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ)) + goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ)) + goto nla_put_failure; } if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, -- cgit v1.2.3 From dfeae619d781dee61666d5551b93ba3be755a86b Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Thu, 24 Apr 2014 20:56:06 -0400 Subject: serial: sc16is7xx The SC16IS7xx is a slave I2C-bus/SPI interface to a single-channel high performance UART. The SC16IS7xx's internal register set is backward-compatible with the widely used and widely popular 16C450. The SC16IS7xx also provides additional advanced features such as auto hardware and software flow control, automatic RS-485 support, and software reset. Signed-off-by: Jon Ringle Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 9 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sc16is7xx.c | 1279 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 1292 insertions(+) create mode 100644 drivers/tty/serial/sc16is7xx.c (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 988fa2b6243b..c3e2b328327c 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1178,6 +1178,15 @@ config SERIAL_SCCNXP_CONSOLE help Support for console on SCCNXP serial ports. +config SERIAL_SC16IS7XX + tristate "SC16IS7xx serial support" + select SERIAL_CORE + select REGMAP_I2C if I2C + help + This selects support for SC16IS7xx serial ports. + Supported ICs are SC16IS740, SC16IS741, SC16IS750, SC16IS752, + SC16IS760 and SC16IS762. + config SERIAL_BFIN_SPORT tristate "Blackfin SPORT emulate UART" depends on BLACKFIN diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 3a5be4633333..712732b43917 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o +obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c new file mode 100644 index 000000000000..ed139f5a6f74 --- /dev/null +++ b/drivers/tty/serial/sc16is7xx.c @@ -0,0 +1,1279 @@ +/* + * SC16IS7xx tty serial driver - Copyright (C) 2014 GridPoint + * Author: Jon Ringle + * + * Based on max310x.c, by Alexander Shiyan + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SC16IS7XX_NAME "sc16is7xx" + +/* SC16IS7XX register definitions */ +#define SC16IS7XX_RHR_REG (0x00) /* RX FIFO */ +#define SC16IS7XX_THR_REG (0x00) /* TX FIFO */ +#define SC16IS7XX_IER_REG (0x01) /* Interrupt enable */ +#define SC16IS7XX_IIR_REG (0x02) /* Interrupt Identification */ +#define SC16IS7XX_FCR_REG (0x02) /* FIFO control */ +#define SC16IS7XX_LCR_REG (0x03) /* Line Control */ +#define SC16IS7XX_MCR_REG (0x04) /* Modem Control */ +#define SC16IS7XX_LSR_REG (0x05) /* Line Status */ +#define SC16IS7XX_MSR_REG (0x06) /* Modem Status */ +#define SC16IS7XX_SPR_REG (0x07) /* Scratch Pad */ +#define SC16IS7XX_TXLVL_REG (0x08) /* TX FIFO level */ +#define SC16IS7XX_RXLVL_REG (0x09) /* RX FIFO level */ +#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction + * - only on 75x/76x + */ +#define SC16IS7XX_IOSTATE_REG (0x0b) /* I/O State + * - only on 75x/76x + */ +#define SC16IS7XX_IOINTENA_REG (0x0c) /* I/O Interrupt Enable + * - only on 75x/76x + */ +#define SC16IS7XX_IOCONTROL_REG (0x0e) /* I/O Control + * - only on 75x/76x + */ +#define SC16IS7XX_EFCR_REG (0x0f) /* Extra Features Control */ + +/* TCR/TLR Register set: Only if ((MCR[2] == 1) && (EFR[4] == 1)) */ +#define SC16IS7XX_TCR_REG (0x06) /* Transmit control */ +#define SC16IS7XX_TLR_REG (0x07) /* Trigger level */ + +/* Special Register set: Only if ((LCR[7] == 1) && (LCR != 0xBF)) */ +#define SC16IS7XX_DLL_REG (0x00) /* Divisor Latch Low */ +#define SC16IS7XX_DLH_REG (0x01) /* Divisor Latch High */ + +/* Enhanced Register set: Only if (LCR == 0xBF) */ +#define SC16IS7XX_EFR_REG (0x02) /* Enhanced Features */ +#define SC16IS7XX_XON1_REG (0x04) /* Xon1 word */ +#define SC16IS7XX_XON2_REG (0x05) /* Xon2 word */ +#define SC16IS7XX_XOFF1_REG (0x06) /* Xoff1 word */ +#define SC16IS7XX_XOFF2_REG (0x07) /* Xoff2 word */ + +/* IER register bits */ +#define SC16IS7XX_IER_RDI_BIT (1 << 0) /* Enable RX data interrupt */ +#define SC16IS7XX_IER_THRI_BIT (1 << 1) /* Enable TX holding register + * interrupt */ +#define SC16IS7XX_IER_RLSI_BIT (1 << 2) /* Enable RX line status + * interrupt */ +#define SC16IS7XX_IER_MSI_BIT (1 << 3) /* Enable Modem status + * interrupt */ + +/* IER register bits - write only if (EFR[4] == 1) */ +#define SC16IS7XX_IER_SLEEP_BIT (1 << 4) /* Enable Sleep mode */ +#define SC16IS7XX_IER_XOFFI_BIT (1 << 5) /* Enable Xoff interrupt */ +#define SC16IS7XX_IER_RTSI_BIT (1 << 6) /* Enable nRTS interrupt */ +#define SC16IS7XX_IER_CTSI_BIT (1 << 7) /* Enable nCTS interrupt */ + +/* FCR register bits */ +#define SC16IS7XX_FCR_FIFO_BIT (1 << 0) /* Enable FIFO */ +#define SC16IS7XX_FCR_RXRESET_BIT (1 << 1) /* Reset RX FIFO */ +#define SC16IS7XX_FCR_TXRESET_BIT (1 << 2) /* Reset TX FIFO */ +#define SC16IS7XX_FCR_RXLVLL_BIT (1 << 6) /* RX Trigger level LSB */ +#define SC16IS7XX_FCR_RXLVLH_BIT (1 << 7) /* RX Trigger level MSB */ + +/* FCR register bits - write only if (EFR[4] == 1) */ +#define SC16IS7XX_FCR_TXLVLL_BIT (1 << 4) /* TX Trigger level LSB */ +#define SC16IS7XX_FCR_TXLVLH_BIT (1 << 5) /* TX Trigger level MSB */ + +/* IIR register bits */ +#define SC16IS7XX_IIR_NO_INT_BIT (1 << 0) /* No interrupts pending */ +#define SC16IS7XX_IIR_ID_MASK 0x3e /* Mask for the interrupt ID */ +#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */ +#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */ +#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */ +#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */ +#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */ +#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state + * from active (LOW) + * to inactive (HIGH) + */ +/* LCR register bits */ +#define SC16IS7XX_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define SC16IS7XX_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define SC16IS7XX_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define SC16IS7XX_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define SC16IS7XX_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define SC16IS7XX_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define SC16IS7XX_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define SC16IS7XX_LCR_DLAB_BIT (1 << 7) /* Divisor Latch enable */ +#define SC16IS7XX_LCR_WORD_LEN_5 (0x00) +#define SC16IS7XX_LCR_WORD_LEN_6 (0x01) +#define SC16IS7XX_LCR_WORD_LEN_7 (0x02) +#define SC16IS7XX_LCR_WORD_LEN_8 (0x03) +#define SC16IS7XX_LCR_CONF_MODE_A SC16IS7XX_LCR_DLAB_BIT /* Special + * reg set */ +#define SC16IS7XX_LCR_CONF_MODE_B 0xBF /* Enhanced + * reg set */ + +/* MCR register bits */ +#define SC16IS7XX_MCR_DTR_BIT (1 << 0) /* DTR complement + * - only on 75x/76x + */ +#define SC16IS7XX_MCR_RTS_BIT (1 << 1) /* RTS complement */ +#define SC16IS7XX_MCR_TCRTLR_BIT (1 << 2) /* TCR/TLR register enable */ +#define SC16IS7XX_MCR_LOOP_BIT (1 << 4) /* Enable loopback test mode */ +#define SC16IS7XX_MCR_XONANY_BIT (1 << 5) /* Enable Xon Any + * - write enabled + * if (EFR[4] == 1) + */ +#define SC16IS7XX_MCR_IRDA_BIT (1 << 6) /* Enable IrDA mode + * - write enabled + * if (EFR[4] == 1) + */ +#define SC16IS7XX_MCR_CLKSEL_BIT (1 << 7) /* Divide clock by 4 + * - write enabled + * if (EFR[4] == 1) + */ + +/* LSR register bits */ +#define SC16IS7XX_LSR_DR_BIT (1 << 0) /* Receiver data ready */ +#define SC16IS7XX_LSR_OE_BIT (1 << 1) /* Overrun Error */ +#define SC16IS7XX_LSR_PE_BIT (1 << 2) /* Parity Error */ +#define SC16IS7XX_LSR_FE_BIT (1 << 3) /* Frame Error */ +#define SC16IS7XX_LSR_BI_BIT (1 << 4) /* Break Interrupt */ +#define SC16IS7XX_LSR_BRK_ERROR_MASK 0x1E /* BI, FE, PE, OE bits */ +#define SC16IS7XX_LSR_THRE_BIT (1 << 5) /* TX holding register empty */ +#define SC16IS7XX_LSR_TEMT_BIT (1 << 6) /* Transmitter empty */ +#define SC16IS7XX_LSR_FIFOE_BIT (1 << 7) /* Fifo Error */ + +/* MSR register bits */ +#define SC16IS7XX_MSR_DCTS_BIT (1 << 0) /* Delta CTS Clear To Send */ +#define SC16IS7XX_MSR_DDSR_BIT (1 << 1) /* Delta DSR Data Set Ready + * or (IO4) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DRI_BIT (1 << 2) /* Delta RI Ring Indicator + * or (IO7) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DCD_BIT (1 << 3) /* Delta CD Carrier Detect + * or (IO6) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_CTS_BIT (1 << 0) /* CTS */ +#define SC16IS7XX_MSR_DSR_BIT (1 << 1) /* DSR (IO4) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_RI_BIT (1 << 2) /* RI (IO7) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_CD_BIT (1 << 3) /* CD (IO6) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DELTA_MASK 0x0F /* Any of the delta bits! */ + +/* + * TCR register bits + * TCR trigger levels are available from 0 to 60 characters with a granularity + * of four. + * The programmer must program the TCR such that TCR[3:0] > TCR[7:4]. There is + * no built-in hardware check to make sure this condition is met. Also, the TCR + * must be programmed with this condition before auto RTS or software flow + * control is enabled to avoid spurious operation of the device. + */ +#define SC16IS7XX_TCR_RX_HALT(words) ((((words) / 4) & 0x0f) << 0) +#define SC16IS7XX_TCR_RX_RESUME(words) ((((words) / 4) & 0x0f) << 4) + +/* + * TLR register bits + * If TLR[3:0] or TLR[7:4] are logical 0, the selectable trigger levels via the + * FIFO Control Register (FCR) are used for the transmit and receive FIFO + * trigger levels. Trigger levels from 4 characters to 60 characters are + * available with a granularity of four. + * + * When the trigger level setting in TLR is zero, the SC16IS740/750/760 uses the + * trigger level setting defined in FCR. If TLR has non-zero trigger level value + * the trigger level defined in FCR is discarded. This applies to both transmit + * FIFO and receive FIFO trigger level setting. + * + * When TLR is used for RX trigger level control, FCR[7:6] should be left at the + * default state, that is, '00'. + */ +#define SC16IS7XX_TLR_TX_TRIGGER(words) ((((words) / 4) & 0x0f) << 0) +#define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4) + +/* IOControl register bits (Only 750/760) */ +#define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0) /* Enable input latching */ +#define SC16IS7XX_IOCONTROL_GPIO_BIT (1 << 1) /* Enable GPIO[7:4] */ +#define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3) /* Software Reset */ + +/* EFCR register bits */ +#define SC16IS7XX_EFCR_9BIT_MODE_BIT (1 << 0) /* Enable 9-bit or Multidrop + * mode (RS485) */ +#define SC16IS7XX_EFCR_RXDISABLE_BIT (1 << 1) /* Disable receiver */ +#define SC16IS7XX_EFCR_TXDISABLE_BIT (1 << 2) /* Disable transmitter */ +#define SC16IS7XX_EFCR_AUTO_RS485_BIT (1 << 4) /* Auto RS485 RTS direction */ +#define SC16IS7XX_EFCR_RTS_INVERT_BIT (1 << 5) /* RTS output inversion */ +#define SC16IS7XX_EFCR_IRDA_MODE_BIT (1 << 7) /* IrDA mode + * 0 = rate upto 115.2 kbit/s + * - Only 750/760 + * 1 = rate upto 1.152 Mbit/s + * - Only 760 + */ + +/* EFR register bits */ +#define SC16IS7XX_EFR_AUTORTS_BIT (1 << 6) /* Auto RTS flow ctrl enable */ +#define SC16IS7XX_EFR_AUTOCTS_BIT (1 << 7) /* Auto CTS flow ctrl enable */ +#define SC16IS7XX_EFR_XOFF2_DETECT_BIT (1 << 5) /* Enable Xoff2 detection */ +#define SC16IS7XX_EFR_ENABLE_BIT (1 << 4) /* Enable enhanced functions + * and writing to IER[7:4], + * FCR[5:4], MCR[7:5] + */ +#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3) /* SWFLOW bit 3 */ +#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2) /* SWFLOW bit 2 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no transmitter flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ +#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1) /* SWFLOW bit 2 */ +#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * 10 -> receiver compares + * XON1 and XOFF1 + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* Misc definitions */ +#define SC16IS7XX_FIFO_SIZE (64) +#define SC16IS7XX_REG_SHIFT 2 + +struct sc16is7xx_devtype { + char name[10]; + int nr_gpio; + int nr_uart; +}; + +struct sc16is7xx_one { + struct uart_port port; + struct work_struct tx_work; + struct work_struct md_work; + + struct serial_rs485 rs485; +}; + +struct sc16is7xx_port { + struct uart_driver uart; + struct sc16is7xx_devtype *devtype; + struct regmap *regmap; + struct mutex mutex; + struct clk *clk; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio; +#endif + struct sc16is7xx_one p[0]; +}; + +#define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e))) + +static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int val = 0; + + regmap_read(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, &val); + + return val; +} + +static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + regmap_write(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, val); +} + +static void sc16is7xx_port_update(struct uart_port *port, u8 reg, + u8 mask, u8 val) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + regmap_update_bits(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, + mask, val); +} + + +static void sc16is7xx_power(struct uart_port *port, int on) +{ + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_IER_SLEEP_BIT, + on ? 0 : SC16IS7XX_IER_SLEEP_BIT); +} + +static const struct sc16is7xx_devtype sc16is74x_devtype = { + .name = "SC16IS74X", + .nr_gpio = 0, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is750_devtype = { + .name = "SC16IS750", + .nr_gpio = 8, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is752_devtype = { + .name = "SC16IS752", + .nr_gpio = 8, + .nr_uart = 2, +}; + +static const struct sc16is7xx_devtype sc16is760_devtype = { + .name = "SC16IS760", + .nr_gpio = 8, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is762_devtype = { + .name = "SC16IS762", + .nr_gpio = 8, + .nr_uart = 2, +}; + +static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg >> SC16IS7XX_REG_SHIFT) { + case SC16IS7XX_RHR_REG: + case SC16IS7XX_IIR_REG: + case SC16IS7XX_LSR_REG: + case SC16IS7XX_MSR_REG: + case SC16IS7XX_TXLVL_REG: + case SC16IS7XX_RXLVL_REG: + case SC16IS7XX_IOSTATE_REG: + return true; + default: + break; + } + + return false; +} + +static bool sc16is7xx_regmap_precious(struct device *dev, unsigned int reg) +{ + switch (reg >> SC16IS7XX_REG_SHIFT) { + case SC16IS7XX_RHR_REG: + return true; + default: + break; + } + + return false; +} + +static int sc16is7xx_set_baud(struct uart_port *port, int baud) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + u8 lcr; + u8 prescaler = 0; + unsigned long clk = port->uartclk, div = clk / 16 / baud; + + if (div > 0xffff) { + prescaler = SC16IS7XX_MCR_CLKSEL_BIT; + div /= 4; + } + + lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); + + /* Open the LCR divisors for configuration */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + /* Enable enhanced features */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, + SC16IS7XX_EFR_ENABLE_BIT); + regcache_cache_bypass(s->regmap, false); + + /* Put LCR back to the normal mode */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_CLKSEL_BIT, + prescaler); + + /* Open the LCR divisors for configuration */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_A); + + /* Write the new divisor */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256); + sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256); + regcache_cache_bypass(s->regmap, false); + + /* Put LCR back to the normal mode */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + return DIV_ROUND_CLOSEST(clk / 16, div); +} + +static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, + unsigned int iir) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int lsr = 0, ch, flag, bytes_read, i; + u8 buf[port->fifosize]; + bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false; + + if (unlikely(rxlen >= port->fifosize)) { + dev_warn_ratelimited(port->dev, + "Port %i: Possible RX FIFO overrun: %d\n", + port->line, rxlen); + port->icount.buf_overrun++; + /* Ensure sanity of RX level */ + rxlen = port->fifosize; + } + + while (rxlen) { + /* Only read lsr if there are possible errors in FIFO */ + if (read_lsr) { + lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + if (!(lsr & SC16IS7XX_LSR_FIFOE_BIT)) + read_lsr = false; /* No errors left in FIFO */ + } else + lsr = 0; + + if (read_lsr) { + buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG); + bytes_read = 1; + } else { + regcache_cache_bypass(s->regmap, true); + regmap_raw_read(s->regmap, SC16IS7XX_RHR_REG, + buf, rxlen); + regcache_cache_bypass(s->regmap, false); + bytes_read = rxlen; + } + + lsr &= SC16IS7XX_LSR_BRK_ERROR_MASK; + + port->icount.rx++; + flag = TTY_NORMAL; + + if (unlikely(lsr)) { + if (lsr & SC16IS7XX_LSR_BI_BIT) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (lsr & SC16IS7XX_LSR_PE_BIT) + port->icount.parity++; + else if (lsr & SC16IS7XX_LSR_FE_BIT) + port->icount.frame++; + else if (lsr & SC16IS7XX_LSR_OE_BIT) + port->icount.overrun++; + + lsr &= port->read_status_mask; + if (lsr & SC16IS7XX_LSR_BI_BIT) + flag = TTY_BREAK; + else if (lsr & SC16IS7XX_LSR_PE_BIT) + flag = TTY_PARITY; + else if (lsr & SC16IS7XX_LSR_FE_BIT) + flag = TTY_FRAME; + else if (lsr & SC16IS7XX_LSR_OE_BIT) + flag = TTY_OVERRUN; + } + + for (i = 0; i < bytes_read; ++i) { + ch = buf[i]; + if (uart_handle_sysrq_char(port, ch)) + continue; + + if (lsr & port->ignore_status_mask) + continue; + + uart_insert_char(port, lsr, SC16IS7XX_LSR_OE_BIT, ch, + flag); + } + rxlen -= bytes_read; + } + + tty_flip_buffer_push(&port->state->port); +} + +static void sc16is7xx_handle_tx(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct circ_buf *xmit = &port->state->xmit; + unsigned int txlen, to_send, i; + u8 buf[port->fifosize]; + + if (unlikely(port->x_char)) { + sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + /* Get length of data pending in circular buffer */ + to_send = uart_circ_chars_pending(xmit); + if (likely(to_send)) { + /* Limit to size of TX FIFO */ + txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + to_send = (to_send > txlen) ? txlen : to_send; + + /* Add data to send */ + port->icount.tx += to_send; + + /* Convert to linear buffer */ + for (i = 0; i < to_send; ++i) { + buf[i] = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } + regcache_cache_bypass(s->regmap, true); + regmap_raw_write(s->regmap, SC16IS7XX_THR_REG, buf, to_send); + regcache_cache_bypass(s->regmap, false); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) +{ + struct uart_port *port = &s->p[portno].port; + + do { + unsigned int iir, msr, rxlen; + + iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); + if (iir & SC16IS7XX_IIR_NO_INT_BIT) + break; + + iir &= SC16IS7XX_IIR_ID_MASK; + + switch (iir) { + case SC16IS7XX_IIR_RDI_SRC: + case SC16IS7XX_IIR_RLSE_SRC: + case SC16IS7XX_IIR_RTOI_SRC: + case SC16IS7XX_IIR_XOFFI_SRC: + rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG); + if (rxlen) + sc16is7xx_handle_rx(port, rxlen, iir); + break; + + case SC16IS7XX_IIR_CTSRTS_SRC: + msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG); + uart_handle_cts_change(port, + !!(msr & SC16IS7XX_MSR_CTS_BIT)); + break; + case SC16IS7XX_IIR_THRI_SRC: + mutex_lock(&s->mutex); + sc16is7xx_handle_tx(port); + mutex_unlock(&s->mutex); + break; + default: + dev_err_ratelimited(port->dev, + "Port %i: Unexpected interrupt: %x", + port->line, iir); + break; + } + } while (1); +} + +static irqreturn_t sc16is7xx_ist(int irq, void *dev_id) +{ + struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; + int i; + + for (i = 0; i < s->uart.nr; ++i) + sc16is7xx_port_irq(s, i); + + return IRQ_HANDLED; +} + +static void sc16is7xx_wq_proc(struct work_struct *ws) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(ws, tx_work); + struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev); + + mutex_lock(&s->mutex); + sc16is7xx_handle_tx(&one->port); + mutex_unlock(&s->mutex); +} + +static void sc16is7xx_stop_tx(struct uart_port* port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + struct circ_buf *xmit = &one->port.state->xmit; + + /* handle rs485 */ + if (one->rs485.flags & SER_RS485_ENABLED) { + /* do nothing if current tx not yet completed */ + int lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + if (!(lsr & SC16IS7XX_LSR_TEMT_BIT)) + return; + + if (uart_circ_empty(xmit) && + (one->rs485.delay_rts_after_send > 0)) + mdelay(one->rs485.delay_rts_after_send); + } + + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_IER_THRI_BIT, + 0); +} + +static void sc16is7xx_stop_rx(struct uart_port* port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + one->port.read_status_mask &= ~SC16IS7XX_LSR_DR_BIT; + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_LSR_DR_BIT, + 0); +} + +static void sc16is7xx_start_tx(struct uart_port *port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + /* handle rs485 */ + if ((one->rs485.flags & SER_RS485_ENABLED) && + (one->rs485.delay_rts_before_send > 0)) { + mdelay(one->rs485.delay_rts_before_send); + } + + if (!work_pending(&one->tx_work)) + schedule_work(&one->tx_work); +} + +static unsigned int sc16is7xx_tx_empty(struct uart_port *port) +{ + unsigned int lvl, lsr; + + lvl = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + + return ((lsr & SC16IS7XX_LSR_THRE_BIT) && !lvl) ? TIOCSER_TEMT : 0; +} + +static unsigned int sc16is7xx_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return TIOCM_DSR | TIOCM_CAR; +} + +static void sc16is7xx_md_proc(struct work_struct *ws) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(ws, md_work); + + sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_LOOP_BIT, + (one->port.mctrl & TIOCM_LOOP) ? + SC16IS7XX_MCR_LOOP_BIT : 0); +} + +static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + schedule_work(&one->md_work); +} + +static void sc16is7xx_break_ctl(struct uart_port *port, int break_state) +{ + sc16is7xx_port_update(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_TXBREAK_BIT, + break_state ? SC16IS7XX_LCR_TXBREAK_BIT : 0); +} + +static void sc16is7xx_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int lcr, flow = 0; + int baud; + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + + /* Word size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr = SC16IS7XX_LCR_WORD_LEN_5; + break; + case CS6: + lcr = SC16IS7XX_LCR_WORD_LEN_6; + break; + case CS7: + lcr = SC16IS7XX_LCR_WORD_LEN_7; + break; + case CS8: + lcr = SC16IS7XX_LCR_WORD_LEN_8; + break; + default: + lcr = SC16IS7XX_LCR_WORD_LEN_8; + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + break; + } + + /* Parity */ + if (termios->c_cflag & PARENB) { + lcr |= SC16IS7XX_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + lcr |= SC16IS7XX_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= SC16IS7XX_LCR_STOPLEN_BIT; /* 2 stops */ + + /* Set read status mask */ + port->read_status_mask = SC16IS7XX_LSR_OE_BIT; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SC16IS7XX_LSR_PE_BIT | + SC16IS7XX_LSR_FE_BIT; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SC16IS7XX_LSR_BI_BIT; + + /* Set status ignore mask */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) + port->ignore_status_mask |= SC16IS7XX_LSR_BI_BIT; + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK; + + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + /* Configure flow control */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]); + sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]); + if (termios->c_cflag & CRTSCTS) + flow |= SC16IS7XX_EFR_AUTOCTS_BIT | + SC16IS7XX_EFR_AUTORTS_BIT; + if (termios->c_iflag & IXON) + flow |= SC16IS7XX_EFR_SWFLOW3_BIT; + if (termios->c_iflag & IXOFF) + flow |= SC16IS7XX_EFR_SWFLOW1_BIT; + + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, flow); + regcache_cache_bypass(s->regmap, false); + + /* Update LCR register */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + /* Get baud rate generator configuration */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 4 / 0xffff, + port->uartclk / 16); + + /* Setup baudrate generator */ + baud = sc16is7xx_set_baud(port, baud); + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); +} + +#if defined(TIOCSRS485) && defined(TIOCGRS485) +static void sc16is7xx_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + one->rs485 = *rs485; + + if (one->rs485.flags & SER_RS485_ENABLED) { + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_AUTO_RS485_BIT, + SC16IS7XX_EFCR_AUTO_RS485_BIT); + } else { + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_AUTO_RS485_BIT, + 0); + } +} +#endif + +static int sc16is7xx_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) +{ +#if defined(TIOCSRS485) && defined(TIOCGRS485) + struct serial_rs485 rs485; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485, (void __user *)arg, sizeof(rs485))) + return -EFAULT; + + sc16is7xx_config_rs485(port, &rs485); + return 0; + case TIOCGRS485: + if (copy_to_user((void __user *)arg, + &(to_sc16is7xx_one(port, port)->rs485), + sizeof(rs485))) + return -EFAULT; + return 0; + default: + break; + } +#endif + + return -ENOIOCTLCMD; +} + +static int sc16is7xx_startup(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int val; + + sc16is7xx_power(port, 1); + + /* Reset FIFOs*/ + val = SC16IS7XX_FCR_RXRESET_BIT | SC16IS7XX_FCR_TXRESET_BIT; + sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, val); + udelay(5); + sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, + SC16IS7XX_FCR_FIFO_BIT); + + /* Enable EFR */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + regcache_cache_bypass(s->regmap, true); + + /* Enable write access to enhanced features and internal clock div */ + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, + SC16IS7XX_EFR_ENABLE_BIT); + + /* Enable TCR/TLR */ + sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_TCRTLR_BIT, + SC16IS7XX_MCR_TCRTLR_BIT); + + /* Configure flow control levels */ + /* Flow control halt level 48, resume level 24 */ + sc16is7xx_port_write(port, SC16IS7XX_TCR_REG, + SC16IS7XX_TCR_RX_RESUME(24) | + SC16IS7XX_TCR_RX_HALT(48)); + + regcache_cache_bypass(s->regmap, false); + + /* Now, initialize the UART */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8); + + /* Enable the Rx and Tx FIFO */ + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT, + 0); + + /* Enable RX, TX, CTS change interrupts */ + val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT | + SC16IS7XX_IER_CTSI_BIT; + sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val); + + return 0; +} + +static void sc16is7xx_shutdown(struct uart_port *port) +{ + /* Disable all interrupts */ + sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0); + /* Disable TX/RX */ + sc16is7xx_port_write(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT); + + sc16is7xx_power(port, 0); +} + +static const char *sc16is7xx_type(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + return (port->type == PORT_SC16IS7XX) ? s->devtype->name : NULL; +} + +static int sc16is7xx_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +static void sc16is7xx_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SC16IS7XX; +} + +static int sc16is7xx_verify_port(struct uart_port *port, + struct serial_struct *s) +{ + if ((s->type != PORT_UNKNOWN) && (s->type != PORT_SC16IS7XX)) + return -EINVAL; + if (s->irq != port->irq) + return -EINVAL; + + return 0; +} + +static void sc16is7xx_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + sc16is7xx_power(port, (state == UART_PM_STATE_ON) ? 1 : 0); +} + +static void sc16is7xx_null_void(struct uart_port *port) +{ + /* Do nothing */ +} + +static const struct uart_ops sc16is7xx_ops = { + .tx_empty = sc16is7xx_tx_empty, + .set_mctrl = sc16is7xx_set_mctrl, + .get_mctrl = sc16is7xx_get_mctrl, + .stop_tx = sc16is7xx_stop_tx, + .start_tx = sc16is7xx_start_tx, + .stop_rx = sc16is7xx_stop_rx, + .enable_ms = sc16is7xx_null_void, + .break_ctl = sc16is7xx_break_ctl, + .startup = sc16is7xx_startup, + .shutdown = sc16is7xx_shutdown, + .set_termios = sc16is7xx_set_termios, + .type = sc16is7xx_type, + .request_port = sc16is7xx_request_port, + .release_port = sc16is7xx_null_void, + .config_port = sc16is7xx_config_port, + .verify_port = sc16is7xx_verify_port, + .ioctl = sc16is7xx_ioctl, + .pm = sc16is7xx_pm, +}; + +#ifdef CONFIG_GPIOLIB +static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int val; + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG); + + return !!(val & BIT(offset)); +} + +static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), + val ? BIT(offset) : 0); +} + +static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), 0); + + return 0; +} + +static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), + val ? BIT(offset) : 0); + sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), + BIT(offset)); + + return 0; +} +#endif + +static int sc16is7xx_probe(struct device *dev, + struct sc16is7xx_devtype *devtype, + struct regmap *regmap, int irq, unsigned long flags) +{ + unsigned long freq, *pfreq = dev_get_platdata(dev); + struct clk *clk; + int i, ret; + struct sc16is7xx_port *s; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Alloc port structure */ + s = devm_kzalloc(dev, sizeof(*s) + + sizeof(struct sc16is7xx_one) * devtype->nr_uart, + GFP_KERNEL); + if (!s) { + dev_err(dev, "Error allocating port structure\n"); + return -ENOMEM; + } + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + if (pfreq) + freq = *pfreq; + else + return PTR_ERR(clk); + } else { + freq = clk_get_rate(clk); + } + + s->regmap = regmap; + s->devtype = devtype; + dev_set_drvdata(dev, s); + + /* Register UART driver */ + s->uart.owner = THIS_MODULE; + s->uart.dev_name = "ttySC"; + s->uart.nr = devtype->nr_uart; + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(dev, "Registering UART driver failed\n"); + goto out_clk; + } + +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) { + /* Setup GPIO cotroller */ + s->gpio.owner = THIS_MODULE; + s->gpio.dev = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = sc16is7xx_gpio_direction_input; + s->gpio.get = sc16is7xx_gpio_get; + s->gpio.direction_output = sc16is7xx_gpio_direction_output; + s->gpio.set = sc16is7xx_gpio_set; + s->gpio.base = -1; + s->gpio.ngpio = devtype->nr_gpio; + s->gpio.can_sleep = 1; + ret = gpiochip_add(&s->gpio); + if (ret) + goto out_uart; + } +#endif + + mutex_init(&s->mutex); + + for (i = 0; i < devtype->nr_uart; ++i) { + /* Initialize port data */ + s->p[i].port.line = i; + s->p[i].port.dev = dev; + s->p[i].port.irq = irq; + s->p[i].port.type = PORT_SC16IS7XX; + s->p[i].port.fifosize = SC16IS7XX_FIFO_SIZE; + s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; + s->p[i].port.iotype = UPIO_PORT; + s->p[i].port.uartclk = freq; + s->p[i].port.ops = &sc16is7xx_ops; + /* Disable all interrupts */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_IER_REG, 0); + /* Disable TX/RX */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT); + /* Initialize queue for start TX */ + INIT_WORK(&s->p[i].tx_work, sc16is7xx_wq_proc); + /* Initialize queue for changing mode */ + INIT_WORK(&s->p[i].md_work, sc16is7xx_md_proc); + /* Register port */ + uart_add_one_port(&s->uart, &s->p[i].port); + /* Go to suspend mode */ + sc16is7xx_power(&s->p[i].port, 0); + } + + /* Setup interrupt */ + ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_ist, + IRQF_ONESHOT | flags, dev_name(dev), s); + if (!ret) + return 0; + + mutex_destroy(&s->mutex); + +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) + WARN_ON(gpiochip_remove(&s->gpio)); + +out_uart: +#endif + uart_unregister_driver(&s->uart); + +out_clk: + if (!IS_ERR(s->clk)) + clk_disable_unprepare(s->clk); + + return ret; +} + +static int sc16is7xx_remove(struct device *dev) +{ + struct sc16is7xx_port *s = dev_get_drvdata(dev); + int i, ret = 0; + +#ifdef CONFIG_GPIOLIB + if (s->devtype->nr_gpio) { + ret = gpiochip_remove(&s->gpio); + if (ret) + return ret; + } +#endif + + for (i = 0; i < s->uart.nr; i++) { + cancel_work_sync(&s->p[i].tx_work); + cancel_work_sync(&s->p[i].md_work); + uart_remove_one_port(&s->uart, &s->p[i].port); + sc16is7xx_power(&s->p[i].port, 0); + } + + mutex_destroy(&s->mutex); + uart_unregister_driver(&s->uart); + if (!IS_ERR(s->clk)) + clk_disable_unprepare(s->clk); + + return ret; +} + +static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { + { .compatible = "nxp,sc16is740", .data = &sc16is74x_devtype, }, + { .compatible = "nxp,sc16is741", .data = &sc16is74x_devtype, }, + { .compatible = "nxp,sc16is750", .data = &sc16is750_devtype, }, + { .compatible = "nxp,sc16is752", .data = &sc16is752_devtype, }, + { .compatible = "nxp,sc16is760", .data = &sc16is760_devtype, }, + { .compatible = "nxp,sc16is762", .data = &sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids); + +static struct regmap_config regcfg = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = sc16is7xx_regmap_volatile, + .precious_reg = sc16is7xx_regmap_precious, +}; + +#ifdef CONFIG_REGMAP_I2C +static int sc16is7xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct sc16is7xx_devtype *devtype; + unsigned long flags = 0; + struct regmap *regmap; + + if (i2c->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(sc16is7xx_dt_ids, &i2c->dev); + + devtype = (struct sc16is7xx_devtype *)of_id->data; + } else { + devtype = (struct sc16is7xx_devtype *)id->driver_data; + flags = IRQF_TRIGGER_FALLING; + } + + regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) | + (devtype->nr_uart - 1); + regmap = devm_regmap_init_i2c(i2c, ®cfg); + + return sc16is7xx_probe(&i2c->dev, devtype, regmap, i2c->irq, flags); +} + +static int sc16is7xx_i2c_remove(struct i2c_client *client) +{ + return sc16is7xx_remove(&client->dev); +} + +static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { + { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, + { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, + { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, + { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); + +static struct i2c_driver sc16is7xx_i2c_uart_driver = { + .driver = { + .name = SC16IS7XX_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sc16is7xx_dt_ids), + }, + .probe = sc16is7xx_i2c_probe, + .remove = sc16is7xx_i2c_remove, + .id_table = sc16is7xx_i2c_id_table, +}; +module_i2c_driver(sc16is7xx_i2c_uart_driver); +MODULE_ALIAS("i2c:sc16is7xx"); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jon Ringle "); +MODULE_DESCRIPTION("SC16IS7XX serial driver"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 6e293622851a..5820269aa132 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -241,4 +241,7 @@ /* MEN 16z135 UART */ #define PORT_MEN_Z135 107 +/* SC16IS74xx */ +#define PORT_SC16IS7XX 108 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3 From 7d568a8383bbb9c1f5167781075906acb2bb1550 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Apr 2014 11:07:30 -0400 Subject: kernfs: implement kernfs_root->supers list Currently, there's no way to find out which super_blocks are associated with a given kernfs_root. Let's implement it - the planned inotify extension to kernfs_notify() needs it. Make kernfs_super_info point back to the super_block and chain it at kernfs_root->supers. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 1 + fs/kernfs/kernfs-internal.h | 5 +++++ fs/kernfs/mount.c | 11 +++++++++++ include/linux/kernfs.h | 4 ++++ 4 files changed, 21 insertions(+) (limited to 'include') diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 78f3403300af..43aa97988c31 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -711,6 +711,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops, return ERR_PTR(-ENOMEM); ida_init(&root->ino_ida); + INIT_LIST_HEAD(&root->supers); kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO, KERNFS_DIR); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 8be13b2a079b..dc84a3ef9ca2 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -49,6 +49,8 @@ static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn) * mount.c */ struct kernfs_super_info { + struct super_block *sb; + /* * The root associated with this super_block. Each super_block is * identified by the root and ns it's associated with. @@ -62,6 +64,9 @@ struct kernfs_super_info { * an array and compare kernfs_node tag against every entry. */ const void *ns; + + /* anchored at kernfs_root->supers, protected by kernfs_mutex */ + struct list_head node; }; #define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_info)) diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 6a5f04ac8704..f25a7c0c3cdc 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -68,6 +68,7 @@ static int kernfs_fill_super(struct super_block *sb) struct inode *inode; struct dentry *root; + info->sb = sb; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = SYSFS_MAGIC; @@ -166,12 +167,18 @@ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, *new_sb_created = !sb->s_root; if (!sb->s_root) { + struct kernfs_super_info *info = kernfs_info(sb); + error = kernfs_fill_super(sb); if (error) { deactivate_locked_super(sb); return ERR_PTR(error); } sb->s_flags |= MS_ACTIVE; + + mutex_lock(&kernfs_mutex); + list_add(&info->node, &root->supers); + mutex_unlock(&kernfs_mutex); } return dget(sb->s_root); @@ -190,6 +197,10 @@ void kernfs_kill_sb(struct super_block *sb) struct kernfs_super_info *info = kernfs_info(sb); struct kernfs_node *root_kn = sb->s_root->d_fsdata; + mutex_lock(&kernfs_mutex); + list_del(&info->node); + mutex_unlock(&kernfs_mutex); + /* * Remove the superblock from fs_supers/s_instances * so we can't find it, before freeing kernfs_super_info. diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index b0122dc6f96a..589318b73e61 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -144,6 +144,10 @@ struct kernfs_root { /* private fields, do not use outside kernfs proper */ struct ida ino_ida; struct kernfs_syscall_ops *syscall_ops; + + /* list of kernfs_super_info of this root, protected by kernfs_mutex */ + struct list_head supers; + wait_queue_head_t deactivate_waitq; }; -- cgit v1.2.3 From 86d56134f1b67d0c18025ba5cade95c048ed528d Mon Sep 17 00:00:00 2001 From: Michael Marineau Date: Thu, 10 Apr 2014 14:09:31 -0700 Subject: kobject: Make support for uevent_helper optional. Support for uevent_helper, aka hotplug, is not required on many systems these days but it can still be enabled via sysfs or sysctl. Reported-by: Darren Shepherd Signed-off-by: Michael Marineau Signed-off-by: Greg Kroah-Hartman --- drivers/base/Kconfig | 17 +++++++++++------ include/linux/kobject.h | 2 ++ kernel/ksysfs.c | 5 ++++- kernel/sysctl.c | 4 ++-- lib/kobject_uevent.c | 6 ++++++ 5 files changed, 25 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 8fa8deab6449..4b7b4522b64f 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -1,10 +1,10 @@ menu "Generic Driver Options" -config UEVENT_HELPER_PATH - string "path to uevent helper" - default "" +config UEVENT_HELPER + bool "Support for uevent helper" + default y help - Path to uevent helper program forked by the kernel for + The uevent helper program is forked by the kernel for every uevent. Before the switch to the netlink-based uevent source, this was used to hook hotplug scripts into kernel device events. It @@ -15,8 +15,13 @@ config UEVENT_HELPER_PATH that it creates a high system load, or on smaller systems it is known to create out-of-memory situations during bootup. - To disable user space helper program execution at early boot - time specify an empty string here. This setting can be altered +config UEVENT_HELPER_PATH + string "path to uevent helper" + depends on UEVENT_HELPER + default "" + help + To disable user space helper program execution at by default + specify an empty string here. This setting can still be altered via /proc/sys/kernel/hotplug or via /sys/kernel/uevent_helper later at runtime. diff --git a/include/linux/kobject.h b/include/linux/kobject.h index f896a33e8341..2d61b909f414 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -32,8 +32,10 @@ #define UEVENT_NUM_ENVP 32 /* number of env pointers */ #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ +#ifdef CONFIG_UEVENT_HELPER /* path to the userspace helper executed on an event */ extern char uevent_helper[]; +#endif /* counter to tag the uevent, read only except for the kobject core */ extern u64 uevent_seqnum; diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index 2495a9b14ac8..6683ccef9fff 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -37,6 +37,7 @@ static ssize_t uevent_seqnum_show(struct kobject *kobj, } KERNEL_ATTR_RO(uevent_seqnum); +#ifdef CONFIG_UEVENT_HELPER /* uevent helper program, used during early boot */ static ssize_t uevent_helper_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -56,7 +57,7 @@ static ssize_t uevent_helper_store(struct kobject *kobj, return count; } KERNEL_ATTR_RW(uevent_helper); - +#endif #ifdef CONFIG_PROFILING static ssize_t profiling_show(struct kobject *kobj, @@ -189,7 +190,9 @@ EXPORT_SYMBOL_GPL(kernel_kobj); static struct attribute * kernel_attrs[] = { &fscaps_attr.attr, &uevent_seqnum_attr.attr, +#ifdef CONFIG_UEVENT_HELPER &uevent_helper_attr.attr, +#endif #ifdef CONFIG_PROFILING &profiling_attr.attr, #endif diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 74f5b580fe34..bc966a8ffc3e 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -643,7 +643,7 @@ static struct ctl_table kern_table[] = { .extra2 = &one, }, #endif - +#ifdef CONFIG_UEVENT_HELPER { .procname = "hotplug", .data = &uevent_helper, @@ -651,7 +651,7 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dostring, }, - +#endif #ifdef CONFIG_CHR_DEV_SG { .procname = "sg-big-buff", diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 4e3bd71bd949..9ebf9e20de53 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -29,7 +29,9 @@ u64 uevent_seqnum; +#ifdef CONFIG_UEVENT_HELPER char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; +#endif #ifdef CONFIG_NET struct uevent_sock { struct list_head list; @@ -109,6 +111,7 @@ static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data) } #endif +#ifdef CONFIG_UEVENT_HELPER static int kobj_usermode_filter(struct kobject *kobj) { const struct kobj_ns_type_operations *ops; @@ -147,6 +150,7 @@ static void cleanup_uevent_env(struct subprocess_info *info) { kfree(info->data); } +#endif /** * kobject_uevent_env - send an uevent with environmental data @@ -323,6 +327,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, #endif mutex_unlock(&uevent_sock_mutex); +#ifdef CONFIG_UEVENT_HELPER /* call uevent_helper, usually only enabled during early boot */ if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { struct subprocess_info *info; @@ -347,6 +352,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, env = NULL; /* freed by cleanup_uevent_env */ } } +#endif exit: kfree(devpath); -- cgit v1.2.3 From 842b597ee0a7e1aa5a3148164ffdba00ec17f614 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 25 Apr 2014 18:28:02 -0400 Subject: cgroup: implement cgroup.populated for the default hierarchy cgroup users often need a way to determine when a cgroup's subhierarchy becomes empty so that it can be cleaned up. cgroup currently provides release_agent for it; unfortunately, this mechanism is riddled with issues. * It delivers events by forking and execing a userland binary specified as the release_agent. This is a long deprecated method of notification delivery. It's extremely heavy, slow and cumbersome to integrate with larger infrastructure. * There is single monitoring point at the root. There's no way to delegate management of a subtree. * The event isn't recursive. It triggers when a cgroup doesn't have any tasks or child cgroups. Events for internal nodes trigger only after all children are removed. This again makes it impossible to delegate management of a subtree. * Events are filtered from the kernel side. "notify_on_release" file is used to subscribe to or suppress release event. This is unnecessarily complicated and probably done this way because event delivery itself was expensive. This patch implements interface file "cgroup.populated" which can be used to monitor whether the cgroup's subhierarchy has tasks in it or not. Its value is 0 if there is no task in the cgroup and its descendants; otherwise, 1, and kernfs_notify() notificaiton is triggers when the value changes, which can be monitored through poll and [di]notify. This is a lot ligther and simpler and trivially allows delegating management of subhierarchy - subhierarchy monitoring can block further propgation simply by putting itself or another process in the root of the subhierarchy and monitor events that it's interested in from there without interfering with monitoring higher in the tree. v2: Patch description updated as per Serge. v3: "cgroup.subtree_populated" renamed to "cgroup.populated". The subtree_ prefix was a bit confusing because "cgroup.subtree_control" uses it to denote the tree rooted at the cgroup sans the cgroup itself while the populated state includes the cgroup itself. Signed-off-by: Tejun Heo Acked-by: Serge Hallyn Acked-by: Li Zefan Cc: Lennart Poettering --- include/linux/cgroup.h | 15 ++++++++++++ kernel/cgroup.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index ada239253ec7..4b38e2d6110d 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -154,6 +154,14 @@ struct cgroup { /* the number of attached css's */ int nr_css; + /* + * If this cgroup contains any tasks, it contributes one to + * populated_cnt. All children with non-zero popuplated_cnt of + * their own contribute one. The count is zero iff there's no task + * in this cgroup or its subtree. + */ + int populated_cnt; + atomic_t refcnt; /* @@ -166,6 +174,7 @@ struct cgroup { struct cgroup *parent; /* my parent */ struct kernfs_node *kn; /* cgroup kernfs entry */ struct kernfs_node *control_kn; /* kn for "cgroup.subtree_control" */ + struct kernfs_node *populated_kn; /* kn for "cgroup.subtree_populated" */ /* * Monotonically increasing unique serial number which defines a @@ -264,6 +273,12 @@ enum { * * - "cgroup.clone_children" is removed. * + * - "cgroup.subtree_populated" is available. Its value is 0 if + * the cgroup and its descendants contain no task; otherwise, 1. + * The file also generates kernfs notification which can be + * monitored through poll and [di]notify when the value of the + * file changes. + * * - If mount is requested with sane_behavior but without any * subsystem, the default unified hierarchy is mounted. * diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 809dd903ceb8..0f986f7afee4 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -411,6 +411,43 @@ static struct css_set init_css_set = { static int css_set_count = 1; /* 1 for init_css_set */ +/** + * cgroup_update_populated - updated populated count of a cgroup + * @cgrp: the target cgroup + * @populated: inc or dec populated count + * + * @cgrp is either getting the first task (css_set) or losing the last. + * Update @cgrp->populated_cnt accordingly. The count is propagated + * towards root so that a given cgroup's populated_cnt is zero iff the + * cgroup and all its descendants are empty. + * + * @cgrp's interface file "cgroup.populated" is zero if + * @cgrp->populated_cnt is zero and 1 otherwise. When @cgrp->populated_cnt + * changes from or to zero, userland is notified that the content of the + * interface file has changed. This can be used to detect when @cgrp and + * its descendants become populated or empty. + */ +static void cgroup_update_populated(struct cgroup *cgrp, bool populated) +{ + lockdep_assert_held(&css_set_rwsem); + + do { + bool trigger; + + if (populated) + trigger = !cgrp->populated_cnt++; + else + trigger = !--cgrp->populated_cnt; + + if (!trigger) + break; + + if (cgrp->populated_kn) + kernfs_notify(cgrp->populated_kn); + cgrp = cgrp->parent; + } while (cgrp); +} + /* * hash table for cgroup groups. This improves the performance to find * an existing css_set. This hash doesn't (currently) take into @@ -456,10 +493,13 @@ static void put_css_set_locked(struct css_set *cset, bool taskexit) list_del(&link->cgrp_link); /* @cgrp can't go away while we're holding css_set_rwsem */ - if (list_empty(&cgrp->cset_links) && notify_on_release(cgrp)) { - if (taskexit) - set_bit(CGRP_RELEASABLE, &cgrp->flags); - check_for_release(cgrp); + if (list_empty(&cgrp->cset_links)) { + cgroup_update_populated(cgrp, false); + if (notify_on_release(cgrp)) { + if (taskexit) + set_bit(CGRP_RELEASABLE, &cgrp->flags); + check_for_release(cgrp); + } } kfree(link); @@ -668,7 +708,11 @@ static void link_css_set(struct list_head *tmp_links, struct css_set *cset, link = list_first_entry(tmp_links, struct cgrp_cset_link, cset_link); link->cset = cset; link->cgrp = cgrp; + + if (list_empty(&cgrp->cset_links)) + cgroup_update_populated(cgrp, true); list_move(&link->cset_link, &cgrp->cset_links); + /* * Always add links to the tail of the list so that the list * is sorted by order of hierarchy creation @@ -2643,6 +2687,12 @@ err_undo_css: goto out_unlock; } +static int cgroup_populated_show(struct seq_file *seq, void *v) +{ + seq_printf(seq, "%d\n", (bool)seq_css(seq)->cgroup->populated_cnt); + return 0; +} + static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { @@ -2809,6 +2859,8 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft) if (cft->seq_show == cgroup_subtree_control_show) cgrp->control_kn = kn; + else if (cft->seq_show == cgroup_populated_show) + cgrp->populated_kn = kn; return 0; } @@ -3918,6 +3970,11 @@ static struct cftype cgroup_base_files[] = { .seq_show = cgroup_subtree_control_show, .write_string = cgroup_subtree_control_write, }, + { + .name = "cgroup.populated", + .flags = CFTYPE_ONLY_ON_DFL | CFTYPE_NOT_ON_ROOT, + .seq_show = cgroup_populated_show, + }, /* * Historical crazy stuff. These don't have "cgroup." prefix and -- cgit v1.2.3 From ea7e586bdd331fd6fba2b6f9fd3777928c2814d8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 13 Apr 2014 20:08:00 +0100 Subject: iio: st_sensors: move regulator retrieveal to core Currently the pressure sensor has code to retrieve and enable two regulators for Vdd and Vdd IO, but actually these voltage inputs are found on all of these ST sensors, so move the regulator handling to the core and make sure all the ST sensors call these functions on probe() and remove() to enable/disable power. Here also mover over to obtaining the regulator from the *parent* device of the IIO device, as the IIO device is created on-the-fly in this very subsystem it very unlikely evert have any regulators attached to it whatsoever. It is much more likely that the parent is a platform device, possibly instantiated from a device tree, which in turn have Vdd and Vdd IO supplied assigned to it. Cc: Lee Jones Cc: Denis CIOCCA Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 4 +++ drivers/iio/common/st_sensors/st_sensors_core.c | 37 +++++++++++++++++++++++ drivers/iio/gyro/st_gyro_core.c | 4 +++ drivers/iio/magnetometer/st_magn_core.c | 4 +++ drivers/iio/pressure/st_pressure_core.c | 39 ++----------------------- include/linux/iio/common/st_sensors.h | 4 +++ 6 files changed, 55 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 4e06fcf5b891..a2abf7c2ce3b 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -459,6 +459,8 @@ int st_accel_common_probe(struct iio_dev *indio_dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &accel_info; + st_sensors_power_enable(indio_dev); + err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_accel_sensors), st_accel_sensors); if (err < 0) @@ -515,6 +517,8 @@ void st_accel_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); + st_sensors_power_disable(indio_dev); + iio_device_unregister(indio_dev); if (adata->get_irq_data_ready(indio_dev) > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 7ba1ef270213..e8b932fed70e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -198,6 +199,42 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) } EXPORT_SYMBOL(st_sensors_set_axis_enable); +void st_sensors_power_enable(struct iio_dev *indio_dev) +{ + struct st_sensor_data *pdata = iio_priv(indio_dev); + int err; + + /* Regulators not mandatory, but if requested we should enable them. */ + pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd"); + if (!IS_ERR(pdata->vdd)) { + err = regulator_enable(pdata->vdd); + if (err != 0) + dev_warn(&indio_dev->dev, + "Failed to enable specified Vdd supply\n"); + } + + pdata->vdd_io = devm_regulator_get_optional(indio_dev->dev.parent, "vddio"); + if (!IS_ERR(pdata->vdd_io)) { + err = regulator_enable(pdata->vdd_io); + if (err != 0) + dev_warn(&indio_dev->dev, + "Failed to enable specified Vdd_IO supply\n"); + } +} +EXPORT_SYMBOL(st_sensors_power_enable); + +void st_sensors_power_disable(struct iio_dev *indio_dev) +{ + struct st_sensor_data *pdata = iio_priv(indio_dev); + + if (!IS_ERR(pdata->vdd)) + regulator_disable(pdata->vdd); + + if (!IS_ERR(pdata->vdd_io)) + regulator_disable(pdata->vdd_io); +} +EXPORT_SYMBOL(st_sensors_power_disable); + static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, struct st_sensors_platform_data *pdata) { diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index bc71f4d1e2ce..ed74a9069989 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -311,6 +311,8 @@ int st_gyro_common_probe(struct iio_dev *indio_dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &gyro_info; + st_sensors_power_enable(indio_dev); + err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors); if (err < 0) @@ -363,6 +365,8 @@ void st_gyro_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *gdata = iio_priv(indio_dev); + st_sensors_power_disable(indio_dev); + iio_device_unregister(indio_dev); if (gdata->get_irq_data_ready(indio_dev) > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 8e33a7682d33..240a21dd0c61 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -355,6 +355,8 @@ int st_magn_common_probe(struct iio_dev *indio_dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &magn_info; + st_sensors_power_enable(indio_dev); + err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_magn_sensors), st_magn_sensors); if (err < 0) @@ -406,6 +408,8 @@ void st_magn_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); + st_sensors_power_disable(indio_dev); + iio_device_unregister(indio_dev); if (mdata->get_irq_data_ready(indio_dev) > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 013becbe8f47..cd7e01f3a93b 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -387,40 +386,6 @@ static const struct iio_trigger_ops st_press_trigger_ops = { #define ST_PRESS_TRIGGER_OPS NULL #endif -static void st_press_power_enable(struct iio_dev *indio_dev) -{ - struct st_sensor_data *pdata = iio_priv(indio_dev); - int err; - - /* Regulators not mandatory, but if requested we should enable them. */ - pdata->vdd = devm_regulator_get_optional(&indio_dev->dev, "vdd"); - if (!IS_ERR(pdata->vdd)) { - err = regulator_enable(pdata->vdd); - if (err != 0) - dev_warn(&indio_dev->dev, - "Failed to enable specified Vdd supply\n"); - } - - pdata->vdd_io = devm_regulator_get_optional(&indio_dev->dev, "vddio"); - if (!IS_ERR(pdata->vdd_io)) { - err = regulator_enable(pdata->vdd_io); - if (err != 0) - dev_warn(&indio_dev->dev, - "Failed to enable specified Vdd_IO supply\n"); - } -} - -static void st_press_power_disable(struct iio_dev *indio_dev) -{ - struct st_sensor_data *pdata = iio_priv(indio_dev); - - if (!IS_ERR(pdata->vdd)) - regulator_disable(pdata->vdd); - - if (!IS_ERR(pdata->vdd_io)) - regulator_disable(pdata->vdd_io); -} - int st_press_common_probe(struct iio_dev *indio_dev, struct st_sensors_platform_data *plat_data) { @@ -431,7 +396,7 @@ int st_press_common_probe(struct iio_dev *indio_dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &press_info; - st_press_power_enable(indio_dev); + st_sensors_power_enable(indio_dev); err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_press_sensors), @@ -493,7 +458,7 @@ void st_press_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); - st_press_power_disable(indio_dev); + st_sensors_power_disable(indio_dev); iio_device_unregister(indio_dev); if (pdata->get_irq_data_ready(indio_dev) > 0) diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 3c005eb3a0a4..96f51f0e0096 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -269,6 +269,10 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable); int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable); +void st_sensors_power_enable(struct iio_dev *indio_dev); + +void st_sensors_power_disable(struct iio_dev *indio_dev); + int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr); int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable); -- cgit v1.2.3 From a89778d8baf19cd7e728d81121a294a06cedaad1 Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Thu, 24 Apr 2014 16:26:46 +0200 Subject: tipc: add support for link state subscriptions When links are established over a bearer plane, we create a node local publication containing information about the peer node and bearer plane. This allows TIPC applications to use the standard TIPC topology server subscription mechanism to get notifications when a link goes up or down. Signed-off-by: Erik Hugne Reviewed-by: Ying Xue Reviewed-by: Jon Maloy Signed-off-by: David S. Miller --- include/uapi/linux/tipc.h | 1 + net/tipc/node.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h index 852373d27dbb..53cd7902d34e 100644 --- a/include/uapi/linux/tipc.h +++ b/include/uapi/linux/tipc.h @@ -87,6 +87,7 @@ static inline unsigned int tipc_node(__u32 addr) #define TIPC_CFG_SRV 0 /* configuration service name type */ #define TIPC_TOP_SRV 1 /* topology service name type */ +#define TIPC_LINK_STATE 2 /* link state name type */ #define TIPC_RESERVED_TYPES 64 /* lowest user-publishable name type */ /* diff --git a/net/tipc/node.c b/net/tipc/node.c index be90115cda1a..3b86a74cb31f 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -144,9 +144,11 @@ void tipc_node_stop(void) void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr) { struct tipc_link **active = &n_ptr->active_links[0]; + u32 addr = n_ptr->addr; n_ptr->working_links++; - + tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, TIPC_NODE_SCOPE, + l_ptr->bearer_id, addr); pr_info("Established link <%s> on network plane %c\n", l_ptr->name, l_ptr->net_plane); @@ -203,8 +205,10 @@ static void node_select_active_links(struct tipc_node *n_ptr) void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr) { struct tipc_link **active; + u32 addr = n_ptr->addr; n_ptr->working_links--; + tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, l_ptr->bearer_id, addr); if (!tipc_link_is_active(l_ptr)) { pr_info("Lost standby link <%s> on network plane %c\n", -- cgit v1.2.3 From 78acb1f9b898e85fa2c1e28e700b54b66b288e8d Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Thu, 24 Apr 2014 16:26:47 +0200 Subject: tipc: add ioctl to fetch link names We add a new ioctl for AF_TIPC that can be used to fetch the logical name for a link to a remote node on a given bearer. This should be used in combination with link state subscriptions. The logical name size limit definitions are moved to tipc.h, as they are now also needed by the new ioctl. Signed-off-by: Erik Hugne Reviewed-by: Ying Xue Reviewed-by: Jon Maloy Signed-off-by: David S. Miller --- include/uapi/linux/tipc.h | 22 ++++++++++++++++++++++ include/uapi/linux/tipc_config.h | 10 +--------- net/tipc/node.c | 27 +++++++++++++++++++++++++++ net/tipc/node.h | 1 + net/tipc/socket.c | 29 ++++++++++++++++++++++++++--- 5 files changed, 77 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h index 53cd7902d34e..6f71b9b41595 100644 --- a/include/uapi/linux/tipc.h +++ b/include/uapi/linux/tipc.h @@ -38,6 +38,7 @@ #define _LINUX_TIPC_H_ #include +#include /* * TIPC addressing primitives @@ -207,4 +208,25 @@ struct sockaddr_tipc { #define TIPC_NODE_RECVQ_DEPTH 131 /* Default: none (read only) */ #define TIPC_SOCK_RECVQ_DEPTH 132 /* Default: none (read only) */ +/* + * Maximum sizes of TIPC bearer-related names (including terminating NULL) + * The string formatting for each name element is: + * media: media + * interface: media:interface name + * link: Z.C.N:interface-Z.C.N:interface + * + */ + +#define TIPC_MAX_MEDIA_NAME 16 +#define TIPC_MAX_IF_NAME 16 +#define TIPC_MAX_BEARER_NAME 32 +#define TIPC_MAX_LINK_NAME 60 + +#define SIOCGETLINKNAME SIOCPROTOPRIVATE + +struct tipc_sioc_ln_req { + __u32 peer; + __u32 bearer_id; + char linkname[TIPC_MAX_LINK_NAME]; +}; #endif diff --git a/include/uapi/linux/tipc_config.h b/include/uapi/linux/tipc_config.h index 6b0bff09b3a7..41a76acbb305 100644 --- a/include/uapi/linux/tipc_config.h +++ b/include/uapi/linux/tipc_config.h @@ -39,6 +39,7 @@ #include #include +#include #include #ifndef __KERNEL__ @@ -154,15 +155,6 @@ #define TIPC_TLV_NAME_TBL_QUERY 25 /* struct tipc_name_table_query */ #define TIPC_TLV_PORT_REF 26 /* 32-bit port reference */ -/* - * Maximum sizes of TIPC bearer-related names (including terminating NUL) - */ - -#define TIPC_MAX_MEDIA_NAME 16 /* format = media */ -#define TIPC_MAX_IF_NAME 16 /* format = interface */ -#define TIPC_MAX_BEARER_NAME 32 /* format = media:interface */ -#define TIPC_MAX_LINK_NAME 60 /* format = Z.C.N:interface-Z.C.N:interface */ - /* * Link priority limits (min, default, max, media default) */ diff --git a/net/tipc/node.c b/net/tipc/node.c index 3b86a74cb31f..1f938f3dba4b 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -438,3 +438,30 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space) rcu_read_unlock(); return buf; } + +/** + * tipc_node_get_linkname - get the name of a link + * + * @bearer_id: id of the bearer + * @node: peer node address + * @linkname: link name output buffer + * + * Returns 0 on success + */ +int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len) +{ + struct tipc_link *link; + struct tipc_node *node = tipc_node_find(addr); + + if ((bearer_id > MAX_BEARERS) || !node) + return -EINVAL; + tipc_node_lock(node); + link = node->links[bearer_id]; + if (link) { + strncpy(linkname, link->name, len); + tipc_node_unlock(node); + return 0; + } + tipc_node_unlock(node); + return -EINVAL; +} diff --git a/net/tipc/node.h b/net/tipc/node.h index 7cbb8cec1a93..411b19114064 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -118,6 +118,7 @@ int tipc_node_active_links(struct tipc_node *n_ptr); int tipc_node_is_up(struct tipc_node *n_ptr); struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space); struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space); +int tipc_node_get_linkname(u32 bearer_id, u32 node, char *linkname, size_t len); static inline void tipc_node_lock(struct tipc_node *n_ptr) { diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 3c0256962f7d..3f9912f87d0d 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -36,6 +36,7 @@ #include "core.h" #include "port.h" +#include "node.h" #include @@ -1905,6 +1906,28 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, return put_user(sizeof(value), ol); } +int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg) +{ + struct tipc_sioc_ln_req lnr; + void __user *argp = (void __user *)arg; + + switch (cmd) { + case SIOCGETLINKNAME: + if (copy_from_user(&lnr, argp, sizeof(lnr))) + return -EFAULT; + if (!tipc_node_get_linkname(lnr.bearer_id, lnr.peer, + lnr.linkname, TIPC_MAX_LINK_NAME)) { + if (copy_to_user(argp, &lnr, sizeof(lnr))) + return -EFAULT; + return 0; + } + return -EADDRNOTAVAIL; + break; + default: + return -ENOIOCTLCMD; + } +} + /* Protocol switches for the various types of TIPC sockets */ static const struct proto_ops msg_ops = { @@ -1917,7 +1940,7 @@ static const struct proto_ops msg_ops = { .accept = sock_no_accept, .getname = tipc_getname, .poll = tipc_poll, - .ioctl = sock_no_ioctl, + .ioctl = tipc_ioctl, .listen = sock_no_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, @@ -1938,7 +1961,7 @@ static const struct proto_ops packet_ops = { .accept = tipc_accept, .getname = tipc_getname, .poll = tipc_poll, - .ioctl = sock_no_ioctl, + .ioctl = tipc_ioctl, .listen = tipc_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, @@ -1959,7 +1982,7 @@ static const struct proto_ops stream_ops = { .accept = tipc_accept, .getname = tipc_getname, .poll = tipc_poll, - .ioctl = sock_no_ioctl, + .ioctl = tipc_ioctl, .listen = tipc_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, -- cgit v1.2.3 From 4af619ae2ccf47a2b3a108e1926736484721370f Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 24 Apr 2014 19:09:05 +0200 Subject: at86rf230: use irq_get_trigger_type This patch removes the platform data for the irq_type. We use instead the irq_get_trigger_type function to get these flags which should already configured by the interrupt controller. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- drivers/net/ieee802154/at86rf230.c | 28 ++++++++-------------------- include/linux/spi/at86rf230.h | 14 -------------- 2 files changed, 8 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index e36f194673a4..17b9c9aea9be 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -970,8 +971,7 @@ static int at86rf230_irq_polarity(struct at86rf230_local *lp, int pol) static int at86rf230_hw_init(struct at86rf230_local *lp) { - struct at86rf230_platform_data *pdata = lp->spi->dev.platform_data; - int rc, irq_pol; + int rc, irq_pol, irq_type; u8 status; u8 csma_seed[2]; @@ -983,8 +983,9 @@ static int at86rf230_hw_init(struct at86rf230_local *lp) if (rc) return rc; + irq_type = irq_get_trigger_type(lp->spi->irq); /* configure irq polarity, defaults to high active */ - if (pdata->irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) + if (irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) irq_pol = IRQ_ACTIVE_LOW; else irq_pol = IRQ_ACTIVE_HIGH; @@ -1032,7 +1033,6 @@ static struct at86rf230_platform_data * at86rf230_get_pdata(struct spi_device *spi) { struct at86rf230_platform_data *pdata; - const char *irq_type; if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) return spi->dev.platform_data; @@ -1044,19 +1044,6 @@ at86rf230_get_pdata(struct spi_device *spi) pdata->rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); pdata->slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); - pdata->irq_type = IRQF_TRIGGER_RISING; - of_property_read_string(spi->dev.of_node, "irq-type", &irq_type); - if (!strcmp(irq_type, "level-high")) - pdata->irq_type = IRQF_TRIGGER_HIGH; - else if (!strcmp(irq_type, "level-low")) - pdata->irq_type = IRQF_TRIGGER_LOW; - else if (!strcmp(irq_type, "edge-rising")) - pdata->irq_type = IRQF_TRIGGER_RISING; - else if (!strcmp(irq_type, "edge-falling")) - pdata->irq_type = IRQF_TRIGGER_FALLING; - else - dev_warn(&spi->dev, "wrong irq-type specified using edge-rising\n"); - spi->dev.platform_data = pdata; done: return pdata; @@ -1071,7 +1058,7 @@ static int at86rf230_probe(struct spi_device *spi) u8 part = 0, version = 0, status; irq_handler_t irq_handler; work_func_t irq_worker; - int rc; + int rc, irq_type; const char *chip; struct ieee802154_ops *ops = NULL; @@ -1176,7 +1163,8 @@ static int at86rf230_probe(struct spi_device *spi) dev->extra_tx_headroom = 0; dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK; - if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + irq_type = irq_get_trigger_type(spi->irq); + if (irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { irq_worker = at86rf230_irqwork; irq_handler = at86rf230_isr; } else { @@ -1203,7 +1191,7 @@ static int at86rf230_probe(struct spi_device *spi) goto err_hw_init; rc = request_irq(spi->irq, irq_handler, - IRQF_SHARED | pdata->irq_type, + IRQF_SHARED | irq_type, dev_name(&spi->dev), lp); if (rc) goto err_hw_init; diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h index aa327a8105ad..b2b1afbb3202 100644 --- a/include/linux/spi/at86rf230.h +++ b/include/linux/spi/at86rf230.h @@ -26,20 +26,6 @@ struct at86rf230_platform_data { int rstn; int slp_tr; int dig2; - - /* Setting the irq_type will configure the driver to request - * the platform irq trigger type according to the given value - * and configure the interrupt polarity of the device to the - * corresponding polarity. - * - * Allowed values are: IRQF_TRIGGER_RISING, IRQF_TRIGGER_FALLING, - * IRQF_TRIGGER_HIGH and IRQF_TRIGGER_LOW - * - * Setting it to 0, the driver does not touch the trigger type - * configuration of the interrupt and sets the interrupt polarity - * of the device to high active (the default value). - */ - int irq_type; }; #endif -- cgit v1.2.3 From eb11022dca4eb22def72d5d4e3140caa357b34e1 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 25 Apr 2014 10:35:07 +0100 Subject: FDDI: Reformat for 8-character tabs Some of our FDDI support code has been apparently written with an assumption that tabs are 4-character wide. In preparation to the next change this update reformats so that it stays within 79 columns and otherwise renders correctly with 8-character tabs. No functional change. Signed-off-by: Maciej W. Rozycki Signed-off-by: David S. Miller --- include/uapi/linux/if_fddi.h | 90 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/if_fddi.h b/include/uapi/linux/if_fddi.h index 0d36909c3aef..1086cd9f6754 100644 --- a/include/uapi/linux/if_fddi.h +++ b/include/uapi/linux/if_fddi.h @@ -30,74 +30,76 @@ * Define max and min legal sizes. The frame sizes do not include * 4 byte FCS/CRC (frame check sequence). */ -#define FDDI_K_ALEN 6 /* Octets in one FDDI address */ -#define FDDI_K_8022_HLEN 16 /* Total octets in 802.2 header */ -#define FDDI_K_SNAP_HLEN 21 /* Total octets in 802.2 SNAP header */ -#define FDDI_K_8022_ZLEN 16 /* Min octets in 802.2 frame sans FCS */ -#define FDDI_K_SNAP_ZLEN 21 /* Min octets in 802.2 SNAP frame sans FCS */ +#define FDDI_K_ALEN 6 /* Octets in one FDDI address */ +#define FDDI_K_8022_HLEN 16 /* Total octets in 802.2 header */ +#define FDDI_K_SNAP_HLEN 21 /* Total octets in 802.2 SNAP header */ +#define FDDI_K_8022_ZLEN 16 /* Min octets in 802.2 frame sans + FCS */ +#define FDDI_K_SNAP_ZLEN 21 /* Min octets in 802.2 SNAP frame sans + FCS */ #define FDDI_K_8022_DLEN 4475 /* Max octets in 802.2 payload */ #define FDDI_K_SNAP_DLEN 4470 /* Max octets in 802.2 SNAP payload */ -#define FDDI_K_LLC_ZLEN 13 /* Min octets in LLC frame sans FCS */ +#define FDDI_K_LLC_ZLEN 13 /* Min octets in LLC frame sans FCS */ #define FDDI_K_LLC_LEN 4491 /* Max octets in LLC frame sans FCS */ +#define FDDI_K_OUI_LEN 3 /* Octets in OUI in 802.2 SNAP + header */ /* Define FDDI Frame Control (FC) Byte values */ -#define FDDI_FC_K_VOID 0x00 -#define FDDI_FC_K_NON_RESTRICTED_TOKEN 0x80 -#define FDDI_FC_K_RESTRICTED_TOKEN 0xC0 -#define FDDI_FC_K_SMT_MIN 0x41 -#define FDDI_FC_K_SMT_MAX 0x4F -#define FDDI_FC_K_MAC_MIN 0xC1 -#define FDDI_FC_K_MAC_MAX 0xCF -#define FDDI_FC_K_ASYNC_LLC_MIN 0x50 -#define FDDI_FC_K_ASYNC_LLC_DEF 0x54 -#define FDDI_FC_K_ASYNC_LLC_MAX 0x5F -#define FDDI_FC_K_SYNC_LLC_MIN 0xD0 -#define FDDI_FC_K_SYNC_LLC_MAX 0xD7 -#define FDDI_FC_K_IMPLEMENTOR_MIN 0x60 -#define FDDI_FC_K_IMPLEMENTOR_MAX 0x6F -#define FDDI_FC_K_RESERVED_MIN 0x70 -#define FDDI_FC_K_RESERVED_MAX 0x7F +#define FDDI_FC_K_VOID 0x00 +#define FDDI_FC_K_NON_RESTRICTED_TOKEN 0x80 +#define FDDI_FC_K_RESTRICTED_TOKEN 0xC0 +#define FDDI_FC_K_SMT_MIN 0x41 +#define FDDI_FC_K_SMT_MAX 0x4F +#define FDDI_FC_K_MAC_MIN 0xC1 +#define FDDI_FC_K_MAC_MAX 0xCF +#define FDDI_FC_K_ASYNC_LLC_MIN 0x50 +#define FDDI_FC_K_ASYNC_LLC_DEF 0x54 +#define FDDI_FC_K_ASYNC_LLC_MAX 0x5F +#define FDDI_FC_K_SYNC_LLC_MIN 0xD0 +#define FDDI_FC_K_SYNC_LLC_MAX 0xD7 +#define FDDI_FC_K_IMPLEMENTOR_MIN 0x60 +#define FDDI_FC_K_IMPLEMENTOR_MAX 0x6F +#define FDDI_FC_K_RESERVED_MIN 0x70 +#define FDDI_FC_K_RESERVED_MAX 0x7F /* Define LLC and SNAP constants */ -#define FDDI_EXTENDED_SAP 0xAA +#define FDDI_EXTENDED_SAP 0xAA #define FDDI_UI_CMD 0x03 /* Define 802.2 Type 1 header */ struct fddi_8022_1_hdr { - __u8 dsap; /* destination service access point */ - __u8 ssap; /* source service access point */ - __u8 ctrl; /* control byte #1 */ + __u8 dsap; /* destination service access point */ + __u8 ssap; /* source service access point */ + __u8 ctrl; /* control byte #1 */ } __attribute__((packed)); /* Define 802.2 Type 2 header */ struct fddi_8022_2_hdr { - __u8 dsap; /* destination service access point */ - __u8 ssap; /* source service access point */ - __u8 ctrl_1; /* control byte #1 */ - __u8 ctrl_2; /* control byte #2 */ + __u8 dsap; /* destination service access point */ + __u8 ssap; /* source service access point */ + __u8 ctrl_1; /* control byte #1 */ + __u8 ctrl_2; /* control byte #2 */ } __attribute__((packed)); /* Define 802.2 SNAP header */ -#define FDDI_K_OUI_LEN 3 struct fddi_snap_hdr { - __u8 dsap; /* always 0xAA */ - __u8 ssap; /* always 0xAA */ - __u8 ctrl; /* always 0x03 */ + __u8 dsap; /* always 0xAA */ + __u8 ssap; /* always 0xAA */ + __u8 ctrl; /* always 0x03 */ __u8 oui[FDDI_K_OUI_LEN]; /* organizational universal id */ - __be16 ethertype; /* packet type ID field */ + __be16 ethertype; /* packet type ID field */ } __attribute__((packed)); /* Define FDDI LLC frame header */ struct fddihdr { - __u8 fc; /* frame control */ - __u8 daddr[FDDI_K_ALEN]; /* destination address */ - __u8 saddr[FDDI_K_ALEN]; /* source address */ - union - { - struct fddi_8022_1_hdr llc_8022_1; - struct fddi_8022_2_hdr llc_8022_2; - struct fddi_snap_hdr llc_snap; - } hdr; + __u8 fc; /* frame control */ + __u8 daddr[FDDI_K_ALEN]; /* destination address */ + __u8 saddr[FDDI_K_ALEN]; /* source address */ + union { + struct fddi_8022_1_hdr llc_8022_1; + struct fddi_8022_2_hdr llc_8022_2; + struct fddi_snap_hdr llc_snap; + } hdr; } __attribute__((packed)); -- cgit v1.2.3 From e2dcdfe95c0bd67e37db6057edd9c4ee1f1c7b17 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Apr 2014 11:15:08 +0930 Subject: virtio: virtio_break_device() to mark all virtqueues broken. Good for post-apocalyptic scenarios, like S/390 hotplug. Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 15 +++++++++++++++ include/linux/virtio.h | 2 ++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 1e443629f76d..4d08f45a9c29 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -865,4 +865,19 @@ bool virtqueue_is_broken(struct virtqueue *_vq) } EXPORT_SYMBOL_GPL(virtqueue_is_broken); +/* + * This should prevent the device from being used, allowing drivers to + * recover. You may need to grab appropriate locks to flush. + */ +void virtio_break_device(struct virtio_device *dev) +{ + struct virtqueue *_vq; + + list_for_each_entry(_vq, &dev->vqs, list) { + struct vring_virtqueue *vq = to_vvq(_vq); + vq->broken = true; + } +} +EXPORT_SYMBOL_GPL(virtio_break_device); + MODULE_LICENSE("GPL"); diff --git a/include/linux/virtio.h b/include/linux/virtio.h index e4abb84199be..b46671e28de2 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -106,6 +106,8 @@ static inline struct virtio_device *dev_to_virtio(struct device *_dev) int register_virtio_device(struct virtio_device *dev); void unregister_virtio_device(struct virtio_device *dev); +void virtio_break_device(struct virtio_device *dev); + /** * virtio_driver - operations for a virtio I/O driver * @driver: underlying device driver (populate name and owner). -- cgit v1.2.3 From 51e158c12aca3c9ac63988611a97c05109b14dc9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Apr 2014 11:34:33 +0930 Subject: param: hand arguments after -- straight to init The kernel passes any args it doesn't need through to init, except it assumes anything containing '.' belongs to the kernel (for a module). This change means all users can clearly distinguish which arguments are for init. For example, the kernel uses debug ("dee-bug") to mean log everything to the console, where systemd uses the debug from the Scandinavian "day-boog" meaning "fail to boot". If a future versions uses argv[] instead of reading /proc/cmdline, this confusion will be avoided. eg: test 'FOO="this is --foo"' -- 'systemd.debug="true true true"' Gives: argv[0] = '/debug-init' argv[1] = 'test' argv[2] = 'systemd.debug=true true true' envp[0] = 'HOME=/' envp[1] = 'TERM=linux' envp[2] = 'FOO=this is --foo' Signed-off-by: Rusty Russell --- include/linux/moduleparam.h | 2 +- init/main.c | 33 +++++++++++++++++++++++++++++---- kernel/module.c | 12 +++++++++--- kernel/params.c | 25 ++++++++++++++----------- 4 files changed, 53 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 204a67743804..b1990c5524e1 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -321,7 +321,7 @@ extern bool parameq(const char *name1, const char *name2); extern bool parameqn(const char *name1, const char *name2, size_t n); /* Called on module insert or kernel boot */ -extern int parse_args(const char *name, +extern char *parse_args(const char *name, char *args, const struct kernel_param *params, unsigned num, diff --git a/init/main.c b/init/main.c index 9c7fd4c9249f..e9d458b5d77b 100644 --- a/init/main.c +++ b/init/main.c @@ -252,6 +252,27 @@ static int __init repair_env_string(char *param, char *val, const char *unused) return 0; } +/* Anything after -- gets handed straight to init. */ +static int __init set_init_arg(char *param, char *val, const char *unused) +{ + unsigned int i; + + if (panic_later) + return 0; + + repair_env_string(param, val, unused); + + for (i = 0; argv_init[i]; i++) { + if (i == MAX_INIT_ARGS) { + panic_later = "init"; + panic_param = param; + return 0; + } + } + argv_init[i] = param; + return 0; +} + /* * Unknown boot options get handed to init, unless they look like * unused parameters (modprobe will find them in /proc/cmdline). @@ -478,7 +499,7 @@ static void __init mm_init(void) asmlinkage void __init start_kernel(void) { - char * command_line; + char * command_line, *after_dashes; extern const struct kernel_param __start___param[], __stop___param[]; /* @@ -519,9 +540,13 @@ asmlinkage void __init start_kernel(void) pr_notice("Kernel command line: %s\n", boot_command_line); parse_early_param(); - parse_args("Booting kernel", static_command_line, __start___param, - __stop___param - __start___param, - -1, -1, &unknown_bootoption); + after_dashes = parse_args("Booting kernel", + static_command_line, __start___param, + __stop___param - __start___param, + -1, -1, &unknown_bootoption); + if (after_dashes) + parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, + set_init_arg); jump_label_init(); diff --git a/kernel/module.c b/kernel/module.c index 11869408f79b..66e4e0d260a9 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3193,6 +3193,7 @@ static int load_module(struct load_info *info, const char __user *uargs, { struct module *mod; long err; + char *after_dashes; err = module_sig_check(info); if (err) @@ -3277,10 +3278,15 @@ static int load_module(struct load_info *info, const char __user *uargs, goto ddebug_cleanup; /* Module is ready to execute: parsing args may do that. */ - err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, - -32768, 32767, unknown_module_param_cb); - if (err < 0) + after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, + -32768, 32767, unknown_module_param_cb); + if (IS_ERR(after_dashes)) { + err = PTR_ERR(after_dashes); goto bug_cleanup; + } else if (after_dashes) { + pr_warn("%s: parameters '%s' after `--' ignored\n", + mod->name, after_dashes); + } /* Link in to syfs. */ err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp); diff --git a/kernel/params.c b/kernel/params.c index b00142e7f3ba..1e52ca233fd9 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -177,13 +177,13 @@ static char *next_arg(char *args, char **param, char **val) } /* Args looks like "foo=bar,bar2 baz=fuz wiz". */ -int parse_args(const char *doing, - char *args, - const struct kernel_param *params, - unsigned num, - s16 min_level, - s16 max_level, - int (*unknown)(char *param, char *val, const char *doing)) +char *parse_args(const char *doing, + char *args, + const struct kernel_param *params, + unsigned num, + s16 min_level, + s16 max_level, + int (*unknown)(char *param, char *val, const char *doing)) { char *param, *val; @@ -198,6 +198,9 @@ int parse_args(const char *doing, int irq_was_disabled; args = next_arg(args, ¶m, &val); + /* Stop at -- */ + if (!val && strcmp(param, "--") == 0) + return args; irq_was_disabled = irqs_disabled(); ret = parse_one(param, val, doing, params, num, min_level, max_level, unknown); @@ -208,22 +211,22 @@ int parse_args(const char *doing, switch (ret) { case -ENOENT: pr_err("%s: Unknown parameter `%s'\n", doing, param); - return ret; + return ERR_PTR(ret); case -ENOSPC: pr_err("%s: `%s' too large for parameter `%s'\n", doing, val ?: "", param); - return ret; + return ERR_PTR(ret); case 0: break; default: pr_err("%s: `%s' invalid for parameter `%s'\n", doing, val ?: "", param); - return ret; + return ERR_PTR(ret); } } /* All parsed OK. */ - return 0; + return NULL; } /* Lazy bastard, eh? */ -- cgit v1.2.3 From 2f7ef2f8790f5bf53db4fc6b2310943139285827 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 25 Apr 2014 13:54:06 -0700 Subject: sched, cls: check if we could overwrite actions when changing a filter When actions are attached to a filter, they are a part of the filter itself, so when changing a filter we should allow to overwrite the actions inside as well. In my specific case, when I tried to _append_ a new action to an existing filter which already has an action, I got EEXIST since kernel refused to overwrite the existing one in kernel. This patch checks if we are changing the filter checking NLM_F_CREATE flag (Sigh, filters don't use NLM_F_REPLACE...) and then passes the boolean down to actions. This fixes the problem above. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 2 +- include/net/sch_generic.h | 2 +- net/sched/cls_api.c | 9 +++++---- net/sched/cls_basic.c | 10 +++++----- net/sched/cls_bpf.c | 10 +++++----- net/sched/cls_cgroup.c | 4 ++-- net/sched/cls_flow.c | 4 ++-- net/sched/cls_fw.c | 10 +++++----- net/sched/cls_route.c | 11 ++++++----- net/sched/cls_rsvp.h | 4 ++-- net/sched/cls_tcindex.c | 8 ++++---- net/sched/cls_u32.c | 10 +++++----- 12 files changed, 43 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index a2441fb1428f..6da46dcf1049 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -136,7 +136,7 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, struct nlattr *rate_tlv, - struct tcf_exts *exts); + struct tcf_exts *exts, bool ovr); void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts); void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, struct tcf_exts *src); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index d062f81c692f..624f9857c83e 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -199,7 +199,7 @@ struct tcf_proto_ops { int (*change)(struct net *net, struct sk_buff *, struct tcf_proto*, unsigned long, u32 handle, struct nlattr **, - unsigned long *); + unsigned long *, bool); int (*delete)(struct tcf_proto*, unsigned long); void (*walk)(struct tcf_proto*, struct tcf_walker *arg); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 29a30a14c315..6786130a4f59 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -317,7 +317,8 @@ replay: } } - err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh); + err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, + n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE); if (err == 0) { if (tp_created) { spin_lock_bh(root_lock); @@ -504,7 +505,7 @@ void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts) EXPORT_SYMBOL(tcf_exts_destroy); int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, - struct nlattr *rate_tlv, struct tcf_exts *exts) + struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr) { #ifdef CONFIG_NET_CLS_ACT { @@ -513,7 +514,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, INIT_LIST_HEAD(&exts->actions); if (exts->police && tb[exts->police]) { act = tcf_action_init_1(net, tb[exts->police], rate_tlv, - "police", TCA_ACT_NOREPLACE, + "police", ovr, TCA_ACT_BIND); if (IS_ERR(act)) return PTR_ERR(act); @@ -523,7 +524,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, } else if (exts->action && tb[exts->action]) { int err; err = tcf_action_init(net, tb[exts->action], rate_tlv, - NULL, TCA_ACT_NOREPLACE, + NULL, ovr, TCA_ACT_BIND, &exts->actions); if (err) return err; diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index e98ca99c202b..0ae1813e3e90 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -130,14 +130,14 @@ static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = { static int basic_set_parms(struct net *net, struct tcf_proto *tp, struct basic_filter *f, unsigned long base, struct nlattr **tb, - struct nlattr *est) + struct nlattr *est, bool ovr) { int err; struct tcf_exts e; struct tcf_ematch_tree t; tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE); - err = tcf_exts_validate(net, tp, tb, est, &e); + err = tcf_exts_validate(net, tp, tb, est, &e, ovr); if (err < 0) return err; @@ -161,7 +161,7 @@ errout: static int basic_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, - struct nlattr **tca, unsigned long *arg) + struct nlattr **tca, unsigned long *arg, bool ovr) { int err; struct basic_head *head = tp->root; @@ -179,7 +179,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, if (f != NULL) { if (handle && f->handle != handle) return -EINVAL; - return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]); + return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr); } err = -ENOBUFS; @@ -206,7 +206,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, f->handle = head->hgenerator; } - err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]); + err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr); if (err < 0) goto errout; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 8e3cf49118e3..16186965af97 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -156,7 +156,7 @@ static void cls_bpf_put(struct tcf_proto *tp, unsigned long f) static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, struct cls_bpf_prog *prog, unsigned long base, struct nlattr **tb, - struct nlattr *est) + struct nlattr *est, bool ovr) { struct sock_filter *bpf_ops, *bpf_old; struct tcf_exts exts; @@ -170,7 +170,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, return -EINVAL; tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE); - ret = tcf_exts_validate(net, tp, tb, est, &exts); + ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr); if (ret < 0) return ret; @@ -242,7 +242,7 @@ static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp, static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg) + unsigned long *arg, bool ovr) { struct cls_bpf_head *head = tp->root; struct cls_bpf_prog *prog = (struct cls_bpf_prog *) *arg; @@ -260,7 +260,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, if (handle && prog->handle != handle) return -EINVAL; return cls_bpf_modify_existing(net, tp, prog, base, tb, - tca[TCA_RATE]); + tca[TCA_RATE], ovr); } prog = kzalloc(sizeof(*prog), GFP_KERNEL); @@ -277,7 +277,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, goto errout; } - ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE]); + ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE], ovr); if (ret < 0) goto errout; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 8e2158ab551c..cacf01bd04f0 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -83,7 +83,7 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg) + unsigned long *arg, bool ovr) { struct nlattr *tb[TCA_CGROUP_MAX + 1]; struct cls_cgroup_head *head = tp->root; @@ -119,7 +119,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, return err; tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); if (err < 0) return err; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 257029c54332..35be16f7c192 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -349,7 +349,7 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { static int flow_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg) + unsigned long *arg, bool ovr) { struct flow_head *head = tp->root; struct flow_filter *f; @@ -393,7 +393,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, } tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE); - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); if (err < 0) return err; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 63a3ce75c02e..861b03ccfed0 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -169,7 +169,7 @@ static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { static int fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, - struct nlattr **tb, struct nlattr **tca, unsigned long base) + struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr) { struct fw_head *head = tp->root; struct tcf_exts e; @@ -177,7 +177,7 @@ fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, int err; tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE); - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); if (err < 0) return err; @@ -218,7 +218,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg) + unsigned long *arg, bool ovr) { struct fw_head *head = tp->root; struct fw_filter *f = (struct fw_filter *) *arg; @@ -236,7 +236,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, if (f != NULL) { if (f->id != handle && handle) return -EINVAL; - return fw_change_attrs(net, tp, f, tb, tca, base); + return fw_change_attrs(net, tp, f, tb, tca, base, ovr); } if (!handle) @@ -264,7 +264,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE); f->id = handle; - err = fw_change_attrs(net, tp, f, tb, tca, base); + err = fw_change_attrs(net, tp, f, tb, tca, base, ovr); if (err < 0) goto errout; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 1ad3068f2ce1..dd9fc2523c76 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -333,7 +333,8 @@ static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = { static int route4_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct route4_filter *f, u32 handle, struct route4_head *head, - struct nlattr **tb, struct nlattr *est, int new) + struct nlattr **tb, struct nlattr *est, int new, + bool ovr) { int err; u32 id = 0, to = 0, nhandle = 0x8000; @@ -343,7 +344,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, struct tcf_exts e; tcf_exts_init(&e, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); - err = tcf_exts_validate(net, tp, tb, est, &e); + err = tcf_exts_validate(net, tp, tb, est, &e, ovr); if (err < 0) return err; @@ -428,7 +429,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg) + unsigned long *arg, bool ovr) { struct route4_head *head = tp->root; struct route4_filter *f, *f1, **fp; @@ -455,7 +456,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, old_handle = f->handle; err = route4_set_parms(net, tp, base, f, handle, head, tb, - tca[TCA_RATE], 0); + tca[TCA_RATE], 0, ovr); if (err < 0) return err; @@ -479,7 +480,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); err = route4_set_parms(net, tp, base, f, handle, head, tb, - tca[TCA_RATE], 1); + tca[TCA_RATE], 1, ovr); if (err < 0) goto errout; diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 19f8e5dfa8bd..1020e233a5d6 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -415,7 +415,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg) + unsigned long *arg, bool ovr) { struct rsvp_head *data = tp->root; struct rsvp_filter *f, **fp; @@ -436,7 +436,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, return err; tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE); - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); if (err < 0) return err; diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index eed8404443d8..d11d0a4fbe34 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -192,7 +192,7 @@ static int tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, u32 handle, struct tcindex_data *p, struct tcindex_filter_result *r, struct nlattr **tb, - struct nlattr *est) + struct nlattr *est, bool ovr) { int err, balloc = 0; struct tcindex_filter_result new_filter_result, *old_r = r; @@ -202,7 +202,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct tcf_exts e; tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); - err = tcf_exts_validate(net, tp, tb, est, &e); + err = tcf_exts_validate(net, tp, tb, est, &e, ovr); if (err < 0) return err; @@ -331,7 +331,7 @@ errout: static int tcindex_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, - struct nlattr **tca, unsigned long *arg) + struct nlattr **tca, unsigned long *arg, bool ovr) { struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_TCINDEX_MAX + 1]; @@ -351,7 +351,7 @@ tcindex_change(struct net *net, struct sk_buff *in_skb, return err; return tcindex_set_parms(net, tp, base, handle, p, r, tb, - tca[TCA_RATE]); + tca[TCA_RATE], ovr); } diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 84c28daff848..c39b583ace32 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -486,13 +486,13 @@ static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = { static int u32_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct tc_u_hnode *ht, struct tc_u_knode *n, struct nlattr **tb, - struct nlattr *est) + struct nlattr *est, bool ovr) { int err; struct tcf_exts e; tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE); - err = tcf_exts_validate(net, tp, tb, est, &e); + err = tcf_exts_validate(net, tp, tb, est, &e, ovr); if (err < 0) return err; @@ -545,7 +545,7 @@ errout: static int u32_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg) + unsigned long *arg, bool ovr) { struct tc_u_common *tp_c = tp->data; struct tc_u_hnode *ht; @@ -569,7 +569,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, return -EINVAL; return u32_set_parms(net, tp, base, n->ht_up, n, tb, - tca[TCA_RATE]); + tca[TCA_RATE], ovr); } if (tb[TCA_U32_DIVISOR]) { @@ -656,7 +656,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, } #endif - err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE]); + err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE], ovr); if (err == 0) { struct tc_u_knode **ins; for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next) -- cgit v1.2.3 From f362e690e5530dd8ace289da991bda558731678e Mon Sep 17 00:00:00 2001 From: Olivier Gay Date: Fri, 25 Apr 2014 20:26:44 +0200 Subject: HID: add missing hid usages Add some missing hid usages from consumer page, add some display brightness control usages from approved hid usage table request HUTTR41: http://www.usb.org/developers/hidpage/HUTRR41.pdf and add voice command usage from approved request HUTTR45: http://www.usb.org/developers/hidpage/Voice_Command_Usage.pdf [jkosina@suse.cz: removed KEY_BRIGHTNESS_TOGGLE / KEY_DISPLAYTOGGLE conflict from hid-debug.c] Signed-off-by: Olivier Gay Signed-off-by: Mathieu Meisser Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 10 ++++++++++ drivers/hid/hid-input.c | 14 ++++++++++++++ include/uapi/linux/input.h | 17 ++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 53b771d5683c..941ab3c287ec 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -855,6 +855,16 @@ static const char *keys[KEY_MAX + 1] = { [KEY_KBDILLUMDOWN] = "KbdIlluminationDown", [KEY_KBDILLUMUP] = "KbdIlluminationUp", [KEY_SWITCHVIDEOMODE] = "SwitchVideoMode", + [KEY_BUTTONCONFIG] = "ButtonConfig", + [KEY_TASKMANAGER] = "TaskManager", + [KEY_JOURNAL] = "Journal", + [KEY_CONTROLPANEL] = "ControlPanel", + [KEY_APPSELECT] = "AppSelect", + [KEY_SCREENSAVER] = "ScreenSaver", + [KEY_VOICECOMMAND] = "VoiceCommand", + [KEY_BRIGHTNESS_MIN] = "BrightnessMin", + [KEY_BRIGHTNESS_MAX] = "BrightnessMax", + [KEY_BRIGHTNESS_AUTO] = "BrightnessAuto", }; static const char *relatives[REL_MAX + 1] = { diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e7e8b19a9284..9f2076acffb1 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -721,6 +721,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x06c: map_key_clear(KEY_YELLOW); break; case 0x06d: map_key_clear(KEY_ZOOM); break; + case 0x06f: map_key_clear(KEY_BRIGHTNESSUP); break; + case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN); break; + case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE); break; + case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN); break; + case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break; + case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break; + case 0x082: map_key_clear(KEY_VIDEO_NEXT); break; case 0x083: map_key_clear(KEY_LAST); break; case 0x084: map_key_clear(KEY_ENTER); break; @@ -761,6 +768,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x0bf: map_key_clear(KEY_SLOW); break; case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; + case 0x0cf: map_key_clear(KEY_VOICECOMMAND); break; case 0x0e0: map_abs_clear(ABS_VOLUME); break; case 0x0e2: map_key_clear(KEY_MUTE); break; case 0x0e5: map_key_clear(KEY_BASSBOOST); break; @@ -768,6 +776,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break; case 0x0f5: map_key_clear(KEY_SLOW); break; + case 0x181: map_key_clear(KEY_BUTTONCONFIG); break; case 0x182: map_key_clear(KEY_BOOKMARKS); break; case 0x183: map_key_clear(KEY_CONFIG); break; case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; @@ -781,6 +790,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x18c: map_key_clear(KEY_VOICEMAIL); break; case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break; case 0x18e: map_key_clear(KEY_CALENDAR); break; + case 0x18f: map_key_clear(KEY_TASKMANAGER); break; + case 0x190: map_key_clear(KEY_JOURNAL); break; case 0x191: map_key_clear(KEY_FINANCE); break; case 0x192: map_key_clear(KEY_CALC); break; case 0x193: map_key_clear(KEY_PLAYER); break; @@ -789,12 +800,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x199: map_key_clear(KEY_CHAT); break; case 0x19c: map_key_clear(KEY_LOGOFF); break; case 0x19e: map_key_clear(KEY_COFFEE); break; + case 0x19f: map_key_clear(KEY_CONTROLPANEL); break; + case 0x1a2: map_key_clear(KEY_APPSELECT); break; case 0x1a3: map_key_clear(KEY_NEXT); break; case 0x1a4: map_key_clear(KEY_PREVIOUS); break; case 0x1a6: map_key_clear(KEY_HELP); break; case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; case 0x1ae: map_key_clear(KEY_KEYBOARD); break; + case 0x1b1: map_key_clear(KEY_SCREENSAVER); break; case 0x1b4: map_key_clear(KEY_FILE); break; case 0x1b6: map_key_clear(KEY_IMAGES); break; case 0x1b7: map_key_clear(KEY_AUDIO); break; diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index bd24470d24a2..b75b5d6116b7 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -461,7 +461,10 @@ struct input_keymap_entry { #define KEY_VIDEO_NEXT 241 /* drive next video source */ #define KEY_VIDEO_PREV 242 /* drive previous video source */ #define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ -#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */ +#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual + brightness control is off, + rely on ambient */ +#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO #define KEY_DISPLAY_OFF 245 /* display device to off state */ #define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ @@ -631,6 +634,7 @@ struct input_keymap_entry { #define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ #define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ #define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ +#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE #define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ #define KEY_LOGOFF 0x1b1 /* AL Logoff */ @@ -722,6 +726,17 @@ struct input_keymap_entry { #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ +#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ +#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ +#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ +#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ +#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ +#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ +#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ + +#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ +#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 -- cgit v1.2.3 From e16821bcfb364b0c41142db275dc74b39fa42c30 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 28 Apr 2014 11:22:08 +0300 Subject: cfg80211: Dynamic channel bandwidth changes in AP mode This extends NL80211_CMD_SET_CHANNEL to allow dynamic channel bandwidth changes in AP mode (including P2P GO) during a lifetime of the BSS. This can be used to implement, e.g., HT 20/40 MHz co-existence rules on the 2.4 GHz band. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ++++++++ include/uapi/linux/nl80211.h | 4 ++++ net/wireless/nl80211.c | 40 ++++++++++++++++++++++++++++------------ net/wireless/rdev-ops.h | 13 +++++++++++++ net/wireless/trace.h | 18 ++++++++++++++++++ 5 files changed, 71 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c98cf08538b9..7eae46ccec01 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2290,6 +2290,10 @@ struct cfg80211_qos_map { * @channel_switch: initiate channel-switch procedure (with CSA) * * @set_qos_map: Set QoS mapping information to the driver + * + * @set_ap_chanwidth: Set the AP (including P2P GO) mode channel width for the + * given interface This is used e.g. for dynamic HT 20/40 MHz channel width + * changes during the lifetime of the BSS. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -2533,9 +2537,13 @@ struct cfg80211_ops { int (*channel_switch)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params); + int (*set_qos_map)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_qos_map *qos_map); + + int (*set_ap_chanwidth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_chan_def *chandef); }; /* diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 0592032ff160..406010d4def0 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3929,6 +3929,9 @@ enum nl80211_ap_sme_features { * interface. An active monitor interface behaves like a normal monitor * interface, but gets added to the driver. It ensures that incoming * unicast packets directed at the configured interface address get ACKed. + * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic + * channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the + * lifetime of a BSS. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3949,6 +3952,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, NL80211_FEATURE_USERSPACE_MPM = 1 << 16, NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17, + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18, }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ca75f60041d2..0f1b18f209d6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1928,18 +1928,20 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, } static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, + struct net_device *dev, struct genl_info *info) { struct cfg80211_chan_def chandef; int result; enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; + struct wireless_dev *wdev = NULL; - if (wdev) - iftype = wdev->iftype; - + if (dev) + wdev = dev->ieee80211_ptr; if (!nl80211_can_set_dev_channel(wdev)) return -EOPNOTSUPP; + if (wdev) + iftype = wdev->iftype; result = nl80211_parse_chandef(rdev, info, &chandef); if (result) @@ -1948,14 +1950,27 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - if (wdev->beacon_interval) { - result = -EBUSY; - break; - } if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) { result = -EINVAL; break; } + if (wdev->beacon_interval) { + if (!dev || !rdev->ops->set_ap_chanwidth || + !(rdev->wiphy.features & + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) { + result = -EBUSY; + break; + } + + /* Only allow dynamic channel width changes */ + if (chandef.chan != wdev->preset_chandef.chan) { + result = -EBUSY; + break; + } + result = rdev_set_ap_chanwidth(rdev, dev, &chandef); + if (result) + break; + } wdev->preset_chandef = chandef; result = 0; break; @@ -1977,7 +1992,7 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *netdev = info->user_ptr[1]; - return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); + return __nl80211_set_channel(rdev, netdev, info); } static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info) @@ -2099,9 +2114,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - result = __nl80211_set_channel(rdev, - nl80211_can_set_dev_channel(wdev) ? wdev : NULL, - info); + result = __nl80211_set_channel( + rdev, + nl80211_can_set_dev_channel(wdev) ? netdev : NULL, + info); if (result) return result; } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 74d97d33c938..00cdf73ba6c4 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -950,4 +950,17 @@ static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct cfg80211_chan_def *chandef) +{ + int ret; + + trace_rdev_set_ap_chanwidth(&rdev->wiphy, dev, chandef); + ret = rdev->ops->set_ap_chanwidth(&rdev->wiphy, dev, chandef); + trace_rdev_return_int(&rdev->wiphy, ret); + + return ret; +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 47b499f8f54d..f3c13ff4d04c 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1919,6 +1919,24 @@ TRACE_EVENT(rdev_set_qos_map, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des) ); +TRACE_EVENT(rdev_set_ap_chanwidth, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, netdev, chandef), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + CHAN_DEF_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ -- cgit v1.2.3 From 1c8732bb0355b929b09173464cdca7df4d516f89 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 9 Apr 2014 13:34:39 +0200 Subject: gpio: support threaded interrupts in irqchip helpers Some off-chip GPIO expanders need to be communicated by I2C or SPI traffic, but may still support IRQs. By the sleeping nature of such buses, such IRQ handlers need to be threaded. Support such handlers in the gpiochip irqchip helpers by flagging IRQs as threaded if the .can_sleep property of the gpiochip is true. Helpfully deny registration of chained IRQ handlers if the .can_sleep property is set, as such chips will invariably need a nested handler rather than a chained handler. Cc: Thomas Gleixner Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 12 ++++++++++++ include/linux/gpio/driver.h | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f48817d97480..c12fe9dfd2db 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1363,6 +1363,11 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, int parent_irq, irq_flow_handler_t parent_handler) { + if (gpiochip->can_sleep) { + chip_err(gpiochip, "you cannot have chained interrupts on a chip that may sleep\n"); + return; + } + irq_set_chained_handler(parent_irq, parent_handler); /* * The parent irqchip is already using the chip_data for this @@ -1389,6 +1394,9 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_set_chip_data(irq, chip); irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler); + /* Chips that can sleep need nested thread handlers */ + if (chip->can_sleep) + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else @@ -1401,9 +1409,13 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) { + struct gpio_chip *chip = d->host_data; + #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif + if (chip->can_sleep) + irq_set_nested_thread(irq, 0); irq_set_chip_and_handler(irq, NULL, NULL); irq_set_chip_data(irq, NULL); } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 1827b43966d9..573e4f3243d0 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -51,7 +51,10 @@ struct seq_file; * format specifier for an unsigned int. It is substituted by the actual * number of the gpio. * @can_sleep: flag must be set iff get()/set() methods sleep, as they - * must while accessing GPIO expander chips over I2C or SPI + * must while accessing GPIO expander chips over I2C or SPI. This + * implies that if the chip supports IRQs, these IRQs need to be threaded + * as the chip access may sleep when e.g. reading out the IRQ status + * registers. * @exported: flags if the gpiochip is exported for use from sysfs. Private. * * A gpio_chip can help platforms abstract various sources of GPIOs so -- cgit v1.2.3 From 5c81f2078b7be63be49916128cc86bc17be7f348 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 9 Apr 2014 12:50:40 +0200 Subject: gpio: tc3589x: get rid of static IRQ base The static IRQ base is not used on any platforms with this chip (only Ux500). Get rid of it forever, and rely on dynamic IRQ descriptor allocation. Cc: Samuel Ortiz Cc: Lee Jones Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tc3589x.c | 10 +--------- include/linux/mfd/tc3589x.h | 1 - 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 113e50cb1f59..4b0d8ccbe561 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -32,9 +32,6 @@ struct tc3589x_gpio { struct device *dev; struct mutex irq_lock; struct irq_domain *domain; - - int irq_base; - /* Caches of interrupt control registers for bus_lock */ u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; @@ -290,8 +287,6 @@ static struct irq_domain_ops tc3589x_irq_ops = { static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio, struct device_node *np) { - int base = tc3589x_gpio->irq_base; - /* * If this results in a linear domain, irq_create_mapping() will * take care of allocating IRQ descriptors at runtime. When a base @@ -299,7 +294,7 @@ static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio, * domain is instantiated. */ tc3589x_gpio->domain = irq_domain_add_simple(np, - tc3589x_gpio->chip.ngpio, base, &tc3589x_irq_ops, + tc3589x_gpio->chip.ngpio, 0, &tc3589x_irq_ops, tc3589x_gpio); if (!tc3589x_gpio->domain) { dev_err(tc3589x_gpio->dev, "Failed to create irqdomain\n"); @@ -348,9 +343,6 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) tc3589x_gpio->chip.of_node = np; #endif - tc3589x_gpio->irq_base = tc3589x->irq_base ? - tc3589x->irq_base + TC3589x_INT_GPIO(0) : 0; - /* Bring the GPIO module out of reset */ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_RSTCTRL_GPIRST, 0); diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h index 6b8e1ff4672b..e6088c2e2092 100644 --- a/include/linux/mfd/tc3589x.h +++ b/include/linux/mfd/tc3589x.h @@ -111,7 +111,6 @@ enum tx3589x_block { #define TC3589x_INT_PORIRQ 7 #define TC3589x_NR_INTERNAL_IRQS 8 -#define TC3589x_INT_GPIO(x) (TC3589x_NR_INTERNAL_IRQS + (x)) struct tc3589x { struct mutex lock; -- cgit v1.2.3 From a9af65223b41cec60cd44fa95a93d10149deb143 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 24 Apr 2014 19:46:49 +0900 Subject: extcon: Add extcon_dev_allocate/free() to control the memory of extcon device This patch add APIs to control the extcon device on extcon provider driver. The extcon_dev_allocate() allocates the memory of extcon device and initializes supported cables. And then extcon_dev_free() decrement the reference of the device of extcon device and free the memory of the extcon device. This APIs must need to implement devm_extcon_dev_allocate()/free() APIs. Signed-off-by: Chanwoo Choi Reviewed-by: Felipe Balbi --- drivers/extcon/extcon-class.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/extcon.h | 13 +++++++++++++ 2 files changed, 49 insertions(+) (limited to 'include') diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index f6df68989651..654ed52e17c2 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -565,6 +565,42 @@ static void dummy_sysfs_dev_release(struct device *dev) { } +/* + * extcon_dev_allocate() - Allocate the memory of extcon device. + * @supported_cable: Array of supported cable names ending with NULL. + * If supported_cable is NULL, cable name related APIs + * are disabled. + * + * This function allocates the memory for extcon device without allocating + * memory in each extcon provider driver and initialize default setting for + * extcon device. + * + * Return the pointer of extcon device if success or ERR_PTR(err) if fail + */ +struct extcon_dev *extcon_dev_allocate(const char **supported_cable) +{ + struct extcon_dev *edev; + + edev = kzalloc(sizeof(*edev), GFP_KERNEL); + if (!edev) + return ERR_PTR(-ENOMEM); + + edev->max_supported = 0; + edev->supported_cable = supported_cable; + + return edev; +} + +/* + * extcon_dev_free() - Free the memory of extcon device. + * @edev: the extcon device to free + */ +void extcon_dev_free(struct extcon_dev *edev) +{ + kfree(edev); +} +EXPORT_SYMBOL_GPL(extcon_dev_free); + /** * extcon_dev_register() - Register a new extcon device * @edev : the new extcon device (should be allocated before calling) diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 548447be2d8f..15361a2f2f19 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -191,6 +191,12 @@ extern void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev); extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); +/* + * Following APIs control the memory of extcon device. + */ +extern struct extcon_dev *extcon_dev_allocate(const char **cables); +extern void extcon_dev_free(struct extcon_dev *edev); + /* * get/set/update_state access the 32b encoded state value, which represents * states of all possible cables of the multistate port. For example, if one @@ -267,6 +273,13 @@ static inline int devm_extcon_dev_register(struct device *dev, static inline void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev) { } +static inline struct extcon_dev *extcon_dev_allocate(const char **cables) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void extcon_dev_free(struct extcon_dev *edev) { } + static inline u32 extcon_get_state(struct extcon_dev *edev) { return 0; -- cgit v1.2.3 From 739ba1bfdb15e773999aafddbd6c59b5737797a0 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 24 Apr 2014 20:12:15 +0900 Subject: extcon: Add devm_extcon_dev_allocate/free to manage the resource of extcon device This patch add device managed devm_extcon_dev_{allocate,free} to automatically free the memory of extcon_dev structure without handling free operation. Signed-off-by: Chanwoo Choi Reviewed-by: Felipe Balbi --- drivers/extcon/extcon-class.c | 70 +++++++++++++++++++++++++++++++++++-------- include/linux/extcon.h | 11 +++++++ 2 files changed, 69 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 654ed52e17c2..18d42c0e4581 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -601,6 +601,64 @@ void extcon_dev_free(struct extcon_dev *edev) } EXPORT_SYMBOL_GPL(extcon_dev_free); +static int devm_extcon_dev_match(struct device *dev, void *res, void *data) +{ + struct extcon_dev **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +static void devm_extcon_dev_release(struct device *dev, void *res) +{ + extcon_dev_free(*(struct extcon_dev **)res); +} + +/** + * devm_extcon_dev_allocate - Allocate managed extcon device + * @dev: device owning the extcon device being created + * @supported_cable: Array of supported cable names ending with NULL. + * If supported_cable is NULL, cable name related APIs + * are disabled. + * + * This function manages automatically the memory of extcon device using device + * resource management and simplify the control of freeing the memory of extcon + * device. + * + * Returns the pointer memory of allocated extcon_dev if success + * or ERR_PTR(err) if fail + */ +struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, + const char **supported_cable) +{ + struct extcon_dev **ptr, *edev; + + ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + edev = extcon_dev_allocate(supported_cable); + if (IS_ERR(edev)) { + devres_free(ptr); + return edev; + } + + *ptr = edev; + devres_add(dev, ptr); + + return edev; +} +EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate); + +void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev) +{ + WARN_ON(devres_release(dev, devm_extcon_dev_release, + devm_extcon_dev_match, edev)); +} +EXPORT_SYMBOL_GPL(devm_extcon_dev_free); + /** * extcon_dev_register() - Register a new extcon device * @edev : the new extcon device (should be allocated before calling) @@ -860,18 +918,6 @@ static void devm_extcon_dev_unreg(struct device *dev, void *res) extcon_dev_unregister(*(struct extcon_dev **)res); } -static int devm_extcon_dev_match(struct device *dev, void *res, void *data) -{ - struct extcon_dev **r = res; - - if (!r || !*r) { - WARN_ON(!r || !*r); - return 0; - } - - return *r == data; -} - /** * devm_extcon_dev_register() - Resource-managed extcon_dev_register() * @dev: device to allocate extcon device diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 15361a2f2f19..36f49c405dfb 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -196,6 +196,9 @@ extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); */ extern struct extcon_dev *extcon_dev_allocate(const char **cables); extern void extcon_dev_free(struct extcon_dev *edev); +extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, + const char **cables); +extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev); /* * get/set/update_state access the 32b encoded state value, which represents @@ -280,6 +283,14 @@ static inline struct extcon_dev *extcon_dev_allocate(const char **cables) static inline void extcon_dev_free(struct extcon_dev *edev) { } +static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, + const char **cables) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void devm_extcon_dev_free(struct extcon_dev *edev) { } + static inline u32 extcon_get_state(struct extcon_dev *edev) { return 0; -- cgit v1.2.3 From 3f79a3fb5f41e8f2229e5bf8aa725eaa79686f14 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 21 Apr 2014 20:44:53 +0900 Subject: extcon: palmas: Use devm_extcon_dev_allocate for extcon_dev This patch use devm_extcon_dev_allocate() to simplify the memory control of extcon device. Cc: Graeme Gregory Cc: Kishon Vijay Abraham I Cc: Felipe Balbi Cc: Samuel Ortiz Cc: Lee Jones Signed-off-by: Chanwoo Choi Acked-by: Lee Jones Acked-by: Felipe Balbi Tested-by: Felipe Balbi --- drivers/extcon/extcon-palmas.c | 35 ++++++++++++++++++++--------------- include/linux/mfd/palmas.h | 2 +- 2 files changed, 21 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 1a770e0ee9ae..7417ce84eb2d 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -57,7 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) { if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; - extcon_set_cable_state(&palmas_usb->edev, "USB", true); + extcon_set_cable_state(palmas_usb->edev, "USB", true); dev_info(palmas_usb->dev, "USB cable is attached\n"); } else { dev_dbg(palmas_usb->dev, @@ -66,7 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) { if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; - extcon_set_cable_state(&palmas_usb->edev, "USB", false); + extcon_set_cable_state(palmas_usb->edev, "USB", false); dev_info(palmas_usb->dev, "USB cable is detached\n"); } else { dev_dbg(palmas_usb->dev, @@ -93,7 +93,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); palmas_usb->linkstat = PALMAS_USB_STATE_ID; - extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); + extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true); dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { @@ -101,17 +101,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; - extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); + extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; - extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); + extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { palmas_usb->linkstat = PALMAS_USB_STATE_ID; - extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); + extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true); dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); } @@ -187,15 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, palmas_usb); - palmas_usb->edev.supported_cable = palmas_extcon_cable; - palmas_usb->edev.dev.parent = palmas_usb->dev; - palmas_usb->edev.name = kstrdup(node->name, GFP_KERNEL); - palmas_usb->edev.mutually_exclusive = mutually_exclusive; + palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev, + palmas_extcon_cable); + if (IS_ERR(palmas_usb->edev)) { + dev_err(&pdev->dev, "failed to allocate extcon device\n"); + return -ENOMEM; + } + palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL); + palmas_usb->edev->dev.parent = palmas_usb->dev; + palmas_usb->edev->mutually_exclusive = mutually_exclusive; - status = devm_extcon_dev_register(&pdev->dev, &palmas_usb->edev); + status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev); if (status) { dev_err(&pdev->dev, "failed to register extcon device\n"); - kfree(palmas_usb->edev.name); + kfree(palmas_usb->edev->name); return status; } @@ -209,7 +214,7 @@ static int palmas_usb_probe(struct platform_device *pdev) if (status < 0) { dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", palmas_usb->id_irq, status); - kfree(palmas_usb->edev.name); + kfree(palmas_usb->edev->name); return status; } } @@ -224,7 +229,7 @@ static int palmas_usb_probe(struct platform_device *pdev) if (status < 0) { dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", palmas_usb->vbus_irq, status); - kfree(palmas_usb->edev.name); + kfree(palmas_usb->edev->name); return status; } } @@ -238,7 +243,7 @@ static int palmas_usb_remove(struct platform_device *pdev) { struct palmas_usb *palmas_usb = platform_get_drvdata(pdev); - kfree(palmas_usb->edev.name); + kfree(palmas_usb->edev->name); return 0; } diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 9974e387e483..b8f87b704409 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -415,7 +415,7 @@ struct palmas_usb { struct palmas *palmas; struct device *dev; - struct extcon_dev edev; + struct extcon_dev *edev; int id_otg_irq; int id_irq; -- cgit v1.2.3 From 73dbd77d88e6974d2e30c239cef67e5370c2b7c5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 29 Apr 2014 11:44:36 +0200 Subject: drm: Fixup flip-work kerneldoc Describe the fifo parameter. It seems like kerneldoc doesn't properly handle fields defined using a macro, so it will end up complaining about this anyway and not generate the documentation for it either. At least the kerneldoc is now complete. Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- include/drm/drm_flip_work.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/drm/drm_flip_work.h b/include/drm/drm_flip_work.h index 35c776ae7d3b..9eed34dcd6af 100644 --- a/include/drm/drm_flip_work.h +++ b/include/drm/drm_flip_work.h @@ -57,6 +57,7 @@ typedef void (*drm_flip_func_t)(struct drm_flip_work *work, void *val); * @count: number of committed items * @func: callback fxn called for each committed item * @worker: worker which calls @func + * @fifo: queue of committed items */ struct drm_flip_work { const char *name; -- cgit v1.2.3 From 8ad357551797b1edc184fb9f6a4f80a6fa626459 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 14 Mar 2014 11:00:21 +0100 Subject: KVM: s390: enable IBS for single running VCPUs This patch enables the IBS facility when a single VCPU is running. The facility is dynamically turned on/off as soon as other VCPUs enter/leave the stopped state. When this facility is operating, some instructions can be executed faster for single-cpu guests. Signed-off-by: David Hildenbrand Reviewed-by: Dominik Dingel Reviewed-by: Cornelia Huck Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 2 + arch/s390/kvm/kvm-s390.c | 123 ++++++++++++++++++++++++++++++++++++++- arch/s390/kvm/trace-s390.h | 22 +++++++ include/linux/kvm_host.h | 2 + 4 files changed, 147 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 0d45f6fe734f..f0a1dc5e5d1f 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -72,6 +72,7 @@ struct sca_block { #define CPUSTAT_ZARCH 0x00000800 #define CPUSTAT_MCDS 0x00000100 #define CPUSTAT_SM 0x00000080 +#define CPUSTAT_IBS 0x00000040 #define CPUSTAT_G 0x00000008 #define CPUSTAT_GED 0x00000004 #define CPUSTAT_J 0x00000002 @@ -411,6 +412,7 @@ struct kvm_arch{ int use_cmma; struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; wait_queue_head_t ipte_wq; + spinlock_t start_stop_lock; }; #define KVM_HVA_ERR_BAD (-1UL) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 6c972d229ace..0a01744cbdd9 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -458,6 +458,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.css_support = 0; kvm->arch.use_irqchip = 0; + spin_lock_init(&kvm->arch.start_stop_lock); + return 0; out_nogmap: debug_unregister(kvm->arch.dbf); @@ -996,8 +998,15 @@ bool kvm_s390_cmma_enabled(struct kvm *kvm) return true; } +static bool ibs_enabled(struct kvm_vcpu *vcpu) +{ + return atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_IBS; +} + static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu) { +retry: + s390_vcpu_unblock(vcpu); /* * We use MMU_RELOAD just to re-arm the ipte notifier for the * guest prefix page. gmap_ipte_notify will wait on the ptl lock. @@ -1005,15 +1014,34 @@ static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu) * already finished. We might race against a second unmapper that * wants to set the blocking bit. Lets just retry the request loop. */ - while (kvm_check_request(KVM_REQ_MMU_RELOAD, vcpu)) { + if (kvm_check_request(KVM_REQ_MMU_RELOAD, vcpu)) { int rc; rc = gmap_ipte_notify(vcpu->arch.gmap, vcpu->arch.sie_block->prefix, PAGE_SIZE * 2); if (rc) return rc; - s390_vcpu_unblock(vcpu); + goto retry; + } + + if (kvm_check_request(KVM_REQ_ENABLE_IBS, vcpu)) { + if (!ibs_enabled(vcpu)) { + trace_kvm_s390_enable_disable_ibs(vcpu->vcpu_id, 1); + atomic_set_mask(CPUSTAT_IBS, + &vcpu->arch.sie_block->cpuflags); + } + goto retry; } + + if (kvm_check_request(KVM_REQ_DISABLE_IBS, vcpu)) { + if (ibs_enabled(vcpu)) { + trace_kvm_s390_enable_disable_ibs(vcpu->vcpu_id, 0); + atomic_clear_mask(CPUSTAT_IBS, + &vcpu->arch.sie_block->cpuflags); + } + goto retry; + } + return 0; } @@ -1362,16 +1390,107 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr) return kvm_s390_store_status_unloaded(vcpu, addr); } +static inline int is_vcpu_stopped(struct kvm_vcpu *vcpu) +{ + return atomic_read(&(vcpu)->arch.sie_block->cpuflags) & CPUSTAT_STOPPED; +} + +static void __disable_ibs_on_vcpu(struct kvm_vcpu *vcpu) +{ + kvm_check_request(KVM_REQ_ENABLE_IBS, vcpu); + kvm_make_request(KVM_REQ_DISABLE_IBS, vcpu); + exit_sie_sync(vcpu); +} + +static void __disable_ibs_on_all_vcpus(struct kvm *kvm) +{ + unsigned int i; + struct kvm_vcpu *vcpu; + + kvm_for_each_vcpu(i, vcpu, kvm) { + __disable_ibs_on_vcpu(vcpu); + } +} + +static void __enable_ibs_on_vcpu(struct kvm_vcpu *vcpu) +{ + kvm_check_request(KVM_REQ_DISABLE_IBS, vcpu); + kvm_make_request(KVM_REQ_ENABLE_IBS, vcpu); + exit_sie_sync(vcpu); +} + void kvm_s390_vcpu_start(struct kvm_vcpu *vcpu) { + int i, online_vcpus, started_vcpus = 0; + + if (!is_vcpu_stopped(vcpu)) + return; + trace_kvm_s390_vcpu_start_stop(vcpu->vcpu_id, 1); + /* Only one cpu at a time may enter/leave the STOPPED state. */ + spin_lock_bh(&vcpu->kvm->arch.start_stop_lock); + online_vcpus = atomic_read(&vcpu->kvm->online_vcpus); + + for (i = 0; i < online_vcpus; i++) { + if (!is_vcpu_stopped(vcpu->kvm->vcpus[i])) + started_vcpus++; + } + + if (started_vcpus == 0) { + /* we're the only active VCPU -> speed it up */ + __enable_ibs_on_vcpu(vcpu); + } else if (started_vcpus == 1) { + /* + * As we are starting a second VCPU, we have to disable + * the IBS facility on all VCPUs to remove potentially + * oustanding ENABLE requests. + */ + __disable_ibs_on_all_vcpus(vcpu->kvm); + } + atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); + /* + * Another VCPU might have used IBS while we were offline. + * Let's play safe and flush the VCPU at startup. + */ + vcpu->arch.sie_block->ihcpu = 0xffff; + spin_unlock_bh(&vcpu->kvm->arch.start_stop_lock); + return; } void kvm_s390_vcpu_stop(struct kvm_vcpu *vcpu) { + int i, online_vcpus, started_vcpus = 0; + struct kvm_vcpu *started_vcpu = NULL; + + if (is_vcpu_stopped(vcpu)) + return; + trace_kvm_s390_vcpu_start_stop(vcpu->vcpu_id, 0); + /* Only one cpu at a time may enter/leave the STOPPED state. */ + spin_lock_bh(&vcpu->kvm->arch.start_stop_lock); + online_vcpus = atomic_read(&vcpu->kvm->online_vcpus); + atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); + __disable_ibs_on_vcpu(vcpu); + + for (i = 0; i < online_vcpus; i++) { + if (!is_vcpu_stopped(vcpu->kvm->vcpus[i])) { + started_vcpus++; + started_vcpu = vcpu->kvm->vcpus[i]; + } + } + + if (started_vcpus == 1) { + /* + * As we only have one VCPU left, we want to enable the + * IBS facility for that VCPU to speed it up. + */ + __enable_ibs_on_vcpu(started_vcpu); + } + + spin_unlock_bh(&vcpu->kvm->arch.start_stop_lock); + return; } static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h index 34d4f8af3a1d..647e9d6a4818 100644 --- a/arch/s390/kvm/trace-s390.h +++ b/arch/s390/kvm/trace-s390.h @@ -244,6 +244,28 @@ TRACE_EVENT(kvm_s390_enable_css, __entry->kvm) ); +/* + * Trace point for enabling and disabling interlocking-and-broadcasting + * suppression. + */ +TRACE_EVENT(kvm_s390_enable_disable_ibs, + TP_PROTO(unsigned int id, int state), + TP_ARGS(id, state), + + TP_STRUCT__entry( + __field(unsigned int, id) + __field(int, state) + ), + + TP_fast_assign( + __entry->id = id; + __entry->state = state; + ), + + TP_printk("%s ibs on cpu %d", + __entry->state ? "enabling" : "disabling", __entry->id) + ); + #endif /* _TRACE_KVMS390_H */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 820fc2e1d9df..1e125b055327 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -134,6 +134,8 @@ static inline bool is_error_page(struct page *page) #define KVM_REQ_EPR_EXIT 20 #define KVM_REQ_SCAN_IOAPIC 21 #define KVM_REQ_GLOBAL_CLOCK_UPDATE 22 +#define KVM_REQ_ENABLE_IBS 23 +#define KVM_REQ_DISABLE_IBS 24 #define KVM_USERSPACE_IRQ_SOURCE_ID 0 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1 -- cgit v1.2.3 From a235c0916543d8b886405f8871dc644124c7cf78 Mon Sep 17 00:00:00 2001 From: Iulia Manda Date: Wed, 12 Mar 2014 18:37:24 +0200 Subject: rcu: Remove "extern" from function declaration in include/linux/rcupdate.h Because functions have the extern storage class specifier by default, this keyword can be removed. It is redundant to use it explicitly. Signed-off-by: Iulia Manda Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 00a7fd61b3c6..fdc422f3d61d 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -328,7 +328,7 @@ extern struct lockdep_map rcu_lock_map; extern struct lockdep_map rcu_bh_lock_map; extern struct lockdep_map rcu_sched_lock_map; extern struct lockdep_map rcu_callback_map; -extern int debug_lockdep_rcu_enabled(void); +int debug_lockdep_rcu_enabled(void); /** * rcu_read_lock_held() - might we be in RCU read-side critical section? -- cgit v1.2.3 From 71a9b26963f8c2d0df6f782e2b29ccefc22d4fba Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 31 Mar 2014 13:13:02 -0700 Subject: rcu: Document RCU_INIT_POINTER()'s lack of ordering guarantees Although rcu_assign_pointer() provides ordering guarantees, RCU_INIT_POINTER() does not. This commit makes that explicit in the docbook comment header. Reported-by: Lai Jiangshan Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index fdc422f3d61d..3c5ef02ea580 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -949,6 +949,9 @@ static inline notrace void rcu_read_unlock_sched_notrace(void) * pointers, but you must use rcu_assign_pointer() to initialize the * external-to-structure pointer -after- you have completely initialized * the reader-accessible portions of the linked structure. + * + * Note that unlike rcu_assign_pointer(), RCU_INIT_POINTER() provides no + * ordering guarantees for either the CPU or the compiler. */ #define RCU_INIT_POINTER(p, v) \ do { \ -- cgit v1.2.3 From 683399eddb9fff742b1a14c5a5d03e12bfc0afff Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Sun, 20 Apr 2014 18:57:36 -0600 Subject: netfilter: nfnetlink_acct: Adding quota support to accounting framework nfacct objects already support accounting at the byte and packet level. As such it is a natural extension to add the possiblity to define a ceiling limit for both metrics. All the support for quotas itself is added to nfnetlink acctounting framework to stay coherent with current accounting object management. Quota limit checks are implemented in xt_nfacct filter where statistic collection is already done. Pablo Neira Ayuso has also contributed to this feature. Signed-off-by: Mathieu Poirier Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink_acct.h | 8 ++- include/uapi/linux/netfilter/nfnetlink.h | 2 + include/uapi/linux/netfilter/nfnetlink_acct.h | 9 +++ net/netfilter/nfnetlink_acct.c | 85 +++++++++++++++++++++++++++ net/netfilter/xt_nfacct.c | 5 +- 5 files changed, 107 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/nfnetlink_acct.h b/include/linux/netfilter/nfnetlink_acct.h index b2e85e59f760..6ec975748742 100644 --- a/include/linux/netfilter/nfnetlink_acct.h +++ b/include/linux/netfilter/nfnetlink_acct.h @@ -3,11 +3,17 @@ #include +enum { + NFACCT_NO_QUOTA = -1, + NFACCT_UNDERQUOTA, + NFACCT_OVERQUOTA, +}; struct nf_acct; struct nf_acct *nfnl_acct_find_get(const char *filter_name); void nfnl_acct_put(struct nf_acct *acct); void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct); - +extern int nfnl_acct_overquota(const struct sk_buff *skb, + struct nf_acct *nfacct); #endif /* _NFNL_ACCT_H */ diff --git a/include/uapi/linux/netfilter/nfnetlink.h b/include/uapi/linux/netfilter/nfnetlink.h index 596ddd45253c..354a7e5e50f2 100644 --- a/include/uapi/linux/netfilter/nfnetlink.h +++ b/include/uapi/linux/netfilter/nfnetlink.h @@ -20,6 +20,8 @@ enum nfnetlink_groups { #define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_NFTABLES, #define NFNLGRP_NFTABLES NFNLGRP_NFTABLES + NFNLGRP_ACCT_QUOTA, +#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA __NFNLGRP_MAX, }; #define NFNLGRP_MAX (__NFNLGRP_MAX - 1) diff --git a/include/uapi/linux/netfilter/nfnetlink_acct.h b/include/uapi/linux/netfilter/nfnetlink_acct.h index c7b6269e760b..51404ec19022 100644 --- a/include/uapi/linux/netfilter/nfnetlink_acct.h +++ b/include/uapi/linux/netfilter/nfnetlink_acct.h @@ -10,15 +10,24 @@ enum nfnl_acct_msg_types { NFNL_MSG_ACCT_GET, NFNL_MSG_ACCT_GET_CTRZERO, NFNL_MSG_ACCT_DEL, + NFNL_MSG_ACCT_OVERQUOTA, NFNL_MSG_ACCT_MAX }; +enum nfnl_acct_flags { + NFACCT_F_QUOTA_PKTS = (1 << 0), + NFACCT_F_QUOTA_BYTES = (1 << 1), + NFACCT_F_OVERQUOTA = (1 << 2), /* can't be set from userspace */ +}; + enum nfnl_acct_type { NFACCT_UNSPEC, NFACCT_NAME, NFACCT_PKTS, NFACCT_BYTES, NFACCT_USE, + NFACCT_FLAGS, + NFACCT_QUOTA, __NFACCT_MAX }; #define NFACCT_MAX (__NFACCT_MAX - 1) diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index c7b6d466a662..70e86bbb3637 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -32,18 +32,24 @@ static LIST_HEAD(nfnl_acct_list); struct nf_acct { atomic64_t pkts; atomic64_t bytes; + unsigned long flags; struct list_head head; atomic_t refcnt; char name[NFACCT_NAME_MAX]; struct rcu_head rcu_head; + char data[0]; }; +#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) + static int nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const tb[]) { struct nf_acct *nfacct, *matching = NULL; char *acct_name; + unsigned int size = 0; + u32 flags = 0; if (!tb[NFACCT_NAME]) return -EINVAL; @@ -68,15 +74,39 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, /* reset counters if you request a replacement. */ atomic64_set(&matching->pkts, 0); atomic64_set(&matching->bytes, 0); + smp_mb__before_clear_bit(); + /* reset overquota flag if quota is enabled. */ + if ((matching->flags & NFACCT_F_QUOTA)) + clear_bit(NFACCT_F_OVERQUOTA, &matching->flags); return 0; } return -EBUSY; } nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL); + if (tb[NFACCT_FLAGS]) { + flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS])); + if (flags & ~NFACCT_F_QUOTA) + return -EOPNOTSUPP; + if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA) + return -EINVAL; + if (flags & NFACCT_F_OVERQUOTA) + return -EINVAL; + + size += sizeof(u64); + } + + nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL); if (nfacct == NULL) return -ENOMEM; + if (flags & NFACCT_F_QUOTA) { + u64 *quota = (u64 *)nfacct->data; + + *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA])); + nfacct->flags = flags; + } + strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); if (tb[NFACCT_BYTES]) { @@ -117,6 +147,9 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (type == NFNL_MSG_ACCT_GET_CTRZERO) { pkts = atomic64_xchg(&acct->pkts, 0); bytes = atomic64_xchg(&acct->bytes, 0); + smp_mb__before_clear_bit(); + if (acct->flags & NFACCT_F_QUOTA) + clear_bit(NFACCT_F_OVERQUOTA, &acct->flags); } else { pkts = atomic64_read(&acct->pkts); bytes = atomic64_read(&acct->bytes); @@ -125,7 +158,13 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) goto nla_put_failure; + if (acct->flags & NFACCT_F_QUOTA) { + u64 *quota = (u64 *)acct->data; + if (nla_put_be32(skb, NFACCT_FLAGS, htonl(acct->flags)) || + nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota))) + goto nla_put_failure; + } nlmsg_end(skb, nlh); return skb->len; @@ -270,6 +309,8 @@ static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, [NFACCT_BYTES] = { .type = NLA_U64 }, [NFACCT_PKTS] = { .type = NLA_U64 }, + [NFACCT_FLAGS] = { .type = NLA_U32 }, + [NFACCT_QUOTA] = { .type = NLA_U64 }, }; static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { @@ -336,6 +377,50 @@ void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) } EXPORT_SYMBOL_GPL(nfnl_acct_update); +static void nfnl_overquota_report(struct nf_acct *nfacct) +{ + int ret; + struct sk_buff *skb; + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (skb == NULL) + return; + + ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0, + nfacct); + if (ret <= 0) { + kfree_skb(skb); + return; + } + netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA, + GFP_ATOMIC); +} + +int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct) +{ + u64 now; + u64 *quota; + int ret = NFACCT_UNDERQUOTA; + + /* no place here if we don't have a quota */ + if (!(nfacct->flags & NFACCT_F_QUOTA)) + return NFACCT_NO_QUOTA; + + quota = (u64 *)nfacct->data; + now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ? + atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes); + + ret = now > *quota; + + if (now >= *quota && + !test_and_set_bit(NFACCT_F_OVERQUOTA, &nfacct->flags)) { + nfnl_overquota_report(nfacct); + } + + return ret; +} +EXPORT_SYMBOL_GPL(nfnl_acct_overquota); + static int __init nfnl_acct_init(void) { int ret; diff --git a/net/netfilter/xt_nfacct.c b/net/netfilter/xt_nfacct.c index b3be0ef21f19..8c646ed9c921 100644 --- a/net/netfilter/xt_nfacct.c +++ b/net/netfilter/xt_nfacct.c @@ -21,11 +21,14 @@ MODULE_ALIAS("ip6t_nfacct"); static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par) { + int overquota; const struct xt_nfacct_match_info *info = par->targinfo; nfnl_acct_update(skb, info->nfacct); - return true; + overquota = nfnl_acct_overquota(skb, info->nfacct); + + return overquota == NFACCT_UNDERQUOTA ? false : true; } static int -- cgit v1.2.3 From f768e5bdefe1ec9adbf7a116dfb156b73cacb582 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 28 Apr 2014 21:09:50 +0200 Subject: netfilter: add helper for adding nat extension Reduce copy-past a bit by adding a common helper. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_nat.h | 2 ++ net/ipv4/netfilter/iptable_nat.c | 14 +++----------- net/ipv4/netfilter/nft_chain_nat_ipv4.c | 12 +++--------- net/ipv6/netfilter/ip6table_nat.c | 14 +++----------- net/ipv6/netfilter/nft_chain_nat_ipv6.c | 12 +++--------- net/netfilter/nf_nat_core.c | 24 ++++++++++++++++-------- 6 files changed, 30 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index 07eaaf604092..a71dd333ac68 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -48,6 +48,8 @@ unsigned int nf_nat_setup_info(struct nf_conn *ct, extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum); +struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct); + /* Is this tuple already taken? (not by us)*/ int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, const struct nf_conn *ignored_conntrack); diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index ee2886126e3d..f1787c04a4dd 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -91,17 +91,9 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, if (nf_ct_is_untracked(ct)) return NF_ACCEPT; - nat = nfct_nat(ct); - if (!nat) { - /* NAT module was loaded late. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) { - pr_debug("failed to add NAT extension\n"); - return NF_ACCEPT; - } - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index b5b256d45e67..3964157d826c 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -48,15 +48,9 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops, NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET))); - nat = nfct_nat(ct); - if (nat == NULL) { - /* Conntrack module was loaded late, can't add extension. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) - return NF_ACCEPT; - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index 84c7f33d0cf8..387d8b8fc18d 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -90,17 +90,9 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, if (nf_ct_is_untracked(ct)) return NF_ACCEPT; - nat = nfct_nat(ct); - if (!nat) { - /* NAT module was loaded late. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) { - pr_debug("failed to add NAT extension\n"); - return NF_ACCEPT; - } - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c index 9c3297a768fd..d189fcb437fe 100644 --- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -47,15 +47,9 @@ static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, if (ct == NULL || nf_ct_is_untracked(ct)) return NF_ACCEPT; - nat = nfct_nat(ct); - if (nat == NULL) { - /* Conntrack module was loaded late, can't add extension. */ - if (nf_ct_is_confirmed(ct)) - return NF_ACCEPT; - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) - return NF_ACCEPT; - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 52ca952b802c..09096a670c45 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -358,6 +358,19 @@ out: rcu_read_unlock(); } +struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct) +{ + struct nf_conn_nat *nat = nfct_nat(ct); + if (nat) + return nat; + + if (!nf_ct_is_confirmed(ct)) + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); + + return nat; +} +EXPORT_SYMBOL_GPL(nf_ct_nat_ext_add); + unsigned int nf_nat_setup_info(struct nf_conn *ct, const struct nf_nat_range *range, @@ -368,14 +381,9 @@ nf_nat_setup_info(struct nf_conn *ct, struct nf_conn_nat *nat; /* nat helper or nfctnetlink also setup binding */ - nat = nfct_nat(ct); - if (!nat) { - nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); - if (nat == NULL) { - pr_debug("failed to add NAT extension\n"); - return NF_ACCEPT; - } - } + nat = nf_ct_nat_ext_add(ct); + if (nat == NULL) + return NF_ACCEPT; NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC || maniptype == NF_NAT_MANIP_DST); -- cgit v1.2.3 From 3046365bb470f0ec2f7cf5cb07a8ee7e4b490103 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 29 Apr 2014 00:51:00 +0100 Subject: devres: introduce API "devm_kmemdup Introduce devm_kmemdup, which uses resource managed kmalloc. There are several request from maintainers to add this instead of using kmemdup. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- Documentation/driver-model/devres.txt | 1 + drivers/base/devres.c | 21 +++++++++++++++++++++ include/linux/device.h | 2 ++ 3 files changed, 24 insertions(+) (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 4f7897e99cba..499951873997 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -236,6 +236,7 @@ certainly invest a bit more effort into libata core layer). MEM devm_kzalloc() devm_kfree() + devm_kmemdup() IIO devm_iio_device_alloc() diff --git a/drivers/base/devres.c b/drivers/base/devres.c index db4e264eecb6..d0914cba2413 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -831,3 +831,24 @@ void devm_kfree(struct device *dev, void *p) WARN_ON(rc); } EXPORT_SYMBOL_GPL(devm_kfree); + +/** + * devm_kmemdup - Resource-managed kmemdup + * @dev: Device this memory belongs to + * @src: Memory region to duplicate + * @len: Memory region length + * @gfp: GFP mask to use + * + * Duplicate region of a memory using resource managed kmalloc + */ +void *devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp) +{ + void *p; + + p = devm_kmalloc(dev, len, gfp); + if (p) + memcpy(p, src, len); + + return p; +} +EXPORT_SYMBOL_GPL(devm_kmemdup); diff --git a/include/linux/device.h b/include/linux/device.h index d1d1c055b48e..ab871588da89 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -623,6 +623,8 @@ static inline void *devm_kcalloc(struct device *dev, } extern void devm_kfree(struct device *dev, void *p); extern char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp); +extern void *devm_kmemdup(struct device *dev, const void *src, size_t len, + gfp_t gfp); void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res); void __iomem *devm_request_and_ioremap(struct device *dev, -- cgit v1.2.3 From 9fbfb4b37ed23f71aa9484484266381c6c6964cb Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 29 Apr 2014 00:51:00 +0100 Subject: IIO: core: Introduce read_raw_multi This callback is introduced to overcome some limitations of existing read_raw callback. The functionality of both existing read_raw and read_raw_multi is similar, both are used to request values from the device. The current read_raw callback allows only two return values. The new read_raw_multi allows returning multiple values. Instead of passing just address of val and val2, it passes length and pointer to values. Depending on the type and length of passed buffer, iio client drivers can return multiple values. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 2 +- drivers/iio/industrialio-core.c | 65 ++++++++++++++++++++++++++-------------- drivers/iio/industrialio-event.c | 6 ++-- drivers/iio/inkern.c | 16 ++++++++-- include/linux/iio/iio.h | 17 +++++++++++ include/linux/iio/types.h | 1 + 6 files changed, 80 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index f6db6af36ba6..5f0ea77fe717 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -35,7 +35,7 @@ int __iio_add_chan_devattr(const char *postfix, struct list_head *attr_list); void iio_free_chan_devattr_list(struct list_head *attr_list); -ssize_t iio_format_value(char *buf, unsigned int type, int val, int val2); +ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals); /* Event interface flags */ #define IIO_BUSY_BIT_POS 1 diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 184444db62ac..59540859bfae 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -373,41 +373,53 @@ EXPORT_SYMBOL_GPL(iio_enum_write); * @buf: The buffer to which the formated value gets written * @type: One of the IIO_VAL_... constants. This decides how the val and val2 * parameters are formatted. - * @val: First part of the value, exact meaning depends on the type parameter. - * @val2: Second part of the value, exact meaning depends on the type parameter. + * @vals: pointer to the values, exact meaning depends on the type parameter. */ -ssize_t iio_format_value(char *buf, unsigned int type, int val, int val2) +ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) { unsigned long long tmp; bool scale_db = false; switch (type) { case IIO_VAL_INT: - return sprintf(buf, "%d\n", val); + return sprintf(buf, "%d\n", vals[0]); case IIO_VAL_INT_PLUS_MICRO_DB: scale_db = true; case IIO_VAL_INT_PLUS_MICRO: - if (val2 < 0) - return sprintf(buf, "-%ld.%06u%s\n", abs(val), -val2, + if (vals[1] < 0) + return sprintf(buf, "-%ld.%06u%s\n", abs(vals[0]), + -vals[1], scale_db ? " dB" : ""); else - return sprintf(buf, "%d.%06u%s\n", val, val2, + return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1], scale_db ? " dB" : ""); case IIO_VAL_INT_PLUS_NANO: - if (val2 < 0) - return sprintf(buf, "-%ld.%09u\n", abs(val), -val2); + if (vals[1] < 0) + return sprintf(buf, "-%ld.%09u\n", abs(vals[0]), + -vals[1]); else - return sprintf(buf, "%d.%09u\n", val, val2); + return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); case IIO_VAL_FRACTIONAL: - tmp = div_s64((s64)val * 1000000000LL, val2); - val2 = do_div(tmp, 1000000000LL); - val = tmp; - return sprintf(buf, "%d.%09u\n", val, val2); + tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]); + vals[1] = do_div(tmp, 1000000000LL); + vals[0] = tmp; + return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); case IIO_VAL_FRACTIONAL_LOG2: - tmp = (s64)val * 1000000000LL >> val2; - val2 = do_div(tmp, 1000000000LL); - val = tmp; - return sprintf(buf, "%d.%09u\n", val, val2); + tmp = (s64)vals[0] * 1000000000LL >> vals[1]; + vals[1] = do_div(tmp, 1000000000LL); + vals[0] = tmp; + return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); + case IIO_VAL_INT_MULTIPLE: + { + int i; + int len = 0; + + for (i = 0; i < size; ++i) + len += snprintf(&buf[len], PAGE_SIZE - len, "%d ", + vals[i]); + len += snprintf(&buf[len], PAGE_SIZE - len, "\n"); + return len; + } default: return 0; } @@ -419,14 +431,23 @@ static ssize_t iio_read_channel_info(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int val, val2; - int ret = indio_dev->info->read_raw(indio_dev, this_attr->c, - &val, &val2, this_attr->address); + int vals[INDIO_MAX_RAW_ELEMENTS]; + int ret; + int val_len = 2; + + if (indio_dev->info->read_raw_multi) + ret = indio_dev->info->read_raw_multi(indio_dev, this_attr->c, + INDIO_MAX_RAW_ELEMENTS, + vals, &val_len, + this_attr->address); + else + ret = indio_dev->info->read_raw(indio_dev, this_attr->c, + &vals[0], &vals[1], this_attr->address); if (ret < 0) return ret; - return iio_format_value(buf, ret, val, val2); + return iio_format_value(buf, ret, val_len, vals); } /** diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index dddfb0f90d34..258a973a1fb8 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -270,7 +270,7 @@ static ssize_t iio_ev_value_show(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int val, val2; + int val, val2, val_arr[2]; int ret; ret = indio_dev->info->read_event_value(indio_dev, @@ -279,7 +279,9 @@ static ssize_t iio_ev_value_show(struct device *dev, &val, &val2); if (ret < 0) return ret; - return iio_format_value(buf, ret, val, val2); + val_arr[0] = val; + val_arr[1] = val2; + return iio_format_value(buf, ret, 2, val_arr); } static ssize_t iio_ev_value_store(struct device *dev, diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index adeba5a0ecf7..d833d55052ea 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -417,12 +417,24 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum info) { int unused; + int vals[INDIO_MAX_RAW_ELEMENTS]; + int ret; + int val_len = 2; if (val2 == NULL) val2 = &unused; - return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel, - val, val2, info); + if (chan->indio_dev->info->read_raw_multi) { + ret = chan->indio_dev->info->read_raw_multi(chan->indio_dev, + chan->channel, INDIO_MAX_RAW_ELEMENTS, + vals, &val_len, info); + *val = vals[0]; + *val2 = vals[1]; + } else + ret = chan->indio_dev->info->read_raw(chan->indio_dev, + chan->channel, val, val2, info); + + return ret; } int iio_read_channel_raw(struct iio_channel *chan, int *val) diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 5f2d00e7e488..5629c92eeadf 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -288,6 +288,8 @@ static inline s64 iio_get_time_ns(void) #define INDIO_ALL_BUFFER_MODES \ (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE) +#define INDIO_MAX_RAW_ELEMENTS 4 + struct iio_trigger; /* forward declaration */ struct iio_dev; @@ -302,6 +304,14 @@ struct iio_dev; * the channel in question. Return value will specify the * type of value returned by the device. val and val2 will * contain the elements making up the returned value. + * @read_raw_multi: function to return values from the device. + * mask specifies which value. Note 0 means a reading of + * the channel in question. Return value will specify the + * type of value returned by the device. vals pointer + * contain the elements making up the returned value. + * max_len specifies maximum number of elements + * vals pointer can contain. val_len is used to return + * length of valid elements in vals. * @write_raw: function to write a value to the device. * Parameters are the same as for read_raw. * @write_raw_get_fmt: callback function to query the expected @@ -328,6 +338,13 @@ struct iio_info { int *val2, long mask); + int (*read_raw_multi)(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int max_len, + int *vals, + int *val_len, + long mask); + int (*write_raw)(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 084d882fe01b..a13c2241abce 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -79,6 +79,7 @@ enum iio_event_direction { #define IIO_VAL_INT_PLUS_MICRO 2 #define IIO_VAL_INT_PLUS_NANO 3 #define IIO_VAL_INT_PLUS_MICRO_DB 4 +#define IIO_VAL_INT_MULTIPLE 5 #define IIO_VAL_FRACTIONAL 10 #define IIO_VAL_FRACTIONAL_LOG2 11 -- cgit v1.2.3 From 0ee8546ac01864b6e12e65199142e00db59c9809 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 29 Apr 2014 00:51:00 +0100 Subject: IIO: core: Modify scan element type The current scan element type uses the following format: [be|le]:[s|u]bits/storagebits[>>shift]. To specify multiple elements in this type, added a repeat value. So new format is: [be|le]:[s|u]bits/storagebitsXr[>>shift]. Here r is specifying how may times, real/storage bits are repeating. When X is value is 0 or 1, then repeat value is not used in the format, and it will be same as existing format. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 41 +++++++++++++++++++++++++++++++++------ include/linux/iio/iio.h | 7 +++++++ 2 files changed, 42 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index e472cff6eeae..36b1ae92e239 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -150,7 +150,16 @@ static ssize_t iio_show_fixed_type(struct device *dev, type = IIO_BE; #endif } - return sprintf(buf, "%s:%c%d/%d>>%u\n", + if (this_attr->c->scan_type.repeat > 1) + return sprintf(buf, "%s:%c%d/%dX%d>>%u\n", + iio_endian_prefix[type], + this_attr->c->scan_type.sign, + this_attr->c->scan_type.realbits, + this_attr->c->scan_type.storagebits, + this_attr->c->scan_type.repeat, + this_attr->c->scan_type.shift); + else + return sprintf(buf, "%s:%c%d/%d>>%u\n", iio_endian_prefix[type], this_attr->c->scan_type.sign, this_attr->c->scan_type.realbits, @@ -475,14 +484,22 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, for_each_set_bit(i, mask, indio_dev->masklength) { ch = iio_find_channel_from_si(indio_dev, i); - length = ch->scan_type.storagebits / 8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; bytes = ALIGN(bytes, length); bytes += length; } if (timestamp) { ch = iio_find_channel_from_si(indio_dev, indio_dev->scan_index_timestamp); - length = ch->scan_type.storagebits / 8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; bytes = ALIGN(bytes, length); bytes += length; } @@ -959,7 +976,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, indio_dev->masklength, in_ind + 1); ch = iio_find_channel_from_si(indio_dev, in_ind); - length = ch->scan_type.storagebits/8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; /* Make sure we are aligned */ in_loc += length; if (in_loc % length) @@ -971,7 +992,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, goto error_clear_mux_table; } ch = iio_find_channel_from_si(indio_dev, in_ind); - length = ch->scan_type.storagebits/8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; if (out_loc % length) out_loc += length - out_loc % length; if (in_loc % length) @@ -992,7 +1017,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, } ch = iio_find_channel_from_si(indio_dev, indio_dev->scan_index_timestamp); - length = ch->scan_type.storagebits/8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; if (out_loc % length) out_loc += length - out_loc % length; if (in_loc % length) diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 5629c92eeadf..ccde91725f98 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -177,6 +177,12 @@ struct iio_event_spec { * shift: Shift right by this before masking out * realbits. * endianness: little or big endian + * repeat: Number of times real/storage bits + * repeats. When the repeat element is + * more than 1, then the type element in + * sysfs will show a repeat value. + * Otherwise, the number of repetitions is + * omitted. * @info_mask_separate: What information is to be exported that is specific to * this channel. * @info_mask_shared_by_type: What information is to be exported that is shared @@ -219,6 +225,7 @@ struct iio_chan_spec { u8 realbits; u8 storagebits; u8 shift; + u8 repeat; enum iio_endian endianness; } scan_type; long info_mask_separate; -- cgit v1.2.3 From 5082f405b74ad1b69aa9595555ce55b75b59b2ec Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 29 Apr 2014 00:51:00 +0100 Subject: IIO: core: Add quaternion modifier Added quaternion in the list of supported modifiers. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + include/linux/iio/types.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 59540859bfae..de8b1c2ed4b4 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -84,6 +84,7 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_LIGHT_RED] = "red", [IIO_MOD_LIGHT_GREEN] = "green", [IIO_MOD_LIGHT_BLUE] = "blue", + [IIO_MOD_QUATERNION] = "quaternion", }; /* relies on pairs of these shared then separate */ diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index a13c2241abce..4fdab2e843b4 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -53,6 +53,7 @@ enum iio_modifier { IIO_MOD_LIGHT_RED, IIO_MOD_LIGHT_GREEN, IIO_MOD_LIGHT_BLUE, + IIO_MOD_QUATERNION, }; enum iio_event_type { -- cgit v1.2.3 From fc18dddc0625cd1fdf6a823e85138ff05848a85f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 29 Apr 2014 00:51:00 +0100 Subject: iio: hid-sensors: Added device rotation support Added usage id processing for device rotation. This uses IIO interfaces for triggered buffer to present data to user mode.This uses HID sensor framework for registering callback events from the sensor hub. Data is exported to user space in the form of quaternion rotation format. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/Kconfig | 12 + drivers/iio/orientation/Makefile | 1 + drivers/iio/orientation/hid-sensor-rotation.c | 348 ++++++++++++++++++++++++++ include/linux/hid-sensor-ids.h | 1 + 4 files changed, 362 insertions(+) create mode 100644 drivers/iio/orientation/hid-sensor-rotation.c (limited to 'include') diff --git a/drivers/iio/orientation/Kconfig b/drivers/iio/orientation/Kconfig index 58c62c837e12..e3aa1e58d920 100644 --- a/drivers/iio/orientation/Kconfig +++ b/drivers/iio/orientation/Kconfig @@ -16,4 +16,16 @@ config HID_SENSOR_INCLINOMETER_3D Say yes here to build support for the HID SENSOR Inclinometer 3D. +config HID_SENSOR_DEVICE_ROTATION + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER + tristate "HID Device Rotation" + help + Say yes here to build support for the HID SENSOR + device rotation. The output of a device rotation sensor + is presented using quaternion format. + endmenu diff --git a/drivers/iio/orientation/Makefile b/drivers/iio/orientation/Makefile index 2c97572ee919..4734dabbde13 100644 --- a/drivers/iio/orientation/Makefile +++ b/drivers/iio/orientation/Makefile @@ -4,3 +4,4 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_HID_SENSOR_INCLINOMETER_3D) += hid-sensor-incl-3d.o +obj-$(CONFIG_HID_SENSOR_DEVICE_ROTATION) += hid-sensor-rotation.o diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c new file mode 100644 index 000000000000..51387bbc1ce1 --- /dev/null +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -0,0 +1,348 @@ +/* + * HID Sensors Driver + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../common/hid-sensors/hid-sensor-trigger.h" + +struct dev_rot_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_common common_attributes; + struct hid_sensor_hub_attribute_info quaternion; + u32 sampled_vals[4]; +}; + +/* Channel definitions */ +static const struct iio_chan_spec dev_rot_channels[] = { + { + .type = IIO_ROT, + .modified = 1, + .channel2 = IIO_MOD_QUATERNION, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS) + } +}; + +/* Adjust channel real bits based on report descriptor */ +static void dev_rot_adjust_channel_bit_mask(struct iio_chan_spec *chan, + int size) +{ + chan->scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + chan->scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is u32 */ + chan->scan_type.storagebits = sizeof(u32) * 8; + chan->scan_type.repeat = 4; +} + +/* Channel read_raw handler */ +static int dev_rot_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, + long mask) +{ + struct dev_rot_state *rot_state = iio_priv(indio_dev); + int ret_type; + int i; + + vals[0] = 0; + vals[1] = 0; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (size >= 4) { + for (i = 0; i < 4; ++i) + vals[i] = rot_state->sampled_vals[i]; + ret_type = IIO_VAL_INT_MULTIPLE; + *val_len = 4; + } else + ret_type = -EINVAL; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret_type = hid_sensor_read_samp_freq_value( + &rot_state->common_attributes, &vals[0], &vals[1]); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret_type = hid_sensor_read_raw_hyst_value( + &rot_state->common_attributes, &vals[0], &vals[1]); + break; + default: + ret_type = -EINVAL; + break; + } + + return ret_type; +} + +/* Channel write_raw handler */ +static int dev_rot_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct dev_rot_state *rot_state = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_write_samp_freq_value( + &rot_state->common_attributes, val, val2); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_write_raw_hyst_value( + &rot_state->common_attributes, val, val2); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info dev_rot_info = { + .driver_module = THIS_MODULE, + .read_raw_multi = &dev_rot_read_raw, + .write_raw = &dev_rot_write_raw, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +{ + dev_dbg(&indio_dev->dev, "hid_sensor_push_data >>\n"); + iio_push_to_buffers(indio_dev, (u8 *)data); + dev_dbg(&indio_dev->dev, "hid_sensor_push_data <<\n"); + +} + +/* Callback handler to send event after all samples are received and captured */ +static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct dev_rot_state *rot_state = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "dev_rot_proc_event [%d]\n", + rot_state->common_attributes.data_ready); + + if (rot_state->common_attributes.data_ready) + hid_sensor_push_data(indio_dev, + (u8 *)rot_state->sampled_vals, + sizeof(rot_state->sampled_vals)); + + return 0; +} + +/* Capture samples in local storage */ +static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + size_t raw_len, char *raw_data, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct dev_rot_state *rot_state = iio_priv(indio_dev); + + if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) { + memcpy(rot_state->sampled_vals, raw_data, + sizeof(rot_state->sampled_vals)); + dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len, + sizeof(rot_state->sampled_vals)); + } + + return 0; +} + +/* Parse report which is specific to an usage id*/ +static int dev_rot_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned usage_id, + struct dev_rot_state *st) +{ + int ret; + + ret = sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_ORIENT_QUATERNION, + &st->quaternion); + if (ret) + return ret; + + dev_rot_adjust_channel_bit_mask(&channels[0], + st->quaternion.size / 4); + + dev_dbg(&pdev->dev, "dev_rot %x:%x\n", st->quaternion.index, + st->quaternion.report_id); + + dev_dbg(&pdev->dev, "dev_rot: attrib size %d\n", + st->quaternion.size); + + /* Set Sensitivity field ids, when there is no individual modifier */ + if (st->common_attributes.sensitivity.index < 0) { + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | + HID_USAGE_SENSOR_DATA_ORIENTATION, + &st->common_attributes.sensitivity); + dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", + st->common_attributes.sensitivity.index, + st->common_attributes.sensitivity.report_id); + } + + return 0; +} + +/* Function to initialize the processing for usage id */ +static int hid_dev_rot_probe(struct platform_device *pdev) +{ + int ret; + static char *name = "dev_rotation"; + struct iio_dev *indio_dev; + struct dev_rot_state *rot_state; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_chan_spec *channels; + + indio_dev = devm_iio_device_alloc(&pdev->dev, + sizeof(struct dev_rot_state)); + if (indio_dev == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + + rot_state = iio_priv(indio_dev); + rot_state->common_attributes.hsdev = hsdev; + rot_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, + HID_USAGE_SENSOR_DEVICE_ORIENTATION, + &rot_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes\n"); + return ret; + } + + channels = devm_kmemdup(&pdev->dev, dev_rot_channels, + sizeof(dev_rot_channels), GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "failed to duplicate channels\n"); + return -ENOMEM; + } + + ret = dev_rot_parse_report(pdev, hsdev, channels, + HID_USAGE_SENSOR_DEVICE_ORIENTATION, rot_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes\n"); + return ret; + } + + indio_dev->channels = channels; + indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &dev_rot_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); + return ret; + } + rot_state->common_attributes.data_ready = false; + ret = hid_sensor_setup_trigger(indio_dev, name, + &rot_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "trigger setup failed\n"); + goto error_unreg_buffer_funcs; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_trigger; + } + + rot_state->callbacks.send_event = dev_rot_proc_event; + rot_state->callbacks.capture_sample = dev_rot_capture_sample; + rot_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, + HID_USAGE_SENSOR_DEVICE_ORIENTATION, + &rot_state->callbacks); + if (ret) { + dev_err(&pdev->dev, "callback reg failed\n"); + goto error_iio_unreg; + } + + return 0; + +error_iio_unreg: + iio_device_unregister(indio_dev); +error_remove_trigger: + hid_sensor_remove_trigger(&rot_state->common_attributes); +error_unreg_buffer_funcs: + iio_triggered_buffer_cleanup(indio_dev); + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_dev_rot_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct dev_rot_state *rot_state = iio_priv(indio_dev); + + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_DEVICE_ORIENTATION); + iio_device_unregister(indio_dev); + hid_sensor_remove_trigger(&rot_state->common_attributes); + iio_triggered_buffer_cleanup(indio_dev); + + return 0; +} + +static struct platform_device_id hid_dev_rot_ids[] = { + { + /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ + .name = "HID-SENSOR-20008a", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_dev_rot_ids); + +static struct platform_driver hid_dev_rot_platform_driver = { + .id_table = hid_dev_rot_ids, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .probe = hid_dev_rot_probe, + .remove = hid_dev_rot_remove, +}; +module_platform_driver(hid_dev_rot_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Device Rotation"); +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 14ead9e8eda8..109f0e633e01 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -76,6 +76,7 @@ #define HID_USAGE_SENSOR_ORIENT_TILT_Y 0x200480 #define HID_USAGE_SENSOR_ORIENT_TILT_Z 0x200481 +#define HID_USAGE_SENSOR_DEVICE_ORIENTATION 0x20008A #define HID_USAGE_SENSOR_ORIENT_ROTATION_MATRIX 0x200482 #define HID_USAGE_SENSOR_ORIENT_QUATERNION 0x200483 #define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX 0x200484 -- cgit v1.2.3 From 27e289dce29764e488c1e13e9aa6950cad1f4aab Mon Sep 17 00:00:00 2001 From: Stratos Karafotis Date: Fri, 25 Apr 2014 23:15:23 +0300 Subject: cpufreq: Introduce macros for cpufreq_frequency_table iteration Many cpufreq drivers need to iterate over the cpufreq_frequency_table for various tasks. This patch introduces two macros which can be used for iteration over cpufreq_frequency_table keeping a common coding style across drivers: - cpufreq_for_each_entry: iterate over each entry of the table - cpufreq_for_each_valid_entry: iterate over each entry that contains a valid frequency. It should have no functional changes. Signed-off-by: Stratos Karafotis Acked-by: Lad, Prabhakar Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- Documentation/cpu-freq/cpu-drivers.txt | 19 +++++++++++++++++++ drivers/cpufreq/cpufreq.c | 11 +++++++++++ include/linux/cpufreq.h | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+) (limited to 'include') diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt index 48da5fdcb9f1..b045fe54986a 100644 --- a/Documentation/cpu-freq/cpu-drivers.txt +++ b/Documentation/cpu-freq/cpu-drivers.txt @@ -228,3 +228,22 @@ is the corresponding frequency table helper for the ->target stage. Just pass the values to this function, and the unsigned int index returns the number of the frequency table entry which contains the frequency the CPU shall be set to. + +The following macros can be used as iterators over cpufreq_frequency_table: + +cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency +table. + +cpufreq-for_each_valid_entry(pos, table) - iterates over all entries, +excluding CPUFREQ_ENTRY_INVALID frequencies. +Use arguments "pos" - a cpufreq_frequency_table * as a loop cursor and +"table" - the cpufreq_frequency_table * you want to iterate over. + +For example: + + struct cpufreq_frequency_table *pos, *driver_freq_table; + + cpufreq_for_each_entry(pos, driver_freq_table) { + /* Do something with pos */ + pos->frequency = ... + } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index abda6609d3e7..a517da996aaf 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -237,6 +237,17 @@ void cpufreq_cpu_put(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(cpufreq_cpu_put); +bool cpufreq_next_valid(struct cpufreq_frequency_table **pos) +{ + while ((*pos)->frequency != CPUFREQ_TABLE_END) + if ((*pos)->frequency != CPUFREQ_ENTRY_INVALID) + return true; + else + (*pos)++; + return false; +} +EXPORT_SYMBOL_GPL(cpufreq_next_valid); + /********************************************************************* * EXTERNALLY AFFECTING FREQUENCY CHANGES * *********************************************************************/ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 5ae5100c1f24..77a5fa191502 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -468,6 +468,27 @@ struct cpufreq_frequency_table { * order */ }; +bool cpufreq_next_valid(struct cpufreq_frequency_table **pos); + +/* + * cpufreq_for_each_entry - iterate over a cpufreq_frequency_table + * @pos: the cpufreq_frequency_table * to use as a loop cursor. + * @table: the cpufreq_frequency_table * to iterate over. + */ + +#define cpufreq_for_each_entry(pos, table) \ + for (pos = table; pos->frequency != CPUFREQ_TABLE_END; pos++) + +/* + * cpufreq_for_each_valid_entry - iterate over a cpufreq_frequency_table + * excluding CPUFREQ_ENTRY_INVALID frequencies. + * @pos: the cpufreq_frequency_table * to use as a loop cursor. + * @table: the cpufreq_frequency_table * to iterate over. + */ + +#define cpufreq_for_each_valid_entry(pos, table) \ + for (pos = table; cpufreq_next_valid(&pos); pos++) + int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table); -- cgit v1.2.3 From cdae05a0f0f7d15837dfd6f4200e8caea03c9cbf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 10:49:43 +0000 Subject: dmaengine: edma: Make reading the position of active channels work As Joel pointed out, edma_read_position() uses memcpy_fromio() to read the parameter ram. That's not synchronized with the internal update as it does a byte by byte copy. We need to do a 32bit read to get a consistent value. Further reading destination and source is pointless. In DEV_TO_MEM transfers we are only interested in the destination, in MEM_TO_DEV we care about the source. In MEM_TO_MEM it really does not matter which one you read. Simple solution: Remove the pointers, select dest/source via a bool and return the read value. Remove the export of this function while at it. The only potential user is the dmaengine and that's always builtin. Signed-off-by: Thomas Gleixner Acked-by: Sekhar Nori Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- arch/arm/common/edma.c | 24 +++++++++--------------- include/linux/platform_data/edma.h | 2 +- 2 files changed, 10 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 0b37f7734d0f..25fa735abc6c 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -994,29 +994,23 @@ void edma_set_dest(unsigned slot, dma_addr_t dest_port, EXPORT_SYMBOL(edma_set_dest); /** - * edma_get_position - returns the current transfer points + * edma_get_position - returns the current transfer point * @slot: parameter RAM slot being examined - * @src: pointer to source port position - * @dst: pointer to destination port position + * @dst: true selects the dest position, false the source * - * Returns current source and destination addresses for a particular - * parameter RAM slot. Its channel should not be active when this is called. + * Returns the position of the current active slot */ -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst) +dma_addr_t edma_get_position(unsigned slot, bool dst) { - struct edmacc_param temp; - unsigned ctlr; + u32 offs, ctlr = EDMA_CTLR(slot); - ctlr = EDMA_CTLR(slot); slot = EDMA_CHAN_SLOT(slot); - edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp); - if (src != NULL) - *src = temp.src; - if (dst != NULL) - *dst = temp.dst; + offs = PARM_OFFSET(slot); + offs += dst ? PARM_DST : PARM_SRC; + + return edma_read(ctlr, offs); } -EXPORT_SYMBOL(edma_get_position); /** * edma_set_src_index - configure DMA source address indexing diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 923f8a3e4ce0..12f134b1493c 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -130,7 +130,7 @@ void edma_set_src(unsigned slot, dma_addr_t src_port, enum address_mode mode, enum fifo_width); void edma_set_dest(unsigned slot, dma_addr_t dest_port, enum address_mode mode, enum fifo_width); -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst); +dma_addr_t edma_get_position(unsigned slot, bool dst); void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx); void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx); void edma_set_transfer_params(unsigned slot, u16 acnt, u16 bcnt, u16 ccnt, -- cgit v1.2.3 From 9cd4360de6090a6daf7fbe024e34953f2ae60ef2 Mon Sep 17 00:00:00 2001 From: Srikanth Thokala Date: Wed, 23 Apr 2014 20:23:26 +0530 Subject: dma: Add Xilinx AXI Video Direct Memory Access Engine driver support This is the driver for the AXI Video Direct Memory Access (AXI VDMA) core, which is a soft Xilinx IP core that provides high- bandwidth direct memory access between memory and AXI4-Stream type video target peripherals. The core provides efficient two dimensional DMA operations with independent asynchronous read and write channel operation. This module works on Zynq (ARM Based SoC) and Microblaze platforms. Signed-off-by: Srikanth Thokala Acked-by: Jassi Brar Reviewed-by: Levente Kurusa Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 14 + drivers/dma/Makefile | 1 + drivers/dma/xilinx/Makefile | 1 + drivers/dma/xilinx/xilinx_vdma.c | 1379 ++++++++++++++++++++++++++++++++++++++ include/linux/amba/xilinx_dma.h | 47 ++ 5 files changed, 1442 insertions(+) create mode 100644 drivers/dma/xilinx/Makefile create mode 100644 drivers/dma/xilinx/xilinx_vdma.c create mode 100644 include/linux/amba/xilinx_dma.h (limited to 'include') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 5c5863842de9..b30b7ed89fb2 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -361,6 +361,20 @@ config FSL_EDMA multiplexing capability for DMA request sources(slot). This module can be found on Freescale Vybrid and LS-1 SoCs. +config XILINX_VDMA + tristate "Xilinx AXI VDMA Engine" + depends on (ARCH_ZYNQ || MICROBLAZE) + select DMA_ENGINE + help + Enable support for Xilinx AXI VDMA Soft IP. + + This engine provides high-bandwidth direct memory access + between memory and AXI4-Stream video type target + peripherals including peripherals which support AXI4- + Stream Video Protocol. It has two stream interfaces/ + channels, Memory Mapped to Stream (MM2S) and Stream to + Memory Mapped (S2MM) for the data transfers. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 5150c82c9caf..c779e1eb2db2 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -46,3 +46,4 @@ obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_MOXART_DMA) += moxart-dma.o obj-$(CONFIG_FSL_EDMA) += fsl-edma.o obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o +obj-y += xilinx/ diff --git a/drivers/dma/xilinx/Makefile b/drivers/dma/xilinx/Makefile new file mode 100644 index 000000000000..3c4e9f2fea28 --- /dev/null +++ b/drivers/dma/xilinx/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_XILINX_VDMA) += xilinx_vdma.o diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c new file mode 100644 index 000000000000..42a13e8d4607 --- /dev/null +++ b/drivers/dma/xilinx/xilinx_vdma.c @@ -0,0 +1,1379 @@ +/* + * DMA driver for Xilinx Video DMA Engine + * + * Copyright (C) 2010-2014 Xilinx, Inc. All rights reserved. + * + * Based on the Freescale DMA driver. + * + * Description: + * The AXI Video Direct Memory Access (AXI VDMA) core is a soft Xilinx IP + * core that provides high-bandwidth direct memory access between memory + * and AXI4-Stream type video target peripherals. The core provides efficient + * two dimensional DMA operations with independent asynchronous read (S2MM) + * and write (MM2S) channel operation. It can be configured to have either + * one channel or two channels. If configured as two channels, one is to + * transmit to the video device (MM2S) and another is to receive from the + * video device (S2MM). Initialization, status, interrupt and management + * registers are accessed through an AXI4-Lite slave interface. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../dmaengine.h" + +/* Register/Descriptor Offsets */ +#define XILINX_VDMA_MM2S_CTRL_OFFSET 0x0000 +#define XILINX_VDMA_S2MM_CTRL_OFFSET 0x0030 +#define XILINX_VDMA_MM2S_DESC_OFFSET 0x0050 +#define XILINX_VDMA_S2MM_DESC_OFFSET 0x00a0 + +/* Control Registers */ +#define XILINX_VDMA_REG_DMACR 0x0000 +#define XILINX_VDMA_DMACR_DELAY_MAX 0xff +#define XILINX_VDMA_DMACR_DELAY_SHIFT 24 +#define XILINX_VDMA_DMACR_FRAME_COUNT_MAX 0xff +#define XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT 16 +#define XILINX_VDMA_DMACR_ERR_IRQ BIT(14) +#define XILINX_VDMA_DMACR_DLY_CNT_IRQ BIT(13) +#define XILINX_VDMA_DMACR_FRM_CNT_IRQ BIT(12) +#define XILINX_VDMA_DMACR_MASTER_SHIFT 8 +#define XILINX_VDMA_DMACR_FSYNCSRC_SHIFT 5 +#define XILINX_VDMA_DMACR_FRAMECNT_EN BIT(4) +#define XILINX_VDMA_DMACR_GENLOCK_EN BIT(3) +#define XILINX_VDMA_DMACR_RESET BIT(2) +#define XILINX_VDMA_DMACR_CIRC_EN BIT(1) +#define XILINX_VDMA_DMACR_RUNSTOP BIT(0) +#define XILINX_VDMA_DMACR_FSYNCSRC_MASK GENMASK(6, 5) + +#define XILINX_VDMA_REG_DMASR 0x0004 +#define XILINX_VDMA_DMASR_EOL_LATE_ERR BIT(15) +#define XILINX_VDMA_DMASR_ERR_IRQ BIT(14) +#define XILINX_VDMA_DMASR_DLY_CNT_IRQ BIT(13) +#define XILINX_VDMA_DMASR_FRM_CNT_IRQ BIT(12) +#define XILINX_VDMA_DMASR_SOF_LATE_ERR BIT(11) +#define XILINX_VDMA_DMASR_SG_DEC_ERR BIT(10) +#define XILINX_VDMA_DMASR_SG_SLV_ERR BIT(9) +#define XILINX_VDMA_DMASR_EOF_EARLY_ERR BIT(8) +#define XILINX_VDMA_DMASR_SOF_EARLY_ERR BIT(7) +#define XILINX_VDMA_DMASR_DMA_DEC_ERR BIT(6) +#define XILINX_VDMA_DMASR_DMA_SLAVE_ERR BIT(5) +#define XILINX_VDMA_DMASR_DMA_INT_ERR BIT(4) +#define XILINX_VDMA_DMASR_IDLE BIT(1) +#define XILINX_VDMA_DMASR_HALTED BIT(0) +#define XILINX_VDMA_DMASR_DELAY_MASK GENMASK(31, 24) +#define XILINX_VDMA_DMASR_FRAME_COUNT_MASK GENMASK(23, 16) + +#define XILINX_VDMA_REG_CURDESC 0x0008 +#define XILINX_VDMA_REG_TAILDESC 0x0010 +#define XILINX_VDMA_REG_REG_INDEX 0x0014 +#define XILINX_VDMA_REG_FRMSTORE 0x0018 +#define XILINX_VDMA_REG_THRESHOLD 0x001c +#define XILINX_VDMA_REG_FRMPTR_STS 0x0024 +#define XILINX_VDMA_REG_PARK_PTR 0x0028 +#define XILINX_VDMA_PARK_PTR_WR_REF_SHIFT 8 +#define XILINX_VDMA_PARK_PTR_RD_REF_SHIFT 0 +#define XILINX_VDMA_REG_VDMA_VERSION 0x002c + +/* Register Direct Mode Registers */ +#define XILINX_VDMA_REG_VSIZE 0x0000 +#define XILINX_VDMA_REG_HSIZE 0x0004 + +#define XILINX_VDMA_REG_FRMDLY_STRIDE 0x0008 +#define XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT 24 +#define XILINX_VDMA_FRMDLY_STRIDE_STRIDE_SHIFT 0 + +#define XILINX_VDMA_REG_START_ADDRESS(n) (0x000c + 4 * (n)) + +/* HW specific definitions */ +#define XILINX_VDMA_MAX_CHANS_PER_DEVICE 0x2 + +#define XILINX_VDMA_DMAXR_ALL_IRQ_MASK \ + (XILINX_VDMA_DMASR_FRM_CNT_IRQ | \ + XILINX_VDMA_DMASR_DLY_CNT_IRQ | \ + XILINX_VDMA_DMASR_ERR_IRQ) + +#define XILINX_VDMA_DMASR_ALL_ERR_MASK \ + (XILINX_VDMA_DMASR_EOL_LATE_ERR | \ + XILINX_VDMA_DMASR_SOF_LATE_ERR | \ + XILINX_VDMA_DMASR_SG_DEC_ERR | \ + XILINX_VDMA_DMASR_SG_SLV_ERR | \ + XILINX_VDMA_DMASR_EOF_EARLY_ERR | \ + XILINX_VDMA_DMASR_SOF_EARLY_ERR | \ + XILINX_VDMA_DMASR_DMA_DEC_ERR | \ + XILINX_VDMA_DMASR_DMA_SLAVE_ERR | \ + XILINX_VDMA_DMASR_DMA_INT_ERR) + +/* + * Recoverable errors are DMA Internal error, SOF Early, EOF Early + * and SOF Late. They are only recoverable when C_FLUSH_ON_FSYNC + * is enabled in the h/w system. + */ +#define XILINX_VDMA_DMASR_ERR_RECOVER_MASK \ + (XILINX_VDMA_DMASR_SOF_LATE_ERR | \ + XILINX_VDMA_DMASR_EOF_EARLY_ERR | \ + XILINX_VDMA_DMASR_SOF_EARLY_ERR | \ + XILINX_VDMA_DMASR_DMA_INT_ERR) + +/* Axi VDMA Flush on Fsync bits */ +#define XILINX_VDMA_FLUSH_S2MM 3 +#define XILINX_VDMA_FLUSH_MM2S 2 +#define XILINX_VDMA_FLUSH_BOTH 1 + +/* Delay loop counter to prevent hardware failure */ +#define XILINX_VDMA_LOOP_COUNT 1000000 + +/** + * struct xilinx_vdma_desc_hw - Hardware Descriptor + * @next_desc: Next Descriptor Pointer @0x00 + * @pad1: Reserved @0x04 + * @buf_addr: Buffer address @0x08 + * @pad2: Reserved @0x0C + * @vsize: Vertical Size @0x10 + * @hsize: Horizontal Size @0x14 + * @stride: Number of bytes between the first + * pixels of each horizontal line @0x18 + */ +struct xilinx_vdma_desc_hw { + u32 next_desc; + u32 pad1; + u32 buf_addr; + u32 pad2; + u32 vsize; + u32 hsize; + u32 stride; +} __aligned(64); + +/** + * struct xilinx_vdma_tx_segment - Descriptor segment + * @hw: Hardware descriptor + * @node: Node in the descriptor segments list + * @phys: Physical address of segment + */ +struct xilinx_vdma_tx_segment { + struct xilinx_vdma_desc_hw hw; + struct list_head node; + dma_addr_t phys; +} __aligned(64); + +/** + * struct xilinx_vdma_tx_descriptor - Per Transaction structure + * @async_tx: Async transaction descriptor + * @segments: TX segments list + * @node: Node in the channel descriptors list + */ +struct xilinx_vdma_tx_descriptor { + struct dma_async_tx_descriptor async_tx; + struct list_head segments; + struct list_head node; +}; + +/** + * struct xilinx_vdma_chan - Driver specific VDMA channel structure + * @xdev: Driver specific device structure + * @ctrl_offset: Control registers offset + * @desc_offset: TX descriptor registers offset + * @lock: Descriptor operation lock + * @pending_list: Descriptors waiting + * @active_desc: Active descriptor + * @allocated_desc: Allocated descriptor + * @done_list: Complete descriptors + * @common: DMA common channel + * @desc_pool: Descriptors pool + * @dev: The dma device + * @irq: Channel IRQ + * @id: Channel ID + * @direction: Transfer direction + * @num_frms: Number of frames + * @has_sg: Support scatter transfers + * @genlock: Support genlock mode + * @err: Channel has errors + * @tasklet: Cleanup work after irq + * @config: Device configuration info + * @flush_on_fsync: Flush on Frame sync + */ +struct xilinx_vdma_chan { + struct xilinx_vdma_device *xdev; + u32 ctrl_offset; + u32 desc_offset; + spinlock_t lock; + struct list_head pending_list; + struct xilinx_vdma_tx_descriptor *active_desc; + struct xilinx_vdma_tx_descriptor *allocated_desc; + struct list_head done_list; + struct dma_chan common; + struct dma_pool *desc_pool; + struct device *dev; + int irq; + int id; + enum dma_transfer_direction direction; + int num_frms; + bool has_sg; + bool genlock; + bool err; + struct tasklet_struct tasklet; + struct xilinx_vdma_config config; + bool flush_on_fsync; +}; + +/** + * struct xilinx_vdma_device - VDMA device structure + * @regs: I/O mapped base address + * @dev: Device Structure + * @common: DMA device structure + * @chan: Driver specific VDMA channel + * @has_sg: Specifies whether Scatter-Gather is present or not + * @flush_on_fsync: Flush on frame sync + */ +struct xilinx_vdma_device { + void __iomem *regs; + struct device *dev; + struct dma_device common; + struct xilinx_vdma_chan *chan[XILINX_VDMA_MAX_CHANS_PER_DEVICE]; + bool has_sg; + u32 flush_on_fsync; +}; + +/* Macros */ +#define to_xilinx_chan(chan) \ + container_of(chan, struct xilinx_vdma_chan, common) +#define to_vdma_tx_descriptor(tx) \ + container_of(tx, struct xilinx_vdma_tx_descriptor, async_tx) + +/* IO accessors */ +static inline u32 vdma_read(struct xilinx_vdma_chan *chan, u32 reg) +{ + return ioread32(chan->xdev->regs + reg); +} + +static inline void vdma_write(struct xilinx_vdma_chan *chan, u32 reg, u32 value) +{ + iowrite32(value, chan->xdev->regs + reg); +} + +static inline void vdma_desc_write(struct xilinx_vdma_chan *chan, u32 reg, + u32 value) +{ + vdma_write(chan, chan->desc_offset + reg, value); +} + +static inline u32 vdma_ctrl_read(struct xilinx_vdma_chan *chan, u32 reg) +{ + return vdma_read(chan, chan->ctrl_offset + reg); +} + +static inline void vdma_ctrl_write(struct xilinx_vdma_chan *chan, u32 reg, + u32 value) +{ + vdma_write(chan, chan->ctrl_offset + reg, value); +} + +static inline void vdma_ctrl_clr(struct xilinx_vdma_chan *chan, u32 reg, + u32 clr) +{ + vdma_ctrl_write(chan, reg, vdma_ctrl_read(chan, reg) & ~clr); +} + +static inline void vdma_ctrl_set(struct xilinx_vdma_chan *chan, u32 reg, + u32 set) +{ + vdma_ctrl_write(chan, reg, vdma_ctrl_read(chan, reg) | set); +} + +/* ----------------------------------------------------------------------------- + * Descriptors and segments alloc and free + */ + +/** + * xilinx_vdma_alloc_tx_segment - Allocate transaction segment + * @chan: Driver specific VDMA channel + * + * Return: The allocated segment on success and NULL on failure. + */ +static struct xilinx_vdma_tx_segment * +xilinx_vdma_alloc_tx_segment(struct xilinx_vdma_chan *chan) +{ + struct xilinx_vdma_tx_segment *segment; + dma_addr_t phys; + + segment = dma_pool_alloc(chan->desc_pool, GFP_ATOMIC, &phys); + if (!segment) + return NULL; + + memset(segment, 0, sizeof(*segment)); + segment->phys = phys; + + return segment; +} + +/** + * xilinx_vdma_free_tx_segment - Free transaction segment + * @chan: Driver specific VDMA channel + * @segment: VDMA transaction segment + */ +static void xilinx_vdma_free_tx_segment(struct xilinx_vdma_chan *chan, + struct xilinx_vdma_tx_segment *segment) +{ + dma_pool_free(chan->desc_pool, segment, segment->phys); +} + +/** + * xilinx_vdma_tx_descriptor - Allocate transaction descriptor + * @chan: Driver specific VDMA channel + * + * Return: The allocated descriptor on success and NULL on failure. + */ +static struct xilinx_vdma_tx_descriptor * +xilinx_vdma_alloc_tx_descriptor(struct xilinx_vdma_chan *chan) +{ + struct xilinx_vdma_tx_descriptor *desc; + unsigned long flags; + + if (chan->allocated_desc) + return chan->allocated_desc; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return NULL; + + spin_lock_irqsave(&chan->lock, flags); + chan->allocated_desc = desc; + spin_unlock_irqrestore(&chan->lock, flags); + + INIT_LIST_HEAD(&desc->segments); + + return desc; +} + +/** + * xilinx_vdma_free_tx_descriptor - Free transaction descriptor + * @chan: Driver specific VDMA channel + * @desc: VDMA transaction descriptor + */ +static void +xilinx_vdma_free_tx_descriptor(struct xilinx_vdma_chan *chan, + struct xilinx_vdma_tx_descriptor *desc) +{ + struct xilinx_vdma_tx_segment *segment, *next; + + if (!desc) + return; + + list_for_each_entry_safe(segment, next, &desc->segments, node) { + list_del(&segment->node); + xilinx_vdma_free_tx_segment(chan, segment); + } + + kfree(desc); +} + +/* Required functions */ + +/** + * xilinx_vdma_free_desc_list - Free descriptors list + * @chan: Driver specific VDMA channel + * @list: List to parse and delete the descriptor + */ +static void xilinx_vdma_free_desc_list(struct xilinx_vdma_chan *chan, + struct list_head *list) +{ + struct xilinx_vdma_tx_descriptor *desc, *next; + + list_for_each_entry_safe(desc, next, list, node) { + list_del(&desc->node); + xilinx_vdma_free_tx_descriptor(chan, desc); + } +} + +/** + * xilinx_vdma_free_descriptors - Free channel descriptors + * @chan: Driver specific VDMA channel + */ +static void xilinx_vdma_free_descriptors(struct xilinx_vdma_chan *chan) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + xilinx_vdma_free_desc_list(chan, &chan->pending_list); + xilinx_vdma_free_desc_list(chan, &chan->done_list); + + xilinx_vdma_free_tx_descriptor(chan, chan->active_desc); + chan->active_desc = NULL; + + spin_unlock_irqrestore(&chan->lock, flags); +} + +/** + * xilinx_vdma_free_chan_resources - Free channel resources + * @dchan: DMA channel + */ +static void xilinx_vdma_free_chan_resources(struct dma_chan *dchan) +{ + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + + dev_dbg(chan->dev, "Free all channel resources.\n"); + + xilinx_vdma_free_descriptors(chan); + dma_pool_destroy(chan->desc_pool); + chan->desc_pool = NULL; +} + +/** + * xilinx_vdma_chan_desc_cleanup - Clean channel descriptors + * @chan: Driver specific VDMA channel + */ +static void xilinx_vdma_chan_desc_cleanup(struct xilinx_vdma_chan *chan) +{ + struct xilinx_vdma_tx_descriptor *desc, *next; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + list_for_each_entry_safe(desc, next, &chan->done_list, node) { + dma_async_tx_callback callback; + void *callback_param; + + /* Remove from the list of running transactions */ + list_del(&desc->node); + + /* Run the link descriptor callback function */ + callback = desc->async_tx.callback; + callback_param = desc->async_tx.callback_param; + if (callback) { + spin_unlock_irqrestore(&chan->lock, flags); + callback(callback_param); + spin_lock_irqsave(&chan->lock, flags); + } + + /* Run any dependencies, then free the descriptor */ + dma_run_dependencies(&desc->async_tx); + xilinx_vdma_free_tx_descriptor(chan, desc); + } + + spin_unlock_irqrestore(&chan->lock, flags); +} + +/** + * xilinx_vdma_do_tasklet - Schedule completion tasklet + * @data: Pointer to the Xilinx VDMA channel structure + */ +static void xilinx_vdma_do_tasklet(unsigned long data) +{ + struct xilinx_vdma_chan *chan = (struct xilinx_vdma_chan *)data; + + xilinx_vdma_chan_desc_cleanup(chan); +} + +/** + * xilinx_vdma_alloc_chan_resources - Allocate channel resources + * @dchan: DMA channel + * + * Return: '0' on success and failure value on error + */ +static int xilinx_vdma_alloc_chan_resources(struct dma_chan *dchan) +{ + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + + /* Has this channel already been allocated? */ + if (chan->desc_pool) + return 0; + + /* + * We need the descriptor to be aligned to 64bytes + * for meeting Xilinx VDMA specification requirement. + */ + chan->desc_pool = dma_pool_create("xilinx_vdma_desc_pool", + chan->dev, + sizeof(struct xilinx_vdma_tx_segment), + __alignof__(struct xilinx_vdma_tx_segment), 0); + if (!chan->desc_pool) { + dev_err(chan->dev, + "unable to allocate channel %d descriptor pool\n", + chan->id); + return -ENOMEM; + } + + dma_cookie_init(dchan); + return 0; +} + +/** + * xilinx_vdma_tx_status - Get VDMA transaction status + * @dchan: DMA channel + * @cookie: Transaction identifier + * @txstate: Transaction state + * + * Return: DMA transaction status + */ +static enum dma_status xilinx_vdma_tx_status(struct dma_chan *dchan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + return dma_cookie_status(dchan, cookie, txstate); +} + +/** + * xilinx_vdma_is_running - Check if VDMA channel is running + * @chan: Driver specific VDMA channel + * + * Return: '1' if running, '0' if not. + */ +static bool xilinx_vdma_is_running(struct xilinx_vdma_chan *chan) +{ + return !(vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) & + XILINX_VDMA_DMASR_HALTED) && + (vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) & + XILINX_VDMA_DMACR_RUNSTOP); +} + +/** + * xilinx_vdma_is_idle - Check if VDMA channel is idle + * @chan: Driver specific VDMA channel + * + * Return: '1' if idle, '0' if not. + */ +static bool xilinx_vdma_is_idle(struct xilinx_vdma_chan *chan) +{ + return vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) & + XILINX_VDMA_DMASR_IDLE; +} + +/** + * xilinx_vdma_halt - Halt VDMA channel + * @chan: Driver specific VDMA channel + */ +static void xilinx_vdma_halt(struct xilinx_vdma_chan *chan) +{ + int loop = XILINX_VDMA_LOOP_COUNT; + + vdma_ctrl_clr(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP); + + /* Wait for the hardware to halt */ + do { + if (vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) & + XILINX_VDMA_DMASR_HALTED) + break; + } while (loop--); + + if (!loop) { + dev_err(chan->dev, "Cannot stop channel %p: %x\n", + chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR)); + chan->err = true; + } + + return; +} + +/** + * xilinx_vdma_start - Start VDMA channel + * @chan: Driver specific VDMA channel + */ +static void xilinx_vdma_start(struct xilinx_vdma_chan *chan) +{ + int loop = XILINX_VDMA_LOOP_COUNT; + + vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RUNSTOP); + + /* Wait for the hardware to start */ + do { + if (!(vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR) & + XILINX_VDMA_DMASR_HALTED)) + break; + } while (loop--); + + if (!loop) { + dev_err(chan->dev, "Cannot start channel %p: %x\n", + chan, vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR)); + + chan->err = true; + } + + return; +} + +/** + * xilinx_vdma_start_transfer - Starts VDMA transfer + * @chan: Driver specific channel struct pointer + */ +static void xilinx_vdma_start_transfer(struct xilinx_vdma_chan *chan) +{ + struct xilinx_vdma_config *config = &chan->config; + struct xilinx_vdma_tx_descriptor *desc; + unsigned long flags; + u32 reg; + struct xilinx_vdma_tx_segment *head, *tail = NULL; + + if (chan->err) + return; + + spin_lock_irqsave(&chan->lock, flags); + + /* There's already an active descriptor, bail out. */ + if (chan->active_desc) + goto out_unlock; + + if (list_empty(&chan->pending_list)) + goto out_unlock; + + desc = list_first_entry(&chan->pending_list, + struct xilinx_vdma_tx_descriptor, node); + + /* If it is SG mode and hardware is busy, cannot submit */ + if (chan->has_sg && xilinx_vdma_is_running(chan) && + !xilinx_vdma_is_idle(chan)) { + dev_dbg(chan->dev, "DMA controller still busy\n"); + goto out_unlock; + } + + /* + * If hardware is idle, then all descriptors on the running lists are + * done, start new transfers + */ + if (chan->has_sg) { + head = list_first_entry(&desc->segments, + struct xilinx_vdma_tx_segment, node); + tail = list_entry(desc->segments.prev, + struct xilinx_vdma_tx_segment, node); + + vdma_ctrl_write(chan, XILINX_VDMA_REG_CURDESC, head->phys); + } + + /* Configure the hardware using info in the config structure */ + reg = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR); + + if (config->frm_cnt_en) + reg |= XILINX_VDMA_DMACR_FRAMECNT_EN; + else + reg &= ~XILINX_VDMA_DMACR_FRAMECNT_EN; + + /* + * With SG, start with circular mode, so that BDs can be fetched. + * In direct register mode, if not parking, enable circular mode + */ + if (chan->has_sg || !config->park) + reg |= XILINX_VDMA_DMACR_CIRC_EN; + + if (config->park) + reg &= ~XILINX_VDMA_DMACR_CIRC_EN; + + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, reg); + + if (config->park && (config->park_frm >= 0) && + (config->park_frm < chan->num_frms)) { + if (chan->direction == DMA_MEM_TO_DEV) + vdma_write(chan, XILINX_VDMA_REG_PARK_PTR, + config->park_frm << + XILINX_VDMA_PARK_PTR_RD_REF_SHIFT); + else + vdma_write(chan, XILINX_VDMA_REG_PARK_PTR, + config->park_frm << + XILINX_VDMA_PARK_PTR_WR_REF_SHIFT); + } + + /* Start the hardware */ + xilinx_vdma_start(chan); + + if (chan->err) + goto out_unlock; + + /* Start the transfer */ + if (chan->has_sg) { + vdma_ctrl_write(chan, XILINX_VDMA_REG_TAILDESC, tail->phys); + } else { + struct xilinx_vdma_tx_segment *segment, *last = NULL; + int i = 0; + + list_for_each_entry(segment, &desc->segments, node) { + vdma_desc_write(chan, + XILINX_VDMA_REG_START_ADDRESS(i++), + segment->hw.buf_addr); + last = segment; + } + + if (!last) + goto out_unlock; + + /* HW expects these parameters to be same for one transaction */ + vdma_desc_write(chan, XILINX_VDMA_REG_HSIZE, last->hw.hsize); + vdma_desc_write(chan, XILINX_VDMA_REG_FRMDLY_STRIDE, + last->hw.stride); + vdma_desc_write(chan, XILINX_VDMA_REG_VSIZE, last->hw.vsize); + } + + list_del(&desc->node); + chan->active_desc = desc; + +out_unlock: + spin_unlock_irqrestore(&chan->lock, flags); +} + +/** + * xilinx_vdma_issue_pending - Issue pending transactions + * @dchan: DMA channel + */ +static void xilinx_vdma_issue_pending(struct dma_chan *dchan) +{ + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + + xilinx_vdma_start_transfer(chan); +} + +/** + * xilinx_vdma_complete_descriptor - Mark the active descriptor as complete + * @chan : xilinx DMA channel + * + * CONTEXT: hardirq + */ +static void xilinx_vdma_complete_descriptor(struct xilinx_vdma_chan *chan) +{ + struct xilinx_vdma_tx_descriptor *desc; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + desc = chan->active_desc; + if (!desc) { + dev_dbg(chan->dev, "no running descriptors\n"); + goto out_unlock; + } + + dma_cookie_complete(&desc->async_tx); + list_add_tail(&desc->node, &chan->done_list); + + chan->active_desc = NULL; + +out_unlock: + spin_unlock_irqrestore(&chan->lock, flags); +} + +/** + * xilinx_vdma_reset - Reset VDMA channel + * @chan: Driver specific VDMA channel + * + * Return: '0' on success and failure value on error + */ +static int xilinx_vdma_reset(struct xilinx_vdma_chan *chan) +{ + int loop = XILINX_VDMA_LOOP_COUNT; + u32 tmp; + + vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, XILINX_VDMA_DMACR_RESET); + + tmp = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) & + XILINX_VDMA_DMACR_RESET; + + /* Wait for the hardware to finish reset */ + do { + tmp = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR) & + XILINX_VDMA_DMACR_RESET; + } while (loop-- && tmp); + + if (!loop) { + dev_err(chan->dev, "reset timeout, cr %x, sr %x\n", + vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR), + vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR)); + return -ETIMEDOUT; + } + + chan->err = false; + + return 0; +} + +/** + * xilinx_vdma_chan_reset - Reset VDMA channel and enable interrupts + * @chan: Driver specific VDMA channel + * + * Return: '0' on success and failure value on error + */ +static int xilinx_vdma_chan_reset(struct xilinx_vdma_chan *chan) +{ + int err; + + /* Reset VDMA */ + err = xilinx_vdma_reset(chan); + if (err) + return err; + + /* Enable interrupts */ + vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR, + XILINX_VDMA_DMAXR_ALL_IRQ_MASK); + + return 0; +} + +/** + * xilinx_vdma_irq_handler - VDMA Interrupt handler + * @irq: IRQ number + * @data: Pointer to the Xilinx VDMA channel structure + * + * Return: IRQ_HANDLED/IRQ_NONE + */ +static irqreturn_t xilinx_vdma_irq_handler(int irq, void *data) +{ + struct xilinx_vdma_chan *chan = data; + u32 status; + + /* Read the status and ack the interrupts. */ + status = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMASR); + if (!(status & XILINX_VDMA_DMAXR_ALL_IRQ_MASK)) + return IRQ_NONE; + + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMASR, + status & XILINX_VDMA_DMAXR_ALL_IRQ_MASK); + + if (status & XILINX_VDMA_DMASR_ERR_IRQ) { + /* + * An error occurred. If C_FLUSH_ON_FSYNC is enabled and the + * error is recoverable, ignore it. Otherwise flag the error. + * + * Only recoverable errors can be cleared in the DMASR register, + * make sure not to write to other error bits to 1. + */ + u32 errors = status & XILINX_VDMA_DMASR_ALL_ERR_MASK; + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMASR, + errors & XILINX_VDMA_DMASR_ERR_RECOVER_MASK); + + if (!chan->flush_on_fsync || + (errors & ~XILINX_VDMA_DMASR_ERR_RECOVER_MASK)) { + dev_err(chan->dev, + "Channel %p has errors %x, cdr %x tdr %x\n", + chan, errors, + vdma_ctrl_read(chan, XILINX_VDMA_REG_CURDESC), + vdma_ctrl_read(chan, XILINX_VDMA_REG_TAILDESC)); + chan->err = true; + } + } + + if (status & XILINX_VDMA_DMASR_DLY_CNT_IRQ) { + /* + * Device takes too long to do the transfer when user requires + * responsiveness. + */ + dev_dbg(chan->dev, "Inter-packet latency too long\n"); + } + + if (status & XILINX_VDMA_DMASR_FRM_CNT_IRQ) { + xilinx_vdma_complete_descriptor(chan); + xilinx_vdma_start_transfer(chan); + } + + tasklet_schedule(&chan->tasklet); + return IRQ_HANDLED; +} + +/** + * xilinx_vdma_tx_submit - Submit DMA transaction + * @tx: Async transaction descriptor + * + * Return: cookie value on success and failure value on error + */ +static dma_cookie_t xilinx_vdma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct xilinx_vdma_tx_descriptor *desc = to_vdma_tx_descriptor(tx); + struct xilinx_vdma_chan *chan = to_xilinx_chan(tx->chan); + dma_cookie_t cookie; + unsigned long flags; + int err; + + if (chan->err) { + /* + * If reset fails, need to hard reset the system. + * Channel is no longer functional + */ + err = xilinx_vdma_chan_reset(chan); + if (err < 0) + return err; + } + + spin_lock_irqsave(&chan->lock, flags); + + cookie = dma_cookie_assign(tx); + + /* Append the transaction to the pending transactions queue. */ + list_add_tail(&desc->node, &chan->pending_list); + + /* Free the allocated desc */ + chan->allocated_desc = NULL; + + spin_unlock_irqrestore(&chan->lock, flags); + + return cookie; +} + +/** + * xilinx_vdma_dma_prep_interleaved - prepare a descriptor for a + * DMA_SLAVE transaction + * @dchan: DMA channel + * @xt: Interleaved template pointer + * @flags: transfer ack flags + * + * Return: Async transaction descriptor on success and NULL on failure + */ +static struct dma_async_tx_descriptor * +xilinx_vdma_dma_prep_interleaved(struct dma_chan *dchan, + struct dma_interleaved_template *xt, + unsigned long flags) +{ + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_vdma_tx_descriptor *desc; + struct xilinx_vdma_tx_segment *segment, *prev = NULL; + struct xilinx_vdma_desc_hw *hw; + + if (!is_slave_direction(xt->dir)) + return NULL; + + if (!xt->numf || !xt->sgl[0].size) + return NULL; + + /* Allocate a transaction descriptor. */ + desc = xilinx_vdma_alloc_tx_descriptor(chan); + if (!desc) + return NULL; + + dma_async_tx_descriptor_init(&desc->async_tx, &chan->common); + desc->async_tx.tx_submit = xilinx_vdma_tx_submit; + async_tx_ack(&desc->async_tx); + + /* Allocate the link descriptor from DMA pool */ + segment = xilinx_vdma_alloc_tx_segment(chan); + if (!segment) + goto error; + + /* Fill in the hardware descriptor */ + hw = &segment->hw; + hw->vsize = xt->numf; + hw->hsize = xt->sgl[0].size; + hw->stride = xt->sgl[0].icg << + XILINX_VDMA_FRMDLY_STRIDE_STRIDE_SHIFT; + hw->stride |= chan->config.frm_dly << + XILINX_VDMA_FRMDLY_STRIDE_FRMDLY_SHIFT; + + if (xt->dir != DMA_MEM_TO_DEV) + hw->buf_addr = xt->dst_start; + else + hw->buf_addr = xt->src_start; + + /* Link the previous next descriptor to current */ + prev = list_last_entry(&desc->segments, + struct xilinx_vdma_tx_segment, node); + prev->hw.next_desc = segment->phys; + + /* Insert the segment into the descriptor segments list. */ + list_add_tail(&segment->node, &desc->segments); + + prev = segment; + + /* Link the last hardware descriptor with the first. */ + segment = list_first_entry(&desc->segments, + struct xilinx_vdma_tx_segment, node); + prev->hw.next_desc = segment->phys; + + return &desc->async_tx; + +error: + xilinx_vdma_free_tx_descriptor(chan, desc); + return NULL; +} + +/** + * xilinx_vdma_terminate_all - Halt the channel and free descriptors + * @chan: Driver specific VDMA Channel pointer + */ +static void xilinx_vdma_terminate_all(struct xilinx_vdma_chan *chan) +{ + /* Halt the DMA engine */ + xilinx_vdma_halt(chan); + + /* Remove and free all of the descriptors in the lists */ + xilinx_vdma_free_descriptors(chan); +} + +/** + * xilinx_vdma_channel_set_config - Configure VDMA channel + * Run-time configuration for Axi VDMA, supports: + * . halt the channel + * . configure interrupt coalescing and inter-packet delay threshold + * . start/stop parking + * . enable genlock + * + * @dchan: DMA channel + * @cfg: VDMA device configuration pointer + * + * Return: '0' on success and failure value on error + */ +int xilinx_vdma_channel_set_config(struct dma_chan *dchan, + struct xilinx_vdma_config *cfg) +{ + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + u32 dmacr; + + if (cfg->reset) + return xilinx_vdma_chan_reset(chan); + + dmacr = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR); + + chan->config.frm_dly = cfg->frm_dly; + chan->config.park = cfg->park; + + /* genlock settings */ + chan->config.gen_lock = cfg->gen_lock; + chan->config.master = cfg->master; + + if (cfg->gen_lock && chan->genlock) { + dmacr |= XILINX_VDMA_DMACR_GENLOCK_EN; + dmacr |= cfg->master << XILINX_VDMA_DMACR_MASTER_SHIFT; + } + + chan->config.frm_cnt_en = cfg->frm_cnt_en; + if (cfg->park) + chan->config.park_frm = cfg->park_frm; + else + chan->config.park_frm = -1; + + chan->config.coalesc = cfg->coalesc; + chan->config.delay = cfg->delay; + + if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) { + dmacr |= cfg->coalesc << XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT; + chan->config.coalesc = cfg->coalesc; + } + + if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) { + dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT; + chan->config.delay = cfg->delay; + } + + /* FSync Source selection */ + dmacr &= ~XILINX_VDMA_DMACR_FSYNCSRC_MASK; + dmacr |= cfg->ext_fsync << XILINX_VDMA_DMACR_FSYNCSRC_SHIFT; + + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr); + + return 0; +} +EXPORT_SYMBOL(xilinx_vdma_channel_set_config); + +/** + * xilinx_vdma_device_control - Configure DMA channel of the device + * @dchan: DMA Channel pointer + * @cmd: DMA control command + * @arg: Channel configuration + * + * Return: '0' on success and failure value on error + */ +static int xilinx_vdma_device_control(struct dma_chan *dchan, + enum dma_ctrl_cmd cmd, unsigned long arg) +{ + struct xilinx_vdma_chan *chan = to_xilinx_chan(dchan); + + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + + xilinx_vdma_terminate_all(chan); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Probe and remove + */ + +/** + * xilinx_vdma_chan_remove - Per Channel remove function + * @chan: Driver specific VDMA channel + */ +static void xilinx_vdma_chan_remove(struct xilinx_vdma_chan *chan) +{ + /* Disable all interrupts */ + vdma_ctrl_clr(chan, XILINX_VDMA_REG_DMACR, + XILINX_VDMA_DMAXR_ALL_IRQ_MASK); + + if (chan->irq > 0) + free_irq(chan->irq, chan); + + tasklet_kill(&chan->tasklet); + + list_del(&chan->common.device_node); +} + +/** + * xilinx_vdma_chan_probe - Per Channel Probing + * It get channel features from the device tree entry and + * initialize special channel handling routines + * + * @xdev: Driver specific device structure + * @node: Device node + * + * Return: '0' on success and failure value on error + */ +static int xilinx_vdma_chan_probe(struct xilinx_vdma_device *xdev, + struct device_node *node) +{ + struct xilinx_vdma_chan *chan; + bool has_dre = false; + u32 value, width; + int err; + + /* Allocate and initialize the channel structure */ + chan = devm_kzalloc(xdev->dev, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + chan->dev = xdev->dev; + chan->xdev = xdev; + chan->has_sg = xdev->has_sg; + + spin_lock_init(&chan->lock); + INIT_LIST_HEAD(&chan->pending_list); + INIT_LIST_HEAD(&chan->done_list); + + /* Retrieve the channel properties from the device tree */ + has_dre = of_property_read_bool(node, "xlnx,include-dre"); + + chan->genlock = of_property_read_bool(node, "xlnx,genlock-mode"); + + err = of_property_read_u32(node, "xlnx,datawidth", &value); + if (err) { + dev_err(xdev->dev, "missing xlnx,datawidth property\n"); + return err; + } + width = value >> 3; /* Convert bits to bytes */ + + /* If data width is greater than 8 bytes, DRE is not in hw */ + if (width > 8) + has_dre = false; + + if (!has_dre) + xdev->common.copy_align = fls(width - 1); + + if (of_device_is_compatible(node, "xlnx,axi-vdma-mm2s-channel")) { + chan->direction = DMA_MEM_TO_DEV; + chan->id = 0; + + chan->ctrl_offset = XILINX_VDMA_MM2S_CTRL_OFFSET; + chan->desc_offset = XILINX_VDMA_MM2S_DESC_OFFSET; + + if (xdev->flush_on_fsync == XILINX_VDMA_FLUSH_BOTH || + xdev->flush_on_fsync == XILINX_VDMA_FLUSH_MM2S) + chan->flush_on_fsync = true; + } else if (of_device_is_compatible(node, + "xlnx,axi-vdma-s2mm-channel")) { + chan->direction = DMA_DEV_TO_MEM; + chan->id = 1; + + chan->ctrl_offset = XILINX_VDMA_S2MM_CTRL_OFFSET; + chan->desc_offset = XILINX_VDMA_S2MM_DESC_OFFSET; + + if (xdev->flush_on_fsync == XILINX_VDMA_FLUSH_BOTH || + xdev->flush_on_fsync == XILINX_VDMA_FLUSH_S2MM) + chan->flush_on_fsync = true; + } else { + dev_err(xdev->dev, "Invalid channel compatible node\n"); + return -EINVAL; + } + + /* Request the interrupt */ + chan->irq = irq_of_parse_and_map(node, 0); + err = request_irq(chan->irq, xilinx_vdma_irq_handler, IRQF_SHARED, + "xilinx-vdma-controller", chan); + if (err) { + dev_err(xdev->dev, "unable to request IRQ %d\n", chan->irq); + return err; + } + + /* Initialize the tasklet */ + tasklet_init(&chan->tasklet, xilinx_vdma_do_tasklet, + (unsigned long)chan); + + /* + * Initialize the DMA channel and add it to the DMA engine channels + * list. + */ + chan->common.device = &xdev->common; + + list_add_tail(&chan->common.device_node, &xdev->common.channels); + xdev->chan[chan->id] = chan; + + /* Reset the channel */ + err = xilinx_vdma_chan_reset(chan); + if (err < 0) { + dev_err(xdev->dev, "Reset channel failed\n"); + return err; + } + + return 0; +} + +/** + * of_dma_xilinx_xlate - Translation function + * @dma_spec: Pointer to DMA specifier as found in the device tree + * @ofdma: Pointer to DMA controller data + * + * Return: DMA channel pointer on success and NULL on error + */ +static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct xilinx_vdma_device *xdev = ofdma->of_dma_data; + int chan_id = dma_spec->args[0]; + + if (chan_id >= XILINX_VDMA_MAX_CHANS_PER_DEVICE) + return NULL; + + return dma_get_slave_channel(&xdev->chan[chan_id]->common); +} + +/** + * xilinx_vdma_probe - Driver probe function + * @pdev: Pointer to the platform_device structure + * + * Return: '0' on success and failure value on error + */ +static int xilinx_vdma_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct xilinx_vdma_device *xdev; + struct device_node *child; + struct resource *io; + u32 num_frames; + int i, err; + + /* Allocate and initialize the DMA engine structure */ + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + xdev->dev = &pdev->dev; + + /* Request and map I/O memory */ + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xdev->regs = devm_ioremap_resource(&pdev->dev, io); + if (IS_ERR(xdev->regs)) + return PTR_ERR(xdev->regs); + + /* Retrieve the DMA engine properties from the device tree */ + xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg"); + + err = of_property_read_u32(node, "xlnx,num-fstores", &num_frames); + if (err < 0) { + dev_err(xdev->dev, "missing xlnx,num-fstores property\n"); + return err; + } + + err = of_property_read_u32(node, "xlnx,flush-fsync", + &xdev->flush_on_fsync); + if (err < 0) + dev_warn(xdev->dev, "missing xlnx,flush-fsync property\n"); + + /* Initialize the DMA engine */ + xdev->common.dev = &pdev->dev; + + INIT_LIST_HEAD(&xdev->common.channels); + dma_cap_set(DMA_SLAVE, xdev->common.cap_mask); + dma_cap_set(DMA_PRIVATE, xdev->common.cap_mask); + + xdev->common.device_alloc_chan_resources = + xilinx_vdma_alloc_chan_resources; + xdev->common.device_free_chan_resources = + xilinx_vdma_free_chan_resources; + xdev->common.device_prep_interleaved_dma = + xilinx_vdma_dma_prep_interleaved; + xdev->common.device_control = xilinx_vdma_device_control; + xdev->common.device_tx_status = xilinx_vdma_tx_status; + xdev->common.device_issue_pending = xilinx_vdma_issue_pending; + + platform_set_drvdata(pdev, xdev); + + /* Initialize the channels */ + for_each_child_of_node(node, child) { + err = xilinx_vdma_chan_probe(xdev, child); + if (err < 0) + goto error; + } + + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) + if (xdev->chan[i]) + xdev->chan[i]->num_frms = num_frames; + + /* Register the DMA engine with the core */ + dma_async_device_register(&xdev->common); + + err = of_dma_controller_register(node, of_dma_xilinx_xlate, + xdev); + if (err < 0) { + dev_err(&pdev->dev, "Unable to register DMA to DT\n"); + dma_async_device_unregister(&xdev->common); + goto error; + } + + dev_info(&pdev->dev, "Xilinx AXI VDMA Engine Driver Probed!!\n"); + + return 0; + +error: + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) + if (xdev->chan[i]) + xilinx_vdma_chan_remove(xdev->chan[i]); + + return err; +} + +/** + * xilinx_vdma_remove - Driver remove function + * @pdev: Pointer to the platform_device structure + * + * Return: Always '0' + */ +static int xilinx_vdma_remove(struct platform_device *pdev) +{ + struct xilinx_vdma_device *xdev = platform_get_drvdata(pdev); + int i; + + of_dma_controller_free(pdev->dev.of_node); + + dma_async_device_unregister(&xdev->common); + + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) + if (xdev->chan[i]) + xilinx_vdma_chan_remove(xdev->chan[i]); + + return 0; +} + +static const struct of_device_id xilinx_vdma_of_ids[] = { + { .compatible = "xlnx,axi-vdma-1.00.a",}, + {} +}; + +static struct platform_driver xilinx_vdma_driver = { + .driver = { + .name = "xilinx-vdma", + .owner = THIS_MODULE, + .of_match_table = xilinx_vdma_of_ids, + }, + .probe = xilinx_vdma_probe, + .remove = xilinx_vdma_remove, +}; + +module_platform_driver(xilinx_vdma_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx VDMA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/amba/xilinx_dma.h b/include/linux/amba/xilinx_dma.h new file mode 100644 index 000000000000..34b98f276ed0 --- /dev/null +++ b/include/linux/amba/xilinx_dma.h @@ -0,0 +1,47 @@ +/* + * Xilinx DMA Engine drivers support header file + * + * Copyright (C) 2010-2014 Xilinx, Inc. All rights reserved. + * + * This 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 __DMA_XILINX_DMA_H +#define __DMA_XILINX_DMA_H + +#include +#include + +/** + * struct xilinx_vdma_config - VDMA Configuration structure + * @frm_dly: Frame delay + * @gen_lock: Whether in gen-lock mode + * @master: Master that it syncs to + * @frm_cnt_en: Enable frame count enable + * @park: Whether wants to park + * @park_frm: Frame to park on + * @coalesc: Interrupt coalescing threshold + * @delay: Delay counter + * @reset: Reset Channel + * @ext_fsync: External Frame Sync source + */ +struct xilinx_vdma_config { + int frm_dly; + int gen_lock; + int master; + int frm_cnt_en; + int park; + int park_frm; + int coalesc; + int delay; + int reset; + int ext_fsync; +}; + +int xilinx_vdma_channel_set_config(struct dma_chan *dchan, + struct xilinx_vdma_config *cfg); + +#endif -- cgit v1.2.3 From ccf3356e6b3d2802ea452c0091314605a9e7b7a0 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 3 Apr 2014 13:38:32 -0500 Subject: of/fdt: consolidate built-in dtb section variables Unify the various architectures __dtb_start and __dtb_end definitions moving them into of_fdt.h. Signed-off-by: Rob Herring Acked-by: Vineet Gupta Acked-by: James Hogan Tested-by: Michal Simek Cc: Ralf Baechle Cc: Jonas Bonn Cc: Chris Zankel Cc: Max Filippov Cc: linux-metag@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: linux@lists.openrisc.net Cc: linux-xtensa@linux-xtensa.org Tested-by: Grant Likely Tested-by: Stephen Chivers --- arch/arc/include/asm/sections.h | 1 - arch/metag/kernel/setup.c | 4 ---- arch/mips/include/asm/mips-boards/generic.h | 2 -- arch/mips/lantiq/prom.h | 2 -- arch/mips/netlogic/xlp/dt.c | 2 +- arch/mips/ralink/of.c | 2 -- arch/openrisc/kernel/vmlinux.h | 2 -- arch/xtensa/kernel/setup.c | 1 - include/linux/of_fdt.h | 3 +++ 9 files changed, 4 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/arc/include/asm/sections.h b/arch/arc/include/asm/sections.h index 764f1e3ba752..09db952e14bd 100644 --- a/arch/arc/include/asm/sections.h +++ b/arch/arc/include/asm/sections.h @@ -12,6 +12,5 @@ #include extern char __arc_dccm_base[]; -extern char __dtb_start[]; #endif diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c index 129c7cdda1ce..31cf53d0eba2 100644 --- a/arch/metag/kernel/setup.c +++ b/arch/metag/kernel/setup.c @@ -105,10 +105,6 @@ extern char _heap_start[]; -#ifdef CONFIG_METAG_BUILTIN_DTB -extern u32 __dtb_start[]; -#endif - #ifdef CONFIG_DA_CONSOLE /* Our early channel based console driver */ extern struct console dash_console; diff --git a/arch/mips/include/asm/mips-boards/generic.h b/arch/mips/include/asm/mips-boards/generic.h index b969491aa98d..c904c24550f6 100644 --- a/arch/mips/include/asm/mips-boards/generic.h +++ b/arch/mips/include/asm/mips-boards/generic.h @@ -67,8 +67,6 @@ extern int mips_revision_sconid; -extern char __dtb_start[]; - #ifdef CONFIG_PCI extern void mips_pcibios_init(void); #else diff --git a/arch/mips/lantiq/prom.h b/arch/mips/lantiq/prom.h index 69a4c582338d..bfd2d58c1d69 100644 --- a/arch/mips/lantiq/prom.h +++ b/arch/mips/lantiq/prom.h @@ -26,6 +26,4 @@ struct ltq_soc_info { extern void ltq_soc_detect(struct ltq_soc_info *i); extern void ltq_soc_init(void); -extern char __dtb_start[]; - #endif diff --git a/arch/mips/netlogic/xlp/dt.c b/arch/mips/netlogic/xlp/dt.c index 7f9615a712fb..bdde33147bce 100644 --- a/arch/mips/netlogic/xlp/dt.c +++ b/arch/mips/netlogic/xlp/dt.c @@ -42,7 +42,7 @@ #include extern u32 __dtb_xlp_evp_begin[], __dtb_xlp_svp_begin[], - __dtb_xlp_fvp_begin[], __dtb_xlp_gvp_begin[], __dtb_start[]; + __dtb_xlp_fvp_begin[], __dtb_xlp_gvp_begin[]; static void *xlp_fdt_blob; void __init *xlp_dt_init(void *fdtp) diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c index 91d7060d5aea..251395210e23 100644 --- a/arch/mips/ralink/of.c +++ b/arch/mips/ralink/of.c @@ -28,8 +28,6 @@ __iomem void *rt_sysc_membase; __iomem void *rt_memc_membase; -extern char __dtb_start[]; - __iomem void *plat_of_remap_node(const char *node) { struct resource res; diff --git a/arch/openrisc/kernel/vmlinux.h b/arch/openrisc/kernel/vmlinux.h index 70b9ce41835c..bbcdf21b0b35 100644 --- a/arch/openrisc/kernel/vmlinux.h +++ b/arch/openrisc/kernel/vmlinux.h @@ -5,6 +5,4 @@ extern char __initrd_start, __initrd_end; #endif -extern u32 __dtb_start[]; - #endif diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 84fe931bb60e..89986e55d594 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -73,7 +73,6 @@ extern int initrd_below_start_ok; #endif #ifdef CONFIG_OF -extern u32 __dtb_start[]; void *dtb_start = __dtb_start; #endif diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index ddd7219af8ac..d4d0efe534b9 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -80,6 +80,9 @@ extern int __initdata dt_root_addr_cells; extern int __initdata dt_root_size_cells; extern struct boot_param_header *initial_boot_params; +extern char __dtb_start[]; +extern char __dtb_end[]; + /* For scanning the flat device-tree at boot time */ extern char *find_flat_dt_string(u32 offset); extern int of_scan_flat_dt(int (*it)(unsigned long node, const char *uname, -- cgit v1.2.3 From bba04d965d06abbbe10afd3687742389107e198e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 29 Mar 2014 14:14:17 -0500 Subject: of/fdt: remove unused of_scan_flat_dt_by_path of_scan_flat_dt_by_path is unused anywhere in the kernel, so remove it. Signed-off-by: Rob Herring Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 67 -------------------------------------------------- include/linux/of_fdt.h | 3 --- 2 files changed, 70 deletions(-) (limited to 'include') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 63bdcee473fa..9c8535291909 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -671,73 +671,6 @@ struct fdt_scan_status { void *data; }; -/** - * fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function - */ -static int __init fdt_scan_node_by_path(unsigned long node, const char *uname, - int depth, void *data) -{ - struct fdt_scan_status *st = data; - - /* - * if scan at the requested fdt node has been completed, - * return -ENXIO to abort further scanning - */ - if (depth <= st->depth) - return -ENXIO; - - /* requested fdt node has been found, so call iterator function */ - if (st->found) - return st->iterator(node, uname, depth, st->data); - - /* check if scanning automata is entering next level of fdt nodes */ - if (depth == st->depth + 1 && - strncmp(st->name, uname, st->namelen) == 0 && - uname[st->namelen] == 0) { - st->depth += 1; - if (st->name[st->namelen] == 0) { - st->found = 1; - } else { - const char *next = st->name + st->namelen + 1; - st->name = next; - st->namelen = strcspn(next, "/"); - } - return 0; - } - - /* scan next fdt node */ - return 0; -} - -/** - * of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each - * child of the given path. - * @path: path to start searching for children - * @it: callback function - * @data: context data pointer - * - * This function is used to scan the flattened device-tree starting from the - * node given by path. It is used to extract information (like reserved - * memory), which is required on ealy boot before we can unflatten the tree. - */ -int __init of_scan_flat_dt_by_path(const char *path, - int (*it)(unsigned long node, const char *name, int depth, void *data), - void *data) -{ - struct fdt_scan_status st = {path, 0, -1, 0, it, data}; - int ret = 0; - - if (initial_boot_params) - ret = of_scan_flat_dt(fdt_scan_node_by_path, &st); - - if (!st.found) - return -ENOENT; - else if (ret == -ENXIO) /* scan has been completed */ - return 0; - else - return ret; -} - const char * __init of_flat_dt_get_machine_name(void) { const char *name; diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index d4d0efe534b9..991ec74b4e11 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -93,9 +93,6 @@ extern void *of_get_flat_dt_prop(unsigned long node, const char *name, extern int of_flat_dt_is_compatible(unsigned long node, const char *name); extern int of_flat_dt_match(unsigned long node, const char *const *matches); extern unsigned long of_get_flat_dt_root(void); -extern int of_scan_flat_dt_by_path(const char *path, - int (*it)(unsigned long node, const char *name, int depth, void *data), - void *data); extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data); -- cgit v1.2.3 From 9d0c4dfedd96ee54fc075b16d02f82499c8cc3a6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 1 Apr 2014 23:49:03 -0500 Subject: of/fdt: update of_get_flat_dt_prop in prep for libfdt Make of_get_flat_dt_prop arguments compatible with libfdt fdt_getprop call in preparation to convert FDT code to use libfdt. Make the return value const and the property length ptr type an int. Signed-off-by: Rob Herring Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- arch/arc/kernel/devtree.c | 2 +- arch/arm/kernel/devtree.c | 2 +- arch/arm/mach-exynos/exynos.c | 2 +- arch/arm/mach-vexpress/platsmp.c | 2 +- arch/arm/plat-samsung/s5p-dev-mfc.c | 4 ++-- arch/microblaze/kernel/prom.c | 8 +++---- arch/powerpc/kernel/epapr_paravirt.c | 2 +- arch/powerpc/kernel/fadump.c | 4 ++-- arch/powerpc/kernel/prom.c | 24 +++++++++++---------- arch/powerpc/kernel/rtas.c | 2 +- arch/powerpc/mm/hash_utils_64.c | 22 +++++++++---------- arch/powerpc/platforms/52xx/efika.c | 4 ++-- arch/powerpc/platforms/chrp/setup.c | 4 ++-- arch/powerpc/platforms/powernv/opal.c | 12 +++++------ arch/powerpc/platforms/pseries/setup.c | 4 ++-- arch/xtensa/kernel/setup.c | 2 +- drivers/of/fdt.c | 39 +++++++++++++++++----------------- drivers/of/of_reserved_mem.c | 4 ++-- include/linux/of_fdt.h | 8 +++---- 19 files changed, 77 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index b6dc4e21fd32..0b3ef4025d89 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -42,7 +42,7 @@ const struct machine_desc * __init setup_machine_fdt(void *dt) const struct machine_desc *mdesc; unsigned long dt_root; void *clk; - unsigned long len; + int len; if (!early_init_dt_scan(dt)) return NULL; diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c index dff9cc0e9bd6..38f4711b4995 100644 --- a/arch/arm/kernel/devtree.c +++ b/arch/arm/kernel/devtree.c @@ -247,7 +247,7 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) if (!mdesc) { const char *prop; - long size; + int size; unsigned long dt_root; early_print("\nError: unrecognized/unsupported " diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index b32a907d021d..77293d39dfc9 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -250,7 +250,7 @@ static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname, { struct map_desc iodesc; __be32 *reg; - unsigned long len; + int len; if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid") && !of_flat_dt_is_compatible(node, "samsung,exynos5440-clock")) diff --git a/arch/arm/mach-vexpress/platsmp.c b/arch/arm/mach-vexpress/platsmp.c index 993c9ae5dc5e..b4a5f0d8390d 100644 --- a/arch/arm/mach-vexpress/platsmp.c +++ b/arch/arm/mach-vexpress/platsmp.c @@ -53,7 +53,7 @@ static int __init vexpress_dt_find_scu(unsigned long node, { if (of_flat_dt_match(node, vexpress_dt_cortex_a9_match)) { phys_addr_t phys_addr; - __be32 *reg = of_get_flat_dt_prop(node, "reg", NULL); + const __be32 *reg = of_get_flat_dt_prop(node, "reg", NULL); if (WARN_ON(!reg)) return -EINVAL; diff --git a/arch/arm/plat-samsung/s5p-dev-mfc.c b/arch/arm/plat-samsung/s5p-dev-mfc.c index 98087b655df0..469b86260fe3 100644 --- a/arch/arm/plat-samsung/s5p-dev-mfc.c +++ b/arch/arm/plat-samsung/s5p-dev-mfc.c @@ -125,8 +125,8 @@ device_initcall(s5p_mfc_memory_init); int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname, int depth, void *data) { - __be32 *prop; - unsigned long len; + const __be32 *prop; + int len; struct s5p_mfc_dt_meminfo mfc_mem; if (!data) diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c index abdfb10e7eca..c76630603058 100644 --- a/arch/microblaze/kernel/prom.c +++ b/arch/microblaze/kernel/prom.c @@ -43,13 +43,13 @@ #include #ifdef CONFIG_EARLY_PRINTK -static char *stdout; +static const char *stdout; static int __init early_init_dt_scan_chosen_serial(unsigned long node, const char *uname, int depth, void *data) { - unsigned long l; - char *p; + int l; + const char *p; pr_debug("%s: depth: %d, uname: %s\n", __func__, depth, uname); @@ -80,7 +80,7 @@ static int __init early_init_dt_scan_chosen_serial(unsigned long node, (strncmp(p, "xlnx,opb-uartlite", 17) == 0) || (strncmp(p, "xlnx,axi-uartlite", 17) == 0) || (strncmp(p, "xlnx,mdm", 8) == 0)) { - unsigned int *addrp; + const unsigned int *addrp; *(u32 *)data = UARTLITE; diff --git a/arch/powerpc/kernel/epapr_paravirt.c b/arch/powerpc/kernel/epapr_paravirt.c index 7898be90f2dc..d64e92b22dd8 100644 --- a/arch/powerpc/kernel/epapr_paravirt.c +++ b/arch/powerpc/kernel/epapr_paravirt.c @@ -36,7 +36,7 @@ static int __init early_init_dt_scan_epapr(unsigned long node, int depth, void *data) { const u32 *insts; - unsigned long len; + int len; int i; insts = of_get_flat_dt_prop(node, "hcall-instructions", &len); diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 2230fd0ca3e4..7213d930918d 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -55,9 +55,9 @@ int crash_mem_ranges; int __init early_init_dt_scan_fw_dump(unsigned long node, const char *uname, int depth, void *data) { - __be32 *sections; + const __be32 *sections; int i, num_sections; - unsigned long size; + int size; const int *token; if (depth != 1 || strcmp(uname, "rtas") != 0) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index d65754935652..483273e5c3e0 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -163,7 +163,7 @@ static struct ibm_pa_feature { {CPU_FTR_REAL_LE, PPC_FEATURE_TRUE_LE, 5, 0, 0}, }; -static void __init scan_features(unsigned long node, unsigned char *ftrs, +static void __init scan_features(unsigned long node, const unsigned char *ftrs, unsigned long tablelen, struct ibm_pa_feature *fp, unsigned long ft_size) @@ -202,8 +202,8 @@ static void __init scan_features(unsigned long node, unsigned char *ftrs, static void __init check_cpu_pa_features(unsigned long node) { - unsigned char *pa_ftrs; - unsigned long tablelen; + const unsigned char *pa_ftrs; + int tablelen; pa_ftrs = of_get_flat_dt_prop(node, "ibm,pa-features", &tablelen); if (pa_ftrs == NULL) @@ -216,7 +216,7 @@ static void __init check_cpu_pa_features(unsigned long node) #ifdef CONFIG_PPC_STD_MMU_64 static void __init check_cpu_slb_size(unsigned long node) { - __be32 *slb_size_ptr; + const __be32 *slb_size_ptr; slb_size_ptr = of_get_flat_dt_prop(node, "slb-size", NULL); if (slb_size_ptr != NULL) { @@ -257,7 +257,7 @@ static struct feature_property { static inline void identical_pvr_fixup(unsigned long node) { unsigned int pvr; - char *model = of_get_flat_dt_prop(node, "model", NULL); + const char *model = of_get_flat_dt_prop(node, "model", NULL); /* * Since 440GR(x)/440EP(x) processors have the same pvr, @@ -295,11 +295,11 @@ static int __init early_init_dt_scan_cpus(unsigned long node, const char *uname, int depth, void *data) { - char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); const __be32 *prop; const __be32 *intserv; int i, nthreads; - unsigned long len; + int len; int found = -1; int found_thread = 0; @@ -392,7 +392,7 @@ static int __init early_init_dt_scan_cpus(unsigned long node, int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname, int depth, void *data) { - unsigned long *lprop; /* All these set by kernel, so no need to convert endian */ + const unsigned long *lprop; /* All these set by kernel, so no need to convert endian */ /* Use common scan routine to determine if this is the chosen node */ if (early_init_dt_scan_chosen(node, uname, depth, data) == 0) @@ -443,8 +443,9 @@ int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname, */ static int __init early_init_dt_scan_drconf_memory(unsigned long node) { - __be32 *dm, *ls, *usm; - unsigned long l, n, flags; + const __be32 *dm, *ls, *usm; + int l; + unsigned long n, flags; u64 base, size, memblock_size; unsigned int is_kexec_kdump = 0, rngs; @@ -564,7 +565,8 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size) static void __init early_reserve_mem_dt(void) { - unsigned long i, len, dt_root; + unsigned long i, dt_root; + int len; const __be32 *prop; early_init_fdt_scan_reserved_mem(); diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 8cd5ed049b5d..8b4c857c1421 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -1142,7 +1142,7 @@ void __init rtas_initialize(void) int __init early_init_dt_scan_rtas(unsigned long node, const char *uname, int depth, void *data) { - u32 *basep, *entryp, *sizep; + const u32 *basep, *entryp, *sizep; if (depth != 1 || strcmp(uname, "rtas") != 0) return 0; diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index d766d6ee33fe..59cc19a23a7a 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -265,9 +265,9 @@ static int __init htab_dt_scan_seg_sizes(unsigned long node, const char *uname, int depth, void *data) { - char *type = of_get_flat_dt_prop(node, "device_type", NULL); - __be32 *prop; - unsigned long size = 0; + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *prop; + int size = 0; /* We are scanning "cpu" nodes only */ if (type == NULL || strcmp(type, "cpu") != 0) @@ -320,9 +320,9 @@ static int __init htab_dt_scan_page_sizes(unsigned long node, const char *uname, int depth, void *data) { - char *type = of_get_flat_dt_prop(node, "device_type", NULL); - __be32 *prop; - unsigned long size = 0; + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *prop; + int size = 0; /* We are scanning "cpu" nodes only */ if (type == NULL || strcmp(type, "cpu") != 0) @@ -402,9 +402,9 @@ static int __init htab_dt_scan_page_sizes(unsigned long node, static int __init htab_dt_scan_hugepage_blocks(unsigned long node, const char *uname, int depth, void *data) { - char *type = of_get_flat_dt_prop(node, "device_type", NULL); - __be64 *addr_prop; - __be32 *page_count_prop; + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be64 *addr_prop; + const __be32 *page_count_prop; unsigned int expected_pages; long unsigned int phys_addr; long unsigned int block_size; @@ -546,8 +546,8 @@ static int __init htab_dt_scan_pftsize(unsigned long node, const char *uname, int depth, void *data) { - char *type = of_get_flat_dt_prop(node, "device_type", NULL); - __be32 *prop; + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *prop; /* We are scanning "cpu" nodes only */ if (type == NULL || strcmp(type, "cpu") != 0) diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c index 18c104820198..6e19b0ad5d26 100644 --- a/arch/powerpc/platforms/52xx/efika.c +++ b/arch/powerpc/platforms/52xx/efika.c @@ -199,8 +199,8 @@ static void __init efika_setup_arch(void) static int __init efika_probe(void) { - char *model = of_get_flat_dt_prop(of_get_flat_dt_root(), - "model", NULL); + const char *model = of_get_flat_dt_prop(of_get_flat_dt_root(), + "model", NULL); if (model == NULL) return 0; diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index c665d7de6c99..7044fd36197b 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -574,8 +574,8 @@ chrp_init2(void) static int __init chrp_probe(void) { - char *dtype = of_get_flat_dt_prop(of_get_flat_dt_root(), - "device_type", NULL); + const char *dtype = of_get_flat_dt_prop(of_get_flat_dt_root(), + "device_type", NULL); if (dtype == NULL) return 0; if (strcmp(dtype, "chrp")) diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 49d2f00019e5..c1329846bfa3 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -61,7 +61,7 @@ int __init early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data) { const void *basep, *entryp, *sizep; - unsigned long basesz, entrysz, runtimesz; + int basesz, entrysz, runtimesz; if (depth != 1 || strcmp(uname, "ibm,opal") != 0) return 0; @@ -77,11 +77,11 @@ int __init early_init_dt_scan_opal(unsigned long node, opal.entry = of_read_number(entryp, entrysz/4); opal.size = of_read_number(sizep, runtimesz/4); - pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", + pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%d)\n", opal.base, basep, basesz); - pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", + pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%d)\n", opal.entry, entryp, entrysz); - pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%ld)\n", + pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%d)\n", opal.size, sizep, runtimesz); powerpc_firmware_features |= FW_FEATURE_OPAL; @@ -102,7 +102,7 @@ int __init early_init_dt_scan_opal(unsigned long node, int __init early_init_dt_scan_recoverable_ranges(unsigned long node, const char *uname, int depth, void *data) { - unsigned long i, psize, size; + int i, psize, size; const __be32 *prop; if (depth != 1 || strcmp(uname, "ibm,opal") != 0) @@ -359,7 +359,7 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count) if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0) return 0; len = cpu_to_be64(count); - rc = opal_console_read(vtermno, &len, buf); + rc = opal_console_read(vtermno, &len, buf); if (rc == OPAL_SUCCESS) return be64_to_cpu(len); return 0; diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 2db8cc691bf4..099d2df976a2 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -665,7 +665,7 @@ static int __init pseries_probe_fw_features(unsigned long node, void *data) { const char *prop; - unsigned long len; + int len; static int hypertas_found; static int vec5_found; @@ -698,7 +698,7 @@ static int __init pseries_probe_fw_features(unsigned long node, static int __init pSeries_probe(void) { unsigned long root = of_get_flat_dt_root(); - char *dtype = of_get_flat_dt_prop(root, "device_type", NULL); + const char *dtype = of_get_flat_dt_prop(root, "device_type", NULL); if (dtype == NULL) return 0; diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 89986e55d594..1991a3d0b2f8 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -220,7 +220,7 @@ static int __init xtensa_dt_io_area(unsigned long node, const char *uname, int depth, void *data) { const __be32 *ranges; - unsigned long len; + int len; if (depth > 1) return 0; diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 9c8535291909..1d1582bb81fb 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -35,7 +35,7 @@ char *of_fdt_get_string(struct boot_param_header *blob, u32 offset) */ void *of_fdt_get_property(struct boot_param_header *blob, unsigned long node, const char *name, - unsigned long *size) + int *size) { unsigned long p = node; @@ -85,7 +85,8 @@ int of_fdt_is_compatible(struct boot_param_header *blob, unsigned long node, const char *compat) { const char *cp; - unsigned long cplen, l, score = 0; + int cplen; + unsigned long l, score = 0; cp = of_fdt_get_property(blob, node, "compatible", &cplen); if (cp == NULL) @@ -444,8 +445,8 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, { int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); phys_addr_t base, size; - unsigned long len; - __be32 *prop; + int len; + const __be32 *prop; int nomap, first = 1; prop = of_get_flat_dt_prop(node, "reg", &len); @@ -488,7 +489,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, */ static int __init __reserved_mem_check_root(unsigned long node) { - __be32 *prop; + const __be32 *prop; prop = of_get_flat_dt_prop(node, "#size-cells", NULL); if (!prop || be32_to_cpup(prop) != dt_root_size_cells) @@ -638,8 +639,8 @@ unsigned long __init of_get_flat_dt_root(void) * This function can be used within scan_flattened_dt callback to get * access to properties */ -void *__init of_get_flat_dt_prop(unsigned long node, const char *name, - unsigned long *size) +const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, + int *size) { return of_fdt_get_property(initial_boot_params, node, name, size); } @@ -710,7 +711,7 @@ const void * __init of_flat_dt_match_machine(const void *default_match, } if (!best_data) { const char *prop; - long size; + int size; pr_err("\n unrecognized device tree list:\n[ "); @@ -739,8 +740,8 @@ const void * __init of_flat_dt_match_machine(const void *default_match, static void __init early_init_dt_check_for_initrd(unsigned long node) { u64 start, end; - unsigned long len; - __be32 *prop; + int len; + const __be32 *prop; pr_debug("Looking for initrd properties... "); @@ -773,7 +774,7 @@ static inline void early_init_dt_check_for_initrd(unsigned long node) int __init early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data) { - __be32 *prop; + const __be32 *prop; if (depth != 0) return 0; @@ -795,9 +796,9 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname, return 1; } -u64 __init dt_mem_next_cell(int s, __be32 **cellp) +u64 __init dt_mem_next_cell(int s, const __be32 **cellp) { - __be32 *p = *cellp; + const __be32 *p = *cellp; *cellp = p + s; return of_read_number(p, s); @@ -809,9 +810,9 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp) int __init early_init_dt_scan_memory(unsigned long node, const char *uname, int depth, void *data) { - char *type = of_get_flat_dt_prop(node, "device_type", NULL); - __be32 *reg, *endp; - unsigned long l; + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *reg, *endp; + int l; /* We are scanning "memory" nodes only */ if (type == NULL) { @@ -832,7 +833,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, endp = reg + (l / sizeof(__be32)); - pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n", + pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,\n", uname, l, reg[0], reg[1], reg[2], reg[3]); while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { @@ -855,8 +856,8 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data) { - unsigned long l; - char *p; + int l; + const char *p; pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index daaaf935911d..e420eb52e5c9 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -95,8 +95,8 @@ static int __init __reserved_mem_alloc_size(unsigned long node, int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); phys_addr_t start = 0, end = 0; phys_addr_t base = 0, align = 0, size; - unsigned long len; - __be32 *prop; + int len; + const __be32 *prop; int nomap; int ret; diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 991ec74b4e11..b36a50d6af37 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -66,7 +66,7 @@ extern char *of_fdt_get_string(struct boot_param_header *blob, u32 offset); extern void *of_fdt_get_property(struct boot_param_header *blob, unsigned long node, const char *name, - unsigned long *size); + int *size); extern int of_fdt_is_compatible(struct boot_param_header *blob, unsigned long node, const char *compat); @@ -88,8 +88,8 @@ extern char *find_flat_dt_string(u32 offset); extern int of_scan_flat_dt(int (*it)(unsigned long node, const char *uname, int depth, void *data), void *data); -extern void *of_get_flat_dt_prop(unsigned long node, const char *name, - unsigned long *size); +extern const void *of_get_flat_dt_prop(unsigned long node, const char *name, + int *size); extern int of_flat_dt_is_compatible(unsigned long node, const char *name); extern int of_flat_dt_match(unsigned long node, const char *const *matches); extern unsigned long of_get_flat_dt_root(void); @@ -103,7 +103,7 @@ extern void early_init_dt_add_memory_arch(u64 base, u64 size); extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, bool no_map); extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); -extern u64 dt_mem_next_cell(int s, __be32 **cellp); +extern u64 dt_mem_next_cell(int s, const __be32 **cellp); /* Early flat tree scan hooks */ extern int early_init_dt_scan_root(unsigned long node, const char *uname, -- cgit v1.2.3 From e6a6928c3ea1d0195ed75a091e345696b916c09b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 2 Apr 2014 15:10:14 -0500 Subject: of/fdt: Convert FDT functions to use libfdt The kernel FDT functions predate libfdt and are much more limited in functionality. Also, the kernel functions and libfdt functions are not compatible with each other because they have different definitions of node offsets. To avoid this incompatibility and in preparation to add more FDT parsing functions which will need libfdt, let's first convert the existing code to use libfdt. The FDT unflattening, top-level FDT scanning, and property retrieval functions are converted to use libfdt. The scanning code should be re-worked to be more efficient and understandable by using libfdt to find nodes directly by path or compatible strings. Signed-off-by: Rob Herring Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/Kconfig | 1 + drivers/of/Makefile | 2 + drivers/of/fdt.c | 209 ++++++++++++++----------------------------------- include/linux/of_fdt.h | 1 - 4 files changed, 60 insertions(+), 153 deletions(-) (limited to 'include') diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 889005fa4d04..2dcb0541012d 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -20,6 +20,7 @@ config OF_SELFTEST config OF_FLATTREE bool select DTC + select LIBFDT config OF_EARLY_FLATTREE bool diff --git a/drivers/of/Makefile b/drivers/of/Makefile index ed9660adad77..9891232f999e 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o + +CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 1d1582bb81fb..8e820a2b106d 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -19,58 +19,11 @@ #include #include #include +#include #include /* for COMMAND_LINE_SIZE */ #include -char *of_fdt_get_string(struct boot_param_header *blob, u32 offset) -{ - return ((char *)blob) + - be32_to_cpu(blob->off_dt_strings) + offset; -} - -/** - * of_fdt_get_property - Given a node in the given flat blob, return - * the property ptr - */ -void *of_fdt_get_property(struct boot_param_header *blob, - unsigned long node, const char *name, - int *size) -{ - unsigned long p = node; - - do { - u32 tag = be32_to_cpup((__be32 *)p); - u32 sz, noff; - const char *nstr; - - p += 4; - if (tag == OF_DT_NOP) - continue; - if (tag != OF_DT_PROP) - return NULL; - - sz = be32_to_cpup((__be32 *)p); - noff = be32_to_cpup((__be32 *)(p + 4)); - p += 8; - if (be32_to_cpu(blob->version) < 0x10) - p = ALIGN(p, sz >= 8 ? 8 : 4); - - nstr = of_fdt_get_string(blob, noff); - if (nstr == NULL) { - pr_warning("Can't find property index name !\n"); - return NULL; - } - if (strcmp(name, nstr) == 0) { - if (size) - *size = sz; - return (void *)p; - } - p += sz; - p = ALIGN(p, 4); - } while (1); -} - /** * of_fdt_is_compatible - Return true if given node from the given blob has * compat in its compatible list @@ -88,7 +41,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob, int cplen; unsigned long l, score = 0; - cp = of_fdt_get_property(blob, node, "compatible", &cplen); + cp = fdt_getprop(blob, node, "compatible", &cplen); if (cp == NULL) return 0; while (cplen > 0) { @@ -147,28 +100,27 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size, */ static void * unflatten_dt_node(struct boot_param_header *blob, void *mem, - void **p, + int *poffset, struct device_node *dad, struct device_node ***allnextpp, unsigned long fpsize) { + const __be32 *p; struct device_node *np; struct property *pp, **prev_pp = NULL; - char *pathp; - u32 tag; + const char *pathp; unsigned int l, allocl; + static int depth = 0; + int old_depth; + int offset; int has_name = 0; int new_format = 0; - tag = be32_to_cpup(*p); - if (tag != OF_DT_BEGIN_NODE) { - pr_err("Weird tag at start of node: %x\n", tag); + pathp = fdt_get_name(blob, *poffset, &l); + if (!pathp) return mem; - } - *p += 4; - pathp = *p; - l = allocl = strlen(pathp) + 1; - *p = PTR_ALIGN(*p + l, 4); + + allocl = l++; /* version 0x10 has a more compact unit name here instead of the full * path. we accumulate the full path size using "fpsize", we'll rebuild @@ -186,7 +138,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob, fpsize = 1; allocl = 2; l = 1; - *pathp = '\0'; + pathp = ""; } else { /* account for '/' and path size minus terminal 0 * already in 'l' @@ -233,32 +185,23 @@ static void * unflatten_dt_node(struct boot_param_header *blob, } } /* process properties */ - while (1) { - u32 sz, noff; - char *pname; - - tag = be32_to_cpup(*p); - if (tag == OF_DT_NOP) { - *p += 4; - continue; - } - if (tag != OF_DT_PROP) + for (offset = fdt_first_property_offset(blob, *poffset); + (offset >= 0); + (offset = fdt_next_property_offset(blob, offset))) { + const char *pname; + u32 sz; + + if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) { + offset = -FDT_ERR_INTERNAL; break; - *p += 4; - sz = be32_to_cpup(*p); - noff = be32_to_cpup(*p + 4); - *p += 8; - if (be32_to_cpu(blob->version) < 0x10) - *p = PTR_ALIGN(*p, sz >= 8 ? 8 : 4); - - pname = of_fdt_get_string(blob, noff); + } + if (pname == NULL) { pr_info("Can't find property name in list !\n"); break; } if (strcmp(pname, "name") == 0) has_name = 1; - l = strlen(pname) + 1; pp = unflatten_dt_alloc(&mem, sizeof(struct property), __alignof__(struct property)); if (allnextpp) { @@ -270,26 +213,25 @@ static void * unflatten_dt_node(struct boot_param_header *blob, if ((strcmp(pname, "phandle") == 0) || (strcmp(pname, "linux,phandle") == 0)) { if (np->phandle == 0) - np->phandle = be32_to_cpup((__be32*)*p); + np->phandle = be32_to_cpup(p); } /* And we process the "ibm,phandle" property * used in pSeries dynamic device tree * stuff */ if (strcmp(pname, "ibm,phandle") == 0) - np->phandle = be32_to_cpup((__be32 *)*p); - pp->name = pname; + np->phandle = be32_to_cpup(p); + pp->name = (char *)pname; pp->length = sz; - pp->value = *p; + pp->value = (__be32 *)p; *prev_pp = pp; prev_pp = &pp->next; } - *p = PTR_ALIGN((*p) + sz, 4); } /* with version 0x10 we may not have the name property, recreate * it here from the unit name if absent */ if (!has_name) { - char *p1 = pathp, *ps = pathp, *pa = NULL; + const char *p1 = pathp, *ps = pathp, *pa = NULL; int sz; while (*p1) { @@ -326,19 +268,18 @@ static void * unflatten_dt_node(struct boot_param_header *blob, if (!np->type) np->type = ""; } - while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) { - if (tag == OF_DT_NOP) - *p += 4; - else - mem = unflatten_dt_node(blob, mem, p, np, allnextpp, - fpsize); - tag = be32_to_cpup(*p); - } - if (tag != OF_DT_END_NODE) { - pr_err("Weird tag at end of node: %x\n", tag); - return mem; - } - *p += 4; + + old_depth = depth; + *poffset = fdt_next_node(blob, *poffset, &depth); + if (depth < 0) + depth = 0; + while (*poffset > 0 && depth > old_depth) + mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp, + fpsize); + + if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) + pr_err("unflatten: error %d processing FDT\n", *poffset); + return mem; } @@ -359,7 +300,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob, void * (*dt_alloc)(u64 size, u64 align)) { unsigned long size; - void *start, *mem; + int start; + void *mem; struct device_node **allnextp = mynodes; pr_debug(" -> unflatten_device_tree()\n"); @@ -380,7 +322,7 @@ static void __unflatten_device_tree(struct boot_param_header *blob, } /* First pass, scan for size */ - start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct); + start = 0; size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0); size = ALIGN(size, 4); @@ -395,10 +337,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob, pr_debug(" unflattening %p...\n", mem); /* Second pass, do actual unflattening */ - start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct); + start = 0; unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); - if (be32_to_cpup(start) != OF_DT_END) - pr_warning("Weird tag at end of tree: %08x\n", be32_to_cpup(start)); if (be32_to_cpup(mem + size) != 0xdeadbeef) pr_warning("End of tree marker overwritten: %08x\n", be32_to_cpup(mem + size)); @@ -574,47 +514,19 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, void *data), void *data) { - unsigned long p = ((unsigned long)initial_boot_params) + - be32_to_cpu(initial_boot_params->off_dt_struct); - int rc = 0; - int depth = -1; - - do { - u32 tag = be32_to_cpup((__be32 *)p); - const char *pathp; - - p += 4; - if (tag == OF_DT_END_NODE) { - depth--; - continue; - } - if (tag == OF_DT_NOP) - continue; - if (tag == OF_DT_END) - break; - if (tag == OF_DT_PROP) { - u32 sz = be32_to_cpup((__be32 *)p); - p += 8; - if (be32_to_cpu(initial_boot_params->version) < 0x10) - p = ALIGN(p, sz >= 8 ? 8 : 4); - p += sz; - p = ALIGN(p, 4); - continue; - } - if (tag != OF_DT_BEGIN_NODE) { - pr_err("Invalid tag %x in flat device tree!\n", tag); - return -EINVAL; - } - depth++; - pathp = (char *)p; - p = ALIGN(p + strlen(pathp) + 1, 4); + const void *blob = initial_boot_params; + const char *pathp; + int offset, rc = 0, depth = -1; + + for (offset = fdt_next_node(blob, -1, &depth); + offset >= 0 && depth >= 0 && !rc; + offset = fdt_next_node(blob, offset, &depth)) { + + pathp = fdt_get_name(blob, offset, NULL); if (*pathp == '/') pathp = kbasename(pathp); - rc = it(p, pathp, depth, data); - if (rc != 0) - break; - } while (1); - + rc = it(offset, pathp, depth, data); + } return rc; } @@ -623,14 +535,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node, */ unsigned long __init of_get_flat_dt_root(void) { - unsigned long p = ((unsigned long)initial_boot_params) + - be32_to_cpu(initial_boot_params->off_dt_struct); - - while (be32_to_cpup((__be32 *)p) == OF_DT_NOP) - p += 4; - BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE); - p += 4; - return ALIGN(p + strlen((char *)p) + 1, 4); + return 0; } /** @@ -642,7 +547,7 @@ unsigned long __init of_get_flat_dt_root(void) const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, int *size) { - return of_fdt_get_property(initial_boot_params, node, name, size); + return fdt_getprop(initial_boot_params, node, name, size); } /** diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index b36a50d6af37..26cef9ac55c5 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -84,7 +84,6 @@ extern char __dtb_start[]; extern char __dtb_end[]; /* For scanning the flat device-tree at boot time */ -extern char *find_flat_dt_string(u32 offset); extern int of_scan_flat_dt(int (*it)(unsigned long node, const char *uname, int depth, void *data), void *data); -- cgit v1.2.3 From c972de14971f1482ab482f0a7abc85679a23326a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 1 Apr 2014 22:48:01 -0500 Subject: of/fdt: use libfdt accessors for header data With libfdt support, we can take advantage of helper accessors in libfdt for accessing the FDT header data. This makes the code more readable and makes the FDT blob structure more opaque to the kernel. This also prepares for removing struct boot_param_header completely. Signed-off-by: Rob Herring Cc: Max Filippov Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 26 ++++++++++++-------------- include/linux/of_fdt.h | 8 ++++---- 2 files changed, 16 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 8e820a2b106d..0b38a6aa8603 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -34,7 +34,7 @@ * On match, returns a non-zero value with smaller values returned for more * specific compatible values. */ -int of_fdt_is_compatible(struct boot_param_header *blob, +int of_fdt_is_compatible(const void *blob, unsigned long node, const char *compat) { const char *cp; @@ -59,7 +59,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob, /** * of_fdt_match - Return true if node matches a list of compatible values */ -int of_fdt_match(struct boot_param_header *blob, unsigned long node, +int of_fdt_match(const void *blob, unsigned long node, const char *const *compat) { unsigned int tmp, score = 0; @@ -98,7 +98,7 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size, * @allnextpp: pointer to ->allnext from last allocated device_node * @fpsize: Size of the node path up at the current depth. */ -static void * unflatten_dt_node(struct boot_param_header *blob, +static void * unflatten_dt_node(void *blob, void *mem, int *poffset, struct device_node *dad, @@ -295,7 +295,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob, * @dt_alloc: An allocator that provides a virtual address to memory * for the resulting tree */ -static void __unflatten_device_tree(struct boot_param_header *blob, +static void __unflatten_device_tree(void *blob, struct device_node **mynodes, void * (*dt_alloc)(u64 size, u64 align)) { @@ -312,11 +312,11 @@ static void __unflatten_device_tree(struct boot_param_header *blob, } pr_debug("Unflattening device tree:\n"); - pr_debug("magic: %08x\n", be32_to_cpu(blob->magic)); - pr_debug("size: %08x\n", be32_to_cpu(blob->totalsize)); - pr_debug("version: %08x\n", be32_to_cpu(blob->version)); + pr_debug("magic: %08x\n", fdt_magic(blob)); + pr_debug("size: %08x\n", fdt_totalsize(blob)); + pr_debug("version: %08x\n", fdt_version(blob)); - if (be32_to_cpu(blob->magic) != OF_DT_HEADER) { + if (fdt_check_header(blob)) { pr_err("Invalid device tree blob header\n"); return; } @@ -363,9 +363,7 @@ static void *kernel_tree_alloc(u64 size, u64 align) void of_fdt_unflatten_tree(unsigned long *blob, struct device_node **mynodes) { - struct boot_param_header *device_tree = - (struct boot_param_header *)blob; - __unflatten_device_tree(device_tree, mynodes, &kernel_tree_alloc); + __unflatten_device_tree(blob, mynodes, &kernel_tree_alloc); } EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree); @@ -852,7 +850,7 @@ bool __init early_init_dt_scan(void *params) initial_boot_params = params; /* check device tree validity */ - if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) { + if (fdt_check_header(params)) { initial_boot_params = NULL; return false; } @@ -907,9 +905,9 @@ void __init unflatten_and_copy_device_tree(void) return; } - size = __be32_to_cpu(initial_boot_params->totalsize); + size = fdt_totalsize(initial_boot_params); dt = early_init_dt_alloc_memory_arch(size, - __alignof__(struct boot_param_header)); + roundup_pow_of_two(FDT_V17_SIZE)); if (dt) { memcpy(dt, initial_boot_params, size); diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 26cef9ac55c5..348dae2c8a3c 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -62,15 +62,15 @@ struct boot_param_header { struct device_node; /* For scanning an arbitrary device-tree at any time */ -extern char *of_fdt_get_string(struct boot_param_header *blob, u32 offset); -extern void *of_fdt_get_property(struct boot_param_header *blob, +extern char *of_fdt_get_string(const void *blob, u32 offset); +extern void *of_fdt_get_property(const void *blob, unsigned long node, const char *name, int *size); -extern int of_fdt_is_compatible(struct boot_param_header *blob, +extern int of_fdt_is_compatible(const void *blob, unsigned long node, const char *compat); -extern int of_fdt_match(struct boot_param_header *blob, unsigned long node, +extern int of_fdt_match(const void *blob, unsigned long node, const char *const *compat); extern void of_fdt_unflatten_tree(unsigned long *blob, struct device_node **mynodes); -- cgit v1.2.3 From c0556d3f2c3f42eaed049139ce6f0899ecdb0217 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 22 Apr 2014 12:55:10 -0500 Subject: of/fdt: introduce of_get_flat_dt_size Add a wrapper function to retrieve the FDT size from the FDT header. This is primarily to avoid libfdt include paths for the whole kernel. Signed-off-by: Rob Herring Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 8 ++++++++ include/linux/of_fdt.h | 1 + 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index d9e64504cda0..358bcf0500d2 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -553,6 +553,14 @@ unsigned long __init of_get_flat_dt_root(void) return 0; } +/** + * of_get_flat_dt_size - Return the total size of the FDT + */ +int __init of_get_flat_dt_size(void) +{ + return fdt_totalsize(initial_boot_params); +} + /** * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr * diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 348dae2c8a3c..e10099c95999 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -92,6 +92,7 @@ extern const void *of_get_flat_dt_prop(unsigned long node, const char *name, extern int of_flat_dt_is_compatible(unsigned long node, const char *name); extern int of_flat_dt_match(unsigned long node, const char *const *matches); extern unsigned long of_get_flat_dt_root(void); +extern int of_get_flat_dt_size(void); extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data); -- cgit v1.2.3 From 1daa0c4ced334f18f458aba6ace7e01e8cdc2ecf Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 31 Mar 2014 15:25:04 -0500 Subject: of/fdt: convert initial_boot_params to opaque pointer Now that all accesses to FDT header data has been converted to accessor helpers, initial_boot_params can become an opaque pointer. Signed-off-by: Rob Herring Tested-by: Michal Simek Tested-by: Grant Likely Tested-by: Stephen Chivers --- drivers/of/fdt.c | 2 +- include/linux/of_fdt.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 358bcf0500d2..a6f83ea107ae 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -372,7 +372,7 @@ EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree); int __initdata dt_root_addr_cells; int __initdata dt_root_size_cells; -struct boot_param_header *initial_boot_params; +void *initial_boot_params; #ifdef CONFIG_OF_EARLY_FLATTREE diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index e10099c95999..1f882e1da728 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -78,7 +78,7 @@ extern void of_fdt_unflatten_tree(unsigned long *blob, /* TBD: Temporary export of fdt globals - remove when code fully merged */ extern int __initdata dt_root_addr_cells; extern int __initdata dt_root_size_cells; -extern struct boot_param_header *initial_boot_params; +extern void *initial_boot_params; extern char __dtb_start[]; extern char __dtb_end[]; -- cgit v1.2.3 From c3fc952d2fbe3ec78defd70cf73d5d76d27092ec Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 1 Apr 2014 22:55:14 -0500 Subject: of: push struct boot_param_header and defines into powerpc Now powerpc is the only user of struct boot_param_header and FDT defines, so they can be moved into the powerpc architecture code. Signed-off-by: Rob Herring Acked-by: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: linuxppc-dev@lists.ozlabs.org Tested-by: Grant Likely Tested-by: Stephen Chivers --- arch/powerpc/include/asm/prom.h | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/of_fdt.h | 37 ------------------------------------- 2 files changed, 39 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index d977b9b78696..74b79f07f041 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -26,6 +26,45 @@ #include #include +#define OF_DT_BEGIN_NODE 0x1 /* Start of node, full name */ +#define OF_DT_END_NODE 0x2 /* End node */ +#define OF_DT_PROP 0x3 /* Property: name off, size, + * content */ +#define OF_DT_NOP 0x4 /* nop */ +#define OF_DT_END 0x9 + +#define OF_DT_VERSION 0x10 + +/* + * This is what gets passed to the kernel by prom_init or kexec + * + * The dt struct contains the device tree structure, full pathes and + * property contents. The dt strings contain a separate block with just + * the strings for the property names, and is fully page aligned and + * self contained in a page, so that it can be kept around by the kernel, + * each property name appears only once in this page (cheap compression) + * + * the mem_rsvmap contains a map of reserved ranges of physical memory, + * passing it here instead of in the device-tree itself greatly simplifies + * the job of everybody. It's just a list of u64 pairs (base/size) that + * ends when size is 0 + */ +struct boot_param_header { + __be32 magic; /* magic word OF_DT_HEADER */ + __be32 totalsize; /* total size of DT block */ + __be32 off_dt_struct; /* offset to structure */ + __be32 off_dt_strings; /* offset to strings */ + __be32 off_mem_rsvmap; /* offset to memory reserve map */ + __be32 version; /* format version */ + __be32 last_comp_version; /* last compatible version */ + /* version 2 fields below */ + __be32 boot_cpuid_phys; /* Physical CPU id we're booting on */ + /* version 3 fields below */ + __be32 dt_strings_size; /* size of the DT strings block */ + /* version 17 fields below */ + __be32 dt_struct_size; /* size of the DT structure block */ +}; + /* * OF address retreival & translation */ diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 1f882e1da728..5c0ab057eecf 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -17,45 +17,8 @@ /* Definitions used by the flattened device tree */ #define OF_DT_HEADER 0xd00dfeed /* marker */ -#define OF_DT_BEGIN_NODE 0x1 /* Start of node, full name */ -#define OF_DT_END_NODE 0x2 /* End node */ -#define OF_DT_PROP 0x3 /* Property: name off, size, - * content */ -#define OF_DT_NOP 0x4 /* nop */ -#define OF_DT_END 0x9 - -#define OF_DT_VERSION 0x10 #ifndef __ASSEMBLY__ -/* - * This is what gets passed to the kernel by prom_init or kexec - * - * The dt struct contains the device tree structure, full pathes and - * property contents. The dt strings contain a separate block with just - * the strings for the property names, and is fully page aligned and - * self contained in a page, so that it can be kept around by the kernel, - * each property name appears only once in this page (cheap compression) - * - * the mem_rsvmap contains a map of reserved ranges of physical memory, - * passing it here instead of in the device-tree itself greatly simplifies - * the job of everybody. It's just a list of u64 pairs (base/size) that - * ends when size is 0 - */ -struct boot_param_header { - __be32 magic; /* magic word OF_DT_HEADER */ - __be32 totalsize; /* total size of DT block */ - __be32 off_dt_struct; /* offset to structure */ - __be32 off_dt_strings; /* offset to strings */ - __be32 off_mem_rsvmap; /* offset to memory reserve map */ - __be32 version; /* format version */ - __be32 last_comp_version; /* last compatible version */ - /* version 2 fields below */ - __be32 boot_cpuid_phys; /* Physical CPU id we're booting on */ - /* version 3 fields below */ - __be32 dt_strings_size; /* size of the DT strings block */ - /* version 17 fields below */ - __be32 dt_struct_size; /* size of the DT structure block */ -}; #if defined(CONFIG_OF_FLATTREE) -- cgit v1.2.3 From 717abd208dff75b343243aa5ed688f62190dda5e Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Tue, 29 Apr 2014 11:24:14 +0530 Subject: KVM: Add capability to advertise PSCI v0.2 support User space (i.e. QEMU or KVMTOOL) should be able to check whether KVM ARM/ARM64 supports in-kernel PSCI v0.2 emulation. For this purpose, we define KVM_CAP_ARM_PSCI_0_2 in KVM user space interface header. Signed-off-by: Anup Patel Signed-off-by: Pranavkumar Sawargaonkar Acked-by: Christoffer Dall Acked-by: Marc Zyngier Signed-off-by: Christoffer Dall --- include/uapi/linux/kvm.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 836e15b7abc8..f3252b14fdba 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -746,6 +746,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_S390_IRQCHIP 99 #define KVM_CAP_IOEVENTFD_NO_LENGTH 100 #define KVM_CAP_VM_ATTRIBUTES 101 +#define KVM_CAP_ARM_PSCI_0_2 102 #ifdef KVM_CAP_IRQ_ROUTING -- cgit v1.2.3 From e546eea74ec66698e29c583639cf6e2a11e46490 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Tue, 29 Apr 2014 11:24:15 +0530 Subject: ARM/ARM64: KVM: Add common header for PSCI related defines We need a common place to share PSCI related defines among ARM kernel, ARM64 kernel, KVM ARM/ARM64 PSCI emulation, and user space. We introduce uapi/linux/psci.h for this purpose. This newly added header will be first used by KVM ARM/ARM64 in-kernel PSCI emulation and user space (i.e. QEMU or KVMTOOL). Signed-off-by: Anup Patel Signed-off-by: Pranavkumar Sawargaonkar Signed-off-by: Ashwin Chaugule Signed-off-by: Christoffer Dall --- include/uapi/linux/Kbuild | 1 + include/uapi/linux/psci.h | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 include/uapi/linux/psci.h (limited to 'include') diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 6929571b79b0..24e9033f8b3f 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -317,6 +317,7 @@ header-y += ppp-ioctl.h header-y += ppp_defs.h header-y += pps.h header-y += prctl.h +header-y += psci.h header-y += ptp_clock.h header-y += ptrace.h header-y += qnx4_fs.h diff --git a/include/uapi/linux/psci.h b/include/uapi/linux/psci.h new file mode 100644 index 000000000000..310d83e0a91b --- /dev/null +++ b/include/uapi/linux/psci.h @@ -0,0 +1,90 @@ +/* + * ARM Power State and Coordination Interface (PSCI) header + * + * This header holds common PSCI defines and macros shared + * by: ARM kernel, ARM64 kernel, KVM ARM/ARM64 and user space. + * + * Copyright (C) 2014 Linaro Ltd. + * Author: Anup Patel + */ + +#ifndef _UAPI_LINUX_PSCI_H +#define _UAPI_LINUX_PSCI_H + +/* + * PSCI v0.1 interface + * + * The PSCI v0.1 function numbers are implementation defined. + * + * Only PSCI return values such as: SUCCESS, NOT_SUPPORTED, + * INVALID_PARAMS, and DENIED defined below are applicable + * to PSCI v0.1. + */ + +/* PSCI v0.2 interface */ +#define PSCI_0_2_FN_BASE 0x84000000 +#define PSCI_0_2_FN(n) (PSCI_0_2_FN_BASE + (n)) +#define PSCI_0_2_64BIT 0x40000000 +#define PSCI_0_2_FN64_BASE \ + (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT) +#define PSCI_0_2_FN64(n) (PSCI_0_2_FN64_BASE + (n)) + +#define PSCI_0_2_FN_PSCI_VERSION PSCI_0_2_FN(0) +#define PSCI_0_2_FN_CPU_SUSPEND PSCI_0_2_FN(1) +#define PSCI_0_2_FN_CPU_OFF PSCI_0_2_FN(2) +#define PSCI_0_2_FN_CPU_ON PSCI_0_2_FN(3) +#define PSCI_0_2_FN_AFFINITY_INFO PSCI_0_2_FN(4) +#define PSCI_0_2_FN_MIGRATE PSCI_0_2_FN(5) +#define PSCI_0_2_FN_MIGRATE_INFO_TYPE PSCI_0_2_FN(6) +#define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7) +#define PSCI_0_2_FN_SYSTEM_OFF PSCI_0_2_FN(8) +#define PSCI_0_2_FN_SYSTEM_RESET PSCI_0_2_FN(9) + +#define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1) +#define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3) +#define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4) +#define PSCI_0_2_FN64_MIGRATE PSCI_0_2_FN64(5) +#define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7) + +/* PSCI v0.2 power state encoding for CPU_SUSPEND function */ +#define PSCI_0_2_POWER_STATE_ID_MASK 0xffff +#define PSCI_0_2_POWER_STATE_ID_SHIFT 0 +#define PSCI_0_2_POWER_STATE_TYPE_SHIFT 16 +#define PSCI_0_2_POWER_STATE_TYPE_MASK \ + (0x1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT) +#define PSCI_0_2_POWER_STATE_AFFL_SHIFT 24 +#define PSCI_0_2_POWER_STATE_AFFL_MASK \ + (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) + +/* PSCI v0.2 affinity level state returned by AFFINITY_INFO */ +#define PSCI_0_2_AFFINITY_LEVEL_ON 0 +#define PSCI_0_2_AFFINITY_LEVEL_OFF 1 +#define PSCI_0_2_AFFINITY_LEVEL_ON_PENDING 2 + +/* PSCI v0.2 multicore support in Trusted OS returned by MIGRATE_INFO_TYPE */ +#define PSCI_0_2_TOS_UP_MIGRATE 0 +#define PSCI_0_2_TOS_UP_NO_MIGRATE 1 +#define PSCI_0_2_TOS_MP 2 + +/* PSCI version decoding (independent of PSCI version) */ +#define PSCI_VERSION_MAJOR_SHIFT 16 +#define PSCI_VERSION_MINOR_MASK \ + ((1U << PSCI_VERSION_MAJOR_SHIFT) - 1) +#define PSCI_VERSION_MAJOR_MASK ~PSCI_VERSION_MINOR_MASK +#define PSCI_VERSION_MAJOR(ver) \ + (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT) +#define PSCI_VERSION_MINOR(ver) \ + ((ver) & PSCI_VERSION_MINOR_MASK) + +/* PSCI return values (inclusive of all PSCI versions) */ +#define PSCI_RET_SUCCESS 0 +#define PSCI_RET_NOT_SUPPORTED -1 +#define PSCI_RET_INVALID_PARAMS -2 +#define PSCI_RET_DENIED -3 +#define PSCI_RET_ALREADY_ON -4 +#define PSCI_RET_ON_PENDING -5 +#define PSCI_RET_INTERNAL_FAILURE -6 +#define PSCI_RET_NOT_PRESENT -7 +#define PSCI_RET_DISABLED -8 + +#endif /* _UAPI_LINUX_PSCI_H */ -- cgit v1.2.3 From 8ad6b634928a25971dc42dce101808b1491f87ec Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Tue, 29 Apr 2014 11:24:19 +0530 Subject: KVM: Add KVM_EXIT_SYSTEM_EVENT to user space API header Currently, we don't have an exit reason to notify user space about a system-level event (for e.g. system reset or shutdown) triggered by the VCPU. This patch adds exit reason KVM_EXIT_SYSTEM_EVENT for this purpose. We can also inform user space about the 'type' and architecture specific 'flags' of a system-level event using the kvm_run structure. This newly added KVM_EXIT_SYSTEM_EVENT will be used by KVM ARM/ARM64 in-kernel PSCI v0.2 support to reset/shutdown VMs. Signed-off-by: Anup Patel Signed-off-by: Pranavkumar Sawargaonkar Reviewed-by: Christoffer Dall Reviewed-by: Marc Zyngier Signed-off-by: Christoffer Dall --- Documentation/virtual/kvm/api.txt | 15 +++++++++++++++ include/uapi/linux/kvm.h | 8 ++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 556d056a9bcd..6a5de5643e0b 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2740,6 +2740,21 @@ It gets triggered whenever both KVM_CAP_PPC_EPR are enabled and an external interrupt has just been delivered into the guest. User space should put the acknowledged interrupt vector into the 'epr' field. + /* KVM_EXIT_SYSTEM_EVENT */ + struct { +#define KVM_SYSTEM_EVENT_SHUTDOWN 1 +#define KVM_SYSTEM_EVENT_RESET 2 + __u32 type; + __u64 flags; + } system_event; + +If exit_reason is KVM_EXIT_SYSTEM_EVENT then the vcpu has triggered +a system-level event using some architecture specific mechanism (hypercall +or some special instruction). In case of ARM/ARM64, this is triggered using +HVC instruction based PSCI call from the vcpu. The 'type' field describes +the system-level event type. The 'flags' field describes architecture +specific flags for the system-level event. + /* Fix the size of the union. */ char padding[256]; }; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index f3252b14fdba..16cb1a14993b 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -171,6 +171,7 @@ struct kvm_pit_config { #define KVM_EXIT_WATCHDOG 21 #define KVM_EXIT_S390_TSCH 22 #define KVM_EXIT_EPR 23 +#define KVM_EXIT_SYSTEM_EVENT 24 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -301,6 +302,13 @@ struct kvm_run { struct { __u32 epr; } epr; + /* KVM_EXIT_SYSTEM_EVENT */ + struct { +#define KVM_SYSTEM_EVENT_SHUTDOWN 1 +#define KVM_SYSTEM_EVENT_RESET 2 + __u32 type; + __u64 flags; + } system_event; /* Fix the size of the union. */ char padding[256]; }; -- cgit v1.2.3 From 37cfdaf782590e277d9352626dba4496734e0375 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 28 Apr 2014 16:34:06 +0300 Subject: usb: phy: msm: Move global regulators variables to driver state Eliminating global variables allows driver to handle multiple device instances. Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 82 ++++++++++++++++++++----------------------- include/linux/usb/msm_hsusb.h | 3 ++ 2 files changed, 42 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 5b37b81f2ef6..878f67d29ed5 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -58,47 +58,43 @@ #define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ #define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ -static struct regulator *hsusb_3p3; -static struct regulator *hsusb_1p8; -static struct regulator *hsusb_vddcx; - static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) { int ret = 0; if (init) { - hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX"); - if (IS_ERR(hsusb_vddcx)) { + motg->vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX"); + if (IS_ERR(motg->vddcx)) { dev_err(motg->phy.dev, "unable to get hsusb vddcx\n"); - return PTR_ERR(hsusb_vddcx); + return PTR_ERR(motg->vddcx); } - ret = regulator_set_voltage(hsusb_vddcx, + ret = regulator_set_voltage(motg->vddcx, USB_PHY_VDD_DIG_VOL_MIN, USB_PHY_VDD_DIG_VOL_MAX); if (ret) { dev_err(motg->phy.dev, "unable to set the voltage " "for hsusb vddcx\n"); - regulator_put(hsusb_vddcx); + regulator_put(motg->vddcx); return ret; } - ret = regulator_enable(hsusb_vddcx); + ret = regulator_enable(motg->vddcx); if (ret) { dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n"); - regulator_put(hsusb_vddcx); + regulator_put(motg->vddcx); } } else { - ret = regulator_set_voltage(hsusb_vddcx, 0, + ret = regulator_set_voltage(motg->vddcx, 0, USB_PHY_VDD_DIG_VOL_MAX); if (ret) dev_err(motg->phy.dev, "unable to set the voltage " "for hsusb vddcx\n"); - ret = regulator_disable(hsusb_vddcx); + ret = regulator_disable(motg->vddcx); if (ret) dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n"); - regulator_put(hsusb_vddcx); + regulator_put(motg->vddcx); } return ret; @@ -109,38 +105,38 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) int rc = 0; if (init) { - hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3"); - if (IS_ERR(hsusb_3p3)) { + motg->v3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3"); + if (IS_ERR(motg->v3p3)) { dev_err(motg->phy.dev, "unable to get hsusb 3p3\n"); - return PTR_ERR(hsusb_3p3); + return PTR_ERR(motg->v3p3); } - rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, + rc = regulator_set_voltage(motg->v3p3, USB_PHY_3P3_VOL_MIN, USB_PHY_3P3_VOL_MAX); if (rc) { dev_err(motg->phy.dev, "unable to set voltage level " "for hsusb 3p3\n"); goto put_3p3; } - rc = regulator_enable(hsusb_3p3); + rc = regulator_enable(motg->v3p3); if (rc) { dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n"); goto put_3p3; } - hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8"); - if (IS_ERR(hsusb_1p8)) { + motg->v1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8"); + if (IS_ERR(motg->v1p8)) { dev_err(motg->phy.dev, "unable to get hsusb 1p8\n"); - rc = PTR_ERR(hsusb_1p8); + rc = PTR_ERR(motg->v1p8); goto disable_3p3; } - rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, + rc = regulator_set_voltage(motg->v1p8, USB_PHY_1P8_VOL_MIN, USB_PHY_1P8_VOL_MAX); if (rc) { dev_err(motg->phy.dev, "unable to set voltage level " "for hsusb 1p8\n"); goto put_1p8; } - rc = regulator_enable(hsusb_1p8); + rc = regulator_enable(motg->v1p8); if (rc) { dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n"); goto put_1p8; @@ -149,54 +145,54 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) return 0; } - regulator_disable(hsusb_1p8); + regulator_disable(motg->v1p8); put_1p8: - regulator_put(hsusb_1p8); + regulator_put(motg->v1p8); disable_3p3: - regulator_disable(hsusb_3p3); + regulator_disable(motg->v3p3); put_3p3: - regulator_put(hsusb_3p3); + regulator_put(motg->v3p3); return rc; } -static int msm_hsusb_ldo_set_mode(int on) +static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, int on) { int ret = 0; - if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) { + if (!motg->v1p8 || IS_ERR(motg->v1p8)) { pr_err("%s: HSUSB_1p8 is not initialized\n", __func__); return -ENODEV; } - if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) { + if (!motg->v3p3 || IS_ERR(motg->v3p3)) { pr_err("%s: HSUSB_3p3 is not initialized\n", __func__); return -ENODEV; } if (on) { - ret = regulator_set_optimum_mode(hsusb_1p8, + ret = regulator_set_optimum_mode(motg->v1p8, USB_PHY_1P8_HPM_LOAD); if (ret < 0) { pr_err("%s: Unable to set HPM of the regulator " "HSUSB_1p8\n", __func__); return ret; } - ret = regulator_set_optimum_mode(hsusb_3p3, + ret = regulator_set_optimum_mode(motg->v3p3, USB_PHY_3P3_HPM_LOAD); if (ret < 0) { pr_err("%s: Unable to set HPM of the regulator " "HSUSB_3p3\n", __func__); - regulator_set_optimum_mode(hsusb_1p8, + regulator_set_optimum_mode(motg->v1p8, USB_PHY_1P8_LPM_LOAD); return ret; } } else { - ret = regulator_set_optimum_mode(hsusb_1p8, + ret = regulator_set_optimum_mode(motg->v1p8, USB_PHY_1P8_LPM_LOAD); if (ret < 0) pr_err("%s: Unable to set LPM of the regulator " "HSUSB_1p8\n", __func__); - ret = regulator_set_optimum_mode(hsusb_3p3, + ret = regulator_set_optimum_mode(motg->v3p3, USB_PHY_3P3_LPM_LOAD); if (ret < 0) pr_err("%s: Unable to set LPM of the regulator " @@ -417,7 +413,7 @@ static int msm_otg_reset(struct usb_phy *phy) #ifdef CONFIG_PM #define USB_PHY_SUSP_DIG_VOL 500000 -static int msm_hsusb_config_vddcx(int high) +static int msm_hsusb_config_vddcx(struct msm_otg *motg, int high) { int max_vol = USB_PHY_VDD_DIG_VOL_MAX; int min_vol; @@ -428,7 +424,7 @@ static int msm_hsusb_config_vddcx(int high) else min_vol = USB_PHY_SUSP_DIG_VOL; - ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol); + ret = regulator_set_voltage(motg->vddcx, min_vol, max_vol); if (ret) { pr_err("%s: unable to set the voltage for regulator " "HSUSB_VDDCX\n", __func__); @@ -518,8 +514,8 @@ static int msm_otg_suspend(struct msm_otg *motg) if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && motg->pdata->otg_control == OTG_PMIC_CONTROL) { - msm_hsusb_ldo_set_mode(0); - msm_hsusb_config_vddcx(0); + msm_hsusb_ldo_set_mode(motg, 0); + msm_hsusb_config_vddcx(motg, 0); } if (device_may_wakeup(phy->dev)) @@ -555,8 +551,8 @@ static int msm_otg_resume(struct msm_otg *motg) if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && motg->pdata->otg_control == OTG_PMIC_CONTROL) { - msm_hsusb_ldo_set_mode(1); - msm_hsusb_config_vddcx(1); + msm_hsusb_ldo_set_mode(motg, 1); + msm_hsusb_config_vddcx(motg, 1); writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); } @@ -1521,7 +1517,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) dev_err(&pdev->dev, "hsusb vreg configuration failed\n"); goto vddcx_exit; } - ret = msm_hsusb_ldo_set_mode(1); + ret = msm_hsusb_ldo_set_mode(motg, 1); if (ret) { dev_err(&pdev->dev, "hsusb vreg enable failed\n"); goto ldo_exit; diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 32754835a39b..8705b0164684 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -183,6 +183,9 @@ struct msm_otg { enum usb_chg_state chg_state; enum usb_chg_type chg_type; u8 dcd_retries; + struct regulator *v3p3; + struct regulator *v1p8; + struct regulator *vddcx; }; #endif -- cgit v1.2.3 From 971232cf7c7a71ad3cbf433f592eee3ae1a578ac Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 28 Apr 2014 16:34:11 +0300 Subject: usb: phy: msm: Replace custom enum usb_mode_type with enum usb_dr_mode Use enum usb_dr_mode and drop default usb_dr_mode from platform data. USB DT bindings states: dr_mode: "...In case this attribute isn't passed via DT, USB DRD controllers should default to OTG...", so remove redundand field. Signed-off-by: Ivan T. Ivanov Acked-by: David Brown Signed-off-by: Felipe Balbi --- arch/arm/mach-msm/board-msm7x30.c | 2 +- arch/arm/mach-msm/board-qsd8x50.c | 2 +- drivers/usb/phy/phy-msm-usb.c | 41 ++++++++++++++++----------------------- include/linux/usb/msm_hsusb.h | 20 +------------------ 4 files changed, 20 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 46de789ad3ae..0c4c200e1221 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -95,7 +95,7 @@ static int hsusb_phy_clk_reset(struct clk *phy_clk) static struct msm_otg_platform_data msm_otg_pdata = { .phy_init_seq = hsusb_phy_init_seq, - .mode = USB_PERIPHERAL, + .mode = USB_DR_MODE_PERIPHERAL, .otg_control = OTG_PHY_CONTROL, .link_clk_reset = hsusb_link_clk_reset, .phy_clk_reset = hsusb_phy_clk_reset, diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c index 9169ec324a43..4c748616ef47 100644 --- a/arch/arm/mach-msm/board-qsd8x50.c +++ b/arch/arm/mach-msm/board-qsd8x50.c @@ -116,7 +116,7 @@ static int hsusb_phy_clk_reset(struct clk *phy_clk) static struct msm_otg_platform_data msm_otg_pdata = { .phy_init_seq = hsusb_phy_init_seq, - .mode = USB_PERIPHERAL, + .mode = USB_DR_MODE_PERIPHERAL, .otg_control = OTG_PHY_CONTROL, .link_clk_reset = hsusb_link_clk_reset, .phy_clk_reset = hsusb_phy_clk_reset, diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 874c51a85683..7eb2abf3f874 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -348,10 +348,10 @@ static int msm_otg_reset(struct usb_phy *phy) if (pdata->otg_control == OTG_PHY_CONTROL) { val = readl(USB_OTGSC); - if (pdata->mode == USB_OTG) { + if (pdata->mode == USB_DR_MODE_OTG) { ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; val |= OTGSC_IDIE | OTGSC_BSVIE; - } else if (pdata->mode == USB_PERIPHERAL) { + } else if (pdata->mode == USB_DR_MODE_PERIPHERAL) { ulpi_val = ULPI_INT_SESS_VALID; val |= OTGSC_BSVIE; } @@ -637,7 +637,7 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) * Fail host registration if this board can support * only peripheral configuration. */ - if (motg->pdata->mode == USB_PERIPHERAL) { + if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL) { dev_info(otg->phy->dev, "Host mode is not supported\n"); return -ENODEV; } @@ -666,7 +666,7 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host) * Kick the state machine work, if peripheral is not supported * or peripheral is already registered with us. */ - if (motg->pdata->mode == USB_HOST || otg->gadget) { + if (motg->pdata->mode == USB_DR_MODE_HOST || otg->gadget) { pm_runtime_get_sync(otg->phy->dev); schedule_work(&motg->sm_work); } @@ -710,7 +710,7 @@ static int msm_otg_set_peripheral(struct usb_otg *otg, * Fail peripheral registration if this board can support * only host configuration. */ - if (motg->pdata->mode == USB_HOST) { + if (motg->pdata->mode == USB_DR_MODE_HOST) { dev_info(otg->phy->dev, "Peripheral mode is not supported\n"); return -ENODEV; } @@ -735,7 +735,7 @@ static int msm_otg_set_peripheral(struct usb_otg *otg, * Kick the state machine work, if host is not supported * or host is already registered with us. */ - if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { + if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL || otg->host) { pm_runtime_get_sync(otg->phy->dev); schedule_work(&motg->sm_work); } @@ -1056,7 +1056,7 @@ static void msm_otg_init_sm(struct msm_otg *motg) u32 otgsc = readl(USB_OTGSC); switch (pdata->mode) { - case USB_OTG: + case USB_DR_MODE_OTG: if (pdata->otg_control == OTG_PHY_CONTROL) { if (otgsc & OTGSC_ID) set_bit(ID, &motg->inputs); @@ -1068,21 +1068,14 @@ static void msm_otg_init_sm(struct msm_otg *motg) else clear_bit(B_SESS_VLD, &motg->inputs); } else if (pdata->otg_control == OTG_USER_CONTROL) { - if (pdata->default_mode == USB_HOST) { - clear_bit(ID, &motg->inputs); - } else if (pdata->default_mode == USB_PERIPHERAL) { - set_bit(ID, &motg->inputs); - set_bit(B_SESS_VLD, &motg->inputs); - } else { set_bit(ID, &motg->inputs); clear_bit(B_SESS_VLD, &motg->inputs); - } } break; - case USB_HOST: + case USB_DR_MODE_HOST: clear_bit(ID, &motg->inputs); break; - case USB_PERIPHERAL: + case USB_DR_MODE_PERIPHERAL: set_bit(ID, &motg->inputs); if (otgsc & OTGSC_BSV) set_bit(B_SESS_VLD, &motg->inputs); @@ -1258,7 +1251,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, char buf[16]; struct usb_otg *otg = motg->phy.otg; int status = count; - enum usb_mode_type req_mode; + enum usb_dr_mode req_mode; memset(buf, 0x00, sizeof(buf)); @@ -1268,18 +1261,18 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, } if (!strncmp(buf, "host", 4)) { - req_mode = USB_HOST; + req_mode = USB_DR_MODE_HOST; } else if (!strncmp(buf, "peripheral", 10)) { - req_mode = USB_PERIPHERAL; + req_mode = USB_DR_MODE_PERIPHERAL; } else if (!strncmp(buf, "none", 4)) { - req_mode = USB_NONE; + req_mode = USB_DR_MODE_UNKNOWN; } else { status = -EINVAL; goto out; } switch (req_mode) { - case USB_NONE: + case USB_DR_MODE_UNKNOWN: switch (otg->phy->state) { case OTG_STATE_A_HOST: case OTG_STATE_B_PERIPHERAL: @@ -1290,7 +1283,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, goto out; } break; - case USB_PERIPHERAL: + case USB_DR_MODE_PERIPHERAL: switch (otg->phy->state) { case OTG_STATE_B_IDLE: case OTG_STATE_A_HOST: @@ -1301,7 +1294,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, goto out; } break; - case USB_HOST: + case USB_DR_MODE_HOST: switch (otg->phy->state) { case OTG_STATE_B_IDLE: case OTG_STATE_B_PERIPHERAL: @@ -1511,7 +1504,7 @@ static int msm_otg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, motg); device_init_wakeup(&pdev->dev, 1); - if (motg->pdata->mode == USB_OTG && + if (motg->pdata->mode == USB_DR_MODE_OTG && motg->pdata->otg_control == OTG_USER_CONTROL) { ret = msm_otg_debugfs_init(motg); if (ret) diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 8705b0164684..72c5830455bf 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -22,21 +22,6 @@ #include #include -/** - * Supported USB modes - * - * USB_PERIPHERAL Only peripheral mode is supported. - * USB_HOST Only host mode is supported. - * USB_OTG OTG mode is supported. - * - */ -enum usb_mode_type { - USB_NONE = 0, - USB_PERIPHERAL, - USB_HOST, - USB_OTG, -}; - /** * OTG control * @@ -121,8 +106,6 @@ enum usb_chg_type { * @power_budget: VBUS power budget in mA (0 will be treated as 500mA). * @mode: Supported mode (OTG/peripheral/host). * @otg_control: OTG switch controlled by user/Id pin - * @default_mode: Default operational mode. Applicable only if - * OTG switch is controller by user. * @pclk_src_name: pclk is derived from ebi1_usb_clk in case of 7x27 and 8k * dfab_usb_hs_clk in case of 8660 and 8960. */ @@ -130,9 +113,8 @@ struct msm_otg_platform_data { int *phy_init_seq; void (*vbus_power)(bool on); unsigned power_budget; - enum usb_mode_type mode; + enum usb_dr_mode mode; enum otg_control_type otg_control; - enum usb_mode_type default_mode; enum msm_usb_phy_type phy_type; void (*setup_gpio)(enum usb_otg_state state); char *pclk_src_name; -- cgit v1.2.3 From ff0e4a68c931dc34e43c081d1b6a895a9aaf8a2b Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 28 Apr 2014 16:34:12 +0300 Subject: usb: phy: msm: Remove unused pclk_src_name There are no references to 'pclk_src_name' in plaform code, so it is unused. Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 26 +------------------------- include/linux/usb/msm_hsusb.h | 5 ----- 2 files changed, 1 insertion(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 7eb2abf3f874..c2361bfd2002 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -464,9 +464,6 @@ static int msm_otg_suspend(struct msm_otg *motg) if (!IS_ERR(motg->core_clk)) clk_disable_unprepare(motg->core_clk); - if (!IS_ERR(motg->pclk_src)) - clk_disable_unprepare(motg->pclk_src); - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && motg->pdata->otg_control == OTG_PMIC_CONTROL) { msm_hsusb_ldo_set_mode(motg, 0); @@ -496,9 +493,6 @@ static int msm_otg_resume(struct msm_otg *motg) if (!atomic_read(&motg->in_lpm)) return 0; - if (!IS_ERR(motg->pclk_src)) - clk_prepare_enable(motg->pclk_src); - clk_prepare_enable(motg->pclk); clk_prepare_enable(motg->clk); if (!IS_ERR(motg->core_clk)) @@ -1396,17 +1390,8 @@ static int msm_otg_probe(struct platform_device *pdev) * If USB Core is running its protocol engine based on CORE CLK, * CORE CLK must be running at >55Mhz for correct HSUSB * operation and USB core cannot tolerate frequency changes on - * CORE CLK. For such USB cores, vote for maximum clk frequency - * on pclk source + * CORE CLK. */ - motg->pclk_src = ERR_PTR(-ENOENT); - if (motg->pdata->pclk_src_name) { - motg->pclk_src = devm_clk_get(&pdev->dev, - motg->pdata->pclk_src_name); - if (IS_ERR(motg->pclk_src)) - return PTR_ERR(motg->pclk_src); - } - motg->pclk = devm_clk_get(&pdev->dev, "usb_hs_pclk"); if (IS_ERR(motg->pclk)) { dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); @@ -1446,10 +1431,6 @@ static int msm_otg_probe(struct platform_device *pdev) motg->v1p8 = regs[2].consumer; clk_set_rate(motg->clk, 60000000); - if (!IS_ERR(motg->pclk_src)) { - clk_set_rate(motg->pclk_src, INT_MAX); - clk_prepare_enable(motg->pclk_src); - } clk_prepare_enable(motg->clk); clk_prepare_enable(motg->pclk); @@ -1525,8 +1506,6 @@ disable_clks: clk_disable_unprepare(motg->clk); if (!IS_ERR(motg->core_clk)) clk_disable_unprepare(motg->core_clk); - if (!IS_ERR(motg->pclk_src)) - clk_disable_unprepare(motg->pclk_src); return ret; } @@ -1571,9 +1550,6 @@ static int msm_otg_remove(struct platform_device *pdev) clk_disable_unprepare(motg->clk); if (!IS_ERR(motg->core_clk)) clk_disable_unprepare(motg->core_clk); - if (!IS_ERR(motg->pclk_src)) - clk_disable_unprepare(motg->pclk_src); - msm_hsusb_ldo_init(motg, 0); pm_runtime_set_suspended(&pdev->dev); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 72c5830455bf..262ed80a0b9e 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -106,8 +106,6 @@ enum usb_chg_type { * @power_budget: VBUS power budget in mA (0 will be treated as 500mA). * @mode: Supported mode (OTG/peripheral/host). * @otg_control: OTG switch controlled by user/Id pin - * @pclk_src_name: pclk is derived from ebi1_usb_clk in case of 7x27 and 8k - * dfab_usb_hs_clk in case of 8660 and 8960. */ struct msm_otg_platform_data { int *phy_init_seq; @@ -117,7 +115,6 @@ struct msm_otg_platform_data { enum otg_control_type otg_control; enum msm_usb_phy_type phy_type; void (*setup_gpio)(enum usb_otg_state state); - char *pclk_src_name; int (*link_clk_reset)(struct clk *link_clk, bool assert); int (*phy_clk_reset)(struct clk *phy_clk); }; @@ -129,7 +126,6 @@ struct msm_otg_platform_data { * @irq: IRQ number assigned for HSUSB controller. * @clk: clock struct of usb_hs_clk. * @pclk: clock struct of usb_hs_pclk. - * @pclk_src: pclk source for voting. * @phy_reset_clk: clock struct of usb_phy_clk. * @core_clk: clock struct of usb_hs_core_clk. * @regs: ioremapped register base address. @@ -150,7 +146,6 @@ struct msm_otg { int irq; struct clk *clk; struct clk *pclk; - struct clk *pclk_src; struct clk *phy_reset_clk; struct clk *core_clk; void __iomem *regs; -- cgit v1.2.3 From 8364f9af237f47fa128bd4e4f7b45beef890c994 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 28 Apr 2014 16:34:15 +0300 Subject: usb: phy: msm: Add device tree support and binding information Allows controller to be specified via device tree. Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/msm-hsusb.txt | 67 ++++++++++++ drivers/usb/phy/phy-msm-usb.c | 113 +++++++++++++++++---- include/linux/usb/msm_hsusb.h | 6 +- 3 files changed, 165 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt index 5ea26c631e3a..ee4123de3de4 100644 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt @@ -15,3 +15,70 @@ Example EHCI controller device node: usb-phy = <&usb_otg>; }; +USB PHY with optional OTG: + +Required properties: +- compatible: Should contain: + "qcom,usb-otg-ci" for chipsets with ChipIdea 45nm PHY + "qcom,usb-otg-snps" for chipsets with Synopsys 28nm PHY + +- regs: Offset and length of the register set in the memory map +- interrupts: interrupt-specifier for the OTG interrupt. + +- clocks: A list of phandle + clock-specifier pairs for the + clocks listed in clock-names +- clock-names: Should contain the following: + "phy" USB PHY reference clock + "core" Protocol engine clock + "iface" Interface bus clock + "alt_core" Protocol engine clock for targets with asynchronous + reset methodology. (optional) + +- vdccx-supply: phandle to the regulator for the vdd supply for + digital circuit operation. +- v1p8-supply: phandle to the regulator for the 1.8V supply +- v3p3-supply: phandle to the regulator for the 3.3V supply + +- resets: A list of phandle + reset-specifier pairs for the + resets listed in reset-names +- reset-names: Should contain the following: + "phy" USB PHY controller reset + "link" USB LINK controller reset + +- qcom,otg-control: OTG control (VBUS and ID notifications) can be one of + 1 - PHY control + 2 - PMIC control + +Optional properties: +- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" + +- qcom,phy-init-sequence: PHY configuration sequence values. This is related to Device + Mode Eye Diagram test. Start address at which these values will be + written is ULPI_EXT_VENDOR_SPECIFIC. Value of -1 is reserved as + "do not overwrite default value at this address". + For example: qcom,phy-init-sequence = < -1 0x63 >; + Will update only value at address ULPI_EXT_VENDOR_SPECIFIC + 1. + +Example HSUSB OTG controller device node: + + usb@f9a55000 { + compatible = "qcom,usb-otg-snps"; + reg = <0xf9a55000 0x400>; + interrupts = <0 134 0>; + dr_mode = "peripheral"; + + clocks = <&gcc GCC_XO_CLK>, <&gcc GCC_USB_HS_SYSTEM_CLK>, + <&gcc GCC_USB_HS_AHB_CLK>; + + clock-names = "phy", "core", "iface"; + + vddcx-supply = <&pm8841_s2_corner>; + v1p8-supply = <&pm8941_l6>; + v3p3-supply = <&pm8941_l24>; + + resets = <&gcc GCC_USB2A_PHY_BCR>, <&gcc GCC_USB_HS_BCR>; + reset-names = "phy", "link"; + + qcom,otg-control = <1>; + qcom,phy-init-sequence = < -1 0x63 >; + }; diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 7e968aa143ce..1bf2d4ee29d2 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -30,9 +30,12 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -217,16 +220,16 @@ static struct usb_phy_io_ops msm_otg_io_ops = { static void ulpi_init(struct msm_otg *motg) { struct msm_otg_platform_data *pdata = motg->pdata; - int *seq = pdata->phy_init_seq; + int *seq = pdata->phy_init_seq, idx; + u32 addr = ULPI_EXT_VENDOR_SPECIFIC; - if (!seq) - return; + for (idx = 0; idx < pdata->phy_init_sz; idx++) { + if (seq[idx] == -1) + continue; - while (seq[0] >= 0) { dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n", - seq[0], seq[1]); - ulpi_write(&motg->phy, seq[0], seq[1]); - seq += 2; + seq[idx], addr + idx); + ulpi_write(&motg->phy, seq[idx], addr + idx); } } @@ -1343,26 +1346,96 @@ static void msm_otg_debugfs_cleanup(void) debugfs_remove(msm_otg_dbg_root); } +static struct of_device_id msm_otg_dt_match[] = { + { + .compatible = "qcom,usb-otg-ci", + .data = (void *) CI_45NM_INTEGRATED_PHY + }, + { + .compatible = "qcom,usb-otg-snps", + .data = (void *) SNPS_28NM_INTEGRATED_PHY + }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_otg_dt_match); + +static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) +{ + struct msm_otg_platform_data *pdata; + const struct of_device_id *id; + struct device_node *node = pdev->dev.of_node; + struct property *prop; + int len, ret, words; + u32 val; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + motg->pdata = pdata; + + id = of_match_device(msm_otg_dt_match, &pdev->dev); + pdata->phy_type = (int) id->data; + + pdata->mode = of_usb_get_dr_mode(node); + if (pdata->mode == USB_DR_MODE_UNKNOWN) + pdata->mode = USB_DR_MODE_OTG; + + pdata->otg_control = OTG_PHY_CONTROL; + if (!of_property_read_u32(node, "qcom,otg-control", &val)) + if (val == OTG_PMIC_CONTROL) + pdata->otg_control = val; + + prop = of_find_property(node, "qcom,phy-init-sequence", &len); + if (!prop || !len) + return 0; + + words = len / sizeof(u32); + + if (words >= ULPI_EXT_VENDOR_SPECIFIC) { + dev_warn(&pdev->dev, "Too big PHY init sequence %d\n", words); + return 0; + } + + pdata->phy_init_seq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!pdata->phy_init_seq) { + dev_warn(&pdev->dev, "No space for PHY init sequence\n"); + return 0; + } + + ret = of_property_read_u32_array(node, "qcom,phy-init-sequence", + pdata->phy_init_seq, words); + if (!ret) + pdata->phy_init_sz = words; + + return 0; +} + static int msm_otg_probe(struct platform_device *pdev) { struct regulator_bulk_data regs[3]; int ret = 0; + struct device_node *np = pdev->dev.of_node; + struct msm_otg_platform_data *pdata; struct resource *res; struct msm_otg *motg; struct usb_phy *phy; - dev_info(&pdev->dev, "msm_otg probe\n"); - if (!dev_get_platdata(&pdev->dev)) { - dev_err(&pdev->dev, "No platform data given. Bailing out\n"); - return -ENODEV; - } - motg = devm_kzalloc(&pdev->dev, sizeof(struct msm_otg), GFP_KERNEL); if (!motg) { dev_err(&pdev->dev, "unable to allocate msm_otg\n"); return -ENOMEM; } + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + if (!np) + return -ENXIO; + ret = msm_otg_read_dt(pdev, motg); + if (ret) + return ret; + } + motg->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), GFP_KERNEL); if (!motg->phy.otg) { @@ -1370,17 +1443,17 @@ static int msm_otg_probe(struct platform_device *pdev) return -ENOMEM; } - motg->pdata = dev_get_platdata(&pdev->dev); phy = &motg->phy; phy->dev = &pdev->dev; - motg->phy_reset_clk = devm_clk_get(&pdev->dev, "usb_phy_clk"); + motg->phy_reset_clk = devm_clk_get(&pdev->dev, + np ? "phy" : "usb_phy_clk"); if (IS_ERR(motg->phy_reset_clk)) { dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); return PTR_ERR(motg->phy_reset_clk); } - motg->clk = devm_clk_get(&pdev->dev, "usb_hs_clk"); + motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk"); if (IS_ERR(motg->clk)) { dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); return PTR_ERR(motg->clk); @@ -1392,7 +1465,7 @@ static int msm_otg_probe(struct platform_device *pdev) * operation and USB core cannot tolerate frequency changes on * CORE CLK. */ - motg->pclk = devm_clk_get(&pdev->dev, "usb_hs_pclk"); + motg->pclk = devm_clk_get(&pdev->dev, np ? "iface" : "usb_hs_pclk"); if (IS_ERR(motg->pclk)) { dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); return PTR_ERR(motg->pclk); @@ -1403,7 +1476,8 @@ static int msm_otg_probe(struct platform_device *pdev) * clock is introduced to remove the dependency on AXI * bus frequency. */ - motg->core_clk = devm_clk_get(&pdev->dev, "usb_hs_core_clk"); + motg->core_clk = devm_clk_get(&pdev->dev, + np ? "alt_core" : "usb_hs_core_clk"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); motg->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); @@ -1486,7 +1560,7 @@ static int msm_otg_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); if (motg->pdata->mode == USB_DR_MODE_OTG && - motg->pdata->otg_control == OTG_USER_CONTROL) { + motg->pdata->otg_control == OTG_USER_CONTROL) { ret = msm_otg_debugfs_init(motg); if (ret) dev_dbg(&pdev->dev, "Can not create mode change file\n"); @@ -1639,6 +1713,7 @@ static struct platform_driver msm_otg_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .pm = &msm_otg_dev_pm_ops, + .of_match_table = msm_otg_dt_match, }, }; diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 262ed80a0b9e..bd68299c278e 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -100,8 +100,9 @@ enum usb_chg_type { /** * struct msm_otg_platform_data - platform device data * for msm_otg driver. - * @phy_init_seq: PHY configuration sequence. val, reg pairs - * terminated by -1. + * @phy_init_seq: PHY configuration sequence values. Value of -1 is reserved as + * "do not overwrite default vaule at this address". + * @phy_init_sz: PHY configuration sequence size. * @vbus_power: VBUS power on/off routine. * @power_budget: VBUS power budget in mA (0 will be treated as 500mA). * @mode: Supported mode (OTG/peripheral/host). @@ -109,6 +110,7 @@ enum usb_chg_type { */ struct msm_otg_platform_data { int *phy_init_seq; + int phy_init_sz; void (*vbus_power)(bool on); unsigned power_budget; enum usb_dr_mode mode; -- cgit v1.2.3 From a27345434134080273e0597e1d9721ff9e6ca67f Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 28 Apr 2014 16:34:16 +0300 Subject: usb: phy: msm: Use reset framework for LINK and PHY resets Using reset framework eliminate need of platform specific callbacks and enable reset lines to be specified in DT files. Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 29 +++++++++++++++++++++-------- include/linux/usb/msm_hsusb.h | 3 +++ 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 1bf2d4ee29d2..a6abb1b3a7f0 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -235,12 +236,15 @@ static void ulpi_init(struct msm_otg *motg) static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) { - int ret = 0; + int ret; - if (!motg->pdata->link_clk_reset) - return ret; + if (motg->pdata->link_clk_reset) + ret = motg->pdata->link_clk_reset(motg->clk, assert); + else if (assert) + ret = reset_control_assert(motg->link_rst); + else + ret = reset_control_deassert(motg->link_rst); - ret = motg->pdata->link_clk_reset(motg->clk, assert); if (ret) dev_err(motg->phy.dev, "usb link clk reset %s failed\n", assert ? "assert" : "deassert"); @@ -250,12 +254,13 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) static int msm_otg_phy_clk_reset(struct msm_otg *motg) { - int ret = 0; + int ret; - if (!motg->pdata->phy_clk_reset) - return ret; + if (motg->pdata->phy_clk_reset) + ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk); + else + ret = reset_control_reset(motg->phy_rst); - ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk); if (ret) dev_err(motg->phy.dev, "usb phy clk reset failed\n"); @@ -1377,6 +1382,14 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) id = of_match_device(msm_otg_dt_match, &pdev->dev); pdata->phy_type = (int) id->data; + motg->link_rst = devm_reset_control_get(&pdev->dev, "link"); + if (IS_ERR(motg->link_rst)) + return PTR_ERR(motg->link_rst); + + motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy"); + if (IS_ERR(motg->phy_rst)) + return PTR_ERR(motg->phy_rst); + pdata->mode = of_usb_get_dr_mode(node); if (pdata->mode == USB_DR_MODE_UNKNOWN) pdata->mode = USB_DR_MODE_OTG; diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index bd68299c278e..4e5d9168f52e 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -165,6 +165,9 @@ struct msm_otg { struct regulator *v3p3; struct regulator *v1p8; struct regulator *vddcx; + + struct reset_control *phy_rst; + struct reset_control *link_rst; }; #endif -- cgit v1.2.3 From cfa3ff5dfe6a11ac8bc4a080416984ab00b0980c Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 28 Apr 2014 16:34:17 +0300 Subject: usb: phy: msm: Add support for secondary PHY control Allow support to use 2nd HSPHY with USB2 Core. Some platforms may have configuration to allow USB controller work with any of the two HSPHYs present. By default driver configures USB core to use primary HSPHY. Add support to allow user select 2nd HSPHY using DT parameter. Signed-off-by: Ivan T. Ivanov Cc: Manu Gautam Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/msm-hsusb.txt | 6 ++++++ drivers/usb/phy/phy-msm-usb.c | 24 ++++++++++++++++++++-- include/linux/usb/msm_hsusb.h | 1 + include/linux/usb/msm_hsusb_hw.h | 1 + 4 files changed, 30 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt index ee4123de3de4..066966706ca1 100644 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt @@ -59,6 +59,12 @@ Optional properties: For example: qcom,phy-init-sequence = < -1 0x63 >; Will update only value at address ULPI_EXT_VENDOR_SPECIFIC + 1. +- qcom,phy-num: Select number of pyco-phy to use, can be one of + 0 - PHY one, default + 1 - Second PHY + Some platforms may have configuration to allow USB + controller work with any of the two HSPHYs present. + Example HSUSB OTG controller device node: usb@f9a55000 { diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index a6abb1b3a7f0..8d57045ac938 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -314,6 +314,9 @@ static int msm_otg_phy_reset(struct msm_otg *motg) if (!retries) return -ETIMEDOUT; + if (motg->phy_number) + writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); + dev_info(motg->phy.dev, "phy_reset: success\n"); return 0; } @@ -368,6 +371,9 @@ static int msm_otg_reset(struct usb_phy *phy) ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); } + if (motg->phy_number) + writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); + return 0; } @@ -404,6 +410,7 @@ static int msm_otg_suspend(struct msm_otg *motg) struct usb_phy *phy = &motg->phy; struct usb_bus *bus = phy->otg->host; struct msm_otg_platform_data *pdata = motg->pdata; + void __iomem *addr; int cnt = 0; if (atomic_read(&motg->in_lpm)) @@ -463,9 +470,13 @@ static int msm_otg_suspend(struct msm_otg *motg) */ writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); + addr = USB_PHY_CTRL; + if (motg->phy_number) + addr = USB_PHY_CTRL2; + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && motg->pdata->otg_control == OTG_PMIC_CONTROL) - writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL); + writel(readl(addr) | PHY_RETEN, addr); clk_disable_unprepare(motg->pclk); clk_disable_unprepare(motg->clk); @@ -495,6 +506,7 @@ static int msm_otg_resume(struct msm_otg *motg) { struct usb_phy *phy = &motg->phy; struct usb_bus *bus = phy->otg->host; + void __iomem *addr; int cnt = 0; unsigned temp; @@ -508,9 +520,14 @@ static int msm_otg_resume(struct msm_otg *motg) if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && motg->pdata->otg_control == OTG_PMIC_CONTROL) { + + addr = USB_PHY_CTRL; + if (motg->phy_number) + addr = USB_PHY_CTRL2; + msm_hsusb_ldo_set_mode(motg, 1); msm_hsusb_config_vddcx(motg, 1); - writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); + writel(readl(addr) & ~PHY_RETEN, addr); } temp = readl(USB_USBCMD); @@ -1399,6 +1416,9 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) if (val == OTG_PMIC_CONTROL) pdata->otg_control = val; + if (!of_property_read_u32(node, "qcom,phy-num", &val) && val < 2) + motg->phy_number = val; + prop = of_find_property(node, "qcom,phy-init-sequence", &len); if (!prop || !len) return 0; diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 4e5d9168f52e..4628f1a4713e 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -158,6 +158,7 @@ struct msm_otg { atomic_t in_lpm; int async_int; unsigned cur_power; + int phy_number; struct delayed_work chg_work; enum usb_chg_state chg_state; enum usb_chg_type chg_type; diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index 6e97a2d3d39f..e6d703567155 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -25,6 +25,7 @@ #define USB_OTGSC (MSM_USB_BASE + 0x01A4) #define USB_USBMODE (MSM_USB_BASE + 0x01A8) #define USB_PHY_CTRL (MSM_USB_BASE + 0x0240) +#define USB_PHY_CTRL2 (MSM_USB_BASE + 0x0278) #define USBCMD_RESET 2 #define USB_USBINTR (MSM_USB_BASE + 0x0148) -- cgit v1.2.3 From d69c6f5df376ea40df5886468b155f515fddfbb2 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 28 Apr 2014 16:34:18 +0300 Subject: usb: phy: msm: Correct USB PHY Reset sequence for newer platform On few legacy platforms, USB PHY is having dedicated reset clk. It is used to reset USB PHY after putting USB PHY into low power mode and for calibration of USB PHY. Putting USB PHY into low power mode is causing ulpi read/write timeout as expected. USB PHY reset clk is not available on newer platform. For 28nm PHY, reset USB PHY after resetting USB LINK. Also reset USB PHY using USB_PHY_PON bit with USB_OTG_HS_PHY_CTRL register after programming USB PHY Override registers as suggested with hardware programming guidelines. Signed-off-by: Ivan T. Ivanov Signed-off-by: Tim Bird Cc: Mayank Rana Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 140 ++++++++++++++++++++++++--------------- include/linux/usb/msm_hsusb_hw.h | 5 ++ 2 files changed, 93 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 8d57045ac938..bb339963f8bb 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -48,6 +48,7 @@ #define DRIVER_NAME "msm_otg" #define ULPI_IO_TIMEOUT_USEC (10 * 1000) +#define LINK_RESET_TIMEOUT_USEC (250 * 1000) #define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ #define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ @@ -267,77 +268,35 @@ static int msm_otg_phy_clk_reset(struct msm_otg *motg) return ret; } -static int msm_otg_phy_reset(struct msm_otg *motg) +static int msm_link_reset(struct msm_otg *motg) { u32 val; int ret; - int retries; ret = msm_otg_link_clk_reset(motg, 1); - if (ret) - return ret; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - ret = msm_otg_link_clk_reset(motg, 0); if (ret) return ret; - val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; - writel(val | PORTSC_PTS_ULPI, USB_PORTSC); - - for (retries = 3; retries > 0; retries--) { - ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM, - ULPI_CLR(ULPI_FUNC_CTRL)); - if (!ret) - break; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - } - if (!retries) - return -ETIMEDOUT; + /* wait for 1ms delay as suggested in HPG. */ + usleep_range(1000, 1200); - /* This reset calibrates the phy, if the above write succeeded */ - ret = msm_otg_phy_clk_reset(motg); + ret = msm_otg_link_clk_reset(motg, 0); if (ret) return ret; - for (retries = 3; retries > 0; retries--) { - ret = ulpi_read(&motg->phy, ULPI_DEBUG); - if (ret != -ETIMEDOUT) - break; - ret = msm_otg_phy_clk_reset(motg); - if (ret) - return ret; - } - if (!retries) - return -ETIMEDOUT; - if (motg->phy_number) writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); - dev_info(motg->phy.dev, "phy_reset: success\n"); + val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; + writel(val | PORTSC_PTS_ULPI, USB_PORTSC); + return 0; } -#define LINK_RESET_TIMEOUT_USEC (250 * 1000) static int msm_otg_reset(struct usb_phy *phy) { struct msm_otg *motg = container_of(phy, struct msm_otg, phy); - struct msm_otg_platform_data *pdata = motg->pdata; int cnt = 0; - int ret; - u32 val = 0; - u32 ulpi_val = 0; - - ret = msm_otg_phy_reset(motg); - if (ret) { - dev_err(phy->dev, "phy_reset failed\n"); - return ret; - } - - ulpi_init(motg); writel(USBCMD_RESET, USB_USBCMD); while (cnt < LINK_RESET_TIMEOUT_USEC) { @@ -351,11 +310,86 @@ static int msm_otg_reset(struct usb_phy *phy) /* select ULPI phy */ writel(0x80000000, USB_PORTSC); + writel(0x0, USB_AHBBURST); + writel(0x08, USB_AHBMODE); + + if (motg->phy_number) + writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); + return 0; +} + +static void msm_phy_reset(struct msm_otg *motg) +{ + void __iomem *addr; + + if (motg->pdata->phy_type != SNPS_28NM_INTEGRATED_PHY) { + msm_otg_phy_clk_reset(motg); + return; + } + + addr = USB_PHY_CTRL; + if (motg->phy_number) + addr = USB_PHY_CTRL2; + + /* Assert USB PHY_POR */ + writel(readl(addr) | PHY_POR_ASSERT, addr); + + /* + * wait for minimum 10 microseconds as suggested in HPG. + * Use a slightly larger value since the exact value didn't + * work 100% of the time. + */ + udelay(12); + + /* Deassert USB PHY_POR */ + writel(readl(addr) & ~PHY_POR_ASSERT, addr); +} + +static int msm_usb_reset(struct usb_phy *phy) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + int ret; + + if (!IS_ERR(motg->core_clk)) + clk_prepare_enable(motg->core_clk); + + ret = msm_link_reset(motg); + if (ret) { + dev_err(phy->dev, "phy_reset failed\n"); + return ret; + } + + ret = msm_otg_reset(&motg->phy); + if (ret) { + dev_err(phy->dev, "link reset failed\n"); + return ret; + } msleep(100); - writel(0x0, USB_AHBBURST); - writel(0x00, USB_AHBMODE); + /* Reset USB PHY after performing USB Link RESET */ + msm_phy_reset(motg); + + if (!IS_ERR(motg->core_clk)) + clk_disable_unprepare(motg->core_clk); + + return 0; +} + +static int msm_phy_init(struct usb_phy *phy) +{ + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); + struct msm_otg_platform_data *pdata = motg->pdata; + u32 val, ulpi_val = 0; + + /* Program USB PHY Override registers. */ + ulpi_init(motg); + + /* + * It is recommended in HPG to reset USB PHY after programming + * USB PHY Override registers. + */ + msm_phy_reset(motg); if (pdata->otg_control == OTG_PHY_CONTROL) { val = readl(USB_OTGSC); @@ -1574,7 +1608,7 @@ static int msm_otg_probe(struct platform_device *pdev) goto disable_ldo; } - phy->init = msm_otg_reset; + phy->init = msm_phy_init; phy->set_power = msm_otg_set_power; phy->io_ops = &msm_otg_io_ops; @@ -1583,6 +1617,8 @@ static int msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; + msm_usb_reset(phy); + ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); if (ret) { dev_err(&pdev->dev, "usb_add_phy failed\n"); diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index e6d703567155..575c74397e52 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -42,9 +42,14 @@ #define ULPI_DATA(n) ((n) & 255) #define ULPI_DATA_READ(n) (((n) >> 8) & 255) +/* synopsys 28nm phy registers */ +#define ULPI_PWR_CLK_MNG_REG 0x88 +#define OTG_COMP_DISABLE BIT(0) + #define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ #define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ #define PHY_RETEN (1 << 1) /* PHY retention enable/disable */ +#define PHY_POR_ASSERT (1 << 0) /* USB2 28nm PHY POR ASSERT */ /* OTG definitions */ #define OTGSC_INTSTS_MASK (0x7f << 16) -- cgit v1.2.3 From 9f27984b9e098ce0a35b210ec0315c76108494e4 Mon Sep 17 00:00:00 2001 From: Tim Bird Date: Mon, 28 Apr 2014 16:34:19 +0300 Subject: usb: phy: msm: Fix PTS definitions for MSM USB controller Fix the value used for Parallel Transceiver Select (PTS) for the MSM USB controller. This is a standard chipidea PORTSC definition, where a PHY_TYPE of 10b (<<30) is ULPI and 11b (<<30) is SERIAL. Fix the definitions and use them correctly in the driver code. Signed-off-by: Tim Bird Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 8 +++++--- include/linux/usb/msm_hsusb_hw.h | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index bb339963f8bb..db8d96377620 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -287,8 +287,9 @@ static int msm_link_reset(struct msm_otg *motg) if (motg->phy_number) writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); + /* put transceiver in serial mode as part of reset */ val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; - writel(val | PORTSC_PTS_ULPI, USB_PORTSC); + writel(val | PORTSC_PTS_SERIAL, USB_PORTSC); return 0; } @@ -308,8 +309,9 @@ static int msm_otg_reset(struct usb_phy *phy) if (cnt >= LINK_RESET_TIMEOUT_USEC) return -ETIMEDOUT; - /* select ULPI phy */ - writel(0x80000000, USB_PORTSC); + /* select ULPI phy and clear other status/control bits in PORTSC */ + writel(PORTSC_PTS_ULPI, USB_PORTSC); + writel(0x0, USB_AHBBURST); writel(0x08, USB_AHBMODE); diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index 575c74397e52..98d3dd8976e5 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -31,8 +31,9 @@ #define USB_USBINTR (MSM_USB_BASE + 0x0148) #define PORTSC_PHCD (1 << 23) /* phy suspend mode */ -#define PORTSC_PTS_MASK (3 << 30) -#define PORTSC_PTS_ULPI (3 << 30) +#define PORTSC_PTS_MASK (3 << 30) +#define PORTSC_PTS_ULPI (2 << 30) +#define PORTSC_PTS_SERIAL (3 << 30) #define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170) #define ULPI_RUN (1 << 30) -- cgit v1.2.3 From 30bf8667cef5655ddfaedf043f13d03606844213 Mon Sep 17 00:00:00 2001 From: Tim Bird Date: Mon, 28 Apr 2014 16:34:20 +0300 Subject: usb: phy: msm: Select secondary PHY via TCSR Select the secondary PHY using the TCSR register, if phy-num=1 in the DTS (or phy_number is set in the platform data). The SOC has 2 PHYs which can be used with the OTG port, and this code allows configuring the correct one. Note: This resolves the problem I was seeing where I couldn't get the USB driver working at all on a dragonboard, from cold boot. This patch depends on patch 5/14 from Ivan's msm USB patch set. It does not use DT for the register address, as there's no evidence that this address changes between SoC versions. Signed-off-by: Tim Bird Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 14 ++++++++++++++ include/linux/usb/msm_hsusb_hw.h | 3 +++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index db8d96377620..9437bcf8c367 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1489,6 +1489,7 @@ static int msm_otg_probe(struct platform_device *pdev) struct resource *res; struct msm_otg *motg; struct usb_phy *phy; + void __iomem *phy_select; motg = devm_kzalloc(&pdev->dev, sizeof(struct msm_otg), GFP_KERNEL); if (!motg) { @@ -1553,6 +1554,19 @@ static int msm_otg_probe(struct platform_device *pdev) if (IS_ERR(motg->regs)) return PTR_ERR(motg->regs); + /* + * NOTE: The PHYs can be multiplexed between the chipidea controller + * and the dwc3 controller, using a single bit. It is important that + * the dwc3 driver does not set this bit in an incompatible way. + */ + if (motg->phy_number) { + phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4); + if (IS_ERR(phy_select)) + return PTR_ERR(phy_select); + /* Enable second PHY with the OTG port */ + writel_relaxed(0x1, phy_select); + } + dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); motg->irq = platform_get_irq(pdev, 0); diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index 98d3dd8976e5..a29f6030afb1 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -16,6 +16,9 @@ #ifndef __LINUX_USB_GADGET_MSM72K_UDC_H__ #define __LINUX_USB_GADGET_MSM72K_UDC_H__ +/* USB phy selector - in TCSR address range */ +#define USB2_PHY_SEL 0xfd4ab000 + #define USB_AHBBURST (MSM_USB_BASE + 0x0090) #define USB_AHBMODE (MSM_USB_BASE + 0x0098) #define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ -- cgit v1.2.3 From 01799b622217ffebdc95e8e0aedbd4cff6a35a50 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 28 Apr 2014 16:34:22 +0300 Subject: usb: phy: msm: Vote for corner of VDD CX instead of voltage of VDD CX New platform uses RBCPR hardware feature, with that voting for absolute voltage of VDD CX is not required. Hence vote for corner of VDD CX which uses nominal corner voltage on VDD CX. Signed-off-by: Ivan T. Ivanov Cc: Mayank Rana Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/msm-hsusb.txt | 5 ++++ drivers/usb/phy/phy-msm-usb.c | 35 +++++++++++++++++----- include/linux/usb/msm_hsusb.h | 1 + 3 files changed, 33 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt index 066966706ca1..2826f2af503a 100644 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt @@ -65,6 +65,10 @@ Optional properties: Some platforms may have configuration to allow USB controller work with any of the two HSPHYs present. +- qcom,vdd-levels: This property must be a list of three integer values + (no, min, max) where each value represents either a voltage + in microvolts or a value corresponding to voltage corner. + Example HSUSB OTG controller device node: usb@f9a55000 { @@ -87,4 +91,5 @@ Example HSUSB OTG controller device node: qcom,otg-control = <1>; qcom,phy-init-sequence = < -1 0x63 >; + qcom,vdd-levels = <1 5 7>; }; diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 366527ecbdd1..8e7956eb8a77 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -62,6 +62,13 @@ #define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ #define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ +#define USB_PHY_SUSP_DIG_VOL 500000 /* uV */ + +enum vdd_levels { + VDD_LEVEL_NONE = 0, + VDD_LEVEL_MIN, + VDD_LEVEL_MAX, +}; static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) { @@ -69,8 +76,8 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) if (init) { ret = regulator_set_voltage(motg->vddcx, - USB_PHY_VDD_DIG_VOL_MIN, - USB_PHY_VDD_DIG_VOL_MAX); + motg->vdd_levels[VDD_LEVEL_MIN], + motg->vdd_levels[VDD_LEVEL_MAX]); if (ret) { dev_err(motg->phy.dev, "Cannot set vddcx voltage\n"); return ret; @@ -81,7 +88,7 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n"); } else { ret = regulator_set_voltage(motg->vddcx, 0, - USB_PHY_VDD_DIG_VOL_MAX); + motg->vdd_levels[VDD_LEVEL_MAX]); if (ret) dev_err(motg->phy.dev, "Cannot set vddcx voltage\n"); ret = regulator_disable(motg->vddcx); @@ -435,17 +442,16 @@ static int msm_phy_init(struct usb_phy *phy) #ifdef CONFIG_PM -#define USB_PHY_SUSP_DIG_VOL 500000 static int msm_hsusb_config_vddcx(struct msm_otg *motg, int high) { - int max_vol = USB_PHY_VDD_DIG_VOL_MAX; + int max_vol = motg->vdd_levels[VDD_LEVEL_MAX]; int min_vol; int ret; if (high) - min_vol = USB_PHY_VDD_DIG_VOL_MIN; + min_vol = motg->vdd_levels[VDD_LEVEL_MIN]; else - min_vol = USB_PHY_SUSP_DIG_VOL; + min_vol = motg->vdd_levels[VDD_LEVEL_NONE]; ret = regulator_set_voltage(motg->vddcx, min_vol, max_vol); if (ret) { @@ -1441,7 +1447,7 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) struct device_node *node = pdev->dev.of_node; struct property *prop; int len, ret, words; - u32 val; + u32 val, tmp[3]; pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -1472,6 +1478,19 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) if (!of_property_read_u32(node, "qcom,phy-num", &val) && val < 2) motg->phy_number = val; + motg->vdd_levels[VDD_LEVEL_NONE] = USB_PHY_SUSP_DIG_VOL; + motg->vdd_levels[VDD_LEVEL_MIN] = USB_PHY_VDD_DIG_VOL_MIN; + motg->vdd_levels[VDD_LEVEL_MAX] = USB_PHY_VDD_DIG_VOL_MAX; + + if (of_get_property(node, "qcom,vdd-levels", &len) && + len == sizeof(tmp)) { + of_property_read_u32_array(node, "qcom,vdd-levels", + tmp, len / sizeof(*tmp)); + motg->vdd_levels[VDD_LEVEL_NONE] = tmp[VDD_LEVEL_NONE]; + motg->vdd_levels[VDD_LEVEL_MIN] = tmp[VDD_LEVEL_MIN]; + motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; + } + prop = of_find_property(node, "qcom,phy-init-sequence", &len); if (!prop || !len) return 0; diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 4628f1a4713e..b0a39243295a 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -169,6 +169,7 @@ struct msm_otg { struct reset_control *phy_rst; struct reset_control *link_rst; + int vdd_levels[3]; }; #endif -- cgit v1.2.3 From 7fa857ed041537ee6cbc7ee4ab0204a1231cfcb9 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 28 Apr 2014 11:14:27 -0700 Subject: net: dsa: add ds_to_priv DSA drivers have a trick which consists in allocating "priv_size" more bytes to account for the DSA driver private context. Add a helper function to access that private context instead of open-coding it in drivers with (void *)(ds + 1). Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 7828ebf99ee1..6efce384451e 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -181,6 +181,11 @@ struct dsa_switch_driver { void register_switch_driver(struct dsa_switch_driver *type); void unregister_switch_driver(struct dsa_switch_driver *type); +static inline void *ds_to_priv(struct dsa_switch *ds) +{ + return (void *)(ds + 1); +} + /* * The original DSA tag format and some other tag formats have no * ethertype, which means that we need to add a little hack to the -- cgit v1.2.3 From 5c98631cca574ac6255885cf372f6bcf9dcfd483 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 29 Apr 2014 11:57:34 +0900 Subject: net: ipv6: Introduce ip6_sk_dst_hoplimit. This replaces 6 identical code snippets with a call to a new static inline function. Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- include/net/addrconf.h | 5 ----- include/net/ipv6.h | 19 +++++++++++++++++++ net/ipv6/icmp.c | 14 ++------------ net/ipv6/ip6_flowlabel.c | 1 - net/ipv6/ping.c | 7 +------ net/ipv6/raw.c | 10 ++-------- net/ipv6/udp.c | 10 ++-------- net/l2tp/l2tp_ip6.c | 10 ++-------- 8 files changed, 28 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 933a9f22a05f..f679877bb601 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -306,11 +306,6 @@ static inline void addrconf_addr_solict_mult(const struct in6_addr *addr, htonl(0xFF000000) | addr->s6_addr32[3]); } -static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr) -{ - return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000); -} - static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 diff --git a/include/net/ipv6.h b/include/net/ipv6.h index d640925bc454..5b40ad297b8c 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -583,6 +583,11 @@ static inline bool ipv6_addr_orchid(const struct in6_addr *a) return (a->s6_addr32[0] & htonl(0xfffffff0)) == htonl(0x20010010); } +static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr) +{ + return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000); +} + static inline void ipv6_addr_set_v4mapped(const __be32 addr, struct in6_addr *v4mapped) { @@ -664,6 +669,20 @@ void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt); int ip6_dst_hoplimit(struct dst_entry *dst); +static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6, + struct dst_entry *dst) +{ + int hlimit; + + if (ipv6_addr_is_multicast(&fl6->daddr)) + hlimit = np->mcast_hops; + else + hlimit = np->hop_limit; + if (hlimit < 0) + hlimit = ip6_dst_hoplimit(dst); + return hlimit; +} + /* * Header manipulation */ diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 7b326529e6a2..3b0905b77127 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -493,12 +493,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) if (IS_ERR(dst)) goto out; - if (ipv6_addr_is_multicast(&fl6.daddr)) - hlimit = np->mcast_hops; - else - hlimit = np->hop_limit; - if (hlimit < 0) - hlimit = ip6_dst_hoplimit(dst); + hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); msg.skb = skb; msg.offset = skb_network_offset(skb); @@ -593,12 +588,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) if (IS_ERR(dst)) goto out; - if (ipv6_addr_is_multicast(&fl6.daddr)) - hlimit = np->mcast_hops; - else - hlimit = np->hop_limit; - if (hlimit < 0) - hlimit = ip6_dst_hoplimit(dst); + hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); idev = __in6_dev_get(skb->dev); diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 0961b5ef866d..4052694c6f2c 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -26,7 +26,6 @@ #include #include -#include #include #include diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index bda74291c3e0..a2a1d80dfe0c 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -168,12 +168,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, pfh.wcheck = 0; pfh.family = AF_INET6; - if (ipv6_addr_is_multicast(&fl6.daddr)) - hlimit = np->mcast_hops; - else - hlimit = np->hop_limit; - if (hlimit < 0) - hlimit = ip6_dst_hoplimit(dst); + hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); lock_sock(sk); err = ip6_append_data(sk, ping_getfrag, &pfh, len, diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 1f29996e368a..dddfb5fa2b7a 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -873,14 +873,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, err = PTR_ERR(dst); goto out; } - if (hlimit < 0) { - if (ipv6_addr_is_multicast(&fl6.daddr)) - hlimit = np->mcast_hops; - else - hlimit = np->hop_limit; - if (hlimit < 0) - hlimit = ip6_dst_hoplimit(dst); - } + if (hlimit < 0) + hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); if (tclass < 0) tclass = np->tclass; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 1e586d92260e..d8d6ca04bb9d 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1232,14 +1232,8 @@ do_udp_sendmsg: goto out; } - if (hlimit < 0) { - if (ipv6_addr_is_multicast(&fl6.daddr)) - hlimit = np->mcast_hops; - else - hlimit = np->hop_limit; - if (hlimit < 0) - hlimit = ip6_dst_hoplimit(dst); - } + if (hlimit < 0) + hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); if (tclass < 0) tclass = np->tclass; diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 7704ea9502fd..e472d44a3b91 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -605,14 +605,8 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, goto out; } - if (hlimit < 0) { - if (ipv6_addr_is_multicast(&fl6.daddr)) - hlimit = np->mcast_hops; - else - hlimit = np->hop_limit; - if (hlimit < 0) - hlimit = ip6_dst_hoplimit(dst); - } + if (hlimit < 0) + hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); if (tclass < 0) tclass = np->tclass; -- cgit v1.2.3 From 0302f71c0aa59571ac306f93068fbbfe65ea349b Mon Sep 17 00:00:00 2001 From: Mark Salter Date: Mon, 30 Dec 2013 12:12:12 -0500 Subject: efi: add helper function to get UEFI params from FDT ARM and ARM64 architectures use the device tree to pass UEFI parameters from stub to kernel. These parameters are things known to the stub but not discoverable by the kernel after the stub calls ExitBootSerives(). There is a helper function in: drivers/firmware/efi/fdt.c which the stub uses to add the UEFI parameters to the device tree. This patch adds a complimentary helper function which UEFI runtime support may use to retrieve the parameters from the device tree. If an architecture wants to use this helper, it should select CONFIG_EFI_PARAMS_FROM_FDT. Signed-off-by: Mark Salter Signed-off-by: Leif Lindholm Acked-by: Catalin Marinas Signed-off-by: Matt Fleming --- drivers/firmware/efi/Kconfig | 7 ++++ drivers/firmware/efi/efi.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 9 +++++ 3 files changed, 95 insertions(+) (limited to 'include') diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 1e75f48b61f8..d420ae2d3413 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -47,6 +47,13 @@ config EFI_RUNTIME_MAP See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. +config EFI_PARAMS_FROM_FDT + bool + help + Select this config option from the architecture Kconfig if + the EFI runtime support gets system table address, memory + map address, and other parameters from the device tree. + endmenu config UEFI_CPER diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index af20f1712337..cd36deb619fa 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include struct efi __read_mostly efi = { @@ -318,3 +320,80 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) return 0; } + +#ifdef CONFIG_EFI_PARAMS_FROM_FDT + +#define UEFI_PARAM(name, prop, field) \ + { \ + { name }, \ + { prop }, \ + offsetof(struct efi_fdt_params, field), \ + FIELD_SIZEOF(struct efi_fdt_params, field) \ + } + +static __initdata struct { + const char name[32]; + const char propname[32]; + int offset; + int size; +} dt_params[] = { + UEFI_PARAM("System Table", "linux,uefi-system-table", system_table), + UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap), + UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size), + UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size), + UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver) +}; + +struct param_info { + int verbose; + void *params; +}; + +static int __init fdt_find_uefi_params(unsigned long node, const char *uname, + int depth, void *data) +{ + struct param_info *info = data; + void *prop, *dest; + unsigned long len; + u64 val; + int i; + + if (depth != 1 || + (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) + return 0; + + pr_info("Getting parameters from FDT:\n"); + + for (i = 0; i < ARRAY_SIZE(dt_params); i++) { + prop = of_get_flat_dt_prop(node, dt_params[i].propname, &len); + if (!prop) { + pr_err("Can't find %s in device tree!\n", + dt_params[i].name); + return 0; + } + dest = info->params + dt_params[i].offset; + + val = of_read_number(prop, len / sizeof(u32)); + + if (dt_params[i].size == sizeof(u32)) + *(u32 *)dest = val; + else + *(u64 *)dest = val; + + if (info->verbose) + pr_info(" %s: 0x%0*llx\n", dt_params[i].name, + dt_params[i].size * 2, val); + } + return 1; +} + +int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose) +{ + struct param_info info; + + info.verbose = verbose; + info.params = params; + + return of_scan_flat_dt(fdt_find_uefi_params, &info); +} +#endif /* CONFIG_EFI_PARAMS_FROM_FDT */ diff --git a/include/linux/efi.h b/include/linux/efi.h index 6a4d8e27d1d7..cd0172e796cb 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -664,6 +664,14 @@ struct efi_memory_map { unsigned long desc_size; }; +struct efi_fdt_params { + u64 system_table; + u64 mmap; + u32 mmap_size; + u32 desc_size; + u32 desc_ver; +}; + typedef struct { u32 revision; u32 parent_handle; @@ -861,6 +869,7 @@ extern void efi_initialize_iomem_resources(struct resource *code_resource, extern void efi_get_time(struct timespec *now); extern int efi_set_rtc_mmss(const struct timespec *now); extern void efi_reserve_boot_services(void); +extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap; /* Iterate through an efi_memory_map */ -- cgit v1.2.3 From 263b4a30bfdb0d756ae9c70c6ff2eef1eb951770 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Wed, 8 Jan 2014 17:54:19 -0800 Subject: efi: Add shared FDT related functions for ARM/ARM64 Both ARM and ARM64 stubs will update the device tree that they pass to the kernel. In both cases they primarily need to add the same UEFI related information, so the function can be shared. Create a new FDT related file for this to avoid use of architecture #ifdefs in efi-stub-helper.c. Signed-off-by: Roy Franz [ Fixed memory node deletion code. ] Signed-off-by: Mark Rutland Signed-off-by: Leif Lindholm Acked-by: Grant Likely Acked-by: Catalin Marinas Signed-off-by: Matt Fleming --- drivers/firmware/efi/fdt.c | 285 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 3 + 2 files changed, 288 insertions(+) create mode 100644 drivers/firmware/efi/fdt.c (limited to 'include') diff --git a/drivers/firmware/efi/fdt.c b/drivers/firmware/efi/fdt.c new file mode 100644 index 000000000000..5c6a8e8a9580 --- /dev/null +++ b/drivers/firmware/efi/fdt.c @@ -0,0 +1,285 @@ +/* + * FDT related Helper functions used by the EFI stub on multiple + * architectures. This should be #included by the EFI stub + * implementation files. + * + * Copyright 2013 Linaro Limited; author Roy Franz + * + * This file is part of the Linux kernel, and is made available + * under the terms of the GNU General Public License version 2. + * + */ + +static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, + unsigned long orig_fdt_size, + void *fdt, int new_fdt_size, char *cmdline_ptr, + u64 initrd_addr, u64 initrd_size, + efi_memory_desc_t *memory_map, + unsigned long map_size, unsigned long desc_size, + u32 desc_ver) +{ + int node, prev; + int status; + u32 fdt_val32; + u64 fdt_val64; + + /* + * Copy definition of linux_banner here. Since this code is + * built as part of the decompressor for ARM v7, pulling + * in version.c where linux_banner is defined for the + * kernel brings other kernel dependencies with it. + */ + const char linux_banner[] = + "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" + LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n"; + + /* Do some checks on provided FDT, if it exists*/ + if (orig_fdt) { + if (fdt_check_header(orig_fdt)) { + pr_efi_err(sys_table, "Device Tree header not valid!\n"); + return EFI_LOAD_ERROR; + } + /* + * We don't get the size of the FDT if we get if from a + * configuration table. + */ + if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) { + pr_efi_err(sys_table, "Truncated device tree! foo!\n"); + return EFI_LOAD_ERROR; + } + } + + if (orig_fdt) + status = fdt_open_into(orig_fdt, fdt, new_fdt_size); + else + status = fdt_create_empty_tree(fdt, new_fdt_size); + + if (status != 0) + goto fdt_set_fail; + + /* + * Delete any memory nodes present. We must delete nodes which + * early_init_dt_scan_memory may try to use. + */ + prev = 0; + for (;;) { + const char *type, *name; + int len; + + node = fdt_next_node(fdt, prev, NULL); + if (node < 0) + break; + + type = fdt_getprop(fdt, node, "device_type", &len); + if (type && strncmp(type, "memory", len) == 0) { + fdt_del_node(fdt, node); + continue; + } + + prev = node; + } + + node = fdt_subnode_offset(fdt, 0, "chosen"); + if (node < 0) { + node = fdt_add_subnode(fdt, 0, "chosen"); + if (node < 0) { + status = node; /* node is error code when negative */ + goto fdt_set_fail; + } + } + + if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) { + status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr, + strlen(cmdline_ptr) + 1); + if (status) + goto fdt_set_fail; + } + + /* Set initrd address/end in device tree, if present */ + if (initrd_size != 0) { + u64 initrd_image_end; + u64 initrd_image_start = cpu_to_fdt64(initrd_addr); + + status = fdt_setprop(fdt, node, "linux,initrd-start", + &initrd_image_start, sizeof(u64)); + if (status) + goto fdt_set_fail; + initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size); + status = fdt_setprop(fdt, node, "linux,initrd-end", + &initrd_image_end, sizeof(u64)); + if (status) + goto fdt_set_fail; + } + + /* Add FDT entries for EFI runtime services in chosen node. */ + node = fdt_subnode_offset(fdt, 0, "chosen"); + fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table); + status = fdt_setprop(fdt, node, "linux,uefi-system-table", + &fdt_val64, sizeof(fdt_val64)); + if (status) + goto fdt_set_fail; + + fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map); + status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", + &fdt_val64, sizeof(fdt_val64)); + if (status) + goto fdt_set_fail; + + fdt_val32 = cpu_to_fdt32(map_size); + status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", + &fdt_val32, sizeof(fdt_val32)); + if (status) + goto fdt_set_fail; + + fdt_val32 = cpu_to_fdt32(desc_size); + status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", + &fdt_val32, sizeof(fdt_val32)); + if (status) + goto fdt_set_fail; + + fdt_val32 = cpu_to_fdt32(desc_ver); + status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", + &fdt_val32, sizeof(fdt_val32)); + if (status) + goto fdt_set_fail; + + /* + * Add kernel version banner so stub/kernel match can be + * verified. + */ + status = fdt_setprop_string(fdt, node, "linux,uefi-stub-kern-ver", + linux_banner); + if (status) + goto fdt_set_fail; + + return EFI_SUCCESS; + +fdt_set_fail: + if (status == -FDT_ERR_NOSPACE) + return EFI_BUFFER_TOO_SMALL; + + return EFI_LOAD_ERROR; +} + +#ifndef EFI_FDT_ALIGN +#define EFI_FDT_ALIGN EFI_PAGE_SIZE +#endif + +/* + * Allocate memory for a new FDT, then add EFI, commandline, and + * initrd related fields to the FDT. This routine increases the + * FDT allocation size until the allocated memory is large + * enough. EFI allocations are in EFI_PAGE_SIZE granules, + * which are fixed at 4K bytes, so in most cases the first + * allocation should succeed. + * EFI boot services are exited at the end of this function. + * There must be no allocations between the get_memory_map() + * call and the exit_boot_services() call, so the exiting of + * boot services is very tightly tied to the creation of the FDT + * with the final memory map in it. + */ + +efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, + void *handle, + unsigned long *new_fdt_addr, + unsigned long max_addr, + u64 initrd_addr, u64 initrd_size, + char *cmdline_ptr, + unsigned long fdt_addr, + unsigned long fdt_size) +{ + unsigned long map_size, desc_size; + u32 desc_ver; + unsigned long mmap_key; + efi_memory_desc_t *memory_map; + unsigned long new_fdt_size; + efi_status_t status; + + /* + * Estimate size of new FDT, and allocate memory for it. We + * will allocate a bigger buffer if this ends up being too + * small, so a rough guess is OK here. + */ + new_fdt_size = fdt_size + EFI_PAGE_SIZE; + while (1) { + status = efi_high_alloc(sys_table, new_fdt_size, EFI_FDT_ALIGN, + new_fdt_addr, max_addr); + if (status != EFI_SUCCESS) { + pr_efi_err(sys_table, "Unable to allocate memory for new device tree.\n"); + goto fail; + } + + /* + * Now that we have done our final memory allocation (and free) + * we can get the memory map key needed for + * exit_boot_services(). + */ + status = efi_get_memory_map(sys_table, &memory_map, &map_size, + &desc_size, &desc_ver, &mmap_key); + if (status != EFI_SUCCESS) + goto fail_free_new_fdt; + + status = update_fdt(sys_table, + (void *)fdt_addr, fdt_size, + (void *)*new_fdt_addr, new_fdt_size, + cmdline_ptr, initrd_addr, initrd_size, + memory_map, map_size, desc_size, desc_ver); + + /* Succeeding the first time is the expected case. */ + if (status == EFI_SUCCESS) + break; + + if (status == EFI_BUFFER_TOO_SMALL) { + /* + * We need to allocate more space for the new + * device tree, so free existing buffer that is + * too small. Also free memory map, as we will need + * to get new one that reflects the free/alloc we do + * on the device tree buffer. + */ + efi_free(sys_table, new_fdt_size, *new_fdt_addr); + sys_table->boottime->free_pool(memory_map); + new_fdt_size += EFI_PAGE_SIZE; + } else { + pr_efi_err(sys_table, "Unable to constuct new device tree.\n"); + goto fail_free_mmap; + } + } + + /* Now we are ready to exit_boot_services.*/ + status = sys_table->boottime->exit_boot_services(handle, mmap_key); + + + if (status == EFI_SUCCESS) + return status; + + pr_efi_err(sys_table, "Exit boot services failed.\n"); + +fail_free_mmap: + sys_table->boottime->free_pool(memory_map); + +fail_free_new_fdt: + efi_free(sys_table, new_fdt_size, *new_fdt_addr); + +fail: + return EFI_LOAD_ERROR; +} + +static void *get_fdt(efi_system_table_t *sys_table) +{ + efi_guid_t fdt_guid = DEVICE_TREE_GUID; + efi_config_table_t *tables; + void *fdt; + int i; + + tables = (efi_config_table_t *) sys_table->tables; + fdt = NULL; + + for (i = 0; i < sys_table->nr_tables; i++) + if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) { + fdt = (void *) tables[i].table; + break; + } + + return fdt; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index cd0172e796cb..41bbf8ba4ba8 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -575,6 +575,9 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si #define EFI_FILE_SYSTEM_GUID \ EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) +#define DEVICE_TREE_GUID \ + EFI_GUID( 0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 ) + typedef struct { efi_guid_t guid; u64 table; -- cgit v1.2.3 From 774b514390b1eb8476bc759262790762bd1ef45a Mon Sep 17 00:00:00 2001 From: Maxime COQUELIN Date: Wed, 29 Jan 2014 17:24:07 +0100 Subject: clk: divider: Add round to closest divider In some cases, we want to be able to round the divider to the closest one, instead than rounding up. This patch adds a new CLK_DIVIDER_ROUND_CLOSEST flag to specify the divider has to round to closest div, keeping rounding up as de default behaviour. Signed-off-by: Maxime Coquelin Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 69 ++++++++++++++++++++++++++++++++++++++++++-- include/linux/clk-provider.h | 3 ++ 2 files changed, 70 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 4637697c139f..c57294563a98 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -43,6 +43,17 @@ static unsigned int _get_table_maxdiv(const struct clk_div_table *table) return maxdiv; } +static unsigned int _get_table_mindiv(const struct clk_div_table *table) +{ + unsigned int mindiv = UINT_MAX; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div < mindiv) + mindiv = clkt->div; + return mindiv; +} + static unsigned int _get_maxdiv(struct clk_divider *divider) { if (divider->flags & CLK_DIVIDER_ONE_BASED) @@ -162,6 +173,24 @@ static int _round_up_table(const struct clk_div_table *table, int div) return up; } +static int _round_down_table(const struct clk_div_table *table, int div) +{ + const struct clk_div_table *clkt; + int down = _get_table_mindiv(table); + + for (clkt = table; clkt->div; clkt++) { + if (clkt->div == div) + return clkt->div; + else if (clkt->div > div) + continue; + + if ((div - clkt->div) < (div - down)) + down = clkt->div; + } + + return down; +} + static int _div_round_up(struct clk_divider *divider, unsigned long parent_rate, unsigned long rate) { @@ -175,6 +204,42 @@ static int _div_round_up(struct clk_divider *divider, return div; } +static int _div_round_closest(struct clk_divider *divider, + unsigned long parent_rate, unsigned long rate) +{ + int up, down, div; + + up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate); + + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) { + up = __roundup_pow_of_two(div); + down = __rounddown_pow_of_two(div); + } else if (divider->table) { + up = _round_up_table(divider->table, div); + down = _round_down_table(divider->table, div); + } + + return (up - div) <= (div - down) ? up : down; +} + +static int _div_round(struct clk_divider *divider, unsigned long parent_rate, + unsigned long rate) +{ + if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) + return _div_round_closest(divider, parent_rate, rate); + + return _div_round_up(divider, parent_rate, rate); +} + +static bool _is_best_div(struct clk_divider *divider, + int rate, int now, int best) +{ + if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) + return abs(rate - now) < abs(rate - best); + + return now <= rate && now > best; +} + static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -190,7 +255,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { parent_rate = *best_parent_rate; - bestdiv = _div_round_up(divider, parent_rate, rate); + bestdiv = _div_round(divider, parent_rate, rate); bestdiv = bestdiv == 0 ? 1 : bestdiv; bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; return bestdiv; @@ -217,7 +282,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); now = DIV_ROUND_UP(parent_rate, i); - if (now <= rate && now > best) { + if (_is_best_div(divider, rate, now, best)) { bestdiv = i; best = now; *best_parent_rate = parent_rate; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 511917416fb0..59e2eb58f555 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -312,6 +312,8 @@ struct clk_div_table { * of this register, and mask of divider bits are in higher 16-bit of this * register. While setting the divider bits, higher 16-bit should also be * updated to indicate changing divider bits. + * CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded + * to the closest integer instead of the up one. */ struct clk_divider { struct clk_hw hw; @@ -327,6 +329,7 @@ struct clk_divider { #define CLK_DIVIDER_POWER_OF_TWO BIT(1) #define CLK_DIVIDER_ALLOW_ZERO BIT(2) #define CLK_DIVIDER_HIWORD_MASK BIT(3) +#define CLK_DIVIDER_ROUND_CLOSEST BIT(4) extern const struct clk_ops clk_divider_ops; struct clk *clk_register_divider(struct device *dev, const char *name, -- cgit v1.2.3 From 0bdab78ba69f057c99e72b9887a997888e7fce15 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:26:23 -0500 Subject: clk: bcm281xx: move compatible string definitions The Broadcom 281xx clock code uses a #define for the compatible string for it's clock control units (CCUs). Rather than defining those in the C source file, define them in the header file that's shared by both the code and the device tree source file (along with all the clock ids). Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-bcm281xx.c | 12 ------------ include/dt-bindings/clock/bcm281xx.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c index 71a65a49f1a4..502a487d62c5 100644 --- a/drivers/clk/bcm/clk-bcm281xx.c +++ b/drivers/clk/bcm/clk-bcm281xx.c @@ -18,18 +18,6 @@ #define BCM281XX_CCU_COMMON(_name, _ucase_name) \ KONA_CCU_COMMON(BCM281XX, _name, _ucase_name) -/* - * These are the bcm281xx CCU device tree "compatible" strings. - * We're stuck with using "bcm11351" in the string because wild - * cards aren't allowed, and that name was the first one defined - * in this family of devices. - */ -#define BCM281XX_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu" -#define BCM281XX_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu" -#define BCM281XX_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu" -#define BCM281XX_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" -#define BCM281XX_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" - /* Root CCU */ static struct peri_clk_data frac_1m_data = { diff --git a/include/dt-bindings/clock/bcm281xx.h b/include/dt-bindings/clock/bcm281xx.h index e0096940886d..a763460cf1af 100644 --- a/include/dt-bindings/clock/bcm281xx.h +++ b/include/dt-bindings/clock/bcm281xx.h @@ -20,6 +20,18 @@ * the clock control units (CCUs) on Broadcom BCM281XX family SoCs. */ +/* + * These are the bcm281xx CCU device tree "compatible" strings. + * We're stuck with using "bcm11351" in the string because wild + * cards aren't allowed, and that name was the first one defined + * in this family of devices. + */ +#define BCM281XX_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu" +#define BCM281XX_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu" +#define BCM281XX_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu" +#define BCM281XX_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" +#define BCM281XX_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" + /* root CCU clock ids */ #define BCM281XX_ROOT_CCU_FRAC_1M 0 -- cgit v1.2.3 From 7d3723ba8c19a9d23a240f8e99010193e5623b06 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:26:26 -0500 Subject: clk: bcm21664: use common clock framework Define the set of CCUs and provided clocks sufficient to satisfy the needs of all the existing clock references for BCM21664. Replace the "fake" fixed-rate clocks used previously with "real" ones. Note that only the minimal set of these clocks and CCUs is defined here. More clock definitions will need to be added as required by the addition of additional drivers. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/Kconfig | 2 +- drivers/clk/bcm/Makefile | 1 + drivers/clk/bcm/clk-bcm21664.c | 290 +++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/bcm21664.h | 62 ++++++++ 4 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/bcm/clk-bcm21664.c create mode 100644 include/dt-bindings/clock/bcm21664.h (limited to 'include') diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index a7262fb8ce55..75506e53075b 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -6,4 +6,4 @@ config CLK_BCM_KONA help Enable common clock framework support for Broadcom SoCs using "Kona" style clock control units, including those - in the BCM281xx family. + in the BCM281xx and BCM21664 families. diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index cf93359aa862..6297d05a9a10 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o +obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o diff --git a/drivers/clk/bcm/clk-bcm21664.c b/drivers/clk/bcm/clk-bcm21664.c new file mode 100644 index 000000000000..eeae4cad2281 --- /dev/null +++ b/drivers/clk/bcm/clk-bcm21664.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * Copyright 2014 Linaro Limited + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "clk-kona.h" +#include "dt-bindings/clock/bcm21664.h" + +#define BCM21664_CCU_COMMON(_name, _capname) \ + KONA_CCU_COMMON(BCM21664, _name, _capname) + +/* Root CCU */ + +static struct peri_clk_data frac_1m_data = { + .gate = HW_SW_GATE(0x214, 16, 0, 1), + .clocks = CLOCKS("ref_crystal"), +}; + +static struct ccu_data root_ccu_data = { + BCM21664_CCU_COMMON(root, ROOT), + /* no policy control */ + .kona_clks = { + [BCM21664_ROOT_CCU_FRAC_1M] = + KONA_CLK(root, frac_1m, peri), + [BCM21664_ROOT_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* AON CCU */ + +static struct peri_clk_data hub_timer_data = { + .gate = HW_SW_GATE(0x0414, 16, 0, 1), + .hyst = HYST(0x0414, 8, 9), + .clocks = CLOCKS("bbl_32k", + "frac_1m", + "dft_19_5m"), + .sel = SELECTOR(0x0a10, 0, 2), + .trig = TRIGGER(0x0a40, 4), +}; + +static struct ccu_data aon_ccu_data = { + BCM21664_CCU_COMMON(aon, AON), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_AON_CCU_HUB_TIMER] = + KONA_CLK(aon, hub_timer, peri), + [BCM21664_AON_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Master CCU */ + +static struct peri_clk_data sdio1_data = { + .gate = HW_SW_GATE(0x0358, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a28, 0, 3), + .div = DIVIDER(0x0a28, 4, 14), + .trig = TRIGGER(0x0afc, 9), +}; + +static struct peri_clk_data sdio2_data = { + .gate = HW_SW_GATE(0x035c, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a2c, 0, 3), + .div = DIVIDER(0x0a2c, 4, 14), + .trig = TRIGGER(0x0afc, 10), +}; + +static struct peri_clk_data sdio3_data = { + .gate = HW_SW_GATE(0x0364, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a34, 0, 3), + .div = DIVIDER(0x0a34, 4, 14), + .trig = TRIGGER(0x0afc, 12), +}; + +static struct peri_clk_data sdio4_data = { + .gate = HW_SW_GATE(0x0360, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a30, 0, 3), + .div = DIVIDER(0x0a30, 4, 14), + .trig = TRIGGER(0x0afc, 11), +}; + +static struct peri_clk_data sdio1_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0358, 18, 2, 3), +}; + +static struct peri_clk_data sdio2_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x035c, 18, 2, 3), +}; + +static struct peri_clk_data sdio3_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0364, 18, 2, 3), +}; + +static struct peri_clk_data sdio4_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0360, 18, 2, 3), +}; + +static struct ccu_data master_ccu_data = { + BCM21664_CCU_COMMON(master, MASTER), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_MASTER_CCU_SDIO1] = + KONA_CLK(master, sdio1, peri), + [BCM21664_MASTER_CCU_SDIO2] = + KONA_CLK(master, sdio2, peri), + [BCM21664_MASTER_CCU_SDIO3] = + KONA_CLK(master, sdio3, peri), + [BCM21664_MASTER_CCU_SDIO4] = + KONA_CLK(master, sdio4, peri), + [BCM21664_MASTER_CCU_SDIO1_SLEEP] = + KONA_CLK(master, sdio1_sleep, peri), + [BCM21664_MASTER_CCU_SDIO2_SLEEP] = + KONA_CLK(master, sdio2_sleep, peri), + [BCM21664_MASTER_CCU_SDIO3_SLEEP] = + KONA_CLK(master, sdio3_sleep, peri), + [BCM21664_MASTER_CCU_SDIO4_SLEEP] = + KONA_CLK(master, sdio4_sleep, peri), + [BCM21664_MASTER_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Slave CCU */ + +static struct peri_clk_data uartb_data = { + .gate = HW_SW_GATE(0x0400, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a10, 0, 2), + .div = FRAC_DIVIDER(0x0a10, 4, 12, 8), + .trig = TRIGGER(0x0afc, 2), +}; + +static struct peri_clk_data uartb2_data = { + .gate = HW_SW_GATE(0x0404, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a14, 0, 2), + .div = FRAC_DIVIDER(0x0a14, 4, 12, 8), + .trig = TRIGGER(0x0afc, 3), +}; + +static struct peri_clk_data uartb3_data = { + .gate = HW_SW_GATE(0x0408, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a18, 0, 2), + .div = FRAC_DIVIDER(0x0a18, 4, 12, 8), + .trig = TRIGGER(0x0afc, 4), +}; + +static struct peri_clk_data bsc1_data = { + .gate = HW_SW_GATE(0x0458, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a64, 0, 3), + .trig = TRIGGER(0x0afc, 23), +}; + +static struct peri_clk_data bsc2_data = { + .gate = HW_SW_GATE(0x045c, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a68, 0, 3), + .trig = TRIGGER(0x0afc, 24), +}; + +static struct peri_clk_data bsc3_data = { + .gate = HW_SW_GATE(0x0470, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a7c, 0, 3), + .trig = TRIGGER(0x0afc, 18), +}; + +static struct peri_clk_data bsc4_data = { + .gate = HW_SW_GATE(0x0474, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a80, 0, 3), + .trig = TRIGGER(0x0afc, 19), +}; + +static struct ccu_data slave_ccu_data = { + BCM21664_CCU_COMMON(slave, SLAVE), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_SLAVE_CCU_UARTB] = + KONA_CLK(slave, uartb, peri), + [BCM21664_SLAVE_CCU_UARTB2] = + KONA_CLK(slave, uartb2, peri), + [BCM21664_SLAVE_CCU_UARTB3] = + KONA_CLK(slave, uartb3, peri), + [BCM21664_SLAVE_CCU_BSC1] = + KONA_CLK(slave, bsc1, peri), + [BCM21664_SLAVE_CCU_BSC2] = + KONA_CLK(slave, bsc2, peri), + [BCM21664_SLAVE_CCU_BSC3] = + KONA_CLK(slave, bsc3, peri), + [BCM21664_SLAVE_CCU_BSC4] = + KONA_CLK(slave, bsc4, peri), + [BCM21664_SLAVE_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Device tree match table callback functions */ + +static void __init kona_dt_root_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&root_ccu_data, node); +} + +static void __init kona_dt_aon_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&aon_ccu_data, node); +} + +static void __init kona_dt_master_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&master_ccu_data, node); +} + +static void __init kona_dt_slave_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&slave_ccu_data, node); +} + +CLK_OF_DECLARE(bcm21664_root_ccu, BCM21664_DT_ROOT_CCU_COMPAT, + kona_dt_root_ccu_setup); +CLK_OF_DECLARE(bcm21664_aon_ccu, BCM21664_DT_AON_CCU_COMPAT, + kona_dt_aon_ccu_setup); +CLK_OF_DECLARE(bcm21664_master_ccu, BCM21664_DT_MASTER_CCU_COMPAT, + kona_dt_master_ccu_setup); +CLK_OF_DECLARE(bcm21664_slave_ccu, BCM21664_DT_SLAVE_CCU_COMPAT, + kona_dt_slave_ccu_setup); diff --git a/include/dt-bindings/clock/bcm21664.h b/include/dt-bindings/clock/bcm21664.h new file mode 100644 index 000000000000..5a7f0e4750a8 --- /dev/null +++ b/include/dt-bindings/clock/bcm21664.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 Broadcom Corporation + * Copyright 2013 Linaro Limited + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CLOCK_BCM21664_H +#define _CLOCK_BCM21664_H + +/* + * This file defines the values used to specify clocks provided by + * the clock control units (CCUs) on Broadcom BCM21664 family SoCs. + */ + +/* bcm21664 CCU device tree "compatible" strings */ +#define BCM21664_DT_ROOT_CCU_COMPAT "brcm,bcm21664-root-ccu" +#define BCM21664_DT_AON_CCU_COMPAT "brcm,bcm21664-aon-ccu" +#define BCM21664_DT_MASTER_CCU_COMPAT "brcm,bcm21664-master-ccu" +#define BCM21664_DT_SLAVE_CCU_COMPAT "brcm,bcm21664-slave-ccu" + +/* root CCU clock ids */ + +#define BCM21664_ROOT_CCU_FRAC_1M 0 +#define BCM21664_ROOT_CCU_CLOCK_COUNT 1 + +/* aon CCU clock ids */ + +#define BCM21664_AON_CCU_HUB_TIMER 0 +#define BCM21664_AON_CCU_CLOCK_COUNT 1 + +/* master CCU clock ids */ + +#define BCM21664_MASTER_CCU_SDIO1 0 +#define BCM21664_MASTER_CCU_SDIO2 1 +#define BCM21664_MASTER_CCU_SDIO3 2 +#define BCM21664_MASTER_CCU_SDIO4 3 +#define BCM21664_MASTER_CCU_SDIO1_SLEEP 4 +#define BCM21664_MASTER_CCU_SDIO2_SLEEP 5 +#define BCM21664_MASTER_CCU_SDIO3_SLEEP 6 +#define BCM21664_MASTER_CCU_SDIO4_SLEEP 7 +#define BCM21664_MASTER_CCU_CLOCK_COUNT 8 + +/* slave CCU clock ids */ + +#define BCM21664_SLAVE_CCU_UARTB 0 +#define BCM21664_SLAVE_CCU_UARTB2 1 +#define BCM21664_SLAVE_CCU_UARTB3 2 +#define BCM21664_SLAVE_CCU_BSC1 3 +#define BCM21664_SLAVE_CCU_BSC2 4 +#define BCM21664_SLAVE_CCU_BSC3 5 +#define BCM21664_SLAVE_CCU_BSC4 6 +#define BCM21664_SLAVE_CCU_CLOCK_COUNT 7 + +#endif /* _CLOCK_BCM21664_H */ -- cgit v1.2.3 From 2c07e3c7dd487fa40983aa20704b2755c9aac477 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 4 Apr 2014 11:32:56 -0500 Subject: clk: qcom: Various fixes for MSM8960's global clock controller * Remove CE2_SLEEP_CLK, doesn't exist on 8960 family SoCs * Fix incorrect offset for PMIC_SSBI2_RESET * Fix typo: SIC_TIC -> SPS_TIC_H SFAB_ADM0_M2_A_CLK -> SFAB_ADM0_M2_H_CLK * Fix naming convention: SFAB_CFPB_S_HCLK -> SFAB_CFPB_S_H_CLK SATA_SRC_CLK -> SATA_CLK_SRC Signed-off-by: Kumar Gala Reviewed-by: Stephen Boyd Signed-off-by: Mike Turquette --- drivers/clk/qcom/gcc-msm8960.c | 4 ++-- include/dt-bindings/clock/qcom,gcc-msm8960.h | 7 +++---- include/dt-bindings/reset/qcom,gcc-msm8960.h | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index 8e2b6ddcb8e4..f4ffd91901f8 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -2810,7 +2810,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { [PPSS_PROC_RESET] = { 0x2594, 1 }, [PPSS_RESET] = { 0x2594}, [DMA_BAM_RESET] = { 0x25c0, 7 }, - [SIC_TIC_RESET] = { 0x2600, 7 }, + [SPS_TIC_H_RESET] = { 0x2600, 7 }, [SLIMBUS_H_RESET] = { 0x2620, 7 }, [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, @@ -2823,7 +2823,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, [RPM_PROC_RESET] = { 0x27c0, 7 }, - [PMIC_SSBI2_RESET] = { 0x270c, 12 }, + [PMIC_SSBI2_RESET] = { 0x280c, 12 }, [SDC1_RESET] = { 0x2830 }, [SDC2_RESET] = { 0x2850 }, [SDC3_RESET] = { 0x2870 }, diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h index 03bbf49d43b7..f9f547146a15 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8960.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h @@ -51,7 +51,7 @@ #define QDSS_TSCTR_CLK 34 #define SFAB_ADM0_M0_A_CLK 35 #define SFAB_ADM0_M1_A_CLK 36 -#define SFAB_ADM0_M2_A_CLK 37 +#define SFAB_ADM0_M2_H_CLK 37 #define ADM0_CLK 38 #define ADM0_PBUS_CLK 39 #define MSS_XPU_CLK 40 @@ -99,7 +99,7 @@ #define CFPB2_H_CLK 82 #define SFAB_CFPB_M_H_CLK 83 #define CFPB_MASTER_H_CLK 84 -#define SFAB_CFPB_S_HCLK 85 +#define SFAB_CFPB_S_H_CLK 85 #define CFPB_SPLITTER_H_CLK 86 #define TSIF_H_CLK 87 #define TSIF_INACTIVITY_TIMERS_CLK 88 @@ -110,7 +110,6 @@ #define CE1_SLEEP_CLK 93 #define CE2_H_CLK 94 #define CE2_CORE_CLK 95 -#define CE2_SLEEP_CLK 96 #define SFPB_H_CLK_SRC 97 #define SFPB_H_CLK 98 #define SFAB_SFPB_M_H_CLK 99 @@ -252,7 +251,7 @@ #define MSS_S_H_CLK 235 #define MSS_CXO_SRC_CLK 236 #define SATA_H_CLK 237 -#define SATA_SRC_CLK 238 +#define SATA_CLK_SRC 238 #define SATA_RXOOB_CLK 239 #define SATA_PMALIVE_CLK 240 #define SATA_PHY_REF_CLK 241 diff --git a/include/dt-bindings/reset/qcom,gcc-msm8960.h b/include/dt-bindings/reset/qcom,gcc-msm8960.h index a840e680323c..07edd0e65eed 100644 --- a/include/dt-bindings/reset/qcom,gcc-msm8960.h +++ b/include/dt-bindings/reset/qcom,gcc-msm8960.h @@ -58,7 +58,7 @@ #define PPSS_PROC_RESET 41 #define PPSS_RESET 42 #define DMA_BAM_RESET 43 -#define SIC_TIC_RESET 44 +#define SPS_TIC_H_RESET 44 #define SLIMBUS_H_RESET 45 #define SFAB_CFPB_M_RESET 46 #define SFAB_CFPB_S_RESET 47 -- cgit v1.2.3 From c1b3156f121fd301191e0b4c5fa2fec42cd17871 Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Mon, 28 Apr 2014 18:43:32 +0200 Subject: drbd: use blk_set_stacking_limits() ...instead directly assigning to q->limits.discard_zeroes_data Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg Signed-off-by: Jens Axboe --- drivers/block/drbd/drbd_nl.c | 12 ++++++------ include/linux/drbd.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index a187c5b0da27..b4fc401b587f 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -1114,15 +1114,18 @@ static void drbd_setup_queue_param(struct drbd_device *device, unsigned int max_ struct request_queue * const q = device->rq_queue; unsigned int max_hw_sectors = max_bio_size >> 9; unsigned int max_segments = 0; + struct request_queue *b = NULL; if (get_ldev_if_state(device, D_ATTACHING)) { - struct request_queue * const b = device->ldev->backing_bdev->bd_disk->queue; + b = device->ldev->backing_bdev->bd_disk->queue; max_hw_sectors = min(queue_max_hw_sectors(b), max_bio_size >> 9); rcu_read_lock(); max_segments = rcu_dereference(device->ldev->disk_conf)->max_bio_bvecs; rcu_read_unlock(); - put_ldev(device); + + blk_set_stacking_limits(&q->limits); + blk_queue_max_write_same_sectors(q, 0); } blk_queue_logical_block_size(q, 512); @@ -1131,14 +1134,11 @@ static void drbd_setup_queue_param(struct drbd_device *device, unsigned int max_ blk_queue_max_segments(q, max_segments ? max_segments : BLK_MAX_SEGMENTS); blk_queue_segment_boundary(q, PAGE_CACHE_SIZE-1); - if (get_ldev_if_state(device, D_ATTACHING)) { - struct request_queue * const b = device->ldev->backing_bdev->bd_disk->queue; + if (b) { struct drbd_connection *connection = first_peer_device(device)->connection; if (blk_queue_discard(b) && (connection->cstate < C_CONNECTED || connection->agreed_features & FF_TRIM)) { - /* inherit from backing queue */ - q->limits.discard_zeroes_data = 1; /* For now, don't allow more than one activity log extent worth of data * to be discarded in one go. We may need to rework drbd_al_begin_io() * to allow for even larger discard ranges */ diff --git a/include/linux/drbd.h b/include/linux/drbd.h index 3dbe9bd57a09..fffd4b8563cb 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -52,7 +52,7 @@ #endif extern const char *drbd_buildtag(void); -#define REL_VERSION "8.4.3" +#define REL_VERSION "8.4.4" #define API_VERSION 1 #define PRO_VERSION_MIN 86 #define PRO_VERSION_MAX 101 -- cgit v1.2.3 From 02df6fe145715f1d3858c0c65aed991f148b70b4 Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Mon, 28 Apr 2014 18:43:33 +0200 Subject: drbd: Test cstate while holding req_lock In case a connection transitions into C_TIMEOUT within the timer function (request_timer_fn()) we need to make sure that the receiver thread (potentially running on a different CPU) sees the updated cstate later on. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg Signed-off-by: Jens Axboe --- drivers/block/drbd/drbd_nl.c | 3 ++- include/linux/drbd.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index b4fc401b587f..f4d3aff89aa1 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -442,12 +442,13 @@ bool conn_try_outdate_peer(struct drbd_connection *connection) char *ex_to_string; int r; + spin_lock_irq(&connection->resource->req_lock); if (connection->cstate >= C_WF_REPORT_PARAMS) { drbd_err(connection, "Expected cstate < C_WF_REPORT_PARAMS\n"); + spin_unlock_irq(&connection->resource->req_lock); return false; } - spin_lock_irq(&connection->resource->req_lock); connect_cnt = connection->connect_cnt; spin_unlock_irq(&connection->resource->req_lock); diff --git a/include/linux/drbd.h b/include/linux/drbd.h index fffd4b8563cb..3dbe9bd57a09 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -52,7 +52,7 @@ #endif extern const char *drbd_buildtag(void); -#define REL_VERSION "8.4.4" +#define REL_VERSION "8.4.3" #define API_VERSION 1 #define PRO_VERSION_MIN 86 #define PRO_VERSION_MAX 101 -- cgit v1.2.3 From 52c324f8a87b336496d0f5e9d8dff1aa32bb08cd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 1 May 2014 00:13:47 +0200 Subject: cpuidle: Combine cpuidle_enabled() with cpuidle_select() Since both cpuidle_enabled() and cpuidle_select() are only called by cpuidle_idle_call(), it is not really useful to keep them separate and combining them will help to avoid complicating cpuidle_idle_call() even further if governors are changed to return error codes sometimes. This code modification shouldn't lead to any functional changes. Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/cpuidle.c | 26 ++++++-------------------- include/linux/cpuidle.h | 5 ----- kernel/sched/idle.c | 20 +++++++------------- 3 files changed, 13 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 8236746e46bb..f38359f64cc6 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -64,26 +64,6 @@ int cpuidle_play_dead(void) return -ENODEV; } -/** - * cpuidle_enabled - check if the cpuidle framework is ready - * @dev: cpuidle device for this cpu - * @drv: cpuidle driver for this cpu - * - * Return 0 on success, otherwise: - * -NODEV : the cpuidle framework is not available - * -EBUSY : the cpuidle framework is not initialized - */ -int cpuidle_enabled(struct cpuidle_driver *drv, struct cpuidle_device *dev) -{ - if (off || !initialized) - return -ENODEV; - - if (!drv || !dev || !dev->enabled) - return -EBUSY; - - return 0; -} - /** * cpuidle_enter_state - enter the state and update stats * @dev: cpuidle device for this cpu @@ -138,6 +118,12 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, */ int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) { + if (off || !initialized) + return -ENODEV; + + if (!drv || !dev || !dev->enabled) + return -EBUSY; + return cpuidle_curr_governor->select(drv, dev); } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index b0238cba440b..a8d5bd391a26 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -120,8 +120,6 @@ struct cpuidle_driver { #ifdef CONFIG_CPU_IDLE extern void disable_cpuidle(void); -extern int cpuidle_enabled(struct cpuidle_driver *drv, - struct cpuidle_device *dev); extern int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev); extern int cpuidle_enter(struct cpuidle_driver *drv, @@ -149,9 +147,6 @@ extern int cpuidle_play_dead(void); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); #else static inline void disable_cpuidle(void) { } -static inline int cpuidle_enabled(struct cpuidle_driver *drv, - struct cpuidle_device *dev) -{return -ENODEV; } static inline int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) {return -ENODEV; } diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 8f4390a079c7..a8f12247ce7c 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -101,19 +101,13 @@ static int cpuidle_idle_call(void) rcu_idle_enter(); /* - * Check if the cpuidle framework is ready, otherwise fallback - * to the default arch specific idle method + * Ask the cpuidle framework to choose a convenient idle state. + * Fall back to the default arch specific idle method on errors. */ - ret = cpuidle_enabled(drv, dev); - - if (!ret) { - /* - * Ask the governor to choose an idle state it thinks - * it is convenient to go to. There is *always* a - * convenient idle state - */ - next_state = cpuidle_select(drv, dev); + next_state = cpuidle_select(drv, dev); + ret = next_state; + if (ret >= 0) { /* * The idle task must be scheduled, it is pointless to * go to idle, just update no idle residency and get @@ -140,7 +134,7 @@ static int cpuidle_idle_call(void) CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); - if (!ret) { + if (ret >= 0) { trace_cpu_idle_rcuidle(next_state, dev->cpu); /* @@ -175,7 +169,7 @@ static int cpuidle_idle_call(void) * We can't use the cpuidle framework, let's use the default * idle routine */ - if (ret) + if (ret < 0) arch_cpu_idle(); __current_set_polling(); -- cgit v1.2.3 From 034cd97ebda4062eb4402a6cf963ccd262caa86a Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 14 Apr 2014 15:28:35 +0200 Subject: PCI/MSI: Remove pci_enable_msi_block() There are no users of pci_enable_msi_block() function left. Obsolete it in favor of pci_enable_msi_range() and pci_enable_msi_exact() functions. Previously, we called arch_setup_msi_irqs() once, requesting the same vector count we passed to arch_msi_check_device(). Now we may call it several times: if it returns failure, we may retry and request fewer vectors. We don't keep track of the vector count we initially passed to arch_msi_check_device(). We only keep track of the number of vectors successfully set up by arch_setup_msi_irqs(), and this is what we use to clean things up when disabling MSI. Therefore, we assume that arch_msi_check_device() does nothing that will have to be cleaned up later. [bhelgaas: changelog] Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 79 ++++++++++++++++++++++------------------------------- include/linux/pci.h | 5 +--- 2 files changed, 34 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 04130c3f9cf6..36dd0caa1759 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -879,50 +879,6 @@ int pci_msi_vec_count(struct pci_dev *dev) } EXPORT_SYMBOL(pci_msi_vec_count); -/** - * pci_enable_msi_block - configure device's MSI capability structure - * @dev: device to configure - * @nvec: number of interrupts to configure - * - * Allocate IRQs for a device with the MSI capability. - * This function returns a negative errno if an error occurs. If it - * is unable to allocate the number of interrupts requested, it returns - * the number of interrupts it might be able to allocate. If it successfully - * allocates at least the number of interrupts requested, it returns 0 and - * updates the @dev's irq member to the lowest new interrupt number; the - * other interrupt numbers allocated to this device are consecutive. - */ -int pci_enable_msi_block(struct pci_dev *dev, int nvec) -{ - int status, maxvec; - - if (dev->current_state != PCI_D0) - return -EINVAL; - - maxvec = pci_msi_vec_count(dev); - if (maxvec < 0) - return maxvec; - if (nvec > maxvec) - return maxvec; - - status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); - if (status) - return status; - - WARN_ON(!!dev->msi_enabled); - - /* Check whether driver already requested MSI-X irqs */ - if (dev->msix_enabled) { - dev_info(&dev->dev, "can't enable MSI " - "(MSI-X already enabled)\n"); - return -EINVAL; - } - - status = msi_capability_init(dev, nvec); - return status; -} -EXPORT_SYMBOL(pci_enable_msi_block); - void pci_msi_shutdown(struct pci_dev *dev) { struct msi_desc *desc; @@ -1128,14 +1084,45 @@ void pci_msi_init_pci_dev(struct pci_dev *dev) **/ int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) { - int nvec = maxvec; + int nvec; int rc; + if (dev->current_state != PCI_D0) + return -EINVAL; + + WARN_ON(!!dev->msi_enabled); + + /* Check whether driver already requested MSI-X irqs */ + if (dev->msix_enabled) { + dev_info(&dev->dev, + "can't enable MSI (MSI-X already enabled)\n"); + return -EINVAL; + } + if (maxvec < minvec) return -ERANGE; + nvec = pci_msi_vec_count(dev); + if (nvec < 0) + return nvec; + else if (nvec < minvec) + return -EINVAL; + else if (nvec > maxvec) + nvec = maxvec; + + do { + rc = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); + if (rc < 0) { + return rc; + } else if (rc > 0) { + if (rc < minvec) + return -ENOSPC; + nvec = rc; + } + } while (rc); + do { - rc = pci_enable_msi_block(dev, nvec); + rc = msi_capability_init(dev, nvec); if (rc < 0) { return rc; } else if (rc > 0) { diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4abe7f..499755e6dab5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1158,7 +1158,6 @@ struct msix_entry { #ifdef CONFIG_PCI_MSI int pci_msi_vec_count(struct pci_dev *dev); -int pci_enable_msi_block(struct pci_dev *dev, int nvec); void pci_msi_shutdown(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev); int pci_msix_vec_count(struct pci_dev *dev); @@ -1188,8 +1187,6 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev, } #else static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; } -static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec) -{ return -ENOSYS; } static inline void pci_msi_shutdown(struct pci_dev *dev) { } static inline void pci_disable_msi(struct pci_dev *dev) { } static inline int pci_msix_vec_count(struct pci_dev *dev) { return -ENOSYS; } @@ -1244,7 +1241,7 @@ static inline void pcie_set_ecrc_checking(struct pci_dev *dev) { } static inline void pcie_ecrc_get_policy(char *str) { } #endif -#define pci_enable_msi(pdev) pci_enable_msi_block(pdev, 1) +#define pci_enable_msi(pdev) pci_enable_msi_exact(pdev, 1) #ifdef CONFIG_HT_IRQ /* The functions a driver should call */ -- cgit v1.2.3 From b87577b7c768683736eea28f70779e8c75b4df62 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 1 May 2014 09:26:53 +1000 Subject: drm: try harder to avoid regression when merging mode bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For QXL hw we really want the bits to be replaced as we change the preferred mode on the fly, and the same goes for virgl when I get to it, however the original fix for this seems to have caused a wierd regression on Intel G33 that in a stunning display of failure at opposition to his normal self, Daniel failed to diagnose. So we are left doing this, ugly ugly ugly ugly, Daniel you fixed that G33 yet?, ugly, ugly. Tested-by: Marc-André Lureau Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_modes.c | 9 ++++-- drivers/gpu/drm/drm_probe_helper.c | 64 +++++++++++++++++++++++++------------ drivers/gpu/drm/qxl/qxl_display.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- include/drm/drm_crtc_helper.h | 4 +++ include/drm/drm_modes.h | 2 +- 6 files changed, 57 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 8b410576fce4..bedf1894e17e 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1013,6 +1013,7 @@ EXPORT_SYMBOL(drm_mode_sort); /** * drm_mode_connector_list_update - update the mode list for the connector * @connector: the connector to update + * @merge_type_bits: whether to merge or overright type bits. * * This moves the modes from the @connector probed_modes list * to the actual mode list. It compares the probed mode against the current @@ -1021,7 +1022,8 @@ EXPORT_SYMBOL(drm_mode_sort); * This is just a helper functions doesn't validate any modes itself and also * doesn't prune any invalid modes. Callers need to do that themselves. */ -void drm_mode_connector_list_update(struct drm_connector *connector) +void drm_mode_connector_list_update(struct drm_connector *connector, + bool merge_type_bits) { struct drm_display_mode *mode; struct drm_display_mode *pmode, *pt; @@ -1039,7 +1041,10 @@ void drm_mode_connector_list_update(struct drm_connector *connector) /* if equal delete the probed mode */ mode->status = pmode->status; /* Merge type bits together */ - mode->type |= pmode->type; + if (merge_type_bits) + mode->type |= pmode->type; + else + mode->type = pmode->type; list_del(&pmode->head); drm_mode_destroy(connector->dev, pmode); break; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index e70f54d4a581..8afdd0998a8c 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -82,26 +82,8 @@ static void drm_mode_validate_flag(struct drm_connector *connector, return; } -/** - * drm_helper_probe_single_connector_modes - get complete set of display modes - * @connector: connector to probe - * @maxX: max width for modes - * @maxY: max height for modes - * - * Based on the helper callbacks implemented by @connector try to detect all - * valid modes. Modes will first be added to the connector's probed_modes list, - * then culled (based on validity and the @maxX, @maxY parameters) and put into - * the normal modes list. - * - * Intended to be use as a generic implementation of the ->fill_modes() - * @connector vfunc for drivers that use the crtc helpers for output mode - * filtering and detection. - * - * Returns: - * The number of modes found on @connector. - */ -int drm_helper_probe_single_connector_modes(struct drm_connector *connector, - uint32_t maxX, uint32_t maxY) +static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connector *connector, + uint32_t maxX, uint32_t maxY, bool merge_type_bits) { struct drm_device *dev = connector->dev; struct drm_display_mode *mode; @@ -155,7 +137,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, if (count == 0) goto prune; - drm_mode_connector_list_update(connector); + drm_mode_connector_list_update(connector, merge_type_bits); if (maxX && maxY) drm_mode_validate_size(dev, &connector->modes, maxX, maxY); @@ -194,8 +176,48 @@ prune: return count; } + +/** + * drm_helper_probe_single_connector_modes - get complete set of display modes + * @connector: connector to probe + * @maxX: max width for modes + * @maxY: max height for modes + * + * Based on the helper callbacks implemented by @connector try to detect all + * valid modes. Modes will first be added to the connector's probed_modes list, + * then culled (based on validity and the @maxX, @maxY parameters) and put into + * the normal modes list. + * + * Intended to be use as a generic implementation of the ->fill_modes() + * @connector vfunc for drivers that use the crtc helpers for output mode + * filtering and detection. + * + * Returns: + * The number of modes found on @connector. + */ +int drm_helper_probe_single_connector_modes(struct drm_connector *connector, + uint32_t maxX, uint32_t maxY) +{ + return drm_helper_probe_single_connector_modes_merge_bits(connector, maxX, maxY, true); +} EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); +/** + * drm_helper_probe_single_connector_modes_nomerge - get complete set of display modes + * @connector: connector to probe + * @maxX: max width for modes + * @maxY: max height for modes + * + * This operates like drm_hehlper_probe_single_connector_modes except it + * replaces the mode bits instead of merging them for preferred modes. + */ +int drm_helper_probe_single_connector_modes_nomerge(struct drm_connector *connector, + uint32_t maxX, uint32_t maxY) +{ + return drm_helper_probe_single_connector_modes_merge_bits(connector, maxX, maxY, false); +} +EXPORT_SYMBOL(drm_helper_probe_single_connector_modes_nomerge); + /** * drm_kms_helper_hotplug_event - fire off KMS hotplug events * @dev: drm_device whose connector state changed diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 41bdd174657e..3ab9072d3623 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -841,7 +841,7 @@ static const struct drm_connector_funcs qxl_connector_funcs = { .save = qxl_conn_save, .restore = qxl_conn_restore, .detect = qxl_conn_detect, - .fill_modes = drm_helper_probe_single_connector_modes, + .fill_modes = drm_helper_probe_single_connector_modes_nomerge, .set_property = qxl_conn_set_property, .destroy = qxl_conn_destroy, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index a2dde5ad8138..e7199b454ca0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2001,7 +2001,7 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector, if (du->pref_mode) list_move(&du->pref_mode->head, &connector->probed_modes); - drm_mode_connector_list_update(connector); + drm_mode_connector_list_update(connector, true); return 1; } diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 36a5febac2a6..f51c8393e9a8 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -165,6 +165,10 @@ extern void drm_helper_resume_force_mode(struct drm_device *dev); extern int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY); +extern int drm_helper_probe_single_connector_modes_nomerge(struct drm_connector + *connector, + uint32_t maxX, + uint32_t maxY); extern void drm_kms_helper_poll_init(struct drm_device *dev); extern void drm_kms_helper_poll_fini(struct drm_device *dev); extern bool drm_helper_hpd_irq_event(struct drm_device *dev); diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 2dbbf9976669..91d0582f924e 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -223,7 +223,7 @@ void drm_mode_validate_size(struct drm_device *dev, void drm_mode_prune_invalid(struct drm_device *dev, struct list_head *mode_list, bool verbose); void drm_mode_sort(struct list_head *mode_list); -void drm_mode_connector_list_update(struct drm_connector *connector); +void drm_mode_connector_list_update(struct drm_connector *connector, bool merge_type_bits); /* parsing cmdline modes */ bool -- cgit v1.2.3 From 3ca041ed04734c1709460184f985f5451a813d69 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 28 Apr 2014 16:07:22 +0200 Subject: ASoC: dt: Allow Aux Codecs to be specified using DT This patch adds support for specifying auxiliary codecs and codec configuration via device tree phandles. This change adds new fields to snd_soc_aux_dev and snd_soc_codec_conf and adds support for the changes to SoC core methods. Signed-off-by: Pavel Machek Signed-off-by: Sebastian Reichel Signed-off-by: Mark Brown --- include/sound/soc.h | 13 +++++++++- sound/soc/soc-core.c | 68 ++++++++++++++++++++++++++++++++-------------------- 2 files changed, 54 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168d8ff4..d371ae1c7279 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -931,7 +931,12 @@ struct snd_soc_dai_link { }; struct snd_soc_codec_conf { + /* + * specify device either by device name, or by + * DT/OF node, but not both. + */ const char *dev_name; + const struct device_node *of_node; /* * optional map of kcontrol, widget and path name prefixes that are @@ -942,7 +947,13 @@ struct snd_soc_codec_conf { struct snd_soc_aux_dev { const char *name; /* Codec name */ - const char *codec_name; /* for multi-codec */ + + /* + * specify multi-codec either by device name, or by + * DT/OF node, but not both. + */ + const char *codec_name; + const struct device_node *codec_of_node; /* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_dapm_context *dapm); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 051c006281f5..448a60748523 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1104,10 +1104,12 @@ static void soc_set_name_prefix(struct snd_soc_card *card, for (i = 0; i < card->num_configs; i++) { struct snd_soc_codec_conf *map = &card->codec_conf[i]; - if (map->dev_name && !strcmp(codec->name, map->dev_name)) { - codec->name_prefix = map->name_prefix; - break; - } + if (map->of_node && codec->dev->of_node != map->of_node) + continue; + if (map->dev_name && strcmp(codec->name, map->dev_name)) + continue; + codec->name_prefix = map->name_prefix; + break; } } @@ -1541,52 +1543,66 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec) } #endif -static int soc_check_aux_dev(struct snd_soc_card *card, int num) +struct snd_soc_codec *soc_find_matching_codec(struct snd_soc_card *card, int num) { struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; struct snd_soc_codec *codec; - /* find CODEC from registered CODECs*/ + /* find CODEC from registered CODECs */ list_for_each_entry(codec, &codec_list, list) { - if (!strcmp(codec->name, aux_dev->codec_name)) - return 0; + if (aux_dev->codec_of_node && + (codec->dev->of_node != aux_dev->codec_of_node)) + continue; + if (aux_dev->codec_name && strcmp(codec->name, aux_dev->codec_name)) + continue; + return codec; } - dev_err(card->dev, "ASoC: %s not registered\n", aux_dev->codec_name); + return NULL; +} + +static int soc_check_aux_dev(struct snd_soc_card *card, int num) +{ + struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; + const char *codecname = aux_dev->codec_name; + struct snd_soc_codec *codec = soc_find_matching_codec(card, num); + if (codec) + return 0; + if (aux_dev->codec_of_node) + codecname = of_node_full_name(aux_dev->codec_of_node); + + dev_err(card->dev, "ASoC: %s not registered\n", codecname); return -EPROBE_DEFER; } static int soc_probe_aux_dev(struct snd_soc_card *card, int num) { struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; - struct snd_soc_codec *codec; + const char *codecname = aux_dev->codec_name; int ret = -ENODEV; + struct snd_soc_codec *codec = soc_find_matching_codec(card, num); - /* find CODEC from registered CODECs*/ - list_for_each_entry(codec, &codec_list, list) { - if (!strcmp(codec->name, aux_dev->codec_name)) { - if (codec->probed) { - dev_err(codec->dev, - "ASoC: codec already probed"); - ret = -EBUSY; - goto out; - } - goto found; - } + if (!codec) { + if (aux_dev->codec_of_node) + codecname = of_node_full_name(aux_dev->codec_of_node); + + /* codec not found */ + dev_err(card->dev, "ASoC: codec %s not found", codecname); + return -EPROBE_DEFER; + } + + if (codec->probed) { + dev_err(codec->dev, "ASoC: codec already probed"); + return -EBUSY; } - /* codec not found */ - dev_err(card->dev, "ASoC: codec %s not found", aux_dev->codec_name); - return -EPROBE_DEFER; -found: ret = soc_probe_codec(card, codec); if (ret < 0) return ret; ret = soc_post_component_init(card, codec, num, 1); -out: return ret; } -- cgit v1.2.3 From ee3468739ed83d862dbbd90397aff5258f8f2c8e Mon Sep 17 00:00:00 2001 From: Brian W Hart Date: Thu, 1 May 2014 14:32:35 -0500 Subject: fbdev/fb.h: silence warning with -Wsign-compare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silence the warning when building with -Wsign-compare when fb.h is included: include/linux/fb.h: In function ‘__fb_pad_aligned_buffer’: include/linux/fb.h:650:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] for (j = 0; j < s_pitch; j++) ^ Signed-off-by: Brian W Hart Signed-off-by: Tomi Valkeinen --- include/linux/fb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fb.h b/include/linux/fb.h index 506242979eea..b6bfda99add3 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -642,7 +642,7 @@ static inline void unlock_fb_info(struct fb_info *info) static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height) { - int i, j; + u32 i, j; d_pitch -= s_pitch; -- cgit v1.2.3 From dfbb85cab5f0819d0424a3637b03e7892704fa42 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 2 Apr 2014 20:17:00 -0700 Subject: DMA: shdma: add cyclic transfer support This patch add cyclic transfer support and enables dmaengine_prep_dma_cyclic() Signed-off-by: Kuninori Morimoto [reflown changelog for readablity] Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 72 ++++++++++++++++++++++++++++++++++++++++----- include/linux/shdma-base.h | 1 + 2 files changed, 66 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 6786ecbd5ed4..974794cdb6ed 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -304,6 +304,7 @@ static dma_async_tx_callback __ld_cleanup(struct shdma_chan *schan, bool all) dma_async_tx_callback callback = NULL; void *param = NULL; unsigned long flags; + LIST_HEAD(cyclic_list); spin_lock_irqsave(&schan->chan_lock, flags); list_for_each_entry_safe(desc, _desc, &schan->ld_queue, node) { @@ -369,10 +370,16 @@ static dma_async_tx_callback __ld_cleanup(struct shdma_chan *schan, bool all) if (((desc->mark == DESC_COMPLETED || desc->mark == DESC_WAITING) && async_tx_test_ack(&desc->async_tx)) || all) { - /* Remove from ld_queue list */ - desc->mark = DESC_IDLE; - list_move(&desc->node, &schan->ld_free); + if (all || !desc->cyclic) { + /* Remove from ld_queue list */ + desc->mark = DESC_IDLE; + list_move(&desc->node, &schan->ld_free); + } else { + /* reuse as cyclic */ + desc->mark = DESC_SUBMITTED; + list_move_tail(&desc->node, &cyclic_list); + } if (list_empty(&schan->ld_queue)) { dev_dbg(schan->dev, "Bring down channel %d\n", schan->id); @@ -389,6 +396,8 @@ static dma_async_tx_callback __ld_cleanup(struct shdma_chan *schan, bool all) */ schan->dma_chan.completed_cookie = schan->dma_chan.cookie; + list_splice_tail(&cyclic_list, &schan->ld_queue); + spin_unlock_irqrestore(&schan->chan_lock, flags); if (callback) @@ -521,7 +530,7 @@ static struct shdma_desc *shdma_add_desc(struct shdma_chan *schan, */ static struct dma_async_tx_descriptor *shdma_prep_sg(struct shdma_chan *schan, struct scatterlist *sgl, unsigned int sg_len, dma_addr_t *addr, - enum dma_transfer_direction direction, unsigned long flags) + enum dma_transfer_direction direction, unsigned long flags, bool cyclic) { struct scatterlist *sg; struct shdma_desc *first = NULL, *new = NULL /* compiler... */; @@ -569,7 +578,11 @@ static struct dma_async_tx_descriptor *shdma_prep_sg(struct shdma_chan *schan, if (!new) goto err_get_desc; - new->chunks = chunks--; + new->cyclic = cyclic; + if (cyclic) + new->chunks = 1; + else + new->chunks = chunks--; list_add_tail(&new->node, &tx_list); } while (len); } @@ -612,7 +625,8 @@ static struct dma_async_tx_descriptor *shdma_prep_memcpy( sg_dma_address(&sg) = dma_src; sg_dma_len(&sg) = len; - return shdma_prep_sg(schan, &sg, 1, &dma_dest, DMA_MEM_TO_MEM, flags); + return shdma_prep_sg(schan, &sg, 1, &dma_dest, DMA_MEM_TO_MEM, + flags, false); } static struct dma_async_tx_descriptor *shdma_prep_slave_sg( @@ -640,7 +654,50 @@ static struct dma_async_tx_descriptor *shdma_prep_slave_sg( slave_addr = ops->slave_addr(schan); return shdma_prep_sg(schan, sgl, sg_len, &slave_addr, - direction, flags); + direction, flags, false); +} + +struct dma_async_tx_descriptor *shdma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct shdma_chan *schan = to_shdma_chan(chan); + struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); + const struct shdma_ops *ops = sdev->ops; + unsigned int sg_len = buf_len / period_len; + int slave_id = schan->slave_id; + dma_addr_t slave_addr; + struct scatterlist sgl[sg_len]; + int i; + + if (!chan) + return NULL; + + BUG_ON(!schan->desc_num); + + /* Someone calling slave DMA on a generic channel? */ + if (slave_id < 0 || (buf_len < period_len)) { + dev_warn(schan->dev, + "%s: bad parameter: buf_len=%d, period_len=%d, id=%d\n", + __func__, buf_len, period_len, slave_id); + return NULL; + } + + slave_addr = ops->slave_addr(schan); + + sg_init_table(sgl, sg_len); + for (i = 0; i < sg_len; i++) { + dma_addr_t src = buf_addr + (period_len * i); + + sg_set_page(&sgl[i], pfn_to_page(PFN_DOWN(src)), period_len, + offset_in_page(src)); + sg_dma_address(&sgl[i]) = src; + sg_dma_len(&sgl[i]) = period_len; + } + + return shdma_prep_sg(schan, sgl, sg_len, &slave_addr, + direction, flags, true); } static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, @@ -915,6 +972,7 @@ int shdma_init(struct device *dev, struct shdma_dev *sdev, /* Compulsory for DMA_SLAVE fields */ dma_dev->device_prep_slave_sg = shdma_prep_slave_sg; + dma_dev->device_prep_dma_cyclic = shdma_prep_dma_cyclic; dma_dev->device_control = shdma_control; dma_dev->dev = dev; diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index f92c0a43c54c..abdf1f229dc3 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -54,6 +54,7 @@ struct shdma_desc { dma_cookie_t cookie; int chunks; int mark; + bool cyclic; /* used as cyclic transfer */ }; struct shdma_chan { -- cgit v1.2.3 From 6d48f44b7b2af67b33c1ae5994b8f642685c8bc8 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 30 Apr 2014 15:23:33 +0300 Subject: mdio_bus: implement devm_mdiobus_alloc/devm_mdiobus_free Add a resource managed devm_mdiobus_alloc[_size]()/devm_mdiobus_free() to automatically clean up MDIO bus alocations made by MDIO drivers, thus leading to simplified MDIO drivers code. Cc: Florian Fainelli Cc: Sergei Shtylyov Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- Documentation/driver-model/devres.txt | 5 +++ drivers/net/phy/mdio_bus.c | 67 +++++++++++++++++++++++++++++++++++ include/linux/phy.h | 7 ++++ 3 files changed, 79 insertions(+) (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 4f7897e99cba..c74e04494ade 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -308,3 +308,8 @@ SLAVE DMA ENGINE SPI devm_spi_register_master() + +MDIO + devm_mdiobus_alloc() + devm_mdiobus_alloc_size() + devm_mdiobus_free() diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 76f54b32a120..68a9a3867c0f 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -69,6 +69,73 @@ struct mii_bus *mdiobus_alloc_size(size_t size) } EXPORT_SYMBOL(mdiobus_alloc_size); +static void _devm_mdiobus_free(struct device *dev, void *res) +{ + mdiobus_free(*(struct mii_bus **)res); +} + +static int devm_mdiobus_match(struct device *dev, void *res, void *data) +{ + struct mii_bus **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +/** + * devm_mdiobus_alloc_size - Resource-managed mdiobus_alloc_size() + * @dev: Device to allocate mii_bus for + * @sizeof_priv: Space to allocate for private structure. + * + * Managed mdiobus_alloc_size. mii_bus allocated with this function is + * automatically freed on driver detach. + * + * If an mii_bus allocated with this function needs to be freed separately, + * devm_mdiobus_free() must be used. + * + * RETURNS: + * Pointer to allocated mii_bus on success, NULL on failure. + */ +struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv) +{ + struct mii_bus **ptr, *bus; + + ptr = devres_alloc(_devm_mdiobus_free, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + /* use raw alloc_dr for kmalloc caller tracing */ + bus = mdiobus_alloc_size(sizeof_priv); + if (bus) { + *ptr = bus; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return bus; +} +EXPORT_SYMBOL_GPL(devm_mdiobus_alloc); + +/** + * devm_mdiobus_free - Resource-managed mdiobus_free() + * @dev: Device this mii_bus belongs to + * @bus: the mii_bus associated with the device + * + * Free mii_bus allocated with devm_mdiobus_alloc_size(). + */ +void devm_mdiobus_free(struct device *dev, struct mii_bus *bus) +{ + int rc; + + rc = devres_release(dev, _devm_mdiobus_free, + devm_mdiobus_match, bus); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_mdiobus_free); + /** * mdiobus_release - mii_bus device release callback * @d: the target struct device that contains the mii_bus diff --git a/include/linux/phy.h b/include/linux/phy.h index 51d15f684e7e..864ddafad8cc 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -198,6 +198,13 @@ static inline struct mii_bus *mdiobus_alloc(void) int mdiobus_register(struct mii_bus *bus); void mdiobus_unregister(struct mii_bus *bus); void mdiobus_free(struct mii_bus *bus); +struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv); +static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev) +{ + return devm_mdiobus_alloc_size(dev, 0); +} + +void devm_mdiobus_free(struct device *dev, struct mii_bus *bus); struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); -- cgit v1.2.3 From d98812082c87732b45c71d63afc6a9ba3cca3f03 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 2 May 2014 10:58:11 +0530 Subject: ASoC: add SND_SOC_BYTES_EXT we need _EXT version for SND_SOC_BYTES so that DSPs can use this to pass data for DSP modules Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/soc.h | 13 +++++++++++++ sound/soc/soc-core.c | 12 ++++++++++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 192ddc40ae0a..3da5f2328ff3 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -266,6 +266,13 @@ {.base = xbase, .num_regs = xregs, \ .mask = xmask }) } +#define SND_SOC_BYTES_EXT(xname, xcount, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_bytes_ext) \ + {.max = xcount} } + #define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \ xmin, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -534,6 +541,8 @@ int snd_soc_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol); int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, @@ -1080,6 +1089,10 @@ struct soc_bytes { u32 mask; }; +struct soc_bytes_ext { + int max; +}; + /* multi register control */ struct soc_mreg_control { long min, max; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 799cbe81fe39..7fa3b4a04bd3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3107,6 +3107,18 @@ out: } EXPORT_SYMBOL_GPL(snd_soc_bytes_put); +int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); + /** * snd_soc_info_xr_sx - signed multi register info callback * @kcontrol: mreg control -- cgit v1.2.3 From e114a710aa5058c0ba4aa1dfb105132aefeb5e04 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 30 Apr 2014 11:58:13 -0700 Subject: tcp: fix cwnd limited checking to improve congestion control Yuchung discovered tcp_is_cwnd_limited() was returning false in slow start phase even if the application filled the socket write queue. All congestion modules take into account tcp_is_cwnd_limited() before increasing cwnd, so this behavior limits slow start from probing the bandwidth at full speed. The problem is that even if write queue is full (aka we are _not_ application limited), cwnd can be under utilized if TSO should auto defer or TCP Small queues decided to hold packets. So the in_flight can be kept to smaller value, and we can get to the point tcp_is_cwnd_limited() returns false. With TCP Small Queues and FQ/pacing, this issue is more visible. We fix this by having tcp_cwnd_validate(), which is supposed to track such things, take into account unsent_segs, the number of segs that we are not sending at the moment due to TSO or TSQ, but intend to send real soon. Then when we are cwnd-limited, remember this fact while we are processing the window of ACKs that comes back. For example, suppose we have a brand new connection with cwnd=10; we are in slow start, and we send a flight of 9 packets. By the time we have received ACKs for all 9 packets we want our cwnd to be 18. We implement this by setting tp->lsnd_pending to 9, and considering ourselves to be cwnd-limited while cwnd is less than twice tp->lsnd_pending (2*9 -> 18). This makes tcp_is_cwnd_limited() more understandable, by removing the GSO/TSO kludge, that tried to work around the issue. Note the in_flight parameter can be removed in a followup cleanup patch. Signed-off-by: Eric Dumazet Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + include/net/tcp.h | 22 +++++++++++++++++++++- net/ipv4/tcp_cong.c | 20 -------------------- net/ipv4/tcp_output.c | 21 ++++++++++++++------- 4 files changed, 36 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 239946868142..4e37c71ecd74 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -230,6 +230,7 @@ struct tcp_sock { u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ u32 snd_cwnd_used; u32 snd_cwnd_stamp; + u32 lsnd_pending; /* packets inflight or unsent since last xmit */ u32 prior_cwnd; /* Congestion window at start of Recovery. */ u32 prr_delivered; /* Number of newly delivered packets to * receiver in Recovery. */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 163d2b467d78..a9fe7bc4f4bb 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -974,7 +974,27 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp) { return tp->snd_una + tp->snd_wnd; } -bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight); + +/* We follow the spirit of RFC2861 to validate cwnd but implement a more + * flexible approach. The RFC suggests cwnd should not be raised unless + * it was fully used previously. But we allow cwnd to grow as long as the + * application has used half the cwnd. + * Example : + * cwnd is 10 (IW10), but application sends 9 frames. + * We allow cwnd to reach 18 when all frames are ACKed. + * This check is safe because it's as aggressive as slow start which already + * risks 100% overshoot. The advantage is that we discourage application to + * either send more filler packets or data to artificially blow up the cwnd + * usage, and allow application-limited process to probe bw more aggressively. + * + * TODO: remove in_flight once we can fix all callers, and their callers... + */ +static inline bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight) +{ + const struct tcp_sock *tp = tcp_sk(sk); + + return tp->snd_cwnd < 2 * tp->lsnd_pending; +} static inline void tcp_check_probe_timer(struct sock *sk) { diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 2b9464c93b88..a93b41ba05ff 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -276,26 +276,6 @@ int tcp_set_congestion_control(struct sock *sk, const char *name) return err; } -/* RFC2861 Check whether we are limited by application or congestion window - * This is the inverse of cwnd check in tcp_tso_should_defer - */ -bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight) -{ - const struct tcp_sock *tp = tcp_sk(sk); - u32 left; - - if (in_flight >= tp->snd_cwnd) - return true; - - left = tp->snd_cwnd - in_flight; - if (sk_can_gso(sk) && - left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd && - left < tp->xmit_size_goal_segs) - return true; - return left <= tcp_max_tso_deferred_mss(tp); -} -EXPORT_SYMBOL_GPL(tcp_is_cwnd_limited); - /* Slow start is used when congestion window is no greater than the slow start * threshold. We base on RFC2581 and also handle stretch ACKs properly. * We do not implement RFC3465 Appropriate Byte Counting (ABC) per se but diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 20847de991ea..f9181a133462 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1402,12 +1402,13 @@ static void tcp_cwnd_application_limited(struct sock *sk) tp->snd_cwnd_stamp = tcp_time_stamp; } -/* Congestion window validation. (RFC2861) */ -static void tcp_cwnd_validate(struct sock *sk) +static void tcp_cwnd_validate(struct sock *sk, u32 unsent_segs) { struct tcp_sock *tp = tcp_sk(sk); - if (tp->packets_out >= tp->snd_cwnd) { + tp->lsnd_pending = tp->packets_out + unsent_segs; + + if (tcp_is_cwnd_limited(sk, 0)) { /* Network is feed fully. */ tp->snd_cwnd_used = 0; tp->snd_cwnd_stamp = tcp_time_stamp; @@ -1880,7 +1881,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - unsigned int tso_segs, sent_pkts; + unsigned int tso_segs, sent_pkts, unsent_segs = 0; int cwnd_quota; int result; @@ -1924,7 +1925,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, break; } else { if (!push_one && tcp_tso_should_defer(sk, skb)) - break; + goto compute_unsent_segs; } /* TCP Small Queues : @@ -1949,8 +1950,14 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, * there is no smp_mb__after_set_bit() yet */ smp_mb__after_clear_bit(); - if (atomic_read(&sk->sk_wmem_alloc) > limit) + if (atomic_read(&sk->sk_wmem_alloc) > limit) { + u32 unsent_bytes; + +compute_unsent_segs: + unsent_bytes = tp->write_seq - tp->snd_nxt; + unsent_segs = DIV_ROUND_UP(unsent_bytes, mss_now); break; + } } limit = mss_now; @@ -1990,7 +1997,7 @@ repair: /* Send one loss probe per tail loss episode. */ if (push_one != 2) tcp_schedule_loss_probe(sk); - tcp_cwnd_validate(sk); + tcp_cwnd_validate(sk, unsent_segs); return false; } return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk)); -- cgit v1.2.3 From 638b43b347216bab1a989b036a92eb7d9d9ee421 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Wed, 5 Feb 2014 16:57:00 +0000 Subject: iio: Add TEMP_AMBIENT and TEMP_OBJECT channel modifiers useful for contactless temperature sensors to distinguish between the ambient temperature and the temperature of the object Signed-off-by: Peter Meerwald Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 ++ include/linux/iio/types.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index de8b1c2ed4b4..4b1f375c5659 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -85,6 +85,8 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_LIGHT_GREEN] = "green", [IIO_MOD_LIGHT_BLUE] = "blue", [IIO_MOD_QUATERNION] = "quaternion", + [IIO_MOD_TEMP_AMBIENT] = "ambient", + [IIO_MOD_TEMP_OBJECT] = "object", }; /* relies on pairs of these shared then separate */ diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 4fdab2e843b4..d480631eabc2 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -54,6 +54,8 @@ enum iio_modifier { IIO_MOD_LIGHT_GREEN, IIO_MOD_LIGHT_BLUE, IIO_MOD_QUATERNION, + IIO_MOD_TEMP_AMBIENT, + IIO_MOD_TEMP_OBJECT, }; enum iio_event_type { -- cgit v1.2.3 From 1319b2f6a565231ca2e3f368ab7f74fc4714c799 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Mon, 28 Apr 2014 19:59:10 +0800 Subject: ASoC: rt5645: Add codec driver This patch adds the Realtek ALC5645 codec driver. It is the base version that because the jack detect function is not implemented to it, the headphone and AMIC1 are not workable. We will fill up the further functions later. Signed-off-by: Bard Liao Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- include/sound/rt5645.h | 25 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5645.c | 2476 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5645.h | 2188 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 4695 insertions(+) create mode 100644 include/sound/rt5645.h create mode 100644 sound/soc/codecs/rt5645.c create mode 100644 sound/soc/codecs/rt5645.h (limited to 'include') diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h new file mode 100644 index 000000000000..1de744c242f6 --- /dev/null +++ b/include/sound/rt5645.h @@ -0,0 +1,25 @@ +/* + * linux/sound/rt5645.h -- Platform data for RT5645 + * + * Copyright 2013 Realtek Microelectronics + * + * 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_SND_RT5645_H +#define __LINUX_SND_RT5645_H + +struct rt5645_platform_data { + /* IN2 can optionally be differential */ + bool in2_diff; + + bool dmic_en; + unsigned int dmic1_data_pin; + /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */ + unsigned int dmic2_data_pin; + /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */ +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index deba71bad6e9..d29f19b44638 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -71,6 +71,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C + select SND_SOC_RT5645 if I2C select SND_SOC_RT5651 if I2C select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE @@ -397,6 +398,9 @@ config SND_SOC_RT5631 config SND_SOC_RT5640 tristate +config SND_SOC_RT5645 + tristate + config SND_SOC_RT5651 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e76938ee82f4..a3d12b4af1e7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -60,6 +60,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o +snd-soc-rt5645-objs := rt5645.o snd-soc-rt5651-objs := rt5651.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o @@ -212,6 +213,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o +obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c new file mode 100644 index 000000000000..7d8cc1688fb5 --- /dev/null +++ b/sound/soc/codecs/rt5645.c @@ -0,0 +1,2476 @@ +/* + * rt5645.c -- RT5645 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Bard Liao + * + * 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 +#include +#include +#include +#include +#include + +#include "rt5645.h" + +#define RT5645_DEVICE_ID 0x6308 + +#define RT5645_PR_RANGE_BASE (0xff + 1) +#define RT5645_PR_SPACING 0x100 + +#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING)) + +static const struct regmap_range_cfg rt5645_ranges[] = { + { + .name = "PR", + .range_min = RT5645_PR_BASE, + .range_max = RT5645_PR_BASE + 0xf8, + .selector_reg = RT5645_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5645_PRIV_DATA, + .window_len = 0x1, + }, +}; + +static const struct reg_default init_list[] = { + {RT5645_PR_BASE + 0x3d, 0x3600}, +}; +#define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list) + +static const struct reg_default rt5645_reg[] = { + { 0x00, 0x0000 }, + { 0x01, 0xc8c8 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x0a, 0x0002 }, + { 0x0b, 0x2827 }, + { 0x0c, 0xe000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x14, 0x3333 }, + { 0x16, 0x4b00 }, + { 0x18, 0x018b }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0001 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x20, 0x0000 }, + { 0x27, 0x7060 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5656 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaaa0 }, + { 0x2f, 0x1002 }, + { 0x31, 0x5000 }, + { 0x32, 0x0000 }, + { 0x33, 0x0000 }, + { 0x34, 0x0000 }, + { 0x35, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x3f, 0x0000 }, + { 0x40, 0x001f }, + { 0x41, 0x0000 }, + { 0x42, 0x001f }, + { 0x45, 0x6000 }, + { 0x46, 0x003e }, + { 0x47, 0x003e }, + { 0x48, 0xf807 }, + { 0x4a, 0x0004 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x53, 0xf000 }, + { 0x56, 0x0111 }, + { 0x57, 0x0064 }, + { 0x58, 0xef0e }, + { 0x59, 0xf0f0 }, + { 0x5a, 0xef0e }, + { 0x5b, 0xf0f0 }, + { 0x5c, 0xef0e }, + { 0x5d, 0xf0f0 }, + { 0x5e, 0xf000 }, + { 0x5f, 0x0000 }, + { 0x61, 0x0300 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c2 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6c, 0x0aaa }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x7770 }, + { 0x74, 0x3e00 }, + { 0x75, 0x2409 }, + { 0x76, 0x000a }, + { 0x77, 0x0c00 }, + { 0x78, 0x0000 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0000 }, + { 0x8a, 0x0000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c06 }, + { 0x93, 0x0000 }, + { 0x94, 0x0200 }, + { 0x95, 0x0000 }, + { 0x9a, 0x2184 }, + { 0x9b, 0x010a }, + { 0x9c, 0x0aea }, + { 0x9d, 0x000c }, + { 0x9e, 0x0400 }, + { 0xa0, 0xa0a8 }, + { 0xa1, 0x0059 }, + { 0xa2, 0x0001 }, + { 0xae, 0x6000 }, + { 0xaf, 0x0000 }, + { 0xb0, 0x6000 }, + { 0xb1, 0x0000 }, + { 0xb2, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x020c }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x3100 }, + { 0xc0, 0x0000 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xc3, 0x2000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x1813 }, + { 0xd0, 0x0690 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd4, 0x0000 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0003 }, + { 0xdc, 0x0049 }, + { 0xdd, 0x001b }, + { 0xe6, 0x8000 }, + { 0xe7, 0x0200 }, + { 0xec, 0xb300 }, + { 0xed, 0x0000 }, + { 0xf0, 0x001f }, + { 0xf1, 0x020c }, + { 0xf2, 0x1f00 }, + { 0xf3, 0x0000 }, + { 0xf4, 0x4000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x2060 }, + { 0xfb, 0x4040 }, + { 0xfc, 0x0000 }, + { 0xfd, 0x0002 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6308 }, +}; + +static int rt5645_reset(struct snd_soc_codec *codec) +{ + return snd_soc_write(codec, RT5645_RESET, 0); +} + +static bool rt5645_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) { + if (reg >= rt5645_ranges[i].range_min && + reg <= rt5645_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT5645_RESET: + case RT5645_PRIV_DATA: + case RT5645_IN1_CTRL1: + case RT5645_IN1_CTRL2: + case RT5645_IN1_CTRL3: + case RT5645_A_JD_CTRL1: + case RT5645_ADC_EQ_CTRL1: + case RT5645_EQ_CTRL1: + case RT5645_ALC_CTRL_1: + case RT5645_IRQ_CTRL2: + case RT5645_IRQ_CTRL3: + case RT5645_INT_IRQ_ST: + case RT5645_IL_CMD: + case RT5645_VENDOR_ID: + case RT5645_VENDOR_ID1: + case RT5645_VENDOR_ID2: + return 1; + default: + return 0; + } +} + +static bool rt5645_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) { + if (reg >= rt5645_ranges[i].range_min && + reg <= rt5645_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT5645_RESET: + case RT5645_SPK_VOL: + case RT5645_HP_VOL: + case RT5645_LOUT1: + case RT5645_IN1_CTRL1: + case RT5645_IN1_CTRL2: + case RT5645_IN1_CTRL3: + case RT5645_IN2_CTRL: + case RT5645_INL1_INR1_VOL: + case RT5645_SPK_FUNC_LIM: + case RT5645_ADJ_HPF_CTRL: + case RT5645_DAC1_DIG_VOL: + case RT5645_DAC2_DIG_VOL: + case RT5645_DAC_CTRL: + case RT5645_STO1_ADC_DIG_VOL: + case RT5645_MONO_ADC_DIG_VOL: + case RT5645_ADC_BST_VOL1: + case RT5645_ADC_BST_VOL2: + case RT5645_STO1_ADC_MIXER: + case RT5645_MONO_ADC_MIXER: + case RT5645_AD_DA_MIXER: + case RT5645_STO_DAC_MIXER: + case RT5645_MONO_DAC_MIXER: + case RT5645_DIG_MIXER: + case RT5645_DIG_INF1_DATA: + case RT5645_PDM_OUT_CTRL: + case RT5645_REC_L1_MIXER: + case RT5645_REC_L2_MIXER: + case RT5645_REC_R1_MIXER: + case RT5645_REC_R2_MIXER: + case RT5645_HPMIXL_CTRL: + case RT5645_HPOMIXL_CTRL: + case RT5645_HPMIXR_CTRL: + case RT5645_HPOMIXR_CTRL: + case RT5645_HPO_MIXER: + case RT5645_SPK_L_MIXER: + case RT5645_SPK_R_MIXER: + case RT5645_SPO_MIXER: + case RT5645_SPO_CLSD_RATIO: + case RT5645_OUT_L1_MIXER: + case RT5645_OUT_R1_MIXER: + case RT5645_OUT_L_GAIN1: + case RT5645_OUT_L_GAIN2: + case RT5645_OUT_R_GAIN1: + case RT5645_OUT_R_GAIN2: + case RT5645_LOUT_MIXER: + case RT5645_HAPTIC_CTRL1: + case RT5645_HAPTIC_CTRL2: + case RT5645_HAPTIC_CTRL3: + case RT5645_HAPTIC_CTRL4: + case RT5645_HAPTIC_CTRL5: + case RT5645_HAPTIC_CTRL6: + case RT5645_HAPTIC_CTRL7: + case RT5645_HAPTIC_CTRL8: + case RT5645_HAPTIC_CTRL9: + case RT5645_HAPTIC_CTRL10: + case RT5645_PWR_DIG1: + case RT5645_PWR_DIG2: + case RT5645_PWR_ANLG1: + case RT5645_PWR_ANLG2: + case RT5645_PWR_MIXER: + case RT5645_PWR_VOL: + case RT5645_PRIV_INDEX: + case RT5645_PRIV_DATA: + case RT5645_I2S1_SDP: + case RT5645_I2S2_SDP: + case RT5645_ADDA_CLK1: + case RT5645_ADDA_CLK2: + case RT5645_DMIC_CTRL1: + case RT5645_DMIC_CTRL2: + case RT5645_TDM_CTRL_1: + case RT5645_TDM_CTRL_2: + case RT5645_GLB_CLK: + case RT5645_PLL_CTRL1: + case RT5645_PLL_CTRL2: + case RT5645_ASRC_1: + case RT5645_ASRC_2: + case RT5645_ASRC_3: + case RT5645_ASRC_4: + case RT5645_DEPOP_M1: + case RT5645_DEPOP_M2: + case RT5645_DEPOP_M3: + case RT5645_MICBIAS: + case RT5645_A_JD_CTRL1: + case RT5645_VAD_CTRL4: + case RT5645_CLSD_OUT_CTRL: + case RT5645_ADC_EQ_CTRL1: + case RT5645_ADC_EQ_CTRL2: + case RT5645_EQ_CTRL1: + case RT5645_EQ_CTRL2: + case RT5645_ALC_CTRL_1: + case RT5645_ALC_CTRL_2: + case RT5645_ALC_CTRL_3: + case RT5645_ALC_CTRL_4: + case RT5645_ALC_CTRL_5: + case RT5645_JD_CTRL: + case RT5645_IRQ_CTRL1: + case RT5645_IRQ_CTRL2: + case RT5645_IRQ_CTRL3: + case RT5645_INT_IRQ_ST: + case RT5645_GPIO_CTRL1: + case RT5645_GPIO_CTRL2: + case RT5645_GPIO_CTRL3: + case RT5645_BASS_BACK: + case RT5645_MP3_PLUS1: + case RT5645_MP3_PLUS2: + case RT5645_ADJ_HPF1: + case RT5645_ADJ_HPF2: + case RT5645_HP_CALIB_AMP_DET: + case RT5645_SV_ZCD1: + case RT5645_SV_ZCD2: + case RT5645_IL_CMD: + case RT5645_IL_CMD2: + case RT5645_IL_CMD3: + case RT5645_DRC1_HL_CTRL1: + case RT5645_DRC2_HL_CTRL1: + case RT5645_ADC_MONO_HP_CTRL1: + case RT5645_ADC_MONO_HP_CTRL2: + case RT5645_DRC2_CTRL1: + case RT5645_DRC2_CTRL2: + case RT5645_DRC2_CTRL3: + case RT5645_DRC2_CTRL4: + case RT5645_DRC2_CTRL5: + case RT5645_JD_CTRL3: + case RT5645_JD_CTRL4: + case RT5645_GEN_CTRL1: + case RT5645_GEN_CTRL2: + case RT5645_GEN_CTRL3: + case RT5645_VENDOR_ID: + case RT5645_VENDOR_ID1: + case RT5645_VENDOR_ID2: + return 1; + default: + return 0; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static unsigned int bst_tlv[] = { + TLV_DB_RANGE_HEAD(7), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), +}; + +static const char * const rt5645_tdm_data_swap_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum, + RT5645_TDM_CTRL_1, 6, rt5645_tdm_data_swap_select); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum, + RT5645_TDM_CTRL_1, 4, rt5645_tdm_data_swap_select); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum, + RT5645_TDM_CTRL_1, 2, rt5645_tdm_data_swap_select); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot6_7_enum, + RT5645_TDM_CTRL_1, 0, rt5645_tdm_data_swap_select); + +static const char * const rt5645_tdm_adc_data_select[] = { + "1/2/R", "2/1/R", "R/1/2", "R/2/1" +}; + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum, + RT5645_TDM_CTRL_1, 8, + rt5645_tdm_adc_data_select); + +static const struct snd_kcontrol_new rt5645_snd_controls[] = { + /* Speaker Output Volume */ + SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, + RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* Headphone Output Volume */ + SOC_DOUBLE("HP Channel Switch", RT5645_HP_VOL, + RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("HP Playback Volume", RT5645_HP_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* OUTPUT Control */ + SOC_DOUBLE("OUT Playback Switch", RT5645_LOUT1, + RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("OUT Channel Switch", RT5645_LOUT1, + RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("OUT Playback Volume", RT5645_LOUT1, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE("DAC2 Playback Switch", RT5645_DAC_CTRL, + RT5645_M_DAC_L2_VOL_SFT, RT5645_M_DAC_R2_VOL_SFT, 1, 1), + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5645_DAC1_DIG_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5645_DAC2_DIG_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv), + + /* IN1/IN2 Control */ + SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1, + RT5645_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL, + RT5645_BST_SFT2, 8, 0, bst_tlv), + + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5645_INL1_INR1_VOL, + RT5645_INL_VOL_SFT, RT5645_INR_VOL_SFT, 31, 1, in_vol_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5645_STO1_ADC_DIG_VOL, + RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5645_STO1_ADC_DIG_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv), + SOC_DOUBLE("Mono ADC Capture Switch", RT5645_MONO_ADC_DIG_VOL, + RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5645_MONO_ADC_DIG_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5645_ADC_BST_VOL1, + RT5645_STO1_ADC_L_BST_SFT, RT5645_STO1_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + SOC_DOUBLE_TLV("STO2 ADC Boost Gain", RT5645_ADC_BST_VOL1, + RT5645_STO2_ADC_L_BST_SFT, RT5645_STO2_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + + /* I2S2 function select */ + SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT, + 1, 1), + + /* TDM */ + SOC_ENUM("TDM Adc Slot0 1 Data", rt5645_tdm_adc_slot0_1_enum), + SOC_ENUM("TDM Adc Slot2 3 Data", rt5645_tdm_adc_slot2_3_enum), + SOC_ENUM("TDM Adc Slot4 5 Data", rt5645_tdm_adc_slot4_5_enum), + SOC_ENUM("TDM Adc Slot6 7 Data", rt5645_tdm_adc_slot6_7_enum), + SOC_ENUM("TDM IF1 ADC DATA Sel", rt5645_tdm_adc_sel_enum), + SOC_SINGLE("TDM IF1_DAC1_L Sel", RT5645_TDM_CTRL_3, 12, 7, 0), + SOC_SINGLE("TDM IF1_DAC1_R Sel", RT5645_TDM_CTRL_3, 8, 7, 0), + SOC_SINGLE("TDM IF1_DAC2_L Sel", RT5645_TDM_CTRL_3, 4, 7, 0), + SOC_SINGLE("TDM IF1_DAC2_R Sel", RT5645_TDM_CTRL_3, 0, 7, 0), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + int div[] = {2, 3, 4, 6, 8, 12}; + int idx = -EINVAL, i; + int rate, red, bound, temp; + + rate = rt5645->sysclk; + red = 3000000 * 12; + for (i = 0; i < ARRAY_SIZE(div); i++) { + bound = div[i] * 3000000; + if (rate > bound) + continue; + temp = bound - rate; + if (temp < red) { + red = temp; + idx = i; + } + } + + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else + snd_soc_update_bits(codec, RT5645_DMIC_CTRL1, + RT5645_DMIC_CLK_MASK, idx << RT5645_DMIC_CLK_SFT); + return idx; +} + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + + val = snd_soc_read(source->codec, RT5645_GLB_CLK); + val &= RT5645_SCLK_SRC_MASK; + if (val == RT5645_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER, + RT5645_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER, + RT5645_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER, + RT5645_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER, + RT5645_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER, + RT5645_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER, + RT5645_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER, + RT5645_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER, + RT5645_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER, + RT5645_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER, + RT5645_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER, + RT5645_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER, + RT5645_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_L2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_R2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_L1_STO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_R2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_L2_MONO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_dig_l_mix[] = { + SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5645_DIG_MIXER, + RT5645_M_STO_L_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER, + RT5645_M_DAC_L2_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER, + RT5645_M_DAC_R2_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_dig_r_mix[] = { + SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5645_DIG_MIXER, + RT5645_M_STO_R_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER, + RT5645_M_DAC_R2_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER, + RT5645_M_DAC_L2_DAC_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5645_rec_l_mix[] = { + SOC_DAPM_SINGLE("HPOL Switch", RT5645_REC_L2_MIXER, + RT5645_M_HP_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5645_REC_L2_MIXER, + RT5645_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_L2_MIXER, + RT5645_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_L2_MIXER, + RT5645_M_BST1_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXL Switch", RT5645_REC_L2_MIXER, + RT5645_M_OM_L_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_rec_r_mix[] = { + SOC_DAPM_SINGLE("HPOR Switch", RT5645_REC_R2_MIXER, + RT5645_M_HP_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5645_REC_R2_MIXER, + RT5645_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_R2_MIXER, + RT5645_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_R2_MIXER, + RT5645_M_BST1_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXR Switch", RT5645_REC_R2_MIXER, + RT5645_M_OM_R_RM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_spk_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPK_L_MIXER, + RT5645_M_DAC_L1_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_SPK_L_MIXER, + RT5645_M_DAC_L2_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5645_SPK_L_MIXER, + RT5645_M_IN_L_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5645_SPK_L_MIXER, + RT5645_M_BST1_L_SM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_spk_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPK_R_MIXER, + RT5645_M_DAC_R1_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_SPK_R_MIXER, + RT5645_M_DAC_R2_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5645_SPK_R_MIXER, + RT5645_M_IN_R_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5645_SPK_R_MIXER, + RT5645_M_BST2_R_SM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_out_l_mix[] = { + SOC_DAPM_SINGLE("BST1 Switch", RT5645_OUT_L1_MIXER, + RT5645_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5645_OUT_L1_MIXER, + RT5645_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_OUT_L1_MIXER, + RT5645_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_OUT_L1_MIXER, + RT5645_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5645_OUT_R1_MIXER, + RT5645_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5645_OUT_R1_MIXER, + RT5645_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_OUT_R1_MIXER, + RT5645_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_OUT_R1_MIXER, + RT5645_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_spo_l_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER, + RT5645_M_DAC_R1_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPO_MIXER, + RT5645_M_DAC_L1_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER, + RT5645_M_SV_R_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL L Switch", RT5645_SPO_MIXER, + RT5645_M_SV_L_SPM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_spo_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER, + RT5645_M_DAC_R1_SPM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER, + RT5645_M_SV_R_SPM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_hpo_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPO_MIXER, + RT5645_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPVOL Switch", RT5645_HPO_MIXER, + RT5645_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_hpvoll_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXL_CTRL, + RT5645_M_DAC1_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXL_CTRL, + RT5645_M_DAC2_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5645_HPOMIXL_CTRL, + RT5645_M_IN_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5645_HPOMIXL_CTRL, + RT5645_M_BST1_HV_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_hpvolr_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXR_CTRL, + RT5645_M_DAC1_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXR_CTRL, + RT5645_M_DAC2_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5645_HPOMIXR_CTRL, + RT5645_M_IN_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5645_HPOMIXR_CTRL, + RT5645_M_BST2_HV_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_LOUT_MIXER, + RT5645_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_LOUT_MIXER, + RT5645_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX L Switch", RT5645_LOUT_MIXER, + RT5645_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX R Switch", RT5645_LOUT_MIXER, + RT5645_M_OV_R_LM_SFT, 1, 1), +}; + +/*DAC1 L/R source*/ /* MX-29 [9:8] [11:10] */ +static const char * const rt5645_dac1_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac1l_enum, RT5645_AD_DA_MIXER, + RT5645_DAC1_L_SEL_SFT, rt5645_dac1_src); + +static const struct snd_kcontrol_new rt5645_dac1l_mux = + SOC_DAPM_ENUM("DAC1 L source", rt5645_dac1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac1r_enum, RT5645_AD_DA_MIXER, + RT5645_DAC1_R_SEL_SFT, rt5645_dac1_src); + +static const struct snd_kcontrol_new rt5645_dac1r_mux = + SOC_DAPM_ENUM("DAC1 R source", rt5645_dac1r_enum); + +/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */ +static const char * const rt5645_dac12_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "VAD_ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac2l_enum, RT5645_DAC_CTRL, + RT5645_DAC2_L_SEL_SFT, rt5645_dac12_src); + +static const struct snd_kcontrol_new rt5645_dac_l2_mux = + SOC_DAPM_ENUM("DAC2 L source", rt5645_dac2l_enum); + +static const char * const rt5645_dacr2_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "Haptic" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac2r_enum, RT5645_DAC_CTRL, + RT5645_DAC2_R_SEL_SFT, rt5645_dacr2_src); + +static const struct snd_kcontrol_new rt5645_dac_r2_mux = + SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum); + + +/* INL/R source */ +static const char * const rt5645_inl_src[] = { + "IN2P", "MonoP" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_inl_enum, RT5645_INL1_INR1_VOL, + RT5645_INL_SEL_SFT, rt5645_inl_src); + +static const struct snd_kcontrol_new rt5645_inl_mux = + SOC_DAPM_ENUM("INL source", rt5645_inl_enum); + +static const char * const rt5645_inr_src[] = { + "IN2N", "MonoN" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_inr_enum, RT5645_INL1_INR1_VOL, + RT5645_INR_SEL_SFT, rt5645_inr_src); + +static const struct snd_kcontrol_new rt5645_inr_mux = + SOC_DAPM_ENUM("INR source", rt5645_inr_enum); + +/* Stereo1 ADC source */ +/* MX-27 [12] */ +static const char * const rt5645_stereo_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_stereo1_adc1_enum, RT5645_STO1_ADC_MIXER, + RT5645_ADC_1_SRC_SFT, rt5645_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5645_sto_adc1_mux = + SOC_DAPM_ENUM("Stereo1 ADC1 Mux", rt5645_stereo1_adc1_enum); + +/* MX-27 [11] */ +static const char * const rt5645_stereo_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_stereo1_adc2_enum, RT5645_STO1_ADC_MIXER, + RT5645_ADC_2_SRC_SFT, rt5645_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5645_sto_adc2_mux = + SOC_DAPM_ENUM("Stereo1 ADC2 Mux", rt5645_stereo1_adc2_enum); + +/* MX-27 [8] */ +static const char * const rt5645_stereo_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_stereo1_dmic_enum, RT5645_STO1_ADC_MIXER, + RT5645_DMIC_SRC_SFT, rt5645_stereo_dmic_src); + +static const struct snd_kcontrol_new rt5645_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC source", rt5645_stereo1_dmic_enum); + +/* Mono ADC source */ +/* MX-28 [12] */ +static const char * const rt5645_mono_adc_l1_src[] = { + "Mono DAC MIXL", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_adc_l1_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_ADC_L1_SRC_SFT, rt5645_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5645_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC1 left source", rt5645_mono_adc_l1_enum); +/* MX-28 [11] */ +static const char * const rt5645_mono_adc_l2_src[] = { + "Mono DAC MIXL", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_adc_l2_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_ADC_L2_SRC_SFT, rt5645_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5645_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC2 left source", rt5645_mono_adc_l2_enum); + +/* MX-28 [8] */ +static const char * const rt5645_mono_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_dmic_l_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_DMIC_L_SRC_SFT, rt5645_mono_dmic_src); + +static const struct snd_kcontrol_new rt5645_mono_dmic_l_mux = + SOC_DAPM_ENUM("Mono DMIC left source", rt5645_mono_dmic_l_enum); +/* MX-28 [1:0] */ +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_dmic_r_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_DMIC_R_SRC_SFT, rt5645_mono_dmic_src); + +static const struct snd_kcontrol_new rt5645_mono_dmic_r_mux = + SOC_DAPM_ENUM("Mono DMIC Right source", rt5645_mono_dmic_r_enum); +/* MX-28 [4] */ +static const char * const rt5645_mono_adc_r1_src[] = { + "Mono DAC MIXR", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_adc_r1_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_ADC_R1_SRC_SFT, rt5645_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5645_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC1 right source", rt5645_mono_adc_r1_enum); +/* MX-28 [3] */ +static const char * const rt5645_mono_adc_r2_src[] = { + "Mono DAC MIXR", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_adc_r2_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_ADC_R2_SRC_SFT, rt5645_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC2 right source", rt5645_mono_adc_r2_enum); + +/* MX-77 [9:8] */ +static const char * const rt5645_if1_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "VAD_ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_if1_adc_in_enum, RT5645_TDM_CTRL_1, + RT5645_IF1_ADC_IN_SFT, rt5645_if1_adc_in_src); + +static const struct snd_kcontrol_new rt5645_if1_adc_in_mux = + SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum); + +/* MX-2F [13:12] */ +static const char * const rt5645_if2_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "VAD_ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_if2_adc_in_enum, RT5645_DIG_INF1_DATA, + RT5645_IF2_ADC_IN_SFT, rt5645_if2_adc_in_src); + +static const struct snd_kcontrol_new rt5645_if2_adc_in_mux = + SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum); + +/* MX-2F [1:0] */ +static const char * const rt5645_if3_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "VAD_ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_if3_adc_in_enum, RT5645_DIG_INF1_DATA, + RT5645_IF3_ADC_IN_SFT, rt5645_if3_adc_in_src); + +static const struct snd_kcontrol_new rt5645_if3_adc_in_mux = + SOC_DAPM_ENUM("IF3 ADC IN source", rt5645_if3_adc_in_enum); + +/* MX-31 [15] [13] [11] [9] */ +static const char * const rt5645_pdm_src[] = { + "Mono DAC", "Stereo DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_pdm1_l_enum, RT5645_PDM_OUT_CTRL, + RT5645_PDM1_L_SFT, rt5645_pdm_src); + +static const struct snd_kcontrol_new rt5645_pdm1_l_mux = + SOC_DAPM_ENUM("PDM1 L source", rt5645_pdm1_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5645_pdm1_r_enum, RT5645_PDM_OUT_CTRL, + RT5645_PDM1_R_SFT, rt5645_pdm_src); + +static const struct snd_kcontrol_new rt5645_pdm1_r_mux = + SOC_DAPM_ENUM("PDM1 R source", rt5645_pdm1_r_enum); + +/* MX-9D [9:8] */ +static const char * const rt5645_vad_adc_src[] = { + "Sto1 ADC L", "Mono ADC L", "Mono ADC R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_vad_adc_enum, RT5645_VAD_CTRL4, + RT5645_VAD_SEL_SFT, rt5645_vad_adc_src); + +static const struct snd_kcontrol_new rt5645_vad_adc_mux = + SOC_DAPM_ENUM("VAD ADC source", rt5645_vad_adc_enum); + +static const struct snd_kcontrol_new spk_l_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL, + RT5645_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new spk_r_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL, + RT5645_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hp_l_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL, + RT5645_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hp_r_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL, + RT5645_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new pdm1_l_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL, + RT5645_M_PDM1_L, 1, 1); + +static const struct snd_kcontrol_new pdm1_r_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL, + RT5645_M_PDM1_R, 1, 1); + +static void hp_amp_power(struct snd_soc_codec *codec, int on) +{ + static int hp_amp_power_count; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + if (on) { + if (hp_amp_power_count <= 0) { + /* depop parameters */ + snd_soc_update_bits(codec, RT5645_DEPOP_M2, + RT5645_DEPOP_MASK, RT5645_DEPOP_MAN); + snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_HP_DCC_INT1, 0x9f01); + mdelay(150); + /* headphone amp power on */ + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2 , 0); + snd_soc_update_bits(codec, RT5645_PWR_VOL, + RT5645_PWR_HV_L | RT5645_PWR_HV_R, + RT5645_PWR_HV_L | RT5645_PWR_HV_R); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA); + mdelay(5); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, + RT5645_PWR_FV1 | RT5645_PWR_FV2); + + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_CO_MASK | RT5645_HP_SG_MASK, + RT5645_HP_CO_EN | RT5645_HP_SG_EN); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x14, 0x1aaa); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x24, 0x0430); + } + hp_amp_power_count++; + } else { + hp_amp_power_count--; + if (hp_amp_power_count <= 0) { + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS | + RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS); + /* headphone amp power down */ + snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA, 0); + } + } +} + +static int rt5645_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + hp_amp_power(codec, 1); + /* headphone unmute sequence */ + snd_soc_update_bits(codec, RT5645_DEPOP_M3, RT5645_CP_FQ1_MASK | + RT5645_CP_FQ2_MASK | RT5645_CP_FQ3_MASK, + (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) | + (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) | + (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT)); + regmap_write(rt5645->regmap, + RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_RSTN_MASK, RT5645_RSTN_EN); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS | + RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN); + msleep(40); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS | + RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS); + break; + + case SND_SOC_DAPM_PRE_PMD: + /* headphone mute sequence */ + snd_soc_update_bits(codec, RT5645_DEPOP_M3, + RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK | + RT5645_CP_FQ3_MASK, + (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) | + (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) | + (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT)); + regmap_write(rt5645->regmap, + RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK, RT5645_HP_SG_EN); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_RSTP_MASK, RT5645_RSTP_EN); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS | + RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN); + msleep(30); + hp_amp_power(codec, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5645_PWR_DIG1, + RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | + RT5645_PWR_CLS_D_L, + RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | + RT5645_PWR_CLS_D_L); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5645_PWR_DIG1, + RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | + RT5645_PWR_CLS_D_L, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + hp_amp_power(codec, 1); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_LM, RT5645_PWR_LM); + snd_soc_update_bits(codec, RT5645_LOUT1, + RT5645_L_MUTE | RT5645_R_MUTE, 0); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5645_LOUT1, + RT5645_L_MUTE | RT5645_R_MUTE, + RT5645_L_MUTE | RT5645_R_MUTE); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_LM, 0); + hp_amp_power(codec, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_bst2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5645_PWR_ANLG2, + RT5645_PWR_BST2_P, RT5645_PWR_BST2_P); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5645_PWR_ANLG2, + RT5645_PWR_BST2_P, 0); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5645_PWR_MIXER, + RT5645_PWR_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5645_PWR_ANLG2, + RT5645_PWR_PLL_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("JD Power", RT5645_PWR_ANLG2, + RT5645_PWR_JD1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL, + RT5645_PWR_MIC_DET_BIT, 0, NULL, 0), + + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2, + RT5645_PWR_MB1_BIT, 0), + SND_SOC_DAPM_MICBIAS("micbias2", RT5645_PWR_ANLG2, + RT5645_PWR_MB2_BIT, 0), + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_INPUT("DMIC L2"), + SND_SOC_DAPM_INPUT("DMIC R2"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + + SND_SOC_DAPM_INPUT("Haptic Generator"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5645_DMIC_CTRL1, + RT5645_DMIC_1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5645_DMIC_CTRL1, + RT5645_DMIC_2_EN_SFT, 0, NULL, 0), + /* Boost */ + SND_SOC_DAPM_PGA("BST1", RT5645_PWR_ANLG2, + RT5645_PWR_BST1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("BST2", RT5645_PWR_ANLG2, + RT5645_PWR_BST2_BIT, 0, NULL, 0, rt5645_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5645_PWR_VOL, + RT5645_PWR_IN_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5645_PWR_VOL, + RT5645_PWR_IN_R_BIT, 0, NULL, 0), + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5645_PWR_MIXER, RT5645_PWR_RM_L_BIT, + 0, rt5645_rec_l_mix, ARRAY_SIZE(rt5645_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5645_PWR_MIXER, RT5645_PWR_RM_R_BIT, + 0, rt5645_rec_r_mix, ARRAY_SIZE(rt5645_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC L power", RT5645_PWR_DIG1, + RT5645_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC R power", RT5645_PWR_DIG1, + RT5645_PWR_ADC_R_BIT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto_adc2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto_adc2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto_adc1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto_adc1_mux), + SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_dmic_l_mux), + SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_dmic_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_adc_r2_mux), + /* ADC Mixer */ + + SND_SOC_DAPM_SUPPLY_S("adc stereo1 filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("adc stereo2 filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_ADC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_sto1_adc_l_mix, ARRAY_SIZE(rt5645_sto1_adc_l_mix), + NULL, 0), + SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_sto1_adc_r_mix, ARRAY_SIZE(rt5645_sto1_adc_r_mix), + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("adc mono left filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("Mono ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_mono_adc_l_mix, ARRAY_SIZE(rt5645_mono_adc_l_mix), + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("adc mono right filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("Mono ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_mono_adc_r_mix, ARRAY_SIZE(rt5645_mono_adc_r_mix), + NULL, 0), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("VAD_ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* IF1 2 Mux */ + SND_SOC_DAPM_MUX("IF1 ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc_in_mux), + SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if2_adc_in_mux), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5645_PWR_DIG1, + RT5645_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5645_PWR_DIG1, + RT5645_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5645_vad_adc_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5645_dac_l_mix, ARRAY_SIZE(rt5645_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5645_dac_r_mix, ARRAY_SIZE(rt5645_dac_r_mix)), + + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_r2_mux), + SND_SOC_DAPM_PGA("DAC L2 Volume", RT5645_PWR_DIG1, + RT5645_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC R2 Volume", RT5645_PWR_DIG1, + RT5645_PWR_DAC_R2_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DAC1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1l_mux), + SND_SOC_DAPM_MUX("DAC1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1r_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY_S("dac stereo1 filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("dac mono left filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_DAC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("dac mono right filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_DAC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_sto_dac_l_mix, ARRAY_SIZE(rt5645_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_sto_dac_r_mix, ARRAY_SIZE(rt5645_sto_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_mono_dac_l_mix, ARRAY_SIZE(rt5645_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_mono_dac_r_mix, ARRAY_SIZE(rt5645_mono_dac_r_mix)), + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_dig_l_mix, ARRAY_SIZE(rt5645_dig_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_dig_r_mix, ARRAY_SIZE(rt5645_dig_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L1_BIT, + 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L2_BIT, + 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R1_BIT, + 0), + SND_SOC_DAPM_DAC("DAC R2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R2_BIT, + 0), + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("SPK MIXL", RT5645_PWR_MIXER, RT5645_PWR_SM_L_BIT, + 0, rt5645_spk_l_mix, ARRAY_SIZE(rt5645_spk_l_mix)), + SND_SOC_DAPM_MIXER("SPK MIXR", RT5645_PWR_MIXER, RT5645_PWR_SM_R_BIT, + 0, rt5645_spk_r_mix, ARRAY_SIZE(rt5645_spk_r_mix)), + SND_SOC_DAPM_MIXER("OUT MIXL", RT5645_PWR_MIXER, RT5645_PWR_OM_L_BIT, + 0, rt5645_out_l_mix, ARRAY_SIZE(rt5645_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5645_PWR_MIXER, RT5645_PWR_OM_R_BIT, + 0, rt5645_out_r_mix, ARRAY_SIZE(rt5645_out_r_mix)), + /* Ouput Volume */ + SND_SOC_DAPM_SWITCH("SPKVOL L", RT5645_PWR_VOL, RT5645_PWR_SV_L_BIT, 0, + &spk_l_vol_control), + SND_SOC_DAPM_SWITCH("SPKVOL R", RT5645_PWR_VOL, RT5645_PWR_SV_R_BIT, 0, + &spk_r_vol_control), + SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5645_PWR_VOL, RT5645_PWR_HV_L_BIT, + 0, rt5645_hpvoll_mix, ARRAY_SIZE(rt5645_hpvoll_mix)), + SND_SOC_DAPM_MIXER("HPOVOL MIXR", RT5645_PWR_VOL, RT5645_PWR_HV_R_BIT, + 0, rt5645_hpvolr_mix, ARRAY_SIZE(rt5645_hpvolr_mix)), + SND_SOC_DAPM_SUPPLY("HPOVOL MIXL Power", RT5645_PWR_MIXER, + RT5645_PWR_HM_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HPOVOL MIXR Power", RT5645_PWR_MIXER, + RT5645_PWR_HM_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("HPOVOL L", SND_SOC_NOPM, 0, 0, &hp_l_vol_control), + SND_SOC_DAPM_SWITCH("HPOVOL R", SND_SOC_NOPM, 0, 0, &hp_r_vol_control), + + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_l_mix, + ARRAY_SIZE(rt5645_spo_l_mix)), + SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_r_mix, + ARRAY_SIZE(rt5645_spo_r_mix)), + SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, rt5645_hpo_mix, + ARRAY_SIZE(rt5645_hpo_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, rt5645_lout_mix, + ARRAY_SIZE(rt5645_lout_mix)), + + SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, rt5645_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, rt5645_lout_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("SPK amp", 2, SND_SOC_NOPM, 0, 0, rt5645_spk_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + + /* PDM */ + SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5645_PWR_DIG2, RT5645_PWR_PDM1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_MUX("PDM1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_l_mux), + SND_SOC_DAPM_MUX("PDM1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_r_mux), + + SND_SOC_DAPM_SWITCH("PDM1 L", SND_SOC_NOPM, 0, 0, &pdm1_l_vol_control), + SND_SOC_DAPM_SWITCH("PDM1 R", SND_SOC_NOPM, 0, 0, &pdm1_r_vol_control), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("PDM1L"), + SND_SOC_DAPM_OUTPUT("PDM1R"), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { + { "IN1P", NULL, "LDO2" }, + { "IN2P", NULL, "LDO2" }, + + { "DMIC1", NULL, "DMIC L1" }, + { "DMIC1", NULL, "DMIC R1" }, + { "DMIC2", NULL, "DMIC L2" }, + { "DMIC2", NULL, "DMIC R2" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST1", NULL, "JD Power" }, + { "BST1", NULL, "Mic Det Power" }, + { "BST2", NULL, "IN2P" }, + { "BST2", NULL, "IN2N" }, + + { "INL VOL", NULL, "IN2P" }, + { "INR VOL", NULL, "IN2N" }, + + { "RECMIXL", "HPOL Switch", "HPOL" }, + { "RECMIXL", "INL Switch", "INL VOL" }, + { "RECMIXL", "BST2 Switch", "BST2" }, + { "RECMIXL", "BST1 Switch", "BST1" }, + { "RECMIXL", "OUT MIXL Switch", "OUT MIXL" }, + + { "RECMIXR", "HPOR Switch", "HPOR" }, + { "RECMIXR", "INR Switch", "INR VOL" }, + { "RECMIXR", "BST2 Switch", "BST2" }, + { "RECMIXR", "BST1 Switch", "BST1" }, + { "RECMIXR", "OUT MIXR Switch", "OUT MIXR" }, + + { "ADC L", NULL, "RECMIXL" }, + { "ADC L", NULL, "ADC L power" }, + { "ADC R", NULL, "RECMIXR" }, + { "ADC R", NULL, "ADC R power" }, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC L2", NULL, "DMIC CLK"}, + {"DMIC L2", NULL, "DMIC2 Power"}, + {"DMIC R2", NULL, "DMIC CLK"}, + {"DMIC R2", NULL, "DMIC2 Power"}, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Mono DMIC L Mux", "DMIC1", "DMIC L1" }, + { "Mono DMIC L Mux", "DMIC2", "DMIC L2" }, + + { "Mono DMIC R Mux", "DMIC1", "DMIC R1" }, + { "Mono DMIC R Mux", "DMIC2", "DMIC R2" }, + + { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" }, + { "Stereo1 ADC L1 Mux", "ADC", "ADC L" }, + { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" }, + + { "Stereo1 ADC R1 Mux", "ADC", "ADC R" }, + { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" }, + { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" }, + + { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" }, + { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "ADC", "ADC L" }, + + { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + { "Mono ADC R1 Mux", "ADC", "ADC R" }, + { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" }, + { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + + { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" }, + { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" }, + { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" }, + { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" }, + { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" }, + { "Mono ADC MIXL", NULL, "adc mono left filter" }, + { "adc mono left filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" }, + { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" }, + { "Mono ADC MIXR", NULL, "adc mono right filter" }, + { "adc mono right filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "VAD ADC Mux", "Sto1 ADC L", "Stereo1 ADC MIXL" }, + { "VAD ADC Mux", "Mono ADC L", "Mono ADC MIXL" }, + { "VAD ADC Mux", "Mono ADC R", "Mono ADC MIXR" }, + + { "IF_ADC1", NULL, "Stereo1 ADC MIXL" }, + { "IF_ADC1", NULL, "Stereo1 ADC MIXR" }, + { "IF_ADC2", NULL, "Mono ADC MIXL" }, + { "IF_ADC2", NULL, "Mono ADC MIXR" }, + { "VAD_ADC", NULL, "VAD ADC Mux" }, + + { "IF1 ADC Mux", "IF_ADC1", "IF_ADC1" }, + { "IF1 ADC Mux", "IF_ADC2", "IF_ADC2" }, + { "IF1 ADC Mux", "VAD_ADC", "VAD_ADC" }, + + { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" }, + { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" }, + { "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" }, + + { "IF1 ADC", NULL, "I2S1" }, + { "IF1 ADC", NULL, "IF1 ADC Mux" }, + { "IF2 ADC", NULL, "I2S2" }, + { "IF2 ADC", NULL, "IF2 ADC Mux" }, + + { "AIF1TX", NULL, "IF1 ADC" }, + { "AIF1TX", NULL, "IF2 ADC" }, + { "AIF2TX", NULL, "IF2 ADC" }, + + { "IF1 DAC1", NULL, "AIF1RX" }, + { "IF1 DAC2", NULL, "AIF1RX" }, + { "IF2 DAC", NULL, "AIF2RX" }, + + { "IF1 DAC1", NULL, "I2S1" }, + { "IF1 DAC2", NULL, "I2S1" }, + { "IF2 DAC", NULL, "I2S2" }, + + { "IF1 DAC2 L", NULL, "IF1 DAC2" }, + { "IF1 DAC2 R", NULL, "IF1 DAC2" }, + { "IF1 DAC1 L", NULL, "IF1 DAC1" }, + { "IF1 DAC1 R", NULL, "IF1 DAC1" }, + { "IF2 DAC L", NULL, "IF2 DAC" }, + { "IF2 DAC R", NULL, "IF2 DAC" }, + + { "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" }, + { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" }, + + { "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" }, + { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" }, + + { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" }, + { "DAC1 MIXL", "DAC1 Switch", "DAC1 L Mux" }, + { "DAC1 MIXL", NULL, "dac stereo1 filter" }, + { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" }, + { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" }, + { "DAC1 MIXR", NULL, "dac stereo1 filter" }, + + { "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" }, + { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" }, + { "DAC L2 Mux", "Mono ADC", "Mono ADC MIXL" }, + { "DAC L2 Mux", "VAD_ADC", "VAD_ADC" }, + { "DAC L2 Volume", NULL, "DAC L2 Mux" }, + { "DAC L2 Volume", NULL, "dac mono left filter" }, + + { "DAC R2 Mux", "IF1 DAC", "IF1 DAC2 R" }, + { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" }, + { "DAC R2 Mux", "Mono ADC", "Mono ADC MIXR" }, + { "DAC R2 Mux", "Haptic", "Haptic Generator" }, + { "DAC R2 Volume", NULL, "DAC R2 Mux" }, + { "DAC R2 Volume", NULL, "dac mono right filter" }, + + { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "Stereo DAC MIXL", NULL, "dac stereo1 filter" }, + { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "Stereo DAC MIXR", NULL, "dac stereo1 filter" }, + + { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" }, + { "Mono DAC MIXL", NULL, "dac mono left filter" }, + { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" }, + { "Mono DAC MIXR", NULL, "dac mono right filter" }, + + { "DAC MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, + { "DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" }, + { "DAC MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, + { "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" }, + + { "DAC L1", NULL, "Stereo DAC MIXL" }, + { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC R1", NULL, "Stereo DAC MIXR" }, + { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC L2", NULL, "Mono DAC MIXL" }, + { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC R2", NULL, "Mono DAC MIXR" }, + { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll }, + + { "SPK MIXL", "BST1 Switch", "BST1" }, + { "SPK MIXL", "INL Switch", "INL VOL" }, + { "SPK MIXL", "DAC L1 Switch", "DAC L1" }, + { "SPK MIXL", "DAC L2 Switch", "DAC L2" }, + { "SPK MIXR", "BST2 Switch", "BST2" }, + { "SPK MIXR", "INR Switch", "INR VOL" }, + { "SPK MIXR", "DAC R1 Switch", "DAC R1" }, + { "SPK MIXR", "DAC R2 Switch", "DAC R2" }, + + { "OUT MIXL", "BST1 Switch", "BST1" }, + { "OUT MIXL", "INL Switch", "INL VOL" }, + { "OUT MIXL", "DAC L2 Switch", "DAC L2" }, + { "OUT MIXL", "DAC L1 Switch", "DAC L1" }, + + { "OUT MIXR", "BST2 Switch", "BST2" }, + { "OUT MIXR", "INR Switch", "INR VOL" }, + { "OUT MIXR", "DAC R2 Switch", "DAC R2" }, + { "OUT MIXR", "DAC R1 Switch", "DAC R1" }, + + { "HPOVOL MIXL", "DAC1 Switch", "DAC L1" }, + { "HPOVOL MIXL", "DAC2 Switch", "DAC L2" }, + { "HPOVOL MIXL", "INL Switch", "INL VOL" }, + { "HPOVOL MIXL", "BST1 Switch", "BST1" }, + { "HPOVOL MIXL", NULL, "HPOVOL MIXL Power" }, + { "HPOVOL MIXR", "DAC1 Switch", "DAC R1" }, + { "HPOVOL MIXR", "DAC2 Switch", "DAC R2" }, + { "HPOVOL MIXR", "INR Switch", "INR VOL" }, + { "HPOVOL MIXR", "BST2 Switch", "BST2" }, + { "HPOVOL MIXR", NULL, "HPOVOL MIXR Power" }, + + { "DAC 2", NULL, "DAC L2" }, + { "DAC 2", NULL, "DAC R2" }, + { "DAC 1", NULL, "DAC L1" }, + { "DAC 1", NULL, "DAC R1" }, + { "HPOVOL L", "Switch", "HPOVOL MIXL" }, + { "HPOVOL R", "Switch", "HPOVOL MIXR" }, + { "HPOVOL", NULL, "HPOVOL L" }, + { "HPOVOL", NULL, "HPOVOL R" }, + { "HPO MIX", "DAC1 Switch", "DAC 1" }, + { "HPO MIX", "HPVOL Switch", "HPOVOL" }, + + { "SPKVOL L", "Switch", "SPK MIXL" }, + { "SPKVOL R", "Switch", "SPK MIXR" }, + + { "SPOL MIX", "DAC R1 Switch", "DAC R1" }, + { "SPOL MIX", "DAC L1 Switch", "DAC L1" }, + { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" }, + { "SPOL MIX", "SPKVOL L Switch", "SPKVOL L" }, + { "SPOR MIX", "DAC R1 Switch", "DAC R1" }, + { "SPOR MIX", "SPKVOL R Switch", "SPKVOL R" }, + + { "LOUT MIX", "DAC L1 Switch", "DAC L1" }, + { "LOUT MIX", "DAC R1 Switch", "DAC R1" }, + { "LOUT MIX", "OUTMIX L Switch", "OUT MIXL" }, + { "LOUT MIX", "OUTMIX R Switch", "OUT MIXR" }, + + { "PDM1 L Mux", "Stereo DAC", "Stereo DAC MIXL" }, + { "PDM1 L Mux", "Mono DAC", "Mono DAC MIXL" }, + { "PDM1 L Mux", NULL, "PDM1 Power" }, + { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, + { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" }, + { "PDM1 R Mux", NULL, "PDM1 Power" }, + + { "HP amp", NULL, "HPO MIX" }, + { "HP amp", NULL, "JD Power" }, + { "HP amp", NULL, "Mic Det Power" }, + { "HP amp", NULL, "LDO2" }, + { "HPOL", NULL, "HP amp" }, + { "HPOR", NULL, "HP amp" }, + + { "LOUT amp", NULL, "LOUT MIX" }, + { "LOUTL", NULL, "LOUT amp" }, + { "LOUTR", NULL, "LOUT amp" }, + + { "PDM1 L", "Switch", "PDM1 L Mux" }, + { "PDM1 R", "Switch", "PDM1 R Mux" }, + + { "PDM1L", NULL, "PDM1 L" }, + { "PDM1R", NULL, "PDM1 R" }, + + { "SPK amp", NULL, "SPOL MIX" }, + { "SPK amp", NULL, "SPOR MIX" }, + { "SPOL", NULL, "SPK amp" }, + { "SPOR", NULL, "SPK amp" }, +}; + +static int get_clk_info(int sclk, int rate) +{ + int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt5645_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5645->lrck[dai->id] = params_rate(params); + pre_div = get_clk_info(rt5645->sysclk, rt5645->lrck[dai->id]); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + bclk_ms = frame_size > 32; + rt5645->bclk[dai->id] = rt5645->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5645->bclk[dai->id], rt5645->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5645_I2S_DL_20; + break; + case 24: + val_len |= RT5645_I2S_DL_24; + break; + case 8: + val_len |= RT5645_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5645_AIF1: + mask_clk = RT5645_I2S_BCLK_MS1_MASK | RT5645_I2S_PD1_MASK; + val_clk = bclk_ms << RT5645_I2S_BCLK_MS1_SFT | + pre_div << RT5645_I2S_PD1_SFT; + snd_soc_update_bits(codec, RT5645_I2S1_SDP, + RT5645_I2S_DL_MASK, val_len); + snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk); + break; + case RT5645_AIF2: + mask_clk = RT5645_I2S_BCLK_MS2_MASK | RT5645_I2S_PD2_MASK; + val_clk = bclk_ms << RT5645_I2S_BCLK_MS2_SFT | + pre_div << RT5645_I2S_PD2_SFT; + snd_soc_update_bits(codec, RT5645_I2S2_SDP, + RT5645_I2S_DL_MASK, val_len); + snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk); + break; + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5645->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5645_I2S_MS_S; + rt5645->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5645_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5645_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5645_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5645_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + switch (dai->id) { + case RT5645_AIF1: + snd_soc_update_bits(codec, RT5645_I2S1_SDP, + RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK | + RT5645_I2S_DF_MASK, reg_val); + break; + case RT5645_AIF2: + snd_soc_update_bits(codec, RT5645_I2S1_SDP, + RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK | + RT5645_I2S_DF_MASK, reg_val); + break; + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5645->sysclk && clk_id == rt5645->sysclk_src) + return 0; + + switch (clk_id) { + case RT5645_SCLK_S_MCLK: + reg_val |= RT5645_SCLK_SRC_MCLK; + break; + case RT5645_SCLK_S_PLL1: + reg_val |= RT5645_SCLK_SRC_PLL1; + break; + case RT5645_SCLK_S_RCCLK: + reg_val |= RT5645_SCLK_SRC_RCCLK; + break; + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_update_bits(codec, RT5645_GLB_CLK, + RT5645_SCLK_SRC_MASK, reg_val); + rt5645->sysclk = freq; + rt5645->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +/** + * rt5645_pll_calc - Calcualte PLL M/N/K code. + * @freq_in: external clock provided to codec. + * @freq_out: target clock which codec works on. + * @pll_code: Pointer to structure with M, N, K and bypass flag. + * + * Calcualte M/N/K code to configure PLL for codec. And K is assigned to 2 + * which make calculation more efficiently. + * + * Returns 0 for success or negative error code. + */ +static int rt5645_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rt5645_pll_code *pll_code) +{ + int max_n = RT5645_PLL_N_MAX, max_m = RT5645_PLL_M_MAX; + int k, n = 0, m = 0, red, n_t, m_t, pll_out, in_t, out_t; + int red_t = abs(freq_out - freq_in); + bool bypass = false; + + if (RT5645_PLL_INP_MAX < freq_in || RT5645_PLL_INP_MIN > freq_in) + return -EINVAL; + + k = 100000000 / freq_out - 2; + if (k > RT5645_PLL_K_MAX) + k = RT5645_PLL_K_MAX; + for (n_t = 0; n_t <= max_n; n_t++) { + in_t = freq_in / (k + 2); + pll_out = freq_out / (n_t + 2); + if (in_t < 0) + continue; + if (in_t == pll_out) { + bypass = true; + n = n_t; + goto code_find; + } + red = abs(in_t - pll_out); + if (red < red_t) { + bypass = true; + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + for (m_t = 0; m_t <= max_m; m_t++) { + out_t = in_t / (m_t + 2); + red = abs(out_t - pll_out); + if (red < red_t) { + bypass = false; + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + } + } + pr_debug("Only get approximation about PLL\n"); + +code_find: + + pll_code->m_bp = bypass; + pll_code->m_code = m; + pll_code->n_code = n; + pll_code->k_code = k; + return 0; +} + +static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + struct rt5645_pll_code pll_code; + int ret; + + if (source == rt5645->pll_src && freq_in == rt5645->pll_in && + freq_out == rt5645->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5645->pll_in = 0; + rt5645->pll_out = 0; + snd_soc_update_bits(codec, RT5645_GLB_CLK, + RT5645_SCLK_SRC_MASK, RT5645_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5645_PLL1_S_MCLK: + snd_soc_update_bits(codec, RT5645_GLB_CLK, + RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_MCLK); + break; + case RT5645_PLL1_S_BCLK1: + case RT5645_PLL1_S_BCLK2: + switch (dai->id) { + case RT5645_AIF1: + snd_soc_update_bits(codec, RT5645_GLB_CLK, + RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK1); + break; + case RT5645_AIF2: + snd_soc_update_bits(codec, RT5645_GLB_CLK, + RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK2); + break; + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rt5645_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_write(codec, RT5645_PLL_CTRL1, + pll_code.n_code << RT5645_PLL_N_SFT | pll_code.k_code); + snd_soc_write(codec, RT5645_PLL_CTRL2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5645_PLL_M_SFT | + pll_code.m_bp << RT5645_PLL_M_BP_SFT); + + rt5645->pll_in = freq_in; + rt5645->pll_out = freq_out; + rt5645->pll_src = source; + + return 0; +} + +static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= (1 << 14); + + switch (slots) { + case 4: + val |= (1 << 12); + break; + case 6: + val |= (2 << 12); + break; + case 8: + val |= (3 << 12); + break; + case 2: + default: + break; + } + + switch (slot_width) { + case 20: + val |= (1 << 10); + break; + case 24: + val |= (2 << 10); + break; + case 32: + val |= (3 << 10); + break; + case 16: + default: + break; + } + + snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, 0x7c00, val); + + return 0; +} + +static int rt5645_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_STANDBY: + if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) { + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2); + mdelay(10); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, + RT5645_PWR_FV1 | RT5645_PWR_FV2); + snd_soc_update_bits(codec, RT5645_GEN_CTRL1, + RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100); + snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128); + snd_soc_write(codec, RT5645_PWR_DIG1, 0x0000); + snd_soc_write(codec, RT5645_PWR_DIG2, 0x0000); + snd_soc_write(codec, RT5645_PWR_VOL, 0x0000); + snd_soc_write(codec, RT5645_PWR_MIXER, 0x0000); + snd_soc_write(codec, RT5645_PWR_ANLG1, 0x0000); + snd_soc_write(codec, RT5645_PWR_ANLG2, 0x0000); + break; + + default: + break; + } + codec->dapm.bias_level = level; + + return 0; +} + +static int rt5645_probe(struct snd_soc_codec *codec) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + rt5645->codec = codec; + + rt5645_set_bias_level(codec, SND_SOC_BIAS_OFF); + + snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200); + snd_soc_write(codec, RT5645_PR_BASE + 0x1c, 0xfd20); + snd_soc_write(codec, RT5645_PR_BASE + 0x20, 0x611f); + snd_soc_write(codec, RT5645_PR_BASE + 0x21, 0x4040); + snd_soc_write(codec, RT5645_PR_BASE + 0x23, 0x0004); + + return 0; +} + +static int rt5645_remove(struct snd_soc_codec *codec) +{ + rt5645_reset(codec); + return 0; +} + +#ifdef CONFIG_PM +static int rt5645_suspend(struct snd_soc_codec *codec) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5645->regmap, true); + regcache_mark_dirty(rt5645->regmap); + + return 0; +} + +static int rt5645_resume(struct snd_soc_codec *codec) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5645->regmap, false); + snd_soc_cache_sync(codec); + + return 0; +} +#else +#define rt5645_suspend NULL +#define rt5645_resume NULL +#endif + +#define RT5645_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5645_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt5645_aif_dai_ops = { + .hw_params = rt5645_hw_params, + .set_fmt = rt5645_set_dai_fmt, + .set_sysclk = rt5645_set_dai_sysclk, + .set_tdm_slot = rt5645_set_tdm_slot, + .set_pll = rt5645_set_dai_pll, +}; + +struct snd_soc_dai_driver rt5645_dai[] = { + { + .name = "rt5645-aif1", + .id = RT5645_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5645_STEREO_RATES, + .formats = RT5645_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5645_STEREO_RATES, + .formats = RT5645_FORMATS, + }, + .ops = &rt5645_aif_dai_ops, + }, + { + .name = "rt5645-aif2", + .id = RT5645_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5645_STEREO_RATES, + .formats = RT5645_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5645_STEREO_RATES, + .formats = RT5645_FORMATS, + }, + .ops = &rt5645_aif_dai_ops, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5645 = { + .probe = rt5645_probe, + .remove = rt5645_remove, + .suspend = rt5645_suspend, + .resume = rt5645_resume, + .set_bias_level = rt5645_set_bias_level, + .idle_bias_off = true, + .controls = rt5645_snd_controls, + .num_controls = ARRAY_SIZE(rt5645_snd_controls), + .dapm_widgets = rt5645_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets), + .dapm_routes = rt5645_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes), +}; + +static const struct regmap_config rt5645_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) * + RT5645_PR_SPACING), + .volatile_reg = rt5645_volatile_register, + .readable_reg = rt5645_readable_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5645_reg, + .num_reg_defaults = ARRAY_SIZE(rt5645_reg), + .ranges = rt5645_ranges, + .num_ranges = ARRAY_SIZE(rt5645_ranges), +}; + +static const struct i2c_device_id rt5645_i2c_id[] = { + { "rt5645", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id); + +static int rt5645_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5645_priv *rt5645; + int ret; + unsigned int val; + + rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv), + GFP_KERNEL); + if (rt5645 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5645); + + if (pdata) + rt5645->pdata = *pdata; + + rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap); + if (IS_ERR(rt5645->regmap)) { + ret = PTR_ERR(rt5645->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val); + if (val != RT5645_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5645\n", val); + return -ENODEV; + } + + regmap_write(rt5645->regmap, RT5645_RESET, 0); + + ret = regmap_register_patch(rt5645->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + if (rt5645->pdata.in2_diff) + regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL, + RT5645_IN_DF2, RT5645_IN_DF2); + + if (rt5645->pdata.dmic_en) { + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP2_PIN_MASK, RT5645_GP2_PIN_DMIC1_SCL); + + switch (rt5645->pdata.dmic1_data_pin) { + case RT5645_DMIC_DATA_IN2N: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N); + break; + + case RT5645_DMIC_DATA_GPIO5: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA); + break; + + case RT5645_DMIC_DATA_GPIO11: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP11_PIN_MASK, + RT5645_GP11_PIN_DMIC1_SDA); + break; + + default: + break; + } + + switch (rt5645->pdata.dmic2_data_pin) { + case RT5645_DMIC_DATA_IN2P: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P); + break; + + case RT5645_DMIC_DATA_GPIO6: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA); + break; + + case RT5645_DMIC_DATA_GPIO10: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP10_PIN_MASK, + RT5645_GP10_PIN_DMIC2_SDA); + break; + + case RT5645_DMIC_DATA_GPIO12: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_2_DP_GPIO12); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP12_PIN_MASK, + RT5645_GP12_PIN_DMIC2_SDA); + break; + + default: + break; + } + + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, + rt5645_dai, ARRAY_SIZE(rt5645_dai)); + if (ret < 0) + goto err; + + return 0; +err: + return ret; +} + +static int rt5645_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +struct i2c_driver rt5645_i2c_driver = { + .driver = { + .name = "rt5645", + .owner = THIS_MODULE, + }, + .probe = rt5645_i2c_probe, + .remove = rt5645_i2c_remove, + .id_table = rt5645_i2c_id, +}; +module_i2c_driver(rt5645_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5645 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h new file mode 100644 index 000000000000..345aa3f5d14f --- /dev/null +++ b/sound/soc/codecs/rt5645.h @@ -0,0 +1,2188 @@ +/* + * rt5645.h -- RT5645 ALSA SoC audio driver + * + * Copyright 2013 Realtek Microelectronics + * Author: Bard Liao + * + * 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 __RT5645_H__ +#define __RT5645_H__ + +#include + +/* Info */ +#define RT5645_RESET 0x00 +#define RT5645_VENDOR_ID 0xfd +#define RT5645_VENDOR_ID1 0xfe +#define RT5645_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5645_SPK_VOL 0x01 +#define RT5645_HP_VOL 0x02 +#define RT5645_LOUT1 0x03 +#define RT5645_LOUT_CTRL 0x05 +/* I/O - Input */ +#define RT5645_IN1_CTRL1 0x0a +#define RT5645_IN1_CTRL2 0x0b +#define RT5645_IN1_CTRL3 0x0c +#define RT5645_IN2_CTRL 0x0d +#define RT5645_INL1_INR1_VOL 0x0f +#define RT5645_SPK_FUNC_LIM 0x14 +#define RT5645_ADJ_HPF_CTRL 0x16 +/* I/O - ADC/DAC/DMIC */ +#define RT5645_DAC1_DIG_VOL 0x19 +#define RT5645_DAC2_DIG_VOL 0x1a +#define RT5645_DAC_CTRL 0x1b +#define RT5645_STO1_ADC_DIG_VOL 0x1c +#define RT5645_MONO_ADC_DIG_VOL 0x1d +#define RT5645_ADC_BST_VOL1 0x1e +/* Mixer - D-D */ +#define RT5645_ADC_BST_VOL2 0x20 +#define RT5645_STO1_ADC_MIXER 0x27 +#define RT5645_MONO_ADC_MIXER 0x28 +#define RT5645_AD_DA_MIXER 0x29 +#define RT5645_STO_DAC_MIXER 0x2a +#define RT5645_MONO_DAC_MIXER 0x2b +#define RT5645_DIG_MIXER 0x2c +#define RT5645_DIG_INF1_DATA 0x2f +/* Mixer - PDM */ +#define RT5645_PDM_OUT_CTRL 0x31 +/* Mixer - ADC */ +#define RT5645_REC_L1_MIXER 0x3b +#define RT5645_REC_L2_MIXER 0x3c +#define RT5645_REC_R1_MIXER 0x3d +#define RT5645_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5645_HPMIXL_CTRL 0x3f +#define RT5645_HPOMIXL_CTRL 0x40 +#define RT5645_HPMIXR_CTRL 0x41 +#define RT5645_HPOMIXR_CTRL 0x42 +#define RT5645_HPO_MIXER 0x45 +#define RT5645_SPK_L_MIXER 0x46 +#define RT5645_SPK_R_MIXER 0x47 +#define RT5645_SPO_MIXER 0x48 +#define RT5645_SPO_CLSD_RATIO 0x4a +#define RT5645_OUT_L_GAIN1 0x4d +#define RT5645_OUT_L_GAIN2 0x4e +#define RT5645_OUT_L1_MIXER 0x4f +#define RT5645_OUT_R_GAIN1 0x50 +#define RT5645_OUT_R_GAIN2 0x51 +#define RT5645_OUT_R1_MIXER 0x52 +#define RT5645_LOUT_MIXER 0x53 +/* Haptic */ +#define RT5645_HAPTIC_CTRL1 0x56 +#define RT5645_HAPTIC_CTRL2 0x57 +#define RT5645_HAPTIC_CTRL3 0x58 +#define RT5645_HAPTIC_CTRL4 0x59 +#define RT5645_HAPTIC_CTRL5 0x5a +#define RT5645_HAPTIC_CTRL6 0x5b +#define RT5645_HAPTIC_CTRL7 0x5c +#define RT5645_HAPTIC_CTRL8 0x5d +#define RT5645_HAPTIC_CTRL9 0x5e +#define RT5645_HAPTIC_CTRL10 0x5f +/* Power */ +#define RT5645_PWR_DIG1 0x61 +#define RT5645_PWR_DIG2 0x62 +#define RT5645_PWR_ANLG1 0x63 +#define RT5645_PWR_ANLG2 0x64 +#define RT5645_PWR_MIXER 0x65 +#define RT5645_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5645_PRIV_INDEX 0x6a +#define RT5645_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5645_I2S1_SDP 0x70 +#define RT5645_I2S2_SDP 0x71 +#define RT5645_ADDA_CLK1 0x73 +#define RT5645_ADDA_CLK2 0x74 +#define RT5645_DMIC_CTRL1 0x75 +#define RT5645_DMIC_CTRL2 0x76 +/* Format - TDM Control */ +#define RT5645_TDM_CTRL_1 0x77 +#define RT5645_TDM_CTRL_2 0x78 +#define RT5645_TDM_CTRL_3 0x79 + +/* Function - Analog */ +#define RT5645_GLB_CLK 0x80 +#define RT5645_PLL_CTRL1 0x81 +#define RT5645_PLL_CTRL2 0x82 +#define RT5645_ASRC_1 0x83 +#define RT5645_ASRC_2 0x84 +#define RT5645_ASRC_3 0x85 +#define RT5645_ASRC_4 0x8a +#define RT5645_DEPOP_M1 0x8e +#define RT5645_DEPOP_M2 0x8f +#define RT5645_DEPOP_M3 0x90 +#define RT5645_CHARGE_PUMP 0x91 +#define RT5645_MICBIAS 0x93 +#define RT5645_A_JD_CTRL1 0x94 +#define RT5645_VAD_CTRL4 0x9d +#define RT5645_CLSD_OUT_CTRL 0xa0 +/* Function - Digital */ +#define RT5645_ADC_EQ_CTRL1 0xae +#define RT5645_ADC_EQ_CTRL2 0xaf +#define RT5645_EQ_CTRL1 0xb0 +#define RT5645_EQ_CTRL2 0xb1 +#define RT5645_ALC_CTRL_1 0xb3 +#define RT5645_ALC_CTRL_2 0xb4 +#define RT5645_ALC_CTRL_3 0xb5 +#define RT5645_ALC_CTRL_4 0xb6 +#define RT5645_ALC_CTRL_5 0xb7 +#define RT5645_JD_CTRL 0xbb +#define RT5645_IRQ_CTRL1 0xbc +#define RT5645_IRQ_CTRL2 0xbd +#define RT5645_IRQ_CTRL3 0xbe +#define RT5645_INT_IRQ_ST 0xbf +#define RT5645_GPIO_CTRL1 0xc0 +#define RT5645_GPIO_CTRL2 0xc1 +#define RT5645_GPIO_CTRL3 0xc2 +#define RT5645_BASS_BACK 0xcf +#define RT5645_MP3_PLUS1 0xd0 +#define RT5645_MP3_PLUS2 0xd1 +#define RT5645_ADJ_HPF1 0xd3 +#define RT5645_ADJ_HPF2 0xd4 +#define RT5645_HP_CALIB_AMP_DET 0xd6 +#define RT5645_SV_ZCD1 0xd9 +#define RT5645_SV_ZCD2 0xda +#define RT5645_IL_CMD 0xdb +#define RT5645_IL_CMD2 0xdc +#define RT5645_IL_CMD3 0xdd +#define RT5645_DRC1_HL_CTRL1 0xe7 +#define RT5645_DRC2_HL_CTRL1 0xe9 +#define RT5645_MUTI_DRC_CTRL1 0xea +#define RT5645_ADC_MONO_HP_CTRL1 0xec +#define RT5645_ADC_MONO_HP_CTRL2 0xed +#define RT5645_DRC2_CTRL1 0xf0 +#define RT5645_DRC2_CTRL2 0xf1 +#define RT5645_DRC2_CTRL3 0xf2 +#define RT5645_DRC2_CTRL4 0xf3 +#define RT5645_DRC2_CTRL5 0xf4 +#define RT5645_JD_CTRL3 0xf8 +#define RT5645_JD_CTRL4 0xf9 +/* General Control */ +#define RT5645_GEN_CTRL1 0xfa +#define RT5645_GEN_CTRL2 0xfb +#define RT5645_GEN_CTRL3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5645_DIG_VOL 0x00 +#define RT5645_PR_ALC_CTRL_1 0x01 +#define RT5645_PR_ALC_CTRL_2 0x02 +#define RT5645_PR_ALC_CTRL_3 0x03 +#define RT5645_PR_ALC_CTRL_4 0x04 +#define RT5645_PR_ALC_CTRL_5 0x05 +#define RT5645_PR_ALC_CTRL_6 0x06 +#define RT5645_BIAS_CUR1 0x12 +#define RT5645_BIAS_CUR3 0x14 +#define RT5645_CLSD_INT_REG1 0x1c +#define RT5645_MAMP_INT_REG2 0x37 +#define RT5645_CHOP_DAC_ADC 0x3d +#define RT5645_MIXER_INT_REG 0x3f +#define RT5645_3D_SPK 0x63 +#define RT5645_WND_1 0x6c +#define RT5645_WND_2 0x6d +#define RT5645_WND_3 0x6e +#define RT5645_WND_4 0x6f +#define RT5645_WND_5 0x70 +#define RT5645_WND_8 0x73 +#define RT5645_DIP_SPK_INF 0x75 +#define RT5645_HP_DCC_INT1 0x77 +#define RT5645_EQ_BW_LOP 0xa0 +#define RT5645_EQ_GN_LOP 0xa1 +#define RT5645_EQ_FC_BP1 0xa2 +#define RT5645_EQ_BW_BP1 0xa3 +#define RT5645_EQ_GN_BP1 0xa4 +#define RT5645_EQ_FC_BP2 0xa5 +#define RT5645_EQ_BW_BP2 0xa6 +#define RT5645_EQ_GN_BP2 0xa7 +#define RT5645_EQ_FC_BP3 0xa8 +#define RT5645_EQ_BW_BP3 0xa9 +#define RT5645_EQ_GN_BP3 0xaa +#define RT5645_EQ_FC_BP4 0xab +#define RT5645_EQ_BW_BP4 0xac +#define RT5645_EQ_GN_BP4 0xad +#define RT5645_EQ_FC_HIP1 0xae +#define RT5645_EQ_GN_HIP1 0xaf +#define RT5645_EQ_FC_HIP2 0xb0 +#define RT5645_EQ_BW_HIP2 0xb1 +#define RT5645_EQ_GN_HIP2 0xb2 +#define RT5645_EQ_PRE_VOL 0xb3 +#define RT5645_EQ_PST_VOL 0xb4 + + +/* global definition */ +#define RT5645_L_MUTE (0x1 << 15) +#define RT5645_L_MUTE_SFT 15 +#define RT5645_VOL_L_MUTE (0x1 << 14) +#define RT5645_VOL_L_SFT 14 +#define RT5645_R_MUTE (0x1 << 7) +#define RT5645_R_MUTE_SFT 7 +#define RT5645_VOL_R_MUTE (0x1 << 6) +#define RT5645_VOL_R_SFT 6 +#define RT5645_L_VOL_MASK (0x3f << 8) +#define RT5645_L_VOL_SFT 8 +#define RT5645_R_VOL_MASK (0x3f) +#define RT5645_R_VOL_SFT 0 + +/* IN1 Control 1 (0x0a) */ +#define RT5645_CBJ_BST1_MASK (0xf << 12) +#define RT5645_CBJ_BST1_SFT (12) +#define RT5645_CBJ_JD_HP_EN (0x1 << 9) +#define RT5645_CBJ_JD_MIC_EN (0x1 << 8) +#define RT5645_CBJ_JD_MIC_SW_EN (0x1 << 7) +#define RT5645_CBJ_MIC_SEL_R (0x1 << 6) +#define RT5645_CBJ_MIC_SEL_L (0x1 << 5) +#define RT5645_CBJ_MIC_SW (0x1 << 4) +#define RT5645_CBJ_BST1_EN (0x1 << 2) + +/* IN1 Control 2 (0x0b) */ +#define RT5645_CBJ_MN_JD (0x1 << 12) +#define RT5645_CAPLESS_EN (0x1 << 11) +#define RT5645_CBJ_DET_MODE (0x1 << 7) + +/* IN1 Control 3 (0x0c) */ +#define RT5645_CBJ_TIE_G_L (0x1 << 15) +#define RT5645_CBJ_TIE_G_R (0x1 << 14) + +/* IN2 Control (0x0d) */ +#define RT5645_BST_MASK1 (0xf<<12) +#define RT5645_BST_SFT1 12 +#define RT5645_BST_MASK2 (0xf<<8) +#define RT5645_BST_SFT2 8 +#define RT5645_IN_DF2 (0x1 << 6) +#define RT5645_IN_SFT2 6 + +/* INL and INR Volume Control (0x0f) */ +#define RT5645_INL_SEL_MASK (0x1 << 15) +#define RT5645_INL_SEL_SFT 15 +#define RT5645_INL_SEL_IN4P (0x0 << 15) +#define RT5645_INL_SEL_MONOP (0x1 << 15) +#define RT5645_INL_VOL_MASK (0x1f << 8) +#define RT5645_INL_VOL_SFT 8 +#define RT5645_INR_SEL_MASK (0x1 << 7) +#define RT5645_INR_SEL_SFT 7 +#define RT5645_INR_SEL_IN4N (0x0 << 7) +#define RT5645_INR_SEL_MONON (0x1 << 7) +#define RT5645_INR_VOL_MASK (0x1f) +#define RT5645_INR_VOL_SFT 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5645_DAC_L1_VOL_MASK (0xff << 8) +#define RT5645_DAC_L1_VOL_SFT 8 +#define RT5645_DAC_R1_VOL_MASK (0xff) +#define RT5645_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5645_DAC_L2_VOL_MASK (0xff << 8) +#define RT5645_DAC_L2_VOL_SFT 8 +#define RT5645_DAC_R2_VOL_MASK (0xff) +#define RT5645_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x1b) */ +#define RT5645_M_DAC_L2_VOL (0x1 << 13) +#define RT5645_M_DAC_L2_VOL_SFT 13 +#define RT5645_M_DAC_R2_VOL (0x1 << 12) +#define RT5645_M_DAC_R2_VOL_SFT 12 +#define RT5645_DAC2_L_SEL_MASK (0x7 << 4) +#define RT5645_DAC2_L_SEL_SFT 4 +#define RT5645_DAC2_R_SEL_MASK (0x7 << 0) +#define RT5645_DAC2_R_SEL_SFT 0 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5645_ADC_L_VOL_MASK (0x7f << 8) +#define RT5645_ADC_L_VOL_SFT 8 +#define RT5645_ADC_R_VOL_MASK (0x7f) +#define RT5645_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5645_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5645_MONO_ADC_L_VOL_SFT 8 +#define RT5645_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5645_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5645_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5645_STO1_ADC_L_BST_SFT 14 +#define RT5645_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5645_STO1_ADC_R_BST_SFT 12 +#define RT5645_STO1_ADC_COMP_MASK (0x3 << 10) +#define RT5645_STO1_ADC_COMP_SFT 10 +#define RT5645_STO2_ADC_L_BST_MASK (0x3 << 8) +#define RT5645_STO2_ADC_L_BST_SFT 8 +#define RT5645_STO2_ADC_R_BST_MASK (0x3 << 6) +#define RT5645_STO2_ADC_R_BST_SFT 6 +#define RT5645_STO2_ADC_COMP_MASK (0x3 << 4) +#define RT5645_STO2_ADC_COMP_SFT 4 + +/* Stereo2 ADC Mixer Control (0x26) */ +#define RT5645_STO2_ADC_SRC_MASK (0x1 << 15) +#define RT5645_STO2_ADC_SRC_SFT 15 + +/* Stereo ADC Mixer Control (0x27) */ +#define RT5645_M_ADC_L1 (0x1 << 14) +#define RT5645_M_ADC_L1_SFT 14 +#define RT5645_M_ADC_L2 (0x1 << 13) +#define RT5645_M_ADC_L2_SFT 13 +#define RT5645_ADC_1_SRC_MASK (0x1 << 12) +#define RT5645_ADC_1_SRC_SFT 12 +#define RT5645_ADC_1_SRC_ADC (0x1 << 12) +#define RT5645_ADC_1_SRC_DACMIX (0x0 << 12) +#define RT5645_ADC_2_SRC_MASK (0x1 << 11) +#define RT5645_ADC_2_SRC_SFT 11 +#define RT5645_DMIC_SRC_MASK (0x1 << 8) +#define RT5645_DMIC_SRC_SFT 8 +#define RT5645_M_ADC_R1 (0x1 << 6) +#define RT5645_M_ADC_R1_SFT 6 +#define RT5645_M_ADC_R2 (0x1 << 5) +#define RT5645_M_ADC_R2_SFT 5 +#define RT5645_DMIC3_SRC_MASK (0x1 << 1) +#define RT5645_DMIC3_SRC_SFT 0 + +/* Mono ADC Mixer Control (0x28) */ +#define RT5645_M_MONO_ADC_L1 (0x1 << 14) +#define RT5645_M_MONO_ADC_L1_SFT 14 +#define RT5645_M_MONO_ADC_L2 (0x1 << 13) +#define RT5645_M_MONO_ADC_L2_SFT 13 +#define RT5645_MONO_ADC_L1_SRC_MASK (0x1 << 12) +#define RT5645_MONO_ADC_L1_SRC_SFT 12 +#define RT5645_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12) +#define RT5645_MONO_ADC_L1_SRC_ADCL (0x1 << 12) +#define RT5645_MONO_ADC_L2_SRC_MASK (0x1 << 11) +#define RT5645_MONO_ADC_L2_SRC_SFT 11 +#define RT5645_MONO_DMIC_L_SRC_MASK (0x1 << 8) +#define RT5645_MONO_DMIC_L_SRC_SFT 8 +#define RT5645_M_MONO_ADC_R1 (0x1 << 6) +#define RT5645_M_MONO_ADC_R1_SFT 6 +#define RT5645_M_MONO_ADC_R2 (0x1 << 5) +#define RT5645_M_MONO_ADC_R2_SFT 5 +#define RT5645_MONO_ADC_R1_SRC_MASK (0x1 << 4) +#define RT5645_MONO_ADC_R1_SRC_SFT 4 +#define RT5645_MONO_ADC_R1_SRC_ADCR (0x1 << 4) +#define RT5645_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4) +#define RT5645_MONO_ADC_R2_SRC_MASK (0x1 << 3) +#define RT5645_MONO_ADC_R2_SRC_SFT 3 +#define RT5645_MONO_DMIC_R_SRC_MASK (0x3) +#define RT5645_MONO_DMIC_R_SRC_SFT 0 + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5645_M_ADCMIX_L (0x1 << 15) +#define RT5645_M_ADCMIX_L_SFT 15 +#define RT5645_M_DAC1_L (0x1 << 14) +#define RT5645_M_DAC1_L_SFT 14 +#define RT5645_DAC1_R_SEL_MASK (0x3 << 10) +#define RT5645_DAC1_R_SEL_SFT 10 +#define RT5645_DAC1_R_SEL_IF1 (0x0 << 10) +#define RT5645_DAC1_R_SEL_IF2 (0x1 << 10) +#define RT5645_DAC1_R_SEL_IF3 (0x2 << 10) +#define RT5645_DAC1_R_SEL_IF4 (0x3 << 10) +#define RT5645_DAC1_L_SEL_MASK (0x3 << 8) +#define RT5645_DAC1_L_SEL_SFT 8 +#define RT5645_DAC1_L_SEL_IF1 (0x0 << 8) +#define RT5645_DAC1_L_SEL_IF2 (0x1 << 8) +#define RT5645_DAC1_L_SEL_IF3 (0x2 << 8) +#define RT5645_DAC1_L_SEL_IF4 (0x3 << 8) +#define RT5645_M_ADCMIX_R (0x1 << 7) +#define RT5645_M_ADCMIX_R_SFT 7 +#define RT5645_M_DAC1_R (0x1 << 6) +#define RT5645_M_DAC1_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5645_M_DAC_L1 (0x1 << 14) +#define RT5645_M_DAC_L1_SFT 14 +#define RT5645_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5645_DAC_L1_STO_L_VOL_SFT 13 +#define RT5645_M_DAC_L2 (0x1 << 12) +#define RT5645_M_DAC_L2_SFT 12 +#define RT5645_DAC_L2_STO_L_VOL_MASK (0x1 << 11) +#define RT5645_DAC_L2_STO_L_VOL_SFT 11 +#define RT5645_M_ANC_DAC_L (0x1 << 10) +#define RT5645_M_ANC_DAC_L_SFT 10 +#define RT5645_M_DAC_R1_STO_L (0x1 << 9) +#define RT5645_M_DAC_R1_STO_L_SFT 9 +#define RT5645_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5645_DAC_R1_STO_L_VOL_SFT 8 +#define RT5645_M_DAC_R1 (0x1 << 6) +#define RT5645_M_DAC_R1_SFT 6 +#define RT5645_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5645_DAC_R1_STO_R_VOL_SFT 5 +#define RT5645_M_DAC_R2 (0x1 << 4) +#define RT5645_M_DAC_R2_SFT 4 +#define RT5645_DAC_R2_STO_R_VOL_MASK (0x1 << 3) +#define RT5645_DAC_R2_STO_R_VOL_SFT 3 +#define RT5645_M_ANC_DAC_R (0x1 << 2) +#define RT5645_M_ANC_DAC_R_SFT 2 +#define RT5645_M_DAC_L1_STO_R (0x1 << 1) +#define RT5645_M_DAC_L1_STO_R_SFT 1 +#define RT5645_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5645_DAC_L1_STO_R_VOL_SFT 0 + +/* Mono DAC Mixer Control (0x2b) */ +#define RT5645_M_DAC_L1_MONO_L (0x1 << 14) +#define RT5645_M_DAC_L1_MONO_L_SFT 14 +#define RT5645_DAC_L1_MONO_L_VOL_MASK (0x1 << 13) +#define RT5645_DAC_L1_MONO_L_VOL_SFT 13 +#define RT5645_M_DAC_L2_MONO_L (0x1 << 12) +#define RT5645_M_DAC_L2_MONO_L_SFT 12 +#define RT5645_DAC_L2_MONO_L_VOL_MASK (0x1 << 11) +#define RT5645_DAC_L2_MONO_L_VOL_SFT 11 +#define RT5645_M_DAC_R2_MONO_L (0x1 << 10) +#define RT5645_M_DAC_R2_MONO_L_SFT 10 +#define RT5645_DAC_R2_MONO_L_VOL_MASK (0x1 << 9) +#define RT5645_DAC_R2_MONO_L_VOL_SFT 9 +#define RT5645_M_DAC_R1_MONO_R (0x1 << 6) +#define RT5645_M_DAC_R1_MONO_R_SFT 6 +#define RT5645_DAC_R1_MONO_R_VOL_MASK (0x1 << 5) +#define RT5645_DAC_R1_MONO_R_VOL_SFT 5 +#define RT5645_M_DAC_R2_MONO_R (0x1 << 4) +#define RT5645_M_DAC_R2_MONO_R_SFT 4 +#define RT5645_DAC_R2_MONO_R_VOL_MASK (0x1 << 3) +#define RT5645_DAC_R2_MONO_R_VOL_SFT 3 +#define RT5645_M_DAC_L2_MONO_R (0x1 << 2) +#define RT5645_M_DAC_L2_MONO_R_SFT 2 +#define RT5645_DAC_L2_MONO_R_VOL_MASK (0x1 << 1) +#define RT5645_DAC_L2_MONO_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5645_M_STO_L_DAC_L (0x1 << 15) +#define RT5645_M_STO_L_DAC_L_SFT 15 +#define RT5645_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5645_STO_L_DAC_L_VOL_SFT 14 +#define RT5645_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5645_M_DAC_L2_DAC_L_SFT 13 +#define RT5645_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5645_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5645_M_STO_R_DAC_R (0x1 << 11) +#define RT5645_M_STO_R_DAC_R_SFT 11 +#define RT5645_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5645_STO_R_DAC_R_VOL_SFT 10 +#define RT5645_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5645_M_DAC_R2_DAC_R_SFT 9 +#define RT5645_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5645_DAC_R2_DAC_R_VOL_SFT 8 +#define RT5645_M_DAC_R2_DAC_L (0x1 << 7) +#define RT5645_M_DAC_R2_DAC_L_SFT 7 +#define RT5645_DAC_R2_DAC_L_VOL_MASK (0x1 << 6) +#define RT5645_DAC_R2_DAC_L_VOL_SFT 6 +#define RT5645_M_DAC_L2_DAC_R (0x1 << 5) +#define RT5645_M_DAC_L2_DAC_R_SFT 5 +#define RT5645_DAC_L2_DAC_R_VOL_MASK (0x1 << 4) +#define RT5645_DAC_L2_DAC_R_VOL_SFT 4 + +/* Digital Interface Data Control (0x2f) */ +#define RT5645_IF1_ADC2_IN_SEL (0x1 << 15) +#define RT5645_IF1_ADC2_IN_SFT 15 +#define RT5645_IF2_ADC_IN_MASK (0x7 << 12) +#define RT5645_IF2_ADC_IN_SFT 12 +#define RT5645_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5645_IF2_DAC_SEL_SFT 10 +#define RT5645_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5645_IF2_ADC_SEL_SFT 8 +#define RT5645_IF3_DAC_SEL_MASK (0x3 << 6) +#define RT5645_IF3_DAC_SEL_SFT 6 +#define RT5645_IF3_ADC_SEL_MASK (0x3 << 4) +#define RT5645_IF3_ADC_SEL_SFT 4 +#define RT5645_IF3_ADC_IN_MASK (0x7) +#define RT5645_IF3_ADC_IN_SFT 0 + +/* PDM Output Control (0x31) */ +#define RT5645_PDM1_L_MASK (0x1 << 15) +#define RT5645_PDM1_L_SFT 15 +#define RT5645_M_PDM1_L (0x1 << 14) +#define RT5645_M_PDM1_L_SFT 14 +#define RT5645_PDM1_R_MASK (0x1 << 13) +#define RT5645_PDM1_R_SFT 13 +#define RT5645_M_PDM1_R (0x1 << 12) +#define RT5645_M_PDM1_R_SFT 12 +#define RT5645_PDM2_L_MASK (0x1 << 11) +#define RT5645_PDM2_L_SFT 11 +#define RT5645_M_PDM2_L (0x1 << 10) +#define RT5645_M_PDM2_L_SFT 10 +#define RT5645_PDM2_R_MASK (0x1 << 9) +#define RT5645_PDM2_R_SFT 9 +#define RT5645_M_PDM2_R (0x1 << 8) +#define RT5645_M_PDM2_R_SFT 8 +#define RT5645_PDM2_BUSY (0x1 << 7) +#define RT5645_PDM1_BUSY (0x1 << 6) +#define RT5645_PDM_PATTERN (0x1 << 5) +#define RT5645_PDM_GAIN (0x1 << 4) +#define RT5645_PDM_DIV_MASK (0x3) + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5645_G_HP_L_RM_L_MASK (0x7 << 13) +#define RT5645_G_HP_L_RM_L_SFT 13 +#define RT5645_G_IN_L_RM_L_MASK (0x7 << 10) +#define RT5645_G_IN_L_RM_L_SFT 10 +#define RT5645_G_BST4_RM_L_MASK (0x7 << 7) +#define RT5645_G_BST4_RM_L_SFT 7 +#define RT5645_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5645_G_BST3_RM_L_SFT 4 +#define RT5645_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5645_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5645_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5645_G_BST1_RM_L_SFT 13 +#define RT5645_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5645_G_OM_L_RM_L_SFT 10 +#define RT5645_M_MM_L_RM_L (0x1 << 6) +#define RT5645_M_MM_L_RM_L_SFT 6 +#define RT5645_M_IN_L_RM_L (0x1 << 5) +#define RT5645_M_IN_L_RM_L_SFT 5 +#define RT5645_M_HP_L_RM_L (0x1 << 4) +#define RT5645_M_HP_L_RM_L_SFT 4 +#define RT5645_M_BST3_RM_L (0x1 << 3) +#define RT5645_M_BST3_RM_L_SFT 3 +#define RT5645_M_BST2_RM_L (0x1 << 2) +#define RT5645_M_BST2_RM_L_SFT 2 +#define RT5645_M_BST1_RM_L (0x1 << 1) +#define RT5645_M_BST1_RM_L_SFT 1 +#define RT5645_M_OM_L_RM_L (0x1) +#define RT5645_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5645_G_HP_R_RM_R_MASK (0x7 << 13) +#define RT5645_G_HP_R_RM_R_SFT 13 +#define RT5645_G_IN_R_RM_R_MASK (0x7 << 10) +#define RT5645_G_IN_R_RM_R_SFT 10 +#define RT5645_G_BST4_RM_R_MASK (0x7 << 7) +#define RT5645_G_BST4_RM_R_SFT 7 +#define RT5645_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5645_G_BST3_RM_R_SFT 4 +#define RT5645_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5645_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5645_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5645_G_BST1_RM_R_SFT 13 +#define RT5645_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5645_G_OM_R_RM_R_SFT 10 +#define RT5645_M_MM_R_RM_R (0x1 << 6) +#define RT5645_M_MM_R_RM_R_SFT 6 +#define RT5645_M_IN_R_RM_R (0x1 << 5) +#define RT5645_M_IN_R_RM_R_SFT 5 +#define RT5645_M_HP_R_RM_R (0x1 << 4) +#define RT5645_M_HP_R_RM_R_SFT 4 +#define RT5645_M_BST3_RM_R (0x1 << 3) +#define RT5645_M_BST3_RM_R_SFT 3 +#define RT5645_M_BST2_RM_R (0x1 << 2) +#define RT5645_M_BST2_RM_R_SFT 2 +#define RT5645_M_BST1_RM_R (0x1 << 1) +#define RT5645_M_BST1_RM_R_SFT 1 +#define RT5645_M_OM_R_RM_R (0x1) +#define RT5645_M_OM_R_RM_R_SFT 0 + +/* HPOMIX Control (0x40) (0x42) */ +#define RT5645_M_BST1_HV (0x1 << 4) +#define RT5645_M_BST1_HV_SFT 4 +#define RT5645_M_BST2_HV (0x1 << 4) +#define RT5645_M_BST2_HV_SFT 4 +#define RT5645_M_BST3_HV (0x1 << 3) +#define RT5645_M_BST3_HV_SFT 3 +#define RT5645_M_IN_HV (0x1 << 2) +#define RT5645_M_IN_HV_SFT 2 +#define RT5645_M_DAC2_HV (0x1 << 1) +#define RT5645_M_DAC2_HV_SFT 1 +#define RT5645_M_DAC1_HV (0x1 << 0) +#define RT5645_M_DAC1_HV_SFT 0 + +/* HPMIX Control (0x45) */ +#define RT5645_M_DAC1_HM (0x1 << 14) +#define RT5645_M_DAC1_HM_SFT 14 +#define RT5645_M_HPVOL_HM (0x1 << 13) +#define RT5645_M_HPVOL_HM_SFT 13 + +/* SPK Left Mixer Control (0x46) */ +#define RT5645_G_RM_L_SM_L_MASK (0x3 << 14) +#define RT5645_G_RM_L_SM_L_SFT 14 +#define RT5645_G_IN_L_SM_L_MASK (0x3 << 12) +#define RT5645_G_IN_L_SM_L_SFT 12 +#define RT5645_G_DAC_L1_SM_L_MASK (0x3 << 10) +#define RT5645_G_DAC_L1_SM_L_SFT 10 +#define RT5645_G_DAC_L2_SM_L_MASK (0x3 << 8) +#define RT5645_G_DAC_L2_SM_L_SFT 8 +#define RT5645_G_OM_L_SM_L_MASK (0x3 << 6) +#define RT5645_G_OM_L_SM_L_SFT 6 +#define RT5645_M_BST1_L_SM_L (0x1 << 5) +#define RT5645_M_BST1_L_SM_L_SFT 5 +#define RT5645_M_IN_L_SM_L (0x1 << 3) +#define RT5645_M_IN_L_SM_L_SFT 3 +#define RT5645_M_DAC_L1_SM_L (0x1 << 1) +#define RT5645_M_DAC_L1_SM_L_SFT 1 +#define RT5645_M_DAC_L2_SM_L (0x1 << 2) +#define RT5645_M_DAC_L2_SM_L_SFT 2 +#define RT5645_M_BST3_L_SM_L (0x1 << 4) +#define RT5645_M_BST3_L_SM_L_SFT 4 + +/* SPK Right Mixer Control (0x47) */ +#define RT5645_G_RM_R_SM_R_MASK (0x3 << 14) +#define RT5645_G_RM_R_SM_R_SFT 14 +#define RT5645_G_IN_R_SM_R_MASK (0x3 << 12) +#define RT5645_G_IN_R_SM_R_SFT 12 +#define RT5645_G_DAC_R1_SM_R_MASK (0x3 << 10) +#define RT5645_G_DAC_R1_SM_R_SFT 10 +#define RT5645_G_DAC_R2_SM_R_MASK (0x3 << 8) +#define RT5645_G_DAC_R2_SM_R_SFT 8 +#define RT5645_G_OM_R_SM_R_MASK (0x3 << 6) +#define RT5645_G_OM_R_SM_R_SFT 6 +#define RT5645_M_BST2_R_SM_R (0x1 << 5) +#define RT5645_M_BST2_R_SM_R_SFT 5 +#define RT5645_M_IN_R_SM_R (0x1 << 3) +#define RT5645_M_IN_R_SM_R_SFT 3 +#define RT5645_M_DAC_R1_SM_R (0x1 << 1) +#define RT5645_M_DAC_R1_SM_R_SFT 1 +#define RT5645_M_DAC_R2_SM_R (0x1 << 2) +#define RT5645_M_DAC_R2_SM_R_SFT 2 +#define RT5645_M_BST3_R_SM_R (0x1 << 4) +#define RT5645_M_BST3_R_SM_R_SFT 4 + +/* SPOLMIX Control (0x48) */ +#define RT5645_M_DAC_L1_SPM_L (0x1 << 15) +#define RT5645_M_DAC_L1_SPM_L_SFT 15 +#define RT5645_M_DAC_R1_SPM_L (0x1 << 14) +#define RT5645_M_DAC_R1_SPM_L_SFT 14 +#define RT5645_M_SV_L_SPM_L (0x1 << 13) +#define RT5645_M_SV_L_SPM_L_SFT 13 +#define RT5645_M_SV_R_SPM_L (0x1 << 12) +#define RT5645_M_SV_R_SPM_L_SFT 12 +#define RT5645_M_BST3_SPM_L (0x1 << 11) +#define RT5645_M_BST3_SPM_L_SFT 11 +#define RT5645_M_DAC_R1_SPM_R (0x1 << 2) +#define RT5645_M_DAC_R1_SPM_R_SFT 2 +#define RT5645_M_BST3_SPM_R (0x1 << 1) +#define RT5645_M_BST3_SPM_R_SFT 1 +#define RT5645_M_SV_R_SPM_R (0x1 << 0) +#define RT5645_M_SV_R_SPM_R_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5645_M_OV_L_MM (0x1 << 9) +#define RT5645_M_OV_L_MM_SFT 9 +#define RT5645_M_DAC_L2_MA (0x1 << 8) +#define RT5645_M_DAC_L2_MA_SFT 8 +#define RT5645_G_MONOMIX_MASK (0x1 << 10) +#define RT5645_G_MONOMIX_SFT 10 +#define RT5645_M_BST2_MM (0x1 << 4) +#define RT5645_M_BST2_MM_SFT 4 +#define RT5645_M_DAC_R1_MM (0x1 << 3) +#define RT5645_M_DAC_R1_MM_SFT 3 +#define RT5645_M_DAC_R2_MM (0x1 << 2) +#define RT5645_M_DAC_R2_MM_SFT 2 +#define RT5645_M_DAC_L2_MM (0x1 << 1) +#define RT5645_M_DAC_L2_MM_SFT 1 +#define RT5645_M_BST3_MM (0x1 << 0) +#define RT5645_M_BST3_MM_SFT 0 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5645_G_BST3_OM_L_MASK (0x7 << 13) +#define RT5645_G_BST3_OM_L_SFT 13 +#define RT5645_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5645_G_BST2_OM_L_SFT 10 +#define RT5645_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5645_G_BST1_OM_L_SFT 7 +#define RT5645_G_IN_L_OM_L_MASK (0x7 << 4) +#define RT5645_G_IN_L_OM_L_SFT 4 +#define RT5645_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5645_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5645_G_DAC_R2_OM_L_MASK (0x7 << 13) +#define RT5645_G_DAC_R2_OM_L_SFT 13 +#define RT5645_G_DAC_L2_OM_L_MASK (0x7 << 10) +#define RT5645_G_DAC_L2_OM_L_SFT 10 +#define RT5645_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5645_G_DAC_L1_OM_L_SFT 7 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5645_M_BST3_OM_L (0x1 << 4) +#define RT5645_M_BST3_OM_L_SFT 4 +#define RT5645_M_BST1_OM_L (0x1 << 3) +#define RT5645_M_BST1_OM_L_SFT 3 +#define RT5645_M_IN_L_OM_L (0x1 << 2) +#define RT5645_M_IN_L_OM_L_SFT 2 +#define RT5645_M_DAC_L2_OM_L (0x1 << 1) +#define RT5645_M_DAC_L2_OM_L_SFT 1 +#define RT5645_M_DAC_L1_OM_L (0x1) +#define RT5645_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5645_G_BST4_OM_R_MASK (0x7 << 13) +#define RT5645_G_BST4_OM_R_SFT 13 +#define RT5645_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5645_G_BST2_OM_R_SFT 10 +#define RT5645_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5645_G_BST1_OM_R_SFT 7 +#define RT5645_G_IN_R_OM_R_MASK (0x7 << 4) +#define RT5645_G_IN_R_OM_R_SFT 4 +#define RT5645_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5645_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5645_G_DAC_L2_OM_R_MASK (0x7 << 13) +#define RT5645_G_DAC_L2_OM_R_SFT 13 +#define RT5645_G_DAC_R2_OM_R_MASK (0x7 << 10) +#define RT5645_G_DAC_R2_OM_R_SFT 10 +#define RT5645_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5645_G_DAC_R1_OM_R_SFT 7 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5645_M_BST3_OM_R (0x1 << 4) +#define RT5645_M_BST3_OM_R_SFT 4 +#define RT5645_M_BST2_OM_R (0x1 << 3) +#define RT5645_M_BST2_OM_R_SFT 3 +#define RT5645_M_IN_R_OM_R (0x1 << 2) +#define RT5645_M_IN_R_OM_R_SFT 2 +#define RT5645_M_DAC_R2_OM_R (0x1 << 1) +#define RT5645_M_DAC_R2_OM_R_SFT 1 +#define RT5645_M_DAC_R1_OM_R (0x1) +#define RT5645_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5645_M_DAC_L1_LM (0x1 << 15) +#define RT5645_M_DAC_L1_LM_SFT 15 +#define RT5645_M_DAC_R1_LM (0x1 << 14) +#define RT5645_M_DAC_R1_LM_SFT 14 +#define RT5645_M_OV_L_LM (0x1 << 13) +#define RT5645_M_OV_L_LM_SFT 13 +#define RT5645_M_OV_R_LM (0x1 << 12) +#define RT5645_M_OV_R_LM_SFT 12 +#define RT5645_G_LOUTMIX_MASK (0x1 << 11) +#define RT5645_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5645_PWR_I2S1 (0x1 << 15) +#define RT5645_PWR_I2S1_BIT 15 +#define RT5645_PWR_I2S2 (0x1 << 14) +#define RT5645_PWR_I2S2_BIT 14 +#define RT5645_PWR_I2S3 (0x1 << 13) +#define RT5645_PWR_I2S3_BIT 13 +#define RT5645_PWR_DAC_L1 (0x1 << 12) +#define RT5645_PWR_DAC_L1_BIT 12 +#define RT5645_PWR_DAC_R1 (0x1 << 11) +#define RT5645_PWR_DAC_R1_BIT 11 +#define RT5645_PWR_CLS_D_R (0x1 << 9) +#define RT5645_PWR_CLS_D_R_BIT 9 +#define RT5645_PWR_CLS_D_L (0x1 << 8) +#define RT5645_PWR_CLS_D_L_BIT 8 +#define RT5645_PWR_ADC_R (0x1 << 1) +#define RT5645_PWR_ADC_R_BIT 1 +#define RT5645_PWR_DAC_L2 (0x1 << 7) +#define RT5645_PWR_DAC_L2_BIT 7 +#define RT5645_PWR_DAC_R2 (0x1 << 6) +#define RT5645_PWR_DAC_R2_BIT 6 +#define RT5645_PWR_ADC_L (0x1 << 2) +#define RT5645_PWR_ADC_L_BIT 2 +#define RT5645_PWR_ADC_R (0x1 << 1) +#define RT5645_PWR_ADC_R_BIT 1 +#define RT5645_PWR_CLS_D (0x1) +#define RT5645_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5645_PWR_ADC_S1F (0x1 << 15) +#define RT5645_PWR_ADC_S1F_BIT 15 +#define RT5645_PWR_ADC_MF_L (0x1 << 14) +#define RT5645_PWR_ADC_MF_L_BIT 14 +#define RT5645_PWR_ADC_MF_R (0x1 << 13) +#define RT5645_PWR_ADC_MF_R_BIT 13 +#define RT5645_PWR_I2S_DSP (0x1 << 12) +#define RT5645_PWR_I2S_DSP_BIT 12 +#define RT5645_PWR_DAC_S1F (0x1 << 11) +#define RT5645_PWR_DAC_S1F_BIT 11 +#define RT5645_PWR_DAC_MF_L (0x1 << 10) +#define RT5645_PWR_DAC_MF_L_BIT 10 +#define RT5645_PWR_DAC_MF_R (0x1 << 9) +#define RT5645_PWR_DAC_MF_R_BIT 9 +#define RT5645_PWR_ADC_S2F (0x1 << 8) +#define RT5645_PWR_ADC_S2F_BIT 8 +#define RT5645_PWR_PDM1 (0x1 << 7) +#define RT5645_PWR_PDM1_BIT 7 +#define RT5645_PWR_PDM2 (0x1 << 6) +#define RT5645_PWR_PDM2_BIT 6 +#define RT5645_PWR_IPTV (0x1 << 1) +#define RT5645_PWR_IPTV_BIT 1 +#define RT5645_PWR_PAD (0x1) +#define RT5645_PWR_PAD_BIT 0 + +/* Power Management for Analog 1 (0x63) */ +#define RT5645_PWR_VREF1 (0x1 << 15) +#define RT5645_PWR_VREF1_BIT 15 +#define RT5645_PWR_FV1 (0x1 << 14) +#define RT5645_PWR_FV1_BIT 14 +#define RT5645_PWR_MB (0x1 << 13) +#define RT5645_PWR_MB_BIT 13 +#define RT5645_PWR_LM (0x1 << 12) +#define RT5645_PWR_LM_BIT 12 +#define RT5645_PWR_BG (0x1 << 11) +#define RT5645_PWR_BG_BIT 11 +#define RT5645_PWR_MA (0x1 << 10) +#define RT5645_PWR_MA_BIT 10 +#define RT5645_PWR_HP_L (0x1 << 7) +#define RT5645_PWR_HP_L_BIT 7 +#define RT5645_PWR_HP_R (0x1 << 6) +#define RT5645_PWR_HP_R_BIT 6 +#define RT5645_PWR_HA (0x1 << 5) +#define RT5645_PWR_HA_BIT 5 +#define RT5645_PWR_VREF2 (0x1 << 4) +#define RT5645_PWR_VREF2_BIT 4 +#define RT5645_PWR_FV2 (0x1 << 3) +#define RT5645_PWR_FV2_BIT 3 +#define RT5645_LDO_SEL_MASK (0x3) +#define RT5645_LDO_SEL_SFT 0 + +/* Power Management for Analog 2 (0x64) */ +#define RT5645_PWR_BST1 (0x1 << 15) +#define RT5645_PWR_BST1_BIT 15 +#define RT5645_PWR_BST2 (0x1 << 14) +#define RT5645_PWR_BST2_BIT 14 +#define RT5645_PWR_BST3 (0x1 << 13) +#define RT5645_PWR_BST3_BIT 13 +#define RT5645_PWR_BST4 (0x1 << 12) +#define RT5645_PWR_BST4_BIT 12 +#define RT5645_PWR_MB1 (0x1 << 11) +#define RT5645_PWR_MB1_BIT 11 +#define RT5645_PWR_MB2 (0x1 << 10) +#define RT5645_PWR_MB2_BIT 10 +#define RT5645_PWR_PLL (0x1 << 9) +#define RT5645_PWR_PLL_BIT 9 +#define RT5645_PWR_BST2_P (0x1 << 5) +#define RT5645_PWR_BST2_P_BIT 5 +#define RT5645_PWR_BST3_P (0x1 << 4) +#define RT5645_PWR_BST3_P_BIT 4 +#define RT5645_PWR_BST4_P (0x1 << 3) +#define RT5645_PWR_BST4_P_BIT 3 +#define RT5645_PWR_JD1 (0x1 << 2) +#define RT5645_PWR_JD1_BIT 2 +#define RT5645_PWR_JD (0x1 << 1) +#define RT5645_PWR_JD_BIT 1 + +/* Power Management for Mixer (0x65) */ +#define RT5645_PWR_OM_L (0x1 << 15) +#define RT5645_PWR_OM_L_BIT 15 +#define RT5645_PWR_OM_R (0x1 << 14) +#define RT5645_PWR_OM_R_BIT 14 +#define RT5645_PWR_SM_L (0x1 << 13) +#define RT5645_PWR_SM_L_BIT 13 +#define RT5645_PWR_SM_R (0x1 << 12) +#define RT5645_PWR_SM_R_BIT 12 +#define RT5645_PWR_RM_L (0x1 << 11) +#define RT5645_PWR_RM_L_BIT 11 +#define RT5645_PWR_RM_R (0x1 << 10) +#define RT5645_PWR_RM_R_BIT 10 +#define RT5645_PWR_MM (0x1 << 8) +#define RT5645_PWR_MM_BIT 8 +#define RT5645_PWR_HM_L (0x1 << 7) +#define RT5645_PWR_HM_L_BIT 7 +#define RT5645_PWR_HM_R (0x1 << 6) +#define RT5645_PWR_HM_R_BIT 6 +#define RT5645_PWR_LDO2 (0x1 << 1) +#define RT5645_PWR_LDO2_BIT 1 + +/* Power Management for Volume (0x66) */ +#define RT5645_PWR_SV_L (0x1 << 15) +#define RT5645_PWR_SV_L_BIT 15 +#define RT5645_PWR_SV_R (0x1 << 14) +#define RT5645_PWR_SV_R_BIT 14 +#define RT5645_PWR_HV_L (0x1 << 11) +#define RT5645_PWR_HV_L_BIT 11 +#define RT5645_PWR_HV_R (0x1 << 10) +#define RT5645_PWR_HV_R_BIT 10 +#define RT5645_PWR_IN_L (0x1 << 9) +#define RT5645_PWR_IN_L_BIT 9 +#define RT5645_PWR_IN_R (0x1 << 8) +#define RT5645_PWR_IN_R_BIT 8 +#define RT5645_PWR_MIC_DET (0x1 << 5) +#define RT5645_PWR_MIC_DET_BIT 5 + +/* I2S1/2 Audio Serial Data Port Control (0x70 0x71) */ +#define RT5645_I2S_MS_MASK (0x1 << 15) +#define RT5645_I2S_MS_SFT 15 +#define RT5645_I2S_MS_M (0x0 << 15) +#define RT5645_I2S_MS_S (0x1 << 15) +#define RT5645_I2S_O_CP_MASK (0x3 << 10) +#define RT5645_I2S_O_CP_SFT 10 +#define RT5645_I2S_O_CP_OFF (0x0 << 10) +#define RT5645_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5645_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5645_I2S_I_CP_MASK (0x3 << 8) +#define RT5645_I2S_I_CP_SFT 8 +#define RT5645_I2S_I_CP_OFF (0x0 << 8) +#define RT5645_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5645_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5645_I2S_BP_MASK (0x1 << 7) +#define RT5645_I2S_BP_SFT 7 +#define RT5645_I2S_BP_NOR (0x0 << 7) +#define RT5645_I2S_BP_INV (0x1 << 7) +#define RT5645_I2S_DL_MASK (0x3 << 2) +#define RT5645_I2S_DL_SFT 2 +#define RT5645_I2S_DL_16 (0x0 << 2) +#define RT5645_I2S_DL_20 (0x1 << 2) +#define RT5645_I2S_DL_24 (0x2 << 2) +#define RT5645_I2S_DL_8 (0x3 << 2) +#define RT5645_I2S_DF_MASK (0x3) +#define RT5645_I2S_DF_SFT 0 +#define RT5645_I2S_DF_I2S (0x0) +#define RT5645_I2S_DF_LEFT (0x1) +#define RT5645_I2S_DF_PCM_A (0x2) +#define RT5645_I2S_DF_PCM_B (0x3) + +/* I2S2 Audio Serial Data Port Control (0x71) */ +#define RT5645_I2S2_SDI_MASK (0x1 << 6) +#define RT5645_I2S2_SDI_SFT 6 +#define RT5645_I2S2_SDI_I2S1 (0x0 << 6) +#define RT5645_I2S2_SDI_I2S2 (0x1 << 6) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5645_I2S_BCLK_MS1_MASK (0x1 << 15) +#define RT5645_I2S_BCLK_MS1_SFT 15 +#define RT5645_I2S_BCLK_MS1_32 (0x0 << 15) +#define RT5645_I2S_BCLK_MS1_64 (0x1 << 15) +#define RT5645_I2S_PD1_MASK (0x7 << 12) +#define RT5645_I2S_PD1_SFT 12 +#define RT5645_I2S_PD1_1 (0x0 << 12) +#define RT5645_I2S_PD1_2 (0x1 << 12) +#define RT5645_I2S_PD1_3 (0x2 << 12) +#define RT5645_I2S_PD1_4 (0x3 << 12) +#define RT5645_I2S_PD1_6 (0x4 << 12) +#define RT5645_I2S_PD1_8 (0x5 << 12) +#define RT5645_I2S_PD1_12 (0x6 << 12) +#define RT5645_I2S_PD1_16 (0x7 << 12) +#define RT5645_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5645_I2S_BCLK_MS2_SFT 11 +#define RT5645_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5645_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5645_I2S_PD2_MASK (0x7 << 8) +#define RT5645_I2S_PD2_SFT 8 +#define RT5645_I2S_PD2_1 (0x0 << 8) +#define RT5645_I2S_PD2_2 (0x1 << 8) +#define RT5645_I2S_PD2_3 (0x2 << 8) +#define RT5645_I2S_PD2_4 (0x3 << 8) +#define RT5645_I2S_PD2_6 (0x4 << 8) +#define RT5645_I2S_PD2_8 (0x5 << 8) +#define RT5645_I2S_PD2_12 (0x6 << 8) +#define RT5645_I2S_PD2_16 (0x7 << 8) +#define RT5645_I2S_BCLK_MS3_MASK (0x1 << 7) +#define RT5645_I2S_BCLK_MS3_SFT 7 +#define RT5645_I2S_BCLK_MS3_32 (0x0 << 7) +#define RT5645_I2S_BCLK_MS3_64 (0x1 << 7) +#define RT5645_I2S_PD3_MASK (0x7 << 4) +#define RT5645_I2S_PD3_SFT 4 +#define RT5645_I2S_PD3_1 (0x0 << 4) +#define RT5645_I2S_PD3_2 (0x1 << 4) +#define RT5645_I2S_PD3_3 (0x2 << 4) +#define RT5645_I2S_PD3_4 (0x3 << 4) +#define RT5645_I2S_PD3_6 (0x4 << 4) +#define RT5645_I2S_PD3_8 (0x5 << 4) +#define RT5645_I2S_PD3_12 (0x6 << 4) +#define RT5645_I2S_PD3_16 (0x7 << 4) +#define RT5645_DAC_OSR_MASK (0x3 << 2) +#define RT5645_DAC_OSR_SFT 2 +#define RT5645_DAC_OSR_128 (0x0 << 2) +#define RT5645_DAC_OSR_64 (0x1 << 2) +#define RT5645_DAC_OSR_32 (0x2 << 2) +#define RT5645_DAC_OSR_16 (0x3 << 2) +#define RT5645_ADC_OSR_MASK (0x3) +#define RT5645_ADC_OSR_SFT 0 +#define RT5645_ADC_OSR_128 (0x0) +#define RT5645_ADC_OSR_64 (0x1) +#define RT5645_ADC_OSR_32 (0x2) +#define RT5645_ADC_OSR_16 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5645_DAC_L_OSR_MASK (0x3 << 14) +#define RT5645_DAC_L_OSR_SFT 14 +#define RT5645_DAC_L_OSR_128 (0x0 << 14) +#define RT5645_DAC_L_OSR_64 (0x1 << 14) +#define RT5645_DAC_L_OSR_32 (0x2 << 14) +#define RT5645_DAC_L_OSR_16 (0x3 << 14) +#define RT5645_ADC_R_OSR_MASK (0x3 << 12) +#define RT5645_ADC_R_OSR_SFT 12 +#define RT5645_ADC_R_OSR_128 (0x0 << 12) +#define RT5645_ADC_R_OSR_64 (0x1 << 12) +#define RT5645_ADC_R_OSR_32 (0x2 << 12) +#define RT5645_ADC_R_OSR_16 (0x3 << 12) +#define RT5645_DAHPF_EN (0x1 << 11) +#define RT5645_DAHPF_EN_SFT 11 +#define RT5645_ADHPF_EN (0x1 << 10) +#define RT5645_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5645_DMIC_1_EN_MASK (0x1 << 15) +#define RT5645_DMIC_1_EN_SFT 15 +#define RT5645_DMIC_1_DIS (0x0 << 15) +#define RT5645_DMIC_1_EN (0x1 << 15) +#define RT5645_DMIC_2_EN_MASK (0x1 << 14) +#define RT5645_DMIC_2_EN_SFT 14 +#define RT5645_DMIC_2_DIS (0x0 << 14) +#define RT5645_DMIC_2_EN (0x1 << 14) +#define RT5645_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5645_DMIC_1L_LH_SFT 13 +#define RT5645_DMIC_1L_LH_FALLING (0x0 << 13) +#define RT5645_DMIC_1L_LH_RISING (0x1 << 13) +#define RT5645_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5645_DMIC_1R_LH_SFT 12 +#define RT5645_DMIC_1R_LH_FALLING (0x0 << 12) +#define RT5645_DMIC_1R_LH_RISING (0x1 << 12) +#define RT5645_DMIC_2_DP_MASK (0x3 << 10) +#define RT5645_DMIC_2_DP_SFT 10 +#define RT5645_DMIC_2_DP_GPIO6 (0x0 << 10) +#define RT5645_DMIC_2_DP_GPIO10 (0x1 << 10) +#define RT5645_DMIC_2_DP_GPIO12 (0x2 << 10) +#define RT5645_DMIC_2_DP_IN2P (0x3 << 10) +#define RT5645_DMIC_2L_LH_MASK (0x1 << 9) +#define RT5645_DMIC_2L_LH_SFT 9 +#define RT5645_DMIC_2L_LH_FALLING (0x0 << 9) +#define RT5645_DMIC_2L_LH_RISING (0x1 << 9) +#define RT5645_DMIC_2R_LH_MASK (0x1 << 8) +#define RT5645_DMIC_2R_LH_SFT 8 +#define RT5645_DMIC_2R_LH_FALLING (0x0 << 8) +#define RT5645_DMIC_2R_LH_RISING (0x1 << 8) +#define RT5645_DMIC_CLK_MASK (0x7 << 5) +#define RT5645_DMIC_CLK_SFT 5 +#define RT5645_DMIC_3_EN_MASK (0x1 << 4) +#define RT5645_DMIC_3_EN_SFT 4 +#define RT5645_DMIC_3_DIS (0x0 << 4) +#define RT5645_DMIC_3_EN (0x1 << 4) +#define RT5645_DMIC_1_DP_MASK (0x3 << 0) +#define RT5645_DMIC_1_DP_SFT 0 +#define RT5645_DMIC_1_DP_GPIO5 (0x0 << 0) +#define RT5645_DMIC_1_DP_IN2N (0x1 << 0) +#define RT5645_DMIC_1_DP_GPIO11 (0x2 << 0) + +/* TDM Control 1 (0x77) */ +#define RT5645_IF1_ADC_IN_MASK (0x3 << 8) +#define RT5645_IF1_ADC_IN_SFT 8 + +/* Global Clock Control (0x80) */ +#define RT5645_SCLK_SRC_MASK (0x3 << 14) +#define RT5645_SCLK_SRC_SFT 14 +#define RT5645_SCLK_SRC_MCLK (0x0 << 14) +#define RT5645_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5645_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */ +#define RT5645_PLL1_SRC_MASK (0x3 << 12) +#define RT5645_PLL1_SRC_SFT 12 +#define RT5645_PLL1_SRC_MCLK (0x0 << 12) +#define RT5645_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5645_PLL1_SRC_BCLK2 (0x2 << 12) +#define RT5645_PLL1_SRC_BCLK3 (0x3 << 12) +#define RT5645_PLL1_PD_MASK (0x1 << 3) +#define RT5645_PLL1_PD_SFT 3 +#define RT5645_PLL1_PD_1 (0x0 << 3) +#define RT5645_PLL1_PD_2 (0x1 << 3) + +#define RT5645_PLL_INP_MAX 40000000 +#define RT5645_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5645_PLL_N_MAX 0x1ff +#define RT5645_PLL_N_MASK (RT5645_PLL_N_MAX << 7) +#define RT5645_PLL_N_SFT 7 +#define RT5645_PLL_K_MAX 0x1f +#define RT5645_PLL_K_MASK (RT5645_PLL_K_MAX) +#define RT5645_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5645_PLL_M_MAX 0xf +#define RT5645_PLL_M_MASK (RT5645_PLL_M_MAX << 12) +#define RT5645_PLL_M_SFT 12 +#define RT5645_PLL_M_BP (0x1 << 11) +#define RT5645_PLL_M_BP_SFT 11 + +/* ASRC Control 1 (0x83) */ +#define RT5645_STO_T_MASK (0x1 << 15) +#define RT5645_STO_T_SFT 15 +#define RT5645_STO_T_SCLK (0x0 << 15) +#define RT5645_STO_T_LRCK1 (0x1 << 15) +#define RT5645_M1_T_MASK (0x1 << 14) +#define RT5645_M1_T_SFT 14 +#define RT5645_M1_T_I2S2 (0x0 << 14) +#define RT5645_M1_T_I2S2_D3 (0x1 << 14) +#define RT5645_I2S2_F_MASK (0x1 << 12) +#define RT5645_I2S2_F_SFT 12 +#define RT5645_I2S2_F_I2S2_D2 (0x0 << 12) +#define RT5645_I2S2_F_I2S1_TCLK (0x1 << 12) +#define RT5645_DMIC_1_M_MASK (0x1 << 9) +#define RT5645_DMIC_1_M_SFT 9 +#define RT5645_DMIC_1_M_NOR (0x0 << 9) +#define RT5645_DMIC_1_M_ASYN (0x1 << 9) +#define RT5645_DMIC_2_M_MASK (0x1 << 8) +#define RT5645_DMIC_2_M_SFT 8 +#define RT5645_DMIC_2_M_NOR (0x0 << 8) +#define RT5645_DMIC_2_M_ASYN (0x1 << 8) + +/* ASRC Control 2 (0x84) */ +#define RT5645_MDA_L_M_MASK (0x1 << 15) +#define RT5645_MDA_L_M_SFT 15 +#define RT5645_MDA_L_M_NOR (0x0 << 15) +#define RT5645_MDA_L_M_ASYN (0x1 << 15) +#define RT5645_MDA_R_M_MASK (0x1 << 14) +#define RT5645_MDA_R_M_SFT 14 +#define RT5645_MDA_R_M_NOR (0x0 << 14) +#define RT5645_MDA_R_M_ASYN (0x1 << 14) +#define RT5645_MAD_L_M_MASK (0x1 << 13) +#define RT5645_MAD_L_M_SFT 13 +#define RT5645_MAD_L_M_NOR (0x0 << 13) +#define RT5645_MAD_L_M_ASYN (0x1 << 13) +#define RT5645_MAD_R_M_MASK (0x1 << 12) +#define RT5645_MAD_R_M_SFT 12 +#define RT5645_MAD_R_M_NOR (0x0 << 12) +#define RT5645_MAD_R_M_ASYN (0x1 << 12) +#define RT5645_ADC_M_MASK (0x1 << 11) +#define RT5645_ADC_M_SFT 11 +#define RT5645_ADC_M_NOR (0x0 << 11) +#define RT5645_ADC_M_ASYN (0x1 << 11) +#define RT5645_STO_DAC_M_MASK (0x1 << 5) +#define RT5645_STO_DAC_M_SFT 5 +#define RT5645_STO_DAC_M_NOR (0x0 << 5) +#define RT5645_STO_DAC_M_ASYN (0x1 << 5) +#define RT5645_I2S1_R_D_MASK (0x1 << 4) +#define RT5645_I2S1_R_D_SFT 4 +#define RT5645_I2S1_R_D_DIS (0x0 << 4) +#define RT5645_I2S1_R_D_EN (0x1 << 4) +#define RT5645_I2S2_R_D_MASK (0x1 << 3) +#define RT5645_I2S2_R_D_SFT 3 +#define RT5645_I2S2_R_D_DIS (0x0 << 3) +#define RT5645_I2S2_R_D_EN (0x1 << 3) +#define RT5645_PRE_SCLK_MASK (0x3) +#define RT5645_PRE_SCLK_SFT 0 +#define RT5645_PRE_SCLK_512 (0x0) +#define RT5645_PRE_SCLK_1024 (0x1) +#define RT5645_PRE_SCLK_2048 (0x2) + +/* ASRC Control 3 (0x85) */ +#define RT5645_I2S1_RATE_MASK (0xf << 12) +#define RT5645_I2S1_RATE_SFT 12 +#define RT5645_I2S2_RATE_MASK (0xf << 8) +#define RT5645_I2S2_RATE_SFT 8 + +/* ASRC Control 4 (0x89) */ +#define RT5645_I2S1_PD_MASK (0x7 << 12) +#define RT5645_I2S1_PD_SFT 12 +#define RT5645_I2S2_PD_MASK (0x7 << 8) +#define RT5645_I2S2_PD_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5645_HP_OVCD_MASK (0x1 << 10) +#define RT5645_HP_OVCD_SFT 10 +#define RT5645_HP_OVCD_DIS (0x0 << 10) +#define RT5645_HP_OVCD_EN (0x1 << 10) +#define RT5645_HP_OC_TH_MASK (0x3 << 8) +#define RT5645_HP_OC_TH_SFT 8 +#define RT5645_HP_OC_TH_90 (0x0 << 8) +#define RT5645_HP_OC_TH_105 (0x1 << 8) +#define RT5645_HP_OC_TH_120 (0x2 << 8) +#define RT5645_HP_OC_TH_135 (0x3 << 8) + +/* Class D Over Current Control (0x8c) */ +#define RT5645_CLSD_OC_MASK (0x1 << 9) +#define RT5645_CLSD_OC_SFT 9 +#define RT5645_CLSD_OC_PU (0x0 << 9) +#define RT5645_CLSD_OC_PD (0x1 << 9) +#define RT5645_AUTO_PD_MASK (0x1 << 8) +#define RT5645_AUTO_PD_SFT 8 +#define RT5645_AUTO_PD_DIS (0x0 << 8) +#define RT5645_AUTO_PD_EN (0x1 << 8) +#define RT5645_CLSD_OC_TH_MASK (0x3f) +#define RT5645_CLSD_OC_TH_SFT 0 + +/* Class D Output Control (0x8d) */ +#define RT5645_CLSD_RATIO_MASK (0xf << 12) +#define RT5645_CLSD_RATIO_SFT 12 +#define RT5645_CLSD_OM_MASK (0x1 << 11) +#define RT5645_CLSD_OM_SFT 11 +#define RT5645_CLSD_OM_MONO (0x0 << 11) +#define RT5645_CLSD_OM_STO (0x1 << 11) +#define RT5645_CLSD_SCH_MASK (0x1 << 10) +#define RT5645_CLSD_SCH_SFT 10 +#define RT5645_CLSD_SCH_L (0x0 << 10) +#define RT5645_CLSD_SCH_S (0x1 << 10) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5645_SMT_TRIG_MASK (0x1 << 15) +#define RT5645_SMT_TRIG_SFT 15 +#define RT5645_SMT_TRIG_DIS (0x0 << 15) +#define RT5645_SMT_TRIG_EN (0x1 << 15) +#define RT5645_HP_L_SMT_MASK (0x1 << 9) +#define RT5645_HP_L_SMT_SFT 9 +#define RT5645_HP_L_SMT_DIS (0x0 << 9) +#define RT5645_HP_L_SMT_EN (0x1 << 9) +#define RT5645_HP_R_SMT_MASK (0x1 << 8) +#define RT5645_HP_R_SMT_SFT 8 +#define RT5645_HP_R_SMT_DIS (0x0 << 8) +#define RT5645_HP_R_SMT_EN (0x1 << 8) +#define RT5645_HP_CD_PD_MASK (0x1 << 7) +#define RT5645_HP_CD_PD_SFT 7 +#define RT5645_HP_CD_PD_DIS (0x0 << 7) +#define RT5645_HP_CD_PD_EN (0x1 << 7) +#define RT5645_RSTN_MASK (0x1 << 6) +#define RT5645_RSTN_SFT 6 +#define RT5645_RSTN_DIS (0x0 << 6) +#define RT5645_RSTN_EN (0x1 << 6) +#define RT5645_RSTP_MASK (0x1 << 5) +#define RT5645_RSTP_SFT 5 +#define RT5645_RSTP_DIS (0x0 << 5) +#define RT5645_RSTP_EN (0x1 << 5) +#define RT5645_HP_CO_MASK (0x1 << 4) +#define RT5645_HP_CO_SFT 4 +#define RT5645_HP_CO_DIS (0x0 << 4) +#define RT5645_HP_CO_EN (0x1 << 4) +#define RT5645_HP_CP_MASK (0x1 << 3) +#define RT5645_HP_CP_SFT 3 +#define RT5645_HP_CP_PD (0x0 << 3) +#define RT5645_HP_CP_PU (0x1 << 3) +#define RT5645_HP_SG_MASK (0x1 << 2) +#define RT5645_HP_SG_SFT 2 +#define RT5645_HP_SG_DIS (0x0 << 2) +#define RT5645_HP_SG_EN (0x1 << 2) +#define RT5645_HP_DP_MASK (0x1 << 1) +#define RT5645_HP_DP_SFT 1 +#define RT5645_HP_DP_PD (0x0 << 1) +#define RT5645_HP_DP_PU (0x1 << 1) +#define RT5645_HP_CB_MASK (0x1) +#define RT5645_HP_CB_SFT 0 +#define RT5645_HP_CB_PD (0x0) +#define RT5645_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5645_DEPOP_MASK (0x1 << 13) +#define RT5645_DEPOP_SFT 13 +#define RT5645_DEPOP_AUTO (0x0 << 13) +#define RT5645_DEPOP_MAN (0x1 << 13) +#define RT5645_RAMP_MASK (0x1 << 12) +#define RT5645_RAMP_SFT 12 +#define RT5645_RAMP_DIS (0x0 << 12) +#define RT5645_RAMP_EN (0x1 << 12) +#define RT5645_BPS_MASK (0x1 << 11) +#define RT5645_BPS_SFT 11 +#define RT5645_BPS_DIS (0x0 << 11) +#define RT5645_BPS_EN (0x1 << 11) +#define RT5645_FAST_UPDN_MASK (0x1 << 10) +#define RT5645_FAST_UPDN_SFT 10 +#define RT5645_FAST_UPDN_DIS (0x0 << 10) +#define RT5645_FAST_UPDN_EN (0x1 << 10) +#define RT5645_MRES_MASK (0x3 << 8) +#define RT5645_MRES_SFT 8 +#define RT5645_MRES_15MO (0x0 << 8) +#define RT5645_MRES_25MO (0x1 << 8) +#define RT5645_MRES_35MO (0x2 << 8) +#define RT5645_MRES_45MO (0x3 << 8) +#define RT5645_VLO_MASK (0x1 << 7) +#define RT5645_VLO_SFT 7 +#define RT5645_VLO_3V (0x0 << 7) +#define RT5645_VLO_32V (0x1 << 7) +#define RT5645_DIG_DP_MASK (0x1 << 6) +#define RT5645_DIG_DP_SFT 6 +#define RT5645_DIG_DP_DIS (0x0 << 6) +#define RT5645_DIG_DP_EN (0x1 << 6) +#define RT5645_DP_TH_MASK (0x3 << 4) +#define RT5645_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5645_CP_SYS_MASK (0x7 << 12) +#define RT5645_CP_SYS_SFT 12 +#define RT5645_CP_FQ1_MASK (0x7 << 8) +#define RT5645_CP_FQ1_SFT 8 +#define RT5645_CP_FQ2_MASK (0x7 << 4) +#define RT5645_CP_FQ2_SFT 4 +#define RT5645_CP_FQ3_MASK (0x7) +#define RT5645_CP_FQ3_SFT 0 +#define RT5645_CP_FQ_1_5_KHZ 0 +#define RT5645_CP_FQ_3_KHZ 1 +#define RT5645_CP_FQ_6_KHZ 2 +#define RT5645_CP_FQ_12_KHZ 3 +#define RT5645_CP_FQ_24_KHZ 4 +#define RT5645_CP_FQ_48_KHZ 5 +#define RT5645_CP_FQ_96_KHZ 6 +#define RT5645_CP_FQ_192_KHZ 7 + +/* PV detection and SPK gain control (0x92) */ +#define RT5645_PVDD_DET_MASK (0x1 << 15) +#define RT5645_PVDD_DET_SFT 15 +#define RT5645_PVDD_DET_DIS (0x0 << 15) +#define RT5645_PVDD_DET_EN (0x1 << 15) +#define RT5645_SPK_AG_MASK (0x1 << 14) +#define RT5645_SPK_AG_SFT 14 +#define RT5645_SPK_AG_DIS (0x0 << 14) +#define RT5645_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5645_MIC1_BS_MASK (0x1 << 15) +#define RT5645_MIC1_BS_SFT 15 +#define RT5645_MIC1_BS_9AV (0x0 << 15) +#define RT5645_MIC1_BS_75AV (0x1 << 15) +#define RT5645_MIC2_BS_MASK (0x1 << 14) +#define RT5645_MIC2_BS_SFT 14 +#define RT5645_MIC2_BS_9AV (0x0 << 14) +#define RT5645_MIC2_BS_75AV (0x1 << 14) +#define RT5645_MIC1_CLK_MASK (0x1 << 13) +#define RT5645_MIC1_CLK_SFT 13 +#define RT5645_MIC1_CLK_DIS (0x0 << 13) +#define RT5645_MIC1_CLK_EN (0x1 << 13) +#define RT5645_MIC2_CLK_MASK (0x1 << 12) +#define RT5645_MIC2_CLK_SFT 12 +#define RT5645_MIC2_CLK_DIS (0x0 << 12) +#define RT5645_MIC2_CLK_EN (0x1 << 12) +#define RT5645_MIC1_OVCD_MASK (0x1 << 11) +#define RT5645_MIC1_OVCD_SFT 11 +#define RT5645_MIC1_OVCD_DIS (0x0 << 11) +#define RT5645_MIC1_OVCD_EN (0x1 << 11) +#define RT5645_MIC1_OVTH_MASK (0x3 << 9) +#define RT5645_MIC1_OVTH_SFT 9 +#define RT5645_MIC1_OVTH_600UA (0x0 << 9) +#define RT5645_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5645_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5645_MIC2_OVCD_MASK (0x1 << 8) +#define RT5645_MIC2_OVCD_SFT 8 +#define RT5645_MIC2_OVCD_DIS (0x0 << 8) +#define RT5645_MIC2_OVCD_EN (0x1 << 8) +#define RT5645_MIC2_OVTH_MASK (0x3 << 6) +#define RT5645_MIC2_OVTH_SFT 6 +#define RT5645_MIC2_OVTH_600UA (0x0 << 6) +#define RT5645_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5645_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5645_PWR_MB_MASK (0x1 << 5) +#define RT5645_PWR_MB_SFT 5 +#define RT5645_PWR_MB_PD (0x0 << 5) +#define RT5645_PWR_MB_PU (0x1 << 5) +#define RT5645_PWR_CLK25M_MASK (0x1 << 4) +#define RT5645_PWR_CLK25M_SFT 4 +#define RT5645_PWR_CLK25M_PD (0x0 << 4) +#define RT5645_PWR_CLK25M_PU (0x1 << 4) + +/* VAD Control 4 (0x9d) */ +#define RT5645_VAD_SEL_MASK (0x3 << 8) +#define RT5645_VAD_SEL_SFT 8 + +/* EQ Control 1 (0xb0) */ +#define RT5645_EQ_SRC_MASK (0x1 << 15) +#define RT5645_EQ_SRC_SFT 15 +#define RT5645_EQ_SRC_DAC (0x0 << 15) +#define RT5645_EQ_SRC_ADC (0x1 << 15) +#define RT5645_EQ_UPD (0x1 << 14) +#define RT5645_EQ_UPD_BIT 14 +#define RT5645_EQ_CD_MASK (0x1 << 13) +#define RT5645_EQ_CD_SFT 13 +#define RT5645_EQ_CD_DIS (0x0 << 13) +#define RT5645_EQ_CD_EN (0x1 << 13) +#define RT5645_EQ_DITH_MASK (0x3 << 8) +#define RT5645_EQ_DITH_SFT 8 +#define RT5645_EQ_DITH_NOR (0x0 << 8) +#define RT5645_EQ_DITH_LSB (0x1 << 8) +#define RT5645_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5645_EQ_DITH_LSB_2 (0x3 << 8) + +/* EQ Control 2 (0xb1) */ +#define RT5645_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5645_EQ_HPF1_M_SFT 8 +#define RT5645_EQ_HPF1_M_HI (0x0 << 8) +#define RT5645_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5645_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5645_EQ_LPF1_M_SFT 7 +#define RT5645_EQ_LPF1_M_LO (0x0 << 7) +#define RT5645_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5645_EQ_HPF2_MASK (0x1 << 6) +#define RT5645_EQ_HPF2_SFT 6 +#define RT5645_EQ_HPF2_DIS (0x0 << 6) +#define RT5645_EQ_HPF2_EN (0x1 << 6) +#define RT5645_EQ_HPF1_MASK (0x1 << 5) +#define RT5645_EQ_HPF1_SFT 5 +#define RT5645_EQ_HPF1_DIS (0x0 << 5) +#define RT5645_EQ_HPF1_EN (0x1 << 5) +#define RT5645_EQ_BPF4_MASK (0x1 << 4) +#define RT5645_EQ_BPF4_SFT 4 +#define RT5645_EQ_BPF4_DIS (0x0 << 4) +#define RT5645_EQ_BPF4_EN (0x1 << 4) +#define RT5645_EQ_BPF3_MASK (0x1 << 3) +#define RT5645_EQ_BPF3_SFT 3 +#define RT5645_EQ_BPF3_DIS (0x0 << 3) +#define RT5645_EQ_BPF3_EN (0x1 << 3) +#define RT5645_EQ_BPF2_MASK (0x1 << 2) +#define RT5645_EQ_BPF2_SFT 2 +#define RT5645_EQ_BPF2_DIS (0x0 << 2) +#define RT5645_EQ_BPF2_EN (0x1 << 2) +#define RT5645_EQ_BPF1_MASK (0x1 << 1) +#define RT5645_EQ_BPF1_SFT 1 +#define RT5645_EQ_BPF1_DIS (0x0 << 1) +#define RT5645_EQ_BPF1_EN (0x1 << 1) +#define RT5645_EQ_LPF_MASK (0x1) +#define RT5645_EQ_LPF_SFT 0 +#define RT5645_EQ_LPF_DIS (0x0) +#define RT5645_EQ_LPF_EN (0x1) +#define RT5645_EQ_CTRL_MASK (0x7f) + +/* Memory Test (0xb2) */ +#define RT5645_MT_MASK (0x1 << 15) +#define RT5645_MT_SFT 15 +#define RT5645_MT_DIS (0x0 << 15) +#define RT5645_MT_EN (0x1 << 15) + +/* DRC/AGC Control 1 (0xb4) */ +#define RT5645_DRC_AGC_P_MASK (0x1 << 15) +#define RT5645_DRC_AGC_P_SFT 15 +#define RT5645_DRC_AGC_P_DAC (0x0 << 15) +#define RT5645_DRC_AGC_P_ADC (0x1 << 15) +#define RT5645_DRC_AGC_MASK (0x1 << 14) +#define RT5645_DRC_AGC_SFT 14 +#define RT5645_DRC_AGC_DIS (0x0 << 14) +#define RT5645_DRC_AGC_EN (0x1 << 14) +#define RT5645_DRC_AGC_UPD (0x1 << 13) +#define RT5645_DRC_AGC_UPD_BIT 13 +#define RT5645_DRC_AGC_AR_MASK (0x1f << 8) +#define RT5645_DRC_AGC_AR_SFT 8 +#define RT5645_DRC_AGC_R_MASK (0x7 << 5) +#define RT5645_DRC_AGC_R_SFT 5 +#define RT5645_DRC_AGC_R_48K (0x1 << 5) +#define RT5645_DRC_AGC_R_96K (0x2 << 5) +#define RT5645_DRC_AGC_R_192K (0x3 << 5) +#define RT5645_DRC_AGC_R_441K (0x5 << 5) +#define RT5645_DRC_AGC_R_882K (0x6 << 5) +#define RT5645_DRC_AGC_R_1764K (0x7 << 5) +#define RT5645_DRC_AGC_RC_MASK (0x1f) +#define RT5645_DRC_AGC_RC_SFT 0 + +/* DRC/AGC Control 2 (0xb5) */ +#define RT5645_DRC_AGC_POB_MASK (0x3f << 8) +#define RT5645_DRC_AGC_POB_SFT 8 +#define RT5645_DRC_AGC_CP_MASK (0x1 << 7) +#define RT5645_DRC_AGC_CP_SFT 7 +#define RT5645_DRC_AGC_CP_DIS (0x0 << 7) +#define RT5645_DRC_AGC_CP_EN (0x1 << 7) +#define RT5645_DRC_AGC_CPR_MASK (0x3 << 5) +#define RT5645_DRC_AGC_CPR_SFT 5 +#define RT5645_DRC_AGC_CPR_1_1 (0x0 << 5) +#define RT5645_DRC_AGC_CPR_1_2 (0x1 << 5) +#define RT5645_DRC_AGC_CPR_1_3 (0x2 << 5) +#define RT5645_DRC_AGC_CPR_1_4 (0x3 << 5) +#define RT5645_DRC_AGC_PRB_MASK (0x1f) +#define RT5645_DRC_AGC_PRB_SFT 0 + +/* DRC/AGC Control 3 (0xb6) */ +#define RT5645_DRC_AGC_NGB_MASK (0xf << 12) +#define RT5645_DRC_AGC_NGB_SFT 12 +#define RT5645_DRC_AGC_TAR_MASK (0x1f << 7) +#define RT5645_DRC_AGC_TAR_SFT 7 +#define RT5645_DRC_AGC_NG_MASK (0x1 << 6) +#define RT5645_DRC_AGC_NG_SFT 6 +#define RT5645_DRC_AGC_NG_DIS (0x0 << 6) +#define RT5645_DRC_AGC_NG_EN (0x1 << 6) +#define RT5645_DRC_AGC_NGH_MASK (0x1 << 5) +#define RT5645_DRC_AGC_NGH_SFT 5 +#define RT5645_DRC_AGC_NGH_DIS (0x0 << 5) +#define RT5645_DRC_AGC_NGH_EN (0x1 << 5) +#define RT5645_DRC_AGC_NGT_MASK (0x1f) +#define RT5645_DRC_AGC_NGT_SFT 0 + +/* ANC Control 1 (0xb8) */ +#define RT5645_ANC_M_MASK (0x1 << 15) +#define RT5645_ANC_M_SFT 15 +#define RT5645_ANC_M_NOR (0x0 << 15) +#define RT5645_ANC_M_REV (0x1 << 15) +#define RT5645_ANC_MASK (0x1 << 14) +#define RT5645_ANC_SFT 14 +#define RT5645_ANC_DIS (0x0 << 14) +#define RT5645_ANC_EN (0x1 << 14) +#define RT5645_ANC_MD_MASK (0x3 << 12) +#define RT5645_ANC_MD_SFT 12 +#define RT5645_ANC_MD_DIS (0x0 << 12) +#define RT5645_ANC_MD_67MS (0x1 << 12) +#define RT5645_ANC_MD_267MS (0x2 << 12) +#define RT5645_ANC_MD_1067MS (0x3 << 12) +#define RT5645_ANC_SN_MASK (0x1 << 11) +#define RT5645_ANC_SN_SFT 11 +#define RT5645_ANC_SN_DIS (0x0 << 11) +#define RT5645_ANC_SN_EN (0x1 << 11) +#define RT5645_ANC_CLK_MASK (0x1 << 10) +#define RT5645_ANC_CLK_SFT 10 +#define RT5645_ANC_CLK_ANC (0x0 << 10) +#define RT5645_ANC_CLK_REG (0x1 << 10) +#define RT5645_ANC_ZCD_MASK (0x3 << 8) +#define RT5645_ANC_ZCD_SFT 8 +#define RT5645_ANC_ZCD_DIS (0x0 << 8) +#define RT5645_ANC_ZCD_T1 (0x1 << 8) +#define RT5645_ANC_ZCD_T2 (0x2 << 8) +#define RT5645_ANC_ZCD_WT (0x3 << 8) +#define RT5645_ANC_CS_MASK (0x1 << 7) +#define RT5645_ANC_CS_SFT 7 +#define RT5645_ANC_CS_DIS (0x0 << 7) +#define RT5645_ANC_CS_EN (0x1 << 7) +#define RT5645_ANC_SW_MASK (0x1 << 6) +#define RT5645_ANC_SW_SFT 6 +#define RT5645_ANC_SW_NOR (0x0 << 6) +#define RT5645_ANC_SW_AUTO (0x1 << 6) +#define RT5645_ANC_CO_L_MASK (0x3f) +#define RT5645_ANC_CO_L_SFT 0 + +/* ANC Control 2 (0xb6) */ +#define RT5645_ANC_FG_R_MASK (0xf << 12) +#define RT5645_ANC_FG_R_SFT 12 +#define RT5645_ANC_FG_L_MASK (0xf << 8) +#define RT5645_ANC_FG_L_SFT 8 +#define RT5645_ANC_CG_R_MASK (0xf << 4) +#define RT5645_ANC_CG_R_SFT 4 +#define RT5645_ANC_CG_L_MASK (0xf) +#define RT5645_ANC_CG_L_SFT 0 + +/* ANC Control 3 (0xb6) */ +#define RT5645_ANC_CD_MASK (0x1 << 6) +#define RT5645_ANC_CD_SFT 6 +#define RT5645_ANC_CD_BOTH (0x0 << 6) +#define RT5645_ANC_CD_IND (0x1 << 6) +#define RT5645_ANC_CO_R_MASK (0x3f) +#define RT5645_ANC_CO_R_SFT 0 + +/* Jack Detect Control (0xbb) */ +#define RT5645_JD_MASK (0x7 << 13) +#define RT5645_JD_SFT 13 +#define RT5645_JD_DIS (0x0 << 13) +#define RT5645_JD_GPIO1 (0x1 << 13) +#define RT5645_JD_JD1_IN4P (0x2 << 13) +#define RT5645_JD_JD2_IN4N (0x3 << 13) +#define RT5645_JD_GPIO2 (0x4 << 13) +#define RT5645_JD_GPIO3 (0x5 << 13) +#define RT5645_JD_GPIO4 (0x6 << 13) +#define RT5645_JD_HP_MASK (0x1 << 11) +#define RT5645_JD_HP_SFT 11 +#define RT5645_JD_HP_DIS (0x0 << 11) +#define RT5645_JD_HP_EN (0x1 << 11) +#define RT5645_JD_HP_TRG_MASK (0x1 << 10) +#define RT5645_JD_HP_TRG_SFT 10 +#define RT5645_JD_HP_TRG_LO (0x0 << 10) +#define RT5645_JD_HP_TRG_HI (0x1 << 10) +#define RT5645_JD_SPL_MASK (0x1 << 9) +#define RT5645_JD_SPL_SFT 9 +#define RT5645_JD_SPL_DIS (0x0 << 9) +#define RT5645_JD_SPL_EN (0x1 << 9) +#define RT5645_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5645_JD_SPL_TRG_SFT 8 +#define RT5645_JD_SPL_TRG_LO (0x0 << 8) +#define RT5645_JD_SPL_TRG_HI (0x1 << 8) +#define RT5645_JD_SPR_MASK (0x1 << 7) +#define RT5645_JD_SPR_SFT 7 +#define RT5645_JD_SPR_DIS (0x0 << 7) +#define RT5645_JD_SPR_EN (0x1 << 7) +#define RT5645_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5645_JD_SPR_TRG_SFT 6 +#define RT5645_JD_SPR_TRG_LO (0x0 << 6) +#define RT5645_JD_SPR_TRG_HI (0x1 << 6) +#define RT5645_JD_MO_MASK (0x1 << 5) +#define RT5645_JD_MO_SFT 5 +#define RT5645_JD_MO_DIS (0x0 << 5) +#define RT5645_JD_MO_EN (0x1 << 5) +#define RT5645_JD_MO_TRG_MASK (0x1 << 4) +#define RT5645_JD_MO_TRG_SFT 4 +#define RT5645_JD_MO_TRG_LO (0x0 << 4) +#define RT5645_JD_MO_TRG_HI (0x1 << 4) +#define RT5645_JD_LO_MASK (0x1 << 3) +#define RT5645_JD_LO_SFT 3 +#define RT5645_JD_LO_DIS (0x0 << 3) +#define RT5645_JD_LO_EN (0x1 << 3) +#define RT5645_JD_LO_TRG_MASK (0x1 << 2) +#define RT5645_JD_LO_TRG_SFT 2 +#define RT5645_JD_LO_TRG_LO (0x0 << 2) +#define RT5645_JD_LO_TRG_HI (0x1 << 2) +#define RT5645_JD1_IN4P_MASK (0x1 << 1) +#define RT5645_JD1_IN4P_SFT 1 +#define RT5645_JD1_IN4P_DIS (0x0 << 1) +#define RT5645_JD1_IN4P_EN (0x1 << 1) +#define RT5645_JD2_IN4N_MASK (0x1) +#define RT5645_JD2_IN4N_SFT 0 +#define RT5645_JD2_IN4N_DIS (0x0) +#define RT5645_JD2_IN4N_EN (0x1) + +/* Jack detect for ANC (0xbc) */ +#define RT5645_ANC_DET_MASK (0x3 << 4) +#define RT5645_ANC_DET_SFT 4 +#define RT5645_ANC_DET_DIS (0x0 << 4) +#define RT5645_ANC_DET_MB1 (0x1 << 4) +#define RT5645_ANC_DET_MB2 (0x2 << 4) +#define RT5645_ANC_DET_JD (0x3 << 4) +#define RT5645_AD_TRG_MASK (0x1 << 3) +#define RT5645_AD_TRG_SFT 3 +#define RT5645_AD_TRG_LO (0x0 << 3) +#define RT5645_AD_TRG_HI (0x1 << 3) +#define RT5645_ANCM_DET_MASK (0x3 << 4) +#define RT5645_ANCM_DET_SFT 4 +#define RT5645_ANCM_DET_DIS (0x0 << 4) +#define RT5645_ANCM_DET_MB1 (0x1 << 4) +#define RT5645_ANCM_DET_MB2 (0x2 << 4) +#define RT5645_ANCM_DET_JD (0x3 << 4) +#define RT5645_AMD_TRG_MASK (0x1 << 3) +#define RT5645_AMD_TRG_SFT 3 +#define RT5645_AMD_TRG_LO (0x0 << 3) +#define RT5645_AMD_TRG_HI (0x1 << 3) + +/* IRQ Control 1 (0xbd) */ +#define RT5645_IRQ_JD_MASK (0x1 << 15) +#define RT5645_IRQ_JD_SFT 15 +#define RT5645_IRQ_JD_BP (0x0 << 15) +#define RT5645_IRQ_JD_NOR (0x1 << 15) +#define RT5645_IRQ_OT_MASK (0x1 << 14) +#define RT5645_IRQ_OT_SFT 14 +#define RT5645_IRQ_OT_BP (0x0 << 14) +#define RT5645_IRQ_OT_NOR (0x1 << 14) +#define RT5645_JD_STKY_MASK (0x1 << 13) +#define RT5645_JD_STKY_SFT 13 +#define RT5645_JD_STKY_DIS (0x0 << 13) +#define RT5645_JD_STKY_EN (0x1 << 13) +#define RT5645_OT_STKY_MASK (0x1 << 12) +#define RT5645_OT_STKY_SFT 12 +#define RT5645_OT_STKY_DIS (0x0 << 12) +#define RT5645_OT_STKY_EN (0x1 << 12) +#define RT5645_JD_P_MASK (0x1 << 11) +#define RT5645_JD_P_SFT 11 +#define RT5645_JD_P_NOR (0x0 << 11) +#define RT5645_JD_P_INV (0x1 << 11) +#define RT5645_OT_P_MASK (0x1 << 10) +#define RT5645_OT_P_SFT 10 +#define RT5645_OT_P_NOR (0x0 << 10) +#define RT5645_OT_P_INV (0x1 << 10) + +/* IRQ Control 2 (0xbe) */ +#define RT5645_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5645_IRQ_MB1_OC_SFT 15 +#define RT5645_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5645_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5645_IRQ_MB2_OC_MASK (0x1 << 14) +#define RT5645_IRQ_MB2_OC_SFT 14 +#define RT5645_IRQ_MB2_OC_BP (0x0 << 14) +#define RT5645_IRQ_MB2_OC_NOR (0x1 << 14) +#define RT5645_MB1_OC_STKY_MASK (0x1 << 13) +#define RT5645_MB1_OC_STKY_SFT 13 +#define RT5645_MB1_OC_STKY_DIS (0x0 << 13) +#define RT5645_MB1_OC_STKY_EN (0x1 << 13) +#define RT5645_MB2_OC_STKY_MASK (0x1 << 12) +#define RT5645_MB2_OC_STKY_SFT 12 +#define RT5645_MB2_OC_STKY_DIS (0x0 << 12) +#define RT5645_MB2_OC_STKY_EN (0x1 << 12) +#define RT5645_MB1_OC_P_MASK (0x1 << 7) +#define RT5645_MB1_OC_P_SFT 7 +#define RT5645_MB1_OC_P_NOR (0x0 << 7) +#define RT5645_MB1_OC_P_INV (0x1 << 7) +#define RT5645_MB2_OC_P_MASK (0x1 << 6) +#define RT5645_MB2_OC_P_SFT 6 +#define RT5645_MB2_OC_P_NOR (0x0 << 6) +#define RT5645_MB2_OC_P_INV (0x1 << 6) +#define RT5645_MB1_OC_CLR (0x1 << 3) +#define RT5645_MB1_OC_CLR_SFT 3 +#define RT5645_MB2_OC_CLR (0x1 << 2) +#define RT5645_MB2_OC_CLR_SFT 2 + +/* GPIO Control 1 (0xc0) */ +#define RT5645_GP1_PIN_MASK (0x1 << 15) +#define RT5645_GP1_PIN_SFT 15 +#define RT5645_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5645_GP1_PIN_IRQ (0x1 << 15) +#define RT5645_GP2_PIN_MASK (0x1 << 14) +#define RT5645_GP2_PIN_SFT 14 +#define RT5645_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5645_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5645_GP3_PIN_MASK (0x3 << 12) +#define RT5645_GP3_PIN_SFT 12 +#define RT5645_GP3_PIN_GPIO3 (0x0 << 12) +#define RT5645_GP3_PIN_DMIC1_SDA (0x1 << 12) +#define RT5645_GP3_PIN_IRQ (0x2 << 12) +#define RT5645_GP4_PIN_MASK (0x1 << 11) +#define RT5645_GP4_PIN_SFT 11 +#define RT5645_GP4_PIN_GPIO4 (0x0 << 11) +#define RT5645_GP4_PIN_DMIC2_SDA (0x1 << 11) +#define RT5645_DP_SIG_MASK (0x1 << 10) +#define RT5645_DP_SIG_SFT 10 +#define RT5645_DP_SIG_TEST (0x0 << 10) +#define RT5645_DP_SIG_AP (0x1 << 10) +#define RT5645_GPIO_M_MASK (0x1 << 9) +#define RT5645_GPIO_M_SFT 9 +#define RT5645_GPIO_M_FLT (0x0 << 9) +#define RT5645_GPIO_M_PH (0x1 << 9) +#define RT5645_I2S2_SEL (0x1 << 8) +#define RT5645_I2S2_SEL_SFT 8 +#define RT5645_GP5_PIN_MASK (0x1 << 7) +#define RT5645_GP5_PIN_SFT 7 +#define RT5645_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5645_GP5_PIN_DMIC1_SDA (0x1 << 7) +#define RT5645_GP6_PIN_MASK (0x1 << 6) +#define RT5645_GP6_PIN_SFT 6 +#define RT5645_GP6_PIN_GPIO6 (0x0 << 6) +#define RT5645_GP6_PIN_DMIC2_SDA (0x1 << 6) +#define RT5645_GP8_PIN_MASK (0x1 << 3) +#define RT5645_GP8_PIN_SFT 3 +#define RT5645_GP8_PIN_GPIO8 (0x0 << 3) +#define RT5645_GP8_PIN_DMIC2_SDA (0x1 << 3) +#define RT5645_GP12_PIN_MASK (0x1 << 2) +#define RT5645_GP12_PIN_SFT 2 +#define RT5645_GP12_PIN_GPIO12 (0x0 << 2) +#define RT5645_GP12_PIN_DMIC2_SDA (0x1 << 2) +#define RT5645_GP11_PIN_MASK (0x1 << 1) +#define RT5645_GP11_PIN_SFT 1 +#define RT5645_GP11_PIN_GPIO11 (0x0 << 1) +#define RT5645_GP11_PIN_DMIC1_SDA (0x1 << 1) +#define RT5645_GP10_PIN_MASK (0x1) +#define RT5645_GP10_PIN_SFT 0 +#define RT5645_GP10_PIN_GPIO10 (0x0) +#define RT5645_GP10_PIN_DMIC2_SDA (0x1) + +/* GPIO Control 3 (0xc2) */ +#define RT5645_GP4_PF_MASK (0x1 << 11) +#define RT5645_GP4_PF_SFT 11 +#define RT5645_GP4_PF_IN (0x0 << 11) +#define RT5645_GP4_PF_OUT (0x1 << 11) +#define RT5645_GP4_OUT_MASK (0x1 << 10) +#define RT5645_GP4_OUT_SFT 10 +#define RT5645_GP4_OUT_LO (0x0 << 10) +#define RT5645_GP4_OUT_HI (0x1 << 10) +#define RT5645_GP4_P_MASK (0x1 << 9) +#define RT5645_GP4_P_SFT 9 +#define RT5645_GP4_P_NOR (0x0 << 9) +#define RT5645_GP4_P_INV (0x1 << 9) +#define RT5645_GP3_PF_MASK (0x1 << 8) +#define RT5645_GP3_PF_SFT 8 +#define RT5645_GP3_PF_IN (0x0 << 8) +#define RT5645_GP3_PF_OUT (0x1 << 8) +#define RT5645_GP3_OUT_MASK (0x1 << 7) +#define RT5645_GP3_OUT_SFT 7 +#define RT5645_GP3_OUT_LO (0x0 << 7) +#define RT5645_GP3_OUT_HI (0x1 << 7) +#define RT5645_GP3_P_MASK (0x1 << 6) +#define RT5645_GP3_P_SFT 6 +#define RT5645_GP3_P_NOR (0x0 << 6) +#define RT5645_GP3_P_INV (0x1 << 6) +#define RT5645_GP2_PF_MASK (0x1 << 5) +#define RT5645_GP2_PF_SFT 5 +#define RT5645_GP2_PF_IN (0x0 << 5) +#define RT5645_GP2_PF_OUT (0x1 << 5) +#define RT5645_GP2_OUT_MASK (0x1 << 4) +#define RT5645_GP2_OUT_SFT 4 +#define RT5645_GP2_OUT_LO (0x0 << 4) +#define RT5645_GP2_OUT_HI (0x1 << 4) +#define RT5645_GP2_P_MASK (0x1 << 3) +#define RT5645_GP2_P_SFT 3 +#define RT5645_GP2_P_NOR (0x0 << 3) +#define RT5645_GP2_P_INV (0x1 << 3) +#define RT5645_GP1_PF_MASK (0x1 << 2) +#define RT5645_GP1_PF_SFT 2 +#define RT5645_GP1_PF_IN (0x0 << 2) +#define RT5645_GP1_PF_OUT (0x1 << 2) +#define RT5645_GP1_OUT_MASK (0x1 << 1) +#define RT5645_GP1_OUT_SFT 1 +#define RT5645_GP1_OUT_LO (0x0 << 1) +#define RT5645_GP1_OUT_HI (0x1 << 1) +#define RT5645_GP1_P_MASK (0x1) +#define RT5645_GP1_P_SFT 0 +#define RT5645_GP1_P_NOR (0x0) +#define RT5645_GP1_P_INV (0x1) + +/* Programmable Register Array Control 1 (0xc8) */ +#define RT5645_REG_SEQ_MASK (0xf << 12) +#define RT5645_REG_SEQ_SFT 12 +#define RT5645_SEQ1_ST_MASK (0x1 << 11) /*RO*/ +#define RT5645_SEQ1_ST_SFT 11 +#define RT5645_SEQ1_ST_RUN (0x0 << 11) +#define RT5645_SEQ1_ST_FIN (0x1 << 11) +#define RT5645_SEQ2_ST_MASK (0x1 << 10) /*RO*/ +#define RT5645_SEQ2_ST_SFT 10 +#define RT5645_SEQ2_ST_RUN (0x0 << 10) +#define RT5645_SEQ2_ST_FIN (0x1 << 10) +#define RT5645_REG_LV_MASK (0x1 << 9) +#define RT5645_REG_LV_SFT 9 +#define RT5645_REG_LV_MX (0x0 << 9) +#define RT5645_REG_LV_PR (0x1 << 9) +#define RT5645_SEQ_2_PT_MASK (0x1 << 8) +#define RT5645_SEQ_2_PT_BIT 8 +#define RT5645_REG_IDX_MASK (0xff) +#define RT5645_REG_IDX_SFT 0 + +/* Programmable Register Array Control 2 (0xc9) */ +#define RT5645_REG_DAT_MASK (0xffff) +#define RT5645_REG_DAT_SFT 0 + +/* Programmable Register Array Control 3 (0xca) */ +#define RT5645_SEQ_DLY_MASK (0xff << 8) +#define RT5645_SEQ_DLY_SFT 8 +#define RT5645_PROG_MASK (0x1 << 7) +#define RT5645_PROG_SFT 7 +#define RT5645_PROG_DIS (0x0 << 7) +#define RT5645_PROG_EN (0x1 << 7) +#define RT5645_SEQ1_PT_RUN (0x1 << 6) +#define RT5645_SEQ1_PT_RUN_BIT 6 +#define RT5645_SEQ2_PT_RUN (0x1 << 5) +#define RT5645_SEQ2_PT_RUN_BIT 5 + +/* Programmable Register Array Control 4 (0xcb) */ +#define RT5645_SEQ1_START_MASK (0xf << 8) +#define RT5645_SEQ1_START_SFT 8 +#define RT5645_SEQ1_END_MASK (0xf) +#define RT5645_SEQ1_END_SFT 0 + +/* Programmable Register Array Control 5 (0xcc) */ +#define RT5645_SEQ2_START_MASK (0xf << 8) +#define RT5645_SEQ2_START_SFT 8 +#define RT5645_SEQ2_END_MASK (0xf) +#define RT5645_SEQ2_END_SFT 0 + +/* Scramble Function (0xcd) */ +#define RT5645_SCB_KEY_MASK (0xff) +#define RT5645_SCB_KEY_SFT 0 + +/* Scramble Control (0xce) */ +#define RT5645_SCB_SWAP_MASK (0x1 << 15) +#define RT5645_SCB_SWAP_SFT 15 +#define RT5645_SCB_SWAP_DIS (0x0 << 15) +#define RT5645_SCB_SWAP_EN (0x1 << 15) +#define RT5645_SCB_MASK (0x1 << 14) +#define RT5645_SCB_SFT 14 +#define RT5645_SCB_DIS (0x0 << 14) +#define RT5645_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5645_BB_MASK (0x1 << 15) +#define RT5645_BB_SFT 15 +#define RT5645_BB_DIS (0x0 << 15) +#define RT5645_BB_EN (0x1 << 15) +#define RT5645_BB_CT_MASK (0x7 << 12) +#define RT5645_BB_CT_SFT 12 +#define RT5645_BB_CT_A (0x0 << 12) +#define RT5645_BB_CT_B (0x1 << 12) +#define RT5645_BB_CT_C (0x2 << 12) +#define RT5645_BB_CT_D (0x3 << 12) +#define RT5645_M_BB_L_MASK (0x1 << 9) +#define RT5645_M_BB_L_SFT 9 +#define RT5645_M_BB_R_MASK (0x1 << 8) +#define RT5645_M_BB_R_SFT 8 +#define RT5645_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5645_M_BB_HPF_L_SFT 7 +#define RT5645_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5645_M_BB_HPF_R_SFT 6 +#define RT5645_G_BB_BST_MASK (0x3f) +#define RT5645_G_BB_BST_SFT 0 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5645_M_MP3_L_MASK (0x1 << 15) +#define RT5645_M_MP3_L_SFT 15 +#define RT5645_M_MP3_R_MASK (0x1 << 14) +#define RT5645_M_MP3_R_SFT 14 +#define RT5645_M_MP3_MASK (0x1 << 13) +#define RT5645_M_MP3_SFT 13 +#define RT5645_M_MP3_DIS (0x0 << 13) +#define RT5645_M_MP3_EN (0x1 << 13) +#define RT5645_EG_MP3_MASK (0x1f << 8) +#define RT5645_EG_MP3_SFT 8 +#define RT5645_MP3_HLP_MASK (0x1 << 7) +#define RT5645_MP3_HLP_SFT 7 +#define RT5645_MP3_HLP_DIS (0x0 << 7) +#define RT5645_MP3_HLP_EN (0x1 << 7) +#define RT5645_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5645_M_MP3_ORG_L_SFT 6 +#define RT5645_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5645_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5645_MP3_WT_MASK (0x1 << 13) +#define RT5645_MP3_WT_SFT 13 +#define RT5645_MP3_WT_1_4 (0x0 << 13) +#define RT5645_MP3_WT_1_2 (0x1 << 13) +#define RT5645_OG_MP3_MASK (0x1f << 8) +#define RT5645_OG_MP3_SFT 8 +#define RT5645_HG_MP3_MASK (0x3f) +#define RT5645_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5645_3D_CF_MASK (0x1 << 15) +#define RT5645_3D_CF_SFT 15 +#define RT5645_3D_CF_DIS (0x0 << 15) +#define RT5645_3D_CF_EN (0x1 << 15) +#define RT5645_3D_HP_MASK (0x1 << 14) +#define RT5645_3D_HP_SFT 14 +#define RT5645_3D_HP_DIS (0x0 << 14) +#define RT5645_3D_HP_EN (0x1 << 14) +#define RT5645_3D_BT_MASK (0x1 << 13) +#define RT5645_3D_BT_SFT 13 +#define RT5645_3D_BT_DIS (0x0 << 13) +#define RT5645_3D_BT_EN (0x1 << 13) +#define RT5645_3D_1F_MIX_MASK (0x3 << 11) +#define RT5645_3D_1F_MIX_SFT 11 +#define RT5645_3D_HP_M_MASK (0x1 << 10) +#define RT5645_3D_HP_M_SFT 10 +#define RT5645_3D_HP_M_SUR (0x0 << 10) +#define RT5645_3D_HP_M_FRO (0x1 << 10) +#define RT5645_M_3D_HRTF_MASK (0x1 << 9) +#define RT5645_M_3D_HRTF_SFT 9 +#define RT5645_M_3D_D2H_MASK (0x1 << 8) +#define RT5645_M_3D_D2H_SFT 8 +#define RT5645_M_3D_D2R_MASK (0x1 << 7) +#define RT5645_M_3D_D2R_SFT 7 +#define RT5645_M_3D_REVB_MASK (0x1 << 6) +#define RT5645_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5645_2ND_HPF_MASK (0x1 << 15) +#define RT5645_2ND_HPF_SFT 15 +#define RT5645_2ND_HPF_DIS (0x0 << 15) +#define RT5645_2ND_HPF_EN (0x1 << 15) +#define RT5645_HPF_CF_L_MASK (0x7 << 12) +#define RT5645_HPF_CF_L_SFT 12 +#define RT5645_1ST_HPF_MASK (0x1 << 11) +#define RT5645_1ST_HPF_SFT 11 +#define RT5645_1ST_HPF_DIS (0x0 << 11) +#define RT5645_1ST_HPF_EN (0x1 << 11) +#define RT5645_HPF_CF_R_MASK (0x7 << 8) +#define RT5645_HPF_CF_R_SFT 8 +#define RT5645_ZD_T_MASK (0x3 << 6) +#define RT5645_ZD_T_SFT 6 +#define RT5645_ZD_F_MASK (0x3 << 4) +#define RT5645_ZD_F_SFT 4 +#define RT5645_ZD_F_IM (0x0 << 4) +#define RT5645_ZD_F_ZC_IM (0x1 << 4) +#define RT5645_ZD_F_ZC_IOD (0x2 << 4) +#define RT5645_ZD_F_UN (0x3 << 4) + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5645_SI_DAC_MASK (0x1 << 11) +#define RT5645_SI_DAC_SFT 11 +#define RT5645_SI_DAC_AUTO (0x0 << 11) +#define RT5645_SI_DAC_TEST (0x1 << 11) +#define RT5645_DC_CAL_M_MASK (0x1 << 10) +#define RT5645_DC_CAL_M_SFT 10 +#define RT5645_DC_CAL_M_CAL (0x0 << 10) +#define RT5645_DC_CAL_M_NOR (0x1 << 10) +#define RT5645_DC_CAL_MASK (0x1 << 9) +#define RT5645_DC_CAL_SFT 9 +#define RT5645_DC_CAL_DIS (0x0 << 9) +#define RT5645_DC_CAL_EN (0x1 << 9) +#define RT5645_HPD_RCV_MASK (0x7 << 6) +#define RT5645_HPD_RCV_SFT 6 +#define RT5645_HPD_PS_MASK (0x1 << 5) +#define RT5645_HPD_PS_SFT 5 +#define RT5645_HPD_PS_DIS (0x0 << 5) +#define RT5645_HPD_PS_EN (0x1 << 5) +#define RT5645_CAL_M_MASK (0x1 << 4) +#define RT5645_CAL_M_SFT 4 +#define RT5645_CAL_M_DEP (0x0 << 4) +#define RT5645_CAL_M_CAL (0x1 << 4) +#define RT5645_CAL_MASK (0x1 << 3) +#define RT5645_CAL_SFT 3 +#define RT5645_CAL_DIS (0x0 << 3) +#define RT5645_CAL_EN (0x1 << 3) +#define RT5645_CAL_TEST_MASK (0x1 << 2) +#define RT5645_CAL_TEST_SFT 2 +#define RT5645_CAL_TEST_DIS (0x0 << 2) +#define RT5645_CAL_TEST_EN (0x1 << 2) +#define RT5645_CAL_P_MASK (0x3) +#define RT5645_CAL_P_SFT 0 +#define RT5645_CAL_P_NONE (0x0) +#define RT5645_CAL_P_CAL (0x1) +#define RT5645_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5645_SV_MASK (0x1 << 15) +#define RT5645_SV_SFT 15 +#define RT5645_SV_DIS (0x0 << 15) +#define RT5645_SV_EN (0x1 << 15) +#define RT5645_SPO_SV_MASK (0x1 << 14) +#define RT5645_SPO_SV_SFT 14 +#define RT5645_SPO_SV_DIS (0x0 << 14) +#define RT5645_SPO_SV_EN (0x1 << 14) +#define RT5645_OUT_SV_MASK (0x1 << 13) +#define RT5645_OUT_SV_SFT 13 +#define RT5645_OUT_SV_DIS (0x0 << 13) +#define RT5645_OUT_SV_EN (0x1 << 13) +#define RT5645_HP_SV_MASK (0x1 << 12) +#define RT5645_HP_SV_SFT 12 +#define RT5645_HP_SV_DIS (0x0 << 12) +#define RT5645_HP_SV_EN (0x1 << 12) +#define RT5645_ZCD_DIG_MASK (0x1 << 11) +#define RT5645_ZCD_DIG_SFT 11 +#define RT5645_ZCD_DIG_DIS (0x0 << 11) +#define RT5645_ZCD_DIG_EN (0x1 << 11) +#define RT5645_ZCD_MASK (0x1 << 10) +#define RT5645_ZCD_SFT 10 +#define RT5645_ZCD_PD (0x0 << 10) +#define RT5645_ZCD_PU (0x1 << 10) +#define RT5645_M_ZCD_MASK (0x3f << 4) +#define RT5645_M_ZCD_SFT 4 +#define RT5645_M_ZCD_RM_L (0x1 << 9) +#define RT5645_M_ZCD_RM_R (0x1 << 8) +#define RT5645_M_ZCD_SM_L (0x1 << 7) +#define RT5645_M_ZCD_SM_R (0x1 << 6) +#define RT5645_M_ZCD_OM_L (0x1 << 5) +#define RT5645_M_ZCD_OM_R (0x1 << 4) +#define RT5645_SV_DLY_MASK (0xf) +#define RT5645_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5645_ZCD_HP_MASK (0x1 << 15) +#define RT5645_ZCD_HP_SFT 15 +#define RT5645_ZCD_HP_DIS (0x0 << 15) +#define RT5645_ZCD_HP_EN (0x1 << 15) + + +/* Codec Private Register definition */ +/* 3D Speaker Control (0x63) */ +#define RT5645_3D_SPK_MASK (0x1 << 15) +#define RT5645_3D_SPK_SFT 15 +#define RT5645_3D_SPK_DIS (0x0 << 15) +#define RT5645_3D_SPK_EN (0x1 << 15) +#define RT5645_3D_SPK_M_MASK (0x3 << 13) +#define RT5645_3D_SPK_M_SFT 13 +#define RT5645_3D_SPK_CG_MASK (0x1f << 8) +#define RT5645_3D_SPK_CG_SFT 8 +#define RT5645_3D_SPK_SG_MASK (0x1f) +#define RT5645_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5645_WND_MASK (0x1 << 15) +#define RT5645_WND_SFT 15 +#define RT5645_WND_DIS (0x0 << 15) +#define RT5645_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5645_WND_FC_NW_MASK (0x3f << 10) +#define RT5645_WND_FC_NW_SFT 10 +#define RT5645_WND_FC_WK_MASK (0x3f << 4) +#define RT5645_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5645_HPF_FC_MASK (0x3f << 6) +#define RT5645_HPF_FC_SFT 6 +#define RT5645_WND_FC_ST_MASK (0x3f) +#define RT5645_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5645_WND_TH_LO_MASK (0x3ff) +#define RT5645_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5645_WND_TH_HI_MASK (0x3ff) +#define RT5645_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5645_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5645_WND_WIND_SFT 13 +#define RT5645_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5645_WND_STRONG_SFT 12 +enum { + RT5645_NO_WIND, + RT5645_BREEZE, + RT5645_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5645_DP_ATT_MASK (0x3 << 14) +#define RT5645_DP_ATT_SFT 14 +#define RT5645_DP_SPK_MASK (0x1 << 10) +#define RT5645_DP_SPK_SFT 10 +#define RT5645_DP_SPK_DIS (0x0 << 10) +#define RT5645_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5645_EQ_PRE_VOL_MASK (0xffff) +#define RT5645_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5645_EQ_PST_VOL_MASK (0xffff) +#define RT5645_EQ_PST_VOL_SFT 0 + +/* Jack Detect Control 3 (0xf8) */ +#define RT5645_CMP_MIC_IN_DET_MASK (0x7 << 12) +#define RT5645_JD_CBJ_EN (0x1 << 7) +#define RT5645_JD_CBJ_POL (0x1 << 6) +#define RT5645_JD_TRI_CBJ_SEL_MASK (0x7 << 3) +#define RT5645_JD_TRI_CBJ_SEL_SFT (3) +#define RT5645_JD_TRI_HPO_SEL_MASK (0x7) +#define RT5645_JD_TRI_HPO_SEL_SFT (0) +#define RT5645_JD_F_GPIO_JD1 (0x0) +#define RT5645_JD_F_JD1_1 (0x1) +#define RT5645_JD_F_JD1_2 (0x2) +#define RT5645_JD_F_JD2 (0x3) +#define RT5645_JD_F_JD3 (0x4) +#define RT5645_JD_F_GPIO_JD2 (0x5) +#define RT5645_JD_F_MX0B_12 (0x6) + +/* Digital Misc Control (0xfa) */ +#define RT5645_RST_DSP (0x1 << 13) +#define RT5645_IF1_ADC1_IN1_SEL (0x1 << 12) +#define RT5645_IF1_ADC1_IN1_SFT 12 +#define RT5645_IF1_ADC1_IN2_SEL (0x1 << 11) +#define RT5645_IF1_ADC1_IN2_SFT 11 +#define RT5645_IF1_ADC2_IN1_SEL (0x1 << 10) +#define RT5645_IF1_ADC2_IN1_SFT 10 +#define RT5645_DIG_GATE_CTRL 0x1 + +/* General Control2 (0xfb) */ +#define RT5645_RXDC_SRC_MASK (0x1 << 7) +#define RT5645_RXDC_SRC_STO (0x0 << 7) +#define RT5645_RXDC_SRC_MONO (0x1 << 7) +#define RT5645_RXDC_SRC_SFT (7) +#define RT5645_RXDP2_SEL_MASK (0x1 << 3) +#define RT5645_RXDP2_SEL_IF2 (0x0 << 3) +#define RT5645_RXDP2_SEL_ADC (0x1 << 3) +#define RT5645_RXDP2_SEL_SFT (3) + + +/* Vendor ID (0xfd) */ +#define RT5645_VER_C 0x2 +#define RT5645_VER_D 0x3 + + +/* Volume Rescale */ +#define RT5645_VOL_RSCL_MAX 0x27 +#define RT5645_VOL_RSCL_RANGE 0x1F +/* Debug String Length */ +#define RT5645_REG_DISP_LEN 23 + + +/* System Clock Source */ +enum { + RT5645_SCLK_S_MCLK, + RT5645_SCLK_S_PLL1, + RT5645_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5645_PLL1_S_MCLK, + RT5645_PLL1_S_BCLK1, + RT5645_PLL1_S_BCLK2, +}; + +enum { + RT5645_AIF1, + RT5645_AIF2, + RT5645_AIFS, +}; + +enum { + RT5645_DMIC_DATA_IN2P, + RT5645_DMIC_DATA_GPIO6, + RT5645_DMIC_DATA_GPIO10, + RT5645_DMIC_DATA_GPIO12, +}; + +enum { + RT5645_DMIC_DATA_IN2N, + RT5645_DMIC_DATA_GPIO5, + RT5645_DMIC_DATA_GPIO11, +}; + +struct rt5645_pll_code { + bool m_bp; /* Indicates bypass m code or not. */ + int m_code; + int n_code; + int k_code; +}; + +struct rt5645_priv { + struct snd_soc_codec *codec; + struct rt5645_platform_data pdata; + struct regmap *regmap; + + int sysclk; + int sysclk_src; + int lrck[RT5645_AIFS]; + int bclk[RT5645_AIFS]; + int master[RT5645_AIFS]; + + int pll_src; + int pll_in; + int pll_out; +}; + +#endif /* __RT5645_H__ */ -- cgit v1.2.3 From 1e77d0a1ed7417d2a5a52a7b8d32aea1833faa6c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 7 Mar 2013 14:53:45 +0100 Subject: genirq: Sanitize spurious interrupt detection of threaded irqs Till reported that the spurious interrupt detection of threaded interrupts is broken in two ways: - note_interrupt() is called for each action thread of a shared interrupt line. That's wrong as we are only interested whether none of the device drivers felt responsible for the interrupt, but by calling multiple times for a single interrupt line we account IRQ_NONE even if one of the drivers felt responsible. - note_interrupt() when called from the thread handler is not serialized. That leaves the members of irq_desc which are used for the spurious detection unprotected. To solve this we need to defer the spurious detection of a threaded interrupt to the next hardware interrupt context where we have implicit serialization. If note_interrupt is called with action_ret == IRQ_WAKE_THREAD, we check whether the previous interrupt requested a deferred check. If not, we request a deferred check for the next hardware interrupt and return. If set, we check whether one of the interrupt threads signaled success. Depending on this information we feed the result into the spurious detector. If one primary handler of a shared interrupt returns IRQ_HANDLED we disable the deferred check of irq threads on the same line, as we have found at least one device driver who cared. Reported-by: Till Straumann Signed-off-by: Thomas Gleixner Tested-by: Austin Schuh Cc: Oliver Hartkopp Cc: Wolfgang Grandegger Cc: Pavel Pisa Cc: Marc Kleine-Budde Cc: linux-can@vger.kernel.org Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/alpine.LFD.2.02.1303071450130.22263@ionos --- include/linux/irqdesc.h | 4 ++ kernel/irq/manage.c | 4 +- kernel/irq/spurious.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 26e2661d3935..472c021a2d4f 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -27,6 +27,8 @@ struct irq_desc; * @irq_count: stats field to detect stalled irqs * @last_unhandled: aging timer for unhandled count * @irqs_unhandled: stats field for spurious unhandled interrupts + * @threads_handled: stats field for deferred spurious detection of threaded handlers + * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers * @lock: locking for SMP * @affinity_hint: hint to user space for preferred irq affinity * @affinity_notify: context for notification of affinity changes @@ -52,6 +54,8 @@ struct irq_desc { unsigned int irq_count; /* For detecting broken IRQs */ unsigned long last_unhandled; /* Aging timer for unhandled count */ unsigned int irqs_unhandled; + atomic_t threads_handled; + int threads_handled_last; raw_spinlock_t lock; struct cpumask *percpu_enabled; #ifdef CONFIG_SMP diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index d34131ca372b..3dc6a61bf06a 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -886,8 +886,8 @@ static int irq_thread(void *data) irq_thread_check_affinity(desc, action); action_ret = handler_fn(desc, action); - if (!noirqdebug) - note_interrupt(action->irq, desc, action_ret); + if (action_ret == IRQ_HANDLED) + atomic_inc(&desc->threads_handled); wake_threads_waitq(desc); } diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index a1d8cc63b56e..e2514b0e439e 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -270,6 +270,8 @@ try_misrouted_irq(unsigned int irq, struct irq_desc *desc, return action && (action->flags & IRQF_IRQPOLL); } +#define SPURIOUS_DEFERRED 0x80000000 + void note_interrupt(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret) { @@ -277,15 +279,111 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc, irq_settings_is_polled(desc)) return; - /* we get here again via the threaded handler */ - if (action_ret == IRQ_WAKE_THREAD) - return; - if (bad_action_ret(action_ret)) { report_bad_irq(irq, desc, action_ret); return; } + /* + * We cannot call note_interrupt from the threaded handler + * because we need to look at the compound of all handlers + * (primary and threaded). Aside of that in the threaded + * shared case we have no serialization against an incoming + * hardware interrupt while we are dealing with a threaded + * result. + * + * So in case a thread is woken, we just note the fact and + * defer the analysis to the next hardware interrupt. + * + * The threaded handlers store whether they sucessfully + * handled an interrupt and we check whether that number + * changed versus the last invocation. + * + * We could handle all interrupts with the delayed by one + * mechanism, but for the non forced threaded case we'd just + * add pointless overhead to the straight hardirq interrupts + * for the sake of a few lines less code. + */ + if (action_ret & IRQ_WAKE_THREAD) { + /* + * There is a thread woken. Check whether one of the + * shared primary handlers returned IRQ_HANDLED. If + * not we defer the spurious detection to the next + * interrupt. + */ + if (action_ret == IRQ_WAKE_THREAD) { + int handled; + /* + * We use bit 31 of thread_handled_last to + * denote the deferred spurious detection + * active. No locking necessary as + * thread_handled_last is only accessed here + * and we have the guarantee that hard + * interrupts are not reentrant. + */ + if (!(desc->threads_handled_last & SPURIOUS_DEFERRED)) { + desc->threads_handled_last |= SPURIOUS_DEFERRED; + return; + } + /* + * Check whether one of the threaded handlers + * returned IRQ_HANDLED since the last + * interrupt happened. + * + * For simplicity we just set bit 31, as it is + * set in threads_handled_last as well. So we + * avoid extra masking. And we really do not + * care about the high bits of the handled + * count. We just care about the count being + * different than the one we saw before. + */ + handled = atomic_read(&desc->threads_handled); + handled |= SPURIOUS_DEFERRED; + if (handled != desc->threads_handled_last) { + action_ret = IRQ_HANDLED; + /* + * Note: We keep the SPURIOUS_DEFERRED + * bit set. We are handling the + * previous invocation right now. + * Keep it for the current one, so the + * next hardware interrupt will + * account for it. + */ + desc->threads_handled_last = handled; + } else { + /* + * None of the threaded handlers felt + * responsible for the last interrupt + * + * We keep the SPURIOUS_DEFERRED bit + * set in threads_handled_last as we + * need to account for the current + * interrupt as well. + */ + action_ret = IRQ_NONE; + } + } else { + /* + * One of the primary handlers returned + * IRQ_HANDLED. So we don't care about the + * threaded handlers on the same line. Clear + * the deferred detection bit. + * + * In theory we could/should check whether the + * deferred bit is set and take the result of + * the previous run into account here as + * well. But it's really not worth the + * trouble. If every other interrupt is + * handled we never trigger the spurious + * detector. And if this is just the one out + * of 100k unhandled ones which is handled + * then we merily delay the spurious detection + * by one hard interrupt. Not a real problem. + */ + desc->threads_handled_last &= ~SPURIOUS_DEFERRED; + } + } + if (unlikely(action_ret == IRQ_NONE)) { /* * If we are seeing only the odd spurious IRQ caused by -- cgit v1.2.3 From 249015515fe3fc9818d86cb5c83bbc92505ad7dc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 2 May 2014 21:18:05 -0700 Subject: tcp: remove in_flight parameter from cong_avoid() methods Commit e114a710aa505 ("tcp: fix cwnd limited checking to improve congestion control") obsoleted in_flight parameter from tcp_is_cwnd_limited() and its callers. This patch does the removal as promised. Signed-off-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 8 +++----- net/ipv4/tcp_bic.c | 5 ++--- net/ipv4/tcp_cong.c | 4 ++-- net/ipv4/tcp_cubic.c | 5 ++--- net/ipv4/tcp_highspeed.c | 4 ++-- net/ipv4/tcp_htcp.c | 4 ++-- net/ipv4/tcp_hybla.c | 7 +++---- net/ipv4/tcp_illinois.c | 5 ++--- net/ipv4/tcp_input.c | 9 ++++----- net/ipv4/tcp_lp.c | 5 ++--- net/ipv4/tcp_output.c | 2 +- net/ipv4/tcp_scalable.c | 5 ++--- net/ipv4/tcp_vegas.c | 7 +++---- net/ipv4/tcp_veno.c | 9 ++++----- net/ipv4/tcp_yeah.c | 5 ++--- 15 files changed, 36 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index a9fe7bc4f4bb..3c9418456640 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -796,7 +796,7 @@ struct tcp_congestion_ops { /* return slow start threshold (required) */ u32 (*ssthresh)(struct sock *sk); /* do new cwnd calculation (required) */ - void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked, u32 in_flight); + void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked); /* call before changing ca_state (optional) */ void (*set_state)(struct sock *sk, u8 new_state); /* call when cwnd event occurs (optional) */ @@ -828,7 +828,7 @@ void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w); extern struct tcp_congestion_ops tcp_init_congestion_ops; u32 tcp_reno_ssthresh(struct sock *sk); -void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight); +void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked); extern struct tcp_congestion_ops tcp_reno; static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state) @@ -986,10 +986,8 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp) * risks 100% overshoot. The advantage is that we discourage application to * either send more filler packets or data to artificially blow up the cwnd * usage, and allow application-limited process to probe bw more aggressively. - * - * TODO: remove in_flight once we can fix all callers, and their callers... */ -static inline bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight) +static inline bool tcp_is_cwnd_limited(const struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index 821846fb0a7e..d5de69bc04f5 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -140,13 +140,12 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) ca->cnt = 1; } -static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; if (tp->snd_cwnd <= tp->snd_ssthresh) diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index a93b41ba05ff..7b09d8b49fa5 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -317,11 +317,11 @@ EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai); /* This is Jacobson's slow start and congestion avoidance. * SIGCOMM '88, p. 328. */ -void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) +void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; /* In "safe" area, increase. */ diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 8bf224516ba2..ba2a4f3a6a1e 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -304,13 +304,12 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) ca->cnt = 1; } -static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; if (tp->snd_cwnd <= tp->snd_ssthresh) { diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c index 8b9e7bad77c0..1c4908280d92 100644 --- a/net/ipv4/tcp_highspeed.c +++ b/net/ipv4/tcp_highspeed.c @@ -109,12 +109,12 @@ static void hstcp_init(struct sock *sk) tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128); } -static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) +static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct hstcp *ca = inet_csk_ca(sk); - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; if (tp->snd_cwnd <= tp->snd_ssthresh) diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c index 4a194acfd923..031361311a8b 100644 --- a/net/ipv4/tcp_htcp.c +++ b/net/ipv4/tcp_htcp.c @@ -227,12 +227,12 @@ static u32 htcp_recalc_ssthresh(struct sock *sk) return max((tp->snd_cwnd * ca->beta) >> 7, 2U); } -static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) +static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct htcp *ca = inet_csk_ca(sk); - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; if (tp->snd_cwnd <= tp->snd_ssthresh) diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c index a15a799bf768..d8f8f05a4951 100644 --- a/net/ipv4/tcp_hybla.c +++ b/net/ipv4/tcp_hybla.c @@ -87,8 +87,7 @@ static inline u32 hybla_fraction(u32 odds) * o Give cwnd a new value based on the model proposed * o remember increments <1 */ -static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct hybla *ca = inet_csk_ca(sk); @@ -101,11 +100,11 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked, ca->minrtt_us = tp->srtt_us; } - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; if (!ca->hybla_en) { - tcp_reno_cong_avoid(sk, ack, acked, in_flight); + tcp_reno_cong_avoid(sk, ack, acked); return; } diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index 863d105e3015..5999b3972e64 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -255,8 +255,7 @@ static void tcp_illinois_state(struct sock *sk, u8 new_state) /* * Increase window in response to successful acknowledgment. */ -static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct illinois *ca = inet_csk_ca(sk); @@ -265,7 +264,7 @@ static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked, update_params(sk); /* RFC2861 only increase cwnd if fully utilized */ - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; /* In slow start */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 6efed134ab63..350b2072f0ab 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2938,10 +2938,11 @@ static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp) tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L); } -static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) +static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { const struct inet_connection_sock *icsk = inet_csk(sk); - icsk->icsk_ca_ops->cong_avoid(sk, ack, acked, in_flight); + + icsk->icsk_ca_ops->cong_avoid(sk, ack, acked); tcp_sk(sk)->snd_cwnd_stamp = tcp_time_stamp; } @@ -3364,7 +3365,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) u32 ack_seq = TCP_SKB_CB(skb)->seq; u32 ack = TCP_SKB_CB(skb)->ack_seq; bool is_dupack = false; - u32 prior_in_flight; u32 prior_fackets; int prior_packets = tp->packets_out; const int prior_unsacked = tp->packets_out - tp->sacked_out; @@ -3397,7 +3397,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) flag |= FLAG_SND_UNA_ADVANCED; prior_fackets = tp->fackets_out; - prior_in_flight = tcp_packets_in_flight(tp); /* ts_recent update must be made after we are sure that the packet * is in window. @@ -3452,7 +3451,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) /* Advance cwnd if state allows */ if (tcp_may_raise_cwnd(sk, flag)) - tcp_cong_avoid(sk, ack, acked, prior_in_flight); + tcp_cong_avoid(sk, ack, acked); if (tcp_ack_is_dubious(sk, flag)) { is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP)); diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c index c9aecae31327..1e70fa8fa793 100644 --- a/net/ipv4/tcp_lp.c +++ b/net/ipv4/tcp_lp.c @@ -115,13 +115,12 @@ static void tcp_lp_init(struct sock *sk) * Will only call newReno CA when away from inference. * From TCP-LP's paper, this will be handled in additive increasement. */ -static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct lp *lp = inet_csk_ca(sk); if (!(lp->flag & LP_WITHIN_INF)) - tcp_reno_cong_avoid(sk, ack, acked, in_flight); + tcp_reno_cong_avoid(sk, ack, acked); } /** diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f9181a133462..89277a34f2c9 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1408,7 +1408,7 @@ static void tcp_cwnd_validate(struct sock *sk, u32 unsent_segs) tp->lsnd_pending = tp->packets_out + unsent_segs; - if (tcp_is_cwnd_limited(sk, 0)) { + if (tcp_is_cwnd_limited(sk)) { /* Network is feed fully. */ tp->snd_cwnd_used = 0; tp->snd_cwnd_stamp = tcp_time_stamp; diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c index 0ac50836da4d..8250949b8853 100644 --- a/net/ipv4/tcp_scalable.c +++ b/net/ipv4/tcp_scalable.c @@ -15,12 +15,11 @@ #define TCP_SCALABLE_AI_CNT 50U #define TCP_SCALABLE_MD_SCALE 3 -static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; if (tp->snd_cwnd <= tp->snd_ssthresh) diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index 48539fff6357..9a5e05f27f4f 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -163,14 +163,13 @@ static inline u32 tcp_vegas_ssthresh(struct tcp_sock *tp) return min(tp->snd_ssthresh, tp->snd_cwnd-1); } -static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct vegas *vegas = inet_csk_ca(sk); if (!vegas->doing_vegas_now) { - tcp_reno_cong_avoid(sk, ack, acked, in_flight); + tcp_reno_cong_avoid(sk, ack, acked); return; } @@ -195,7 +194,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked, /* We don't have enough RTT samples to do the Vegas * calculation, so we'll behave like Reno. */ - tcp_reno_cong_avoid(sk, ack, acked, in_flight); + tcp_reno_cong_avoid(sk, ack, acked); } else { u32 rtt, diff; u64 target_cwnd; diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c index 1b8e28fcd7e1..27b9825753d1 100644 --- a/net/ipv4/tcp_veno.c +++ b/net/ipv4/tcp_veno.c @@ -114,19 +114,18 @@ static void tcp_veno_cwnd_event(struct sock *sk, enum tcp_ca_event event) tcp_veno_init(sk); } -static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct veno *veno = inet_csk_ca(sk); if (!veno->doing_veno_now) { - tcp_reno_cong_avoid(sk, ack, acked, in_flight); + tcp_reno_cong_avoid(sk, ack, acked); return; } /* limited by applications */ - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; /* We do the Veno calculations only if we got enough rtt samples */ @@ -134,7 +133,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked, /* We don't have enough rtt samples to do the Veno * calculation, so we'll behave like Reno. */ - tcp_reno_cong_avoid(sk, ack, acked, in_flight); + tcp_reno_cong_avoid(sk, ack, acked); } else { u64 target_cwnd; u32 rtt; diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index 5ede0e727945..599b79b8eac0 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -69,13 +69,12 @@ static void tcp_yeah_pkts_acked(struct sock *sk, u32 pkts_acked, s32 rtt_us) tcp_vegas_pkts_acked(sk, pkts_acked, rtt_us); } -static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked, - u32 in_flight) +static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); struct yeah *yeah = inet_csk_ca(sk); - if (!tcp_is_cwnd_limited(sk, in_flight)) + if (!tcp_is_cwnd_limited(sk)) return; if (tp->snd_cwnd <= tp->snd_ssthresh) -- cgit v1.2.3 From d3ba720dd58cdf6630fee4b89482c465d5ad0d0f Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Tue, 8 Apr 2014 18:45:53 -0700 Subject: Drivers: hv: Eliminate the channel spinlock in the callback path By ensuring that we set the callback handler to NULL in the channel close path on the same CPU that the channel is bound to, we can eliminate this lock acquisition and release in a performance critical path. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 16 ++++++++++++---- drivers/hv/channel_mgmt.c | 11 +++++++---- drivers/hv/connection.c | 11 ++++------- include/linux/hyperv.h | 2 ++ 4 files changed, 25 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 602ca86a6488..740edec161bb 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -471,18 +471,26 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) } EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); +static void reset_channel_cb(void *arg) +{ + struct vmbus_channel *channel = arg; + + channel->onchannel_callback = NULL; +} + static void vmbus_close_internal(struct vmbus_channel *channel) { struct vmbus_channel_close_channel *msg; int ret; - unsigned long flags; channel->state = CHANNEL_OPEN_STATE; channel->sc_creation_callback = NULL; /* Stop callback and cancel the timer asap */ - spin_lock_irqsave(&channel->inbound_lock, flags); - channel->onchannel_callback = NULL; - spin_unlock_irqrestore(&channel->inbound_lock, flags); + if (channel->target_cpu != smp_processor_id()) + smp_call_function_single(channel->target_cpu, reset_channel_cb, + channel, true); + else + reset_channel_cb(channel); /* Send a closing message */ diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index fa920469bf10..6f7fdd9a7e77 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -365,7 +365,7 @@ static u32 next_vp; * performance critical channels (IDE, SCSI and Network) will be uniformly * distributed across all available CPUs. */ -static u32 get_vp_index(uuid_le *type_guid) +static void init_vp_index(struct vmbus_channel *channel, uuid_le *type_guid) { u32 cur_cpu; int i; @@ -387,10 +387,13 @@ static u32 get_vp_index(uuid_le *type_guid) * Also if the channel is not a performance critical * channel, bind it to cpu 0. */ - return 0; + channel->target_cpu = 0; + channel->target_vp = 0; + return; } cur_cpu = (++next_vp % max_cpus); - return hv_context.vp_index[cur_cpu]; + channel->target_cpu = cur_cpu; + channel->target_vp = hv_context.vp_index[cur_cpu]; } /* @@ -438,7 +441,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) offer->connection_id; } - newchannel->target_vp = get_vp_index(&offer->offer.if_type); + init_vp_index(newchannel, &offer->offer.if_type); memcpy(&newchannel->offermsg, offer, sizeof(struct vmbus_channel_offer_channel)); diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 2e7801af466e..df2363ea017f 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -277,7 +277,6 @@ struct vmbus_channel *relid2channel(u32 relid) static void process_chn_event(u32 relid) { struct vmbus_channel *channel; - unsigned long flags; void *arg; bool read_state; u32 bytes_to_read; @@ -296,13 +295,12 @@ static void process_chn_event(u32 relid) /* * A channel once created is persistent even when there * is no driver handling the device. An unloading driver - * sets the onchannel_callback to NULL under the - * protection of the channel inbound_lock. Thus, checking - * and invoking the driver specific callback takes care of - * orderly unloading of the driver. + * sets the onchannel_callback to NULL on the same CPU + * as where this interrupt is handled (in an interrupt context). + * Thus, checking and invoking the driver specific callback takes + * care of orderly unloading of the driver. */ - spin_lock_irqsave(&channel->inbound_lock, flags); if (channel->onchannel_callback != NULL) { arg = channel->channel_callback_context; read_state = channel->batched_reading; @@ -327,7 +325,6 @@ static void process_chn_event(u32 relid) pr_err("no channel callback for relid - %u\n", relid); } - spin_unlock_irqrestore(&channel->inbound_lock, flags); } /* diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 2d7b4f139c32..a274e089df78 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -696,6 +696,8 @@ struct vmbus_channel { * preserve the earlier behavior. */ u32 target_vp; + /* The corresponding CPUID in the guest */ + u32 target_cpu; /* * Support for sub-channels. For high performance devices, * it will be useful to have multiple sub-channels to support -- cgit v1.2.3 From 3a28fa35d6658703cd26f9c16aaea0eae06afd40 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Tue, 8 Apr 2014 18:45:54 -0700 Subject: Drivers: hv: vmbus: Implement per-CPU mapping of relid to channel Currently the mapping of the relID to channel is done under the protection of a single spin lock. Starting with ws2012, each channel is bound to a specific VCPU in the guest. Use this binding to eliminate the spin lock by setting up per-cpu state for mapping relId to the channel. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 41 ++++++++++++++++++++++++++++++++++++++++- drivers/hv/connection.c | 24 +++++++++++++++++++++++- drivers/hv/hv.c | 2 ++ drivers/hv/hyperv_vmbus.h | 5 +++++ include/linux/hyperv.h | 5 +++++ 5 files changed, 75 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 6f7fdd9a7e77..6c8b032cacba 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -149,6 +149,7 @@ static struct vmbus_channel *alloc_channel(void) spin_lock_init(&channel->sc_lock); INIT_LIST_HEAD(&channel->sc_list); + INIT_LIST_HEAD(&channel->percpu_list); channel->controlwq = create_workqueue("hv_vmbus_ctl"); if (!channel->controlwq) { @@ -188,7 +189,20 @@ static void free_channel(struct vmbus_channel *channel) queue_work(vmbus_connection.work_queue, &channel->work); } +static void percpu_channel_enq(void *arg) +{ + struct vmbus_channel *channel = arg; + int cpu = smp_processor_id(); + + list_add_tail(&channel->percpu_list, &hv_context.percpu_list[cpu]); +} +static void percpu_channel_deq(void *arg) +{ + struct vmbus_channel *channel = arg; + + list_del(&channel->percpu_list); +} /* * vmbus_process_rescind_offer - @@ -210,6 +224,12 @@ static void vmbus_process_rescind_offer(struct work_struct *work) msg.header.msgtype = CHANNELMSG_RELID_RELEASED; vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); + if (channel->target_cpu != smp_processor_id()) + smp_call_function_single(channel->target_cpu, + percpu_channel_deq, channel, true); + else + percpu_channel_deq(channel); + if (channel->primary_channel == NULL) { spin_lock_irqsave(&vmbus_connection.channel_lock, flags); list_del(&channel->listentry); @@ -245,6 +265,7 @@ static void vmbus_process_offer(struct work_struct *work) work); struct vmbus_channel *channel; bool fnew = true; + bool enq = false; int ret; unsigned long flags; @@ -264,12 +285,22 @@ static void vmbus_process_offer(struct work_struct *work) } } - if (fnew) + if (fnew) { list_add_tail(&newchannel->listentry, &vmbus_connection.chn_list); + enq = true; + } spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); + if (enq) { + if (newchannel->target_cpu != smp_processor_id()) + smp_call_function_single(newchannel->target_cpu, + percpu_channel_enq, + newchannel, true); + else + percpu_channel_enq(newchannel); + } if (!fnew) { /* * Check to see if this is a sub-channel. @@ -282,6 +313,14 @@ static void vmbus_process_offer(struct work_struct *work) spin_lock_irqsave(&channel->sc_lock, flags); list_add_tail(&newchannel->sc_list, &channel->sc_list); spin_unlock_irqrestore(&channel->sc_lock, flags); + + if (newchannel->target_cpu != smp_processor_id()) + smp_call_function_single(newchannel->target_cpu, + percpu_channel_enq, + newchannel, true); + else + percpu_channel_enq(newchannel); + newchannel->state = CHANNEL_OPEN_STATE; if (channel->sc_creation_callback != NULL) channel->sc_creation_callback(newchannel); diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index df2363ea017f..7f10c151632a 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -234,6 +234,28 @@ cleanup: return ret; } +/* + * Map the given relid to the corresponding channel based on the + * per-cpu list of channels that have been affinitized to this CPU. + * This will be used in the channel callback path as we can do this + * mapping in a lock-free fashion. + */ +static struct vmbus_channel *pcpu_relid2channel(u32 relid) +{ + struct vmbus_channel *channel; + struct vmbus_channel *found_channel = NULL; + int cpu = smp_processor_id(); + struct list_head *pcpu_head = &hv_context.percpu_list[cpu]; + + list_for_each_entry(channel, pcpu_head, percpu_list) { + if (channel->offermsg.child_relid == relid) { + found_channel = channel; + break; + } + } + + return found_channel; +} /* * relid2channel - Get the channel object given its @@ -285,7 +307,7 @@ static void process_chn_event(u32 relid) * Find the channel based on this relid and invokes the * channel callback to process the event */ - channel = relid2channel(relid); + channel = pcpu_relid2channel(relid); if (!channel) { pr_err("channel not found for relid - %u\n", relid); diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index bcb49502c3bf..edfc8488cb03 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -383,6 +383,8 @@ void hv_synic_init(void *arg) */ rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); hv_context.vp_index[cpu] = (u32)vp_index; + + INIT_LIST_HEAD(&hv_context.percpu_list[cpu]); return; } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 860134da8039..18d1a8404cbc 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -510,6 +510,11 @@ struct hv_context { * basis. */ struct tasklet_struct *event_dpc[NR_CPUS]; + /* + * To optimize the mapping of relid to channel, maintain + * per-cpu list of the channels based on their CPU affinity. + */ + struct list_head percpu_list[NR_CPUS]; }; extern struct hv_context hv_context; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index a274e089df78..08cfaff8a072 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -734,6 +734,11 @@ struct vmbus_channel { * Support per-channel state for use by vmbus drivers. */ void *per_channel_state; + /* + * To support per-cpu lookup mapping of relid to channel, + * link up channels based on their CPU affinity. + */ + struct list_head percpu_list; }; static inline void set_channel_read_state(struct vmbus_channel *c, bool state) -- cgit v1.2.3 From 425f3740cf8e93fac6318ed862bcc3081b818f0b Mon Sep 17 00:00:00 2001 From: Alan Date: Mon, 28 Apr 2014 20:47:36 +0100 Subject: goldfish: Add a 64bit write helper The base code imported from the Google tree is ifdef heaven. Prepare to fix this by adding a helper function. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/goldfish.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 include/linux/goldfish.h (limited to 'include') diff --git a/include/linux/goldfish.h b/include/linux/goldfish.h new file mode 100644 index 000000000000..9cc28902b54c --- /dev/null +++ b/include/linux/goldfish.h @@ -0,0 +1,15 @@ +#ifndef __LINUX_GOLDFISH_H +#define __LINUX_GOLDFISH_H + +/* Helpers for Goldfish virtual platform */ + +static inline void gf_write64(unsigned long data, + void __iomem *portl, void __iomem *porth) +{ + writel((u32)data, portl); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + writel(data>>32, porth); +#endif +} + +#endif /* __LINUX_GOLDFISH_H */ -- cgit v1.2.3 From 69dfa00ccb72a37f3810687ca110e5a8154c6eed Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 4 May 2014 15:09:13 -0400 Subject: cgroup: make flags and subsys_masks unsigned int There's no reason to use atomic bitops for cgroup_subsys_state->flags, cgroup_root->flags and various subsys_masks. This patch updates those to use bitwise and/or operations instead and converts them form unsigned long to unsigned int. This makes the fields occupy (marginally) smaller space and makes it clear that they don't require atomicity. This patch doesn't cause any behavior difference. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 8 ++++---- kernel/cgroup.c | 37 ++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 4b38e2d6110d..c6c703f2486b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -62,7 +62,7 @@ struct cgroup_subsys_state { /* the parent css */ struct cgroup_subsys_state *parent; - unsigned long flags; + unsigned int flags; /* percpu_ref killing and RCU release */ struct rcu_head rcu_head; @@ -185,7 +185,7 @@ struct cgroup { u64 serial_nr; /* the bitmask of subsystems enabled on the child cgroups */ - unsigned long child_subsys_mask; + unsigned int child_subsys_mask; /* Private pointers for each registered subsystem */ struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT]; @@ -312,7 +312,7 @@ struct cgroup_root { struct kernfs_root *kf_root; /* The bitmask of subsystems attached to this hierarchy */ - unsigned long subsys_mask; + unsigned int subsys_mask; /* Unique id for this hierarchy. */ int hierarchy_id; @@ -327,7 +327,7 @@ struct cgroup_root { struct list_head root_list; /* Hierarchy-specific flags */ - unsigned long flags; + unsigned int flags; /* IDs for cgroups in this hierarchy */ struct idr cgroup_idr; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 3873267c9ee3..21667f396a1e 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -181,7 +181,7 @@ static struct cftype cgroup_base_files[]; static void cgroup_put(struct cgroup *cgrp); static int rebind_subsystems(struct cgroup_root *dst_root, - unsigned long ss_mask); + unsigned int ss_mask); static void cgroup_destroy_css_killed(struct cgroup *cgrp); static int cgroup_destroy_locked(struct cgroup *cgrp); static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss); @@ -963,7 +963,7 @@ static struct cgroup *task_cgroup_from_root(struct task_struct *task, * update of a tasks cgroup pointer by cgroup_attach_task() */ -static int cgroup_populate_dir(struct cgroup *cgrp, unsigned long subsys_mask); +static int cgroup_populate_dir(struct cgroup *cgrp, unsigned int subsys_mask); static struct kernfs_syscall_ops cgroup_kf_syscall_ops; static const struct file_operations proc_cgroupstats_operations; @@ -1079,7 +1079,7 @@ static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft) * @cgrp: target cgroup * @subsys_mask: mask of the subsystem ids whose files should be removed */ -static void cgroup_clear_dir(struct cgroup *cgrp, unsigned long subsys_mask) +static void cgroup_clear_dir(struct cgroup *cgrp, unsigned int subsys_mask) { struct cgroup_subsys *ss; int i; @@ -1087,15 +1087,14 @@ static void cgroup_clear_dir(struct cgroup *cgrp, unsigned long subsys_mask) for_each_subsys(ss, i) { struct cftype *cfts; - if (!test_bit(i, &subsys_mask)) + if (!(subsys_mask & (1 << i))) continue; list_for_each_entry(cfts, &ss->cfts, node) cgroup_addrm_files(cgrp, cfts, false); } } -static int rebind_subsystems(struct cgroup_root *dst_root, - unsigned long ss_mask) +static int rebind_subsystems(struct cgroup_root *dst_root, unsigned int ss_mask) { struct cgroup_subsys *ss; int ssid, i, ret; @@ -1128,7 +1127,7 @@ static int rebind_subsystems(struct cgroup_root *dst_root, * Just warn about it and continue. */ if (cgrp_dfl_root_visible) { - pr_warn("failed to create files (%d) while rebinding 0x%lx to default root\n", + pr_warn("failed to create files (%d) while rebinding 0x%x to default root\n", ret, ss_mask); pr_warn("you may retry by moving them to a different hierarchy and unbinding\n"); } @@ -1214,8 +1213,8 @@ static int cgroup_show_options(struct seq_file *seq, } struct cgroup_sb_opts { - unsigned long subsys_mask; - unsigned long flags; + unsigned int subsys_mask; + unsigned int flags; char *release_agent; bool cpuset_clone_children; char *name; @@ -1227,12 +1226,12 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts) { char *token, *o = data; bool all_ss = false, one_ss = false; - unsigned long mask = (unsigned long)-1; + unsigned int mask = -1U; struct cgroup_subsys *ss; int i; #ifdef CONFIG_CPUSETS - mask = ~(1UL << cpuset_cgrp_id); + mask = ~(1U << cpuset_cgrp_id); #endif memset(opts, 0, sizeof(*opts)); @@ -1313,7 +1312,7 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts) /* Mutually exclusive option 'all' + subsystem name */ if (all_ss) return -EINVAL; - set_bit(i, &opts->subsys_mask); + opts->subsys_mask |= (1 << i); one_ss = true; break; @@ -1342,7 +1341,7 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts) if (all_ss || (!one_ss && !opts->none && !opts->name)) for_each_subsys(ss, i) if (!ss->disabled) - set_bit(i, &opts->subsys_mask); + opts->subsys_mask |= (1 << i); /* * We either have to specify by name or by subsystems. (So @@ -1373,7 +1372,7 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data) int ret = 0; struct cgroup_root *root = cgroup_root_from_kf(kf_root); struct cgroup_sb_opts opts; - unsigned long added_mask, removed_mask; + unsigned int added_mask, removed_mask; if (root->flags & CGRP_ROOT_SANE_BEHAVIOR) { pr_err("sane_behavior: remount is not allowed\n"); @@ -1398,7 +1397,7 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data) /* Don't allow flags or name to change at remount */ if (((opts.flags ^ root->flags) & CGRP_ROOT_OPTION_MASK) || (opts.name && strcmp(opts.name, root->name))) { - pr_err("option or name mismatch, new: 0x%lx \"%s\", old: 0x%lx \"%s\"\n", + pr_err("option or name mismatch, new: 0x%x \"%s\", old: 0x%x \"%s\"\n", opts.flags & CGRP_ROOT_OPTION_MASK, opts.name ?: "", root->flags & CGRP_ROOT_OPTION_MASK, root->name); ret = -EINVAL; @@ -1522,7 +1521,7 @@ static void init_cgroup_root(struct cgroup_root *root, set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags); } -static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask) +static int cgroup_setup_root(struct cgroup_root *root, unsigned int ss_mask) { LIST_HEAD(tmp_links); struct cgroup *root_cgrp = &root->cgrp; @@ -2507,7 +2506,7 @@ out_finish: static int cgroup_subtree_control_write(struct cgroup_subsys_state *dummy_css, struct cftype *cft, char *buffer) { - unsigned long enable_req = 0, disable_req = 0, enable, disable; + unsigned int enable_req = 0, disable_req = 0, enable, disable; struct cgroup *cgrp = dummy_css->cgroup, *child; struct cgroup_subsys *ss; char *tok, *p; @@ -3998,7 +3997,7 @@ static struct cftype cgroup_base_files[] = { * * On failure, no file is added. */ -static int cgroup_populate_dir(struct cgroup *cgrp, unsigned long subsys_mask) +static int cgroup_populate_dir(struct cgroup *cgrp, unsigned int subsys_mask) { struct cgroup_subsys *ss; int i, ret = 0; @@ -4007,7 +4006,7 @@ static int cgroup_populate_dir(struct cgroup *cgrp, unsigned long subsys_mask) for_each_subsys(ss, i) { struct cftype *cfts; - if (!test_bit(i, &subsys_mask)) + if (!(subsys_mask & (1 << i))) continue; list_for_each_entry(cfts, &ss->cfts, node) { -- cgit v1.2.3 From 7d699ddb2b181a2c76e5ea18b1bdf102c4bebe4b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 4 May 2014 15:09:13 -0400 Subject: cgroup, memcg: allocate cgroup ID from 1 Currently, cgroup->id is allocated from 0, which is always assigned to the root cgroup; unfortunately, memcg wants to use ID 0 to indicate invalid IDs and ends up incrementing all IDs by one. It's reasonable to reserve 0 for special purposes. This patch updates cgroup core so that ID 0 is not used and the root cgroups get ID 1. The ID incrementing is removed form memcg. Signed-off-by: Tejun Heo Acked-by: Michal Hocko Cc: Johannes Weiner Acked-by: Li Zefan --- include/linux/cgroup.h | 4 ++-- kernel/cgroup.c | 4 ++-- mm/memcontrol.c | 8 ++------ 3 files changed, 6 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c6c703f2486b..793f70a48820 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -144,8 +144,8 @@ struct cgroup { /* * idr allocated in-hierarchy ID. * - * The ID of the root cgroup is always 0, and a new cgroup - * will be assigned with a smallest available ID. + * ID 0 is not used, the ID of the root cgroup is always 1, and a + * new cgroup will be assigned with a smallest available ID. * * Allocating/Removing ID must be protected by cgroup_mutex. */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 21667f396a1e..3fa0463e74bb 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1531,7 +1531,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned int ss_mask) lockdep_assert_held(&cgroup_tree_mutex); lockdep_assert_held(&cgroup_mutex); - ret = idr_alloc(&root->cgroup_idr, root_cgrp, 0, 1, GFP_KERNEL); + ret = idr_alloc(&root->cgroup_idr, root_cgrp, 1, 2, GFP_KERNEL); if (ret < 0) goto out; root_cgrp->id = ret; @@ -4225,7 +4225,7 @@ static long cgroup_create(struct cgroup *parent, const char *name, * Temporarily set the pointer to NULL, so idr_find() won't return * a half-baked cgroup. */ - cgrp->id = idr_alloc(&root->cgroup_idr, NULL, 1, 0, GFP_KERNEL); + cgrp->id = idr_alloc(&root->cgroup_idr, NULL, 2, 0, GFP_KERNEL); if (cgrp->id < 0) { err = -ENOMEM; goto err_unlock; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 29501f040568..1d0b29715b73 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -527,18 +527,14 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) static inline unsigned short mem_cgroup_id(struct mem_cgroup *memcg) { - /* - * The ID of the root cgroup is 0, but memcg treat 0 as an - * invalid ID, so we return (cgroup_id + 1). - */ - return memcg->css.cgroup->id + 1; + return memcg->css.cgroup->id; } static inline struct mem_cgroup *mem_cgroup_from_id(unsigned short id) { struct cgroup_subsys_state *css; - css = css_from_id(id - 1, &memory_cgrp_subsys); + css = css_from_id(id, &memory_cgrp_subsys); return mem_cgroup_from_css(css); } -- cgit v1.2.3 From 15a4c835e4ed3e60dd68727cd1907e3dd89563f4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 4 May 2014 15:09:14 -0400 Subject: cgroup, memcg: implement css->id and convert css_from_id() to use it Until now, cgroup->id has been used to identify all the associated csses and css_from_id() takes cgroup ID and returns the matching css by looking up the cgroup and then dereferencing the css associated with it; however, now that the lifetimes of cgroup and css are separate, this is incorrect and breaks on the unified hierarchy when a controller is disabled and enabled back again before the previous instance is released. This patch adds css->id which is a subsystem-unique ID and converts css_from_id() to look up by the new css->id instead. memcg is the only user of css_from_id() and also converted to use css->id instead. For traditional hierarchies, this shouldn't make any functional difference. Signed-off-by: Tejun Heo Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Jianyu Zhan Acked-by: Li Zefan --- include/linux/cgroup.h | 9 ++++++++ kernel/cgroup.c | 59 ++++++++++++++++++++++++++++++++------------------ mm/memcontrol.c | 4 ++-- 3 files changed, 49 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 793f70a48820..2dfabb3b749a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -62,6 +62,12 @@ struct cgroup_subsys_state { /* the parent css */ struct cgroup_subsys_state *parent; + /* + * Subsys-unique ID. 0 is unused and root is always 1. The + * matching css can be looked up using css_from_id(). + */ + int id; + unsigned int flags; /* percpu_ref killing and RCU release */ @@ -655,6 +661,9 @@ struct cgroup_subsys { /* link to parent, protected by cgroup_lock() */ struct cgroup_root *root; + /* idr for css->id */ + struct idr css_idr; + /* * List of cftypes. Each entry is the first entry of an array * terminated by zero length name. diff --git a/kernel/cgroup.c b/kernel/cgroup.c index f1c98c527b2d..a1a20e8c973a 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -100,8 +100,8 @@ static DECLARE_RWSEM(css_set_rwsem); #endif /* - * Protects cgroup_idr so that IDs can be released without grabbing - * cgroup_mutex. + * Protects cgroup_idr and css_idr so that IDs can be released without + * grabbing cgroup_mutex. */ static DEFINE_SPINLOCK(cgroup_idr_lock); @@ -1089,12 +1089,6 @@ static void cgroup_put(struct cgroup *cgrp) if (WARN_ON_ONCE(cgrp->parent && !cgroup_is_dead(cgrp))) return; - /* - * XXX: cgrp->id is only used to look up css's. As cgroup and - * css's lifetimes will be decoupled, it should be made - * per-subsystem and moved to css->id so that lookups are - * successful until the target css is released. - */ cgroup_idr_remove(&cgrp->root->cgroup_idr, cgrp->id); cgrp->id = -1; @@ -4104,8 +4098,11 @@ static void css_release(struct percpu_ref *ref) { struct cgroup_subsys_state *css = container_of(ref, struct cgroup_subsys_state, refcnt); + struct cgroup_subsys *ss = css->ss; + + RCU_INIT_POINTER(css->cgroup->subsys[ss->id], NULL); + cgroup_idr_remove(&ss->css_idr, css->id); - RCU_INIT_POINTER(css->cgroup->subsys[css->ss->id], NULL); call_rcu(&css->rcu_head, css_free_rcu_fn); } @@ -4195,9 +4192,17 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss) if (err) goto err_free_css; + err = cgroup_idr_alloc(&ss->css_idr, NULL, 2, 0, GFP_NOWAIT); + if (err < 0) + goto err_free_percpu_ref; + css->id = err; + err = cgroup_populate_dir(cgrp, 1 << ss->id); if (err) - goto err_free_percpu_ref; + goto err_free_id; + + /* @css is ready to be brought online now, make it visible */ + cgroup_idr_replace(&ss->css_idr, css, css->id); err = online_css(css); if (err) @@ -4216,6 +4221,8 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss) err_clear_dir: cgroup_clear_dir(css->cgroup, 1 << css->ss->id); +err_free_id: + cgroup_idr_remove(&ss->css_idr, css->id); err_free_percpu_ref: percpu_ref_cancel_init(&css->refcnt); err_free_css: @@ -4642,7 +4649,7 @@ static struct kernfs_syscall_ops cgroup_kf_syscall_ops = { .rename = cgroup_rename, }; -static void __init cgroup_init_subsys(struct cgroup_subsys *ss) +static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) { struct cgroup_subsys_state *css; @@ -4651,6 +4658,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss) mutex_lock(&cgroup_tree_mutex); mutex_lock(&cgroup_mutex); + idr_init(&ss->css_idr); INIT_LIST_HEAD(&ss->cfts); /* Create the root cgroup state for this subsystem */ @@ -4659,6 +4667,13 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss) /* We don't handle early failures gracefully */ BUG_ON(IS_ERR(css)); init_and_link_css(css, ss, &cgrp_dfl_root.cgrp); + if (early) { + /* idr_alloc() can't be called safely during early init */ + css->id = 1; + } else { + css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, GFP_KERNEL); + BUG_ON(css->id < 0); + } /* Update the init_css_set to contain a subsys * pointer to this state - since the subsystem is @@ -4709,7 +4724,7 @@ int __init cgroup_init_early(void) ss->name = cgroup_subsys_name[i]; if (ss->early_init) - cgroup_init_subsys(ss); + cgroup_init_subsys(ss, true); } return 0; } @@ -4741,8 +4756,16 @@ int __init cgroup_init(void) mutex_unlock(&cgroup_tree_mutex); for_each_subsys(ss, ssid) { - if (!ss->early_init) - cgroup_init_subsys(ss); + if (ss->early_init) { + struct cgroup_subsys_state *css = + init_css_set.subsys[ss->id]; + + css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, + GFP_KERNEL); + BUG_ON(css->id < 0); + } else { + cgroup_init_subsys(ss, false); + } list_add_tail(&init_css_set.e_cset_node[ssid], &cgrp_dfl_root.cgrp.e_csets[ssid]); @@ -5196,14 +5219,8 @@ struct cgroup_subsys_state *css_tryget_from_dir(struct dentry *dentry, */ struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss) { - struct cgroup *cgrp; - WARN_ON_ONCE(!rcu_read_lock_held()); - - cgrp = idr_find(&ss->root->cgroup_idr, id); - if (cgrp) - return cgroup_css(cgrp, ss); - return NULL; + return idr_find(&ss->css_idr, id); } #ifdef CONFIG_CGROUP_DEBUG diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 1d0b29715b73..c3f82f69ef58 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -527,7 +527,7 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) static inline unsigned short mem_cgroup_id(struct mem_cgroup *memcg) { - return memcg->css.cgroup->id; + return memcg->css.id; } static inline struct mem_cgroup *mem_cgroup_from_id(unsigned short id) @@ -6401,7 +6401,7 @@ mem_cgroup_css_online(struct cgroup_subsys_state *css) struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(css)); - if (css->cgroup->id > MEM_CGROUP_ID_MAX) + if (css->id > MEM_CGROUP_ID_MAX) return -ENOSPC; if (!parent) -- cgit v1.2.3 From 5bcfedf06f7fdf9efcf65dc11198e9012f7530f4 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 1 May 2014 18:34:18 +0200 Subject: net: filter: simplify label names from jump-table This patch simplifies label naming for the BPF jump-table. When we define labels via DL(), we just concatenate/textify the combination of instruction opcode which consists of the class, subclass, word size, target register and so on. Each time we leave BPF_ prefix intact, so that e.g. the preprocessor generates a label BPF_ALU_BPF_ADD_BPF_X for DL(BPF_ALU, BPF_ADD, BPF_X) whereas a label name of ALU_ADD_X is much more easy to grasp. Pure cleanup only. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 3 + net/core/filter.c | 308 ++++++++++++++++++++++++------------------------- 2 files changed, 157 insertions(+), 154 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index 759abf78dd61..b042d1db127f 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -37,6 +37,9 @@ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ +/* Placeholder/dummy for 0 */ +#define BPF_0 0 + /* BPF has 10 general purpose 64-bit registers and stack frame. */ #define MAX_BPF_REG 11 diff --git a/net/core/filter.c b/net/core/filter.c index 7c4db3dd3d1e..a1784e95b049 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -156,94 +156,94 @@ unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn) static const void *jumptable[256] = { [0 ... 255] = &&default_label, /* Now overwrite non-defaults ... */ -#define DL(A, B, C) [A|B|C] = &&A##_##B##_##C - DL(BPF_ALU, BPF_ADD, BPF_X), - DL(BPF_ALU, BPF_ADD, BPF_K), - DL(BPF_ALU, BPF_SUB, BPF_X), - DL(BPF_ALU, BPF_SUB, BPF_K), - DL(BPF_ALU, BPF_AND, BPF_X), - DL(BPF_ALU, BPF_AND, BPF_K), - DL(BPF_ALU, BPF_OR, BPF_X), - DL(BPF_ALU, BPF_OR, BPF_K), - DL(BPF_ALU, BPF_LSH, BPF_X), - DL(BPF_ALU, BPF_LSH, BPF_K), - DL(BPF_ALU, BPF_RSH, BPF_X), - DL(BPF_ALU, BPF_RSH, BPF_K), - DL(BPF_ALU, BPF_XOR, BPF_X), - DL(BPF_ALU, BPF_XOR, BPF_K), - DL(BPF_ALU, BPF_MUL, BPF_X), - DL(BPF_ALU, BPF_MUL, BPF_K), - DL(BPF_ALU, BPF_MOV, BPF_X), - DL(BPF_ALU, BPF_MOV, BPF_K), - DL(BPF_ALU, BPF_DIV, BPF_X), - DL(BPF_ALU, BPF_DIV, BPF_K), - DL(BPF_ALU, BPF_MOD, BPF_X), - DL(BPF_ALU, BPF_MOD, BPF_K), - DL(BPF_ALU, BPF_NEG, 0), - DL(BPF_ALU, BPF_END, BPF_TO_BE), - DL(BPF_ALU, BPF_END, BPF_TO_LE), - DL(BPF_ALU64, BPF_ADD, BPF_X), - DL(BPF_ALU64, BPF_ADD, BPF_K), - DL(BPF_ALU64, BPF_SUB, BPF_X), - DL(BPF_ALU64, BPF_SUB, BPF_K), - DL(BPF_ALU64, BPF_AND, BPF_X), - DL(BPF_ALU64, BPF_AND, BPF_K), - DL(BPF_ALU64, BPF_OR, BPF_X), - DL(BPF_ALU64, BPF_OR, BPF_K), - DL(BPF_ALU64, BPF_LSH, BPF_X), - DL(BPF_ALU64, BPF_LSH, BPF_K), - DL(BPF_ALU64, BPF_RSH, BPF_X), - DL(BPF_ALU64, BPF_RSH, BPF_K), - DL(BPF_ALU64, BPF_XOR, BPF_X), - DL(BPF_ALU64, BPF_XOR, BPF_K), - DL(BPF_ALU64, BPF_MUL, BPF_X), - DL(BPF_ALU64, BPF_MUL, BPF_K), - DL(BPF_ALU64, BPF_MOV, BPF_X), - DL(BPF_ALU64, BPF_MOV, BPF_K), - DL(BPF_ALU64, BPF_ARSH, BPF_X), - DL(BPF_ALU64, BPF_ARSH, BPF_K), - DL(BPF_ALU64, BPF_DIV, BPF_X), - DL(BPF_ALU64, BPF_DIV, BPF_K), - DL(BPF_ALU64, BPF_MOD, BPF_X), - DL(BPF_ALU64, BPF_MOD, BPF_K), - DL(BPF_ALU64, BPF_NEG, 0), - DL(BPF_JMP, BPF_CALL, 0), - DL(BPF_JMP, BPF_JA, 0), - DL(BPF_JMP, BPF_JEQ, BPF_X), - DL(BPF_JMP, BPF_JEQ, BPF_K), - DL(BPF_JMP, BPF_JNE, BPF_X), - DL(BPF_JMP, BPF_JNE, BPF_K), - DL(BPF_JMP, BPF_JGT, BPF_X), - DL(BPF_JMP, BPF_JGT, BPF_K), - DL(BPF_JMP, BPF_JGE, BPF_X), - DL(BPF_JMP, BPF_JGE, BPF_K), - DL(BPF_JMP, BPF_JSGT, BPF_X), - DL(BPF_JMP, BPF_JSGT, BPF_K), - DL(BPF_JMP, BPF_JSGE, BPF_X), - DL(BPF_JMP, BPF_JSGE, BPF_K), - DL(BPF_JMP, BPF_JSET, BPF_X), - DL(BPF_JMP, BPF_JSET, BPF_K), - DL(BPF_JMP, BPF_EXIT, 0), - DL(BPF_STX, BPF_MEM, BPF_B), - DL(BPF_STX, BPF_MEM, BPF_H), - DL(BPF_STX, BPF_MEM, BPF_W), - DL(BPF_STX, BPF_MEM, BPF_DW), - DL(BPF_STX, BPF_XADD, BPF_W), - DL(BPF_STX, BPF_XADD, BPF_DW), - DL(BPF_ST, BPF_MEM, BPF_B), - DL(BPF_ST, BPF_MEM, BPF_H), - DL(BPF_ST, BPF_MEM, BPF_W), - DL(BPF_ST, BPF_MEM, BPF_DW), - DL(BPF_LDX, BPF_MEM, BPF_B), - DL(BPF_LDX, BPF_MEM, BPF_H), - DL(BPF_LDX, BPF_MEM, BPF_W), - DL(BPF_LDX, BPF_MEM, BPF_DW), - DL(BPF_LD, BPF_ABS, BPF_W), - DL(BPF_LD, BPF_ABS, BPF_H), - DL(BPF_LD, BPF_ABS, BPF_B), - DL(BPF_LD, BPF_IND, BPF_W), - DL(BPF_LD, BPF_IND, BPF_H), - DL(BPF_LD, BPF_IND, BPF_B), +#define DL(A, B, C) [BPF_##A|BPF_##B|BPF_##C] = &&A##_##B##_##C + DL(ALU, ADD, X), + DL(ALU, ADD, K), + DL(ALU, SUB, X), + DL(ALU, SUB, K), + DL(ALU, AND, X), + DL(ALU, AND, K), + DL(ALU, OR, X), + DL(ALU, OR, K), + DL(ALU, LSH, X), + DL(ALU, LSH, K), + DL(ALU, RSH, X), + DL(ALU, RSH, K), + DL(ALU, XOR, X), + DL(ALU, XOR, K), + DL(ALU, MUL, X), + DL(ALU, MUL, K), + DL(ALU, MOV, X), + DL(ALU, MOV, K), + DL(ALU, DIV, X), + DL(ALU, DIV, K), + DL(ALU, MOD, X), + DL(ALU, MOD, K), + DL(ALU, NEG, 0), + DL(ALU, END, TO_BE), + DL(ALU, END, TO_LE), + DL(ALU64, ADD, X), + DL(ALU64, ADD, K), + DL(ALU64, SUB, X), + DL(ALU64, SUB, K), + DL(ALU64, AND, X), + DL(ALU64, AND, K), + DL(ALU64, OR, X), + DL(ALU64, OR, K), + DL(ALU64, LSH, X), + DL(ALU64, LSH, K), + DL(ALU64, RSH, X), + DL(ALU64, RSH, K), + DL(ALU64, XOR, X), + DL(ALU64, XOR, K), + DL(ALU64, MUL, X), + DL(ALU64, MUL, K), + DL(ALU64, MOV, X), + DL(ALU64, MOV, K), + DL(ALU64, ARSH, X), + DL(ALU64, ARSH, K), + DL(ALU64, DIV, X), + DL(ALU64, DIV, K), + DL(ALU64, MOD, X), + DL(ALU64, MOD, K), + DL(ALU64, NEG, 0), + DL(JMP, CALL, 0), + DL(JMP, JA, 0), + DL(JMP, JEQ, X), + DL(JMP, JEQ, K), + DL(JMP, JNE, X), + DL(JMP, JNE, K), + DL(JMP, JGT, X), + DL(JMP, JGT, K), + DL(JMP, JGE, X), + DL(JMP, JGE, K), + DL(JMP, JSGT, X), + DL(JMP, JSGT, K), + DL(JMP, JSGE, X), + DL(JMP, JSGE, K), + DL(JMP, JSET, X), + DL(JMP, JSET, K), + DL(JMP, EXIT, 0), + DL(STX, MEM, B), + DL(STX, MEM, H), + DL(STX, MEM, W), + DL(STX, MEM, DW), + DL(STX, XADD, W), + DL(STX, XADD, DW), + DL(ST, MEM, B), + DL(ST, MEM, H), + DL(ST, MEM, W), + DL(ST, MEM, DW), + DL(LDX, MEM, B), + DL(LDX, MEM, H), + DL(LDX, MEM, W), + DL(LDX, MEM, DW), + DL(LD, ABS, W), + DL(LD, ABS, H), + DL(LD, ABS, B), + DL(LD, IND, W), + DL(LD, IND, H), + DL(LD, IND, B), #undef DL }; @@ -257,93 +257,93 @@ select_insn: /* ALU */ #define ALU(OPCODE, OP) \ - BPF_ALU64_##OPCODE##_BPF_X: \ + ALU64_##OPCODE##_X: \ A = A OP X; \ CONT; \ - BPF_ALU_##OPCODE##_BPF_X: \ + ALU_##OPCODE##_X: \ A = (u32) A OP (u32) X; \ CONT; \ - BPF_ALU64_##OPCODE##_BPF_K: \ + ALU64_##OPCODE##_K: \ A = A OP K; \ CONT; \ - BPF_ALU_##OPCODE##_BPF_K: \ + ALU_##OPCODE##_K: \ A = (u32) A OP (u32) K; \ CONT; - ALU(BPF_ADD, +) - ALU(BPF_SUB, -) - ALU(BPF_AND, &) - ALU(BPF_OR, |) - ALU(BPF_LSH, <<) - ALU(BPF_RSH, >>) - ALU(BPF_XOR, ^) - ALU(BPF_MUL, *) + ALU(ADD, +) + ALU(SUB, -) + ALU(AND, &) + ALU(OR, |) + ALU(LSH, <<) + ALU(RSH, >>) + ALU(XOR, ^) + ALU(MUL, *) #undef ALU - BPF_ALU_BPF_NEG_0: + ALU_NEG_0: A = (u32) -A; CONT; - BPF_ALU64_BPF_NEG_0: + ALU64_NEG_0: A = -A; CONT; - BPF_ALU_BPF_MOV_BPF_X: + ALU_MOV_X: A = (u32) X; CONT; - BPF_ALU_BPF_MOV_BPF_K: + ALU_MOV_K: A = (u32) K; CONT; - BPF_ALU64_BPF_MOV_BPF_X: + ALU64_MOV_X: A = X; CONT; - BPF_ALU64_BPF_MOV_BPF_K: + ALU64_MOV_K: A = K; CONT; - BPF_ALU64_BPF_ARSH_BPF_X: + ALU64_ARSH_X: (*(s64 *) &A) >>= X; CONT; - BPF_ALU64_BPF_ARSH_BPF_K: + ALU64_ARSH_K: (*(s64 *) &A) >>= K; CONT; - BPF_ALU64_BPF_MOD_BPF_X: + ALU64_MOD_X: if (unlikely(X == 0)) return 0; tmp = A; A = do_div(tmp, X); CONT; - BPF_ALU_BPF_MOD_BPF_X: + ALU_MOD_X: if (unlikely(X == 0)) return 0; tmp = (u32) A; A = do_div(tmp, (u32) X); CONT; - BPF_ALU64_BPF_MOD_BPF_K: + ALU64_MOD_K: tmp = A; A = do_div(tmp, K); CONT; - BPF_ALU_BPF_MOD_BPF_K: + ALU_MOD_K: tmp = (u32) A; A = do_div(tmp, (u32) K); CONT; - BPF_ALU64_BPF_DIV_BPF_X: + ALU64_DIV_X: if (unlikely(X == 0)) return 0; do_div(A, X); CONT; - BPF_ALU_BPF_DIV_BPF_X: + ALU_DIV_X: if (unlikely(X == 0)) return 0; tmp = (u32) A; do_div(tmp, (u32) X); A = (u32) tmp; CONT; - BPF_ALU64_BPF_DIV_BPF_K: + ALU64_DIV_K: do_div(A, K); CONT; - BPF_ALU_BPF_DIV_BPF_K: + ALU_DIV_K: tmp = (u32) A; do_div(tmp, (u32) K); A = (u32) tmp; CONT; - BPF_ALU_BPF_END_BPF_TO_BE: + ALU_END_TO_BE: switch (K) { case 16: A = (__force u16) cpu_to_be16(A); @@ -356,7 +356,7 @@ select_insn: break; } CONT; - BPF_ALU_BPF_END_BPF_TO_LE: + ALU_END_TO_LE: switch (K) { case 16: A = (__force u16) cpu_to_le16(A); @@ -371,7 +371,7 @@ select_insn: CONT; /* CALL */ - BPF_JMP_BPF_CALL_0: + JMP_CALL_0: /* Function call scratches R1-R5 registers, preserves R6-R9, * and stores return value into R0. */ @@ -380,122 +380,122 @@ select_insn: CONT; /* JMP */ - BPF_JMP_BPF_JA_0: + JMP_JA_0: insn += insn->off; CONT; - BPF_JMP_BPF_JEQ_BPF_X: + JMP_JEQ_X: if (A == X) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JEQ_BPF_K: + JMP_JEQ_K: if (A == K) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JNE_BPF_X: + JMP_JNE_X: if (A != X) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JNE_BPF_K: + JMP_JNE_K: if (A != K) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JGT_BPF_X: + JMP_JGT_X: if (A > X) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JGT_BPF_K: + JMP_JGT_K: if (A > K) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JGE_BPF_X: + JMP_JGE_X: if (A >= X) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JGE_BPF_K: + JMP_JGE_K: if (A >= K) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JSGT_BPF_X: - if (((s64)A) > ((s64)X)) { + JMP_JSGT_X: + if (((s64) A) > ((s64) X)) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JSGT_BPF_K: - if (((s64)A) > ((s64)K)) { + JMP_JSGT_K: + if (((s64) A) > ((s64) K)) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JSGE_BPF_X: - if (((s64)A) >= ((s64)X)) { + JMP_JSGE_X: + if (((s64) A) >= ((s64) X)) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JSGE_BPF_K: - if (((s64)A) >= ((s64)K)) { + JMP_JSGE_K: + if (((s64) A) >= ((s64) K)) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JSET_BPF_X: + JMP_JSET_X: if (A & X) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_JSET_BPF_K: + JMP_JSET_K: if (A & K) { insn += insn->off; CONT_JMP; } CONT; - BPF_JMP_BPF_EXIT_0: + JMP_EXIT_0: return R0; /* STX and ST and LDX*/ #define LDST(SIZEOP, SIZE) \ - BPF_STX_BPF_MEM_##SIZEOP: \ + STX_MEM_##SIZEOP: \ *(SIZE *)(unsigned long) (A + insn->off) = X; \ CONT; \ - BPF_ST_BPF_MEM_##SIZEOP: \ + ST_MEM_##SIZEOP: \ *(SIZE *)(unsigned long) (A + insn->off) = K; \ CONT; \ - BPF_LDX_BPF_MEM_##SIZEOP: \ + LDX_MEM_##SIZEOP: \ A = *(SIZE *)(unsigned long) (X + insn->off); \ CONT; - LDST(BPF_B, u8) - LDST(BPF_H, u16) - LDST(BPF_W, u32) - LDST(BPF_DW, u64) + LDST(B, u8) + LDST(H, u16) + LDST(W, u32) + LDST(DW, u64) #undef LDST - BPF_STX_BPF_XADD_BPF_W: /* lock xadd *(u32 *)(A + insn->off) += X */ + STX_XADD_W: /* lock xadd *(u32 *)(A + insn->off) += X */ atomic_add((u32) X, (atomic_t *)(unsigned long) (A + insn->off)); CONT; - BPF_STX_BPF_XADD_BPF_DW: /* lock xadd *(u64 *)(A + insn->off) += X */ + STX_XADD_DW: /* lock xadd *(u64 *)(A + insn->off) += X */ atomic64_add((u64) X, (atomic64_t *)(unsigned long) (A + insn->off)); CONT; - BPF_LD_BPF_ABS_BPF_W: /* R0 = ntohl(*(u32 *) (skb->data + K)) */ + LD_ABS_W: /* R0 = ntohl(*(u32 *) (skb->data + K)) */ off = K; load_word: /* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are only @@ -524,7 +524,7 @@ load_word: CONT; } return 0; - BPF_LD_BPF_ABS_BPF_H: /* R0 = ntohs(*(u16 *) (skb->data + K)) */ + LD_ABS_H: /* R0 = ntohs(*(u16 *) (skb->data + K)) */ off = K; load_half: ptr = load_pointer((struct sk_buff *) ctx, off, 2, &tmp); @@ -533,7 +533,7 @@ load_half: CONT; } return 0; - BPF_LD_BPF_ABS_BPF_B: /* R0 = *(u8 *) (ctx + K) */ + LD_ABS_B: /* R0 = *(u8 *) (ctx + K) */ off = K; load_byte: ptr = load_pointer((struct sk_buff *) ctx, off, 1, &tmp); @@ -542,13 +542,13 @@ load_byte: CONT; } return 0; - BPF_LD_BPF_IND_BPF_W: /* R0 = ntohl(*(u32 *) (skb->data + X + K)) */ + LD_IND_W: /* R0 = ntohl(*(u32 *) (skb->data + X + K)) */ off = K + X; goto load_word; - BPF_LD_BPF_IND_BPF_H: /* R0 = ntohs(*(u16 *) (skb->data + X + K)) */ + LD_IND_H: /* R0 = ntohs(*(u16 *) (skb->data + X + K)) */ off = K + X; goto load_half; - BPF_LD_BPF_IND_BPF_B: /* R0 = *(u8 *) (skb->data + X + K) */ + LD_IND_B: /* R0 = *(u8 *) (skb->data + X + K) */ off = K + X; goto load_byte; -- cgit v1.2.3 From 30743837dd204d2b04fd4e9d3db78cc7b118c81a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 1 May 2014 18:34:19 +0200 Subject: net: filter: make register naming more comprehensible The current code is a bit hard to parse on which registers can be used, how they are mapped and all play together. It makes much more sense to define this a bit more clearly so that the code is a bit more intuitive. This patch cleans this up, and makes naming a bit more consistent among the code. This also allows for moving some of the defines into the header file. Clearing of A and X registers in __sk_run_filter() do not get a particular register name assigned as they have not an 'official' function, but rather just result from the concrete initial mapping of old BPF programs. Since for BPF helper functions for BPF_CALL we already use small letters, so be consistent here as well. No functional changes. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 44 ++++++++-- net/core/filter.c | 215 +++++++++++++++++++++++++------------------------ 2 files changed, 145 insertions(+), 114 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index b042d1db127f..ed1efab10b8f 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -40,16 +40,47 @@ /* Placeholder/dummy for 0 */ #define BPF_0 0 +/* Register numbers */ +enum { + BPF_REG_0 = 0, + BPF_REG_1, + BPF_REG_2, + BPF_REG_3, + BPF_REG_4, + BPF_REG_5, + BPF_REG_6, + BPF_REG_7, + BPF_REG_8, + BPF_REG_9, + BPF_REG_10, + __MAX_BPF_REG, +}; + /* BPF has 10 general purpose 64-bit registers and stack frame. */ -#define MAX_BPF_REG 11 +#define MAX_BPF_REG __MAX_BPF_REG + +/* ArgX, context and stack frame pointer register positions. Note, + * Arg1, Arg2, Arg3, etc are used as argument mappings of function + * calls in BPF_CALL instruction. + */ +#define BPF_REG_ARG1 BPF_REG_1 +#define BPF_REG_ARG2 BPF_REG_2 +#define BPF_REG_ARG3 BPF_REG_3 +#define BPF_REG_ARG4 BPF_REG_4 +#define BPF_REG_ARG5 BPF_REG_5 +#define BPF_REG_CTX BPF_REG_6 +#define BPF_REG_FP BPF_REG_10 + +/* Additional register mappings for converted user programs. */ +#define BPF_REG_A BPF_REG_0 +#define BPF_REG_X BPF_REG_7 +#define BPF_REG_TMP BPF_REG_8 /* BPF program can access up to 512 bytes of stack space. */ #define MAX_BPF_STACK 512 -/* Arg1, context and stack frame pointer register positions. */ -#define ARG1_REG 1 -#define CTX_REG 6 -#define FP_REG 10 +/* Macro to invoke filter function. */ +#define SK_RUN_FILTER(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) struct sock_filter_int { __u8 code; /* opcode */ @@ -100,9 +131,6 @@ static inline unsigned int sk_filter_size(unsigned int proglen) #define sk_filter_proglen(fprog) \ (fprog->len * sizeof(fprog->filter[0])) -#define SK_RUN_FILTER(filter, ctx) \ - (*filter->bpf_func)(ctx, filter->insnsi) - int sk_filter(struct sock *sk, struct sk_buff *skb); u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx, diff --git a/net/core/filter.c b/net/core/filter.c index a1784e95b049..c93ca8d66f37 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -45,6 +45,27 @@ #include #include +/* Registers */ +#define R0 regs[BPF_REG_0] +#define R1 regs[BPF_REG_1] +#define R2 regs[BPF_REG_2] +#define R3 regs[BPF_REG_3] +#define R4 regs[BPF_REG_4] +#define R5 regs[BPF_REG_5] +#define R6 regs[BPF_REG_6] +#define R7 regs[BPF_REG_7] +#define R8 regs[BPF_REG_8] +#define R9 regs[BPF_REG_9] +#define R10 regs[BPF_REG_10] + +/* Named registers */ +#define A regs[insn->a_reg] +#define X regs[insn->x_reg] +#define FP regs[BPF_REG_FP] +#define ARG1 regs[BPF_REG_ARG1] +#define CTX regs[BPF_REG_CTX] +#define K insn->imm + /* No hurry in this branch * * Exported for the bpf jit load helper. @@ -122,13 +143,6 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) return 0; } -/* Register mappings for user programs. */ -#define A_REG 0 -#define X_REG 7 -#define TMP_REG 8 -#define ARG2_REG 2 -#define ARG3_REG 3 - /** * __sk_run_filter - run a filter on a given context * @ctx: buffer to run the filter on @@ -142,17 +156,6 @@ unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn) { u64 stack[MAX_BPF_STACK / sizeof(u64)]; u64 regs[MAX_BPF_REG], tmp; - void *ptr; - int off; - -#define K insn->imm -#define A regs[insn->a_reg] -#define X regs[insn->x_reg] -#define R0 regs[0] - -#define CONT ({insn++; goto select_insn; }) -#define CONT_JMP ({insn++; goto select_insn; }) - static const void *jumptable[256] = { [0 ... 255] = &&default_label, /* Now overwrite non-defaults ... */ @@ -246,11 +249,18 @@ unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn) DL(LD, IND, B), #undef DL }; + void *ptr; + int off; - regs[FP_REG] = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)]; - regs[ARG1_REG] = (u64) (unsigned long) ctx; - regs[A_REG] = 0; - regs[X_REG] = 0; +#define CONT ({ insn++; goto select_insn; }) +#define CONT_JMP ({ insn++; goto select_insn; }) + + FP = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)]; + ARG1 = (u64) (unsigned long) ctx; + + /* Register for user BPF programs need to be reset first. */ + regs[BPF_REG_A] = 0; + regs[BPF_REG_X] = 0; select_insn: goto *jumptable[insn->code]; @@ -375,8 +385,7 @@ select_insn: /* Function call scratches R1-R5 registers, preserves R6-R9, * and stores return value into R0. */ - R0 = (__bpf_call_base + insn->imm)(regs[1], regs[2], regs[3], - regs[4], regs[5]); + R0 = (__bpf_call_base + insn->imm)(R1, R2, R3, R4, R5); CONT; /* JMP */ @@ -500,7 +509,7 @@ select_insn: load_word: /* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are only * appearing in the programs where ctx == skb. All programs - * keep 'ctx' in regs[CTX_REG] == R6, sk_convert_filter() + * keep 'ctx' in regs[BPF_REG_CTX] == R6, sk_convert_filter() * saves it in R6, internal BPF verifier will check that * R6 == ctx. * @@ -556,13 +565,6 @@ load_byte: /* If we ever reach this, we have a bug somewhere. */ WARN_RATELIMIT(1, "unknown opcode %02x\n", insn->code); return 0; -#undef CONT_JMP -#undef CONT - -#undef R0 -#undef X -#undef A -#undef K } u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx, @@ -594,14 +596,14 @@ static unsigned int pkt_type_offset(void) return -1; } -static u64 __skb_get_pay_offset(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) +static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) { struct sk_buff *skb = (struct sk_buff *)(long) ctx; return __skb_get_poff(skb); } -static u64 __skb_get_nlattr(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) +static u64 __skb_get_nlattr(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) { struct sk_buff *skb = (struct sk_buff *)(long) ctx; struct nlattr *nla; @@ -612,17 +614,17 @@ static u64 __skb_get_nlattr(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) if (skb->len < sizeof(struct nlattr)) return 0; - if (A > skb->len - sizeof(struct nlattr)) + if (a > skb->len - sizeof(struct nlattr)) return 0; - nla = nla_find((struct nlattr *) &skb->data[A], skb->len - A, X); + nla = nla_find((struct nlattr *) &skb->data[a], skb->len - a, x); if (nla) return (void *) nla - (void *) skb->data; return 0; } -static u64 __skb_get_nlattr_nest(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) +static u64 __skb_get_nlattr_nest(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) { struct sk_buff *skb = (struct sk_buff *)(long) ctx; struct nlattr *nla; @@ -633,27 +635,27 @@ static u64 __skb_get_nlattr_nest(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) if (skb->len < sizeof(struct nlattr)) return 0; - if (A > skb->len - sizeof(struct nlattr)) + if (a > skb->len - sizeof(struct nlattr)) return 0; - nla = (struct nlattr *) &skb->data[A]; - if (nla->nla_len > skb->len - A) + nla = (struct nlattr *) &skb->data[a]; + if (nla->nla_len > skb->len - a) return 0; - nla = nla_find_nested(nla, X); + nla = nla_find_nested(nla, x); if (nla) return (void *) nla - (void *) skb->data; return 0; } -static u64 __get_raw_cpu_id(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) +static u64 __get_raw_cpu_id(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) { return raw_smp_processor_id(); } /* note that this only generates 32-bit random numbers */ -static u64 __get_random_u32(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) +static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) { return (u64)prandom_u32(); } @@ -668,28 +670,28 @@ static bool convert_bpf_extensions(struct sock_filter *fp, BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); insn->code = BPF_LDX | BPF_MEM | BPF_H; - insn->a_reg = A_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_CTX; insn->off = offsetof(struct sk_buff, protocol); insn++; /* A = ntohs(A) [emitting a nop or swap16] */ insn->code = BPF_ALU | BPF_END | BPF_FROM_BE; - insn->a_reg = A_REG; + insn->a_reg = BPF_REG_A; insn->imm = 16; break; case SKF_AD_OFF + SKF_AD_PKTTYPE: insn->code = BPF_LDX | BPF_MEM | BPF_B; - insn->a_reg = A_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_CTX; insn->off = pkt_type_offset(); if (insn->off < 0) return false; insn++; insn->code = BPF_ALU | BPF_AND | BPF_K; - insn->a_reg = A_REG; + insn->a_reg = BPF_REG_A; insn->imm = PKT_TYPE_MAX; break; @@ -699,13 +701,13 @@ static bool convert_bpf_extensions(struct sock_filter *fp, insn->code = BPF_LDX | BPF_MEM | BPF_DW; else insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = TMP_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_TMP; + insn->x_reg = BPF_REG_CTX; insn->off = offsetof(struct sk_buff, dev); insn++; insn->code = BPF_JMP | BPF_JNE | BPF_K; - insn->a_reg = TMP_REG; + insn->a_reg = BPF_REG_TMP; insn->imm = 0; insn->off = 1; insn++; @@ -716,8 +718,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4); BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2); - insn->a_reg = A_REG; - insn->x_reg = TMP_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_TMP; if (fp->k == SKF_AD_OFF + SKF_AD_IFINDEX) { insn->code = BPF_LDX | BPF_MEM | BPF_W; @@ -732,8 +734,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = A_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_CTX; insn->off = offsetof(struct sk_buff, mark); break; @@ -741,8 +743,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = A_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_CTX; insn->off = offsetof(struct sk_buff, hash); break; @@ -750,8 +752,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); insn->code = BPF_LDX | BPF_MEM | BPF_H; - insn->a_reg = A_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_CTX; insn->off = offsetof(struct sk_buff, queue_mapping); break; @@ -760,8 +762,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); insn->code = BPF_LDX | BPF_MEM | BPF_H; - insn->a_reg = A_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_CTX; insn->off = offsetof(struct sk_buff, vlan_tci); insn++; @@ -769,16 +771,16 @@ static bool convert_bpf_extensions(struct sock_filter *fp, if (fp->k == SKF_AD_OFF + SKF_AD_VLAN_TAG) { insn->code = BPF_ALU | BPF_AND | BPF_K; - insn->a_reg = A_REG; + insn->a_reg = BPF_REG_A; insn->imm = ~VLAN_TAG_PRESENT; } else { insn->code = BPF_ALU | BPF_RSH | BPF_K; - insn->a_reg = A_REG; + insn->a_reg = BPF_REG_A; insn->imm = 12; insn++; insn->code = BPF_ALU | BPF_AND | BPF_K; - insn->a_reg = A_REG; + insn->a_reg = BPF_REG_A; insn->imm = 1; } break; @@ -790,20 +792,20 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_RANDOM: /* arg1 = ctx */ insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = ARG1_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_ARG1; + insn->x_reg = BPF_REG_CTX; insn++; /* arg2 = A */ insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = ARG2_REG; - insn->x_reg = A_REG; + insn->a_reg = BPF_REG_ARG2; + insn->x_reg = BPF_REG_A; insn++; /* arg3 = X */ insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = ARG3_REG; - insn->x_reg = X_REG; + insn->a_reg = BPF_REG_ARG3; + insn->x_reg = BPF_REG_X; insn++; /* Emit call(ctx, arg2=A, arg3=X) */ @@ -829,8 +831,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_ALU_XOR_X: insn->code = BPF_ALU | BPF_XOR | BPF_X; - insn->a_reg = A_REG; - insn->x_reg = X_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_X; break; default: @@ -880,7 +882,7 @@ int sk_convert_filter(struct sock_filter *prog, int len, u8 bpf_src; BUILD_BUG_ON(BPF_MEMWORDS * sizeof(u32) > MAX_BPF_STACK); - BUILD_BUG_ON(FP_REG + 1 != MAX_BPF_REG); + BUILD_BUG_ON(BPF_REG_FP + 1 != MAX_BPF_REG); if (len <= 0 || len >= BPF_MAXINSNS) return -EINVAL; @@ -897,8 +899,8 @@ do_pass: if (new_insn) { new_insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - new_insn->a_reg = CTX_REG; - new_insn->x_reg = ARG1_REG; + new_insn->a_reg = BPF_REG_CTX; + new_insn->x_reg = BPF_REG_ARG1; } new_insn++; @@ -948,8 +950,8 @@ do_pass: break; insn->code = fp->code; - insn->a_reg = A_REG; - insn->x_reg = X_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_X; insn->imm = fp->k; break; @@ -983,16 +985,16 @@ do_pass: * in compare insn. */ insn->code = BPF_ALU | BPF_MOV | BPF_K; - insn->a_reg = TMP_REG; + insn->a_reg = BPF_REG_TMP; insn->imm = fp->k; insn++; - insn->a_reg = A_REG; - insn->x_reg = TMP_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_TMP; bpf_src = BPF_X; } else { - insn->a_reg = A_REG; - insn->x_reg = X_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_X; insn->imm = fp->k; bpf_src = BPF_SRC(fp->code); } @@ -1027,33 +1029,33 @@ do_pass: /* ldxb 4 * ([14] & 0xf) is remaped into 6 insns. */ case BPF_LDX | BPF_MSH | BPF_B: insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = TMP_REG; - insn->x_reg = A_REG; + insn->a_reg = BPF_REG_TMP; + insn->x_reg = BPF_REG_A; insn++; insn->code = BPF_LD | BPF_ABS | BPF_B; - insn->a_reg = A_REG; + insn->a_reg = BPF_REG_A; insn->imm = fp->k; insn++; insn->code = BPF_ALU | BPF_AND | BPF_K; - insn->a_reg = A_REG; + insn->a_reg = BPF_REG_A; insn->imm = 0xf; insn++; insn->code = BPF_ALU | BPF_LSH | BPF_K; - insn->a_reg = A_REG; + insn->a_reg = BPF_REG_A; insn->imm = 2; insn++; insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = X_REG; - insn->x_reg = A_REG; + insn->a_reg = BPF_REG_X; + insn->x_reg = BPF_REG_A; insn++; insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = A_REG; - insn->x_reg = TMP_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_TMP; break; /* RET_K, RET_A are remaped into 2 insns. */ @@ -1063,7 +1065,7 @@ do_pass: (BPF_RVAL(fp->code) == BPF_K ? BPF_K : BPF_X); insn->a_reg = 0; - insn->x_reg = A_REG; + insn->x_reg = BPF_REG_A; insn->imm = fp->k; insn++; @@ -1074,8 +1076,9 @@ do_pass: case BPF_ST: case BPF_STX: insn->code = BPF_STX | BPF_MEM | BPF_W; - insn->a_reg = FP_REG; - insn->x_reg = fp->code == BPF_ST ? A_REG : X_REG; + insn->a_reg = BPF_REG_FP; + insn->x_reg = fp->code == BPF_ST ? + BPF_REG_A : BPF_REG_X; insn->off = -(BPF_MEMWORDS - fp->k) * 4; break; @@ -1084,8 +1087,8 @@ do_pass: case BPF_LDX | BPF_MEM: insn->code = BPF_LDX | BPF_MEM | BPF_W; insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? - A_REG : X_REG; - insn->x_reg = FP_REG; + BPF_REG_A : BPF_REG_X; + insn->x_reg = BPF_REG_FP; insn->off = -(BPF_MEMWORDS - fp->k) * 4; break; @@ -1094,22 +1097,22 @@ do_pass: case BPF_LDX | BPF_IMM: insn->code = BPF_ALU | BPF_MOV | BPF_K; insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? - A_REG : X_REG; + BPF_REG_A : BPF_REG_X; insn->imm = fp->k; break; /* X = A */ case BPF_MISC | BPF_TAX: insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = X_REG; - insn->x_reg = A_REG; + insn->a_reg = BPF_REG_X; + insn->x_reg = BPF_REG_A; break; /* A = X */ case BPF_MISC | BPF_TXA: insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = A_REG; - insn->x_reg = X_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_X; break; /* A = skb->len or X = skb->len */ @@ -1117,16 +1120,16 @@ do_pass: case BPF_LDX | BPF_W | BPF_LEN: insn->code = BPF_LDX | BPF_MEM | BPF_W; insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? - A_REG : X_REG; - insn->x_reg = CTX_REG; + BPF_REG_A : BPF_REG_X; + insn->x_reg = BPF_REG_CTX; insn->off = offsetof(struct sk_buff, len); break; /* access seccomp_data fields */ case BPF_LDX | BPF_ABS | BPF_W: insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = A_REG; - insn->x_reg = CTX_REG; + insn->a_reg = BPF_REG_A; + insn->x_reg = BPF_REG_CTX; insn->off = fp->k; break; -- cgit v1.2.3 From fd3c269f8ff940cc0fbb3b7f7e84c0572f6f759a Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 17 Apr 2014 10:37:35 +0800 Subject: drm/i915: Split the BDW device definition to prepare for dual BSD rings on BDW GT3 Based on the hardware spec, the BDW GT3 has the different configuration with the BDW GT1/GT2. So split the BDW device info definition. This is to do the preparation for adding the Dual BSD rings on BDW GT3 machine. V1->V2: Follow Daniel's comment to pay attention to the stolen check for BDW in kernel/early-quirks.c Reviewed-by: Imre Deak Signed-off-by: Zhao Yakui Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 26 ++++++++++++++++++++++++-- include/drm/i915_pciids.h | 22 +++++++++++++++++----- 2 files changed, 41 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f2ca3e929cd7..9f47aab7915f 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -279,6 +279,26 @@ static const struct intel_device_info intel_broadwell_m_info = { GEN_DEFAULT_PIPEOFFSETS, }; +static const struct intel_device_info intel_broadwell_gt3d_info = { + .gen = 8, .num_pipes = 3, + .need_gfx_hws = 1, .has_hotplug = 1, + .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, + .has_llc = 1, + .has_ddi = 1, + .has_fbc = 1, + GEN_DEFAULT_PIPEOFFSETS, +}; + +static const struct intel_device_info intel_broadwell_gt3m_info = { + .gen = 8, .is_mobile = 1, .num_pipes = 3, + .need_gfx_hws = 1, .has_hotplug = 1, + .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, + .has_llc = 1, + .has_ddi = 1, + .has_fbc = 1, + GEN_DEFAULT_PIPEOFFSETS, +}; + /* * Make sure any device matches here are from most specific to most * general. For example, since the Quanta match is based on the subsystem @@ -311,8 +331,10 @@ static const struct intel_device_info intel_broadwell_m_info = { INTEL_HSW_M_IDS(&intel_haswell_m_info), \ INTEL_VLV_M_IDS(&intel_valleyview_m_info), \ INTEL_VLV_D_IDS(&intel_valleyview_d_info), \ - INTEL_BDW_M_IDS(&intel_broadwell_m_info), \ - INTEL_BDW_D_IDS(&intel_broadwell_d_info) + INTEL_BDW_GT12M_IDS(&intel_broadwell_m_info), \ + INTEL_BDW_GT12D_IDS(&intel_broadwell_d_info), \ + INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info), \ + INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info) static const struct pci_device_id pciidlist[] = { /* aka */ INTEL_PCI_IDS, diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 940ece4934ba..24f3cad045db 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -223,14 +223,26 @@ _INTEL_BDW_D(gt, 0x160A, info), /* Server */ \ _INTEL_BDW_D(gt, 0x160D, info) /* Workstation */ -#define INTEL_BDW_M_IDS(info) \ +#define INTEL_BDW_GT12M_IDS(info) \ _INTEL_BDW_M_IDS(1, info), \ - _INTEL_BDW_M_IDS(2, info), \ - _INTEL_BDW_M_IDS(3, info) + _INTEL_BDW_M_IDS(2, info) -#define INTEL_BDW_D_IDS(info) \ +#define INTEL_BDW_GT12D_IDS(info) \ _INTEL_BDW_D_IDS(1, info), \ - _INTEL_BDW_D_IDS(2, info), \ + _INTEL_BDW_D_IDS(2, info) + +#define INTEL_BDW_GT3M_IDS(info) \ + _INTEL_BDW_M_IDS(3, info) + +#define INTEL_BDW_GT3D_IDS(info) \ _INTEL_BDW_D_IDS(3, info) +#define INTEL_BDW_M_IDS(info) \ + INTEL_BDW_GT12M_IDS(info), \ + INTEL_BDW_GT3M_IDS(info) + +#define INTEL_BDW_D_IDS(info) \ + INTEL_BDW_GT12D_IDS(info), \ + INTEL_BDW_GT3D_IDS(info) + #endif /* _I915_PCIIDS_H */ -- cgit v1.2.3 From 9da93f9b7cdf8ab28da6b364cdc1fafc8670b4dc Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Mon, 5 May 2014 17:25:50 +1000 Subject: xfs: fix Q_XQUOTARM ioctl The Q_XQUOTARM quotactl was not working properly, because we weren't passing around proper flags. The xfs_fs_set_xstate() ioctl handler used the same flags for Q_XQUOTAON/OFF as well as for Q_XQUOTARM, but Q_XQUOTAON/OFF look for XFS_UQUOTA_ACCT, XFS_UQUOTA_ENFD, XFS_GQUOTA_ACCT etc, i.e. quota type + state, while Q_XQUOTARM looks only for the type of quota, i.e. XFS_DQ_USER, XFS_DQ_GROUP etc. Unfortunately these flag spaces overlap a bit, so we got semi-random results for Q_XQUOTARM; i.e. the value for XFS_DQ_USER == XFS_UQUOTA_ACCT, etc. yeargh. Add a new quotactl op vector specifically for the QUOTARM operation, since it operates with a different flag space. This has been broken more or less forever, AFAICT. Signed-off-by: Eric Sandeen Acked-by: Jan Kara Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner --- fs/quota/quota.c | 14 +++++++++++++- fs/xfs/xfs_quotaops.c | 29 +++++++++++++++++++++++++---- include/linux/quota.h | 1 + 3 files changed, 39 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 2b363e23f36e..ff3f0b3cfdb3 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -278,6 +278,17 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id, return ret; } +static int quota_rmxquota(struct super_block *sb, void __user *addr) +{ + __u32 flags; + + if (copy_from_user(&flags, addr, sizeof(flags))) + return -EFAULT; + if (!sb->s_qcop->rm_xquota) + return -ENOSYS; + return sb->s_qcop->rm_xquota(sb, flags); +} + /* Copy parameters and call proper function */ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void __user *addr, struct path *path) @@ -316,8 +327,9 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, return sb->s_qcop->quota_sync(sb, type); case Q_XQUOTAON: case Q_XQUOTAOFF: - case Q_XQUOTARM: return quota_setxstate(sb, cmd, addr); + case Q_XQUOTARM: + return quota_rmxquota(sb, addr); case Q_XGETQSTAT: return quota_getxstate(sb, addr); case Q_XGETQSTATV: diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index af33cafe69b6..2ad1b9822e92 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c @@ -100,15 +100,35 @@ xfs_fs_set_xstate( if (!XFS_IS_QUOTA_ON(mp)) return -EINVAL; return -xfs_qm_scall_quotaoff(mp, flags); - case Q_XQUOTARM: - if (XFS_IS_QUOTA_ON(mp)) - return -EINVAL; - return -xfs_qm_scall_trunc_qfiles(mp, flags); } return -EINVAL; } +STATIC int +xfs_fs_rm_xquota( + struct super_block *sb, + unsigned int uflags) +{ + struct xfs_mount *mp = XFS_M(sb); + unsigned int flags = 0; + + if (sb->s_flags & MS_RDONLY) + return -EROFS; + + if (XFS_IS_QUOTA_ON(mp)) + return -EINVAL; + + if (uflags & FS_USER_QUOTA) + flags |= XFS_DQ_USER; + if (uflags & FS_GROUP_QUOTA) + flags |= XFS_DQ_GROUP; + if (uflags & FS_USER_QUOTA) + flags |= XFS_DQ_PROJ; + + return -xfs_qm_scall_trunc_qfiles(mp, flags); +} + STATIC int xfs_fs_get_dqblk( struct super_block *sb, @@ -149,6 +169,7 @@ const struct quotactl_ops xfs_quotactl_operations = { .get_xstatev = xfs_fs_get_xstatev, .get_xstate = xfs_fs_get_xstate, .set_xstate = xfs_fs_set_xstate, + .rm_xquota = xfs_fs_rm_xquota, .get_dqblk = xfs_fs_get_dqblk, .set_dqblk = xfs_fs_set_dqblk, }; diff --git a/include/linux/quota.h b/include/linux/quota.h index cc7494a35429..0f3c5d38da1f 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -329,6 +329,7 @@ struct quotactl_ops { int (*get_xstate)(struct super_block *, struct fs_quota_stat *); int (*set_xstate)(struct super_block *, unsigned int, int); int (*get_xstatev)(struct super_block *, struct fs_quota_statv *); + int (*rm_xquota)(struct super_block *, unsigned int); }; struct quota_format_type { -- cgit v1.2.3 From 5d02edfc3957446fd625c0b018e14c6631a791f4 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Sat, 19 Apr 2014 00:22:00 +0100 Subject: iio: hid-sensors: Convert units and exponent HID sensor hub specify a default unit and alternative units. This along with unit exponent can be used adjust scale. This change change HID sensor data units to IIO defined units for each sensor type. So in this way user space can use a simply use: "(data + offset) * scale" to get final result. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- .../iio/common/hid-sensors/hid-sensor-attributes.c | 114 +++++++++++++++++++++ include/linux/hid-sensor-hub.h | 4 + 2 files changed, 118 insertions(+) (limited to 'include') diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 75b54730a963..e61b1faa1e06 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -26,6 +26,40 @@ #include #include +struct { + u32 usage_id; + int unit; /* 0 for default others from HID sensor spec */ + int scale_val0; /* scale, whole number */ + int scale_val1; /* scale, fraction in micros */ +} static unit_conversion[] = { + {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650}, + {HID_USAGE_SENSOR_ACCEL_3D, + HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0}, + {HID_USAGE_SENSOR_ACCEL_3D, + HID_USAGE_SENSOR_UNITS_G, 9, 806650}, + + {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453}, + {HID_USAGE_SENSOR_GYRO_3D, + HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0}, + {HID_USAGE_SENSOR_GYRO_3D, + HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453}, + + {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000}, + {HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0}, + + {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453}, + {HID_USAGE_SENSOR_INCLINOMETER_3D, + HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453}, + {HID_USAGE_SENSOR_INCLINOMETER_3D, + HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0}, + + {HID_USAGE_SENSOR_ALS, 0, 1, 0}, + {HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0}, + + {HID_USAGE_SENSOR_PRESSURE, 0, 100000, 0}, + {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 1, 0}, +}; + static int pow_10(unsigned power) { int i; @@ -209,6 +243,86 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, } EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); +/* + * This fuction applies the unit exponent to the scale. + * For example: + * 9.806650 ->exp:2-> val0[980]val1[665000] + * 9.000806 ->exp:2-> val0[900]val1[80600] + * 0.174535 ->exp:2-> val0[17]val1[453500] + * 1.001745 ->exp:0-> val0[1]val1[1745] + * 1.001745 ->exp:2-> val0[100]val1[174500] + * 1.001745 ->exp:4-> val0[10017]val1[450000] + * 9.806650 ->exp:-2-> val0[0]val1[98066] + */ +static void adjust_exponent_micro(int *val0, int *val1, int scale0, + int scale1, int exp) +{ + int i; + int x; + int res; + int rem; + + if (exp > 0) { + *val0 = scale0 * pow_10(exp); + res = 0; + if (exp > 6) { + *val1 = 0; + return; + } + for (i = 0; i < exp; ++i) { + x = scale1 / pow_10(5 - i); + res += (pow_10(exp - 1 - i) * x); + scale1 = scale1 % pow_10(5 - i); + } + *val0 += res; + *val1 = scale1 * pow_10(exp); + } else if (exp < 0) { + exp = abs(exp); + if (exp > 6) { + *val0 = *val1 = 0; + return; + } + *val0 = scale0 / pow_10(exp); + rem = scale0 % pow_10(exp); + res = 0; + for (i = 0; i < (6 - exp); ++i) { + x = scale1 / pow_10(5 - i); + res += (pow_10(5 - exp - i) * x); + scale1 = scale1 % pow_10(5 - i); + } + *val1 = rem * pow_10(6 - exp) + res; + } else { + *val0 = scale0; + *val1 = scale1; + } +} + +int hid_sensor_format_scale(u32 usage_id, + struct hid_sensor_hub_attribute_info *attr_info, + int *val0, int *val1) +{ + int i; + int exp; + + *val0 = 1; + *val1 = 0; + + for (i = 0; ARRAY_SIZE(unit_conversion); ++i) { + if (unit_conversion[i].usage_id == usage_id && + unit_conversion[i].unit == attr_info->units) { + exp = hid_sensor_convert_exponent( + attr_info->unit_expo); + adjust_exponent_micro(val0, val1, + unit_conversion[i].scale_val0, + unit_conversion[i].scale_val1, exp); + break; + } + } + + return IIO_VAL_INT_PLUS_MICRO; +} +EXPORT_SYMBOL(hid_sensor_format_scale); + int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, u32 usage_id, struct hid_sensor_common *st) diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index b70cfd7ff29c..89626b23c246 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -223,4 +223,8 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, int hid_sensor_get_usage_index(struct hid_sensor_hub_device *hsdev, u32 report_id, int field_index, u32 usage_id); +int hid_sensor_format_scale(u32 usage_id, + struct hid_sensor_hub_attribute_info *attr_info, + int *val0, int *val1); + #endif -- cgit v1.2.3 From 9030924510a9e7d4b7d218749533840075879f2f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Sat, 19 Apr 2014 00:22:00 +0100 Subject: iio: hid-sensors: Add api to get poll value Added interface to get poll value in milli-seconds. This value is changed by changing sampling frequency. This API allows clients to wait for at least some poll milli seconds before reading a new sample. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- .../iio/common/hid-sensors/hid-sensor-attributes.c | 20 ++++++++++++++++++++ include/linux/hid-sensor-hub.h | 2 ++ 2 files changed, 22 insertions(+) (limited to 'include') diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index e61b1faa1e06..372964635ccf 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -147,6 +147,26 @@ static u32 convert_to_vtf_format(int size, int exp, int val1, int val2) return value; } +s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) +{ + s32 value = 0; + int ret; + + ret = sensor_hub_get_feature(st->hsdev, + st->poll.report_id, + st->poll.index, &value); + + if (ret < 0 || value < 0) { + return -EINVAL; + } else { + if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) + value = value * 1000; + } + + return value; +} +EXPORT_SYMBOL(hid_sensor_read_poll_value); + int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, int *val1, int *val2) { diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index 89626b23c246..88d8d636a68f 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -227,4 +227,6 @@ int hid_sensor_format_scale(u32 usage_id, struct hid_sensor_hub_attribute_info *attr_info, int *val0, int *val1); +s32 hid_sensor_read_poll_value(struct hid_sensor_common *st); + #endif -- cgit v1.2.3 From 56ff6be608659ac06d4e3cc5827476efa29d610f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Sat, 19 Apr 2014 00:22:00 +0100 Subject: iio: hid-sensors: Add API to power on/off Added an API to allow client drivers to turn ON and OFF sensors for quick read. Added data_read as counting varaible instead of boolean, so that sensor is powered off only when last user released it. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 7 +++---- drivers/iio/common/hid-sensors/hid-sensor-trigger.c | 17 +++++++++++++---- drivers/iio/common/hid-sensors/hid-sensor-trigger.h | 1 + drivers/iio/gyro/hid-sensor-gyro-3d.c | 7 +++---- drivers/iio/light/hid-sensor-als.c | 7 +++---- drivers/iio/light/hid-sensor-prox.c | 7 +++---- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 7 +++---- drivers/iio/orientation/hid-sensor-incl-3d.c | 7 +++---- drivers/iio/orientation/hid-sensor-rotation.c | 8 +++----- drivers/iio/pressure/hid-sensor-press.c | 7 +++---- include/linux/hid-sensor-hub.h | 2 +- 11 files changed, 39 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index ca50a91752d8..cf61c87a47e9 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -201,9 +201,8 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "accel_3d_proc_event [%d]\n", - accel_state->common_attributes.data_ready); - if (accel_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n"); + if (atomic_read(&accel_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, accel_state->accel_val, sizeof(accel_state->accel_val)); @@ -342,7 +341,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - accel_state->common_attributes.data_ready = false; + atomic_set(&accel_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &accel_state->common_attributes); if (ret < 0) { diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index dbefbdaf7cd1..73282cee0c81 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -28,16 +28,17 @@ #include #include "hid-sensor-trigger.h" -static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) +int hid_sensor_power_state(struct hid_sensor_common *st, bool state) { - struct hid_sensor_common *st = iio_trigger_get_drvdata(trig); int state_val; int report_val; if (state) { if (sensor_hub_device_open(st->hsdev)) return -EIO; + + atomic_inc(&st->data_ready); + state_val = hid_sensor_get_usage_index(st->hsdev, st->power_state.report_id, st->power_state.index, @@ -47,6 +48,8 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, st->report_state.index, HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM); } else { + if (!atomic_dec_and_test(&st->data_ready)) + return 0; sensor_hub_device_close(st->hsdev); state_val = hid_sensor_get_usage_index(st->hsdev, st->power_state.report_id, @@ -57,7 +60,6 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, st->report_state.index, HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM); } - st->data_ready = state; if (state_val >= 0) { state_val += st->power_state.logical_minimum; @@ -75,6 +77,13 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, return 0; } +EXPORT_SYMBOL(hid_sensor_power_state); + +static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + return hid_sensor_power_state(iio_trigger_get_drvdata(trig), state); +} void hid_sensor_remove_trigger(struct hid_sensor_common *attrb) { diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h index ca02f7811aa8..0f8e78c249d3 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -22,5 +22,6 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, struct hid_sensor_common *attrb); void hid_sensor_remove_trigger(struct hid_sensor_common *attrb); +int hid_sensor_power_state(struct hid_sensor_common *st, bool state); #endif diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 53ac06040fbe..392c30b8cd74 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -201,9 +201,8 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "gyro_3d_proc_event [%d]\n", - gyro_state->common_attributes.data_ready); - if (gyro_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n"); + if (atomic_read(&gyro_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, gyro_state->gyro_val, sizeof(gyro_state->gyro_val)); @@ -339,7 +338,7 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - gyro_state->common_attributes.data_ready = false; + atomic_set(&gyro_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &gyro_state->common_attributes); if (ret < 0) { diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 39b50be9d456..e124b395f320 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -180,9 +180,8 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "als_proc_event [%d]\n", - als_state->common_attributes.data_ready); - if (als_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "als_proc_event\n"); + if (atomic_read(&als_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, &als_state->illum, sizeof(als_state->illum)); @@ -305,7 +304,7 @@ static int hid_als_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - als_state->common_attributes.data_ready = false; + atomic_set(&als_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &als_state->common_attributes); if (ret < 0) { diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 1894ab196f97..07e98ec8e9f1 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -176,9 +176,8 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "prox_proc_event [%d]\n", - prox_state->common_attributes.data_ready); - if (prox_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "prox_proc_event\n"); + if (atomic_read(&prox_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, &prox_state->human_presence, sizeof(prox_state->human_presence)); @@ -297,7 +296,7 @@ static int hid_prox_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - prox_state->common_attributes.data_ready = false; + atomic_set(&prox_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &prox_state->common_attributes); if (ret) { diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index 131ced0dcb1c..54eea6a17061 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -202,9 +202,8 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "magn_3d_proc_event [%d]\n", - magn_state->common_attributes.data_ready); - if (magn_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n"); + if (atomic_read(&magn_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, magn_state->magn_val, sizeof(magn_state->magn_val)); @@ -343,7 +342,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - magn_state->common_attributes.data_ready = false; + atomic_set(&magn_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &magn_state->common_attributes); if (ret < 0) { diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index f0c465cc192a..bf11678dd04e 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -200,9 +200,8 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "incl_3d_proc_event [%d]\n", - incl_state->common_attributes.data_ready); - if (incl_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "incl_3d_proc_event\n"); + if (atomic_read(&incl_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, (u8 *)incl_state->incl_val, sizeof(incl_state->incl_val)); @@ -358,7 +357,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - incl_state->common_attributes.data_ready = false; + atomic_set(&incl_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &incl_state->common_attributes); if (ret) { diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 51387bbc1ce1..dccf848e8b0f 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -145,10 +145,8 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct dev_rot_state *rot_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "dev_rot_proc_event [%d]\n", - rot_state->common_attributes.data_ready); - - if (rot_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n"); + if (atomic_read(&rot_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, (u8 *)rot_state->sampled_vals, sizeof(rot_state->sampled_vals)); @@ -272,7 +270,7 @@ static int hid_dev_rot_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); return ret; } - rot_state->common_attributes.data_ready = false; + atomic_set(&rot_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &rot_state->common_attributes); if (ret) { diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index ff69da4443b8..39df50c45dab 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -180,9 +180,8 @@ static int press_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct press_state *press_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "press_proc_event [%d]\n", - press_state->common_attributes.data_ready); - if (press_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "press_proc_event\n"); + if (atomic_read(&press_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, &press_state->press_data, sizeof(press_state->press_data)); @@ -307,7 +306,7 @@ static int hid_press_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - press_state->common_attributes.data_ready = false; + atomic_set(&press_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &press_state->common_attributes); if (ret) { diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index 88d8d636a68f..51f7ccadf923 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -189,7 +189,7 @@ struct hid_sensor_common { struct hid_sensor_hub_device *hsdev; struct platform_device *pdev; unsigned usage_id; - bool data_ready; + atomic_t data_ready; struct iio_trigger *trigger; struct hid_sensor_hub_attribute_info poll; struct hid_sensor_hub_attribute_info report_state; -- cgit v1.2.3 From 792e6aa7a15ea0fb16f8687e93caede1ea9118c7 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 30 Apr 2014 16:14:23 +0300 Subject: cfg80211: add cfg80211_sched_scan_stopped_rtnl Add locked-version for cfg80211_sched_scan_stopped. This is used for some users that might want to call it when rtnl is already locked. Fixes: d43c6b6 ("mac80211: reschedule sched scan after HW restart") Cc: stable@vger.kernel.org (3.14+) Signed-off-by: Eliad Peller Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 12 ++++++++++++ net/wireless/scan.c | 12 ++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f3539a15c411..f856e5a746fa 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3668,6 +3668,18 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy); */ void cfg80211_sched_scan_stopped(struct wiphy *wiphy); +/** + * cfg80211_sched_scan_stopped_rtnl - notify that the scheduled scan has stopped + * + * @wiphy: the wiphy on which the scheduled scan stopped + * + * The driver can call this function to inform cfg80211 that the + * scheduled scan had to be stopped, for whatever reason. The driver + * is then called back via the sched_scan_stop operation when done. + * This function should be called with rtnl locked. + */ +void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy); + /** * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame * diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 7d09a712cb1f..88f108edfb58 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -284,14 +284,22 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy) } EXPORT_SYMBOL(cfg80211_sched_scan_results); -void cfg80211_sched_scan_stopped(struct wiphy *wiphy) +void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + ASSERT_RTNL(); + trace_cfg80211_sched_scan_stopped(wiphy); - rtnl_lock(); __cfg80211_stop_sched_scan(rdev, true); +} +EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl); + +void cfg80211_sched_scan_stopped(struct wiphy *wiphy) +{ + rtnl_lock(); + cfg80211_sched_scan_stopped_rtnl(wiphy); rtnl_unlock(); } EXPORT_SYMBOL(cfg80211_sched_scan_stopped); -- cgit v1.2.3 From 8c1eb25326552bfe6912ea160dfb3de0207a7550 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 11 Mar 2014 11:23:50 +0100 Subject: of: Spelling s/anonymouns/anonymous/ Signed-off-by: Geert Uytterhoeven Cc: Grant Likely Cc: devicetree@vger.kernel.org Signed-off-by: Jiri Kosina --- include/linux/of_platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 05cb4a928252..8cdd53bf1114 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -37,7 +37,7 @@ * Note: Using an auxdata lookup table should be considered a last resort when * converting a platform to use the DT. Normally the automatically generated * device name will not matter, and drivers should obtain data from the device - * node instead of from an anonymouns platform_data pointer. + * node instead of from an anonymous platform_data pointer. */ struct of_dev_auxdata { char *compatible; -- cgit v1.2.3 From 325483a9bf8a878e94541195f6f27f39dd393db3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 11 Mar 2014 11:23:47 +0100 Subject: wimax: Spelling s/than/that/, wording s/destinatary/recipient/ Signed-off-by: Geert Uytterhoeven Cc: Inaky Perez-Gonzalez Cc: wimax@linuxwimax.org Signed-off-by: Jiri Kosina --- include/net/wimax.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/wimax.h b/include/net/wimax.h index 98498e1daa06..e52ef5357e08 100644 --- a/include/net/wimax.h +++ b/include/net/wimax.h @@ -483,8 +483,8 @@ void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state); * Be sure not to modify skb->data in the middle (ie: don't use * skb_push()/skb_pull()/skb_reserve() on the skb). * - * "pipe_name" is any string, than can be interpreted as the name of - * the pipe or destinatary; the interpretation of it is driver + * "pipe_name" is any string, that can be interpreted as the name of + * the pipe or recipient; the interpretation of it is driver * specific, so the recipient can multiplex it as wished. It can be * NULL, it won't be used - an example is using a "diagnostics" tag to * send diagnostics information that a device-specific diagnostics -- cgit v1.2.3 From 0c4972ccaa27620fe4281ac5c8c536978a563345 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 1 May 2014 10:17:27 +0300 Subject: mac80211: set an external flag for TDLS stations Expose a new tdls flag for the public ieee80211_sta struct. This can be used in some rate control decisions. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ net/mac80211/cfg.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 451c1bf00df9..bdb4a7cbab31 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1374,6 +1374,7 @@ struct ieee80211_sta_rates { * the station moves to associated state. * @smps_mode: current SMPS mode (off, static or dynamic) * @rates: rate control selection table + * @tdls: indicates whether the STA is a TDLS peer */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; @@ -1388,6 +1389,7 @@ struct ieee80211_sta { enum ieee80211_sta_rx_bandwidth bandwidth; enum ieee80211_smps_mode smps_mode; struct ieee80211_sta_rates __rcu *rates; + bool tdls; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7b8d3cf89574..d8b236633ca3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1459,6 +1459,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) { sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + } else { + sta->sta.tdls = true; } err = sta_apply_parameters(local, sta, params); -- cgit v1.2.3 From 719d93cd5f5c5c8775b7a38192069e8e1d1ac46e Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Thu, 16 Jan 2014 13:44:20 +0100 Subject: kvm/irqchip: Speed up KVM_SET_GSI_ROUTING When starting lots of dataplane devices the bootup takes very long on Christian's s390 with irqfd patches. With larger setups he is even able to trigger some timeouts in some components. Turns out that the KVM_SET_GSI_ROUTING ioctl takes very long (strace claims up to 0.1 sec) when having multiple CPUs. This is caused by the synchronize_rcu and the HZ=100 of s390. By changing the code to use a private srcu we can speed things up. This patch reduces the boot time till mounting root from 8 to 2 seconds on my s390 guest with 100 disks. Uses of hlist_for_each_entry_rcu, hlist_add_head_rcu, hlist_del_init_rcu are fine because they do not have lockdep checks (hlist_for_each_entry_rcu uses rcu_dereference_raw rather than rcu_dereference, and write-sides do not do rcu lockdep at all). Note that we're hardly relying on the "sleepable" part of srcu. We just want SRCU's faster detection of grace periods. Testing was done by Andrew Theurer using netperf tests STREAM, MAERTS and RR. The difference between results "before" and "after" the patch has mean -0.2% and standard deviation 0.6%. Using a paired t-test on the data points says that there is a 2.5% probability that the patch is the cause of the performance difference (rather than a random fluctuation). (Restricting the t-test to RR, which is the most likely to be affected, changes the numbers to respectively -0.3% mean, 0.7% stdev, and 8% probability that the numbers actually say something about the patch. The probability increases mostly because there are fewer data points). Cc: Marcelo Tosatti Cc: Michael S. Tsirkin Tested-by: Christian Borntraeger # s390 Reviewed-by: Christian Borntraeger Signed-off-by: Christian Borntraeger Signed-off-by: Paolo Bonzini --- include/linux/kvm_host.h | 1 + virt/kvm/eventfd.c | 25 +++++++++++++++---------- virt/kvm/irq_comm.c | 17 +++++++++-------- virt/kvm/irqchip.c | 31 ++++++++++++++++--------------- virt/kvm/kvm_main.c | 16 ++++++++++------ 5 files changed, 51 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 1e125b055327..970c68197c69 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -370,6 +370,7 @@ struct kvm { struct mm_struct *mm; /* userspace tied to this vm */ struct kvm_memslots *memslots; struct srcu_struct srcu; + struct srcu_struct irq_srcu; #ifdef CONFIG_KVM_APIC_ARCHITECTURE u32 bsp_vcpu_id; #endif diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 912ec5a95e2c..20c3af7692c5 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "iodev.h" @@ -118,19 +119,22 @@ static void irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian) { struct _irqfd_resampler *resampler; + struct kvm *kvm; struct _irqfd *irqfd; + int idx; resampler = container_of(kian, struct _irqfd_resampler, notifier); + kvm = resampler->kvm; - kvm_set_irq(resampler->kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID, + kvm_set_irq(kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID, resampler->notifier.gsi, 0, false); - rcu_read_lock(); + idx = srcu_read_lock(&kvm->irq_srcu); list_for_each_entry_rcu(irqfd, &resampler->list, resampler_link) eventfd_signal(irqfd->resamplefd, 1); - rcu_read_unlock(); + srcu_read_unlock(&kvm->irq_srcu, idx); } static void @@ -142,7 +146,7 @@ irqfd_resampler_shutdown(struct _irqfd *irqfd) mutex_lock(&kvm->irqfds.resampler_lock); list_del_rcu(&irqfd->resampler_link); - synchronize_rcu(); + synchronize_srcu(&kvm->irq_srcu); if (list_empty(&resampler->list)) { list_del(&resampler->link); @@ -221,17 +225,18 @@ irqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) unsigned long flags = (unsigned long)key; struct kvm_kernel_irq_routing_entry *irq; struct kvm *kvm = irqfd->kvm; + int idx; if (flags & POLLIN) { - rcu_read_lock(); - irq = rcu_dereference(irqfd->irq_entry); + idx = srcu_read_lock(&kvm->irq_srcu); + irq = srcu_dereference(irqfd->irq_entry, &kvm->irq_srcu); /* An event has been signaled, inject an interrupt */ if (irq) kvm_set_msi(irq, kvm, KVM_USERSPACE_IRQ_SOURCE_ID, 1, false); else schedule_work(&irqfd->inject); - rcu_read_unlock(); + srcu_read_unlock(&kvm->irq_srcu, idx); } if (flags & POLLHUP) { @@ -363,7 +368,7 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args) } list_add_rcu(&irqfd->resampler_link, &irqfd->resampler->list); - synchronize_rcu(); + synchronize_srcu(&kvm->irq_srcu); mutex_unlock(&kvm->irqfds.resampler_lock); } @@ -465,7 +470,7 @@ kvm_irqfd_deassign(struct kvm *kvm, struct kvm_irqfd *args) * another thread calls kvm_irq_routing_update before * we flush workqueue below (we synchronize with * kvm_irq_routing_update using irqfds.lock). - * It is paired with synchronize_rcu done by caller + * It is paired with synchronize_srcu done by caller * of that function. */ rcu_assign_pointer(irqfd->irq_entry, NULL); @@ -524,7 +529,7 @@ kvm_irqfd_release(struct kvm *kvm) /* * Change irq_routing and irqfd. - * Caller must invoke synchronize_rcu afterwards. + * Caller must invoke synchronize_srcu(&kvm->irq_srcu) afterwards. */ void kvm_irq_routing_update(struct kvm *kvm, struct kvm_irq_routing_table *irq_rt) diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c index e2e6b4473a96..ced4a542a031 100644 --- a/virt/kvm/irq_comm.c +++ b/virt/kvm/irq_comm.c @@ -163,6 +163,7 @@ int kvm_set_irq_inatomic(struct kvm *kvm, int irq_source_id, u32 irq, int level) struct kvm_kernel_irq_routing_entry *e; int ret = -EINVAL; struct kvm_irq_routing_table *irq_rt; + int idx; trace_kvm_set_irq(irq, level, irq_source_id); @@ -174,8 +175,8 @@ int kvm_set_irq_inatomic(struct kvm *kvm, int irq_source_id, u32 irq, int level) * Since there's no easy way to do this, we only support injecting MSI * which is limited to 1:1 GSI mapping. */ - rcu_read_lock(); - irq_rt = rcu_dereference(kvm->irq_routing); + idx = srcu_read_lock(&kvm->irq_srcu); + irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu); if (irq < irq_rt->nr_rt_entries) hlist_for_each_entry(e, &irq_rt->map[irq], link) { if (likely(e->type == KVM_IRQ_ROUTING_MSI)) @@ -184,7 +185,7 @@ int kvm_set_irq_inatomic(struct kvm *kvm, int irq_source_id, u32 irq, int level) ret = -EWOULDBLOCK; break; } - rcu_read_unlock(); + srcu_read_unlock(&kvm->irq_srcu, idx); return ret; } @@ -253,22 +254,22 @@ void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, mutex_lock(&kvm->irq_lock); hlist_del_rcu(&kimn->link); mutex_unlock(&kvm->irq_lock); - synchronize_rcu(); + synchronize_srcu(&kvm->irq_srcu); } void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, bool mask) { struct kvm_irq_mask_notifier *kimn; - int gsi; + int idx, gsi; - rcu_read_lock(); - gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin]; + idx = srcu_read_lock(&kvm->irq_srcu); + gsi = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu)->chip[irqchip][pin]; if (gsi != -1) hlist_for_each_entry_rcu(kimn, &kvm->mask_notifier_list, link) if (kimn->irq == gsi) kimn->func(kimn, mask); - rcu_read_unlock(); + srcu_read_unlock(&kvm->irq_srcu, idx); } int kvm_set_routing_entry(struct kvm_irq_routing_table *rt, diff --git a/virt/kvm/irqchip.c b/virt/kvm/irqchip.c index 20dc9e4a8f6c..b43c275775cd 100644 --- a/virt/kvm/irqchip.c +++ b/virt/kvm/irqchip.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include "irq.h" @@ -33,19 +34,19 @@ bool kvm_irq_has_notifier(struct kvm *kvm, unsigned irqchip, unsigned pin) { struct kvm_irq_ack_notifier *kian; - int gsi; + int gsi, idx; - rcu_read_lock(); - gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin]; + idx = srcu_read_lock(&kvm->irq_srcu); + gsi = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu)->chip[irqchip][pin]; if (gsi != -1) hlist_for_each_entry_rcu(kian, &kvm->irq_ack_notifier_list, link) if (kian->gsi == gsi) { - rcu_read_unlock(); + srcu_read_unlock(&kvm->irq_srcu, idx); return true; } - rcu_read_unlock(); + srcu_read_unlock(&kvm->irq_srcu, idx); return false; } @@ -54,18 +55,18 @@ EXPORT_SYMBOL_GPL(kvm_irq_has_notifier); void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin) { struct kvm_irq_ack_notifier *kian; - int gsi; + int gsi, idx; trace_kvm_ack_irq(irqchip, pin); - rcu_read_lock(); - gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin]; + idx = srcu_read_lock(&kvm->irq_srcu); + gsi = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu)->chip[irqchip][pin]; if (gsi != -1) hlist_for_each_entry_rcu(kian, &kvm->irq_ack_notifier_list, link) if (kian->gsi == gsi) kian->irq_acked(kian); - rcu_read_unlock(); + srcu_read_unlock(&kvm->irq_srcu, idx); } void kvm_register_irq_ack_notifier(struct kvm *kvm, @@ -85,7 +86,7 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm, mutex_lock(&kvm->irq_lock); hlist_del_init_rcu(&kian->link); mutex_unlock(&kvm->irq_lock); - synchronize_rcu(); + synchronize_srcu(&kvm->irq_srcu); #ifdef __KVM_HAVE_IOAPIC kvm_vcpu_request_scan_ioapic(kvm); #endif @@ -115,7 +116,7 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level, bool line_status) { struct kvm_kernel_irq_routing_entry *e, irq_set[KVM_NR_IRQCHIPS]; - int ret = -1, i = 0; + int ret = -1, i = 0, idx; struct kvm_irq_routing_table *irq_rt; trace_kvm_set_irq(irq, level, irq_source_id); @@ -124,12 +125,12 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level, * IOAPIC. So set the bit in both. The guest will ignore * writes to the unused one. */ - rcu_read_lock(); - irq_rt = rcu_dereference(kvm->irq_routing); + idx = srcu_read_lock(&kvm->irq_srcu); + irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu); if (irq < irq_rt->nr_rt_entries) hlist_for_each_entry(e, &irq_rt->map[irq], link) irq_set[i++] = *e; - rcu_read_unlock(); + srcu_read_unlock(&kvm->irq_srcu, idx); while(i--) { int r; @@ -226,7 +227,7 @@ int kvm_set_irq_routing(struct kvm *kvm, kvm_irq_routing_update(kvm, new); mutex_unlock(&kvm->irq_lock); - synchronize_rcu(); + synchronize_srcu_expedited(&kvm->irq_srcu); new = old; r = 0; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index fa70c6e642b4..95b4c2b3906a 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -457,11 +457,11 @@ static struct kvm *kvm_create_vm(unsigned long type) r = kvm_arch_init_vm(kvm, type); if (r) - goto out_err_nodisable; + goto out_err_no_disable; r = hardware_enable_all(); if (r) - goto out_err_nodisable; + goto out_err_no_disable; #ifdef CONFIG_HAVE_KVM_IRQCHIP INIT_HLIST_HEAD(&kvm->mask_notifier_list); @@ -473,10 +473,12 @@ static struct kvm *kvm_create_vm(unsigned long type) r = -ENOMEM; kvm->memslots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL); if (!kvm->memslots) - goto out_err_nosrcu; + goto out_err_no_srcu; kvm_init_memslots_id(kvm); if (init_srcu_struct(&kvm->srcu)) - goto out_err_nosrcu; + goto out_err_no_srcu; + if (init_srcu_struct(&kvm->irq_srcu)) + goto out_err_no_irq_srcu; for (i = 0; i < KVM_NR_BUSES; i++) { kvm->buses[i] = kzalloc(sizeof(struct kvm_io_bus), GFP_KERNEL); @@ -505,10 +507,12 @@ static struct kvm *kvm_create_vm(unsigned long type) return kvm; out_err: + cleanup_srcu_struct(&kvm->irq_srcu); +out_err_no_irq_srcu: cleanup_srcu_struct(&kvm->srcu); -out_err_nosrcu: +out_err_no_srcu: hardware_disable_all(); -out_err_nodisable: +out_err_no_disable: for (i = 0; i < KVM_NR_BUSES; i++) kfree(kvm->buses[i]); kfree(kvm->memslots); -- cgit v1.2.3 From 8757ad65d30f009fe0beeb2d70d3cd834cb998f2 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Fri, 11 Apr 2014 10:37:39 -0400 Subject: NVMe: Update copyright headers Make the copyright dates accurate and remove the final paragraph that includes the address of the FSF. Signed-off-by: Matthew Wilcox --- drivers/block/nvme-core.c | 4 ---- drivers/block/nvme-scsi.c | 6 +----- include/linux/nvme.h | 6 +----- include/uapi/linux/nvme.h | 6 +----- 4 files changed, 3 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index 7c64fa756cce..eacd64e36be3 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -10,10 +10,6 @@ * 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #include diff --git a/drivers/block/nvme-scsi.c b/drivers/block/nvme-scsi.c index 2c3f5be06da1..da3b252dea6f 100644 --- a/drivers/block/nvme-scsi.c +++ b/drivers/block/nvme-scsi.c @@ -1,6 +1,6 @@ /* * NVM Express device driver - * Copyright (c) 2011, Intel Corporation. + * Copyright (c) 2011-2014, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -10,10 +10,6 @@ * 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ /* diff --git a/include/linux/nvme.h b/include/linux/nvme.h index a50173ca1d72..cfd084cab22b 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -1,6 +1,6 @@ /* * Definitions for the NVM Express interface - * Copyright (c) 2011-2013, Intel Corporation. + * Copyright (c) 2011-2014, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -10,10 +10,6 @@ * 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _LINUX_NVME_H diff --git a/include/uapi/linux/nvme.h b/include/uapi/linux/nvme.h index 096fe1c6f83d..ad9014e49693 100644 --- a/include/uapi/linux/nvme.h +++ b/include/uapi/linux/nvme.h @@ -1,6 +1,6 @@ /* * Definitions for the NVM Express interface - * Copyright (c) 2011-2013, Intel Corporation. + * Copyright (c) 2011-2014, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -10,10 +10,6 @@ * 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _UAPI_LINUX_NVME_H -- cgit v1.2.3 From a7d2ce2832d84e0182585f63bf96ca7323b3aee7 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 29 Apr 2014 11:41:28 -0600 Subject: NVMe: Configure support for block flush This configures an nvme request_queue as flush capable if the device has a volatile write cache present. Signed-off-by: Keith Busch Signed-off-by: Matthew Wilcox --- drivers/block/nvme-core.c | 3 +++ include/linux/nvme.h | 1 + include/uapi/linux/nvme.h | 1 + 3 files changed, 5 insertions(+) (limited to 'include') diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index 025dd4cad4a6..e7c4fdb6a651 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -1897,6 +1897,8 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid, blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift); if (dev->max_hw_sectors) blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors); + if (dev->vwc & NVME_CTRL_VWC_PRESENT) + blk_queue_flush(ns->queue, REQ_FLUSH | REQ_FUA); disk->major = nvme_major; disk->first_minor = 0; @@ -2201,6 +2203,7 @@ static int nvme_dev_add(struct nvme_dev *dev) nn = le32_to_cpup(&ctrl->nn); dev->oncs = le16_to_cpup(&ctrl->oncs); dev->abort_limit = ctrl->acl + 1; + dev->vwc = ctrl->vwc; memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn)); memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn)); memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr)); diff --git a/include/linux/nvme.h b/include/linux/nvme.h index cfd084cab22b..6266373d3147 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -99,6 +99,7 @@ struct nvme_dev { u32 stripe_size; u16 oncs; u16 abort_limit; + u8 vwc; u8 initialized; }; diff --git a/include/uapi/linux/nvme.h b/include/uapi/linux/nvme.h index ad9014e49693..f090336d5bad 100644 --- a/include/uapi/linux/nvme.h +++ b/include/uapi/linux/nvme.h @@ -73,6 +73,7 @@ enum { NVME_CTRL_ONCS_COMPARE = 1 << 0, NVME_CTRL_ONCS_WRITE_UNCORRECTABLE = 1 << 1, NVME_CTRL_ONCS_DSM = 1 << 2, + NVME_CTRL_VWC_PRESENT = 1 << 0, }; struct nvme_lbaf { -- cgit v1.2.3 From 53562be74bd06bbe74d2acf3caca5398f8eeb160 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 29 Apr 2014 11:41:29 -0600 Subject: NVMe: Flush with data support It is possible a filesystem may send a flush flagged bio with write data. There is no such composite NVMe command, so the driver sends flush and write separately. The device is allowed to execute these commands in any order, so it was possible the driver ends the bio after the write completes, but while the flush is still active. We don't want to let a filesystem believe flush succeeded before it really has; this could cause data corruption on a power loss between these events. To fix, this patch splits the flush and write into chained bios. Signed-off-by: Keith Busch Signed-off-by: Matthew Wilcox --- drivers/block/nvme-core.c | 44 ++++++++++++++++++++++++-------------------- include/linux/nvme.h | 1 - 2 files changed, 24 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index e7c4fdb6a651..cd8a8bc711cc 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -197,16 +197,13 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, #define CMD_CTX_CANCELLED (0x30C + CMD_CTX_BASE) #define CMD_CTX_COMPLETED (0x310 + CMD_CTX_BASE) #define CMD_CTX_INVALID (0x314 + CMD_CTX_BASE) -#define CMD_CTX_FLUSH (0x318 + CMD_CTX_BASE) -#define CMD_CTX_ABORT (0x31C + CMD_CTX_BASE) +#define CMD_CTX_ABORT (0x318 + CMD_CTX_BASE) static void special_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { if (ctx == CMD_CTX_CANCELLED) return; - if (ctx == CMD_CTX_FLUSH) - return; if (ctx == CMD_CTX_ABORT) { ++nvmeq->dev->abort_limit; return; @@ -629,16 +626,6 @@ static int nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns, return 0; } -int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns) -{ - int cmdid = alloc_cmdid(nvmeq, (void *)CMD_CTX_FLUSH, - special_completion, NVME_IO_TIMEOUT); - if (unlikely(cmdid < 0)) - return cmdid; - - return nvme_submit_flush(nvmeq, ns, cmdid); -} - static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod) { struct bio *bio = iod->private; @@ -654,7 +641,7 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod) if (bio->bi_rw & REQ_DISCARD) return nvme_submit_discard(nvmeq, ns, bio, iod, cmdid); - if ((bio->bi_rw & REQ_FLUSH) && !iod->nents) + if (bio->bi_rw & REQ_FLUSH) return nvme_submit_flush(nvmeq, ns, cmdid); control = 0; @@ -688,6 +675,26 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod) return 0; } +static int nvme_split_flush_data(struct nvme_queue *nvmeq, struct bio *bio) +{ + struct bio *split = bio_clone(bio, GFP_ATOMIC); + if (!split) + return -ENOMEM; + + split->bi_iter.bi_size = 0; + split->bi_phys_segments = 0; + bio->bi_rw &= ~REQ_FLUSH; + bio_chain(split, bio); + + if (!waitqueue_active(&nvmeq->sq_full)) + add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); + bio_list_add(&nvmeq->sq_cong, split); + bio_list_add(&nvmeq->sq_cong, bio); + wake_up_process(nvme_thread); + + return 0; +} + /* * Called with local interrupts disabled and the q_lock held. May not sleep. */ @@ -698,11 +705,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, int psegs = bio_phys_segments(ns->queue, bio); int result; - if ((bio->bi_rw & REQ_FLUSH) && psegs) { - result = nvme_submit_flush_data(nvmeq, ns); - if (result) - return result; - } + if ((bio->bi_rw & REQ_FLUSH) && psegs) + return nvme_split_flush_data(nvmeq, bio); iod = nvme_alloc_iod(psegs, bio->bi_iter.bi_size, GFP_ATOMIC); if (!iod) diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 6266373d3147..1813cfdb7e80 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -156,7 +156,6 @@ struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write, void nvme_unmap_user_pages(struct nvme_dev *dev, int write, struct nvme_iod *iod); int nvme_submit_io_cmd(struct nvme_dev *, struct nvme_command *, u32 *); -int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns); int nvme_submit_admin_cmd(struct nvme_dev *, struct nvme_command *, u32 *result); int nvme_identify(struct nvme_dev *, unsigned nsid, unsigned cns, -- cgit v1.2.3 From 07064c6e022ba8dc0c86ce12f7851a1de24e04fc Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 2 May 2014 16:28:03 -0700 Subject: net: Allow csum_add to be provided in arch csum_add is really nothing more then add-with-carry which can be implemented efficiently in some architectures. Allow architecture to define this protected by HAVE_ARCH_CSUM_ADD. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/checksum.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/checksum.h b/include/net/checksum.h index a28f4e0f6251..87cb1903640d 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -57,12 +57,14 @@ static __inline__ __wsum csum_and_copy_to_user } #endif +#ifndef HAVE_ARCH_CSUM_ADD static inline __wsum csum_add(__wsum csum, __wsum addend) { u32 res = (__force u32)csum; res += (__force u32)addend; return (__force __wsum)(res + (res < (__force u32)addend)); } +#endif static inline __wsum csum_sub(__wsum csum, __wsum addend) { -- cgit v1.2.3 From 76ba0aae673075c77a8b775e9133c8e8b1a44563 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 2 May 2014 16:29:18 -0700 Subject: net: Generalize checksum_init functions Create a general __skb_checksum_validate function (actually a macro) to subsume the various checksum_init functions. This function can either init the checksum, or do the full validation (logically checksum_init+skb_check_complete)-- a flag specifies if full vaidation is performed. Also, there is a flag to the function to indicate that zero checksums are allowed (to support optional UDP checksums). Added several stub functions for calling __skb_checksum_validate. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/skbuff.h | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 08074a810164..3ca0dda5a42e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2741,6 +2741,99 @@ static inline __sum16 skb_checksum_complete(struct sk_buff *skb) 0 : __skb_checksum_complete(skb); } +/* Check if we need to perform checksum complete validation. + * + * Returns true if checksum complete is needed, false otherwise + * (either checksum is unnecessary or zero checksum is allowed). + */ +static inline bool __skb_checksum_validate_needed(struct sk_buff *skb, + bool zero_okay, + __sum16 check) +{ + if (skb_csum_unnecessary(skb)) { + return false; + } else if (zero_okay && !check) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + return false; + } + + return true; +} + +/* For small packets <= CHECKSUM_BREAK peform checksum complete directly + * in checksum_init. + */ +#define CHECKSUM_BREAK 76 + +/* Validate (init) checksum based on checksum complete. + * + * Return values: + * 0: checksum is validated or try to in skb_checksum_complete. In the latter + * case the ip_summed will not be CHECKSUM_UNNECESSARY and the pseudo + * checksum is stored in skb->csum for use in __skb_checksum_complete + * non-zero: value of invalid checksum + * + */ +static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb, + bool complete, + __wsum psum) +{ + if (skb->ip_summed == CHECKSUM_COMPLETE) { + if (!csum_fold(csum_add(psum, skb->csum))) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + return 0; + } + } + + skb->csum = psum; + + if (complete || skb->len <= CHECKSUM_BREAK) + return __skb_checksum_complete(skb); + + return 0; +} + +static inline __wsum null_compute_pseudo(struct sk_buff *skb, int proto) +{ + return 0; +} + +/* Perform checksum validate (init). Note that this is a macro since we only + * want to calculate the pseudo header which is an input function if necessary. + * First we try to validate without any computation (checksum unnecessary) and + * then calculate based on checksum complete calling the function to compute + * pseudo header. + * + * Return values: + * 0: checksum is validated or try to in skb_checksum_complete + * non-zero: value of invalid checksum + */ +#define __skb_checksum_validate(skb, proto, complete, \ + zero_okay, check, compute_pseudo) \ +({ \ + __sum16 __ret = 0; \ + if (__skb_checksum_validate_needed(skb, zero_okay, check)) \ + __ret = __skb_checksum_validate_complete(skb, \ + complete, compute_pseudo(skb, proto)); \ + __ret; \ +}) + +#define skb_checksum_init(skb, proto, compute_pseudo) \ + __skb_checksum_validate(skb, proto, false, false, 0, compute_pseudo) + +#define skb_checksum_init_zero_check(skb, proto, check, compute_pseudo) \ + __skb_checksum_validate(skb, proto, false, true, check, compute_pseudo) + +#define skb_checksum_validate(skb, proto, compute_pseudo) \ + __skb_checksum_validate(skb, proto, true, false, 0, compute_pseudo) + +#define skb_checksum_validate_zero_check(skb, proto, check, \ + compute_pseudo) \ + __skb_checksum_validate_(skb, proto, true, true, check, compute_pseudo) + +#define skb_checksum_simple_validate(skb) \ + __skb_checksum_validate(skb, 0, true, false, 0, null_compute_pseudo) + #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) void nf_conntrack_destroy(struct nf_conntrack *nfct); static inline void nf_conntrack_put(struct nf_conntrack *nfct) -- cgit v1.2.3 From ed70fcfcee953a76028bfc3f963d2167c2990020 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 2 May 2014 16:29:38 -0700 Subject: net: Call skb_checksum_init in IPv4 Call skb_checksum_init instead of private functions. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/ip.h | 6 ++++++ net/ipv4/tcp_ipv4.c | 25 ++----------------------- net/ipv4/udp.c | 19 ++----------------- 3 files changed, 10 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 3ec2b0fb9d83..1988cefdbb70 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -342,6 +342,12 @@ static inline void ip_select_ident_more(struct sk_buff *skb, struct dst_entry *d __ip_select_ident(iph, dst, more); } +static inline __wsum inet_compute_pseudo(struct sk_buff *skb, int proto) +{ + return csum_tcpudp_nofold(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, + skb->len, proto, 0); +} + /* * Map a multicast IP onto multicast MAC for type ethernet. */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 438f3b95143d..ad166dcc278f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1744,28 +1744,6 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) return sk; } -static __sum16 tcp_v4_checksum_init(struct sk_buff *skb) -{ - const struct iphdr *iph = ip_hdr(skb); - - if (skb->ip_summed == CHECKSUM_COMPLETE) { - if (!tcp_v4_check(skb->len, iph->saddr, - iph->daddr, skb->csum)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - return 0; - } - } - - skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, - skb->len, IPPROTO_TCP, 0); - - if (skb->len <= 76) { - return __skb_checksum_complete(skb); - } - return 0; -} - - /* The socket must have it's spinlock held when we get * here. * @@ -1960,7 +1938,8 @@ int tcp_v4_rcv(struct sk_buff *skb) * Packet length and doff are validated by header prediction, * provided case of th->doff==0 is eliminated. * So, we defer the checks. */ - if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb)) + + if (skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo)) goto csum_error; th = tcp_hdr(skb); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 4468e1adc094..f2d05d7be743 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1672,7 +1672,6 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) { - const struct iphdr *iph; int err; UDP_SKB_CB(skb)->partial_cov = 0; @@ -1684,22 +1683,8 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh, return err; } - iph = ip_hdr(skb); - if (uh->check == 0) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - } else if (skb->ip_summed == CHECKSUM_COMPLETE) { - if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, - proto, skb->csum)) - skb->ip_summed = CHECKSUM_UNNECESSARY; - } - if (!skb_csum_unnecessary(skb)) - skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, - skb->len, proto, 0); - /* Probably, we should checksum udp header (it should be in cache - * in any case) and data in tiny packets (< rx copybreak). - */ - - return 0; + return skb_checksum_init_zero_check(skb, proto, uh->check, + inet_compute_pseudo); } /* -- cgit v1.2.3 From e4f45b7f40bdaade5ef8f45e7c6daed4c909fdf5 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 2 May 2014 16:29:51 -0700 Subject: net: Call skb_checksum_init in IPv6 Call skb_checksum_init instead of private functions. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/ip6_checksum.h | 7 +++++++ net/ipv6/ip6_checksum.c | 11 +---------- net/ipv6/tcp_ipv6.c | 21 +-------------------- 3 files changed, 9 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/ip6_checksum.h b/include/net/ip6_checksum.h index 9e3c540c1b11..8ac5c21f8456 100644 --- a/include/net/ip6_checksum.h +++ b/include/net/ip6_checksum.h @@ -41,6 +41,13 @@ __sum16 csum_ipv6_magic(const struct in6_addr *saddr, __wsum csum); #endif +static inline __wsum ip6_compute_pseudo(struct sk_buff *skb, int proto) +{ + return ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + skb->len, proto, 0)); +} + static __inline__ __sum16 tcp_v6_check(int len, const struct in6_addr *saddr, const struct in6_addr *daddr, diff --git a/net/ipv6/ip6_checksum.c b/net/ipv6/ip6_checksum.c index ee7a97f510cb..c69fe37b8104 100644 --- a/net/ipv6/ip6_checksum.c +++ b/net/ipv6/ip6_checksum.c @@ -84,16 +84,7 @@ int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) &ipv6_hdr(skb)->daddr, ntohs(uh->dest)); return 1; } - if (skb->ip_summed == CHECKSUM_COMPLETE && - !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, - skb->len, proto, skb->csum)) - skb->ip_summed = CHECKSUM_UNNECESSARY; - if (!skb_csum_unnecessary(skb)) - skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - skb->len, proto, 0)); - - return 0; + return skb_checksum_init(skb, IPPROTO_UDP, ip6_compute_pseudo); } EXPORT_SYMBOL(udp6_csum_init); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index e289830ed6e3..7fa67439f4d6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1294,25 +1294,6 @@ out: return NULL; } -static __sum16 tcp_v6_checksum_init(struct sk_buff *skb) -{ - if (skb->ip_summed == CHECKSUM_COMPLETE) { - if (!tcp_v6_check(skb->len, &ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, skb->csum)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - return 0; - } - } - - skb->csum = ~csum_unfold(tcp_v6_check(skb->len, - &ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, 0)); - - if (skb->len <= 76) - return __skb_checksum_complete(skb); - return 0; -} - /* The socket must have it's spinlock held when we get * here. * @@ -1486,7 +1467,7 @@ static int tcp_v6_rcv(struct sk_buff *skb) if (!pskb_may_pull(skb, th->doff*4)) goto discard_it; - if (!skb_csum_unnecessary(skb) && tcp_v6_checksum_init(skb)) + if (skb_checksum_init(skb, IPPROTO_TCP, ip6_compute_pseudo)) goto csum_error; th = tcp_hdr(skb); -- cgit v1.2.3 From 7c2fcccc323909c1a4e56b79fc882168a0880146 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 5 May 2014 11:49:23 +0200 Subject: ASoC: sta350: add support for bits in miscellaneous registers Add support for RPDNEN, NSHHPEN, BRIDGOFF, CPWMEN and PNDLSL, and add DT bindings to access them. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/st,sta350.txt | 24 +++++++++++- include/sound/sta350.h | 5 +++ sound/soc/codecs/sta350.c | 45 ++++++++++++++++++++++ sound/soc/codecs/sta350.h | 10 +++++ 4 files changed, 83 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/st,sta350.txt b/Documentation/devicetree/bindings/sound/st,sta350.txt index ecd7a623c8bd..b7e71bf5caf4 100644 --- a/Documentation/devicetree/bindings/sound/st,sta350.txt +++ b/Documentation/devicetree/bindings/sound/st,sta350.txt @@ -86,7 +86,29 @@ Optional properties: - st,invalid-input-detect-mute: If present, automatic invalid input detect mute is enabled. - + - st,activate-mute-output: + If present, a mute output will be activated in ase the volume will + reach a value lower than -76 dBFS. + + - st,bridge-immediate-off: + If present, the bridge will be switched off immediately after the + power-down-gpio goes low. Otherwise, the bridge will wait for 13 + million clock cycles to pass before shutting down. + + - st,noise-shape-dc-cut: + If present, the noise-shaping technique on the DC cutoff filter are + enabled. + + - st,powerdown-master-volume: + If present, the power-down pin and I2C power-down functions will + act on the master volume. Otherwise, the functions will act on the + mute commands. + + - st,powerdown-delay-divider: + If present, the bridge power-down time will be divided by the provided + value. If not specified, a divider of 1 will be used. Allowed values + are 1, 2, 4, 8, 16, 32, 64 and 128. + This property has to be specified as '/bits/ 8' value. Example: diff --git a/include/sound/sta350.h b/include/sound/sta350.h index 3a3298106b22..42edceb096a0 100644 --- a/include/sound/sta350.h +++ b/include/sound/sta350.h @@ -37,6 +37,7 @@ struct sta350_platform_data { u8 ch3_output_mapping; u8 ffx_power_output_mode; u8 drop_compensation_ns; + u8 powerdown_delay_divider; unsigned int thermal_warning_recovery:1; unsigned int thermal_warning_adjustment:1; unsigned int fault_detect_recovery:1; @@ -47,6 +48,10 @@ struct sta350_platform_data { unsigned int odd_pwm_speed_mode:1; unsigned int distortion_compensation:1; unsigned int invalid_input_detect_mute:1; + unsigned int activate_mute_output:1; + unsigned int bridge_immediate_off:1; + unsigned int noise_shape_dc_cut:1; + unsigned int powerdown_master_vol:1; }; #endif /* __LINUX_SND__STA350_H */ diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 12ebbaf5d95f..cc97dd52aa9c 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1020,6 +1020,29 @@ static int sta350_probe(struct snd_soc_codec *codec) pdata->ch3_output_mapping << STA350_CxCFG_OM_SHIFT); + /* miscellaneous registers */ + regmap_update_bits(sta350->regmap, STA350_MISC1, + STA350_MISC1_CPWMEN, + pdata->activate_mute_output ? + STA350_MISC1_CPWMEN : 0); + regmap_update_bits(sta350->regmap, STA350_MISC1, + STA350_MISC1_BRIDGOFF, + pdata->bridge_immediate_off ? + STA350_MISC1_BRIDGOFF : 0); + regmap_update_bits(sta350->regmap, STA350_MISC1, + STA350_MISC1_NSHHPEN, + pdata->noise_shape_dc_cut ? + STA350_MISC1_NSHHPEN : 0); + regmap_update_bits(sta350->regmap, STA350_MISC1, + STA350_MISC1_RPDNEN, + pdata->powerdown_master_vol ? + STA350_MISC1_RPDNEN: 0); + + regmap_update_bits(sta350->regmap, STA350_MISC2, + STA350_MISC2_PNDLSL_MASK, + pdata->powerdown_delay_divider + << STA350_MISC2_PNDLSL_SHIFT); + /* initialize coefficient shadow RAM with reset values */ for (i = 4; i <= 49; i += 5) sta350->coef_shadow[i] = 0x400000; @@ -1094,6 +1117,7 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350) struct sta350_platform_data *pdata; const char *ffx_power_mode; u16 tmp; + u8 tmp8; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -1158,6 +1182,27 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350) if (of_get_property(np, "st,invalid-input-detect-mute", NULL)) pdata->invalid_input_detect_mute = 1; + /* MISC */ + if (of_get_property(np, "st,activate-mute-output", NULL)) + pdata->activate_mute_output = 1; + + if (of_get_property(np, "st,bridge-immediate-off", NULL)) + pdata->bridge_immediate_off = 1; + + if (of_get_property(np, "st,noise-shape-dc-cut", NULL)) + pdata->noise_shape_dc_cut = 1; + + if (of_get_property(np, "st,powerdown-master-volume", NULL)) + pdata->powerdown_master_vol = 1; + + if (!of_property_read_u8(np, "st,powerdown-delay-divider", &tmp8)) { + if (is_power_of_2(tmp8) && tmp8 >= 1 && tmp8 <= 128) + pdata->powerdown_delay_divider = ilog2(tmp8); + else + dev_warn(dev, "Unsupported powerdown delay divider %d\n", + tmp8); + } + sta350->pdata = pdata; return 0; diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h index c3248f0fad2c..fb7285290779 100644 --- a/sound/soc/codecs/sta350.h +++ b/sound/soc/codecs/sta350.h @@ -225,4 +225,14 @@ #define STA350_C3_MIX1 60 #define STA350_C3_MIX2 61 +/* miscellaneous register 1 */ +#define STA350_MISC1_CPWMEN BIT(2) +#define STA350_MISC1_BRIDGOFF BIT(5) +#define STA350_MISC1_NSHHPEN BIT(6) +#define STA350_MISC1_RPDNEN BIT(7) + +/* miscellaneous register 2 */ +#define STA350_MISC2_PNDLSL_MASK 0x1c +#define STA350_MISC2_PNDLSL_SHIFT 2 + #endif /* _ASOC_STA_350_H */ -- cgit v1.2.3 From 8febcaa2aac184d7e729acb75e9c4b80b04ad1b9 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 24 Apr 2014 11:30:01 -0400 Subject: device: introduce per device dma_pfn_offset On few architectures, there are few restrictions on DMAble area of system RAM. That also means that devices needs to know about this restrictions so that the dma_masks can be updated accordingly and dma address translation helpers can add/subtract the dma offset. In most of cases DMA addresses can be performed using offset value of Bus address space relatively to physical address space as following: PFN->DMA: __pfn_to_phys(pfn + [-]dma_pfn_offset) DMA->PFN: __phys_to_pfn(dma_addr) + [-]dma_pfn_offset So we introduce per device dma_pfn_offset which can be popullated by architecture init code while creating the devices. Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Grygorii Strashko Signed-off-by: Santosh Shilimkar --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index 233bbbeb768d..85a52d698f78 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -691,6 +691,7 @@ struct acpi_dev_node { * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all * hardware supports 64-bit addresses for consistent allocations * such descriptors. + * @dma_pfn_offset: offset of DMA memory range relatively of RAM * @dma_parms: A low level driver may set these to teach IOMMU code about * segment limitations. * @dma_pools: Dma pools (if dma'ble device). @@ -756,6 +757,7 @@ struct device { not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ + unsigned long dma_pfn_offset; struct device_dma_parameters *dma_parms; -- cgit v1.2.3 From 18308c94723e162ed121942335bc186e66820a7a Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Thu, 24 Apr 2014 11:30:02 -0400 Subject: of: introduce of_dma_get_range() helper The of_dma_get_range() allows to find "dma-range" property for the specified device and parse it. dma-ranges format: DMA addr (dma_addr) : naddr cells CPU addr (phys_addr_t) : pna cells size : nsize cells Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Grygorii Strashko Signed-off-by: Santosh Shilimkar --- drivers/of/address.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_address.h | 8 +++++ 2 files changed, 95 insertions(+) (limited to 'include') diff --git a/drivers/of/address.c b/drivers/of/address.c index cb4242a69cd5..c54baee87d93 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -721,3 +721,90 @@ void __iomem *of_iomap(struct device_node *np, int index) return ioremap(res.start, resource_size(&res)); } EXPORT_SYMBOL(of_iomap); + +/** + * of_dma_get_range - Get DMA range info + * @np: device node to get DMA range info + * @dma_addr: pointer to store initial DMA address of DMA range + * @paddr: pointer to store initial CPU address of DMA range + * @size: pointer to store size of DMA range + * + * Look in bottom up direction for the first "dma-ranges" property + * and parse it. + * dma-ranges format: + * DMA addr (dma_addr) : naddr cells + * CPU addr (phys_addr_t) : pna cells + * size : nsize cells + * + * It returns -ENODEV if "dma-ranges" property was not found + * for this device in DT. + */ +int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size) +{ + struct device_node *node = of_node_get(np); + const __be32 *ranges = NULL; + int len, naddr, nsize, pna; + int ret = 0; + u64 dmaaddr; + + if (!node) + return -EINVAL; + + while (1) { + naddr = of_n_addr_cells(node); + nsize = of_n_size_cells(node); + node = of_get_next_parent(node); + if (!node) + break; + + ranges = of_get_property(node, "dma-ranges", &len); + + /* Ignore empty ranges, they imply no translation required */ + if (ranges && len > 0) + break; + + /* + * At least empty ranges has to be defined for parent node if + * DMA is supported + */ + if (!ranges) + break; + } + + if (!ranges) { + pr_debug("%s: no dma-ranges found for node(%s)\n", + __func__, np->full_name); + ret = -ENODEV; + goto out; + } + + len /= sizeof(u32); + + pna = of_n_addr_cells(node); + + /* dma-ranges format: + * DMA addr : naddr cells + * CPU addr : pna cells + * size : nsize cells + */ + dmaaddr = of_read_number(ranges, naddr); + *paddr = of_translate_dma_address(np, ranges); + if (*paddr == OF_BAD_ADDR) { + pr_err("%s: translation of DMA address(%pad) to CPU address failed node(%s)\n", + __func__, dma_addr, np->full_name); + ret = -EINVAL; + goto out; + } + *dma_addr = dmaaddr; + + *size = of_read_number(ranges + naddr + pna, nsize); + + pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n", + *dma_addr, *paddr, *size); + +out: + of_node_put(node); + + return ret; +} +EXPORT_SYMBOL_GPL(of_dma_get_range); diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 5f6ed6b182b8..4d7b325af2ca 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -63,6 +63,8 @@ extern int of_pci_range_parser_init(struct of_pci_range_parser *parser, extern struct of_pci_range *of_pci_range_parser_one( struct of_pci_range_parser *parser, struct of_pci_range *range); +extern int of_dma_get_range(struct device_node *np, u64 *dma_addr, + u64 *paddr, u64 *size); #else /* CONFIG_OF_ADDRESS */ static inline struct device_node *of_find_matching_node_by_address( struct device_node *from, @@ -90,6 +92,12 @@ static inline struct of_pci_range *of_pci_range_parser_one( { return NULL; } + +static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr, + u64 *paddr, u64 *size) +{ + return -ENODEV; +} #endif /* CONFIG_OF_ADDRESS */ #ifdef CONFIG_OF -- cgit v1.2.3 From 92ea637edea36e58236e3124f199161da6f5c5de Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 24 Apr 2014 11:30:03 -0400 Subject: of: introduce of_dma_is_coherent() helper The of_dma_is_coherent() helper parses the given DT device node to see if the "dma-coherent" property is supported and returns true or false accordingly. If the arch is always coherent or always noncoherent, then the default DMA ops has to be specified accordingly. Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Santosh Shilimkar Signed-off-by: Grygorii Strashko --- drivers/of/address.c | 23 +++++++++++++++++++++++ include/linux/of_address.h | 6 ++++++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/of/address.c b/drivers/of/address.c index c54baee87d93..d244b2859aac 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -808,3 +808,26 @@ out: return ret; } EXPORT_SYMBOL_GPL(of_dma_get_range); + +/** + * of_dma_is_coherent - Check if device is coherent + * @np: device node + * + * It returns true if "dma-coherent" property was found + * for this device in DT. + */ +bool of_dma_is_coherent(struct device_node *np) +{ + struct device_node *node = of_node_get(np); + + while (node) { + if (of_property_read_bool(node, "dma-coherent")) { + of_node_put(node); + return true; + } + node = of_get_next_parent(node); + } + of_node_put(node); + return false; +} +EXPORT_SYMBOL_GPL(of_dma_is_coherent); \ No newline at end of file diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 4d7b325af2ca..839a3521b28e 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -65,6 +65,7 @@ extern struct of_pci_range *of_pci_range_parser_one( struct of_pci_range *range); extern int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size); +extern bool of_dma_is_coherent(struct device_node *np); #else /* CONFIG_OF_ADDRESS */ static inline struct device_node *of_find_matching_node_by_address( struct device_node *from, @@ -98,6 +99,11 @@ static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr, { return -ENODEV; } + +static inline bool of_dma_is_coherent(struct device_node *np) +{ + return false; +} #endif /* CONFIG_OF_ADDRESS */ #ifdef CONFIG_OF -- cgit v1.2.3 From 95713978b0a2929b72933235bb07c0a793e71afa Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 2 May 2014 17:57:16 +0200 Subject: clk: sunxi: Implement MMC phase control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HdG: add header exporting clk_sunxi_mmc_phase_control Signed-off-by: Emilio López Signed-off-by: Hans de Goede Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/clk/sunxi.h | 22 ++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 include/linux/clk/sunxi.h (limited to 'include') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index bd7dc733c1ca..59f90401b900 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -506,6 +506,42 @@ CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", +/** + * clk_sunxi_mmc_phase_control() - configures MMC clock phase control + */ + +void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output) +{ + #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + + struct clk_composite *composite = to_clk_composite(hw); + struct clk_hw *rate_hw = composite->rate_hw; + struct clk_factors *factors = to_clk_factors(rate_hw); + unsigned long flags = 0; + u32 reg; + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + reg = readl(factors->reg); + + /* set sample clock phase control */ + reg &= ~(0x7 << 20); + reg |= ((sample & 0x7) << 20); + + /* set output clock phase control */ + reg &= ~(0x7 << 8); + reg |= ((output & 0x7) << 8); + + writel(reg, factors->reg); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); +} +EXPORT_SYMBOL(clk_sunxi_mmc_phase_control); + + /** * sunxi_factors_clk_setup() - Setup function for factor clocks */ diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h new file mode 100644 index 000000000000..1ef5c899e458 --- /dev/null +++ b/include/linux/clk/sunxi.h @@ -0,0 +1,22 @@ +/* + * Copyright 2013 - Hans de Goede + * + * 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 __LINUX_CLK_SUNXI_H_ +#define __LINUX_CLK_SUNXI_H_ + +#include + +void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output); + +#endif -- cgit v1.2.3 From 272b5edd3b8fe51d4e22fbea996a8d6560b8d048 Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Mon, 5 May 2014 15:09:08 -0500 Subject: ASoC: Add support for CS42L56 CODEC This patch adds support for the Cirrus Logic Low Power Stereo I2C CODEC Signed-off-by: Brian Austin Signed-off-by: Mark Brown --- include/sound/cs42l56.h | 48 ++ sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs42l56.c | 1427 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs42l56.h | 175 ++++++ 5 files changed, 1657 insertions(+) create mode 100644 include/sound/cs42l56.h create mode 100644 sound/soc/codecs/cs42l56.c create mode 100644 sound/soc/codecs/cs42l56.h (limited to 'include') diff --git a/include/sound/cs42l56.h b/include/sound/cs42l56.h new file mode 100644 index 000000000000..2467c8ff132c --- /dev/null +++ b/include/sound/cs42l56.h @@ -0,0 +1,48 @@ +/* + * linux/sound/cs42l56.h -- Platform data for CS42L56 + * + * Copyright (c) 2014 Cirrus Logic Inc. + * + * 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 __CS42L56_H +#define __CS42L56_H + +struct cs42l56_platform_data { + + /* GPIO for Reset */ + unsigned int gpio_nreset; + + /* MICBIAS Level. Check datasheet Pg48 */ + unsigned int micbias_lvl; + + /* Analog Input 1A Reference 0=Single 1=Pseudo-Differential */ + unsigned int ain1a_ref_cfg; + + /* Analog Input 2A Reference 0=Single 1=Pseudo-Differential */ + unsigned int ain2a_ref_cfg; + + /* Analog Input 1B Reference 0=Single 1=Pseudo-Differential */ + unsigned int ain1b_ref_cfg; + + /* Analog Input 2B Reference 0=Single 1=Pseudo-Differential */ + unsigned int ain2b_ref_cfg; + + /* Charge Pump Freq. Check datasheet Pg62 */ + unsigned int chgfreq; + + /* HighPass Filter Right Channel Corner Frequency */ + unsigned int hpfb_freq; + + /* HighPass Filter Left Channel Corner Frequency */ + unsigned int hpfa_freq; + + /* Adaptive Power Control for LO/HP */ + unsigned int adaptive_pwr; + +}; + +#endif /* __CS42L56_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d739ba7baeff..755372509cc8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -41,6 +41,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51_I2C if I2C select SND_SOC_CS42L52 if I2C && INPUT + select SND_SOC_CS42L56 if I2C && INPUT select SND_SOC_CS42L73 if I2C select SND_SOC_CS4270 if I2C select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI @@ -288,6 +289,10 @@ config SND_SOC_CS42L52 tristate "Cirrus Logic CS42L52 CODEC" depends on I2C && INPUT +config SND_SOC_CS42L56 + tristate "Cirrus Logic CS42L56 CODEC" + depends on I2C && INPUT + config SND_SOC_CS42L73 tristate "Cirrus Logic CS42L73 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e06f10032344..c225dae517c3 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -28,6 +28,7 @@ snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o +snd-soc-cs42l56-objs := cs42l56.o snd-soc-cs42l73-objs := cs42l73.o snd-soc-cs4270-objs := cs4270.o snd-soc-cs4271-objs := cs4271.o @@ -180,6 +181,7 @@ obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o +obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c new file mode 100644 index 000000000000..5bb134b4ab9b --- /dev/null +++ b/sound/soc/codecs/cs42l56.c @@ -0,0 +1,1427 @@ +/* + * cs42l56.c -- CS42L56 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs42l56.h" + +#define CS42L56_NUM_SUPPLIES 3 +static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = { + "VA", + "VCP", + "VLDO", +}; + +struct cs42l56_private { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct device *dev; + struct cs42l56_platform_data pdata; + struct regulator_bulk_data supplies[CS42L56_NUM_SUPPLIES]; + u32 mclk; + u8 mclk_prediv; + u8 mclk_div2; + u8 mclk_ratio; + u8 iface; + u8 iface_fmt; + u8 iface_inv; +#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) + struct input_dev *beep; + struct work_struct beep_work; + int beep_rate; +#endif +}; + +static const struct reg_default cs42l56_reg_defaults[] = { + { 1, 0x56 }, /* r01 - ID 1 */ + { 2, 0x04 }, /* r02 - ID 2 */ + { 3, 0x7f }, /* r03 - Power Ctl 1 */ + { 4, 0xff }, /* r04 - Power Ctl 2 */ + { 5, 0x00 }, /* ro5 - Clocking Ctl 1 */ + { 6, 0x0b }, /* r06 - Clocking Ctl 2 */ + { 7, 0x00 }, /* r07 - Serial Format */ + { 8, 0x05 }, /* r08 - Class H Ctl */ + { 9, 0x0c }, /* r09 - Misc Ctl */ + { 10, 0x80 }, /* r0a - INT Status */ + { 11, 0x00 }, /* r0b - Playback Ctl */ + { 12, 0x0c }, /* r0c - DSP Mute Ctl */ + { 13, 0x00 }, /* r0d - ADCA Mixer Volume */ + { 14, 0x00 }, /* r0e - ADCB Mixer Volume */ + { 15, 0x00 }, /* r0f - PCMA Mixer Volume */ + { 16, 0x00 }, /* r10 - PCMB Mixer Volume */ + { 17, 0x00 }, /* r11 - Analog Input Advisory Volume */ + { 18, 0x00 }, /* r12 - Digital Input Advisory Volume */ + { 19, 0x00 }, /* r13 - Master A Volume */ + { 20, 0x00 }, /* r14 - Master B Volume */ + { 21, 0x00 }, /* r15 - Beep Freq / On Time */ + { 22, 0x00 }, /* r16 - Beep Volume / Off Time */ + { 23, 0x00 }, /* r17 - Beep Tone Ctl */ + { 24, 0x88 }, /* r18 - Tone Ctl */ + { 25, 0x00 }, /* r19 - Channel Mixer & Swap */ + { 26, 0x00 }, /* r1a - AIN Ref Config / ADC Mux */ + { 27, 0xa0 }, /* r1b - High-Pass Filter Ctl */ + { 28, 0x00 }, /* r1c - Misc ADC Ctl */ + { 29, 0x00 }, /* r1d - Gain & Bias Ctl */ + { 30, 0x00 }, /* r1e - PGAA Mux & Volume */ + { 31, 0x00 }, /* r1f - PGAB Mux & Volume */ + { 32, 0x00 }, /* r20 - ADCA Attenuator */ + { 33, 0x00 }, /* r21 - ADCB Attenuator */ + { 34, 0x00 }, /* r22 - ALC Enable & Attack Rate */ + { 35, 0xbf }, /* r23 - ALC Release Rate */ + { 36, 0x00 }, /* r24 - ALC Threshold */ + { 37, 0x00 }, /* r25 - Noise Gate Ctl */ + { 38, 0x00 }, /* r26 - ALC, Limiter, SFT, ZeroCross */ + { 39, 0x00 }, /* r27 - Analog Mute, LO & HP Mux */ + { 40, 0x00 }, /* r28 - HP A Volume */ + { 41, 0x00 }, /* r29 - HP B Volume */ + { 42, 0x00 }, /* r2a - LINEOUT A Volume */ + { 43, 0x00 }, /* r2b - LINEOUT B Volume */ + { 44, 0x00 }, /* r2c - Limit Threshold Ctl */ + { 45, 0x7f }, /* r2d - Limiter Ctl & Release Rate */ + { 46, 0x00 }, /* r2e - Limiter Attack Rate */ +}; + +static bool cs42l56_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L56_CHIP_ID_1: + case CS42L56_CHIP_ID_2: + case CS42L56_PWRCTL_1: + case CS42L56_PWRCTL_2: + case CS42L56_CLKCTL_1: + case CS42L56_CLKCTL_2: + case CS42L56_SERIAL_FMT: + case CS42L56_CLASSH_CTL: + case CS42L56_MISC_CTL: + case CS42L56_INT_STATUS: + case CS42L56_PLAYBACK_CTL: + case CS42L56_DSP_MUTE_CTL: + case CS42L56_ADCA_MIX_VOLUME: + case CS42L56_ADCB_MIX_VOLUME: + case CS42L56_PCMA_MIX_VOLUME: + case CS42L56_PCMB_MIX_VOLUME: + case CS42L56_ANAINPUT_ADV_VOLUME: + case CS42L56_DIGINPUT_ADV_VOLUME: + case CS42L56_MASTER_A_VOLUME: + case CS42L56_MASTER_B_VOLUME: + case CS42L56_BEEP_FREQ_ONTIME: + case CS42L56_BEEP_FREQ_OFFTIME: + case CS42L56_BEEP_TONE_CFG: + case CS42L56_TONE_CTL: + case CS42L56_CHAN_MIX_SWAP: + case CS42L56_AIN_REFCFG_ADC_MUX: + case CS42L56_HPF_CTL: + case CS42L56_MISC_ADC_CTL: + case CS42L56_GAIN_BIAS_CTL: + case CS42L56_PGAA_MUX_VOLUME: + case CS42L56_PGAB_MUX_VOLUME: + case CS42L56_ADCA_ATTENUATOR: + case CS42L56_ADCB_ATTENUATOR: + case CS42L56_ALC_EN_ATTACK_RATE: + case CS42L56_ALC_RELEASE_RATE: + case CS42L56_ALC_THRESHOLD: + case CS42L56_NOISE_GATE_CTL: + case CS42L56_ALC_LIM_SFT_ZC: + case CS42L56_AMUTE_HPLO_MUX: + case CS42L56_HPA_VOLUME: + case CS42L56_HPB_VOLUME: + case CS42L56_LOA_VOLUME: + case CS42L56_LOB_VOLUME: + case CS42L56_LIM_THRESHOLD_CTL: + case CS42L56_LIM_CTL_RELEASE_RATE: + case CS42L56_LIM_ATTACK_RATE: + return true; + default: + return false; + } +} + +static bool cs42l56_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L56_INT_STATUS: + return 1; + default: + return 0; + } +} + +static DECLARE_TLV_DB_SCALE(beep_tlv, -5000, 200, 0); +static DECLARE_TLV_DB_SCALE(hl_tlv, -6000, 50, 0); +static DECLARE_TLV_DB_SCALE(adv_tlv, -10200, 50, 0); +static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, 0); +static DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); +static DECLARE_TLV_DB_SCALE(preamp_tlv, 0, 1000, 0); +static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0); + +static const unsigned int ngnb_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(-8200, 600, 0), + 2, 5, TLV_DB_SCALE_ITEM(-7600, 300, 0), +}; +static const unsigned int ngb_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 2, TLV_DB_SCALE_ITEM(-6400, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-4600, 300, 0), +}; +static const unsigned int alc_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0), +}; + +static const char * const beep_config_text[] = { + "Off", "Single", "Multiple", "Continuous" +}; + +static const struct soc_enum beep_config_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 6, + ARRAY_SIZE(beep_config_text), beep_config_text); + +static const char * const beep_pitch_text[] = { + "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5", + "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7" +}; + +static const struct soc_enum beep_pitch_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 4, + ARRAY_SIZE(beep_pitch_text), beep_pitch_text); + +static const char * const beep_ontime_text[] = { + "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s", + "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s", + "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s" +}; + +static const struct soc_enum beep_ontime_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 0, + ARRAY_SIZE(beep_ontime_text), beep_ontime_text); + +static const char * const beep_offtime_text[] = { + "1.23 s", "2.58 s", "3.90 s", "5.20 s", + "6.60 s", "8.05 s", "9.35 s", "10.80 s" +}; + +static const struct soc_enum beep_offtime_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_OFFTIME, 5, + ARRAY_SIZE(beep_offtime_text), beep_offtime_text); + +static const char * const beep_treble_text[] = { + "5kHz", "7kHz", "10kHz", "15kHz" +}; + +static const struct soc_enum beep_treble_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 3, + ARRAY_SIZE(beep_treble_text), beep_treble_text); + +static const char * const beep_bass_text[] = { + "50Hz", "100Hz", "200Hz", "250Hz" +}; + +static const struct soc_enum beep_bass_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 1, + ARRAY_SIZE(beep_bass_text), beep_bass_text); + +static const char * const adc_swap_text[] = { + "None", "A+B/2", "A-B/2", "Swap" +}; + +static const struct soc_enum adc_swap_enum = + SOC_ENUM_SINGLE(CS42L56_MISC_ADC_CTL, 3, + ARRAY_SIZE(adc_swap_text), adc_swap_text); + +static const char * const pgaa_mux_text[] = { + "AIN1A", "AIN2A", "AIN3A"}; + +static const struct soc_enum pgaa_mux_enum = + SOC_ENUM_SINGLE(CS42L56_PGAA_MUX_VOLUME, 0, + ARRAY_SIZE(pgaa_mux_text), + pgaa_mux_text); + +static const struct snd_kcontrol_new pgaa_mux = + SOC_DAPM_ENUM("Route", pgaa_mux_enum); + +static const char * const pgab_mux_text[] = { + "AIN1B", "AIN2B", "AIN3B"}; + +static const struct soc_enum pgab_mux_enum = + SOC_ENUM_SINGLE(CS42L56_PGAB_MUX_VOLUME, 0, + ARRAY_SIZE(pgab_mux_text), + pgab_mux_text); + +static const struct snd_kcontrol_new pgab_mux = + SOC_DAPM_ENUM("Route", pgab_mux_enum); + +static const char * const adca_mux_text[] = { + "PGAA", "AIN1A", "AIN2A", "AIN3A"}; + +static const struct soc_enum adca_mux_enum = + SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 0, + ARRAY_SIZE(adca_mux_text), + adca_mux_text); + +static const struct snd_kcontrol_new adca_mux = + SOC_DAPM_ENUM("Route", adca_mux_enum); + +static const char * const adcb_mux_text[] = { + "PGAB", "AIN1B", "AIN2B", "AIN3B"}; + +static const struct soc_enum adcb_mux_enum = + SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 2, + ARRAY_SIZE(adcb_mux_text), + adcb_mux_text); + +static const struct snd_kcontrol_new adcb_mux = + SOC_DAPM_ENUM("Route", adcb_mux_enum); + +static const char * const left_swap_text[] = { + "Left", "LR 2", "Right"}; + +static const char * const right_swap_text[] = { + "Right", "LR 2", "Left"}; + +static const unsigned int swap_values[] = { 0, 1, 3 }; + +static const struct soc_enum adca_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 0, 3, + ARRAY_SIZE(left_swap_text), + left_swap_text, + swap_values); + +static const struct soc_enum pcma_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 4, 3, + ARRAY_SIZE(left_swap_text), + left_swap_text, + swap_values); + +static const struct soc_enum adcb_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 2, 3, + ARRAY_SIZE(right_swap_text), + right_swap_text, + swap_values); + +static const struct soc_enum pcmb_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 6, 3, + ARRAY_SIZE(right_swap_text), + right_swap_text, + swap_values); + +static const struct snd_kcontrol_new hpa_switch = + SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 6, 1, 1); + +static const struct snd_kcontrol_new hpb_switch = + SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 4, 1, 1); + +static const struct snd_kcontrol_new loa_switch = + SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 2, 1, 1); + +static const struct snd_kcontrol_new lob_switch = + SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 0, 1, 1); + +static const char * const hploa_input_text[] = { + "DACA", "PGAA"}; + +static const struct soc_enum lineouta_input_enum = + SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 2, + ARRAY_SIZE(hploa_input_text), + hploa_input_text); + +static const struct snd_kcontrol_new lineouta_input = + SOC_DAPM_ENUM("Route", lineouta_input_enum); + +static const struct soc_enum hpa_input_enum = + SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 0, + ARRAY_SIZE(hploa_input_text), + hploa_input_text); + +static const struct snd_kcontrol_new hpa_input = + SOC_DAPM_ENUM("Route", hpa_input_enum); + +static const char * const hplob_input_text[] = { + "DACB", "PGAB"}; + +static const struct soc_enum lineoutb_input_enum = + SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 3, + ARRAY_SIZE(hplob_input_text), + hplob_input_text); + +static const struct snd_kcontrol_new lineoutb_input = + SOC_DAPM_ENUM("Route", lineoutb_input_enum); + +static const struct soc_enum hpb_input_enum = + SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 1, + ARRAY_SIZE(hplob_input_text), + hplob_input_text); + +static const struct snd_kcontrol_new hpb_input = + SOC_DAPM_ENUM("Route", hpb_input_enum); + +static const char * const dig_mux_text[] = { + "ADC", "DSP"}; + +static const struct soc_enum dig_mux_enum = + SOC_ENUM_SINGLE(CS42L56_MISC_CTL, 7, + ARRAY_SIZE(dig_mux_text), + dig_mux_text); + +static const struct snd_kcontrol_new dig_mux = + SOC_DAPM_ENUM("Route", dig_mux_enum); + +static const char * const hpf_freq_text[] = { + "1.8Hz", "119Hz", "236Hz", "464Hz" +}; + +static const struct soc_enum hpfa_freq_enum = + SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 0, + ARRAY_SIZE(hpf_freq_text), hpf_freq_text); + +static const struct soc_enum hpfb_freq_enum = + SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 2, + ARRAY_SIZE(hpf_freq_text), hpf_freq_text); + +static const char * const ng_delay_text[] = { + "50ms", "100ms", "150ms", "200ms" +}; + +static const struct soc_enum ng_delay_enum = + SOC_ENUM_SINGLE(CS42L56_NOISE_GATE_CTL, 0, + ARRAY_SIZE(ng_delay_text), ng_delay_text); + +static const struct snd_kcontrol_new cs42l56_snd_controls[] = { + + SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L56_MASTER_A_VOLUME, + CS42L56_MASTER_B_VOLUME, 0, 0x34, 0xfd, adv_tlv), + SOC_DOUBLE("Master Mute Switch", CS42L56_DSP_MUTE_CTL, 0, 1, 1, 1), + + SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", CS42L56_ADCA_MIX_VOLUME, + CS42L56_ADCB_MIX_VOLUME, 0, 0x88, 0xa9, hl_tlv), + SOC_DOUBLE("ADC Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 6, 7, 1, 1), + + SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume", CS42L56_PCMA_MIX_VOLUME, + CS42L56_PCMB_MIX_VOLUME, 0, 0x88, 0xa9, hl_tlv), + SOC_DOUBLE("PCM Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 4, 5, 1, 1), + + SOC_SINGLE_TLV("Analog Advisory Volume", + CS42L56_ANAINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv), + SOC_SINGLE_TLV("Digital Advisory Volume", + CS42L56_DIGINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv), + + SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L56_PGAA_MUX_VOLUME, + CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0xfd, pga_tlv), + SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR, + CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv), + SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1), + SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1), + + SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME, + CS42L56_HPA_VOLUME, 0, 0x44, 0x55, hl_tlv), + SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME, + CS42L56_LOA_VOLUME, 0, 0x44, 0x55, hl_tlv), + + SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL, + 0, 0x00, 1, tone_tlv), + SOC_SINGLE_TLV("Treble Shelving Volume", CS42L56_TONE_CTL, + 4, 0x00, 1, tone_tlv), + + SOC_DOUBLE_TLV("PGA Preamp Volume", CS42L56_GAIN_BIAS_CTL, + 4, 6, 0x02, 1, preamp_tlv), + + SOC_SINGLE("DSP Switch", CS42L56_PLAYBACK_CTL, 7, 1, 1), + SOC_SINGLE("Gang Playback Switch", CS42L56_PLAYBACK_CTL, 4, 1, 1), + SOC_SINGLE("Gang ADC Switch", CS42L56_MISC_ADC_CTL, 7, 1, 1), + SOC_SINGLE("Gang PGA Switch", CS42L56_MISC_ADC_CTL, 6, 1, 1), + + SOC_SINGLE("PCMA Invert", CS42L56_PLAYBACK_CTL, 2, 1, 1), + SOC_SINGLE("PCMB Invert", CS42L56_PLAYBACK_CTL, 3, 1, 1), + SOC_SINGLE("ADCA Invert", CS42L56_MISC_ADC_CTL, 2, 1, 1), + SOC_SINGLE("ADCB Invert", CS42L56_MISC_ADC_CTL, 3, 1, 1), + + SOC_ENUM("PCMA Swap", pcma_swap_enum), + SOC_ENUM("PCMB Swap", pcmb_swap_enum), + SOC_ENUM("ADCA Swap", adca_swap_enum), + SOC_ENUM("ADCB Swap", adcb_swap_enum), + + SOC_DOUBLE("HPF Switch", CS42L56_HPF_CTL, 5, 7, 1, 1), + SOC_DOUBLE("HPF Freeze Switch", CS42L56_HPF_CTL, 4, 6, 1, 1), + SOC_ENUM("HPFA Corner Freq", hpfa_freq_enum), + SOC_ENUM("HPFB Corner Freq", hpfb_freq_enum), + + SOC_SINGLE("Analog Soft Ramp", CS42L56_MISC_CTL, 4, 1, 1), + SOC_DOUBLE("Analog Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC, + 7, 5, 1, 1), + SOC_SINGLE("Analog Zero Cross", CS42L56_MISC_CTL, 3, 1, 1), + SOC_DOUBLE("Analog Zero Cross Disable", CS42L56_ALC_LIM_SFT_ZC, + 6, 4, 1, 1), + SOC_SINGLE("Digital Soft Ramp", CS42L56_MISC_CTL, 2, 1, 1), + SOC_SINGLE("Digital Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC, + 3, 1, 1), + + SOC_SINGLE("HL Deemphasis", CS42L56_PLAYBACK_CTL, 6, 1, 1), + + SOC_SINGLE("ALC Switch", CS42L56_ALC_EN_ATTACK_RATE, 6, 1, 1), + SOC_SINGLE("ALC Limit All Switch", CS42L56_ALC_RELEASE_RATE, 7, 1, 1), + SOC_SINGLE_RANGE("ALC Attack", CS42L56_ALC_EN_ATTACK_RATE, + 0, 0, 0x3f, 0), + SOC_SINGLE_RANGE("ALC Release", CS42L56_ALC_RELEASE_RATE, + 0, 0x3f, 0, 0), + SOC_SINGLE_TLV("ALC MAX", CS42L56_ALC_THRESHOLD, + 5, 0x07, 1, alc_tlv), + SOC_SINGLE_TLV("ALC MIN", CS42L56_ALC_THRESHOLD, + 2, 0x07, 1, alc_tlv), + + SOC_SINGLE("Limiter Switch", CS42L56_LIM_CTL_RELEASE_RATE, 7, 1, 1), + SOC_SINGLE("Limit All Switch", CS42L56_LIM_CTL_RELEASE_RATE, 6, 1, 1), + SOC_SINGLE_RANGE("Limiter Attack", CS42L56_LIM_ATTACK_RATE, + 0, 0, 0x3f, 0), + SOC_SINGLE_RANGE("Limiter Release", CS42L56_LIM_CTL_RELEASE_RATE, + 0, 0x3f, 0, 0), + SOC_SINGLE_TLV("Limiter MAX", CS42L56_LIM_THRESHOLD_CTL, + 5, 0x07, 1, alc_tlv), + SOC_SINGLE_TLV("Limiter Cushion", CS42L56_ALC_THRESHOLD, + 2, 0x07, 1, alc_tlv), + + SOC_SINGLE("NG Switch", CS42L56_NOISE_GATE_CTL, 6, 1, 1), + SOC_SINGLE("NG All Switch", CS42L56_NOISE_GATE_CTL, 7, 1, 1), + SOC_SINGLE("NG Boost Switch", CS42L56_NOISE_GATE_CTL, 5, 1, 1), + SOC_SINGLE_TLV("NG Unboost Threshold", CS42L56_NOISE_GATE_CTL, + 2, 0x07, 1, ngnb_tlv), + SOC_SINGLE_TLV("NG Boost Threshold", CS42L56_NOISE_GATE_CTL, + 2, 0x07, 1, ngb_tlv), + SOC_ENUM("NG Delay", ng_delay_enum), + + SOC_ENUM("Beep Config", beep_config_enum), + SOC_ENUM("Beep Pitch", beep_pitch_enum), + SOC_ENUM("Beep on Time", beep_ontime_enum), + SOC_ENUM("Beep off Time", beep_offtime_enum), + SOC_SINGLE_SX_TLV("Beep Volume", CS42L56_BEEP_FREQ_OFFTIME, + 0, 0x07, 0x23, beep_tlv), + SOC_SINGLE("Beep Tone Ctl Switch", CS42L56_BEEP_TONE_CFG, 0, 1, 1), + SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum), + SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum), + +}; + +static const struct snd_soc_dapm_widget cs42l56_dapm_widgets[] = { + + SND_SOC_DAPM_SIGGEN("Beep"), + SND_SOC_DAPM_SUPPLY("VBUF", CS42L56_PWRCTL_1, 5, 1, NULL, 0), + SND_SOC_DAPM_MICBIAS("MIC1 Bias", CS42L56_PWRCTL_1, 4, 1), + SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L56_PWRCTL_1, 3, 1, NULL, 0), + + SND_SOC_DAPM_INPUT("AIN1A"), + SND_SOC_DAPM_INPUT("AIN2A"), + SND_SOC_DAPM_INPUT("AIN1B"), + SND_SOC_DAPM_INPUT("AIN2B"), + SND_SOC_DAPM_INPUT("AIN3A"), + SND_SOC_DAPM_INPUT("AIN3B"), + + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("Digital Output Mux", SND_SOC_NOPM, + 0, 0, &dig_mux), + + SND_SOC_DAPM_PGA("PGAA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGAB", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("PGAA Input Mux", + SND_SOC_NOPM, 0, 0, &pgaa_mux), + SND_SOC_DAPM_MUX("PGAB Input Mux", + SND_SOC_NOPM, 0, 0, &pgab_mux), + + SND_SOC_DAPM_MUX("ADCA Mux", SND_SOC_NOPM, + 0, 0, &adca_mux), + SND_SOC_DAPM_MUX("ADCB Mux", SND_SOC_NOPM, + 0, 0, &adcb_mux), + + SND_SOC_DAPM_ADC("ADCA", NULL, CS42L56_PWRCTL_1, 1, 1), + SND_SOC_DAPM_ADC("ADCB", NULL, CS42L56_PWRCTL_1, 2, 1), + + SND_SOC_DAPM_DAC("DACA", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACB", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("HPA"), + SND_SOC_DAPM_OUTPUT("LOA"), + SND_SOC_DAPM_OUTPUT("HPB"), + SND_SOC_DAPM_OUTPUT("LOB"), + + SND_SOC_DAPM_SWITCH("Headphone Right", + CS42L56_PWRCTL_2, 4, 1, &hpb_switch), + SND_SOC_DAPM_SWITCH("Headphone Left", + CS42L56_PWRCTL_2, 6, 1, &hpa_switch), + + SND_SOC_DAPM_SWITCH("Lineout Right", + CS42L56_PWRCTL_2, 0, 1, &lob_switch), + SND_SOC_DAPM_SWITCH("Lineout Left", + CS42L56_PWRCTL_2, 2, 1, &loa_switch), + + SND_SOC_DAPM_MUX("LINEOUTA Input Mux", SND_SOC_NOPM, + 0, 0, &lineouta_input), + SND_SOC_DAPM_MUX("LINEOUTB Input Mux", SND_SOC_NOPM, + 0, 0, &lineoutb_input), + SND_SOC_DAPM_MUX("HPA Input Mux", SND_SOC_NOPM, + 0, 0, &hpa_input), + SND_SOC_DAPM_MUX("HPB Input Mux", SND_SOC_NOPM, + 0, 0, &hpb_input), + +}; + +static const struct snd_soc_dapm_route cs42l56_audio_map[] = { + + {"HiFi Capture", "DSP", "Digital Output Mux"}, + {"HiFi Capture", "ADC", "Digital Output Mux"}, + + {"Digital Output Mux", NULL, "ADCA"}, + {"Digital Output Mux", NULL, "ADCB"}, + + {"ADCB", NULL, "ADCB Mux"}, + {"ADCA", NULL, "ADCA Mux"}, + + {"ADCA Mux", NULL, "AIN3A"}, + {"ADCA Mux", NULL, "AIN2A"}, + {"ADCA Mux", NULL, "AIN1A"}, + {"ADCA Mux", NULL, "PGAA"}, + {"ADCB Mux", NULL, "AIN3B"}, + {"ADCB Mux", NULL, "AIN2B"}, + {"ADCB Mux", NULL, "AIN1B"}, + {"ADCB Mux", NULL, "PGAB"}, + + {"PGAA", "AIN1A", "PGAA Input Mux"}, + {"PGAA", "AIN2A", "PGAA Input Mux"}, + {"PGAA", "AIN3A", "PGAA Input Mux"}, + {"PGAB", "AIN1B", "PGAB Input Mux"}, + {"PGAB", "AIN2B", "PGAB Input Mux"}, + {"PGAB", "AIN3B", "PGAB Input Mux"}, + + {"PGAA Input Mux", NULL, "AIN1A"}, + {"PGAA Input Mux", NULL, "AIN2A"}, + {"PGAA Input Mux", NULL, "AIN3A"}, + {"PGAB Input Mux", NULL, "AIN1B"}, + {"PGAB Input Mux", NULL, "AIN2B"}, + {"PGAB Input Mux", NULL, "AIN3B"}, + + {"LOB", NULL, "Lineout Right"}, + {"LOA", NULL, "Lineout Left"}, + + {"Lineout Right", "Switch", "LINEOUTB Input Mux"}, + {"Lineout Left", "Switch", "LINEOUTA Input Mux"}, + + {"LINEOUTA Input Mux", "PGAA", "PGAA"}, + {"LINEOUTB Input Mux", "PGAB", "PGAB"}, + {"LINEOUTA Input Mux", "DACA", "DACA"}, + {"LINEOUTB Input Mux", "DACB", "DACB"}, + + {"HPA", NULL, "Headphone Left"}, + {"HPB", NULL, "Headphone Right"}, + + {"Headphone Right", "Switch", "HPB Input Mux"}, + {"Headphone Left", "Switch", "HPA Input Mux"}, + + {"HPA Input Mux", "PGAA", "PGAA"}, + {"HPB Input Mux", "PGAB", "PGAB"}, + {"HPA Input Mux", "DACA", "DACA"}, + {"HPB Input Mux", "DACB", "DACB"}, + + {"DACB", NULL, "HiFi Playback"}, + {"DACA", NULL, "HiFi Playback"}, + +}; + +struct cs42l56_clk_para { + u32 mclk; + u32 srate; + u8 ratio; +}; + +static const struct cs42l56_clk_para clk_ratio_table[] = { + /* 8k */ + { 6000000, 8000, CS42L56_MCLK_LRCLK_768 }, + { 6144000, 8000, CS42L56_MCLK_LRCLK_750 }, + { 12000000, 8000, CS42L56_MCLK_LRCLK_768 }, + { 12288000, 8000, CS42L56_MCLK_LRCLK_750 }, + { 24000000, 8000, CS42L56_MCLK_LRCLK_768 }, + { 24576000, 8000, CS42L56_MCLK_LRCLK_750 }, + /* 11.025k */ + { 5644800, 11025, CS42L56_MCLK_LRCLK_512}, + { 11289600, 11025, CS42L56_MCLK_LRCLK_512}, + { 22579200, 11025, CS42L56_MCLK_LRCLK_512 }, + /* 11.0294k */ + { 6000000, 110294, CS42L56_MCLK_LRCLK_544 }, + { 12000000, 110294, CS42L56_MCLK_LRCLK_544 }, + { 24000000, 110294, CS42L56_MCLK_LRCLK_544 }, + /* 12k */ + { 6000000, 12000, CS42L56_MCLK_LRCLK_500 }, + { 6144000, 12000, CS42L56_MCLK_LRCLK_512 }, + { 12000000, 12000, CS42L56_MCLK_LRCLK_500 }, + { 12288000, 12000, CS42L56_MCLK_LRCLK_512 }, + { 24000000, 12000, CS42L56_MCLK_LRCLK_500 }, + { 24576000, 12000, CS42L56_MCLK_LRCLK_512 }, + /* 16k */ + { 6000000, 16000, CS42L56_MCLK_LRCLK_375 }, + { 6144000, 16000, CS42L56_MCLK_LRCLK_384 }, + { 12000000, 16000, CS42L56_MCLK_LRCLK_375 }, + { 12288000, 16000, CS42L56_MCLK_LRCLK_384 }, + { 24000000, 16000, CS42L56_MCLK_LRCLK_375 }, + { 24576000, 16000, CS42L56_MCLK_LRCLK_384 }, + /* 22.050k */ + { 5644800, 22050, CS42L56_MCLK_LRCLK_256 }, + { 11289600, 22050, CS42L56_MCLK_LRCLK_256 }, + { 22579200, 22050, CS42L56_MCLK_LRCLK_256 }, + /* 22.0588k */ + { 6000000, 220588, CS42L56_MCLK_LRCLK_272 }, + { 12000000, 220588, CS42L56_MCLK_LRCLK_272 }, + { 24000000, 220588, CS42L56_MCLK_LRCLK_272 }, + /* 24k */ + { 6000000, 24000, CS42L56_MCLK_LRCLK_250 }, + { 6144000, 24000, CS42L56_MCLK_LRCLK_256 }, + { 12000000, 24000, CS42L56_MCLK_LRCLK_250 }, + { 12288000, 24000, CS42L56_MCLK_LRCLK_256 }, + { 24000000, 24000, CS42L56_MCLK_LRCLK_250 }, + { 24576000, 24000, CS42L56_MCLK_LRCLK_256 }, + /* 32k */ + { 6000000, 32000, CS42L56_MCLK_LRCLK_187P5 }, + { 6144000, 32000, CS42L56_MCLK_LRCLK_192 }, + { 12000000, 32000, CS42L56_MCLK_LRCLK_187P5 }, + { 12288000, 32000, CS42L56_MCLK_LRCLK_192 }, + { 24000000, 32000, CS42L56_MCLK_LRCLK_187P5 }, + { 24576000, 32000, CS42L56_MCLK_LRCLK_192 }, + /* 44.118k */ + { 6000000, 44118, CS42L56_MCLK_LRCLK_136 }, + { 12000000, 44118, CS42L56_MCLK_LRCLK_136 }, + { 24000000, 44118, CS42L56_MCLK_LRCLK_136 }, + /* 44.1k */ + { 5644800, 44100, CS42L56_MCLK_LRCLK_128 }, + { 11289600, 44100, CS42L56_MCLK_LRCLK_128 }, + { 22579200, 44100, CS42L56_MCLK_LRCLK_128 }, + /* 48k */ + { 6000000, 48000, CS42L56_MCLK_LRCLK_125 }, + { 6144000, 48000, CS42L56_MCLK_LRCLK_128 }, + { 12000000, 48000, CS42L56_MCLK_LRCLK_125 }, + { 12288000, 48000, CS42L56_MCLK_LRCLK_128 }, + { 24000000, 48000, CS42L56_MCLK_LRCLK_125 }, + { 24576000, 48000, CS42L56_MCLK_LRCLK_128 }, +}; + +static int cs42l56_get_mclk_ratio(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clk_ratio_table); i++) { + if (clk_ratio_table[i].mclk == mclk && + clk_ratio_table[i].srate == rate) + return clk_ratio_table[i].ratio; + } + return -EINVAL; +} + +static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); + + switch (freq) { + case CS42L56_MCLK_5P6448MHZ: + case CS42L56_MCLK_6MHZ: + case CS42L56_MCLK_6P144MHZ: + cs42l56->mclk_div2 = 0; + cs42l56->mclk_prediv = 0; + break; + case CS42L56_MCLK_11P2896MHZ: + case CS42L56_MCLK_12MHZ: + case CS42L56_MCLK_12P288MHZ: + cs42l56->mclk_div2 = 1; + cs42l56->mclk_prediv = 0; + break; + case CS42L56_MCLK_22P5792MHZ: + case CS42L56_MCLK_24MHZ: + case CS42L56_MCLK_24P576MHZ: + cs42l56->mclk_div2 = 1; + cs42l56->mclk_prediv = 1; + break; + default: + return -EINVAL; + } + cs42l56->mclk = freq; + + snd_soc_update_bits(codec, CS42L56_CLKCTL_1, + CS42L56_MCLK_PREDIV_MASK, + cs42l56->mclk_prediv); + snd_soc_update_bits(codec, CS42L56_CLKCTL_1, + CS42L56_MCLK_DIV2_MASK, + cs42l56->mclk_div2); + + return 0; +} + +static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + cs42l56->iface = CS42L56_MASTER_MODE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + cs42l56->iface = CS42L56_SLAVE_MODE; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + cs42l56->iface_fmt = CS42L56_DIG_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + cs42l56->iface_fmt = CS42L56_DIG_FMT_LEFT_J; + break; + default: + return -EINVAL; + } + + /* sclk inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + cs42l56->iface_inv = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + cs42l56->iface_inv = CS42L56_SCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, CS42L56_CLKCTL_1, + CS42L56_MS_MODE_MASK, cs42l56->iface); + snd_soc_update_bits(codec, CS42L56_SERIAL_FMT, + CS42L56_DIG_FMT_MASK, cs42l56->iface_fmt); + snd_soc_update_bits(codec, CS42L56_CLKCTL_1, + CS42L56_SCLK_INV_MASK, cs42l56->iface_inv); + return 0; +} + +static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) { + /* Hit the DSP Mixer first */ + snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL, + CS42L56_ADCAMIX_MUTE_MASK | + CS42L56_ADCBMIX_MUTE_MASK | + CS42L56_PCMAMIX_MUTE_MASK | + CS42L56_PCMBMIX_MUTE_MASK | + CS42L56_MSTB_MUTE_MASK | + CS42L56_MSTA_MUTE_MASK, + CS42L56_MUTE); + /* Mute ADC's */ + snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL, + CS42L56_ADCA_MUTE_MASK | + CS42L56_ADCB_MUTE_MASK, + CS42L56_MUTE); + /* HP And LO */ + snd_soc_update_bits(codec, CS42L56_HPA_VOLUME, + CS42L56_HP_MUTE_MASK, + CS42L56_MUTE); + snd_soc_update_bits(codec, CS42L56_HPB_VOLUME, + CS42L56_HP_MUTE_MASK, + CS42L56_MUTE); + snd_soc_update_bits(codec, CS42L56_LOA_VOLUME, + CS42L56_LO_MUTE_MASK, + CS42L56_MUTE); + snd_soc_update_bits(codec, CS42L56_LOB_VOLUME, + CS42L56_LO_MUTE_MASK, + CS42L56_MUTE); + + + } else { + snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL, + CS42L56_ADCAMIX_MUTE_MASK | + CS42L56_ADCBMIX_MUTE_MASK | + CS42L56_PCMAMIX_MUTE_MASK | + CS42L56_PCMBMIX_MUTE_MASK | + CS42L56_MSTB_MUTE_MASK | + CS42L56_MSTA_MUTE_MASK, + CS42L56_UNMUTE); + snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL, + CS42L56_ADCA_MUTE_MASK | + CS42L56_ADCB_MUTE_MASK, + CS42L56_UNMUTE); + snd_soc_update_bits(codec, CS42L56_HPA_VOLUME, + CS42L56_HP_MUTE_MASK, + CS42L56_UNMUTE); + snd_soc_update_bits(codec, CS42L56_HPB_VOLUME, + CS42L56_HP_MUTE_MASK, + CS42L56_UNMUTE); + snd_soc_update_bits(codec, CS42L56_LOA_VOLUME, + CS42L56_LO_MUTE_MASK, + CS42L56_UNMUTE); + snd_soc_update_bits(codec, CS42L56_LOB_VOLUME, + CS42L56_LO_MUTE_MASK, + CS42L56_UNMUTE); + } + return 0; +} + +static int cs42l56_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); + int ratio; + + ratio = cs42l56_get_mclk_ratio(cs42l56->mclk, params_rate(params)); + if (ratio >= 0) { + snd_soc_update_bits(codec, CS42L56_CLKCTL_2, + CS42L56_CLK_RATIO_MASK, ratio); + } else { + dev_err(codec->dev, "unsupported mclk/sclk/lrclk ratio\n"); + return -EINVAL; + } + + return 0; +} + +static int cs42l56_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + snd_soc_update_bits(codec, CS42L56_CLKCTL_1, + CS42L56_MCLK_DIS_MASK, 0); + snd_soc_update_bits(codec, CS42L56_PWRCTL_1, + CS42L56_PDN_ALL_MASK, 0); + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + regcache_cache_only(cs42l56->regmap, false); + regcache_sync(cs42l56->regmap); + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + if (ret != 0) { + dev_err(cs42l56->dev, + "Failed to enable regulators: %d\n", + ret); + return ret; + } + } + snd_soc_update_bits(codec, CS42L56_PWRCTL_1, + CS42L56_PDN_ALL_MASK, 1); + break; + case SND_SOC_BIAS_OFF: + snd_soc_update_bits(codec, CS42L56_PWRCTL_1, + CS42L56_PDN_ALL_MASK, 1); + snd_soc_update_bits(codec, CS42L56_CLKCTL_1, + CS42L56_MCLK_DIS_MASK, 1); + regcache_cache_only(cs42l56->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + break; + } + codec->dapm.bias_level = level; + + return 0; +} + +#define CS42L56_RATES (SNDRV_PCM_RATE_8000_48000) + +#define CS42L56_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +static struct snd_soc_dai_ops cs42l56_ops = { + .hw_params = cs42l56_pcm_hw_params, + .digital_mute = cs42l56_digital_mute, + .set_fmt = cs42l56_set_dai_fmt, + .set_sysclk = cs42l56_set_sysclk, +}; + +static struct snd_soc_dai_driver cs42l56_dai = { + .name = "cs42l56", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L56_RATES, + .formats = CS42L56_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L56_RATES, + .formats = CS42L56_FORMATS, + }, + .ops = &cs42l56_ops, +}; + +static int cs42l56_suspend(struct snd_soc_codec *codec) +{ + cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int cs42l56_resume(struct snd_soc_codec *codec) +{ + cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static int beep_freq[] = { + 261, 522, 585, 667, 706, 774, 889, 1000, + 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182 +}; + +static void cs42l56_beep_work(struct work_struct *work) +{ + struct cs42l56_private *cs42l56 = + container_of(work, struct cs42l56_private, beep_work); + struct snd_soc_codec *codec = cs42l56->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int i; + int val = 0; + int best = 0; + + if (cs42l56->beep_rate) { + for (i = 0; i < ARRAY_SIZE(beep_freq); i++) { + if (abs(cs42l56->beep_rate - beep_freq[i]) < + abs(cs42l56->beep_rate - beep_freq[best])) + best = i; + } + + dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n", + beep_freq[best], cs42l56->beep_rate); + + val = (best << CS42L56_BEEP_RATE_SHIFT); + + snd_soc_dapm_enable_pin(dapm, "Beep"); + } else { + dev_dbg(codec->dev, "Disabling beep\n"); + snd_soc_dapm_disable_pin(dapm, "Beep"); + } + + snd_soc_update_bits(codec, CS42L56_BEEP_FREQ_ONTIME, + CS42L56_BEEP_FREQ_MASK, val); + + snd_soc_dapm_sync(dapm); +} + +/* For usability define a way of injecting beep events for the device - + * many systems will not have a keyboard. + */ +static int cs42l56_beep_event(struct input_dev *dev, unsigned int type, + unsigned int code, int hz) +{ + struct snd_soc_codec *codec = input_get_drvdata(dev); + struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "Beep event %x %x\n", code, hz); + + switch (code) { + case SND_BELL: + if (hz) + hz = 261; + case SND_TONE: + break; + default: + return -1; + } + + /* Kick the beep from a workqueue */ + cs42l56->beep_rate = hz; + schedule_work(&cs42l56->beep_work); + return 0; +} + +static ssize_t cs42l56_beep_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs42l56_private *cs42l56 = dev_get_drvdata(dev); + long int time; + int ret; + + ret = kstrtol(buf, 10, &time); + if (ret != 0) + return ret; + + input_event(cs42l56->beep, EV_SND, SND_TONE, time); + + return count; +} + +static DEVICE_ATTR(beep, 0200, NULL, cs42l56_beep_set); + +static void cs42l56_init_beep(struct snd_soc_codec *codec) +{ + struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); + int ret; + + cs42l56->beep = devm_input_allocate_device(codec->dev); + if (!cs42l56->beep) { + dev_err(codec->dev, "Failed to allocate beep device\n"); + return; + } + + INIT_WORK(&cs42l56->beep_work, cs42l56_beep_work); + cs42l56->beep_rate = 0; + + cs42l56->beep->name = "CS42L56 Beep Generator"; + cs42l56->beep->phys = dev_name(codec->dev); + cs42l56->beep->id.bustype = BUS_I2C; + + cs42l56->beep->evbit[0] = BIT_MASK(EV_SND); + cs42l56->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + cs42l56->beep->event = cs42l56_beep_event; + cs42l56->beep->dev.parent = codec->dev; + input_set_drvdata(cs42l56->beep, codec); + + ret = input_register_device(cs42l56->beep); + if (ret != 0) { + cs42l56->beep = NULL; + dev_err(codec->dev, "Failed to register beep device\n"); + } + + ret = device_create_file(codec->dev, &dev_attr_beep); + if (ret != 0) { + dev_err(codec->dev, "Failed to create keyclick file: %d\n", + ret); + } +} + +static void cs42l56_free_beep(struct snd_soc_codec *codec) +{ + struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); + + device_remove_file(codec->dev, &dev_attr_beep); + cancel_work_sync(&cs42l56->beep_work); + cs42l56->beep = NULL; + + snd_soc_update_bits(codec, CS42L56_BEEP_TONE_CFG, + CS42L56_BEEP_EN_MASK, 0); +} + +static int cs42l56_probe(struct snd_soc_codec *codec) +{ + cs42l56_init_beep(codec); + + cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +static int cs42l56_remove(struct snd_soc_codec *codec) +{ + struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec); + + cs42l56_free_beep(codec); + cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF); + regulator_bulk_free(ARRAY_SIZE(cs42l56->supplies), cs42l56->supplies); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_cs42l56 = { + .probe = cs42l56_probe, + .remove = cs42l56_remove, + .suspend = cs42l56_suspend, + .resume = cs42l56_resume, + .set_bias_level = cs42l56_set_bias_level, + + .dapm_widgets = cs42l56_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets), + .dapm_routes = cs42l56_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l56_audio_map), + + .controls = cs42l56_snd_controls, + .num_controls = ARRAY_SIZE(cs42l56_snd_controls), +}; + +static struct regmap_config cs42l56_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS42L56_MAX_REGISTER, + .reg_defaults = cs42l56_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults), + .readable_reg = cs42l56_readable_register, + .volatile_reg = cs42l56_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int cs42l56_handle_of_data(struct i2c_client *i2c_client, + struct cs42l56_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + u32 val32; + + if (of_property_read_bool(np, "cirrus,ain1a-reference-cfg")) + pdata->ain1a_ref_cfg = true; + + if (of_property_read_bool(np, "cirrus,ain2a-reference-cfg")) + pdata->ain2a_ref_cfg = true; + + if (of_property_read_bool(np, "cirrus,ain1b-reference-cfg")) + pdata->ain1b_ref_cfg = true; + + if (of_property_read_bool(np, "cirrus,ain2b-reference-cfg")) + pdata->ain2b_ref_cfg = true; + + if (of_property_read_u32(np, "cirrus,micbias-lvl", &val32) >= 0) + pdata->micbias_lvl = val32; + + if (of_property_read_u32(np, "cirrus,chgfreq-divisor", &val32) >= 0) + pdata->chgfreq = val32; + + if (of_property_read_u32(np, "cirrus,adaptive-pwr-cfg", &val32) >= 0) + pdata->adaptive_pwr = val32; + + if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0) + pdata->hpfa_freq = val32; + + if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0) + pdata->hpfb_freq = val32; + + pdata->gpio_nreset = of_get_named_gpio(np, "cirrus,gpio-nreset", 0); + + return 0; +} + +static int cs42l56_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs42l56_private *cs42l56; + struct cs42l56_platform_data *pdata = + dev_get_platdata(&i2c_client->dev); + int ret, i; + unsigned int devid = 0; + unsigned int alpha_rev, metal_rev; + unsigned int reg; + + cs42l56 = devm_kzalloc(&i2c_client->dev, + sizeof(struct cs42l56_private), + GFP_KERNEL); + if (cs42l56 == NULL) + return -ENOMEM; + cs42l56->dev = &i2c_client->dev; + + cs42l56->regmap = devm_regmap_init_i2c(i2c_client, &cs42l56_regmap); + if (IS_ERR(cs42l56->regmap)) { + ret = PTR_ERR(cs42l56->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + if (pdata) { + cs42l56->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, + sizeof(struct cs42l56_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(&i2c_client->dev, + "could not allocate pdata\n"); + return -ENOMEM; + } + if (i2c_client->dev.of_node) { + ret = cs42l56_handle_of_data(i2c_client, + &cs42l56->pdata); + if (ret != 0) + return ret; + } + cs42l56->pdata = *pdata; + } + + if (cs42l56->pdata.gpio_nreset) { + ret = gpio_request_one(cs42l56->pdata.gpio_nreset, + GPIOF_OUT_INIT_HIGH, "CS42L56 /RST"); + if (ret < 0) { + dev_err(&i2c_client->dev, + "Failed to request /RST %d: %d\n", + cs42l56->pdata.gpio_nreset, ret); + return ret; + } + gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0); + gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1); + } + + + i2c_set_clientdata(i2c_client, cs42l56); + + for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++) + cs42l56->supplies[i].supply = cs42l56_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_bypass(cs42l56->regmap, true); + + ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, ®); + devid = reg & CS42L56_CHIP_ID_MASK; + if (devid != CS42L56_DEVID) { + dev_err(&i2c_client->dev, + "CS42L56 Device ID (%X). Expected %X\n", + devid, CS42L56_DEVID); + goto err_enable; + } + alpha_rev = reg & CS42L56_AREV_MASK; + metal_rev = reg & CS42L56_MTLREV_MASK; + + dev_info(&i2c_client->dev, "Cirrus Logic CS42L56 "); + dev_info(&i2c_client->dev, "Alpha Rev %X Metal Rev %X\n", + alpha_rev, metal_rev); + + regcache_cache_bypass(cs42l56->regmap, false); + + if (cs42l56->pdata.ain1a_ref_cfg) + regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, + CS42L56_AIN1A_REF_MASK, 1); + + if (cs42l56->pdata.ain1b_ref_cfg) + regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, + CS42L56_AIN1B_REF_MASK, 1); + + if (cs42l56->pdata.ain2a_ref_cfg) + regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, + CS42L56_AIN2A_REF_MASK, 1); + + if (cs42l56->pdata.ain2b_ref_cfg) + regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, + CS42L56_AIN2B_REF_MASK, 1); + + if (cs42l56->pdata.micbias_lvl) + regmap_update_bits(cs42l56->regmap, CS42L56_GAIN_BIAS_CTL, + CS42L56_MIC_BIAS_MASK, + cs42l56->pdata.micbias_lvl); + + if (cs42l56->pdata.chgfreq) + regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL, + CS42L56_CHRG_FREQ_MASK, + cs42l56->pdata.chgfreq); + + if (cs42l56->pdata.hpfb_freq) + regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL, + CS42L56_HPFB_FREQ_MASK, + cs42l56->pdata.hpfb_freq); + + if (cs42l56->pdata.hpfa_freq) + regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL, + CS42L56_HPFA_FREQ_MASK, + cs42l56->pdata.hpfa_freq); + + if (cs42l56->pdata.adaptive_pwr) + regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL, + CS42L56_ADAPT_PWR_MASK, + cs42l56->pdata.adaptive_pwr); + + ret = snd_soc_register_codec(&i2c_client->dev, + &soc_codec_dev_cs42l56, &cs42l56_dai, 1); + if (ret < 0) + return ret; + + return 0; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + return ret; +} + +static int cs42l56_i2c_remove(struct i2c_client *client) +{ + struct cs42l56_private *cs42l56 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + return 0; +} + +static const struct of_device_id cs42l56_of_match[] = { + { .compatible = "cirrus,cs42l56", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs42l56_of_match); + + +static const struct i2c_device_id cs42l56_id[] = { + { "cs42l56", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs42l56_id); + +static struct i2c_driver cs42l56_i2c_driver = { + .driver = { + .name = "cs42l56", + .owner = THIS_MODULE, + .of_match_table = cs42l56_of_match, + }, + .id_table = cs42l56_id, + .probe = cs42l56_i2c_probe, + .remove = cs42l56_i2c_remove, +}; + +module_i2c_driver(cs42l56_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L56 driver"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l56.h b/sound/soc/codecs/cs42l56.h new file mode 100644 index 000000000000..ad2b50a90b16 --- /dev/null +++ b/sound/soc/codecs/cs42l56.h @@ -0,0 +1,175 @@ +/* + * cs42l52.h -- CS42L56 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + * + * 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 __CS42L56_H__ +#define __CS42L56_H__ + +#define CS42L56_CHIP_ID_1 0x01 +#define CS42L56_CHIP_ID_2 0x02 +#define CS42L56_PWRCTL_1 0x03 +#define CS42L56_PWRCTL_2 0x04 +#define CS42L56_CLKCTL_1 0x05 +#define CS42L56_CLKCTL_2 0x06 +#define CS42L56_SERIAL_FMT 0x07 +#define CS42L56_CLASSH_CTL 0x08 +#define CS42L56_MISC_CTL 0x09 +#define CS42L56_INT_STATUS 0x0a +#define CS42L56_PLAYBACK_CTL 0x0b +#define CS42L56_DSP_MUTE_CTL 0x0c +#define CS42L56_ADCA_MIX_VOLUME 0x0d +#define CS42L56_ADCB_MIX_VOLUME 0x0e +#define CS42L56_PCMA_MIX_VOLUME 0x0f +#define CS42L56_PCMB_MIX_VOLUME 0x10 +#define CS42L56_ANAINPUT_ADV_VOLUME 0x11 +#define CS42L56_DIGINPUT_ADV_VOLUME 0x12 +#define CS42L56_MASTER_A_VOLUME 0x13 +#define CS42L56_MASTER_B_VOLUME 0x14 +#define CS42L56_BEEP_FREQ_ONTIME 0x15 +#define CS42L56_BEEP_FREQ_OFFTIME 0x16 +#define CS42L56_BEEP_TONE_CFG 0x17 +#define CS42L56_TONE_CTL 0x18 +#define CS42L56_CHAN_MIX_SWAP 0x19 +#define CS42L56_AIN_REFCFG_ADC_MUX 0x1a +#define CS42L56_HPF_CTL 0x1b +#define CS42L56_MISC_ADC_CTL 0x1c +#define CS42L56_GAIN_BIAS_CTL 0x1d +#define CS42L56_PGAA_MUX_VOLUME 0x1e +#define CS42L56_PGAB_MUX_VOLUME 0x1f +#define CS42L56_ADCA_ATTENUATOR 0x20 +#define CS42L56_ADCB_ATTENUATOR 0x21 +#define CS42L56_ALC_EN_ATTACK_RATE 0x22 +#define CS42L56_ALC_RELEASE_RATE 0x23 +#define CS42L56_ALC_THRESHOLD 0x24 +#define CS42L56_NOISE_GATE_CTL 0x25 +#define CS42L56_ALC_LIM_SFT_ZC 0x26 +#define CS42L56_AMUTE_HPLO_MUX 0x27 +#define CS42L56_HPA_VOLUME 0x28 +#define CS42L56_HPB_VOLUME 0x29 +#define CS42L56_LOA_VOLUME 0x2a +#define CS42L56_LOB_VOLUME 0x2b +#define CS42L56_LIM_THRESHOLD_CTL 0x2c +#define CS42L56_LIM_CTL_RELEASE_RATE 0x2d +#define CS42L56_LIM_ATTACK_RATE 0x2e + +/* Device ID and Rev ID Masks */ +#define CS42L56_DEVID 0x56 +#define CS42L56_CHIP_ID_MASK 0xff +#define CS42L56_AREV_MASK 0x1c +#define CS42L56_MTLREV_MASK 0x03 + +/* Power bit masks */ +#define CS42L56_PDN_ALL_MASK 0x01 +#define CS42L56_PDN_ADCA_MASK 0x02 +#define CS42L56_PDN_ADCB_MASK 0x04 +#define CS42L56_PDN_CHRG_MASK 0x08 +#define CS42L56_PDN_BIAS_MASK 0x10 +#define CS42L56_PDN_VBUF_MASK 0x20 +#define CS42L56_PDN_LOA_MASK 0x03 +#define CS42L56_PDN_LOB_MASK 0x0c +#define CS42L56_PDN_HPA_MASK 0x30 +#define CS42L56_PDN_HPB_MASK 0xc0 + +/* serial port and clk masks */ +#define CS42L56_MASTER_MODE 1 +#define CS42L56_SLAVE_MODE 0 +#define CS42L56_MS_MODE_MASK 0x40 +#define CS42L56_SCLK_INV 1 +#define CS42L56_SCLK_INV_MASK 0x20 +#define CS42L56_SCLK_MCLK_MASK 0x18 +#define CS42L56_MCLK_PREDIV_MASK 0x04 +#define CS42L56_MCLK_DIV2_MASK 0x02 +#define CS42L56_MCLK_DIS_MASK 0x01 +#define CS42L56_CLK_AUTO_MASK 0x20 +#define CS42L56_CLK_RATIO_MASK 0x1f +#define CS42L56_DIG_FMT_I2S 0 +#define CS42L56_DIG_FMT_LEFT_J 1 +#define CS42L56_DIG_FMT_MASK 0x08 + +/* Class H and misc ctl masks */ +#define CS42L56_ADAPT_PWR_MASK 0xc0 +#define CS42L56_CHRG_FREQ_MASK 0x0f +#define CS42L56_DIG_MUX_MASK 0x80 +#define CS42L56_ANLGSFT_MASK 0x10 +#define CS42L56_ANLGZC_MASK 0x08 +#define CS42L56_DIGSFT_MASK 0x04 +#define CS42L56_FREEZE_MASK 0x01 +#define CS42L56_MIC_BIAS_MASK 0x03 +#define CS42L56_HPFA_FREQ_MASK 0x03 +#define CS42L56_HPFB_FREQ_MASK 0xc0 +#define CS42L56_AIN1A_REF_MASK 0x10 +#define CS42L56_AIN2A_REF_MASK 0x40 +#define CS42L56_AIN1B_REF_MASK 0x20 +#define CS42L56_AIN2B_REF_MASK 0x80 + +/* Playback Capture ctl masks */ +#define CS42L56_PDN_DSP_MASK 0x80 +#define CS42L56_DEEMPH_MASK 0x40 +#define CS42L56_PLYBCK_GANG_MASK 0x10 +#define CS42L56_PCM_INV_MASK 0x0c +#define CS42L56_MUTE 1 +#define CS42L56_UNMUTE 0 +#define CS42L56_ADCAMIX_MUTE_MASK 0x40 +#define CS42L56_ADCBMIX_MUTE_MASK 0x80 +#define CS42L56_PCMAMIX_MUTE_MASK 0x10 +#define CS42L56_PCMBMIX_MUTE_MASK 0x20 +#define CS42L56_MSTB_MUTE_MASK 0x02 +#define CS42L56_MSTA_MUTE_MASK 0x01 +#define CS42L56_ADCA_MUTE_MASK 0x01 +#define CS42L56_ADCB_MUTE_MASK 0x02 +#define CS42L56_HP_MUTE_MASK 0x80 +#define CS42L56_LO_MUTE_MASK 0x80 + +/* Beep masks */ +#define CS42L56_BEEP_FREQ_MASK 0xf0 +#define CS42L56_BEEP_ONTIME_MASK 0x0f +#define CS42L56_BEEP_OFFTIME_MASK 0xe0 +#define CS42L56_BEEP_CFG_MASK 0xc0 +#define CS42L56_BEEP_TREBCF_MASK 0x18 +#define CS42L56_BEEP_BASSCF_MASK 0x06 +#define CS42L56_BEEP_TCEN_MASK 0x01 +#define CS42L56_BEEP_RATE_SHIFT 4 +#define CS42L56_BEEP_EN_MASK 0x3f + + +/* Supported MCLKS */ +#define CS42L56_MCLK_5P6448MHZ 5644800 +#define CS42L56_MCLK_6MHZ 6000000 +#define CS42L56_MCLK_6P144MHZ 6144000 +#define CS42L56_MCLK_11P2896MHZ 11289600 +#define CS42L56_MCLK_12MHZ 12000000 +#define CS42L56_MCLK_12P288MHZ 12288000 +#define CS42L56_MCLK_22P5792MHZ 22579200 +#define CS42L56_MCLK_24MHZ 24000000 +#define CS42L56_MCLK_24P576MHZ 24576000 + +/* Clock ratios */ +#define CS42L56_MCLK_LRCLK_128 0x08 +#define CS42L56_MCLK_LRCLK_125 0x09 +#define CS42L56_MCLK_LRCLK_136 0x0b +#define CS42L56_MCLK_LRCLK_192 0x0c +#define CS42L56_MCLK_LRCLK_187P5 0x0d +#define CS42L56_MCLK_LRCLK_256 0x10 +#define CS42L56_MCLK_LRCLK_250 0x11 +#define CS42L56_MCLK_LRCLK_272 0x13 +#define CS42L56_MCLK_LRCLK_384 0x14 +#define CS42L56_MCLK_LRCLK_375 0x15 +#define CS42L56_MCLK_LRCLK_512 0x18 +#define CS42L56_MCLK_LRCLK_500 0x19 +#define CS42L56_MCLK_LRCLK_544 0x1b +#define CS42L56_MCLK_LRCLK_750 0x1c +#define CS42L56_MCLK_LRCLK_768 0x1d + + +#define CS42L56_MAX_REGISTER 0x34 + +#endif -- cgit v1.2.3 From 86aae6c7b577654b7293374973985a153e0c147e Mon Sep 17 00:00:00 2001 From: Libor Pechacek Date: Tue, 29 Apr 2014 20:38:34 +0200 Subject: Bluetooth: Convert RFCOMM spinlocks into mutexes Enabling CONFIG_DEBUG_ATOMIC_SLEEP has shown that some rfcomm functions acquiring spinlocks call sleeping locks further in the chain. Converting the offending spinlocks into mutexes makes sleeping safe. Signed-off-by: Libor Pechacek Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 6 +++--- net/bluetooth/rfcomm/core.c | 2 +- net/bluetooth/rfcomm/tty.c | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 2611cc389d7d..578b83127af1 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -173,7 +173,7 @@ struct rfcomm_dlc { struct sk_buff_head tx_queue; struct timer_list timer; - spinlock_t lock; + struct mutex lock; unsigned long state; unsigned long flags; atomic_t refcnt; @@ -244,8 +244,8 @@ int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); void rfcomm_dlc_accept(struct rfcomm_dlc *d); struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel); -#define rfcomm_dlc_lock(d) spin_lock(&d->lock) -#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) +#define rfcomm_dlc_lock(d) mutex_lock(&d->lock) +#define rfcomm_dlc_unlock(d) mutex_unlock(&d->lock) static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d) { diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 633cceeb943e..05c609b12a1d 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -307,7 +307,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio) setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d); skb_queue_head_init(&d->tx_queue); - spin_lock_init(&d->lock); + mutex_init(&d->lock); atomic_set(&d->refcnt, 1); rfcomm_dlc_clear_state(d); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 403ec09f480a..8e385a0ae60e 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -70,7 +70,7 @@ struct rfcomm_dev { }; static LIST_HEAD(rfcomm_dev_list); -static DEFINE_SPINLOCK(rfcomm_dev_lock); +static DEFINE_MUTEX(rfcomm_dev_lock); static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); @@ -96,9 +96,9 @@ static void rfcomm_dev_destruct(struct tty_port *port) if (dev->tty_dev) tty_unregister_device(rfcomm_tty_driver, dev->id); - spin_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); list_del(&dev->list); - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); kfree(dev); @@ -161,14 +161,14 @@ static struct rfcomm_dev *rfcomm_dev_get(int id) { struct rfcomm_dev *dev; - spin_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); dev = __rfcomm_dev_lookup(id); if (dev && !tty_port_get(&dev->port)) dev = NULL; - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); return dev; } @@ -224,7 +224,7 @@ static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req, if (!dev) return ERR_PTR(-ENOMEM); - spin_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); if (req->dev_id < 0) { dev->id = 0; @@ -305,11 +305,11 @@ static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req, holds reference to this module. */ __module_get(THIS_MODULE); - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); return dev; out: - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); kfree(dev); return ERR_PTR(err); } @@ -524,7 +524,7 @@ static int rfcomm_get_dev_list(void __user *arg) di = dl->dev_info; - spin_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); list_for_each_entry(dev, &rfcomm_dev_list, list) { if (!tty_port_get(&dev->port)) @@ -540,7 +540,7 @@ static int rfcomm_get_dev_list(void __user *arg) break; } - spin_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); dl->dev_num = n; size = sizeof(*dl) + n * sizeof(*di); -- cgit v1.2.3 From bdffd893a0e9c431304142d12d9a0a21d365c502 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Tue, 29 Apr 2014 14:17:40 -0500 Subject: tracing: Replace __get_cpu_var uses with this_cpu_ptr Replace uses of &__get_cpu_var for address calculation with this_cpu_ptr. Link: http://lkml.kernel.org/p/alpine.DEB.2.10.1404291415560.18364@gentwo.org Acked-by: Masami Hiramatsu Signed-off-by: Christoph Lameter Signed-off-by: Steven Rostedt --- include/linux/kprobes.h | 2 +- kernel/trace/ftrace.c | 4 ++-- kernel/trace/trace.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 925eaf28fca9..7bd2ad01e39c 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -355,7 +355,7 @@ static inline void reset_current_kprobe(void) static inline struct kprobe_ctlblk *get_kprobe_ctlblk(void) { - return (&__get_cpu_var(kprobe_ctlblk)); + return this_cpu_ptr(&kprobe_ctlblk); } int register_kprobe(struct kprobe *p); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 9eb1aa03a18d..38e5cf73b9ae 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -822,7 +822,7 @@ function_profile_call(unsigned long ip, unsigned long parent_ip, local_irq_save(flags); - stat = &__get_cpu_var(ftrace_profile_stats); + stat = this_cpu_ptr(&ftrace_profile_stats); if (!stat->hash || !ftrace_profile_enabled) goto out; @@ -853,7 +853,7 @@ static void profile_graph_return(struct ftrace_graph_ret *trace) unsigned long flags; local_irq_save(flags); - stat = &__get_cpu_var(ftrace_profile_stats); + stat = this_cpu_ptr(&ftrace_profile_stats); if (!stat->hash || !ftrace_profile_enabled) goto out; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4c392c8238bf..05431696b10c 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1726,7 +1726,7 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer, */ barrier(); if (use_stack == 1) { - trace.entries = &__get_cpu_var(ftrace_stack).calls[0]; + trace.entries = this_cpu_ptr(ftrace_stack.calls); trace.max_entries = FTRACE_STACK_MAX_ENTRIES; if (regs) -- cgit v1.2.3 From 16fb4f8bd59e0e954991f624bcc53dad2052ef0d Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Tue, 15 Apr 2014 17:27:07 -0500 Subject: ARM: socfpga: dts: add reset-controller Add the necessary #reset-cells property to the rst-mgr node and provide a header-file with all possible resets specified. Signed-off-by: Steffen Trumtrar Signed-off-by: Dinh Nguyen --- arch/arm/boot/dts/socfpga.dtsi | 7 ++- include/dt-bindings/reset/altr,rst-mgr.h | 90 ++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 include/dt-bindings/reset/altr,rst-mgr.h (limited to 'include') diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index f31c5fa5525b..917464ac01ef 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -16,6 +16,7 @@ */ #include "skeleton.dtsi" +#include / { #address-cells = <1>; @@ -483,6 +484,8 @@ mac-address = [00 00 00 00 00 00];/* Filled in by U-Boot */ clocks = <&emac0_clk>; clock-names = "stmmaceth"; + resets = <&rst EMAC0_RESET>; + reset-names = "stmmaceth"; status = "disabled"; }; @@ -495,6 +498,8 @@ mac-address = [00 00 00 00 00 00];/* Filled in by U-Boot */ clocks = <&emac1_clk>; clock-names = "stmmaceth"; + resets = <&rst EMAC1_RESET>; + reset-names = "stmmaceth"; status = "disabled"; }; @@ -617,7 +622,7 @@ clocks = <&l4_sp_clk>; }; - rstmgr@ffd05000 { + rst: rstmgr@ffd05000 { compatible = "altr,rst-mgr"; reg = <0xffd05000 0x1000>; }; diff --git a/include/dt-bindings/reset/altr,rst-mgr.h b/include/dt-bindings/reset/altr,rst-mgr.h new file mode 100644 index 000000000000..3f04908fb87c --- /dev/null +++ b/include/dt-bindings/reset/altr,rst-mgr.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, Steffen Trumtrar + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_RESET_ALTR_RST_MGR_H +#define _DT_BINDINGS_RESET_ALTR_RST_MGR_H + +/* MPUMODRST */ +#define CPU0_RESET 0 +#define CPU1_RESET 1 +#define WDS_RESET 2 +#define SCUPER_RESET 3 +#define L2_RESET 4 + +/* PERMODRST */ +#define EMAC0_RESET 32 +#define EMAC1_RESET 33 +#define USB0_RESET 34 +#define USB1_RESET 35 +#define NAND_RESET 36 +#define QSPI_RESET 37 +#define L4WD0_RESET 38 +#define L4WD1_RESET 39 +#define OSC1TIMER0_RESET 40 +#define OSC1TIMER1_RESET 41 +#define SPTIMER0_RESET 42 +#define SPTIMER1_RESET 43 +#define I2C0_RESET 44 +#define I2C1_RESET 45 +#define I2C2_RESET 46 +#define I2C3_RESET 47 +#define UART0_RESET 48 +#define UART1_RESET 49 +#define SPIM0_RESET 50 +#define SPIM1_RESET 51 +#define SPIS0_RESET 52 +#define SPIS1_RESET 53 +#define SDMMC_RESET 54 +#define CAN0_RESET 55 +#define CAN1_RESET 56 +#define GPIO0_RESET 57 +#define GPIO1_RESET 58 +#define GPIO2_RESET 59 +#define DMA_RESET 60 +#define SDR_RESET 61 + +/* PER2MODRST */ +#define DMAIF0_RESET 64 +#define DMAIF1_RESET 65 +#define DMAIF2_RESET 66 +#define DMAIF3_RESET 67 +#define DMAIF4_RESET 68 +#define DMAIF5_RESET 69 +#define DMAIF6_RESET 70 +#define DMAIF7_RESET 71 + +/* BRGMODRST */ +#define HPS2FPGA_RESET 96 +#define LWHPS2FPGA_RESET 97 +#define FPGA2HPS_RESET 98 + +/* MISCMODRST*/ +#define ROM_RESET 128 +#define OCRAM_RESET 129 +#define SYSMGR_RESET 130 +#define SYSMGRCOLD_RESET 131 +#define FPGAMGR_RESET 132 +#define ACPIDMAP_RESET 133 +#define S2F_RESET 134 +#define S2FCOLD_RESET 135 +#define NRSTPIN_RESET 136 +#define TIMESTAMPCOLD_RESET 137 +#define CLKMGRCOLD_RESET 138 +#define SCANMGR_RESET 139 +#define FRZCTRLCOLD_RESET 140 +#define SYSDBG_RESET 141 +#define DBG_RESET 142 +#define TAPCOLD_RESET 143 +#define SDRCOLD_RESET 144 + +#endif -- cgit v1.2.3 From e029ae5b787e08e976a683c6a45fac20fc227447 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 26 Mar 2014 16:11:54 +0100 Subject: KVM: s390: Add clock comparator and CPU timer IRQ injection Add an interface to inject clock comparator and CPU timer interrupts into the guest. This is needed for handling the external interrupt interception. Signed-off-by: Thomas Huth Reviewed-by: David Hildenbrand Reviewed-by: Cornelia Huck Signed-off-by: Christian Borntraeger --- Documentation/virtual/kvm/api.txt | 2 ++ arch/s390/kvm/interrupt.c | 32 ++++++++++++++++++++++++++++++++ include/uapi/linux/kvm.h | 2 ++ 3 files changed, 36 insertions(+) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 2014ff12b492..0581f6c40f2b 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2211,6 +2211,8 @@ KVM_S390_SIGP_STOP (vcpu) - sigp restart KVM_S390_PROGRAM_INT (vcpu) - program check; code in parm KVM_S390_SIGP_SET_PREFIX (vcpu) - sigp set prefix; prefix address in parm KVM_S390_RESTART (vcpu) - restart +KVM_S390_INT_CLOCK_COMP (vcpu) - clock comparator interrupt +KVM_S390_INT_CPU_TIMER (vcpu) - CPU timer interrupt KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt parameters in parm and parm64 KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index d9526bb29194..75cd3217cd5a 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -27,6 +27,8 @@ #define IOINT_CSSID_MASK 0x03fc0000 #define IOINT_AI_MASK 0x04000000 +static void deliver_ckc_interrupt(struct kvm_vcpu *vcpu); + static int is_ioint(u64 type) { return ((type & 0xfffe0000u) != 0xfffe0000u); @@ -89,6 +91,14 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, if (vcpu->arch.sie_block->gcr[0] & 0x4000ul) return 1; return 0; + case KVM_S390_INT_CLOCK_COMP: + return ckc_interrupts_enabled(vcpu); + case KVM_S390_INT_CPU_TIMER: + if (psw_extint_disabled(vcpu)) + return 0; + if (vcpu->arch.sie_block->gcr[0] & 0x400ul) + return 1; + return 0; case KVM_S390_INT_SERVICE: case KVM_S390_INT_PFAULT_INIT: case KVM_S390_INT_PFAULT_DONE: @@ -166,6 +176,8 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu, case KVM_S390_INT_PFAULT_INIT: case KVM_S390_INT_PFAULT_DONE: case KVM_S390_INT_VIRTIO: + case KVM_S390_INT_CLOCK_COMP: + case KVM_S390_INT_CPU_TIMER: if (psw_extint_disabled(vcpu)) __set_cpuflag(vcpu, CPUSTAT_EXT_INT); else @@ -326,6 +338,24 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu, &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); break; + case KVM_S390_INT_CLOCK_COMP: + trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, + inti->ext.ext_params, 0); + deliver_ckc_interrupt(vcpu); + break; + case KVM_S390_INT_CPU_TIMER: + trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, + inti->ext.ext_params, 0); + rc = put_guest_lc(vcpu, EXT_IRQ_CPU_TIMER, + (u16 *)__LC_EXT_INT_CODE); + rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, + &vcpu->arch.sie_block->gpsw, + sizeof(psw_t)); + rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, + &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); + rc |= put_guest_lc(vcpu, inti->ext.ext_params, + (u32 *)__LC_EXT_PARAMS); + break; case KVM_S390_INT_SERVICE: VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x", inti->ext.ext_params); @@ -984,6 +1014,8 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, break; case KVM_S390_SIGP_STOP: case KVM_S390_RESTART: + case KVM_S390_INT_CLOCK_COMP: + case KVM_S390_INT_CPU_TIMER: VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); inti->type = s390int->type; break; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 836e15b7abc8..2b83cf35437a 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -416,6 +416,8 @@ struct kvm_s390_psw { #define KVM_S390_INT_PFAULT_INIT 0xfffe0004u #define KVM_S390_INT_PFAULT_DONE 0xfffe0005u #define KVM_S390_MCHK 0xfffe1000u +#define KVM_S390_INT_CLOCK_COMP 0xffff1004u +#define KVM_S390_INT_CPU_TIMER 0xffff1005u #define KVM_S390_INT_VIRTIO 0xffff2603u #define KVM_S390_INT_SERVICE 0xffff2401u #define KVM_S390_INT_EMERGENCY 0xffff1201u -- cgit v1.2.3 From 59af6928d2099479c0bc2ef3f66cc7b33998120a Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 9 Apr 2014 15:10:59 +0200 Subject: mac80211: fix CSA tx queue stopping It was possible for tx queues to be stuck stopped if AP CSA finalization failed. In that case neither stop_ap nor do_stop woke the queues up. This means it was impossible to perform tx at all until driver was reloaded or a successful CSA was performed later. It was possible to solve this in a simpler manner however this is more robust and future proof (having multi-vif CSA in mind). New sdata->csa_block_tx is introduced to keep track of which interfaces requested tx to be blocked for CSA. This is required because mac80211 stops all tx queues for that purpose. This means queues must be awoken only when last tx-blocking CSA interface is finished. It is still possible to have tx queues stopped after CSA failure but as soon as offending interfaces are stopped from userspace (stop_ap or ifdown) tx queues are woken up properly. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++- net/mac80211/cfg.c | 77 ++++++++++++++++++++++++++++++++++++++-------- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 7 +++++ net/mac80211/mlme.c | 37 ++++++++++++++++------ 5 files changed, 104 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index bdb4a7cbab31..3541c48a97cd 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1113,7 +1113,9 @@ enum ieee80211_vif_flags { * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively - * @csa_active: marks whether a channel switch is going on + * @csa_active: marks whether a channel switch is going on. Internally it is + * write-protected by sdata_lock and local->mtx so holding either is fine + * for read access. * @driver_flags: flags/capabilities the driver has for this interface, * these need to be set (or cleared) when the interface is added * or, if supported by the driver, the interface type is changed diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 19a7e6ff45d3..f789c3198af4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1084,6 +1084,31 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, return 0; } +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + + lockdep_assert_held(&local->mtx); + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata)) + continue; + + if (!sdata->vif.csa_active) + continue; + + if (!sdata->csa_block_tx) + continue; + + rcu_read_unlock(); + return true; + } + rcu_read_unlock(); + + return false; +} + static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1101,7 +1126,14 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata); /* abort any running channel switch */ + mutex_lock(&local->mtx); sdata->vif.csa_active = false; + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); + kfree(sdata->u.ap.next_beacon); sdata->u.ap.next_beacon = NULL; @@ -3027,11 +3059,10 @@ static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) int err, changed = 0; sdata_assert_lock(sdata); + lockdep_assert_held(&local->mtx); - mutex_lock(&local->mtx); sdata->radar_required = sdata->csa_radar_required; err = ieee80211_vif_change_channel(sdata, &changed); - mutex_unlock(&local->mtx); if (WARN_ON(err < 0)) return; @@ -3072,11 +3103,12 @@ static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) ieee80211_bss_info_change_notify(sdata, changed); - ieee80211_wake_queues_by_reason(&sdata->local->hw, + cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); + + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); - - cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); } void ieee80211_csa_finalize_work(struct work_struct *work) @@ -3084,8 +3116,11 @@ void ieee80211_csa_finalize_work(struct work_struct *work) struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, csa_finalize_work); + struct ieee80211_local *local = sdata->local; sdata_lock(sdata); + mutex_lock(&local->mtx); + /* AP might have been stopped while waiting for the lock. */ if (!sdata->vif.csa_active) goto unlock; @@ -3096,6 +3131,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work) ieee80211_csa_finalize(sdata); unlock: + mutex_unlock(&local->mtx); sdata_unlock(sdata); } @@ -3222,8 +3258,8 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, return 0; } -int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_csa_settings *params) +int __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -3232,6 +3268,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, int err, num_chanctx, changed = 0; sdata_assert_lock(sdata); + lockdep_assert_held(&local->mtx); if (!list_empty(&local->roc_list) || local->scanning) return -EBUSY; @@ -3274,15 +3311,15 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, return err; sdata->csa_radar_required = params->radar_required; - - if (params->block_tx) - ieee80211_stop_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); - sdata->csa_chandef = params->chandef; + sdata->csa_block_tx = params->block_tx; sdata->vif.csa_active = true; + if (sdata->csa_block_tx) + ieee80211_stop_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + if (changed) { ieee80211_bss_info_change_notify(sdata, changed); drv_channel_switch_beacon(sdata, ¶ms->chandef); @@ -3294,6 +3331,20 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, return 0; } +int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + int err; + + mutex_lock(&local->mtx); + err = __ieee80211_channel_switch(wiphy, dev, params); + mutex_unlock(&local->mtx); + + return err; +} + static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 *cookie) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f86d06aaf54b..f4ba0c4a4314 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -756,6 +756,7 @@ struct ieee80211_sub_if_data { int csa_counter_offset_beacon; int csa_counter_offset_presp; bool csa_radar_required; + bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ struct cfg80211_chan_def csa_chandef; struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */ @@ -1472,6 +1473,7 @@ void ieee80211_sw_roc_work(struct work_struct *work); void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); /* channel switch handling */ +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local); void ieee80211_csa_finalize_work(struct work_struct *work); int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7fff3dcaac43..79fc98815da8 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -838,8 +838,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->recalc_smps); sdata_lock(sdata); + mutex_lock(&local->mtx); sdata->vif.csa_active = false; + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); sdata_unlock(sdata); + cancel_work_sync(&sdata->csa_finalize_work); cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 488826f188a7..d68e73cbdcd6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -975,16 +975,20 @@ static void ieee80211_chswitch_work(struct work_struct *work) /* XXX: shouldn't really modify cfg80211-owned data! */ ifmgd->associated->channel = sdata->csa_chandef.chan; + ieee80211_bss_info_change_notify(sdata, changed); + + mutex_lock(&local->mtx); + sdata->vif.csa_active = false; /* XXX: wait for a beacon first? */ - ieee80211_wake_queues_by_reason(&local->hw, + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); - ieee80211_bss_info_change_notify(sdata, changed); - - out: - sdata->vif.csa_active = false; ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + +out: sdata_unlock(sdata); } @@ -1100,12 +1104,16 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, mutex_unlock(&local->chanctx_mtx); sdata->csa_chandef = csa_ie.chandef; + + mutex_lock(&local->mtx); sdata->vif.csa_active = true; + sdata->csa_block_tx = csa_ie.mode; - if (csa_ie.mode) + if (sdata->csa_block_tx) ieee80211_stop_queues_by_reason(&local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); if (local->ops->channel_switch) { /* use driver's channel switch callback */ @@ -1817,6 +1825,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ifmgd->flags = 0; mutex_lock(&local->mtx); ieee80211_vif_release_channel(sdata); + + sdata->vif.csa_active = false; + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); mutex_unlock(&local->mtx); sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; @@ -2045,6 +2059,7 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get); static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) { + struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -2058,10 +2073,14 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, true, frame_buf); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + + mutex_lock(&local->mtx); sdata->vif.csa_active = false; - ieee80211_wake_queues_by_reason(&sdata->local->hw, + if (!ieee80211_csa_needs_block_tx(local)) + ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); + mutex_unlock(&local->mtx); cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); -- cgit v1.2.3 From f04c22033c25f71617ac62bcfe75698baa17a0b8 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 9 Apr 2014 15:11:01 +0200 Subject: cfg80211: export interface stopping function This exports a new cfg80211_stop_iface() function. This is intended for driver internal interface combination management and channel switching. Due to locking issues (it re-enters driver) the call is asynchronous and uses cfg80211 event list/worker. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 15 +++++++++++++++ net/wireless/ap.c | 4 ++-- net/wireless/core.c | 43 ++++++++++++++++++++++++++++++++++++------- net/wireless/core.h | 7 +++++++ net/wireless/mesh.c | 4 ++-- net/wireless/trace.h | 15 +++++++++++++++ net/wireless/util.c | 3 +++ 7 files changed, 80 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7eae46ccec01..0631230b01eb 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4756,6 +4756,21 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, void *data), void *data); +/* + * cfg80211_stop_iface - trigger interface disconnection + * + * @wiphy: the wiphy + * @wdev: wireless device + * @gfp: context flags + * + * Trigger interface to be stopped as if AP was stopped, IBSS/mesh left, STA + * disconnected. + * + * Note: This doesn't need any locks and is asynchronous. + */ +void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, + gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 3e02ade508d8..bdad1f951561 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -6,8 +6,8 @@ #include "rdev-ops.h" -static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool notify) +int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool notify) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; diff --git a/net/wireless/core.c b/net/wireless/core.c index f509da4d9be9..7e023b74f009 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -792,23 +792,23 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, rdev->num_running_monitor_ifaces += num; } -void cfg80211_leave(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +void __cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; ASSERT_RTNL(); + ASSERT_WDEV_LOCK(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - cfg80211_leave_ibss(rdev, dev, true); + __cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) __cfg80211_stop_sched_scan(rdev, false); - wdev_lock(wdev); #ifdef CONFIG_CFG80211_WEXT kfree(wdev->wext.ie); wdev->wext.ie = NULL; @@ -817,20 +817,49 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, #endif cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); - wdev_unlock(wdev); break; case NL80211_IFTYPE_MESH_POINT: - cfg80211_leave_mesh(rdev, dev); + __cfg80211_leave_mesh(rdev, dev); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - cfg80211_stop_ap(rdev, dev, true); + __cfg80211_stop_ap(rdev, dev, true); break; default: break; } } +void cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + wdev_lock(wdev); + __cfg80211_leave(rdev, wdev); + wdev_unlock(wdev); +} + +void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, + gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + trace_cfg80211_stop_iface(wiphy, wdev); + + ev = kzalloc(sizeof(*ev), gfp); + if (!ev) + return; + + ev->type = EVENT_STOPPED; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + queue_work(cfg80211_wq, &rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_stop_iface); + static int cfg80211_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ptr) { diff --git a/net/wireless/core.h b/net/wireless/core.h index 681b8fa4355b..e9afbf10e756 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -185,6 +185,7 @@ enum cfg80211_event_type { EVENT_ROAMED, EVENT_DISCONNECTED, EVENT_IBSS_JOINED, + EVENT_STOPPED, }; struct cfg80211_event { @@ -281,6 +282,8 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, struct mesh_setup *setup, const struct mesh_config *conf); +int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev); int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev); int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, @@ -288,6 +291,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, struct cfg80211_chan_def *chandef); /* AP */ +int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool notify); int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev, bool notify); @@ -441,6 +446,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); +void __cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev); void cfg80211_leave(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 3ddfb7cd335e..092300b30c37 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -238,8 +238,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, return 0; } -static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev) +int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index f3c13ff4d04c..cdfbb00e1b37 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2636,6 +2636,21 @@ TRACE_EVENT(cfg80211_ft_event, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap)) ); +TRACE_EVENT(cfg80211_stop_iface, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, + WIPHY_PR_ARG, WDEV_PR_ARG) +); + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/wireless/util.c b/net/wireless/util.c index 7c47fa07b276..a756429b3a0a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -839,6 +839,9 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid, ev->ij.channel); break; + case EVENT_STOPPED: + __cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev); + break; } wdev_unlock(wdev); -- cgit v1.2.3 From 31f0820a6709fd9f4e4208081f949f9036972e06 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 5 May 2014 17:27:39 -0700 Subject: ARM: dts: Fix omap serial wake-up when booted with device tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've had deeper idle states working on omaps for few years now, but only in the legacy mode. When booted with device tree, the wake-up events did not have a chance to work until commit 3e6cee1786a1 (pinctrl: single: Add support for wake-up interrupts) that recently got merged. In addition to that we also needed commit 79d9701559a9 (of/irq: create interrupts-extended property) and 9ec36cafe43b (of/irq: do irq resolution in platform_get_irq) that are now also merged. So let's fix the wake-up events for some selected omaps so devices booted in device tree mode won't just hang if deeper power states are enabled, and so systems can wake up from suspend to the serial port event. Note that there's no longer need to specify the wake-up bit in the pinctrl settings, the request_irq on the wake-up pin takes care of that. Cc: devicetree@vger.kernel.org Cc: "Benoît Cousson" Cc: Kevin Hilman Cc: Nishanth Menon Cc: Paul Walmsley Cc: Tero Kristo [tony@atomide.com: updated comments, added board LDP] Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/omap3-evm-37xx.dts | 9 +++++++++ arch/arm/boot/dts/omap3-ldp.dts | 4 ++++ arch/arm/boot/dts/omap3-n900.dts | 2 ++ arch/arm/boot/dts/omap3.dtsi | 6 +++--- arch/arm/boot/dts/omap4-panda-common.dtsi | 15 +++++++++++++++ arch/arm/boot/dts/omap4-sdp.dts | 6 ++++++ arch/arm/boot/dts/omap4.dtsi | 6 +++--- include/dt-bindings/pinctrl/omap.h | 12 ++++++++++++ 8 files changed, 54 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/omap3-evm-37xx.dts b/arch/arm/boot/dts/omap3-evm-37xx.dts index 4df68ad3736a..9cba94bed7ad 100644 --- a/arch/arm/boot/dts/omap3-evm-37xx.dts +++ b/arch/arm/boot/dts/omap3-evm-37xx.dts @@ -89,7 +89,16 @@ status = "disabled"; }; +&uart1 { + interrupts-extended = <&intc 72 &omap3_pmx_core OMAP3_UART1_RX>; +}; + +&uart2 { + interrupts-extended = <&intc 73 &omap3_pmx_core OMAP3_UART2_RX>; +}; + &uart3 { + interrupts-extended = <&intc 74 &omap3_pmx_core OMAP3_UART3_RX>; pinctrl-names = "default"; pinctrl-0 = <&uart3_pins>; }; diff --git a/arch/arm/boot/dts/omap3-ldp.dts b/arch/arm/boot/dts/omap3-ldp.dts index 0abe986a4ecc..476ff158ddb3 100644 --- a/arch/arm/boot/dts/omap3-ldp.dts +++ b/arch/arm/boot/dts/omap3-ldp.dts @@ -234,6 +234,10 @@ }; }; +&uart3 { + interrupts-extended = <&intc 74 &omap3_pmx_core OMAP3_UART3_RX>; +}; + &usb_otg_hs { pinctrl-names = "default"; pinctrl-0 = <&musb_pins>; diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index 1a57b61f5e24..656ae403018f 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -618,11 +618,13 @@ }; &uart2 { + interrupts-extended = <&intc 73 &omap3_pmx_core OMAP3_UART2_RX>; pinctrl-names = "default"; pinctrl-0 = <&uart2_pins>; }; &uart3 { + interrupts-extended = <&intc 74 &omap3_pmx_core OMAP3_UART3_RX>; pinctrl-names = "default"; pinctrl-0 = <&uart3_pins>; }; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index acb9019dc437..8a0ce605fdd8 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -267,7 +267,7 @@ uart1: serial@4806a000 { compatible = "ti,omap3-uart"; reg = <0x4806a000 0x2000>; - interrupts = <72>; + interrupts-extended = <&intc 72>; dmas = <&sdma 49 &sdma 50>; dma-names = "tx", "rx"; ti,hwmods = "uart1"; @@ -277,7 +277,7 @@ uart2: serial@4806c000 { compatible = "ti,omap3-uart"; reg = <0x4806c000 0x400>; - interrupts = <73>; + interrupts-extended = <&intc 73>; dmas = <&sdma 51 &sdma 52>; dma-names = "tx", "rx"; ti,hwmods = "uart2"; @@ -287,7 +287,7 @@ uart3: serial@49020000 { compatible = "ti,omap3-uart"; reg = <0x49020000 0x400>; - interrupts = <74>; + interrupts-extended = <&intc 74>; dmas = <&sdma 53 &sdma 54>; dma-names = "tx", "rx"; ti,hwmods = "uart3"; diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi index d2c45bfaaa2c..8cfa3c8a72b0 100644 --- a/arch/arm/boot/dts/omap4-panda-common.dtsi +++ b/arch/arm/boot/dts/omap4-panda-common.dtsi @@ -481,6 +481,21 @@ usb-supply = <&vusb>; }; +&uart2 { + interrupts-extended = <&gic GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH + &omap4_pmx_core OMAP4_UART2_RX>; +}; + +&uart3 { + interrupts-extended = <&gic GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH + &omap4_pmx_core OMAP4_UART3_RX>; +}; + +&uart4 { + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH + &omap4_pmx_core OMAP4_UART4_RX>; +}; + &usb_otg_hs { interface-type = <1>; mode = <3>; diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts index 48983c8d56c2..3e1da43068f6 100644 --- a/arch/arm/boot/dts/omap4-sdp.dts +++ b/arch/arm/boot/dts/omap4-sdp.dts @@ -570,16 +570,22 @@ }; &uart2 { + interrupts-extended = <&gic GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH + &omap4_pmx_core OMAP4_UART2_RX>; pinctrl-names = "default"; pinctrl-0 = <&uart2_pins>; }; &uart3 { + interrupts-extended = <&gic GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH + &omap4_pmx_core OMAP4_UART3_RX>; pinctrl-names = "default"; pinctrl-0 = <&uart3_pins>; }; &uart4 { + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH + &omap4_pmx_core OMAP4_UART4_RX>; pinctrl-names = "default"; pinctrl-0 = <&uart4_pins>; }; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 649b5cd38b40..b22664544a09 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -311,7 +311,7 @@ uart2: serial@4806c000 { compatible = "ti,omap4-uart"; reg = <0x4806c000 0x100>; - interrupts = ; + interrupts-extended = <&gic GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "uart2"; clock-frequency = <48000000>; }; @@ -319,7 +319,7 @@ uart3: serial@48020000 { compatible = "ti,omap4-uart"; reg = <0x48020000 0x100>; - interrupts = ; + interrupts-extended = <&gic GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "uart3"; clock-frequency = <48000000>; }; @@ -327,7 +327,7 @@ uart4: serial@4806e000 { compatible = "ti,omap4-uart"; reg = <0x4806e000 0x100>; - interrupts = ; + interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "uart4"; clock-frequency = <48000000>; }; diff --git a/include/dt-bindings/pinctrl/omap.h b/include/dt-bindings/pinctrl/omap.h index b04528cd033c..404ba7ef7813 100644 --- a/include/dt-bindings/pinctrl/omap.h +++ b/include/dt-bindings/pinctrl/omap.h @@ -69,5 +69,17 @@ #define OMAP5_WKUP_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0xc840) (val) #define DRA7XX_CORE_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x3400) (val) +/* + * Define some commonly used pins configured by the boards. + * Note that some boards use alternative pins, so check + * the schematics before using these. + */ +#define OMAP3_UART1_RX 0x152 +#define OMAP3_UART2_RX 0x14a +#define OMAP3_UART3_RX 0x16e +#define OMAP4_UART2_RX 0xdc +#define OMAP4_UART3_RX 0x104 +#define OMAP4_UART4_RX 0x11c + #endif -- cgit v1.2.3 From e7c24607b5d68a4cdc56e09d70a3c8bae5f0519f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Apr 2014 20:54:51 -0400 Subject: kill iov_iter_copy_from_user() all callers can use copy_page_from_iter() and it actually simplifies them. Signed-off-by: Al Viro --- fs/ceph/file.c | 3 +-- fs/cifs/file.c | 7 +++---- include/linux/uio.h | 2 -- mm/iov_iter.c | 27 --------------------------- mm/process_vm_access.c | 6 +----- 5 files changed, 5 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 88a6df4cbe6d..ef9115e4a6fa 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -737,13 +737,12 @@ static ssize_t ceph_sync_write(struct kiocb *iocb, const struct iovec *iov, left = len; for (n = 0; n < num_pages; n++) { size_t plen = min_t(size_t, left, PAGE_SIZE); - ret = iov_iter_copy_from_user(pages[n], &i, 0, plen); + ret = copy_page_from_iter(pages[n], 0, plen, &i); if (ret != plen) { ret = -EFAULT; break; } left -= ret; - iov_iter_advance(&i, ret); } if (ret < 0) { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 5ed03e0b8b40..2900d150654e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2444,11 +2444,10 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, save_len = cur_len; for (i = 0; i < nr_pages; i++) { - bytes = min_t(const size_t, cur_len, PAGE_SIZE); - copied = iov_iter_copy_from_user(wdata->pages[i], &it, - 0, bytes); + bytes = min_t(size_t, cur_len, PAGE_SIZE); + copied = copy_page_from_iter(wdata->pages[i], 0, bytes, + &it); cur_len -= copied; - iov_iter_advance(&it, copied); /* * If we didn't copy as much as we expected, then that * may mean we trod into an unmapped area. Stop copying diff --git a/include/linux/uio.h b/include/linux/uio.h index 199bcc34241b..abbe83ded630 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -62,8 +62,6 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to); size_t iov_iter_copy_from_user_atomic(struct page *page, struct iov_iter *i, unsigned long offset, size_t bytes); -size_t iov_iter_copy_from_user(struct page *page, - struct iov_iter *i, unsigned long offset, size_t bytes); void iov_iter_advance(struct iov_iter *i, size_t bytes); int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); size_t iov_iter_single_seg_count(const struct iov_iter *i); diff --git a/mm/iov_iter.c b/mm/iov_iter.c index 10e46cd721de..22ec1ef068a8 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -129,33 +129,6 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, } EXPORT_SYMBOL(iov_iter_copy_from_user_atomic); -/* - * This has the same sideeffects and return value as - * iov_iter_copy_from_user_atomic(). - * The difference is that it attempts to resolve faults. - * Page must not be locked. - */ -size_t iov_iter_copy_from_user(struct page *page, - struct iov_iter *i, unsigned long offset, size_t bytes) -{ - char *kaddr; - size_t copied; - - kaddr = kmap(page); - if (likely(i->nr_segs == 1)) { - int left; - char __user *buf = i->iov->iov_base + i->iov_offset; - left = __copy_from_user(kaddr + offset, buf, bytes); - copied = bytes - left; - } else { - copied = __iovec_copy_from_user_inatomic(kaddr + offset, - i->iov, i->iov_offset, bytes); - } - kunmap(page); - return copied; -} -EXPORT_SYMBOL(iov_iter_copy_from_user); - void iov_iter_advance(struct iov_iter *i, size_t bytes) { BUG_ON(i->count < bytes); diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c index 8505c9262b35..f32b1fbbfe69 100644 --- a/mm/process_vm_access.c +++ b/mm/process_vm_access.c @@ -46,11 +46,7 @@ static int process_vm_rw_pages(struct page **pages, copy = len; if (vm_write) { - if (copy > iov_iter_count(iter)) - copy = iov_iter_count(iter); - copied = iov_iter_copy_from_user(page, iter, - offset, copy); - iov_iter_advance(iter, copied); + copied = copy_page_from_iter(page, offset, copy, iter); set_page_dirty_lock(page); } else { copied = copy_page_to_iter(page, offset, copy, iter); -- cgit v1.2.3 From f8579f8673b7ecdb7a81d5d5bb1d981093d9aa94 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 3 Mar 2014 22:03:20 -0500 Subject: generic_file_direct_write(): switch to iov_iter Signed-off-by: Al Viro --- fs/btrfs/file.c | 6 ++---- fs/fuse/file.c | 6 ++---- fs/ocfs2/file.c | 6 +++--- fs/xfs/xfs_file.c | 5 +++-- include/linux/fs.h | 4 ++-- mm/filemap.c | 15 +++++++-------- 6 files changed, 19 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index ae6af072b635..9fe20c2052af 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1669,15 +1669,13 @@ static ssize_t __btrfs_direct_write(struct kiocb *iocb, loff_t endbyte; int err; - written = generic_file_direct_write(iocb, iov, &nr_segs, pos, - count, ocount); + iov_iter_init(&i, iov, nr_segs, count, 0); + written = generic_file_direct_write(iocb, &i, pos, count, ocount); if (written < 0 || written == count) return written; pos += written; - count -= written; - iov_iter_init(&i, iov, nr_segs, count, written); written_buffered = __btrfs_buffered_write(file, &i, pos); if (written_buffered < 0) { err = written_buffered; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 96d513e01a5d..126deb5d0a9c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1235,15 +1235,13 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, goto out; if (file->f_flags & O_DIRECT) { - written = generic_file_direct_write(iocb, iov, &nr_segs, pos, - count, ocount); + iov_iter_init(&i, iov, nr_segs, count, 0); + written = generic_file_direct_write(iocb, &i, pos, count, ocount); if (written < 0 || written == count) goto out; pos += written; - count -= written; - iov_iter_init(&i, iov, nr_segs, count, written); written_buffered = fuse_perform_write(file, mapping, &i, pos); if (written_buffered < 0) { err = written_buffered; diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 8970dcf74de5..d6d78c2aa96e 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2251,6 +2251,7 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, int full_coherency = !(osb->s_mount_opt & OCFS2_MOUNT_COHERENCY_BUFFERED); int unaligned_dio = 0; + struct iov_iter from; trace_ocfs2_file_aio_write(inode, file, file->f_path.dentry, (unsigned long long)OCFS2_I(inode)->ip_blkno, @@ -2365,16 +2366,15 @@ relock: if (ret) goto out_dio; + iov_iter_init(&from, iov, nr_segs, count, 0); if (direct_io) { - written = generic_file_direct_write(iocb, iov, &nr_segs, *ppos, + written = generic_file_direct_write(iocb, &from, *ppos, count, ocount); if (written < 0) { ret = written; goto out_dio; } } else { - struct iov_iter from; - iov_iter_init(&from, iov, nr_segs, count, 0); current->backing_dev_info = file->f_mapping->backing_dev_info; written = generic_perform_write(file, &from, *ppos); if (likely(written >= 0)) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 951a2321ee01..8617497867c7 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -641,6 +641,7 @@ xfs_file_dio_aio_write( int iolock; struct xfs_buftarg *target = XFS_IS_REALTIME_INODE(ip) ? mp->m_rtdev_targp : mp->m_ddev_targp; + struct iov_iter from; /* DIO must be aligned to device logical sector size */ if ((pos | count) & target->bt_logical_sectormask) @@ -698,8 +699,8 @@ xfs_file_dio_aio_write( } trace_xfs_file_direct_write(ip, count, iocb->ki_pos, 0); - ret = generic_file_direct_write(iocb, iovp, - &nr_segs, pos, count, ocount); + iov_iter_init(&from, iovp, nr_segs, count, 0); + ret = generic_file_direct_write(iocb, &from, pos, count, ocount); out: xfs_rw_iunlock(ip, iolock); diff --git a/include/linux/fs.h b/include/linux/fs.h index 878031227c57..262f96e579b8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2407,8 +2407,8 @@ int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isbl extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long); extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t); -extern ssize_t generic_file_direct_write(struct kiocb *, const struct iovec *, - unsigned long *, loff_t, size_t, size_t); +extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, + loff_t, size_t, size_t); extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t); extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); diff --git a/mm/filemap.c b/mm/filemap.c index 000a220e2a41..a840890ed39f 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2385,9 +2385,8 @@ int pagecache_write_end(struct file *file, struct address_space *mapping, EXPORT_SYMBOL(pagecache_write_end); ssize_t -generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long *nr_segs, loff_t pos, - size_t count, size_t ocount) +generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from, + loff_t pos, size_t count, size_t ocount) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -2397,9 +2396,9 @@ generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov, pgoff_t end; if (count != ocount) - *nr_segs = iov_shorten((struct iovec *)iov, *nr_segs, count); + from->nr_segs = iov_shorten((struct iovec *)from->iov, from->nr_segs, count); - write_len = iov_length(iov, *nr_segs); + write_len = iov_length(from->iov, from->nr_segs); end = (pos + write_len - 1) >> PAGE_CACHE_SHIFT; written = filemap_write_and_wait_range(mapping, pos, pos + write_len - 1); @@ -2426,7 +2425,7 @@ generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov, } } - written = mapping->a_ops->direct_IO(WRITE, iocb, iov, pos, *nr_segs); + written = mapping->a_ops->direct_IO(WRITE, iocb, from->iov, pos, from->nr_segs); /* * Finally, try again to invalidate clean pages which might have been @@ -2443,6 +2442,7 @@ generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov, if (written > 0) { pos += written; + iov_iter_advance(from, written); if (pos > i_size_read(inode) && !S_ISBLK(inode->i_mode)) { i_size_write(inode, pos); mark_inode_dirty(inode); @@ -2645,11 +2645,10 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (unlikely(file->f_flags & O_DIRECT)) { loff_t endbyte; - written = generic_file_direct_write(iocb, iov, &from.nr_segs, pos, + written = generic_file_direct_write(iocb, &from, pos, count, ocount); if (written < 0 || written == count) goto out; - iov_iter_advance(&from, written); /* * direct-io write to a hole: fall through to buffered I/O -- cgit v1.2.3 From cb66a7a1f149ff705fa37cad6d1252b046e0ad4f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 4 Mar 2014 15:24:06 -0500 Subject: kill generic_segment_checks() all callers of ->aio_read() and ->aio_write() have iov/nr_segs already checked - generic_segment_checks() done after that is just an odd way to spell iov_length(). Signed-off-by: Al Viro --- drivers/staging/lustre/lustre/llite/file.c | 10 ++---- fs/btrfs/file.c | 7 +--- fs/ceph/file.c | 13 ++------ fs/fuse/file.c | 7 +--- fs/ntfs/file.c | 5 +-- fs/ocfs2/file.c | 7 +--- fs/xfs/xfs_file.c | 9 ++--- include/linux/fs.h | 2 -- mm/filemap.c | 53 ++---------------------------- mm/shmem.c | 7 ++-- 10 files changed, 16 insertions(+), 104 deletions(-) (limited to 'include') diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c index 8e844a6371e0..220bd8390a84 100644 --- a/drivers/staging/lustre/lustre/llite/file.c +++ b/drivers/staging/lustre/lustre/llite/file.c @@ -1180,9 +1180,7 @@ static ssize_t ll_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ssize_t result; int refcheck; - result = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE); - if (result) - return result; + count = iov_length(iov, nr_segs); env = cl_env_get(&refcheck); if (IS_ERR(env)) @@ -1235,14 +1233,10 @@ static ssize_t ll_file_aio_write(struct kiocb *iocb, const struct iovec *iov, { struct lu_env *env; struct vvp_io_args *args; - size_t count = 0; + size_t count = iov_length(iov, nr_segs); ssize_t result; int refcheck; - result = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ); - if (result) - return result; - env = cl_env_get(&refcheck); if (IS_ERR(env)) return PTR_ERR(env); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 1dafe0701daf..a0a94a30d85a 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1726,12 +1726,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, mutex_lock(&inode->i_mutex); - err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ); - if (err) { - mutex_unlock(&inode->i_mutex); - goto out; - } - count = ocount; + count = ocount = iov_length(iov, nr_segs); current->backing_dev_info = inode->i_mapping->backing_dev_info; err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index ef9115e4a6fa..21a56c27b74c 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -828,12 +828,8 @@ again: inode, ceph_vinop(inode), iocb->ki_pos, (unsigned)len, ceph_cap_string(got)); - if (!read) { - ret = generic_segment_checks(iov, &nr_segs, - &len, VERIFY_WRITE); - if (ret) - goto out; - } + if (!read) + len = iov_length(iov, nr_segs); iov_iter_init(&i, iov, nr_segs, len, read); @@ -855,7 +851,6 @@ again: ret = generic_file_aio_read(iocb, iov, nr_segs, pos); } -out: dout("aio_read %p %llx.%llx dropping cap refs on %s = %d\n", inode, ceph_vinop(inode), ceph_cap_string(got), (int)ret); ceph_put_cap_refs(ci, got); @@ -911,9 +906,7 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov, mutex_lock(&inode->i_mutex); - err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ); - if (err) - goto out; + count = iov_length(iov, nr_segs); /* We can write back this queue in page reclaim */ current->backing_dev_info = file->f_mapping->backing_dev_info; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 126deb5d0a9c..9c7f346879e7 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1208,12 +1208,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, WARN_ON(iocb->ki_pos != pos); - ocount = 0; - err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ); - if (err) - return err; - - count = ocount; + count = ocount = iov_length(iov, nr_segs); mutex_lock(&inode->i_mutex); /* We can write back this queue in page reclaim */ diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index db9bd8a31725..b6fa457d8d01 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -2091,10 +2091,7 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb, size_t count; /* after file limit checks */ ssize_t written, err; - count = 0; - err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ); - if (err) - return err; + count = iov_length(iov, nr_segs); pos = *ppos; /* We can write back this queue in page reclaim. */ current->backing_dev_info = mapping->backing_dev_info; diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index d6d78c2aa96e..d33c4ced0baf 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2355,12 +2355,7 @@ relock: /* communicate with ocfs2_dio_end_io */ ocfs2_iocb_set_rw_locked(iocb, rw_level); - ret = generic_segment_checks(iov, &nr_segs, &ocount, - VERIFY_READ); - if (ret) - goto out_dio; - - count = ocount; + count = ocount = iov_length(iov, nr_segs); ret = generic_write_checks(file, ppos, &count, S_ISBLK(inode->i_mode)); if (ret) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 8617497867c7..f0f8084a67be 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -253,9 +253,7 @@ xfs_file_aio_read( if (file->f_mode & FMODE_NOCMTIME) ioflags |= IO_INVIS; - ret = generic_segment_checks(iovp, &nr_segs, &size, VERIFY_WRITE); - if (ret < 0) - return ret; + size = iov_length(iovp, nr_segs); if (unlikely(ioflags & IO_ISDIRECT)) { xfs_buftarg_t *target = @@ -777,10 +775,7 @@ xfs_file_aio_write( BUG_ON(iocb->ki_pos != pos); - ret = generic_segment_checks(iovp, &nr_segs, &ocount, VERIFY_READ); - if (ret) - return ret; - + ocount = iov_length(iovp, nr_segs); if (ocount == 0) return 0; diff --git a/include/linux/fs.h b/include/linux/fs.h index 262f96e579b8..796de742fe4a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2412,8 +2412,6 @@ extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t); extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); -extern int generic_segment_checks(const struct iovec *iov, - unsigned long *nr_segs, size_t *count, int access_flags); /* fs/block_dev.c */ extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, diff --git a/mm/filemap.c b/mm/filemap.c index a840890ed39f..7c1417b0bd7b 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1663,45 +1663,6 @@ out: return written ? written : error; } -/* - * Performs necessary checks before doing a write - * @iov: io vector request - * @nr_segs: number of segments in the iovec - * @count: number of bytes to write - * @access_flags: type of access: %VERIFY_READ or %VERIFY_WRITE - * - * Adjust number of segments and amount of bytes to write (nr_segs should be - * properly initialized first). Returns appropriate error code that caller - * should return or zero in case that write should be allowed. - */ -int generic_segment_checks(const struct iovec *iov, - unsigned long *nr_segs, size_t *count, int access_flags) -{ - unsigned long seg; - size_t cnt = 0; - for (seg = 0; seg < *nr_segs; seg++) { - const struct iovec *iv = &iov[seg]; - - /* - * If any segment has a negative length, or the cumulative - * length ever wraps negative then return -EINVAL. - */ - cnt += iv->iov_len; - if (unlikely((ssize_t)(cnt|iv->iov_len) < 0)) - return -EINVAL; - if (access_ok(access_flags, iv->iov_base, iv->iov_len)) - continue; - if (seg == 0) - return -EFAULT; - *nr_segs = seg; - cnt -= iv->iov_len; /* This segment is no good */ - break; - } - *count = cnt; - return 0; -} -EXPORT_SYMBOL(generic_segment_checks); - /** * generic_file_aio_read - generic filesystem read routine * @iocb: kernel I/O control block @@ -1717,15 +1678,12 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct file *filp = iocb->ki_filp; - ssize_t retval; + ssize_t retval = 0; size_t count; loff_t *ppos = &iocb->ki_pos; struct iov_iter i; - count = 0; - retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE); - if (retval) - return retval; + count = iov_length(iov, nr_segs); iov_iter_init(&i, iov, nr_segs, count, 0); /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ @@ -2615,12 +2573,7 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, ssize_t status; struct iov_iter from; - ocount = 0; - err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ); - if (err) - return err; - - count = ocount; + count = ocount = iov_length(iov, nr_segs); /* We can write back this queue in page reclaim */ current->backing_dev_info = mapping->backing_dev_info; diff --git a/mm/shmem.c b/mm/shmem.c index 9f70e02111c6..2a93e625adaf 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1412,14 +1412,11 @@ static ssize_t shmem_file_aio_read(struct kiocb *iocb, unsigned long offset; enum sgp_type sgp = SGP_READ; int error = 0; - ssize_t retval; - size_t count; + ssize_t retval = 0; + size_t count = iov_length(iov, nr_segs); loff_t *ppos = &iocb->ki_pos; struct iov_iter iter; - retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE); - if (retval) - return retval; iov_iter_init(&iter, iov, nr_segs, count, 0); /* -- cgit v1.2.3 From d8d3d94b80aa1a1c0ca75c58b8abdc7356f38418 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 4 Mar 2014 21:27:34 -0500 Subject: pass iov_iter to ->direct_IO() unmodified, for now Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 3 +-- Documentation/filesystems/vfs.txt | 3 +-- drivers/staging/lustre/lustre/llite/rw26.c | 17 ++++++++--------- fs/9p/vfs_addr.c | 5 ++--- fs/block_dev.c | 9 +++++---- fs/btrfs/inode.c | 12 ++++++------ fs/ceph/addr.c | 4 ++-- fs/cifs/file.c | 4 ++-- fs/exofs/inode.c | 2 +- fs/ext2/inode.c | 11 ++++++----- fs/ext3/inode.c | 16 +++++++--------- fs/ext4/inode.c | 11 +++++------ fs/f2fs/data.c | 8 ++++---- fs/fat/inode.c | 13 +++++++------ fs/fuse/file.c | 10 +++++----- fs/gfs2/aops.c | 11 +++++------ fs/hfs/inode.c | 8 ++++---- fs/hfsplus/inode.c | 6 +++--- fs/jfs/inode.c | 8 ++++---- fs/nfs/direct.c | 8 ++++---- fs/nilfs2/inode.c | 10 +++++----- fs/ocfs2/aops.c | 7 +++---- fs/reiserfs/inode.c | 9 ++++----- fs/udf/file.c | 4 ++-- fs/udf/inode.c | 8 ++++---- fs/xfs/xfs_aops.c | 15 +++++++-------- include/linux/fs.h | 3 +-- include/linux/nfs_fs.h | 3 +-- mm/filemap.c | 9 ++++----- mm/page_io.c | 6 ++++-- 30 files changed, 117 insertions(+), 126 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index eba790134253..9b0d5a33c8bf 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -196,8 +196,7 @@ prototypes: void (*invalidatepage) (struct page *, unsigned int, unsigned int); int (*releasepage) (struct page *, int); void (*freepage)(struct page *); - int (*direct_IO)(int, struct kiocb *, const struct iovec *iov, - loff_t offset, unsigned long nr_segs); + int (*direct_IO)(int, struct kiocb *, struct iov_iter *iter, loff_t offset); int (*get_xip_mem)(struct address_space *, pgoff_t, int, void **, unsigned long *); int (*migratepage)(struct address_space *, struct page *, struct page *); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 617f6d70c077..1846374a5add 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -589,8 +589,7 @@ struct address_space_operations { void (*invalidatepage) (struct page *, unsigned int, unsigned int); int (*releasepage) (struct page *, int); void (*freepage)(struct page *); - ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, - loff_t offset, unsigned long nr_segs); + ssize_t (*direct_IO)(int, struct kiocb *, struct iov_iter *iter, loff_t offset); struct page* (*get_xip_page)(struct address_space *, sector_t, int); /* migrate the contents of a page to the specified target */ diff --git a/drivers/staging/lustre/lustre/llite/rw26.c b/drivers/staging/lustre/lustre/llite/rw26.c index 7e3e0967993b..66e05c6f4d27 100644 --- a/drivers/staging/lustre/lustre/llite/rw26.c +++ b/drivers/staging/lustre/lustre/llite/rw26.c @@ -363,15 +363,14 @@ static ssize_t ll_direct_IO_26_seg(const struct lu_env *env, struct cl_io *io, #define MAX_DIO_SIZE ((MAX_MALLOC / sizeof(struct brw_page) * PAGE_CACHE_SIZE) & \ ~(DT_MAX_BRW_SIZE - 1)) static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t file_offset, - unsigned long nr_segs) + struct iov_iter *iter, loff_t file_offset) { struct lu_env *env; struct cl_io *io; struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; struct ccc_object *obj = cl_inode2ccc(inode); - long count = iov_length(iov, nr_segs); + long count = iov_length(iter->iov, iter->nr_segs); long tot_bytes = 0, result = 0; struct ll_inode_info *lli = ll_i2info(inode); unsigned long seg = 0; @@ -392,9 +391,9 @@ static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb, MAX_DIO_SIZE >> PAGE_CACHE_SHIFT); /* Check that all user buffers are aligned as well */ - for (seg = 0; seg < nr_segs; seg++) { - if (((unsigned long)iov[seg].iov_base & ~CFS_PAGE_MASK) || - (iov[seg].iov_len & ~CFS_PAGE_MASK)) + for (seg = 0; seg < iter->nr_segs; seg++) { + if (((unsigned long)iter->iov[seg].iov_base & ~CFS_PAGE_MASK) || + (iter->iov[seg].iov_len & ~CFS_PAGE_MASK)) return -EINVAL; } @@ -411,9 +410,9 @@ static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb, mutex_lock(&inode->i_mutex); LASSERT(obj->cob_transient_pages == 0); - for (seg = 0; seg < nr_segs; seg++) { - long iov_left = iov[seg].iov_len; - unsigned long user_addr = (unsigned long)iov[seg].iov_base; + for (seg = 0; seg < iter->nr_segs; seg++) { + long iov_left = iter->iov[seg].iov_len; + unsigned long user_addr = (unsigned long)iter->iov[seg].iov_base; if (rw == READ) { if (file_offset >= i_size_read(inode)) diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index c71e88602ff4..cc1cfae726b3 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -259,8 +259,7 @@ static int v9fs_launder_page(struct page *page) * */ static ssize_t -v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, - loff_t pos, unsigned long nr_segs) +v9fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t pos) { /* * FIXME @@ -269,7 +268,7 @@ v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, */ p9_debug(P9_DEBUG_VFS, "v9fs_direct_IO: v9fs_direct_IO (%s) off/no(%lld/%lu) EINVAL\n", iocb->ki_filp->f_path.dentry->d_name.name, - (long long)pos, nr_segs); + (long long)pos, iter->nr_segs); return -EINVAL; } diff --git a/fs/block_dev.c b/fs/block_dev.c index 552a8d13bc32..938fc707d769 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -165,14 +165,15 @@ blkdev_get_block(struct inode *inode, sector_t iblock, } static ssize_t -blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, - loff_t offset, unsigned long nr_segs) +blkdev_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, + loff_t offset) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; - return __blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iov, offset, - nr_segs, blkdev_get_block, NULL, NULL, 0); + return __blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iter->iov, + offset, iter->nr_segs, blkdev_get_block, + NULL, NULL, 0); } int __sync_blockdev(struct block_device *bdev, int wait) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5f805bc944fa..30a6cc51f32c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7433,8 +7433,7 @@ out: } static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, - unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; @@ -7444,8 +7443,8 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, bool relock = false; ssize_t ret; - if (check_direct_IO(BTRFS_I(inode)->root, rw, iocb, iov, - offset, nr_segs)) + if (check_direct_IO(BTRFS_I(inode)->root, rw, iocb, iter->iov, + offset, iter->nr_segs)) return 0; atomic_inc(&inode->i_dio_count); @@ -7457,7 +7456,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, * we need to flush the dirty pages again to make absolutely sure * that any outstanding dirty pages are on disk. */ - count = iov_length(iov, nr_segs); + count = iov_length(iter->iov, iter->nr_segs); if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT, &BTRFS_I(inode)->runtime_flags)) filemap_fdatawrite_range(inode->i_mapping, offset, count); @@ -7484,7 +7483,8 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, ret = __blockdev_direct_IO(rw, iocb, inode, BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev, - iov, offset, nr_segs, btrfs_get_blocks_direct, NULL, + iter->iov, offset, iter->nr_segs, + btrfs_get_blocks_direct, NULL, btrfs_submit_direct, flags); if (rw & WRITE) { if (ret < 0 && ret != -EIOCBQUEUED) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index b53278c9fd97..342ca5e423f9 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -1187,8 +1187,8 @@ static int ceph_write_end(struct file *file, struct address_space *mapping, * never get called. */ static ssize_t ceph_direct_io(int rw, struct kiocb *iocb, - const struct iovec *iov, - loff_t pos, unsigned long nr_segs) + struct iov_iter *iter, + loff_t pos) { WARN_ON(1); return -EINVAL; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 2900d150654e..a4ccc39e6c11 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -3702,8 +3702,8 @@ void cifs_oplock_break(struct work_struct *work) * Direct IO is not yet supported in the cached mode. */ static ssize_t -cifs_direct_io(int rw, struct kiocb *iocb, const struct iovec *iov, - loff_t pos, unsigned long nr_segs) +cifs_direct_io(int rw, struct kiocb *iocb, struct iov_iter *iter, + loff_t pos) { /* * FIXME diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index d1c244d67667..3f9cafd73931 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -964,7 +964,7 @@ static void exofs_invalidatepage(struct page *page, unsigned int offset, /* TODO: Should be easy enough to do proprly */ static ssize_t exofs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { return 0; } diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index b1d2a4675d42..47fbe760a7f8 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -850,18 +850,19 @@ static sector_t ext2_bmap(struct address_space *mapping, sector_t block) } static ssize_t -ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, - loff_t offset, unsigned long nr_segs) +ext2_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, + loff_t offset) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - ext2_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, + iter->nr_segs, ext2_get_block); if (ret < 0 && (rw & WRITE)) - ext2_write_failed(mapping, offset + iov_length(iov, nr_segs)); + ext2_write_failed(mapping, offset + + iov_length(iter->iov, iter->nr_segs)); return ret; } diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index f5157d0d1b43..7a5c501dc31b 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1820,8 +1820,7 @@ static int ext3_releasepage(struct page *page, gfp_t wait) * VFS code falls back into buffered path in that case so we are safe. */ static ssize_t ext3_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, - unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; @@ -1829,10 +1828,10 @@ static ssize_t ext3_direct_IO(int rw, struct kiocb *iocb, handle_t *handle; ssize_t ret; int orphan = 0; - size_t count = iov_length(iov, nr_segs); + size_t count = iov_length(iter->iov, iter->nr_segs); int retries = 0; - trace_ext3_direct_IO_enter(inode, offset, iov_length(iov, nr_segs), rw); + trace_ext3_direct_IO_enter(inode, offset, count, rw); if (rw == WRITE) { loff_t final_size = offset + count; @@ -1856,15 +1855,15 @@ static ssize_t ext3_direct_IO(int rw, struct kiocb *iocb, } retry: - ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - ext3_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, + iter->nr_segs, ext3_get_block); /* * In case of error extending write may have instantiated a few * blocks outside i_size. Trim these off again. */ if (unlikely((rw & WRITE) && ret < 0)) { loff_t isize = i_size_read(inode); - loff_t end = offset + iov_length(iov, nr_segs); + loff_t end = offset + count; if (end > isize) ext3_truncate_failed_direct_write(inode); @@ -1909,8 +1908,7 @@ retry: ret = err; } out: - trace_ext3_direct_IO_exit(inode, offset, - iov_length(iov, nr_segs), rw, ret); + trace_ext3_direct_IO_exit(inode, offset, count, rw, ret); return ret; } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d7b7462a0e13..f51db730da39 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3222,8 +3222,7 @@ retake_lock: } static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, - unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; @@ -3239,13 +3238,13 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb, if (ext4_has_inline_data(inode)) return 0; - trace_ext4_direct_IO_enter(inode, offset, iov_length(iov, nr_segs), rw); + trace_ext4_direct_IO_enter(inode, offset, iov_length(iter->iov, iter->nr_segs), rw); if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) - ret = ext4_ext_direct_IO(rw, iocb, iov, offset, nr_segs); + ret = ext4_ext_direct_IO(rw, iocb, iter->iov, offset, iter->nr_segs); else - ret = ext4_ind_direct_IO(rw, iocb, iov, offset, nr_segs); + ret = ext4_ind_direct_IO(rw, iocb, iter->iov, offset, iter->nr_segs); trace_ext4_direct_IO_exit(inode, offset, - iov_length(iov, nr_segs), rw, ret); + iov_length(iter->iov, iter->nr_segs), rw, ret); return ret; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 45abd60e2bff..3a6ef121c095 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1010,7 +1010,7 @@ static int check_direct_IO(struct inode *inode, int rw, } static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; @@ -1019,11 +1019,11 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, if (f2fs_has_inline_data(inode)) return 0; - if (check_direct_IO(inode, rw, iov, offset, nr_segs)) + if (check_direct_IO(inode, rw, iter->iov, offset, iter->nr_segs)) return 0; - return blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - get_data_block); + return blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, + iter->nr_segs, get_data_block); } static void f2fs_invalidate_data_page(struct page *page, unsigned int offset, diff --git a/fs/fat/inode.c b/fs/fat/inode.c index b3361fe2bcb5..d5237a199055 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -185,8 +185,8 @@ static int fat_write_end(struct file *file, struct address_space *mapping, } static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, - loff_t offset, unsigned long nr_segs) + struct iov_iter *iter, + loff_t offset) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -203,7 +203,7 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, * * Return 0, and fallback to normal buffered write. */ - loff_t size = offset + iov_length(iov, nr_segs); + loff_t size = offset + iov_length(iter->iov, iter->nr_segs); if (MSDOS_I(inode)->mmu_private < size) return 0; } @@ -212,10 +212,11 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, * FAT need to use the DIO_LOCKING for avoiding the race * condition of fat_get_block() and ->truncate(). */ - ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - fat_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, + iter->nr_segs, fat_get_block); if (ret < 0 && (rw & WRITE)) - fat_write_failed(mapping, offset + iov_length(iov, nr_segs)); + fat_write_failed(mapping, offset + + iov_length(iter->iov, iter->nr_segs)); return ret; } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 9c7f346879e7..17d96f36df15 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2890,8 +2890,8 @@ static inline loff_t fuse_round_up(loff_t off) } static ssize_t -fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, - loff_t offset, unsigned long nr_segs) +fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, + loff_t offset) { ssize_t ret = 0; struct file *file = iocb->ki_filp; @@ -2900,7 +2900,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos = 0; struct inode *inode; loff_t i_size; - size_t count = iov_length(iov, nr_segs); + size_t count = iov_length(iter->iov, iter->nr_segs); struct fuse_io_priv *io; pos = offset; @@ -2944,9 +2944,9 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, io->async = false; if (rw == WRITE) - ret = __fuse_direct_write(io, iov, nr_segs, &pos); + ret = __fuse_direct_write(io, iter->iov, iter->nr_segs, &pos); else - ret = __fuse_direct_read(io, iov, nr_segs, &pos, count); + ret = __fuse_direct_read(io, iter->iov, iter->nr_segs, &pos, count); if (io->async) { fuse_aio_complete(io, ret < 0 ? ret : 0, -1); diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index ce62dcac90b6..e84ddaa42104 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -1041,8 +1041,7 @@ static int gfs2_ok_for_dio(struct gfs2_inode *ip, int rw, loff_t offset) static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, - unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; @@ -1082,7 +1081,7 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, */ if (mapping->nrpages) { loff_t lstart = offset & (PAGE_CACHE_SIZE - 1); - loff_t len = iov_length(iov, nr_segs); + loff_t len = iov_length(iter->iov, iter->nr_segs); loff_t end = PAGE_ALIGN(offset + len) - 1; rv = 0; @@ -1097,9 +1096,9 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, truncate_inode_pages_range(mapping, lstart, end); } - rv = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, - offset, nr_segs, gfs2_get_block_direct, - NULL, NULL, 0); + rv = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, + iter->iov, offset, iter->nr_segs, + gfs2_get_block_direct, NULL, NULL, 0); out: gfs2_glock_dq(&gh); gfs2_holder_uninit(&gh); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 9e2fecd62f62..09cff13528c5 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -125,15 +125,15 @@ static int hfs_releasepage(struct page *page, gfp_t mask) } static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = file_inode(file)->i_mapping->host; ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - hfs_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, + iter->nr_segs, hfs_get_block); /* * In case of error extending write may have instantiated a few @@ -141,7 +141,7 @@ static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb, */ if (unlikely((rw & WRITE) && ret < 0)) { loff_t isize = i_size_read(inode); - loff_t end = offset + iov_length(iov, nr_segs); + loff_t end = offset + iov_length(iter->iov, iter->nr_segs); if (end > isize) hfs_write_failed(mapping, end); diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index a4f45bd88a63..7f894a5b5eaf 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -123,14 +123,14 @@ static int hfsplus_releasepage(struct page *page, gfp_t mask) } static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = file_inode(file)->i_mapping->host; ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, + ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, iter->nr_segs, hfsplus_get_block); /* @@ -139,7 +139,7 @@ static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb, */ if (unlikely((rw & WRITE) && ret < 0)) { loff_t isize = i_size_read(inode); - loff_t end = offset + iov_length(iov, nr_segs); + loff_t end = offset + iov_length(iter->iov, iter->nr_segs); if (end > isize) hfsplus_write_failed(mapping, end); diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 6f8fe72c2a7a..7052744d5107 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -331,15 +331,15 @@ static sector_t jfs_bmap(struct address_space *mapping, sector_t block) } static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = file->f_mapping->host; ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - jfs_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, + iter->nr_segs, jfs_get_block); /* * In case of error extending write may have instantiated a few @@ -347,7 +347,7 @@ static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb, */ if (unlikely((rw & WRITE) && ret < 0)) { loff_t isize = i_size_read(inode); - loff_t end = offset + iov_length(iov, nr_segs); + loff_t end = offset + iov_length(iter->iov, iter->nr_segs); if (end > isize) jfs_write_failed(mapping, end); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index b8797ae6831f..e9cde3935001 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -121,20 +121,20 @@ static inline int put_dreq(struct nfs_direct_req *dreq) * shunt off direct read and write requests before the VFS gets them, * so this method is only ever called for swap. */ -ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs) +ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t pos) { #ifndef CONFIG_NFS_SWAP dprintk("NFS: nfs_direct_IO (%pD) off/no(%Ld/%lu) EINVAL\n", - iocb->ki_filp, (long long) pos, nr_segs); + iocb->ki_filp, (long long) pos, iter->nr_segs); return -EINVAL; #else VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE); if (rw == READ || rw == KERNEL_READ) - return nfs_file_direct_read(iocb, iov, nr_segs, pos, + return nfs_file_direct_read(iocb, iter->iov, iter->nr_segs, pos, rw == READ ? true : false); - return nfs_file_direct_write(iocb, iov, nr_segs, pos, + return nfs_file_direct_write(iocb, iter->iov, iter->nr_segs, pos, rw == WRITE ? true : false); #endif /* CONFIG_NFS_SWAP */ } diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index b9c5726120e3..1c0e8fedc095 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -298,8 +298,8 @@ static int nilfs_write_end(struct file *file, struct address_space *mapping, } static ssize_t -nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, - loff_t offset, unsigned long nr_segs) +nilfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, + loff_t offset) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -310,8 +310,8 @@ nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, return 0; /* Needs synchronization with the cleaner */ - size = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - nilfs_get_block); + size = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, + iter->nr_segs, nilfs_get_block); /* * In case of error extending write may have instantiated a few @@ -319,7 +319,7 @@ nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, */ if (unlikely((rw & WRITE) && size < 0)) { loff_t isize = i_size_read(inode); - loff_t end = offset + iov_length(iov, nr_segs); + loff_t end = offset + iov_length(iter->iov, iter->nr_segs); if (end > isize) nilfs_write_failed(mapping, end); diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index d310d12a9adc..799fd0afcb35 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -599,9 +599,8 @@ static int ocfs2_releasepage(struct page *page, gfp_t wait) static ssize_t ocfs2_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, - loff_t offset, - unsigned long nr_segs) + struct iov_iter *iter, + loff_t offset) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file)->i_mapping->host; @@ -618,7 +617,7 @@ static ssize_t ocfs2_direct_IO(int rw, return 0; return __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, - iov, offset, nr_segs, + iter->iov, offset, iter->nr_segs, ocfs2_direct_IO_get_blocks, ocfs2_dio_end_io, NULL, 0); } diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index bc8b8009897d..17bf4c41a509 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -3083,15 +3083,14 @@ static int reiserfs_releasepage(struct page *page, gfp_t unused_gfp_flags) /* We thank Mingming Cao for helping us understand in great detail what to do in this section of the code. */ static ssize_t reiserfs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, - unsigned long nr_segs) + struct iov_iter *iter, loff_t offset) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - reiserfs_get_blocks_direct_io); + ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, + iter->nr_segs, reiserfs_get_blocks_direct_io); /* * In case of error extending write may have instantiated a few @@ -3099,7 +3098,7 @@ static ssize_t reiserfs_direct_IO(int rw, struct kiocb *iocb, */ if (unlikely((rw & WRITE) && ret < 0)) { loff_t isize = i_size_read(inode); - loff_t end = offset + iov_length(iov, nr_segs); + loff_t end = offset + iov_length(iter->iov, iter->nr_segs); if ((end > isize) && inode_newsize_ok(inode, isize) == 0) { truncate_setsize(inode, isize); diff --git a/fs/udf/file.c b/fs/udf/file.c index d2c170f8b035..ade886401658 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -119,8 +119,8 @@ static int udf_adinicb_write_end(struct file *file, } static ssize_t udf_adinicb_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, - loff_t offset, unsigned long nr_segs) + struct iov_iter *iter, + loff_t offset) { /* Fallback to buffered I/O. */ return 0; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 5d643706212f..5b184c7f7dcb 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -217,18 +217,18 @@ static int udf_write_begin(struct file *file, struct address_space *mapping, } static ssize_t udf_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, - loff_t offset, unsigned long nr_segs) + struct iov_iter *iter, + loff_t offset) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, + ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, iter->nr_segs, udf_get_block); if (unlikely(ret < 0 && (rw & WRITE))) - udf_write_failed(mapping, offset + iov_length(iov, nr_segs)); + udf_write_failed(mapping, offset + iov_length(iter->iov, iter->nr_segs)); return ret; } diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 0479c32c5eb1..330d7b1c4be3 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -1449,9 +1449,8 @@ STATIC ssize_t xfs_vm_direct_IO( int rw, struct kiocb *iocb, - const struct iovec *iov, - loff_t offset, - unsigned long nr_segs) + struct iov_iter *iter, + loff_t offset) { struct inode *inode = iocb->ki_filp->f_mapping->host; struct block_device *bdev = xfs_find_bdev_for_inode(inode); @@ -1459,7 +1458,7 @@ xfs_vm_direct_IO( ssize_t ret; if (rw & WRITE) { - size_t size = iov_length(iov, nr_segs); + size_t size = iov_length(iter->iov, iter->nr_segs); /* * We cannot preallocate a size update transaction here as we @@ -1471,16 +1470,16 @@ xfs_vm_direct_IO( if (offset + size > XFS_I(inode)->i_d.di_size) ioend->io_isdirect = 1; - ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov, - offset, nr_segs, + ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter->iov, + offset, iter->nr_segs, xfs_get_blocks_direct, xfs_end_io_direct_write, NULL, DIO_ASYNC_EXTEND); if (ret != -EIOCBQUEUED && iocb->private) goto out_destroy_ioend; } else { - ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov, - offset, nr_segs, + ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter->iov, + offset, iter->nr_segs, xfs_get_blocks_direct, NULL, NULL, 0); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 796de742fe4a..399a338c92b5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -343,8 +343,7 @@ struct address_space_operations { void (*invalidatepage) (struct page *, unsigned int, unsigned int); int (*releasepage) (struct page *, gfp_t); void (*freepage)(struct page *); - ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, - loff_t offset, unsigned long nr_segs); + ssize_t (*direct_IO)(int, struct kiocb *, struct iov_iter *iter, loff_t offset); int (*get_xip_mem)(struct address_space *, pgoff_t, int, void **, unsigned long *); /* diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index fa6918b0f829..5a0d78ec739d 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -459,8 +459,7 @@ extern int nfs3_removexattr (struct dentry *, const char *name); /* * linux/fs/nfs/direct.c */ -extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t, - unsigned long); +extern ssize_t nfs_direct_IO(int, struct kiocb *, struct iov_iter *, loff_t); extern ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos, bool uio); diff --git a/mm/filemap.c b/mm/filemap.c index 7c1417b0bd7b..139641274f1e 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1699,10 +1699,9 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, size = i_size_read(inode); retval = filemap_write_and_wait_range(mapping, pos, pos + iov_length(iov, nr_segs) - 1); - if (!retval) { - retval = mapping->a_ops->direct_IO(READ, iocb, - iov, pos, nr_segs); - } + if (!retval) + retval = mapping->a_ops->direct_IO(READ, iocb, &i, pos); + if (retval > 0) { *ppos = pos + retval; count -= retval; @@ -2383,7 +2382,7 @@ generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from, } } - written = mapping->a_ops->direct_IO(WRITE, iocb, from->iov, pos, from->nr_segs); + written = mapping->a_ops->direct_IO(WRITE, iocb, from, pos); /* * Finally, try again to invalidate clean pages which might have been diff --git a/mm/page_io.c b/mm/page_io.c index 7c59ef681381..0ed0644c73db 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -263,16 +263,18 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc, .iov_base = kmap(page), .iov_len = PAGE_SIZE, }; + struct iov_iter from; init_sync_kiocb(&kiocb, swap_file); kiocb.ki_pos = page_file_offset(page); kiocb.ki_nbytes = PAGE_SIZE; + iov_iter_init(&from, &iov, 1, PAGE_SIZE, 0); set_page_writeback(page); unlock_page(page); ret = mapping->a_ops->direct_IO(KERNEL_WRITE, - &kiocb, &iov, - kiocb.ki_pos, 1); + &kiocb, &from, + kiocb.ki_pos); kunmap(page); if (ret == PAGE_SIZE) { count_vm_event(PSWPOUT); -- cgit v1.2.3 From 619d30b4b8c488042b4a720ca79dccc346d1a516 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 4 Mar 2014 21:53:33 -0500 Subject: convert the guts of nfs_direct_IO() to iov_iter Signed-off-by: Al Viro --- fs/nfs/direct.c | 46 +++++++++++++++++++++------------------------- fs/nfs/file.c | 18 ++++++++++++------ include/linux/nfs_fs.h | 4 ++-- 3 files changed, 35 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index e9cde3935001..21723149668b 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -132,9 +132,9 @@ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE); if (rw == READ || rw == KERNEL_READ) - return nfs_file_direct_read(iocb, iter->iov, iter->nr_segs, pos, + return nfs_file_direct_read(iocb, iter, pos, rw == READ ? true : false); - return nfs_file_direct_write(iocb, iter->iov, iter->nr_segs, pos, + return nfs_file_direct_write(iocb, iter, pos, rw == WRITE ? true : false); #endif /* CONFIG_NFS_SWAP */ } @@ -414,8 +414,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de } static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, - const struct iovec *iov, - unsigned long nr_segs, + struct iov_iter *iter, loff_t pos, bool uio) { struct nfs_pageio_descriptor desc; @@ -430,8 +429,8 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, desc.pg_dreq = dreq; atomic_inc(&inode->i_dio_count); - for (seg = 0; seg < nr_segs; seg++) { - const struct iovec *vec = &iov[seg]; + for (seg = 0; seg < iter->nr_segs; seg++) { + const struct iovec *vec = &iter->iov[seg]; result = nfs_direct_read_schedule_segment(&desc, vec, pos, uio); if (result < 0) break; @@ -461,8 +460,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, /** * nfs_file_direct_read - file direct read operation for NFS files * @iocb: target I/O control block - * @iov: vector of user buffers into which to read data - * @nr_segs: size of iov vector + * @iter: vector of user buffers into which to read data * @pos: byte offset in file where reading starts * * We use this function for direct reads instead of calling @@ -479,8 +477,8 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, * client must read the updated atime from the server back into its * cache. */ -ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos, bool uio) +ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter, + loff_t pos, bool uio) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -490,7 +488,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, ssize_t result = -EINVAL; size_t count; - count = iov_length(iov, nr_segs); + count = iov_length(iter->iov, iter->nr_segs); nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count); dfprintk(FILE, "NFS: direct read(%pD2, %zd@%Ld)\n", @@ -513,7 +511,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, goto out_unlock; dreq->inode = inode; - dreq->bytes_left = iov_length(iov, nr_segs); + dreq->bytes_left = count; dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); l_ctx = nfs_get_lock_context(dreq->ctx); if (IS_ERR(l_ctx)) { @@ -524,8 +522,8 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - NFS_I(inode)->read_io += iov_length(iov, nr_segs); - result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos, uio); + NFS_I(inode)->read_io += count; + result = nfs_direct_read_schedule_iovec(dreq, iter, pos, uio); mutex_unlock(&inode->i_mutex); @@ -864,8 +862,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = { }; static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, - const struct iovec *iov, - unsigned long nr_segs, + struct iov_iter *iter, loff_t pos, bool uio) { struct nfs_pageio_descriptor desc; @@ -880,9 +877,9 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, get_dreq(dreq); atomic_inc(&inode->i_dio_count); - NFS_I(dreq->inode)->write_io += iov_length(iov, nr_segs); - for (seg = 0; seg < nr_segs; seg++) { - const struct iovec *vec = &iov[seg]; + NFS_I(dreq->inode)->write_io += iov_length(iter->iov, iter->nr_segs); + for (seg = 0; seg < iter->nr_segs; seg++) { + const struct iovec *vec = &iter->iov[seg]; result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio); if (result < 0) break; @@ -911,8 +908,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, /** * nfs_file_direct_write - file direct write operation for NFS files * @iocb: target I/O control block - * @iov: vector of user buffers from which to write data - * @nr_segs: size of iov vector + * @iter: vector of user buffers from which to write data * @pos: byte offset in file where writing starts * * We use this function for direct writes instead of calling @@ -930,8 +926,8 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, * Note that O_APPEND is not supported for NFS direct writes, as there * is no atomic O_APPEND write facility in the NFS protocol. */ -ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos, bool uio) +ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter, + loff_t pos, bool uio) { ssize_t result = -EINVAL; struct file *file = iocb->ki_filp; @@ -942,7 +938,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, loff_t end; size_t count; - count = iov_length(iov, nr_segs); + count = iov_length(iter->iov, iter->nr_segs); end = (pos + count - 1) >> PAGE_CACHE_SHIFT; nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count); @@ -993,7 +989,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, uio); + result = nfs_direct_write_schedule_iovec(dreq, iter, pos, uio); if (mapping->nrpages) { invalidate_inode_pages2_range(mapping, diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 284ca901fe16..3d01b152894e 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -169,14 +169,18 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct inode *inode = file_inode(iocb->ki_filp); + size_t count = iov_length(iov, nr_segs); ssize_t result; + struct iov_iter to; + + iov_iter_init(&to, iov, nr_segs, count, 0); if (iocb->ki_filp->f_flags & O_DIRECT) - return nfs_file_direct_read(iocb, iov, nr_segs, pos, true); + return nfs_file_direct_read(iocb, &to, pos, true); - dprintk("NFS: read(%pD2, %lu@%lu)\n", + dprintk("NFS: read(%pD2, %zu@%lu)\n", iocb->ki_filp, - (unsigned long) iov_length(iov, nr_segs), (unsigned long) pos); + count, (unsigned long) pos); result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping); if (!result) { @@ -643,16 +647,18 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, unsigned long written = 0; ssize_t result; size_t count = iov_length(iov, nr_segs); + struct iov_iter from; + iov_iter_init(&from, iov, nr_segs, count, 0); result = nfs_key_timeout_notify(file, inode); if (result) return result; if (file->f_flags & O_DIRECT) - return nfs_file_direct_write(iocb, iov, nr_segs, pos, true); + return nfs_file_direct_write(iocb, &from, pos, true); - dprintk("NFS: write(%pD2, %lu@%Ld)\n", - file, (unsigned long) count, (long long) pos); + dprintk("NFS: write(%pD2, %zu@%Ld)\n", + file, count, (long long) pos); result = -EBUSY; if (IS_SWAPFILE(inode)) diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 5a0d78ec739d..0a82b6fbae8a 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -461,10 +461,10 @@ extern int nfs3_removexattr (struct dentry *, const char *name); */ extern ssize_t nfs_direct_IO(int, struct kiocb *, struct iov_iter *, loff_t); extern ssize_t nfs_file_direct_read(struct kiocb *iocb, - const struct iovec *iov, unsigned long nr_segs, + struct iov_iter *iter, loff_t pos, bool uio); extern ssize_t nfs_file_direct_write(struct kiocb *iocb, - const struct iovec *iov, unsigned long nr_segs, + struct iov_iter *iter, loff_t pos, bool uio); /* -- cgit v1.2.3 From 31b140398ce56ab41646eda7f02bcb78d6a4c916 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 5 Mar 2014 01:33:16 -0500 Subject: switch {__,}blockdev_direct_IO() to iov_iter Signed-off-by: Al Viro --- fs/block_dev.c | 4 ++-- fs/btrfs/inode.c | 3 +-- fs/direct-io.c | 33 ++++++++++++++++----------------- fs/ext2/inode.c | 3 +-- fs/ext3/inode.c | 3 +-- fs/ext4/indirect.c | 7 +++---- fs/ext4/inode.c | 4 ++-- fs/f2fs/data.c | 4 ++-- fs/fat/inode.c | 3 +-- fs/gfs2/aops.c | 2 +- fs/hfs/inode.c | 3 +-- fs/hfsplus/inode.c | 2 +- fs/jfs/inode.c | 3 +-- fs/nilfs2/inode.c | 4 ++-- fs/ocfs2/aops.c | 2 +- fs/reiserfs/inode.c | 4 ++-- fs/udf/inode.c | 3 +-- fs/xfs/xfs_aops.c | 10 ++++------ include/linux/fs.h | 12 ++++++------ 19 files changed, 49 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/fs/block_dev.c b/fs/block_dev.c index 938fc707d769..937e3011ed58 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -171,8 +171,8 @@ blkdev_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; - return __blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iter->iov, - offset, iter->nr_segs, blkdev_get_block, + return __blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iter, + offset, blkdev_get_block, NULL, NULL, 0); } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c46a025d0c4b..b0b8fa0efba3 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7483,8 +7483,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, ret = __blockdev_direct_IO(rw, iocb, inode, BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev, - iter->iov, offset, iter->nr_segs, - btrfs_get_blocks_direct, NULL, + iter, offset, btrfs_get_blocks_direct, NULL, btrfs_submit_direct, flags); if (rw & WRITE) { if (ret < 0 && ret != -EIOCBQUEUED) diff --git a/fs/direct-io.c b/fs/direct-io.c index 31ba0935e32e..1c677899b989 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -1107,8 +1107,8 @@ static inline int drop_refcount(struct dio *dio) */ static inline ssize_t do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, - struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, + struct block_device *bdev, struct iov_iter *iter, loff_t offset, + get_block_t get_block, dio_iodone_t end_io, dio_submit_t submit_io, int flags) { int seg; @@ -1143,9 +1143,9 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, } /* Check the memory alignment. Blocks cannot straddle pages */ - for (seg = 0; seg < nr_segs; seg++) { - addr = (unsigned long)iov[seg].iov_base; - size = iov[seg].iov_len; + for (seg = 0; seg < iter->nr_segs; seg++) { + addr = (unsigned long)iter->iov[seg].iov_base; + size = iter->iov[seg].iov_len; end += size; if (unlikely((addr & blocksize_mask) || (size & blocksize_mask))) { @@ -1256,18 +1256,18 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, if (unlikely(sdio.blkfactor)) sdio.pages_in_io = 2; - for (seg = 0; seg < nr_segs; seg++) { - user_addr = (unsigned long)iov[seg].iov_base; + for (seg = 0; seg < iter->nr_segs; seg++) { + user_addr = (unsigned long)iter->iov[seg].iov_base; sdio.pages_in_io += - ((user_addr + iov[seg].iov_len + PAGE_SIZE-1) / + ((user_addr + iter->iov[seg].iov_len + PAGE_SIZE-1) / PAGE_SIZE - user_addr / PAGE_SIZE); } blk_start_plug(&plug); - for (seg = 0; seg < nr_segs; seg++) { - user_addr = (unsigned long)iov[seg].iov_base; - sdio.size += bytes = iov[seg].iov_len; + for (seg = 0; seg < iter->nr_segs; seg++) { + user_addr = (unsigned long)iter->iov[seg].iov_base; + sdio.size += bytes = iter->iov[seg].iov_len; /* Index into the first page of the first block */ sdio.first_block_in_page = (user_addr & ~PAGE_MASK) >> blkbits; @@ -1288,7 +1288,7 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, retval = do_direct_IO(dio, &sdio, &map_bh); - dio->result += iov[seg].iov_len - + dio->result += iter->iov[seg].iov_len - ((sdio.final_block_in_request - sdio.block_in_file) << blkbits); @@ -1365,8 +1365,8 @@ out: ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, - struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, + struct block_device *bdev, struct iov_iter *iter, loff_t offset, + get_block_t get_block, dio_iodone_t end_io, dio_submit_t submit_io, int flags) { /* @@ -1381,9 +1381,8 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, prefetch(bdev->bd_queue); prefetch((char *)bdev->bd_queue + SMP_CACHE_BYTES); - return do_blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_block, end_io, - submit_io, flags); + return do_blockdev_direct_IO(rw, iocb, inode, bdev, iter, offset, + get_block, end_io, submit_io, flags); } EXPORT_SYMBOL(__blockdev_direct_IO); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 116e809aa7cb..36d35c36311d 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -859,8 +859,7 @@ ext2_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, size_t count = iov_iter_count(iter); ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, - iter->nr_segs, ext2_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, ext2_get_block); if (ret < 0 && (rw & WRITE)) ext2_write_failed(mapping, offset + count); return ret; diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 8582ae2c80b0..4d32133a76c4 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1855,8 +1855,7 @@ static ssize_t ext3_direct_IO(int rw, struct kiocb *iocb, } retry: - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, - iter->nr_segs, ext3_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, ext3_get_block); /* * In case of error extending write may have instantiated a few * blocks outside i_size. Trim these off again. diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 123898a6af05..8a57e9fcd1b9 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -686,14 +686,13 @@ retry: goto locked; } ret = __blockdev_direct_IO(rw, iocb, inode, - inode->i_sb->s_bdev, iter->iov, - offset, iter->nr_segs, + inode->i_sb->s_bdev, iter, offset, ext4_get_block, NULL, NULL, 0); inode_dio_done(inode); } else { locked: - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, - offset, iter->nr_segs, ext4_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter, + offset, ext4_get_block); if (unlikely((rw & WRITE) && ret < 0)) { loff_t isize = i_size_read(inode); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2b993579a968..e5718385a037 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3166,8 +3166,8 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, dio_flags = DIO_LOCKING; } ret = __blockdev_direct_IO(rw, iocb, inode, - inode->i_sb->s_bdev, iter->iov, - offset, iter->nr_segs, + inode->i_sb->s_bdev, iter, + offset, get_block_func, ext4_end_io_dio, NULL, diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 3a6ef121c095..151488f27755 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1022,8 +1022,8 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, if (check_direct_IO(inode, rw, iter->iov, offset, iter->nr_segs)) return 0; - return blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, - iter->nr_segs, get_data_block); + return blockdev_direct_IO(rw, iocb, inode, iter, offset, + get_data_block); } static void f2fs_invalidate_data_page(struct page *page, unsigned int offset, diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 154a6f9d3189..385cce464e82 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -213,8 +213,7 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, * FAT need to use the DIO_LOCKING for avoiding the race * condition of fat_get_block() and ->truncate(). */ - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, - iter->nr_segs, fat_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, fat_get_block); if (ret < 0 && (rw & WRITE)) fat_write_failed(mapping, offset + count); diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 228a12d2afa9..910838951d66 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -1097,7 +1097,7 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, } rv = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, - iter->iov, offset, iter->nr_segs, + iter, offset, gfs2_get_block_direct, NULL, NULL, 0); out: gfs2_glock_dq(&gh); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index dc69e8f31581..f5fb09ebc850 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -133,8 +133,7 @@ static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb, size_t count = iov_iter_count(iter); ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, - iter->nr_segs, hfs_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, hfs_get_block); /* * In case of error extending write may have instantiated a few diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index e6b1251af47a..76b930ff58ae 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -131,7 +131,7 @@ static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb, size_t count = iov_iter_count(iter); ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, iter->nr_segs, + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, hfsplus_get_block); /* diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 6cde5928693b..bd3df1ca3c9b 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -339,8 +339,7 @@ static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb, size_t count = iov_iter_count(iter); ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, - iter->nr_segs, jfs_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, jfs_get_block); /* * In case of error extending write may have instantiated a few diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 7aaf913e8709..6252b173a465 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -311,8 +311,8 @@ nilfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, return 0; /* Needs synchronization with the cleaner */ - size = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, - iter->nr_segs, nilfs_get_block); + size = blockdev_direct_IO(rw, iocb, inode, iter, offset, + nilfs_get_block); /* * In case of error extending write may have instantiated a few diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 799fd0afcb35..4a231a166cf8 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -617,7 +617,7 @@ static ssize_t ocfs2_direct_IO(int rw, return 0; return __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, - iter->iov, offset, iter->nr_segs, + iter, offset, ocfs2_direct_IO_get_blocks, ocfs2_dio_end_io, NULL, 0); } diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 723affe921f1..b8003e8dd1f4 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -3090,8 +3090,8 @@ static ssize_t reiserfs_direct_IO(int rw, struct kiocb *iocb, size_t count = iov_iter_count(iter); ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, - iter->nr_segs, reiserfs_get_blocks_direct_io); + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, + reiserfs_get_blocks_direct_io); /* * In case of error extending write may have instantiated a few diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 28984baf6194..236cd48184c2 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -226,8 +226,7 @@ static ssize_t udf_direct_IO(int rw, struct kiocb *iocb, size_t count = iov_iter_count(iter); ssize_t ret; - ret = blockdev_direct_IO(rw, iocb, inode, iter->iov, offset, iter->nr_segs, - udf_get_block); + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, udf_get_block); if (unlikely(ret < 0 && (rw & WRITE))) udf_write_failed(mapping, offset + count); return ret; diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 6462b3186784..08d13e395252 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -1470,17 +1470,15 @@ xfs_vm_direct_IO( if (offset + size > XFS_I(inode)->i_d.di_size) ioend->io_isdirect = 1; - ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter->iov, - offset, iter->nr_segs, - xfs_get_blocks_direct, + ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter, + offset, xfs_get_blocks_direct, xfs_end_io_direct_write, NULL, DIO_ASYNC_EXTEND); if (ret != -EIOCBQUEUED && iocb->private) goto out_destroy_ioend; } else { - ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter->iov, - offset, iter->nr_segs, - xfs_get_blocks_direct, + ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter, + offset, xfs_get_blocks_direct, NULL, NULL, 0); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 399a338c92b5..946a9484844f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2474,16 +2474,16 @@ enum { void dio_end_io(struct bio *bio, int error); ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, - struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, + struct block_device *bdev, struct iov_iter *iter, loff_t offset, + get_block_t get_block, dio_iodone_t end_io, dio_submit_t submit_io, int flags); static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb, - struct inode *inode, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_block_t get_block) + struct inode *inode, struct iov_iter *iter, loff_t offset, + get_block_t get_block) { - return __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, - offset, nr_segs, get_block, NULL, NULL, + return __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iter, + offset, get_block, NULL, NULL, DIO_LOCKING | DIO_SKIP_HOLES); } #endif -- cgit v1.2.3 From 886a39115005ced8b15ab067c9c2a8d546b40a5e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 5 Mar 2014 13:50:45 -0500 Subject: new primitive: iov_iter_alignment() returns the value aligned as badly as the worst remaining segment in iov_iter is. Use instead of open-coded equivalents. Signed-off-by: Al Viro --- drivers/staging/lustre/lustre/llite/rw26.c | 7 ++----- fs/direct-io.c | 27 +++++---------------------- include/linux/uio.h | 2 ++ mm/iov_iter.c | 25 +++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/staging/lustre/lustre/llite/rw26.c b/drivers/staging/lustre/lustre/llite/rw26.c index 38a5b580e7f0..f718585c9e08 100644 --- a/drivers/staging/lustre/lustre/llite/rw26.c +++ b/drivers/staging/lustre/lustre/llite/rw26.c @@ -391,11 +391,8 @@ static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb, MAX_DIO_SIZE >> PAGE_CACHE_SHIFT); /* Check that all user buffers are aligned as well */ - for (seg = 0; seg < iter->nr_segs; seg++) { - if (((unsigned long)iter->iov[seg].iov_base & ~CFS_PAGE_MASK) || - (iter->iov[seg].iov_len & ~CFS_PAGE_MASK)) - return -EINVAL; - } + if (iov_iter_alignment(iter) & ~CFS_PAGE_MASK) + return -EINVAL; env = cl_env_get(&refcheck); LASSERT(!IS_ERR(env)); diff --git a/fs/direct-io.c b/fs/direct-io.c index 1c677899b989..adfa1fb33456 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -1112,19 +1112,18 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, dio_submit_t submit_io, int flags) { int seg; - size_t size; - unsigned long addr; unsigned i_blkbits = ACCESS_ONCE(inode->i_blkbits); unsigned blkbits = i_blkbits; unsigned blocksize_mask = (1 << blkbits) - 1; ssize_t retval = -EINVAL; - loff_t end = offset; + loff_t end = offset + iov_iter_count(iter); struct dio *dio; struct dio_submit sdio = { 0, }; unsigned long user_addr; size_t bytes; struct buffer_head map_bh = { 0, }; struct blk_plug plug; + unsigned long align = offset | iov_iter_alignment(iter); if (rw & WRITE) rw = WRITE_ODIRECT; @@ -1134,32 +1133,16 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, * the early prefetch in the caller enough time. */ - if (offset & blocksize_mask) { + if (align & blocksize_mask) { if (bdev) blkbits = blksize_bits(bdev_logical_block_size(bdev)); blocksize_mask = (1 << blkbits) - 1; - if (offset & blocksize_mask) + if (align & blocksize_mask) goto out; } - /* Check the memory alignment. Blocks cannot straddle pages */ - for (seg = 0; seg < iter->nr_segs; seg++) { - addr = (unsigned long)iter->iov[seg].iov_base; - size = iter->iov[seg].iov_len; - end += size; - if (unlikely((addr & blocksize_mask) || - (size & blocksize_mask))) { - if (bdev) - blkbits = blksize_bits( - bdev_logical_block_size(bdev)); - blocksize_mask = (1 << blkbits) - 1; - if ((addr & blocksize_mask) || (size & blocksize_mask)) - goto out; - } - } - /* watch out for a 0 len io from a tricksy fs */ - if (rw == READ && end == offset) + if (rw == READ && !iov_iter_count(iter)) return 0; dio = kmem_cache_alloc(dio_cache, GFP_KERNEL); diff --git a/include/linux/uio.h b/include/linux/uio.h index abbe83ded630..4ee17413fe1b 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -67,6 +67,7 @@ int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); size_t iov_iter_single_seg_count(const struct iov_iter *i); size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i); +unsigned long iov_iter_alignment(const struct iov_iter *i); static inline void iov_iter_init(struct iov_iter *i, const struct iovec *iov, unsigned long nr_segs, @@ -88,4 +89,5 @@ static inline size_t iov_iter_count(struct iov_iter *i) int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len); int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len); + #endif diff --git a/mm/iov_iter.c b/mm/iov_iter.c index 22ec1ef068a8..2f762cc21080 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -195,3 +195,28 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i) return min(i->count, iov->iov_len - i->iov_offset); } EXPORT_SYMBOL(iov_iter_single_seg_count); + +unsigned long iov_iter_alignment(const struct iov_iter *i) +{ + const struct iovec *iov = i->iov; + unsigned long res; + size_t size = i->count; + size_t n; + + if (!size) + return 0; + + res = (unsigned long)iov->iov_base + i->iov_offset; + n = iov->iov_len - i->iov_offset; + if (n >= size) + return res | size; + size -= n; + res |= n; + while (size > (++iov)->iov_len) { + res |= (unsigned long)iov->iov_base | iov->iov_len; + size -= iov->iov_len; + } + res |= (unsigned long)iov->iov_base | size; + return res; +} +EXPORT_SYMBOL(iov_iter_alignment); -- cgit v1.2.3 From ed978a811ec528dbe40243605c3afab55892f722 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 5 Mar 2014 22:53:04 -0500 Subject: new helper: generic_file_read_iter() iov_iter-using variant of generic_file_aio_read(). Some callers converted. Note that it's still not quite there for use as ->read_iter() - we depend on having zero iter->iov_offset in O_DIRECT case. Fortunately, that's true for all converted callers (and for generic_file_aio_read() itself). Signed-off-by: Al Viro --- fs/ceph/file.c | 15 +----------- fs/nfs/file.c | 2 +- include/linux/fs.h | 1 + mm/filemap.c | 67 +++++++++++++++++++++++++++--------------------------- 4 files changed, 37 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/fs/ceph/file.c b/fs/ceph/file.c index d8f383d59449..910a3022eb27 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -833,24 +833,11 @@ again: /* hmm, this isn't really async... */ ret = ceph_sync_read(iocb, &i, &checkeof); } else { - /* - * We can't modify the content of iov, - * so we only read from beginning. - * - * When we switch generic_file_aio_read() to iov_iter, the - * if () below will be removed -- AV - */ - if (read) { - iocb->ki_pos = pos; - len = iocb->ki_nbytes; - read = 0; - iov_iter_init(&i, iov, nr_segs, len, 0); - } dout("aio_read %p %llx.%llx %llu~%u got cap refs on %s\n", inode, ceph_vinop(inode), pos, (unsigned)len, ceph_cap_string(got)); - ret = generic_file_aio_read(iocb, iov, nr_segs, pos); + ret = generic_file_read_iter(iocb, &i); } dout("aio_read %p %llx.%llx dropping cap refs on %s = %d\n", inode, ceph_vinop(inode), ceph_cap_string(got), (int)ret); diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 3d01b152894e..a352bc6d613f 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -184,7 +184,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping); if (!result) { - result = generic_file_aio_read(iocb, iov, nr_segs, pos); + result = generic_file_read_iter(iocb, &to); if (result > 0) nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, result); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 946a9484844f..d096ebc7f348 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2404,6 +2404,7 @@ extern int generic_file_remap_pages(struct vm_area_struct *, unsigned long addr, unsigned long size, pgoff_t pgoff); int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk); extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t); +extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long); extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, diff --git a/mm/filemap.c b/mm/filemap.c index 866f4ae8223b..a7f79e90209c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1663,55 +1663,34 @@ out: return written ? written : error; } -/** - * generic_file_aio_read - generic filesystem read routine - * @iocb: kernel I/O control block - * @iov: io vector request - * @nr_segs: number of segments in the iovec - * @pos: current file position - * - * This is the "read()" routine for all filesystems - * that can use the page cache directly. - */ ssize_t -generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) { - struct file *filp = iocb->ki_filp; + struct file *file = iocb->ki_filp; ssize_t retval = 0; - size_t count; loff_t *ppos = &iocb->ki_pos; - struct iov_iter i; - - count = iov_length(iov, nr_segs); - iov_iter_init(&i, iov, nr_segs, count, 0); + loff_t pos = *ppos; /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ - if (filp->f_flags & O_DIRECT) { + if (file->f_flags & O_DIRECT) { + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_iter_count(iter); loff_t size; - struct address_space *mapping; - struct inode *inode; - mapping = filp->f_mapping; - inode = mapping->host; if (!count) goto out; /* skip atime */ size = i_size_read(inode); retval = filemap_write_and_wait_range(mapping, pos, pos + count - 1); if (!retval) { - struct iov_iter data = i; + struct iov_iter data = *iter; retval = mapping->a_ops->direct_IO(READ, iocb, &data, pos); } if (retval > 0) { *ppos = pos + retval; - count -= retval; - /* - * If we did a short DIO read we need to skip the - * section of the iov that we've already read data into. - */ - iov_iter_advance(&i, retval); + iov_iter_advance(iter, retval); } /* @@ -1722,16 +1701,38 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, * and return. Otherwise fallthrough to buffered io for * the rest of the read. */ - if (retval < 0 || !count || *ppos >= size) { - file_accessed(filp); + if (retval < 0 || !iov_iter_count(iter) || *ppos >= size) { + file_accessed(file); goto out; } } - retval = do_generic_file_read(filp, ppos, &i, retval); + retval = do_generic_file_read(file, ppos, iter, retval); out: return retval; } +EXPORT_SYMBOL(generic_file_read_iter); + +/** + * generic_file_aio_read - generic filesystem read routine + * @iocb: kernel I/O control block + * @iov: io vector request + * @nr_segs: number of segments in the iovec + * @pos: current file position + * + * This is the "read()" routine for all filesystems + * that can use the page cache directly. + */ +ssize_t +generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + size_t count = iov_length(iov, nr_segs); + struct iov_iter i; + + iov_iter_init(&i, iov, nr_segs, count, 0); + return generic_file_read_iter(iocb, &i); +} EXPORT_SYMBOL(generic_file_aio_read); #ifdef CONFIG_MMU -- cgit v1.2.3 From 71d8e532b1549a478e6a6a8a44f309d050294d00 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 5 Mar 2014 19:28:09 -0500 Subject: start adding the tag to iov_iter For now, just use the same thing we pass to ->direct_IO() - it's all iovec-based at the moment. Pass it explicitly to iov_iter_init() and account for kvec vs. iovec in there, by the same kludge NFS ->direct_IO() uses. Signed-off-by: Al Viro --- fs/btrfs/file.c | 2 +- fs/ceph/file.c | 8 ++++---- fs/cifs/file.c | 4 ++-- fs/fuse/file.c | 6 +++--- fs/nfs/file.c | 4 ++-- fs/ocfs2/file.c | 2 +- fs/pipe.c | 2 +- fs/splice.c | 2 +- fs/xfs/xfs_file.c | 4 ++-- include/linux/uio.h | 15 +++------------ mm/filemap.c | 4 ++-- mm/iov_iter.c | 15 +++++++++++++++ mm/page_io.c | 2 +- mm/process_vm_access.c | 4 ++-- mm/shmem.c | 2 +- 15 files changed, 41 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index a0a94a30d85a..f8cee205618a 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1740,7 +1740,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, goto out; } - iov_iter_init(&i, iov, nr_segs, count, 0); + iov_iter_init(&i, WRITE, iov, nr_segs, count); err = file_remove_suid(file); if (err) { diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 910a3022eb27..5b93cadedfbe 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -582,7 +582,7 @@ ceph_sync_direct_write(struct kiocb *iocb, const struct iovec *iov, CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE; - iov_iter_init(&i, iov, nr_segs, count, 0); + iov_iter_init(&i, WRITE, iov, nr_segs, count); while (iov_iter_count(&i) > 0) { void __user *data = i.iov->iov_base + i.iov_offset; @@ -703,7 +703,7 @@ static ssize_t ceph_sync_write(struct kiocb *iocb, const struct iovec *iov, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ACK; - iov_iter_init(&i, iov, nr_segs, count, 0); + iov_iter_init(&i, WRITE, iov, nr_segs, count); while ((len = iov_iter_count(&i)) > 0) { size_t left; @@ -808,7 +808,7 @@ static ssize_t ceph_aio_read(struct kiocb *iocb, const struct iovec *iov, int checkeof = 0, read = 0; struct iov_iter i; - iov_iter_init(&i, iov, nr_segs, len, 0); + iov_iter_init(&i, READ, iov, nr_segs, len); again: dout("aio_read %p %llx.%llx %llu~%u trying to get caps on %p\n", @@ -961,7 +961,7 @@ retry_snap: * are pending vmtruncate. So write and vmtruncate * can not run at the same time */ - iov_iter_init(&from, iov, nr_segs, count, 0); + iov_iter_init(&from, WRITE, iov, nr_segs, count); written = generic_perform_write(file, &from, pos); if (likely(written >= 0)) iocb->ki_pos = pos + written; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index a4ccc39e6c11..15201c21ac88 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2424,7 +2424,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, else pid = current->tgid; - iov_iter_init(&it, iov, nr_segs, len, 0); + iov_iter_init(&it, WRITE, iov, nr_segs, len); do { size_t save_len; @@ -2854,7 +2854,7 @@ ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, if (!len) return 0; - iov_iter_init(&to, iov, nr_segs, len, 0); + iov_iter_init(&to, READ, iov, nr_segs, len); INIT_LIST_HEAD(&rdata_list); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index fc54d04a41e2..4a5519ca253f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1217,7 +1217,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); if (err) goto out; - iov_iter_init(&i, iov, nr_segs, count, 0); + iov_iter_init(&i, WRITE, iov, nr_segs, count); if (count == 0) goto out; @@ -1386,7 +1386,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov, struct fuse_req *req; struct iov_iter ii; - iov_iter_init(&ii, iov, nr_segs, count, 0); + iov_iter_init(&ii, write ? WRITE : READ, iov, nr_segs, count); if (io->async) req = fuse_get_req_for_background(fc, fuse_iter_npages(&ii)); @@ -2367,7 +2367,7 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov, if (!bytes) return 0; - iov_iter_init(&ii, iov, nr_segs, bytes, 0); + iov_iter_init(&ii, to_user ? READ : WRITE, iov, nr_segs, bytes); while (iov_iter_count(&ii)) { struct page *page = pages[page_idx++]; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index a352bc6d613f..ead8f44f7973 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -173,7 +173,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, ssize_t result; struct iov_iter to; - iov_iter_init(&to, iov, nr_segs, count, 0); + iov_iter_init(&to, READ, iov, nr_segs, count); if (iocb->ki_filp->f_flags & O_DIRECT) return nfs_file_direct_read(iocb, &to, pos, true); @@ -648,7 +648,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, ssize_t result; size_t count = iov_length(iov, nr_segs); struct iov_iter from; - iov_iter_init(&from, iov, nr_segs, count, 0); + iov_iter_init(&from, WRITE, iov, nr_segs, count); result = nfs_key_timeout_notify(file, inode); if (result) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index d33c4ced0baf..9ce9ed7615c1 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2361,7 +2361,7 @@ relock: if (ret) goto out_dio; - iov_iter_init(&from, iov, nr_segs, count, 0); + iov_iter_init(&from, WRITE, iov, nr_segs, count); if (direct_io) { written = generic_file_direct_write(iocb, &from, *ppos, count, ocount); diff --git a/fs/pipe.c b/fs/pipe.c index 034bffac3f97..cd4ccf07e772 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -287,7 +287,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov, if (unlikely(total_len == 0)) return 0; - iov_iter_init(&iter, iov, nr_segs, total_len, 0); + iov_iter_init(&iter, READ, iov, nr_segs, total_len); do_wakeup = 0; ret = 0; diff --git a/fs/splice.c b/fs/splice.c index 9bc07d2b53cf..f99e420744c7 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1548,7 +1548,7 @@ static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov, if (ret <= 0) return ret; - iov_iter_init(&iter, iov, nr_segs, count, 0); + iov_iter_init(&iter, READ, iov, nr_segs, count); sd.len = 0; sd.total_len = count; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index f0f8084a67be..762bb3e148a6 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -697,7 +697,7 @@ xfs_file_dio_aio_write( } trace_xfs_file_direct_write(ip, count, iocb->ki_pos, 0); - iov_iter_init(&from, iovp, nr_segs, count, 0); + iov_iter_init(&from, WRITE, iovp, nr_segs, count); ret = generic_file_direct_write(iocb, &from, pos, count, ocount); out: @@ -731,7 +731,7 @@ xfs_file_buffered_aio_write( if (ret) goto out; - iov_iter_init(&from, iovp, nr_segs, count, 0); + iov_iter_init(&from, WRITE, iovp, nr_segs, count); /* We can write back this queue in page reclaim */ current->backing_dev_info = mapping->backing_dev_info; diff --git a/include/linux/uio.h b/include/linux/uio.h index 4ee17413fe1b..b80bbe197d13 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -20,6 +20,7 @@ struct kvec { }; struct iov_iter { + int type; const struct iovec *iov; unsigned long nr_segs; size_t iov_offset; @@ -68,18 +69,8 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i); size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i); unsigned long iov_iter_alignment(const struct iov_iter *i); - -static inline void iov_iter_init(struct iov_iter *i, - const struct iovec *iov, unsigned long nr_segs, - size_t count, size_t written) -{ - i->iov = iov; - i->nr_segs = nr_segs; - i->iov_offset = 0; - i->count = count + written; - - iov_iter_advance(i, written); -} +void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov, + unsigned long nr_segs, size_t count); static inline size_t iov_iter_count(struct iov_iter *i) { diff --git a/mm/filemap.c b/mm/filemap.c index a7f79e90209c..3aeaf2df4135 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1730,7 +1730,7 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, size_t count = iov_length(iov, nr_segs); struct iov_iter i; - iov_iter_init(&i, iov, nr_segs, count, 0); + iov_iter_init(&i, READ, iov, nr_segs, count); return generic_file_read_iter(iocb, &i); } EXPORT_SYMBOL(generic_file_aio_read); @@ -2596,7 +2596,7 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (err) goto out; - iov_iter_init(&from, iov, nr_segs, count, 0); + iov_iter_init(&from, WRITE, iov, nr_segs, count); /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ if (unlikely(file->f_flags & O_DIRECT)) { diff --git a/mm/iov_iter.c b/mm/iov_iter.c index 2f762cc21080..e2c9a2db4350 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -220,3 +220,18 @@ unsigned long iov_iter_alignment(const struct iov_iter *i) return res; } EXPORT_SYMBOL(iov_iter_alignment); + +void iov_iter_init(struct iov_iter *i, int direction, + const struct iovec *iov, unsigned long nr_segs, + size_t count) +{ + /* It will get better. Eventually... */ + if (segment_eq(get_fs(), KERNEL_DS)) + direction |= REQ_KERNEL; + i->type = direction; + i->iov = iov; + i->nr_segs = nr_segs; + i->iov_offset = 0; + i->count = count; +} +EXPORT_SYMBOL(iov_iter_init); diff --git a/mm/page_io.c b/mm/page_io.c index 0ed0644c73db..313bfedb75d1 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -268,7 +268,7 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc, init_sync_kiocb(&kiocb, swap_file); kiocb.ki_pos = page_file_offset(page); kiocb.ki_nbytes = PAGE_SIZE; - iov_iter_init(&from, &iov, 1, PAGE_SIZE, 0); + iov_iter_init(&from, KERNEL_WRITE, &iov, 1, PAGE_SIZE); set_page_writeback(page); unlock_page(page); diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c index f32b1fbbfe69..5077afcd9e11 100644 --- a/mm/process_vm_access.c +++ b/mm/process_vm_access.c @@ -274,7 +274,7 @@ static ssize_t process_vm_rw(pid_t pid, if (rc <= 0) goto free_iovecs; - iov_iter_init(&iter, iov_l, liovcnt, rc, 0); + iov_iter_init(&iter, vm_write ? WRITE : READ, iov_l, liovcnt, rc); rc = rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt, UIO_FASTIOV, iovstack_r, &iov_r); @@ -337,7 +337,7 @@ compat_process_vm_rw(compat_pid_t pid, &iov_l); if (rc <= 0) goto free_iovecs; - iov_iter_init(&iter, iov_l, liovcnt, rc, 0); + iov_iter_init(&iter, vm_write ? WRITE : READ, iov_l, liovcnt, rc); rc = compat_rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt, UIO_FASTIOV, iovstack_r, &iov_r); diff --git a/mm/shmem.c b/mm/shmem.c index 2a93e625adaf..e0b76696c3f9 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1417,7 +1417,7 @@ static ssize_t shmem_file_aio_read(struct kiocb *iocb, loff_t *ppos = &iocb->ki_pos; struct iov_iter iter; - iov_iter_init(&iter, iov, nr_segs, count, 0); + iov_iter_init(&iter, READ, iov, nr_segs, count); /* * Might this read be for a stacking filesystem? Then when reading -- cgit v1.2.3 From 7b2c99d15559e285384c742db52316802e24b0bd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 15 Mar 2014 04:05:57 -0400 Subject: new helper: iov_iter_get_pages() iov_iter_get_pages(iter, pages, maxsize, &start) grabs references pinning the pages of up to maxsize of (contiguous) data from iter. Returns the amount of memory grabbed or -error. In case of success, the requested area begins at offset start in pages[0] and runs through pages[1], etc. Less than requested amount might be returned - either because the contiguous area in the beginning of iterator is smaller than requested, or because the kernel failed to pin that many pages. direct-io.c switched to using iov_iter_get_pages() Signed-off-by: Al Viro --- fs/direct-io.c | 111 ++++++++++++++++++---------------------------------- include/linux/uio.h | 2 + mm/iov_iter.c | 27 +++++++++++++ 3 files changed, 67 insertions(+), 73 deletions(-) (limited to 'include') diff --git a/fs/direct-io.c b/fs/direct-io.c index 387d91989c45..4b410d58faae 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -77,7 +77,6 @@ struct dio_submit { unsigned blocks_available; /* At block_in_file. changes */ int reap_counter; /* rate limit reaping */ sector_t final_block_in_request;/* doesn't change */ - unsigned first_block_in_page; /* doesn't change, Used only once */ int boundary; /* prev block is at a boundary */ get_block_t *get_block; /* block mapping function */ dio_submit_t *submit_io; /* IO submition function */ @@ -98,19 +97,14 @@ struct dio_submit { sector_t cur_page_block; /* Where it starts */ loff_t cur_page_fs_offset; /* Offset in file */ - /* - * Page fetching state. These variables belong to dio_refill_pages(). - */ - int curr_page; /* changes */ - int total_pages; /* doesn't change */ - unsigned long curr_user_address;/* changes */ - + struct iov_iter *iter; /* * Page queue. These variables belong to dio_refill_pages() and * dio_get_page(). */ unsigned head; /* next page to process */ unsigned tail; /* last valid page + 1 */ + size_t from, to; }; /* dio_state communicated between submission path and end_io */ @@ -163,15 +157,10 @@ static inline unsigned dio_pages_present(struct dio_submit *sdio) */ static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio) { - int ret; - int nr_pages; + ssize_t ret; - nr_pages = min(sdio->total_pages - sdio->curr_page, DIO_PAGES); - ret = get_user_pages_fast( - sdio->curr_user_address, /* Where from? */ - nr_pages, /* How many pages? */ - dio->rw == READ, /* Write to memory? */ - &dio->pages[0]); /* Put results here */ + ret = iov_iter_get_pages(sdio->iter, dio->pages, DIO_PAGES * PAGE_SIZE, + &sdio->from); if (ret < 0 && sdio->blocks_available && (dio->rw & WRITE)) { struct page *page = ZERO_PAGE(0); @@ -186,18 +175,19 @@ static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio) dio->pages[0] = page; sdio->head = 0; sdio->tail = 1; - ret = 0; - goto out; + sdio->from = 0; + sdio->to = PAGE_SIZE; + return 0; } if (ret >= 0) { - sdio->curr_user_address += ret * PAGE_SIZE; - sdio->curr_page += ret; + iov_iter_advance(sdio->iter, ret); + ret += sdio->from; sdio->head = 0; - sdio->tail = ret; - ret = 0; + sdio->tail = (ret + PAGE_SIZE - 1) / PAGE_SIZE; + sdio->to = ((ret - 1) & (PAGE_SIZE - 1)) + 1; + return 0; } -out: return ret; } @@ -208,8 +198,9 @@ out: * L1 cache. */ static inline struct page *dio_get_page(struct dio *dio, - struct dio_submit *sdio) + struct dio_submit *sdio, size_t *from, size_t *to) { + int n; if (dio_pages_present(sdio) == 0) { int ret; @@ -218,7 +209,10 @@ static inline struct page *dio_get_page(struct dio *dio, return ERR_PTR(ret); BUG_ON(dio_pages_present(sdio) == 0); } - return dio->pages[sdio->head++]; + n = sdio->head++; + *from = n ? 0 : sdio->from; + *to = (n == sdio->tail - 1) ? sdio->to : PAGE_SIZE; + return dio->pages[n]; } /** @@ -422,8 +416,8 @@ static inline void dio_bio_submit(struct dio *dio, struct dio_submit *sdio) */ static inline void dio_cleanup(struct dio *dio, struct dio_submit *sdio) { - while (dio_pages_present(sdio)) - page_cache_release(dio_get_page(dio, sdio)); + while (sdio->head < sdio->tail) + page_cache_release(dio->pages[sdio->head++]); } /* @@ -912,23 +906,18 @@ static int do_direct_IO(struct dio *dio, struct dio_submit *sdio, struct buffer_head *map_bh) { const unsigned blkbits = sdio->blkbits; - const unsigned blocks_per_page = PAGE_SIZE >> blkbits; - struct page *page; - unsigned block_in_page; int ret = 0; - /* The I/O can start at any block offset within the first page */ - block_in_page = sdio->first_block_in_page; - while (sdio->block_in_file < sdio->final_block_in_request) { - page = dio_get_page(dio, sdio); + struct page *page; + size_t from, to; + page = dio_get_page(dio, sdio, &from, &to); if (IS_ERR(page)) { ret = PTR_ERR(page); goto out; } - while (block_in_page < blocks_per_page) { - unsigned offset_in_page = block_in_page << blkbits; + while (from < to) { unsigned this_chunk_bytes; /* # of bytes mapped */ unsigned this_chunk_blocks; /* # of blocks */ unsigned u; @@ -999,10 +988,9 @@ do_holes: page_cache_release(page); goto out; } - zero_user(page, block_in_page << blkbits, - 1 << blkbits); + zero_user(page, from, 1 << blkbits); sdio->block_in_file++; - block_in_page++; + from += 1 << blkbits; dio->result += 1 << blkbits; goto next_block; } @@ -1020,7 +1008,7 @@ do_holes: * can add to this page */ this_chunk_blocks = sdio->blocks_available; - u = (PAGE_SIZE - offset_in_page) >> blkbits; + u = (to - from) >> blkbits; if (this_chunk_blocks > u) this_chunk_blocks = u; u = sdio->final_block_in_request - sdio->block_in_file; @@ -1032,7 +1020,7 @@ do_holes: if (this_chunk_blocks == sdio->blocks_available) sdio->boundary = buffer_boundary(map_bh); ret = submit_page_section(dio, sdio, page, - offset_in_page, + from, this_chunk_bytes, sdio->next_block_for_io, map_bh); @@ -1043,9 +1031,9 @@ do_holes: sdio->next_block_for_io += this_chunk_blocks; sdio->block_in_file += this_chunk_blocks; - block_in_page += this_chunk_blocks; + from += this_chunk_bytes; + dio->result += this_chunk_bytes; sdio->blocks_available -= this_chunk_blocks; - dio->result += this_chunk_blocks << blkbits; next_block: BUG_ON(sdio->block_in_file > sdio->final_block_in_request); if (sdio->block_in_file == sdio->final_block_in_request) @@ -1054,7 +1042,6 @@ next_block: /* Drop the ref which was taken in get_user_pages() */ page_cache_release(page); - block_in_page = 0; } out: return ret; @@ -1122,7 +1109,6 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct dio *dio; struct dio_submit sdio = { 0, }; unsigned long user_addr; - size_t bytes; struct buffer_head map_bh = { 0, }; struct blk_plug plug; unsigned long align = offset | iov_iter_alignment(iter); @@ -1234,6 +1220,10 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, spin_lock_init(&dio->bio_lock); dio->refcount = 1; + sdio.iter = iter; + sdio.final_block_in_request = + (offset + iov_iter_count(iter)) >> blkbits; + /* * In case of non-aligned buffers, we may need 2 more * pages since we need to zero out first and last block. @@ -1250,34 +1240,9 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, blk_start_plug(&plug); - for (seg = 0; seg < iter->nr_segs; seg++) { - user_addr = (unsigned long)iter->iov[seg].iov_base; - sdio.size += bytes = iter->iov[seg].iov_len; - - /* Index into the first page of the first block */ - sdio.first_block_in_page = (user_addr & ~PAGE_MASK) >> blkbits; - sdio.final_block_in_request = sdio.block_in_file + - (bytes >> blkbits); - /* Page fetching state */ - sdio.head = 0; - sdio.tail = 0; - sdio.curr_page = 0; - - sdio.total_pages = 0; - if (user_addr & (PAGE_SIZE-1)) { - sdio.total_pages++; - bytes -= PAGE_SIZE - (user_addr & (PAGE_SIZE - 1)); - } - sdio.total_pages += (bytes + PAGE_SIZE - 1) / PAGE_SIZE; - sdio.curr_user_address = user_addr; - - retval = do_direct_IO(dio, &sdio, &map_bh); - - if (retval) { - dio_cleanup(dio, &sdio); - break; - } - } /* end iovec loop */ + retval = do_direct_IO(dio, &sdio, &map_bh); + if (retval) + dio_cleanup(dio, &sdio); if (retval == -ENOTBLK) { /* diff --git a/include/linux/uio.h b/include/linux/uio.h index b80bbe197d13..341986116d83 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -71,6 +71,8 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, unsigned long iov_iter_alignment(const struct iov_iter *i); void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov, unsigned long nr_segs, size_t count); +ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages, + size_t maxsize, size_t *start); static inline size_t iov_iter_count(struct iov_iter *i) { diff --git a/mm/iov_iter.c b/mm/iov_iter.c index e2c9a2db4350..45204cd5ccd8 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -235,3 +235,30 @@ void iov_iter_init(struct iov_iter *i, int direction, i->count = count; } EXPORT_SYMBOL(iov_iter_init); + +ssize_t iov_iter_get_pages(struct iov_iter *i, + struct page **pages, size_t maxsize, + size_t *start) +{ + size_t offset = i->iov_offset; + const struct iovec *iov = i->iov; + size_t len; + unsigned long addr; + int n; + int res; + + len = iov->iov_len - offset; + if (len > i->count) + len = i->count; + if (len > maxsize) + len = maxsize; + addr = (unsigned long)iov->iov_base + offset; + len += *start = addr & (PAGE_SIZE - 1); + addr &= ~(PAGE_SIZE - 1); + n = (len + PAGE_SIZE - 1) / PAGE_SIZE; + res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, pages); + if (unlikely(res < 0)) + return res; + return (res == n ? len : res * PAGE_SIZE) - *start; +} +EXPORT_SYMBOL(iov_iter_get_pages); -- cgit v1.2.3 From f67da30c1d5fc9e341bc8121708874bfd7b31e45 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 19 Mar 2014 01:16:16 -0400 Subject: new helper: iov_iter_npages() counts the pages covered by iov_iter, up to given limit. do_block_direct_io() and fuse_iter_npages() switched to it. Signed-off-by: Al Viro --- fs/direct-io.c | 9 +-------- fs/fuse/file.c | 16 ++-------------- include/linux/uio.h | 1 + mm/iov_iter.c | 27 +++++++++++++++++++++++++++ 4 files changed, 31 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/fs/direct-io.c b/fs/direct-io.c index 4b410d58faae..98040ba388ac 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -1100,7 +1100,6 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, get_block_t get_block, dio_iodone_t end_io, dio_submit_t submit_io, int flags) { - int seg; unsigned i_blkbits = ACCESS_ONCE(inode->i_blkbits); unsigned blkbits = i_blkbits; unsigned blocksize_mask = (1 << blkbits) - 1; @@ -1108,7 +1107,6 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, loff_t end = offset + iov_iter_count(iter); struct dio *dio; struct dio_submit sdio = { 0, }; - unsigned long user_addr; struct buffer_head map_bh = { 0, }; struct blk_plug plug; unsigned long align = offset | iov_iter_alignment(iter); @@ -1231,12 +1229,7 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, if (unlikely(sdio.blkfactor)) sdio.pages_in_io = 2; - for (seg = 0; seg < iter->nr_segs; seg++) { - user_addr = (unsigned long)iter->iov[seg].iov_base; - sdio.pages_in_io += - ((user_addr + iter->iov[seg].iov_len + PAGE_SIZE-1) / - PAGE_SIZE - user_addr / PAGE_SIZE); - } + sdio.pages_in_io += iov_iter_npages(iter, INT_MAX); blk_start_plug(&plug); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 7db564d18dc6..7026014717bc 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1310,7 +1310,7 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii, while (nbytes < *nbytesp && req->num_pages < req->max_pages) { unsigned npages; - size_t start, end, frag_size; + size_t start; unsigned n = req->max_pages - req->num_pages; ssize_t ret = iov_iter_get_pages(ii, &req->pages[req->num_pages], @@ -1344,19 +1344,7 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii, static inline int fuse_iter_npages(const struct iov_iter *ii_p) { - struct iov_iter ii = *ii_p; - int npages = 0; - - while (iov_iter_count(&ii) && npages < FUSE_MAX_PAGES_PER_REQ) { - unsigned long user_addr = fuse_get_user_addr(&ii); - unsigned offset = user_addr & ~PAGE_MASK; - size_t frag_size = iov_iter_single_seg_count(&ii); - - npages += (frag_size + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; - iov_iter_advance(&ii, frag_size); - } - - return min(npages, FUSE_MAX_PAGES_PER_REQ); + return iov_iter_npages(ii_p, FUSE_MAX_PAGES_PER_REQ); } ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, diff --git a/include/linux/uio.h b/include/linux/uio.h index 341986116d83..2f8825b06680 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -73,6 +73,7 @@ void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov, unsigned long nr_segs, size_t count); ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages, size_t maxsize, size_t *start); +int iov_iter_npages(const struct iov_iter *i, int maxpages); static inline size_t iov_iter_count(struct iov_iter *i) { diff --git a/mm/iov_iter.c b/mm/iov_iter.c index 45204cd5ccd8..0b677f8f9bad 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -262,3 +262,30 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, return (res == n ? len : res * PAGE_SIZE) - *start; } EXPORT_SYMBOL(iov_iter_get_pages); + +int iov_iter_npages(const struct iov_iter *i, int maxpages) +{ + size_t offset = i->iov_offset; + size_t size = i->count; + const struct iovec *iov = i->iov; + int npages = 0; + int n; + + for (n = 0; size && n < i->nr_segs; n++, iov++) { + unsigned long addr = (unsigned long)iov->iov_base + offset; + size_t len = iov->iov_len - offset; + offset = 0; + if (unlikely(!len)) /* empty segment */ + continue; + if (len > size) + len = size; + npages += (addr + len + PAGE_SIZE - 1) / PAGE_SIZE + - addr / PAGE_SIZE; + if (npages >= maxpages) /* don't bother going further */ + return maxpages; + size -= len; + offset = 0; + } + return min(npages, maxpages); +} +EXPORT_SYMBOL(iov_iter_npages); -- cgit v1.2.3 From 91f79c43d1b54d7154b118860d81b39bad07dfff Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 21 Mar 2014 04:58:33 -0400 Subject: new helper: iov_iter_get_pages_alloc() same as iov_iter_get_pages(), except that pages array is allocated (kmalloc if possible, vmalloc if that fails) and left for caller to free. Lustre and NFS ->direct_IO() switched to it. Signed-off-by: Al Viro --- drivers/staging/lustre/lustre/llite/rw26.c | 92 ++++----- fs/nfs/direct.c | 290 +++++++++-------------------- include/linux/uio.h | 2 + mm/iov_iter.c | 40 ++++ 4 files changed, 167 insertions(+), 257 deletions(-) (limited to 'include') diff --git a/drivers/staging/lustre/lustre/llite/rw26.c b/drivers/staging/lustre/lustre/llite/rw26.c index f718585c9e08..6b5994577b6b 100644 --- a/drivers/staging/lustre/lustre/llite/rw26.c +++ b/drivers/staging/lustre/lustre/llite/rw26.c @@ -218,14 +218,11 @@ static void ll_free_user_pages(struct page **pages, int npages, int do_dirty) int i; for (i = 0; i < npages; i++) { - if (pages[i] == NULL) - break; if (do_dirty) set_page_dirty_lock(pages[i]); page_cache_release(pages[i]); } - - OBD_FREE_LARGE(pages, npages * sizeof(*pages)); + kvfree(pages); } ssize_t ll_direct_rw_pages(const struct lu_env *env, struct cl_io *io, @@ -370,10 +367,9 @@ static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb, struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; struct ccc_object *obj = cl_inode2ccc(inode); - long count = iov_iter_count(iter); - long tot_bytes = 0, result = 0; + ssize_t count = iov_iter_count(iter); + ssize_t tot_bytes = 0, result = 0; struct ll_inode_info *lli = ll_i2info(inode); - unsigned long seg = 0; long size = MAX_DIO_SIZE; int refcheck; @@ -407,63 +403,49 @@ static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb, mutex_lock(&inode->i_mutex); LASSERT(obj->cob_transient_pages == 0); - for (seg = 0; seg < iter->nr_segs; seg++) { - long iov_left = iter->iov[seg].iov_len; - unsigned long user_addr = (unsigned long)iter->iov[seg].iov_base; + while (iov_iter_count(iter)) { + struct page **pages; + size_t offs; + count = min_t(size_t, iov_iter_count(iter), size); if (rw == READ) { if (file_offset >= i_size_read(inode)) break; - if (file_offset + iov_left > i_size_read(inode)) - iov_left = i_size_read(inode) - file_offset; + if (file_offset + count > i_size_read(inode)) + count = i_size_read(inode) - file_offset; } - while (iov_left > 0) { - struct page **pages; - int page_count, max_pages = 0; - long bytes; - - bytes = min(size, iov_left); - page_count = ll_get_user_pages(rw, user_addr, bytes, - &pages, &max_pages); - if (likely(page_count > 0)) { - if (unlikely(page_count < max_pages)) - bytes = page_count << PAGE_CACHE_SHIFT; - result = ll_direct_IO_26_seg(env, io, rw, inode, - file->f_mapping, - bytes, file_offset, - pages, page_count); - ll_free_user_pages(pages, max_pages, rw==READ); - } else if (page_count == 0) { - GOTO(out, result = -EFAULT); - } else { - result = page_count; - } - if (unlikely(result <= 0)) { - /* If we can't allocate a large enough buffer - * for the request, shrink it to a smaller - * PAGE_SIZE multiple and try again. - * We should always be able to kmalloc for a - * page worth of page pointers = 4MB on i386. */ - if (result == -ENOMEM && - size > (PAGE_CACHE_SIZE / sizeof(*pages)) * - PAGE_CACHE_SIZE) { - size = ((((size / 2) - 1) | - ~CFS_PAGE_MASK) + 1) & - CFS_PAGE_MASK; - CDEBUG(D_VFSTRACE,"DIO size now %lu\n", - size); - continue; - } - - GOTO(out, result); + result = iov_iter_get_pages_alloc(iter, &pages, count, &offs); + if (likely(result > 0)) { + int n = (result + offs + PAGE_SIZE - 1) / PAGE_SIZE; + result = ll_direct_IO_26_seg(env, io, rw, inode, + file->f_mapping, + result, file_offset, + pages, n); + ll_free_user_pages(pages, n, rw==READ); + } + if (unlikely(result <= 0)) { + /* If we can't allocate a large enough buffer + * for the request, shrink it to a smaller + * PAGE_SIZE multiple and try again. + * We should always be able to kmalloc for a + * page worth of page pointers = 4MB on i386. */ + if (result == -ENOMEM && + size > (PAGE_CACHE_SIZE / sizeof(*pages)) * + PAGE_CACHE_SIZE) { + size = ((((size / 2) - 1) | + ~CFS_PAGE_MASK) + 1) & + CFS_PAGE_MASK; + CDEBUG(D_VFSTRACE,"DIO size now %lu\n", + size); + continue; } - tot_bytes += result; - file_offset += result; - iov_left -= result; - user_addr += result; + GOTO(out, result); } + iov_iter_advance(iter, result); + tot_bytes += result; + file_offset += result; } out: LASSERT(obj->cob_transient_pages == 0); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 1d34f454989e..b122fe21fea0 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -322,60 +322,37 @@ static const struct nfs_pgio_completion_ops nfs_direct_read_completion_ops = { * handled automatically by nfs_direct_read_result(). Otherwise, if * no requests have been sent, just return an error. */ -static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *desc, - const struct iovec *iov, - loff_t pos, bool uio) -{ - struct nfs_direct_req *dreq = desc->pg_dreq; - struct nfs_open_context *ctx = dreq->ctx; - struct inode *inode = ctx->dentry->d_inode; - unsigned long user_addr = (unsigned long)iov->iov_base; - size_t count = iov->iov_len; - size_t rsize = NFS_SERVER(inode)->rsize; - unsigned int pgbase; - int result; - ssize_t started = 0; - struct page **pagevec = NULL; - unsigned int npages; - - do { - size_t bytes; - int i; - pgbase = user_addr & ~PAGE_MASK; - bytes = min(max_t(size_t, rsize, PAGE_SIZE), count); +static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, + struct iov_iter *iter, + loff_t pos) +{ + struct nfs_pageio_descriptor desc; + struct inode *inode = dreq->inode; + ssize_t result = -EINVAL; + size_t requested_bytes = 0; + size_t rsize = max_t(size_t, NFS_SERVER(inode)->rsize, PAGE_SIZE); - result = -ENOMEM; - npages = nfs_page_array_len(pgbase, bytes); - if (!pagevec) - pagevec = kmalloc(npages * sizeof(struct page *), - GFP_KERNEL); - if (!pagevec) - break; - if (uio) { - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, - npages, 1, 0, pagevec, NULL); - up_read(¤t->mm->mmap_sem); - if (result < 0) - break; - } else { - WARN_ON(npages != 1); - result = get_kernel_page(user_addr, 1, pagevec); - if (WARN_ON(result != 1)) - break; - } + NFS_PROTO(dreq->inode)->read_pageio_init(&desc, dreq->inode, + &nfs_direct_read_completion_ops); + get_dreq(dreq); + desc.pg_dreq = dreq; + atomic_inc(&inode->i_dio_count); - if ((unsigned)result < npages) { - bytes = result * PAGE_SIZE; - if (bytes <= pgbase) { - nfs_direct_release_pages(pagevec, result); - break; - } - bytes -= pgbase; - npages = result; - } + while (iov_iter_count(iter)) { + struct page **pagevec; + size_t bytes; + size_t pgbase; + unsigned npages, i; + result = iov_iter_get_pages_alloc(iter, &pagevec, + rsize, &pgbase); + if (result < 0) + break; + + bytes = result; + iov_iter_advance(iter, bytes); + npages = (result + pgbase + PAGE_SIZE - 1) / PAGE_SIZE; for (i = 0; i < npages; i++) { struct nfs_page *req; unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase); @@ -389,55 +366,21 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de } req->wb_index = pos >> PAGE_SHIFT; req->wb_offset = pos & ~PAGE_MASK; - if (!nfs_pageio_add_request(desc, req)) { - result = desc->pg_error; + if (!nfs_pageio_add_request(&desc, req)) { + result = desc.pg_error; nfs_release_request(req); break; } pgbase = 0; bytes -= req_len; - started += req_len; - user_addr += req_len; + requested_bytes += req_len; pos += req_len; - count -= req_len; dreq->bytes_left -= req_len; } - /* The nfs_page now hold references to these pages */ nfs_direct_release_pages(pagevec, npages); - } while (count != 0 && result >= 0); - - kfree(pagevec); - - if (started) - return started; - return result < 0 ? (ssize_t) result : -EFAULT; -} - -static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, - struct iov_iter *iter, - loff_t pos, bool uio) -{ - struct nfs_pageio_descriptor desc; - struct inode *inode = dreq->inode; - ssize_t result = -EINVAL; - size_t requested_bytes = 0; - unsigned long seg; - - NFS_PROTO(dreq->inode)->read_pageio_init(&desc, dreq->inode, - &nfs_direct_read_completion_ops); - get_dreq(dreq); - desc.pg_dreq = dreq; - atomic_inc(&inode->i_dio_count); - - for (seg = 0; seg < iter->nr_segs; seg++) { - const struct iovec *vec = &iter->iov[seg]; - result = nfs_direct_read_schedule_segment(&desc, vec, pos, uio); + kvfree(pagevec); if (result < 0) break; - requested_bytes += result; - if ((size_t)result < vec->iov_len) - break; - pos += vec->iov_len; } nfs_pageio_complete(&desc); @@ -521,7 +464,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter, dreq->iocb = iocb; NFS_I(inode)->read_io += count; - result = nfs_direct_read_schedule_iovec(dreq, iter, pos, uio); + result = nfs_direct_read_schedule_iovec(dreq, iter, pos); mutex_unlock(&inode->i_mutex); @@ -677,109 +620,6 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode } #endif -/* - * NB: Return the value of the first error return code. Subsequent - * errors after the first one are ignored. - */ -/* - * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE - * operation. If nfs_writedata_alloc() or get_user_pages() fails, - * bail and stop sending more writes. Write length accounting is - * handled automatically by nfs_direct_write_result(). Otherwise, if - * no requests have been sent, just return an error. - */ -static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *desc, - const struct iovec *iov, - loff_t pos, bool uio) -{ - struct nfs_direct_req *dreq = desc->pg_dreq; - struct nfs_open_context *ctx = dreq->ctx; - struct inode *inode = ctx->dentry->d_inode; - unsigned long user_addr = (unsigned long)iov->iov_base; - size_t count = iov->iov_len; - size_t wsize = NFS_SERVER(inode)->wsize; - unsigned int pgbase; - int result; - ssize_t started = 0; - struct page **pagevec = NULL; - unsigned int npages; - - do { - size_t bytes; - int i; - - pgbase = user_addr & ~PAGE_MASK; - bytes = min(max_t(size_t, wsize, PAGE_SIZE), count); - - result = -ENOMEM; - npages = nfs_page_array_len(pgbase, bytes); - if (!pagevec) - pagevec = kmalloc(npages * sizeof(struct page *), GFP_KERNEL); - if (!pagevec) - break; - - if (uio) { - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, - npages, 0, 0, pagevec, NULL); - up_read(¤t->mm->mmap_sem); - if (result < 0) - break; - } else { - WARN_ON(npages != 1); - result = get_kernel_page(user_addr, 0, pagevec); - if (WARN_ON(result != 1)) - break; - } - - if ((unsigned)result < npages) { - bytes = result * PAGE_SIZE; - if (bytes <= pgbase) { - nfs_direct_release_pages(pagevec, result); - break; - } - bytes -= pgbase; - npages = result; - } - - for (i = 0; i < npages; i++) { - struct nfs_page *req; - unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase); - - req = nfs_create_request(dreq->ctx, dreq->inode, - pagevec[i], - pgbase, req_len); - if (IS_ERR(req)) { - result = PTR_ERR(req); - break; - } - nfs_lock_request(req); - req->wb_index = pos >> PAGE_SHIFT; - req->wb_offset = pos & ~PAGE_MASK; - if (!nfs_pageio_add_request(desc, req)) { - result = desc->pg_error; - nfs_unlock_and_release_request(req); - break; - } - pgbase = 0; - bytes -= req_len; - started += req_len; - user_addr += req_len; - pos += req_len; - count -= req_len; - dreq->bytes_left -= req_len; - } - /* The nfs_page now hold references to these pages */ - nfs_direct_release_pages(pagevec, npages); - } while (count != 0 && result >= 0); - - kfree(pagevec); - - if (started) - return started; - return result < 0 ? (ssize_t) result : -EFAULT; -} - static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) { struct nfs_direct_req *dreq = hdr->dreq; @@ -859,15 +699,27 @@ static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = { .completion = nfs_direct_write_completion, }; + +/* + * NB: Return the value of the first error return code. Subsequent + * errors after the first one are ignored. + */ +/* + * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE + * operation. If nfs_writedata_alloc() or get_user_pages() fails, + * bail and stop sending more writes. Write length accounting is + * handled automatically by nfs_direct_write_result(). Otherwise, if + * no requests have been sent, just return an error. + */ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, struct iov_iter *iter, - loff_t pos, bool uio) + loff_t pos) { struct nfs_pageio_descriptor desc; struct inode *inode = dreq->inode; ssize_t result = 0; size_t requested_bytes = 0; - unsigned long seg; + size_t wsize = max_t(size_t, NFS_SERVER(inode)->wsize, PAGE_SIZE); NFS_PROTO(inode)->write_pageio_init(&desc, inode, FLUSH_COND_STABLE, &nfs_direct_write_completion_ops); @@ -875,16 +727,50 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, get_dreq(dreq); atomic_inc(&inode->i_dio_count); - NFS_I(dreq->inode)->write_io += iov_iter_count(iter); - for (seg = 0; seg < iter->nr_segs; seg++) { - const struct iovec *vec = &iter->iov[seg]; - result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio); + NFS_I(inode)->write_io += iov_iter_count(iter); + while (iov_iter_count(iter)) { + struct page **pagevec; + size_t bytes; + size_t pgbase; + unsigned npages, i; + + result = iov_iter_get_pages_alloc(iter, &pagevec, + wsize, &pgbase); if (result < 0) break; - requested_bytes += result; - if ((size_t)result < vec->iov_len) + + bytes = result; + iov_iter_advance(iter, bytes); + npages = (result + pgbase + PAGE_SIZE - 1) / PAGE_SIZE; + for (i = 0; i < npages; i++) { + struct nfs_page *req; + unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase); + + req = nfs_create_request(dreq->ctx, inode, + pagevec[i], + pgbase, req_len); + if (IS_ERR(req)) { + result = PTR_ERR(req); + break; + } + nfs_lock_request(req); + req->wb_index = pos >> PAGE_SHIFT; + req->wb_offset = pos & ~PAGE_MASK; + if (!nfs_pageio_add_request(&desc, req)) { + result = desc.pg_error; + nfs_unlock_and_release_request(req); + break; + } + pgbase = 0; + bytes -= req_len; + requested_bytes += req_len; + pos += req_len; + dreq->bytes_left -= req_len; + } + nfs_direct_release_pages(pagevec, npages); + kvfree(pagevec); + if (result < 0) break; - pos += vec->iov_len; } nfs_pageio_complete(&desc); @@ -985,7 +871,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter, if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - result = nfs_direct_write_schedule_iovec(dreq, iter, pos, uio); + result = nfs_direct_write_schedule_iovec(dreq, iter, pos); if (mapping->nrpages) { invalidate_inode_pages2_range(mapping, diff --git a/include/linux/uio.h b/include/linux/uio.h index 2f8825b06680..4876e9f2a58f 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -73,6 +73,8 @@ void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov, unsigned long nr_segs, size_t count); ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages, size_t maxsize, size_t *start); +ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages, + size_t maxsize, size_t *start); int iov_iter_npages(const struct iov_iter *i, int maxpages); static inline size_t iov_iter_count(struct iov_iter *i) diff --git a/mm/iov_iter.c b/mm/iov_iter.c index 0b677f8f9bad..a5c691c1a283 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i) @@ -263,6 +265,44 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, } EXPORT_SYMBOL(iov_iter_get_pages); +ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, + struct page ***pages, size_t maxsize, + size_t *start) +{ + size_t offset = i->iov_offset; + const struct iovec *iov = i->iov; + size_t len; + unsigned long addr; + void *p; + int n; + int res; + + len = iov->iov_len - offset; + if (len > i->count) + len = i->count; + if (len > maxsize) + len = maxsize; + addr = (unsigned long)iov->iov_base + offset; + len += *start = addr & (PAGE_SIZE - 1); + addr &= ~(PAGE_SIZE - 1); + n = (len + PAGE_SIZE - 1) / PAGE_SIZE; + + p = kmalloc(n * sizeof(struct page *), GFP_KERNEL); + if (!p) + p = vmalloc(n * sizeof(struct page *)); + if (!p) + return -ENOMEM; + + res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p); + if (unlikely(res < 0)) { + kvfree(p); + return res; + } + *pages = p; + return (res == n ? len : res * PAGE_SIZE) - *start; +} +EXPORT_SYMBOL(iov_iter_get_pages_alloc); + int iov_iter_npages(const struct iov_iter *i, int maxpages) { size_t offset = i->iov_offset; -- cgit v1.2.3 From 0c949334a9e2581646c6ff0d1470a805b1e5be99 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 22 Mar 2014 06:51:37 -0400 Subject: iov_iter_truncate() Now It Can Be Done(tm) - we don't need to do iov_shorten() in generic_file_direct_write() anymore, now that all ->direct_IO() instances are converted to proper iov_iter methods and honour iter->count and iter->iov_offset properly. Get rid of count/ocount arguments of generic_file_direct_write(), while we are at it. Signed-off-by: Al Viro --- fs/btrfs/file.c | 17 ++++++++--------- fs/fuse/file.c | 18 ++++++++---------- fs/ocfs2/file.c | 10 +++++----- fs/xfs/xfs_file.c | 9 +++++---- include/linux/fs.h | 3 +-- include/linux/uio.h | 6 ++++++ mm/filemap.c | 19 +++++++------------ 7 files changed, 40 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index f8cee205618a..ea63a51c148c 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1659,8 +1659,7 @@ again: static ssize_t __btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from, - loff_t pos, - size_t count, size_t ocount) + loff_t pos) { struct file *file = iocb->ki_filp; ssize_t written; @@ -1668,9 +1667,9 @@ static ssize_t __btrfs_direct_write(struct kiocb *iocb, loff_t endbyte; int err; - written = generic_file_direct_write(iocb, from, pos, count, ocount); + written = generic_file_direct_write(iocb, from, pos); - if (written < 0 || written == count) + if (written < 0 || !iov_iter_count(from)) return written; pos += written; @@ -1720,13 +1719,14 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, u64 end_pos; ssize_t num_written = 0; ssize_t err = 0; - size_t count, ocount; + size_t count; bool sync = (file->f_flags & O_DSYNC) || IS_SYNC(file->f_mapping->host); struct iov_iter i; mutex_lock(&inode->i_mutex); - count = ocount = iov_length(iov, nr_segs); + count = iov_length(iov, nr_segs); + iov_iter_init(&i, WRITE, iov, nr_segs, count); current->backing_dev_info = inode->i_mapping->backing_dev_info; err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); @@ -1740,7 +1740,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, goto out; } - iov_iter_init(&i, WRITE, iov, nr_segs, count); + iov_iter_truncate(&i, count); err = file_remove_suid(file); if (err) { @@ -1783,8 +1783,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, atomic_inc(&BTRFS_I(inode)->sync_writers); if (unlikely(file->f_flags & O_DIRECT)) { - num_written = __btrfs_direct_write(iocb, &i, - pos, count, ocount); + num_written = __btrfs_direct_write(iocb, &i, pos); } else { num_written = __btrfs_buffered_write(file, &i, pos); if (num_written > 0) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 7026014717bc..66d2d5de19d2 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1188,8 +1188,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; - size_t count = 0; - size_t ocount = 0; + size_t count; ssize_t written = 0; ssize_t written_buffered = 0; struct inode *inode = mapping->host; @@ -1208,7 +1207,8 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, WARN_ON(iocb->ki_pos != pos); - count = ocount = iov_length(iov, nr_segs); + count = iov_length(iov, nr_segs); + iov_iter_init(&i, WRITE, iov, nr_segs, count); mutex_lock(&inode->i_mutex); /* We can write back this queue in page reclaim */ @@ -1217,11 +1217,11 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); if (err) goto out; - iov_iter_init(&i, WRITE, iov, nr_segs, count); if (count == 0) goto out; + iov_iter_truncate(&i, count); err = file_remove_suid(file); if (err) goto out; @@ -1231,8 +1231,8 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, goto out; if (file->f_flags & O_DIRECT) { - written = generic_file_direct_write(iocb, &i, pos, count, ocount); - if (written < 0 || written == count) + written = generic_file_direct_write(iocb, &i, pos); + if (written < 0 || !iov_iter_count(&i)) goto out; pos += written; @@ -1469,8 +1469,7 @@ static ssize_t __fuse_direct_write(struct fuse_io_priv *io, res = generic_write_checks(file, ppos, &count, 0); if (!res) { - if (iter->count > count) - iter->count = count; + iov_iter_truncate(iter, count); res = fuse_direct_io(io, iter, ppos, FUSE_DIO_WRITE); } @@ -2896,8 +2895,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, if (offset >= i_size) return 0; count = min_t(loff_t, count, fuse_round_up(i_size - offset)); - if (iter->count > count) - iter->count = count; + iov_iter_truncate(iter, count); } io = kmalloc(sizeof(struct fuse_io_priv), GFP_KERNEL); diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 9ce9ed7615c1..06b6a16d9776 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2241,7 +2241,6 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, int ret, direct_io, appending, rw_level, have_alloc_sem = 0; int can_do_direct, has_refcount = 0; ssize_t written = 0; - size_t ocount; /* original count */ size_t count; /* after file limit checks */ loff_t old_size, *ppos = &iocb->ki_pos; u32 old_clusters; @@ -2253,6 +2252,9 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, int unaligned_dio = 0; struct iov_iter from; + count = iov_length(iov, nr_segs); + iov_iter_init(&from, WRITE, iov, nr_segs, count); + trace_ocfs2_file_aio_write(inode, file, file->f_path.dentry, (unsigned long long)OCFS2_I(inode)->ip_blkno, file->f_path.dentry->d_name.len, @@ -2355,16 +2357,14 @@ relock: /* communicate with ocfs2_dio_end_io */ ocfs2_iocb_set_rw_locked(iocb, rw_level); - count = ocount = iov_length(iov, nr_segs); ret = generic_write_checks(file, ppos, &count, S_ISBLK(inode->i_mode)); if (ret) goto out_dio; - iov_iter_init(&from, WRITE, iov, nr_segs, count); + iov_iter_truncate(&from, count); if (direct_io) { - written = generic_file_direct_write(iocb, &from, *ppos, - count, ocount); + written = generic_file_direct_write(iocb, &from, *ppos); if (written < 0) { ret = written; goto out_dio; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 762bb3e148a6..c997aa2751b2 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -626,7 +626,7 @@ xfs_file_dio_aio_write( const struct iovec *iovp, unsigned long nr_segs, loff_t pos, - size_t ocount) + size_t count) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -634,7 +634,6 @@ xfs_file_dio_aio_write( struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; ssize_t ret = 0; - size_t count = ocount; int unaligned_io = 0; int iolock; struct xfs_buftarg *target = XFS_IS_REALTIME_INODE(ip) ? @@ -645,6 +644,8 @@ xfs_file_dio_aio_write( if ((pos | count) & target->bt_logical_sectormask) return -XFS_ERROR(EINVAL); + iov_iter_init(&from, WRITE, iovp, nr_segs, count); + /* "unaligned" here means not aligned to a filesystem block */ if ((pos & mp->m_blockmask) || ((pos + count) & mp->m_blockmask)) unaligned_io = 1; @@ -676,6 +677,7 @@ xfs_file_dio_aio_write( ret = xfs_file_aio_write_checks(file, &pos, &count, &iolock); if (ret) goto out; + iov_iter_truncate(&from, count); if (mapping->nrpages) { ret = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, @@ -697,8 +699,7 @@ xfs_file_dio_aio_write( } trace_xfs_file_direct_write(ip, count, iocb->ki_pos, 0); - iov_iter_init(&from, WRITE, iovp, nr_segs, count); - ret = generic_file_direct_write(iocb, &from, pos, count, ocount); + ret = generic_file_direct_write(iocb, &from, pos); out: xfs_rw_iunlock(ip, iolock); diff --git a/include/linux/fs.h b/include/linux/fs.h index d096ebc7f348..8153396d19b4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2407,8 +2407,7 @@ extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsig extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long); extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t); -extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, - loff_t, size_t, size_t); +extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, loff_t); extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t); extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); diff --git a/include/linux/uio.h b/include/linux/uio.h index 4876e9f2a58f..532f59d0adbb 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -82,6 +82,12 @@ static inline size_t iov_iter_count(struct iov_iter *i) return i->count; } +static inline void iov_iter_truncate(struct iov_iter *i, size_t count) +{ + if (i->count > count) + i->count = count; +} + int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len); int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len); diff --git a/mm/filemap.c b/mm/filemap.c index 3aeaf2df4135..c0404b763a17 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2345,8 +2345,7 @@ int pagecache_write_end(struct file *file, struct address_space *mapping, EXPORT_SYMBOL(pagecache_write_end); ssize_t -generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from, - loff_t pos, size_t count, size_t ocount) +generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -2356,10 +2355,7 @@ generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from, pgoff_t end; struct iov_iter data; - if (count != ocount) - from->nr_segs = iov_shorten((struct iovec *)from->iov, from->nr_segs, count); - - write_len = iov_length(from->iov, from->nr_segs); + write_len = iov_iter_count(from); end = (pos + write_len - 1) >> PAGE_CACHE_SHIFT; written = filemap_write_and_wait_range(mapping, pos, pos + write_len - 1); @@ -2568,7 +2564,6 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, { struct file *file = iocb->ki_filp; struct address_space * mapping = file->f_mapping; - size_t ocount; /* original count */ size_t count; /* after file limit checks */ struct inode *inode = mapping->host; loff_t pos = iocb->ki_pos; @@ -2577,7 +2572,8 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, ssize_t status; struct iov_iter from; - count = ocount = iov_length(iov, nr_segs); + count = iov_length(iov, nr_segs); + iov_iter_init(&from, WRITE, iov, nr_segs, count); /* We can write back this queue in page reclaim */ current->backing_dev_info = mapping->backing_dev_info; @@ -2588,6 +2584,8 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (count == 0) goto out; + iov_iter_truncate(&from, count); + err = file_remove_suid(file); if (err) goto out; @@ -2596,14 +2594,11 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (err) goto out; - iov_iter_init(&from, WRITE, iov, nr_segs, count); - /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ if (unlikely(file->f_flags & O_DIRECT)) { loff_t endbyte; - written = generic_file_direct_write(iocb, &from, pos, - count, ocount); + written = generic_file_direct_write(iocb, &from, pos); if (written < 0 || written == count) goto out; -- cgit v1.2.3 From 7f7f25e82d54870df24d415a7007fbd327da027b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 11 Feb 2014 17:49:24 -0500 Subject: replace checking for ->read/->aio_read presence with check in ->f_mode Since we are about to introduce new methods (read_iter/write_iter), the tests in a bunch of places would have to grow inconveniently. Check once (at open() time) and store results in ->f_mode as FMODE_CAN_READ and FMODE_CAN_WRITE resp. It might end up being a temporary measure - once everything switches from ->aio_{read,write} to ->{read,write}_iter it might make sense to return to open-coded checks. We'll see... Signed-off-by: Al Viro --- drivers/mtd/nand/nandsim.c | 4 ++-- drivers/usb/gadget/storage_common.c | 4 ++-- fs/file_table.c | 4 ++++ fs/open.c | 4 ++++ fs/read_write.c | 14 +++++++------- include/linux/fs.h | 4 ++++ 6 files changed, 23 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 42e8a770e631..4f0d83648e5a 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -575,12 +575,12 @@ static int alloc_device(struct nandsim *ns) cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); if (IS_ERR(cfile)) return PTR_ERR(cfile); - if (!cfile->f_op->read && !cfile->f_op->aio_read) { + if (!(cfile->f_mode & FMODE_CAN_READ)) { NS_ERR("alloc_device: cache file not readable\n"); err = -EINVAL; goto err_close; } - if (!cfile->f_op->write && !cfile->f_op->aio_write) { + if (!(cfile->f_mode & FMODE_CAN_WRITE)) { NS_ERR("alloc_device: cache file not writeable\n"); err = -EINVAL; goto err_close; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index ec20a1f50c2d..a8898df131ed 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -220,11 +220,11 @@ int fsg_lun_open(struct fsg_lun *curlun, const char *filename) * If we can't read the file, it's no good. * If we can't write the file, use it read-only. */ - if (!(filp->f_op->read || filp->f_op->aio_read)) { + if (!(filp->f_mode & FMODE_CAN_READ)) { LINFO(curlun, "file not readable: %s\n", filename); goto out; } - if (!(filp->f_op->write || filp->f_op->aio_write)) + if (!(filp->f_mode & FMODE_CAN_WRITE)) ro = 1; size = i_size_read(inode->i_mapping->host); diff --git a/fs/file_table.c b/fs/file_table.c index a374f5033e97..be73cbc48c12 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -175,6 +175,10 @@ struct file *alloc_file(struct path *path, fmode_t mode, file->f_path = *path; file->f_inode = path->dentry->d_inode; file->f_mapping = path->dentry->d_inode->i_mapping; + if ((mode & FMODE_READ) && likely(fop->read || fop->aio_read)) + mode |= FMODE_CAN_READ; + if ((mode & FMODE_WRITE) && likely(fop->write || fop->aio_write)) + mode |= FMODE_CAN_WRITE; file->f_mode = mode; file->f_op = fop; if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) diff --git a/fs/open.c b/fs/open.c index 9d64679cec73..39d3d0468ee6 100644 --- a/fs/open.c +++ b/fs/open.c @@ -725,6 +725,10 @@ static int do_dentry_open(struct file *f, } if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) i_readcount_inc(inode); + if ((f->f_mode & FMODE_READ) && likely(f->f_op->read || f->f_op->aio_read)) + f->f_mode |= FMODE_CAN_READ; + if ((f->f_mode & FMODE_WRITE) && likely(f->f_op->write || f->f_op->aio_write)) + f->f_mode |= FMODE_CAN_WRITE; f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); diff --git a/fs/read_write.c b/fs/read_write.c index 31c6efa43183..d29d2a361d2c 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -396,7 +396,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) if (!(file->f_mode & FMODE_READ)) return -EBADF; - if (!file->f_op->read && !file->f_op->aio_read) + if (!(file->f_mode & FMODE_CAN_READ)) return -EINVAL; if (unlikely(!access_ok(VERIFY_WRITE, buf, count))) return -EFAULT; @@ -445,7 +445,7 @@ ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t const char __user *p; ssize_t ret; - if (!file->f_op->write && !file->f_op->aio_write) + if (!(file->f_mode & FMODE_CAN_WRITE)) return -EINVAL; old_fs = get_fs(); @@ -472,7 +472,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ if (!(file->f_mode & FMODE_WRITE)) return -EBADF; - if (!file->f_op->write && !file->f_op->aio_write) + if (!(file->f_mode & FMODE_CAN_WRITE)) return -EINVAL; if (unlikely(!access_ok(VERIFY_READ, buf, count))) return -EFAULT; @@ -785,7 +785,7 @@ ssize_t vfs_readv(struct file *file, const struct iovec __user *vec, { if (!(file->f_mode & FMODE_READ)) return -EBADF; - if (!file->f_op->aio_read && !file->f_op->read) + if (!(file->f_mode & FMODE_CAN_READ)) return -EINVAL; return do_readv_writev(READ, file, vec, vlen, pos); @@ -798,7 +798,7 @@ ssize_t vfs_writev(struct file *file, const struct iovec __user *vec, { if (!(file->f_mode & FMODE_WRITE)) return -EBADF; - if (!file->f_op->aio_write && !file->f_op->write) + if (!(file->f_mode & FMODE_CAN_WRITE)) return -EINVAL; return do_readv_writev(WRITE, file, vec, vlen, pos); @@ -964,7 +964,7 @@ static size_t compat_readv(struct file *file, goto out; ret = -EINVAL; - if (!file->f_op->aio_read && !file->f_op->read) + if (!(file->f_mode & FMODE_CAN_READ)) goto out; ret = compat_do_readv_writev(READ, file, vec, vlen, pos); @@ -1041,7 +1041,7 @@ static size_t compat_writev(struct file *file, goto out; ret = -EINVAL; - if (!file->f_op->aio_write && !file->f_op->write) + if (!(file->f_mode & FMODE_CAN_WRITE)) goto out; ret = compat_do_readv_writev(WRITE, file, vec, vlen, pos); diff --git a/include/linux/fs.h b/include/linux/fs.h index 8153396d19b4..75eb71357adf 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -128,6 +128,10 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, #define FMODE_ATOMIC_POS ((__force fmode_t)0x8000) /* Write access to underlying fs */ #define FMODE_WRITER ((__force fmode_t)0x10000) +/* Has read method(s) */ +#define FMODE_CAN_READ ((__force fmode_t)0x20000) +/* Has write method(s) */ +#define FMODE_CAN_WRITE ((__force fmode_t)0x40000) /* File was opened by fanotify and shouldn't generate fanotify events */ #define FMODE_NONOTIFY ((__force fmode_t)0x1000000) -- cgit v1.2.3 From 293bc9822fa9b3c9d4b7893bcb241e085580771a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 11 Feb 2014 18:37:41 -0500 Subject: new methods: ->read_iter() and ->write_iter() Beginning to introduce those. Just the callers for now, and it's clumsier than it'll eventually become; once we finish converting aio_read and aio_write instances, the things will get nicer. For now, these guys are in parallel to ->aio_read() and ->aio_write(); they take iocb and iov_iter, with everything in iov_iter already validated. File offset is passed in iocb->ki_pos, iov/nr_segs - in iov_iter. Main concerns in that series are stack footprint and ability to split the damn thing cleanly. [fix from Peter Ujfalusi folded] Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 2 + Documentation/filesystems/vfs.txt | 10 ++++- fs/aio.c | 14 +++++- fs/file_table.c | 6 ++- fs/open.c | 6 ++- fs/read_write.c | 90 ++++++++++++++++++++++++++++++++++++--- include/linux/fs.h | 6 +++ 7 files changed, 121 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 9b0d5a33c8bf..b18dd1779029 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -430,6 +430,8 @@ prototypes: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); + ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); + ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 1846374a5add..a1d0d7a30165 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -806,6 +806,8 @@ struct file_operations { ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); + ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); + ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); @@ -836,11 +838,15 @@ otherwise noted. read: called by read(2) and related system calls - aio_read: called by io_submit(2) and other asynchronous I/O operations + aio_read: vectored, possibly asynchronous read + + read_iter: possibly asynchronous read with iov_iter as destination write: called by write(2) and related system calls - aio_write: called by io_submit(2) and other asynchronous I/O operations + aio_write: vectored, possibly asynchronous write + + write_iter: possibly asynchronous write with iov_iter as source iterate: called when the VFS needs to read the directory contents diff --git a/fs/aio.c b/fs/aio.c index a0ed6c7d2cd2..56b28607c32d 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1241,6 +1241,7 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) typedef ssize_t (aio_rw_op)(struct kiocb *, const struct iovec *, unsigned long, loff_t); +typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *); static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb, int rw, char __user *buf, @@ -1298,7 +1299,9 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, int rw; fmode_t mode; aio_rw_op *rw_op; + rw_iter_op *iter_op; struct iovec inline_vec, *iovec = &inline_vec; + struct iov_iter iter; switch (opcode) { case IOCB_CMD_PREAD: @@ -1306,6 +1309,7 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, mode = FMODE_READ; rw = READ; rw_op = file->f_op->aio_read; + iter_op = file->f_op->read_iter; goto rw_common; case IOCB_CMD_PWRITE: @@ -1313,12 +1317,13 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, mode = FMODE_WRITE; rw = WRITE; rw_op = file->f_op->aio_write; + iter_op = file->f_op->write_iter; goto rw_common; rw_common: if (unlikely(!(file->f_mode & mode))) return -EBADF; - if (!rw_op) + if (!rw_op && !iter_op) return -EINVAL; ret = (opcode == IOCB_CMD_PREADV || @@ -1347,7 +1352,12 @@ rw_common: if (rw == WRITE) file_start_write(file); - ret = rw_op(req, iovec, nr_segs, req->ki_pos); + if (iter_op) { + iov_iter_init(&iter, rw, iovec, nr_segs, req->ki_nbytes); + ret = iter_op(req, &iter); + } else { + ret = rw_op(req, iovec, nr_segs, req->ki_pos); + } if (rw == WRITE) file_end_write(file); diff --git a/fs/file_table.c b/fs/file_table.c index be73cbc48c12..f8cc881fbbfb 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -175,9 +175,11 @@ struct file *alloc_file(struct path *path, fmode_t mode, file->f_path = *path; file->f_inode = path->dentry->d_inode; file->f_mapping = path->dentry->d_inode->i_mapping; - if ((mode & FMODE_READ) && likely(fop->read || fop->aio_read)) + if ((mode & FMODE_READ) && + likely(fop->read || fop->aio_read || fop->read_iter)) mode |= FMODE_CAN_READ; - if ((mode & FMODE_WRITE) && likely(fop->write || fop->aio_write)) + if ((mode & FMODE_WRITE) && + likely(fop->write || fop->aio_write || fop->write_iter)) mode |= FMODE_CAN_WRITE; file->f_mode = mode; file->f_op = fop; diff --git a/fs/open.c b/fs/open.c index 39d3d0468ee6..36662d036237 100644 --- a/fs/open.c +++ b/fs/open.c @@ -725,9 +725,11 @@ static int do_dentry_open(struct file *f, } if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) i_readcount_inc(inode); - if ((f->f_mode & FMODE_READ) && likely(f->f_op->read || f->f_op->aio_read)) + if ((f->f_mode & FMODE_READ) && + likely(f->f_op->read || f->f_op->aio_read || f->f_op->read_iter)) f->f_mode |= FMODE_CAN_READ; - if ((f->f_mode & FMODE_WRITE) && likely(f->f_op->write || f->f_op->aio_write)) + if ((f->f_mode & FMODE_WRITE) && + likely(f->f_op->write || f->f_op->aio_write || f->f_op->write_iter)) f->f_mode |= FMODE_CAN_WRITE; f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); diff --git a/fs/read_write.c b/fs/read_write.c index d29d2a361d2c..fe2f9d5e3536 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -25,6 +25,7 @@ typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *); typedef ssize_t (*iov_fn_t)(struct kiocb *, const struct iovec *, unsigned long, loff_t); +typedef ssize_t (*iter_fn_t)(struct kiocb *, struct iov_iter *); const struct file_operations generic_ro_fops = { .llseek = generic_file_llseek, @@ -390,6 +391,27 @@ ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pp EXPORT_SYMBOL(do_sync_read); +ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) +{ + struct iovec iov = { .iov_base = buf, .iov_len = len }; + struct kiocb kiocb; + struct iov_iter iter; + ssize_t ret; + + init_sync_kiocb(&kiocb, filp); + kiocb.ki_pos = *ppos; + kiocb.ki_nbytes = len; + iov_iter_init(&iter, READ, &iov, 1, len); + + ret = filp->f_op->read_iter(&kiocb, &iter); + if (-EIOCBQUEUED == ret) + ret = wait_on_sync_kiocb(&kiocb); + *ppos = kiocb.ki_pos; + return ret; +} + +EXPORT_SYMBOL(new_sync_read); + ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { ssize_t ret; @@ -406,8 +428,10 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) count = ret; if (file->f_op->read) ret = file->f_op->read(file, buf, count, pos); - else + else if (file->f_op->aio_read) ret = do_sync_read(file, buf, count, pos); + else + ret = new_sync_read(file, buf, count, pos); if (ret > 0) { fsnotify_access(file); add_rchar(current, ret); @@ -439,6 +463,27 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof EXPORT_SYMBOL(do_sync_write); +ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) +{ + struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; + struct kiocb kiocb; + struct iov_iter iter; + ssize_t ret; + + init_sync_kiocb(&kiocb, filp); + kiocb.ki_pos = *ppos; + kiocb.ki_nbytes = len; + iov_iter_init(&iter, WRITE, &iov, 1, len); + + ret = filp->f_op->write_iter(&kiocb, &iter); + if (-EIOCBQUEUED == ret) + ret = wait_on_sync_kiocb(&kiocb); + *ppos = kiocb.ki_pos; + return ret; +} + +EXPORT_SYMBOL(new_sync_write); + ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) { mm_segment_t old_fs; @@ -455,8 +500,10 @@ ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t count = MAX_RW_COUNT; if (file->f_op->write) ret = file->f_op->write(file, p, count, pos); - else + else if (file->f_op->aio_write) ret = do_sync_write(file, p, count, pos); + else + ret = new_sync_write(file, p, count, pos); set_fs(old_fs); if (ret > 0) { fsnotify_modify(file); @@ -483,8 +530,10 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ file_start_write(file); if (file->f_op->write) ret = file->f_op->write(file, buf, count, pos); - else + else if (file->f_op->aio_write) ret = do_sync_write(file, buf, count, pos); + else + ret = new_sync_write(file, buf, count, pos); if (ret > 0) { fsnotify_modify(file); add_wchar(current, ret); @@ -601,6 +650,25 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to) } EXPORT_SYMBOL(iov_shorten); +static ssize_t do_iter_readv_writev(struct file *filp, int rw, const struct iovec *iov, + unsigned long nr_segs, size_t len, loff_t *ppos, iter_fn_t fn) +{ + struct kiocb kiocb; + struct iov_iter iter; + ssize_t ret; + + init_sync_kiocb(&kiocb, filp); + kiocb.ki_pos = *ppos; + kiocb.ki_nbytes = len; + + iov_iter_init(&iter, rw, iov, nr_segs, len); + ret = fn(&kiocb, &iter); + if (ret == -EIOCBQUEUED) + ret = wait_on_sync_kiocb(&kiocb); + *ppos = kiocb.ki_pos; + return ret; +} + static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn) { @@ -738,6 +806,7 @@ static ssize_t do_readv_writev(int type, struct file *file, ssize_t ret; io_fn_t fn; iov_fn_t fnv; + iter_fn_t iter_fn; ret = rw_copy_check_uvector(type, uvector, nr_segs, ARRAY_SIZE(iovstack), iovstack, &iov); @@ -753,13 +822,18 @@ static ssize_t do_readv_writev(int type, struct file *file, if (type == READ) { fn = file->f_op->read; fnv = file->f_op->aio_read; + iter_fn = file->f_op->read_iter; } else { fn = (io_fn_t)file->f_op->write; fnv = file->f_op->aio_write; + iter_fn = file->f_op->write_iter; file_start_write(file); } - if (fnv) + if (iter_fn) + ret = do_iter_readv_writev(file, type, iov, nr_segs, tot_len, + pos, iter_fn); + else if (fnv) ret = do_sync_readv_writev(file, iov, nr_segs, tot_len, pos, fnv); else @@ -912,6 +986,7 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, ssize_t ret; io_fn_t fn; iov_fn_t fnv; + iter_fn_t iter_fn; ret = compat_rw_copy_check_uvector(type, uvector, nr_segs, UIO_FASTIOV, iovstack, &iov); @@ -927,13 +1002,18 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, if (type == READ) { fn = file->f_op->read; fnv = file->f_op->aio_read; + iter_fn = file->f_op->read_iter; } else { fn = (io_fn_t)file->f_op->write; fnv = file->f_op->aio_write; + iter_fn = file->f_op->write_iter; file_start_write(file); } - if (fnv) + if (iter_fn) + ret = do_iter_readv_writev(file, type, iov, nr_segs, tot_len, + pos, iter_fn); + else if (fnv) ret = do_sync_readv_writev(file, iov, nr_segs, tot_len, pos, fnv); else diff --git a/include/linux/fs.h b/include/linux/fs.h index 75eb71357adf..17535e0a4547 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1451,6 +1451,8 @@ struct block_device_operations; #define HAVE_COMPAT_IOCTL 1 #define HAVE_UNLOCKED_IOCTL 1 +struct iov_iter; + struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); @@ -1458,6 +1460,8 @@ struct file_operations { ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); + ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); + ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); @@ -2415,6 +2419,8 @@ extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, loff extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t); extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); +extern ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); +extern ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); /* fs/block_dev.c */ extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, -- cgit v1.2.3 From 8174202b34c30e0c07231bf63f18ab29af634f0b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 3 Apr 2014 03:17:43 -0400 Subject: write_iter variants of {__,}generic_file_aio_write() Signed-off-by: Al Viro --- fs/9p/vfs_file.c | 8 +++---- fs/adfs/file.c | 4 ++-- fs/affs/file.c | 4 ++-- fs/bfs/file.c | 4 ++-- fs/ecryptfs/file.c | 4 ++-- fs/exofs/file.c | 4 ++-- fs/ext2/file.c | 4 ++-- fs/ext3/file.c | 4 ++-- fs/f2fs/file.c | 4 ++-- fs/fat/file.c | 4 ++-- fs/hfs/inode.c | 4 ++-- fs/hfsplus/inode.c | 4 ++-- fs/hostfs/hostfs_kern.c | 4 ++-- fs/hpfs/file.c | 4 ++-- fs/jffs2/file.c | 4 ++-- fs/jfs/file.c | 4 ++-- fs/logfs/file.c | 4 ++-- fs/minix/file.c | 4 ++-- fs/nilfs2/file.c | 4 ++-- fs/omfs/file.c | 4 ++-- fs/ramfs/file-mmu.c | 4 ++-- fs/ramfs/file-nommu.c | 4 ++-- fs/reiserfs/file.c | 4 ++-- fs/sysv/file.c | 4 ++-- fs/ufs/file.c | 4 ++-- include/linux/fs.h | 2 ++ mm/filemap.c | 61 ++++++++++++++++++++++++++++++------------------- mm/shmem.c | 4 ++-- mm/vmscan.c | 2 +- 29 files changed, 94 insertions(+), 79 deletions(-) (limited to 'include') diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 47e0597d1e9b..b9b5f979a2ca 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -763,7 +763,7 @@ err_out: buff_write: mutex_unlock(&inode->i_mutex); - return do_sync_write(filp, data, count, offsetp); + return new_sync_write(filp, data, count, offsetp); } /** @@ -781,7 +781,7 @@ v9fs_cached_file_write(struct file *filp, const char __user * data, if (filp->f_flags & O_DIRECT) return v9fs_direct_write(filp, data, count, offset); - return do_sync_write(filp, data, count, offset); + return new_sync_write(filp, data, count, offset); } @@ -851,7 +851,7 @@ const struct file_operations v9fs_cached_file_operations = { .read = v9fs_cached_file_read, .write = v9fs_cached_file_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .open = v9fs_file_open, .release = v9fs_dir_release, .lock = v9fs_file_lock, @@ -864,7 +864,7 @@ const struct file_operations v9fs_cached_file_operations_dotl = { .read = v9fs_cached_file_read, .write = v9fs_cached_file_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .open = v9fs_file_open, .release = v9fs_dir_release, .lock = v9fs_file_lock_dotl, diff --git a/fs/adfs/file.c b/fs/adfs/file.c index 3bfc9efa29b4..07c9edce5aa7 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -27,8 +27,8 @@ const struct file_operations adfs_file_operations = { .read_iter = generic_file_read_iter, .mmap = generic_file_mmap, .fsync = generic_file_fsync, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .splice_read = generic_file_splice_read, }; diff --git a/fs/affs/file.c b/fs/affs/file.c index 982853f17afc..9df23175e28b 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -29,8 +29,8 @@ const struct file_operations affs_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .open = affs_file_open, .release = affs_file_release, diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 0aa788892f93..e7f88ace1a25 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -25,8 +25,8 @@ const struct file_operations bfs_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, }; diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index b32827c917e1..db0fad3269c0 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -353,8 +353,8 @@ const struct file_operations ecryptfs_main_fops = { .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = ecryptfs_read_update_atime, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .iterate = ecryptfs_readdir, .unlocked_ioctl = ecryptfs_unlocked_ioctl, #ifdef CONFIG_COMPAT diff --git a/fs/exofs/file.c b/fs/exofs/file.c index 90d394da7471..5b7f6be5a2d5 100644 --- a/fs/exofs/file.c +++ b/fs/exofs/file.c @@ -68,9 +68,9 @@ static int exofs_flush(struct file *file, fl_owner_t id) const struct file_operations exofs_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .open = generic_file_open, .release = exofs_release_file, diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 407305072597..970c6aca15cc 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -63,9 +63,9 @@ int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync) const struct file_operations ext2_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .unlocked_ioctl = ext2_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = ext2_compat_ioctl, diff --git a/fs/ext3/file.c b/fs/ext3/file.c index 5439d2f0141b..c833b1226d4d 100644 --- a/fs/ext3/file.c +++ b/fs/ext3/file.c @@ -51,9 +51,9 @@ static int ext3_release_file (struct inode * inode, struct file * filp) const struct file_operations ext3_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .unlocked_ioctl = ext3_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = ext3_compat_ioctl, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 0e01fb0bc97c..22f4900dd8eb 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -680,9 +680,9 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) const struct file_operations f2fs_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .open = generic_file_open, .mmap = f2fs_file_mmap, .fsync = f2fs_sync_file, diff --git a/fs/fat/file.c b/fs/fat/file.c index 29285e990c90..85f79a89e747 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -171,9 +171,9 @@ int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) const struct file_operations fat_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .release = fat_file_release, .unlocked_ioctl = fat_generic_ioctl, diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 6d4055aff109..d0929bc81782 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -676,8 +676,8 @@ static const struct file_operations hfs_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, .fsync = hfs_file_fsync, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index cccc89e47cb6..0cf786f2d046 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -343,8 +343,8 @@ static const struct file_operations hfsplus_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, .fsync = hfsplus_file_fsync, diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index ce0005d8ffeb..bb529f3b7f2b 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -381,8 +381,8 @@ static const struct file_operations hostfs_file_fops = { .read = new_sync_read, .splice_read = generic_file_splice_read, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, - .write = do_sync_write, + .write_iter = generic_file_write_iter, + .write = new_sync_write, .mmap = generic_file_mmap, .open = hostfs_file_open, .release = hostfs_file_release, diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index bacb478a4990..7f54e5f76cec 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -199,8 +199,8 @@ const struct file_operations hpfs_file_ops = .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .release = hpfs_file_release, .fsync = hpfs_file_fsync, diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 9192127d591c..64989ca9ba90 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -53,8 +53,8 @@ const struct file_operations jffs2_file_operations = .open = generic_file_open, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .unlocked_ioctl=jffs2_ioctl, .mmap = generic_file_readonly_mmap, .fsync = jffs2_fsync, diff --git a/fs/jfs/file.c b/fs/jfs/file.c index a5d8299b2208..cc744ecaf51f 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -151,10 +151,10 @@ const struct inode_operations jfs_file_inode_operations = { const struct file_operations jfs_file_operations = { .open = jfs_open, .llseek = generic_file_llseek, - .write = do_sync_write, + .write = new_sync_write, .read = new_sync_read, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, diff --git a/fs/logfs/file.c b/fs/logfs/file.c index 1ca8026dc664..8538752df2f6 100644 --- a/fs/logfs/file.c +++ b/fs/logfs/file.c @@ -265,14 +265,14 @@ const struct inode_operations logfs_reg_iops = { const struct file_operations logfs_reg_fops = { .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .fsync = logfs_fsync, .unlocked_ioctl = logfs_ioctl, .llseek = generic_file_llseek, .mmap = generic_file_readonly_mmap, .open = generic_file_open, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, }; const struct address_space_operations logfs_reg_aops = { diff --git a/fs/minix/file.c b/fs/minix/file.c index 607b47145325..a967de085ac0 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -16,8 +16,8 @@ const struct file_operations minix_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .fsync = generic_file_fsync, .splice_read = generic_file_splice_read, diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c index dcb1b0e8b435..24978153c0c4 100644 --- a/fs/nilfs2/file.c +++ b/fs/nilfs2/file.c @@ -153,9 +153,9 @@ static int nilfs_file_mmap(struct file *file, struct vm_area_struct *vma) const struct file_operations nilfs_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .unlocked_ioctl = nilfs_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = nilfs_compat_ioctl, diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 3bf28da9f0df..902e88527fce 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -338,9 +338,9 @@ static sector_t omfs_bmap(struct address_space *mapping, sector_t block) const struct file_operations omfs_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .fsync = generic_file_fsync, .splice_read = generic_file_splice_read, diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c index 30ffb367bc0b..6ea0b9718a9d 100644 --- a/fs/ramfs/file-mmu.c +++ b/fs/ramfs/file-mmu.c @@ -33,8 +33,8 @@ const struct file_operations ramfs_file_operations = { .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .fsync = noop_fsync, .splice_read = generic_file_splice_read, diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 416db04f8464..9ed420f8f3ca 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -39,8 +39,8 @@ const struct file_operations ramfs_file_operations = { .get_unmapped_area = ramfs_nommu_get_unmapped_area, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .fsync = noop_fsync, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 7592d681fd8c..7c8ecd6468db 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -236,7 +236,7 @@ int reiserfs_commit_page(struct inode *inode, struct page *page, const struct file_operations reiserfs_file_operations = { .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .unlocked_ioctl = reiserfs_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = reiserfs_compat_ioctl, @@ -246,7 +246,7 @@ const struct file_operations reiserfs_file_operations = { .release = reiserfs_file_release, .fsync = reiserfs_sync_file, .read_iter = generic_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, .llseek = generic_file_llseek, diff --git a/fs/sysv/file.c b/fs/sysv/file.c index d99be8877388..b00811c75b24 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -23,8 +23,8 @@ const struct file_operations sysv_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .fsync = generic_file_fsync, .splice_read = generic_file_splice_read, diff --git a/fs/ufs/file.c b/fs/ufs/file.c index b6b402989e6b..c84ec010a676 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -37,8 +37,8 @@ const struct file_operations ufs_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = generic_file_aio_write, + .write = new_sync_write, + .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .open = generic_file_open, .fsync = generic_file_fsync, diff --git a/include/linux/fs.h b/include/linux/fs.h index 17535e0a4547..4b221637f09e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2414,7 +2414,9 @@ int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isbl extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long); +extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t); +extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, loff_t); extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t); extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); diff --git a/mm/filemap.c b/mm/filemap.c index c0404b763a17..d2d9eeec8bf0 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2542,10 +2542,9 @@ again: EXPORT_SYMBOL(generic_perform_write); /** - * __generic_file_aio_write - write data to a file + * __generic_file_write_iter - write data to a file * @iocb: IO state structure (file, offset, etc.) - * @iov: vector with data to write - * @nr_segs: number of segments in the vector + * @from: iov_iter with data to write * * This function does all the work needed for actually writing data to a * file. It does all basic checks, removes SUID from the file, updates @@ -2559,21 +2558,16 @@ EXPORT_SYMBOL(generic_perform_write); * A caller has to handle it. This is mainly due to the fact that we want to * avoid syncing under i_mutex. */ -ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs) +ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct address_space * mapping = file->f_mapping; - size_t count; /* after file limit checks */ struct inode *inode = mapping->host; loff_t pos = iocb->ki_pos; ssize_t written = 0; ssize_t err; ssize_t status; - struct iov_iter from; - - count = iov_length(iov, nr_segs); - iov_iter_init(&from, WRITE, iov, nr_segs, count); + size_t count = iov_iter_count(from); /* We can write back this queue in page reclaim */ current->backing_dev_info = mapping->backing_dev_info; @@ -2584,7 +2578,7 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (count == 0) goto out; - iov_iter_truncate(&from, count); + iov_iter_truncate(from, count); err = file_remove_suid(file); if (err) @@ -2598,7 +2592,7 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (unlikely(file->f_flags & O_DIRECT)) { loff_t endbyte; - written = generic_file_direct_write(iocb, &from, pos); + written = generic_file_direct_write(iocb, from, pos); if (written < 0 || written == count) goto out; @@ -2609,7 +2603,7 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, pos += written; count -= written; - status = generic_perform_write(file, &from, pos); + status = generic_perform_write(file, from, pos); /* * If generic_perform_write() returned a synchronous error * then we want to return the number of bytes which were @@ -2641,7 +2635,7 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, */ } } else { - written = generic_perform_write(file, &from, pos); + written = generic_perform_write(file, from, pos); if (likely(written >= 0)) iocb->ki_pos = pos + written; } @@ -2649,30 +2643,36 @@ out: current->backing_dev_info = NULL; return written ? written : err; } +EXPORT_SYMBOL(__generic_file_write_iter); + +ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs) +{ + size_t count = iov_length(iov, nr_segs); + struct iov_iter from; + + iov_iter_init(&from, WRITE, iov, nr_segs, count); + return __generic_file_write_iter(iocb, &from); +} EXPORT_SYMBOL(__generic_file_aio_write); /** - * generic_file_aio_write - write data to a file + * generic_file_write_iter - write data to a file * @iocb: IO state structure - * @iov: vector with data to write - * @nr_segs: number of segments in the vector - * @pos: position in file where to write + * @from: iov_iter with data to write * - * This is a wrapper around __generic_file_aio_write() to be used by most + * This is a wrapper around __generic_file_write_iter() to be used by most * filesystems. It takes care of syncing the file in case of O_SYNC file * and acquires i_mutex as needed. */ -ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +ssize_t generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; ssize_t ret; - BUG_ON(iocb->ki_pos != pos); - mutex_lock(&inode->i_mutex); - ret = __generic_file_aio_write(iocb, iov, nr_segs); + ret = __generic_file_write_iter(iocb, from); mutex_unlock(&inode->i_mutex); if (ret > 0) { @@ -2684,6 +2684,19 @@ ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, } return ret; } +EXPORT_SYMBOL(generic_file_write_iter); + +ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + size_t count = iov_length(iov, nr_segs); + struct iov_iter from; + + BUG_ON(iocb->ki_pos != pos); + + iov_iter_init(&from, WRITE, iov, nr_segs, count); + return generic_file_write_iter(iocb, &from); +} EXPORT_SYMBOL(generic_file_aio_write); /** diff --git a/mm/shmem.c b/mm/shmem.c index edc6c7e817e9..d3e5c6fc313c 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2618,9 +2618,9 @@ static const struct file_operations shmem_file_operations = { #ifdef CONFIG_TMPFS .llseek = shmem_file_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = shmem_file_read_iter, - .aio_write = generic_file_aio_write, + .write_iter = generic_file_write_iter, .fsync = noop_fsync, .splice_read = shmem_file_splice_read, .splice_write = generic_file_splice_write, diff --git a/mm/vmscan.c b/mm/vmscan.c index 32c661d66a45..9c2dba6ac685 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -458,7 +458,7 @@ static pageout_t pageout(struct page *page, struct address_space *mapping, * stalls if we need to run get_block(). We could test * PagePrivate for that. * - * If this process is currently in __generic_file_aio_write() against + * If this process is currently in __generic_file_write_iter() against * this page's queue, we can perform writeback even if that * will block. * -- cgit v1.2.3 From 1456c0a87c4241d3a801651019e66983c69ad17d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 3 Apr 2014 03:21:50 -0400 Subject: blkdev_aio_write() - turn into blkdev_write_iter() Signed-off-by: Al Viro --- drivers/char/raw.c | 4 ++-- fs/block_dev.c | 16 ++++++---------- include/linux/fs.h | 3 +-- 3 files changed, 9 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/char/raw.c b/drivers/char/raw.c index cfb607a64b85..0102dc788608 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -286,8 +286,8 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd, static const struct file_operations raw_fops = { .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = do_sync_write, - .aio_write = blkdev_aio_write, + .write = new_sync_write, + .write_iter = blkdev_write_iter, .fsync = blkdev_fsync, .open = raw_open, .release = raw_release, diff --git a/fs/block_dev.c b/fs/block_dev.c index 3d97f4a257ff..4e36b8ea8aa4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1509,28 +1509,24 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg) * Does not take i_mutex for the write and thus is not for general purpose * use. */ -ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct blk_plug plug; ssize_t ret; - BUG_ON(iocb->ki_pos != pos); - blk_start_plug(&plug); - ret = __generic_file_aio_write(iocb, iov, nr_segs); + ret = __generic_file_write_iter(iocb, from); if (ret > 0) { ssize_t err; - - err = generic_write_sync(file, pos, ret); + err = generic_write_sync(file, iocb->ki_pos - ret, ret); if (err < 0) ret = err; } blk_finish_plug(&plug); return ret; } -EXPORT_SYMBOL_GPL(blkdev_aio_write); +EXPORT_SYMBOL_GPL(blkdev_write_iter); static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to) { @@ -1577,9 +1573,9 @@ const struct file_operations def_blk_fops = { .release = blkdev_close, .llseek = block_llseek, .read = new_sync_read, - .write = do_sync_write, + .write = new_sync_write, .read_iter = blkdev_read_iter, - .aio_write = blkdev_aio_write, + .write_iter = blkdev_write_iter, .mmap = generic_file_mmap, .fsync = blkdev_fsync, .unlocked_ioctl = block_ioctl, diff --git a/include/linux/fs.h b/include/linux/fs.h index 4b221637f09e..1b9b6c59abdd 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2425,8 +2425,7 @@ extern ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, lo extern ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); /* fs/block_dev.c */ -extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos); +extern ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from); extern int blkdev_fsync(struct file *filp, loff_t start, loff_t end, int datasync); extern void block_sync_page(struct page *page); -- cgit v1.2.3 From a8f3550cd228b6edc5d17fce1a9af8cc7004f185 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 3 Apr 2014 03:32:25 -0400 Subject: bury __generic_file_aio_write() all users converted to __generic_file_write_iter() now Signed-off-by: Al Viro --- include/linux/fs.h | 1 - mm/filemap.c | 11 ----------- 2 files changed, 12 deletions(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 1b9b6c59abdd..99817c9e665e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2413,7 +2413,6 @@ extern int generic_file_remap_pages(struct vm_area_struct *, unsigned long addr, int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk); extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); -extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long); extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *); diff --git a/mm/filemap.c b/mm/filemap.c index d2d9eeec8bf0..7dcdb9db710d 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2645,17 +2645,6 @@ out: } EXPORT_SYMBOL(__generic_file_write_iter); -ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs) -{ - size_t count = iov_length(iov, nr_segs); - struct iov_iter from; - - iov_iter_init(&from, WRITE, iov, nr_segs, count); - return __generic_file_write_iter(iocb, &from); -} -EXPORT_SYMBOL(__generic_file_aio_write); - /** * generic_file_write_iter - write data to a file * @iocb: IO state structure -- cgit v1.2.3 From f0d1bec9d58d4c038d0ac958c9af82be6eb18045 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 3 Apr 2014 15:05:18 -0400 Subject: new helper: copy_page_from_iter() parallel to copy_page_to_iter(). pipe_write() switched to it (and became ->write_iter()). Signed-off-by: Al Viro --- fs/pipe.c | 129 ++++++++-------------------------------------------- include/linux/uio.h | 2 + mm/iov_iter.c | 78 +++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 110 deletions(-) (limited to 'include') diff --git a/fs/pipe.c b/fs/pipe.c index 05ccb00cb407..21981e58e2a6 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -116,50 +116,6 @@ void pipe_wait(struct pipe_inode_info *pipe) pipe_lock(pipe); } -static int -pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len, - int atomic) -{ - unsigned long copy; - - while (len > 0) { - while (!iov->iov_len) - iov++; - copy = min_t(unsigned long, len, iov->iov_len); - - if (atomic) { - if (__copy_from_user_inatomic(to, iov->iov_base, copy)) - return -EFAULT; - } else { - if (copy_from_user(to, iov->iov_base, copy)) - return -EFAULT; - } - to += copy; - len -= copy; - iov->iov_base += copy; - iov->iov_len -= copy; - } - return 0; -} - -/* - * Pre-fault in the user memory, so we can use atomic copies. - */ -static void iov_fault_in_pages_read(struct iovec *iov, unsigned long len) -{ - while (!iov->iov_len) - iov++; - - while (len > 0) { - unsigned long this_len; - - this_len = min_t(unsigned long, len, iov->iov_len); - fault_in_pages_readable(iov->iov_base, this_len); - len -= this_len; - iov++; - } -} - static void anon_pipe_buf_release(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { @@ -380,24 +336,19 @@ static inline int is_packetized(struct file *file) } static ssize_t -pipe_write(struct kiocb *iocb, const struct iovec *_iov, - unsigned long nr_segs, loff_t ppos) +pipe_write(struct kiocb *iocb, struct iov_iter *from) { struct file *filp = iocb->ki_filp; struct pipe_inode_info *pipe = filp->private_data; - ssize_t ret; - int do_wakeup; - struct iovec *iov = (struct iovec *)_iov; - size_t total_len; + ssize_t ret = 0; + int do_wakeup = 0; + size_t total_len = iov_iter_count(from); ssize_t chars; - total_len = iov_length(iov, nr_segs); /* Null write succeeds. */ if (unlikely(total_len == 0)) return 0; - do_wakeup = 0; - ret = 0; __pipe_lock(pipe); if (!pipe->readers) { @@ -416,38 +367,19 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov, int offset = buf->offset + buf->len; if (ops->can_merge && offset + chars <= PAGE_SIZE) { - int error, atomic = 1; - void *addr; - - error = ops->confirm(pipe, buf); + int error = ops->confirm(pipe, buf); if (error) goto out; - iov_fault_in_pages_read(iov, chars); -redo1: - if (atomic) - addr = kmap_atomic(buf->page); - else - addr = kmap(buf->page); - error = pipe_iov_copy_from_user(offset + addr, iov, - chars, atomic); - if (atomic) - kunmap_atomic(addr); - else - kunmap(buf->page); - ret = error; - do_wakeup = 1; - if (error) { - if (atomic) { - atomic = 0; - goto redo1; - } + ret = copy_page_from_iter(buf->page, offset, chars, from); + if (unlikely(ret < chars)) { + error = -EFAULT; goto out; } + do_wakeup = 1; buf->len += chars; - total_len -= chars; ret = chars; - if (!total_len) + if (!iov_iter_count(from)) goto out; } } @@ -466,8 +398,7 @@ redo1: int newbuf = (pipe->curbuf + bufs) & (pipe->buffers-1); struct pipe_buffer *buf = pipe->bufs + newbuf; struct page *page = pipe->tmp_page; - char *src; - int error, atomic = 1; + int copied; if (!page) { page = alloc_page(GFP_HIGHUSER); @@ -483,40 +414,19 @@ redo1: * FIXME! Is this really true? */ do_wakeup = 1; - chars = PAGE_SIZE; - if (chars > total_len) - chars = total_len; - - iov_fault_in_pages_read(iov, chars); -redo2: - if (atomic) - src = kmap_atomic(page); - else - src = kmap(page); - - error = pipe_iov_copy_from_user(src, iov, chars, - atomic); - if (atomic) - kunmap_atomic(src); - else - kunmap(page); - - if (unlikely(error)) { - if (atomic) { - atomic = 0; - goto redo2; - } + copied = copy_page_from_iter(page, 0, PAGE_SIZE, from); + if (unlikely(copied < PAGE_SIZE && iov_iter_count(from))) { if (!ret) - ret = error; + ret = -EFAULT; break; } - ret += chars; + ret += copied; /* Insert it into the buffer array */ buf->page = page; buf->ops = &anon_pipe_buf_ops; buf->offset = 0; - buf->len = chars; + buf->len = copied; buf->flags = 0; if (is_packetized(filp)) { buf->ops = &packet_pipe_buf_ops; @@ -525,8 +435,7 @@ redo2: pipe->nrbufs = ++bufs; pipe->tmp_page = NULL; - total_len -= chars; - if (!total_len) + if (!iov_iter_count(from)) break; } if (bufs < pipe->buffers) @@ -1040,8 +949,8 @@ const struct file_operations pipefifo_fops = { .llseek = no_llseek, .read = new_sync_read, .read_iter = pipe_read, - .write = do_sync_write, - .aio_write = pipe_write, + .write = new_sync_write, + .write_iter = pipe_write, .poll = pipe_poll, .unlocked_ioctl = pipe_ioctl, .release = pipe_release, diff --git a/include/linux/uio.h b/include/linux/uio.h index 532f59d0adbb..66012352d333 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -68,6 +68,8 @@ int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); size_t iov_iter_single_seg_count(const struct iov_iter *i); size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i); +size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, + struct iov_iter *i); unsigned long iov_iter_alignment(const struct iov_iter *i); void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov, unsigned long nr_segs, size_t count); diff --git a/mm/iov_iter.c b/mm/iov_iter.c index a5c691c1a283..081e3273085b 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -82,6 +82,84 @@ done: } EXPORT_SYMBOL(copy_page_to_iter); +size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, + struct iov_iter *i) +{ + size_t skip, copy, left, wanted; + const struct iovec *iov; + char __user *buf; + void *kaddr, *to; + + if (unlikely(bytes > i->count)) + bytes = i->count; + + if (unlikely(!bytes)) + return 0; + + wanted = bytes; + iov = i->iov; + skip = i->iov_offset; + buf = iov->iov_base + skip; + copy = min(bytes, iov->iov_len - skip); + + if (!fault_in_pages_readable(buf, copy)) { + kaddr = kmap_atomic(page); + to = kaddr + offset; + + /* first chunk, usually the only one */ + left = __copy_from_user_inatomic(to, buf, copy); + copy -= left; + skip += copy; + to += copy; + bytes -= copy; + + while (unlikely(!left && bytes)) { + iov++; + buf = iov->iov_base; + copy = min(bytes, iov->iov_len); + left = __copy_from_user_inatomic(to, buf, copy); + copy -= left; + skip = copy; + to += copy; + bytes -= copy; + } + if (likely(!bytes)) { + kunmap_atomic(kaddr); + goto done; + } + offset = to - kaddr; + buf += copy; + kunmap_atomic(kaddr); + copy = min(bytes, iov->iov_len - skip); + } + /* Too bad - revert to non-atomic kmap */ + kaddr = kmap(page); + to = kaddr + offset; + left = __copy_from_user(to, buf, copy); + copy -= left; + skip += copy; + to += copy; + bytes -= copy; + while (unlikely(!left && bytes)) { + iov++; + buf = iov->iov_base; + copy = min(bytes, iov->iov_len); + left = __copy_from_user(to, buf, copy); + copy -= left; + skip = copy; + to += copy; + bytes -= copy; + } + kunmap(page); +done: + i->count -= wanted - bytes; + i->nr_segs -= iov - i->iov; + i->iov = iov; + i->iov_offset = skip; + return wanted - bytes; +} +EXPORT_SYMBOL(copy_page_from_iter); + static size_t __iovec_copy_from_user_inatomic(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) { -- cgit v1.2.3 From 2b777c9dd9ebbb2f8b6818d454cc5e6d7c1e3c8b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 3 Apr 2014 22:31:22 -0400 Subject: ceph_sync_read: stop poking into iov_iter guts Signed-off-by: Al Viro --- fs/ceph/file.c | 46 +++++++++++++++++--------------------------- include/linux/ceph/libceph.h | 2 -- net/ceph/pagevec.c | 35 ++++----------------------------- 3 files changed, 22 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/fs/ceph/file.c b/fs/ceph/file.c index c9a24ba98c9a..672b0fedb17b 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -418,7 +418,7 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i, struct page **pages; u64 off = iocb->ki_pos; int num_pages, ret; - size_t len = i->count; + size_t len = iov_iter_count(i); dout("sync_read on file %p %llu~%u %s\n", file, off, (unsigned)len, @@ -436,25 +436,26 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i, if (file->f_flags & O_DIRECT) { while (iov_iter_count(i)) { - void __user *data = i->iov[0].iov_base + i->iov_offset; - size_t len = i->iov[0].iov_len - i->iov_offset; + size_t start; + ssize_t n; - num_pages = calc_pages_for((unsigned long)data, len); - pages = ceph_get_direct_page_vector(data, - num_pages, true); - if (IS_ERR(pages)) - return PTR_ERR(pages); + n = iov_iter_get_pages_alloc(i, &pages, INT_MAX, &start); + if (n < 0) + return n; - ret = striped_read(inode, off, len, + num_pages = (n + start + PAGE_SIZE - 1) / PAGE_SIZE; + + ret = striped_read(inode, off, n, pages, num_pages, checkeof, - 1, (unsigned long)data & ~PAGE_MASK); + 1, start); + ceph_put_page_vector(pages, num_pages, true); if (ret <= 0) break; off += ret; iov_iter_advance(i, ret); - if (ret < len) + if (ret < n) break; } } else { @@ -466,25 +467,14 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i, num_pages, checkeof, 0, 0); if (ret > 0) { int l, k = 0; - size_t left = len = ret; + size_t left = ret; while (left) { - void __user *data = i->iov[0].iov_base - + i->iov_offset; - l = min(i->iov[0].iov_len - i->iov_offset, - left); - - ret = ceph_copy_page_vector_to_user(&pages[k], - data, off, - l); - if (ret > 0) { - iov_iter_advance(i, ret); - left -= ret; - off += ret; - k = calc_pages_for(iocb->ki_pos, - len - left + 1) - 1; - BUG_ON(k >= num_pages && left); - } else + int copy = min_t(size_t, PAGE_SIZE, left); + l = copy_page_to_iter(pages[k++], 0, copy, i); + off += l; + left -= l; + if (l < copy) break; } } diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 2f49aa4c4f7f..279b0afac1c1 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -222,8 +222,6 @@ extern void ceph_copy_to_page_vector(struct page **pages, extern void ceph_copy_from_page_vector(struct page **pages, void *data, loff_t off, size_t len); -extern int ceph_copy_page_vector_to_user(struct page **pages, void __user *data, - loff_t off, size_t len); extern void ceph_zero_page_vector_range(int off, int len, struct page **pages); diff --git a/net/ceph/pagevec.c b/net/ceph/pagevec.c index 815a2249cfa9..555013034f7a 100644 --- a/net/ceph/pagevec.c +++ b/net/ceph/pagevec.c @@ -53,7 +53,10 @@ void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty) set_page_dirty_lock(pages[i]); put_page(pages[i]); } - kfree(pages); + if (is_vmalloc_addr(pages)) + vfree(pages); + else + kfree(pages); } EXPORT_SYMBOL(ceph_put_page_vector); @@ -164,36 +167,6 @@ void ceph_copy_from_page_vector(struct page **pages, } EXPORT_SYMBOL(ceph_copy_from_page_vector); -/* - * copy user data from a page vector into a user pointer - */ -int ceph_copy_page_vector_to_user(struct page **pages, - void __user *data, - loff_t off, size_t len) -{ - int i = 0; - int po = off & ~PAGE_CACHE_MASK; - int left = len; - int l, bad; - - while (left > 0) { - l = min_t(int, left, PAGE_CACHE_SIZE-po); - bad = copy_to_user(data, page_address(pages[i]) + po, l); - if (bad == l) - return -EFAULT; - data += l - bad; - left -= l - bad; - if (po) { - po += l - bad; - if (po == PAGE_CACHE_SIZE) - po = 0; - } - i++; - } - return len; -} -EXPORT_SYMBOL(ceph_copy_page_vector_to_user); - /* * Zero an extent within a page vector. Offset is relative to the * start of the first page. -- cgit v1.2.3 From b42b15fdad3ebb790250041d1517acebb9bd56d9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Apr 2014 12:15:19 -0400 Subject: lustre: get rid of messing with iovecs * switch to ->read_iter/->write_iter * keep a pointer to iov_iter instead of iov/nr_segs * do not modify iovecs; use iov_iter_truncate()/iov_iter_advance() and a new primitive - iov_iter_reexpand() (expand previously truncated iterator) istead. * (racy) check for lustre VMAs intersecting with iovecs kept for now as for_each_iov() loop. Signed-off-by: Al Viro --- drivers/staging/lustre/lustre/include/lclient.h | 11 +-- drivers/staging/lustre/lustre/lclient/lcommon_cl.c | 48 +--------- drivers/staging/lustre/lustre/llite/file.c | 106 ++++----------------- .../staging/lustre/lustre/llite/llite_internal.h | 3 +- drivers/staging/lustre/lustre/llite/rw.c | 3 +- drivers/staging/lustre/lustre/llite/vvp_io.c | 29 +++--- include/linux/uio.h | 9 ++ 7 files changed, 46 insertions(+), 163 deletions(-) (limited to 'include') diff --git a/drivers/staging/lustre/lustre/include/lclient.h b/drivers/staging/lustre/lustre/include/lclient.h index 827209ea6bd0..386a36c00f57 100644 --- a/drivers/staging/lustre/lustre/include/lclient.h +++ b/drivers/staging/lustre/lustre/include/lclient.h @@ -82,16 +82,7 @@ struct ccc_io { /** * I/O vector information to or from which read/write is going. */ - struct iovec *cui_iov; - unsigned long cui_nrsegs; - /** - * Total iov count for left IO. - */ - unsigned long cui_tot_nrsegs; - /** - * Old length for iov that was truncated partially. - */ - size_t cui_iov_olen; + struct iov_iter *cui_iter; /** * Total size for the left IO. */ diff --git a/drivers/staging/lustre/lustre/lclient/lcommon_cl.c b/drivers/staging/lustre/lustre/lclient/lcommon_cl.c index 6907a16dbbd1..a07d5156bc50 100644 --- a/drivers/staging/lustre/lustre/lclient/lcommon_cl.c +++ b/drivers/staging/lustre/lustre/lclient/lcommon_cl.c @@ -721,31 +721,12 @@ int ccc_io_one_lock_index(const struct lu_env *env, struct cl_io *io, void ccc_io_update_iov(const struct lu_env *env, struct ccc_io *cio, struct cl_io *io) { - int i; size_t size = io->u.ci_rw.crw_count; - cio->cui_iov_olen = 0; - if (!cl_is_normalio(env, io) || cio->cui_tot_nrsegs == 0) + if (!cl_is_normalio(env, io) || cio->cui_iter == NULL) return; - for (i = 0; i < cio->cui_tot_nrsegs; i++) { - struct iovec *iv = &cio->cui_iov[i]; - - if (iv->iov_len < size) - size -= iv->iov_len; - else { - if (iv->iov_len > size) { - cio->cui_iov_olen = iv->iov_len; - iv->iov_len = size; - } - break; - } - } - - cio->cui_nrsegs = i + 1; - LASSERTF(cio->cui_tot_nrsegs >= cio->cui_nrsegs, - "tot_nrsegs: %lu, nrsegs: %lu\n", - cio->cui_tot_nrsegs, cio->cui_nrsegs); + iov_iter_truncate(cio->cui_iter, size); } int ccc_io_one_lock(const struct lu_env *env, struct cl_io *io, @@ -776,30 +757,7 @@ void ccc_io_advance(const struct lu_env *env, if (!cl_is_normalio(env, io)) return; - LASSERT(cio->cui_tot_nrsegs >= cio->cui_nrsegs); - LASSERT(cio->cui_tot_count >= nob); - - cio->cui_iov += cio->cui_nrsegs; - cio->cui_tot_nrsegs -= cio->cui_nrsegs; - cio->cui_tot_count -= nob; - - /* update the iov */ - if (cio->cui_iov_olen > 0) { - struct iovec *iv; - - cio->cui_iov--; - cio->cui_tot_nrsegs++; - iv = &cio->cui_iov[0]; - if (io->ci_continue) { - iv->iov_base += iv->iov_len; - LASSERT(cio->cui_iov_olen > iv->iov_len); - iv->iov_len = cio->cui_iov_olen - iv->iov_len; - } else { - /* restore the iov_len, in case of restart io. */ - iv->iov_len = cio->cui_iov_olen; - } - cio->cui_iov_olen = 0; - } + iov_iter_reexpand(cio->cui_iter, cio->cui_tot_count -= nob); } /** diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c index 220bd8390a84..3efda2540d29 100644 --- a/drivers/staging/lustre/lustre/llite/file.c +++ b/drivers/staging/lustre/lustre/llite/file.c @@ -1105,9 +1105,7 @@ restart: switch (vio->cui_io_subtype) { case IO_NORMAL: - cio->cui_iov = args->u.normal.via_iov; - cio->cui_nrsegs = args->u.normal.via_nrsegs; - cio->cui_tot_nrsegs = cio->cui_nrsegs; + cio->cui_iter = args->u.normal.via_iter; cio->cui_iocb = args->u.normal.via_iocb; if ((iot == CIT_WRITE) && !(cio->cui_fd->fd_flags & LL_FILE_GROUP_LOCKED)) { @@ -1171,56 +1169,23 @@ out: return result; } -static ssize_t ll_file_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t ll_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct lu_env *env; struct vvp_io_args *args; - size_t count = 0; ssize_t result; int refcheck; - count = iov_length(iov, nr_segs); - env = cl_env_get(&refcheck); if (IS_ERR(env)) return PTR_ERR(env); args = vvp_env_args(env, IO_NORMAL); - args->u.normal.via_iov = (struct iovec *)iov; - args->u.normal.via_nrsegs = nr_segs; + args->u.normal.via_iter = to; args->u.normal.via_iocb = iocb; result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_READ, - &iocb->ki_pos, count); - cl_env_put(env, &refcheck); - return result; -} - -static ssize_t ll_file_read(struct file *file, char *buf, size_t count, - loff_t *ppos) -{ - struct lu_env *env; - struct iovec *local_iov; - struct kiocb *kiocb; - ssize_t result; - int refcheck; - - env = cl_env_get(&refcheck); - if (IS_ERR(env)) - return PTR_ERR(env); - - local_iov = &vvp_env_info(env)->vti_local_iov; - kiocb = &vvp_env_info(env)->vti_kiocb; - local_iov->iov_base = (void __user *)buf; - local_iov->iov_len = count; - init_sync_kiocb(kiocb, file); - kiocb->ki_pos = *ppos; - kiocb->ki_nbytes = count; - - result = ll_file_aio_read(kiocb, local_iov, 1, kiocb->ki_pos); - *ppos = kiocb->ki_pos; - + &iocb->ki_pos, iov_iter_count(to)); cl_env_put(env, &refcheck); return result; } @@ -1228,12 +1193,10 @@ static ssize_t ll_file_read(struct file *file, char *buf, size_t count, /* * Write to a file (through the page cache). */ -static ssize_t ll_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t ll_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct lu_env *env; struct vvp_io_args *args; - size_t count = iov_length(iov, nr_segs); ssize_t result; int refcheck; @@ -1242,46 +1205,15 @@ static ssize_t ll_file_aio_write(struct kiocb *iocb, const struct iovec *iov, return PTR_ERR(env); args = vvp_env_args(env, IO_NORMAL); - args->u.normal.via_iov = (struct iovec *)iov; - args->u.normal.via_nrsegs = nr_segs; + args->u.normal.via_iter = from; args->u.normal.via_iocb = iocb; result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_WRITE, - &iocb->ki_pos, count); + &iocb->ki_pos, iov_iter_count(from)); cl_env_put(env, &refcheck); return result; } -static ssize_t ll_file_write(struct file *file, const char *buf, size_t count, - loff_t *ppos) -{ - struct lu_env *env; - struct iovec *local_iov; - struct kiocb *kiocb; - ssize_t result; - int refcheck; - - env = cl_env_get(&refcheck); - if (IS_ERR(env)) - return PTR_ERR(env); - - local_iov = &vvp_env_info(env)->vti_local_iov; - kiocb = &vvp_env_info(env)->vti_kiocb; - local_iov->iov_base = (void __user *)buf; - local_iov->iov_len = count; - init_sync_kiocb(kiocb, file); - kiocb->ki_pos = *ppos; - kiocb->ki_nbytes = count; - - result = ll_file_aio_write(kiocb, local_iov, 1, kiocb->ki_pos); - *ppos = kiocb->ki_pos; - - cl_env_put(env, &refcheck); - return result; -} - - - /* * Send file content (through pagecache) somewhere with helper */ @@ -3133,10 +3065,10 @@ int ll_inode_permission(struct inode *inode, int mask) /* -o localflock - only provides locally consistent flock locks */ struct file_operations ll_file_operations = { - .read = ll_file_read, - .aio_read = ll_file_aio_read, - .write = ll_file_write, - .aio_write = ll_file_aio_write, + .read = new_sync_read, + .read_iter = ll_file_read_iter, + .write = new_sync_write, + .write_iter = ll_file_write_iter, .unlocked_ioctl = ll_file_ioctl, .open = ll_file_open, .release = ll_file_release, @@ -3148,10 +3080,10 @@ struct file_operations ll_file_operations = { }; struct file_operations ll_file_operations_flock = { - .read = ll_file_read, - .aio_read = ll_file_aio_read, - .write = ll_file_write, - .aio_write = ll_file_aio_write, + .read = new_sync_read, + .read_iter = ll_file_read_iter, + .write = new_sync_write, + .write_iter = ll_file_write_iter, .unlocked_ioctl = ll_file_ioctl, .open = ll_file_open, .release = ll_file_release, @@ -3166,10 +3098,10 @@ struct file_operations ll_file_operations_flock = { /* These are for -o noflock - to return ENOSYS on flock calls */ struct file_operations ll_file_operations_noflock = { - .read = ll_file_read, - .aio_read = ll_file_aio_read, - .write = ll_file_write, - .aio_write = ll_file_aio_write, + .read = new_sync_read, + .read_iter = ll_file_read_iter, + .write = new_sync_write, + .write_iter = ll_file_write_iter, .unlocked_ioctl = ll_file_ioctl, .open = ll_file_open, .release = ll_file_release, diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h index 69aba0afca41..fbb8650ead34 100644 --- a/drivers/staging/lustre/lustre/llite/llite_internal.h +++ b/drivers/staging/lustre/lustre/llite/llite_internal.h @@ -974,8 +974,7 @@ struct vvp_io_args { union { struct { struct kiocb *via_iocb; - struct iovec *via_iov; - unsigned long via_nrsegs; + struct iov_iter *via_iter; } normal; struct { struct pipe_inode_info *via_pipe; diff --git a/drivers/staging/lustre/lustre/llite/rw.c b/drivers/staging/lustre/lustre/llite/rw.c index 416f7a094a6d..b345dfa599f3 100644 --- a/drivers/staging/lustre/lustre/llite/rw.c +++ b/drivers/staging/lustre/lustre/llite/rw.c @@ -157,8 +157,7 @@ static struct ll_cl_context *ll_cl_init(struct file *file, result = cl_io_rw_init(env, io, CIT_WRITE, pos, PAGE_CACHE_SIZE); if (result == 0) { cio->cui_fd = LUSTRE_FPRIVATE(file); - cio->cui_iov = NULL; - cio->cui_nrsegs = 0; + cio->cui_iter = NULL; result = cl_io_iter_init(env, io); if (result == 0) { result = cl_io_lock(env, io); diff --git a/drivers/staging/lustre/lustre/llite/vvp_io.c b/drivers/staging/lustre/lustre/llite/vvp_io.c index c7d70091246e..cfe8c625ae64 100644 --- a/drivers/staging/lustre/lustre/llite/vvp_io.c +++ b/drivers/staging/lustre/lustre/llite/vvp_io.c @@ -211,27 +211,26 @@ static int vvp_mmap_locks(const struct lu_env *env, struct cl_lock_descr *descr = &cti->cti_descr; ldlm_policy_data_t policy; unsigned long addr; - unsigned long seg; ssize_t count; int result; + struct iov_iter i; + struct iovec iov; LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE); if (!cl_is_normalio(env, io)) return 0; - if (vio->cui_iov == NULL) /* nfs or loop back device write */ + if (vio->cui_iter == NULL) /* nfs or loop back device write */ return 0; /* No MM (e.g. NFS)? No vmas too. */ if (mm == NULL) return 0; - for (seg = 0; seg < vio->cui_nrsegs; seg++) { - const struct iovec *iv = &vio->cui_iov[seg]; - - addr = (unsigned long)iv->iov_base; - count = iv->iov_len; + iov_for_each(iov, i, *(vio->cui_iter)) { + addr = (unsigned long)iov.iov_base; + count = iov.iov_len; if (count == 0) continue; @@ -527,9 +526,7 @@ static int vvp_io_read_start(const struct lu_env *env, switch (vio->cui_io_subtype) { case IO_NORMAL: LASSERT(cio->cui_iocb->ki_pos == pos); - result = generic_file_aio_read(cio->cui_iocb, - cio->cui_iov, cio->cui_nrsegs, - cio->cui_iocb->ki_pos); + result = generic_file_read_iter(cio->cui_iocb, cio->cui_iter); break; case IO_SPLICE: result = generic_file_splice_read(file, &pos, @@ -595,12 +592,11 @@ static int vvp_io_write_start(const struct lu_env *env, CDEBUG(D_VFSTRACE, "write: [%lli, %lli)\n", pos, pos + (long long)cnt); - if (cio->cui_iov == NULL) /* from a temp io in ll_cl_init(). */ + if (cio->cui_iter == NULL) /* from a temp io in ll_cl_init(). */ result = 0; else - result = generic_file_aio_write(cio->cui_iocb, - cio->cui_iov, cio->cui_nrsegs, - cio->cui_iocb->ki_pos); + result = generic_file_write_iter(cio->cui_iocb, cio->cui_iter); + if (result > 0) { if (result < cnt) io->ci_continue = 0; @@ -1162,10 +1158,9 @@ int vvp_io_init(const struct lu_env *env, struct cl_object *obj, * results." -- Single Unix Spec */ if (count == 0) result = 1; - else { + else cio->cui_tot_count = count; - cio->cui_tot_nrsegs = 0; - } + /* for read/write, we store the jobid in the inode, and * it'll be fetched by osc when building RPC. * diff --git a/include/linux/uio.h b/include/linux/uio.h index 66012352d333..e8a109a75de1 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -90,6 +90,15 @@ static inline void iov_iter_truncate(struct iov_iter *i, size_t count) i->count = count; } +/* + * reexpand a previously truncated iterator; count must be no more than how much + * we had shrunk it. + */ +static inline void iov_iter_reexpand(struct iov_iter *i, size_t count) +{ + i->count = count; +} + int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len); int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len); -- cgit v1.2.3 From 6abd232274fd652e4a57f486d14e52ffee6f72e9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Apr 2014 14:20:57 -0400 Subject: bury generic_file_aio_{read,write} no callers left Signed-off-by: Al Viro --- include/linux/fs.h | 2 -- mm/filemap.c | 43 ++++++++----------------------------------- 2 files changed, 8 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 99817c9e665e..a6448849dbce 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2411,10 +2411,8 @@ extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *); extern int generic_file_remap_pages(struct vm_area_struct *, unsigned long addr, unsigned long size, pgoff_t pgoff); int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk); -extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *); -extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, loff_t); extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t); diff --git a/mm/filemap.c b/mm/filemap.c index 7dcdb9db710d..2f724e3cdf24 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1663,6 +1663,14 @@ out: return written ? written : error; } +/** + * generic_file_read_iter - generic filesystem read routine + * @iocb: kernel I/O control block + * @iter: destination for the data read + * + * This is the "read_iter()" routine for all filesystems + * that can use the page cache directly. + */ ssize_t generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) { @@ -1713,28 +1721,6 @@ out: } EXPORT_SYMBOL(generic_file_read_iter); -/** - * generic_file_aio_read - generic filesystem read routine - * @iocb: kernel I/O control block - * @iov: io vector request - * @nr_segs: number of segments in the iovec - * @pos: current file position - * - * This is the "read()" routine for all filesystems - * that can use the page cache directly. - */ -ssize_t -generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - size_t count = iov_length(iov, nr_segs); - struct iov_iter i; - - iov_iter_init(&i, READ, iov, nr_segs, count); - return generic_file_read_iter(iocb, &i); -} -EXPORT_SYMBOL(generic_file_aio_read); - #ifdef CONFIG_MMU /** * page_cache_read - adds requested page to the page cache if not already there @@ -2675,19 +2661,6 @@ ssize_t generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) } EXPORT_SYMBOL(generic_file_write_iter); -ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - size_t count = iov_length(iov, nr_segs); - struct iov_iter from; - - BUG_ON(iocb->ki_pos != pos); - - iov_iter_init(&from, WRITE, iov, nr_segs, count); - return generic_file_write_iter(iocb, &from); -} -EXPORT_SYMBOL(generic_file_aio_write); - /** * try_to_release_page() - release old fs-specific metadata on a page * -- cgit v1.2.3 From 62a8067a7f35dba2de501c9cb00e4cf36da90bc0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Apr 2014 23:12:29 -0400 Subject: bio_vec-backed iov_iter New variant of iov_iter - ITER_BVEC in iter->type, backed with bio_vec array instead of iovec one. Primitives taught to deal with such beasts, __swap_write() switched to using that kind of iov_iter. Note that bio_vec is just a triple - there's nothing block-specific about it. I've left the definition where it was, but took it from under ifdef CONFIG_BLOCK. Next target: ->splice_write()... Signed-off-by: Al Viro --- fs/fuse/file.c | 2 +- include/linux/blk_types.h | 4 +- include/linux/uio.h | 14 +- mm/iov_iter.c | 390 ++++++++++++++++++++++++++++++++++++++++++---- mm/page_io.c | 19 ++- 5 files changed, 385 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 7fbc803cf51d..b2dae9d1437c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1288,7 +1288,7 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii, size_t nbytes = 0; /* # bytes already packed in req */ /* Special case for kernel I/O: can copy directly into the buffer */ - if (ii->type & REQ_KERNEL) { + if (ii->type & ITER_KVEC) { unsigned long user_addr = fuse_get_user_addr(ii); size_t frag_size = fuse_get_frag_size(ii, *nbytesp); diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index aa0eaa2d0bd8..86df13b97160 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -5,8 +5,6 @@ #ifndef __LINUX_BLK_TYPES_H #define __LINUX_BLK_TYPES_H -#ifdef CONFIG_BLOCK - #include struct bio_set; @@ -28,6 +26,8 @@ struct bio_vec { unsigned int bv_offset; }; +#ifdef CONFIG_BLOCK + struct bvec_iter { sector_t bi_sector; /* device address in 512 byte sectors */ diff --git a/include/linux/uio.h b/include/linux/uio.h index e8a109a75de1..e2231e47cec1 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -19,12 +19,21 @@ struct kvec { size_t iov_len; }; +enum { + ITER_IOVEC = 0, + ITER_KVEC = 2, + ITER_BVEC = 4, +}; + struct iov_iter { int type; - const struct iovec *iov; - unsigned long nr_segs; size_t iov_offset; size_t count; + union { + const struct iovec *iov; + const struct bio_vec *bvec; + }; + unsigned long nr_segs; }; /* @@ -54,6 +63,7 @@ static inline struct iovec iov_iter_iovec(const struct iov_iter *iter) } #define iov_for_each(iov, iter, start) \ + if (!((start).type & ITER_BVEC)) \ for (iter = (start); \ (iter).count && \ ((iov = iov_iter_iovec(&(iter))), 1); \ diff --git a/mm/iov_iter.c b/mm/iov_iter.c index fcdaaab438b6..7b5dbd1517b5 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -4,7 +4,7 @@ #include #include -size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, +static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes, struct iov_iter *i) { size_t skip, copy, left, wanted; @@ -84,9 +84,8 @@ done: i->iov_offset = skip; return wanted - bytes; } -EXPORT_SYMBOL(copy_page_to_iter); -size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, +static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t bytes, struct iov_iter *i) { size_t skip, copy, left, wanted; @@ -166,7 +165,6 @@ done: i->iov_offset = skip; return wanted - bytes; } -EXPORT_SYMBOL(copy_page_from_iter); static size_t __iovec_copy_from_user_inatomic(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) @@ -195,7 +193,7 @@ static size_t __iovec_copy_from_user_inatomic(char *vaddr, * were successfully copied. If a fault is encountered then return the number of * bytes which were copied. */ -size_t iov_iter_copy_from_user_atomic(struct page *page, +static size_t copy_from_user_atomic_iovec(struct page *page, struct iov_iter *i, unsigned long offset, size_t bytes) { char *kaddr; @@ -215,9 +213,8 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, return copied; } -EXPORT_SYMBOL(iov_iter_copy_from_user_atomic); -void iov_iter_advance(struct iov_iter *i, size_t bytes) +static void advance_iovec(struct iov_iter *i, size_t bytes) { BUG_ON(i->count < bytes); @@ -252,7 +249,6 @@ void iov_iter_advance(struct iov_iter *i, size_t bytes) i->nr_segs = nr_segs; } } -EXPORT_SYMBOL(iov_iter_advance); /* * Fault in the first iovec of the given iov_iter, to a maximum length @@ -265,26 +261,16 @@ EXPORT_SYMBOL(iov_iter_advance); */ int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes) { - char __user *buf = i->iov->iov_base + i->iov_offset; - bytes = min(bytes, i->iov->iov_len - i->iov_offset); - return fault_in_pages_readable(buf, bytes); + if (!(i->type & ITER_BVEC)) { + char __user *buf = i->iov->iov_base + i->iov_offset; + bytes = min(bytes, i->iov->iov_len - i->iov_offset); + return fault_in_pages_readable(buf, bytes); + } + return 0; } EXPORT_SYMBOL(iov_iter_fault_in_readable); -/* - * Return the count of just the current iov_iter segment. - */ -size_t iov_iter_single_seg_count(const struct iov_iter *i) -{ - const struct iovec *iov = i->iov; - if (i->nr_segs == 1) - return i->count; - else - return min(i->count, iov->iov_len - i->iov_offset); -} -EXPORT_SYMBOL(iov_iter_single_seg_count); - -unsigned long iov_iter_alignment(const struct iov_iter *i) +static unsigned long alignment_iovec(const struct iov_iter *i) { const struct iovec *iov = i->iov; unsigned long res; @@ -307,7 +293,6 @@ unsigned long iov_iter_alignment(const struct iov_iter *i) res |= (unsigned long)iov->iov_base | size; return res; } -EXPORT_SYMBOL(iov_iter_alignment); void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov, unsigned long nr_segs, @@ -315,7 +300,7 @@ void iov_iter_init(struct iov_iter *i, int direction, { /* It will get better. Eventually... */ if (segment_eq(get_fs(), KERNEL_DS)) - direction |= REQ_KERNEL; + direction |= ITER_KVEC; i->type = direction; i->iov = iov; i->nr_segs = nr_segs; @@ -324,7 +309,7 @@ void iov_iter_init(struct iov_iter *i, int direction, } EXPORT_SYMBOL(iov_iter_init); -ssize_t iov_iter_get_pages(struct iov_iter *i, +static ssize_t get_pages_iovec(struct iov_iter *i, struct page **pages, size_t maxsize, size_t *start) { @@ -349,9 +334,8 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, return res; return (res == n ? len : res * PAGE_SIZE) - *start; } -EXPORT_SYMBOL(iov_iter_get_pages); -ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, +static ssize_t get_pages_alloc_iovec(struct iov_iter *i, struct page ***pages, size_t maxsize, size_t *start) { @@ -387,9 +371,8 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, *pages = p; return (res == n ? len : res * PAGE_SIZE) - *start; } -EXPORT_SYMBOL(iov_iter_get_pages_alloc); -int iov_iter_npages(const struct iov_iter *i, int maxpages) +static int iov_iter_npages_iovec(const struct iov_iter *i, int maxpages) { size_t offset = i->iov_offset; size_t size = i->count; @@ -414,4 +397,347 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages) } return min(npages, maxpages); } + +static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len) +{ + char *from = kmap_atomic(page); + memcpy(to, from + offset, len); + kunmap_atomic(from); +} + +static void memcpy_to_page(struct page *page, size_t offset, char *from, size_t len) +{ + char *to = kmap_atomic(page); + memcpy(to + offset, from, len); + kunmap_atomic(to); +} + +static size_t copy_page_to_iter_bvec(struct page *page, size_t offset, size_t bytes, + struct iov_iter *i) +{ + size_t skip, copy, wanted; + const struct bio_vec *bvec; + void *kaddr, *from; + + if (unlikely(bytes > i->count)) + bytes = i->count; + + if (unlikely(!bytes)) + return 0; + + wanted = bytes; + bvec = i->bvec; + skip = i->iov_offset; + copy = min_t(size_t, bytes, bvec->bv_len - skip); + + kaddr = kmap_atomic(page); + from = kaddr + offset; + memcpy_to_page(bvec->bv_page, skip + bvec->bv_offset, from, copy); + skip += copy; + from += copy; + bytes -= copy; + while (bytes) { + bvec++; + copy = min(bytes, (size_t)bvec->bv_len); + memcpy_to_page(bvec->bv_page, bvec->bv_offset, from, copy); + skip = copy; + from += copy; + bytes -= copy; + } + kunmap_atomic(kaddr); + if (skip == bvec->bv_len) { + bvec++; + skip = 0; + } + i->count -= wanted - bytes; + i->nr_segs -= bvec - i->bvec; + i->bvec = bvec; + i->iov_offset = skip; + return wanted - bytes; +} + +static size_t copy_page_from_iter_bvec(struct page *page, size_t offset, size_t bytes, + struct iov_iter *i) +{ + size_t skip, copy, wanted; + const struct bio_vec *bvec; + void *kaddr, *to; + + if (unlikely(bytes > i->count)) + bytes = i->count; + + if (unlikely(!bytes)) + return 0; + + wanted = bytes; + bvec = i->bvec; + skip = i->iov_offset; + + kaddr = kmap_atomic(page); + + to = kaddr + offset; + + copy = min(bytes, bvec->bv_len - skip); + + memcpy_from_page(to, bvec->bv_page, bvec->bv_offset + skip, copy); + + to += copy; + skip += copy; + bytes -= copy; + + while (bytes) { + bvec++; + copy = min(bytes, (size_t)bvec->bv_len); + memcpy_from_page(to, bvec->bv_page, bvec->bv_offset, copy); + skip = copy; + to += copy; + bytes -= copy; + } + kunmap_atomic(kaddr); + if (skip == bvec->bv_len) { + bvec++; + skip = 0; + } + i->count -= wanted; + i->nr_segs -= bvec - i->bvec; + i->bvec = bvec; + i->iov_offset = skip; + return wanted; +} + +static size_t copy_from_user_bvec(struct page *page, + struct iov_iter *i, unsigned long offset, size_t bytes) +{ + char *kaddr; + size_t left; + const struct bio_vec *bvec; + size_t base = i->iov_offset; + + kaddr = kmap_atomic(page); + for (left = bytes, bvec = i->bvec; left; bvec++, base = 0) { + size_t copy = min(left, bvec->bv_len - base); + if (!bvec->bv_len) + continue; + memcpy_from_page(kaddr + offset, bvec->bv_page, + bvec->bv_offset + base, copy); + offset += copy; + left -= copy; + } + kunmap_atomic(kaddr); + return bytes; +} + +static void advance_bvec(struct iov_iter *i, size_t bytes) +{ + BUG_ON(i->count < bytes); + + if (likely(i->nr_segs == 1)) { + i->iov_offset += bytes; + i->count -= bytes; + } else { + const struct bio_vec *bvec = i->bvec; + size_t base = i->iov_offset; + unsigned long nr_segs = i->nr_segs; + + /* + * The !iov->iov_len check ensures we skip over unlikely + * zero-length segments (without overruning the iovec). + */ + while (bytes || unlikely(i->count && !bvec->bv_len)) { + int copy; + + copy = min(bytes, bvec->bv_len - base); + BUG_ON(!i->count || i->count < copy); + i->count -= copy; + bytes -= copy; + base += copy; + if (bvec->bv_len == base) { + bvec++; + nr_segs--; + base = 0; + } + } + i->bvec = bvec; + i->iov_offset = base; + i->nr_segs = nr_segs; + } +} + +static unsigned long alignment_bvec(const struct iov_iter *i) +{ + const struct bio_vec *bvec = i->bvec; + unsigned long res; + size_t size = i->count; + size_t n; + + if (!size) + return 0; + + res = bvec->bv_offset + i->iov_offset; + n = bvec->bv_len - i->iov_offset; + if (n >= size) + return res | size; + size -= n; + res |= n; + while (size > (++bvec)->bv_len) { + res |= bvec->bv_offset | bvec->bv_len; + size -= bvec->bv_len; + } + res |= bvec->bv_offset | size; + return res; +} + +static ssize_t get_pages_bvec(struct iov_iter *i, + struct page **pages, size_t maxsize, + size_t *start) +{ + const struct bio_vec *bvec = i->bvec; + size_t len = bvec->bv_len - i->iov_offset; + if (len > i->count) + len = i->count; + if (len > maxsize) + len = maxsize; + *start = bvec->bv_offset + i->iov_offset; + + get_page(*pages = bvec->bv_page); + + return len; +} + +static ssize_t get_pages_alloc_bvec(struct iov_iter *i, + struct page ***pages, size_t maxsize, + size_t *start) +{ + const struct bio_vec *bvec = i->bvec; + size_t len = bvec->bv_len - i->iov_offset; + if (len > i->count) + len = i->count; + if (len > maxsize) + len = maxsize; + *start = bvec->bv_offset + i->iov_offset; + + *pages = kmalloc(sizeof(struct page *), GFP_KERNEL); + if (!*pages) + return -ENOMEM; + + get_page(**pages = bvec->bv_page); + + return len; +} + +static int iov_iter_npages_bvec(const struct iov_iter *i, int maxpages) +{ + size_t offset = i->iov_offset; + size_t size = i->count; + const struct bio_vec *bvec = i->bvec; + int npages = 0; + int n; + + for (n = 0; size && n < i->nr_segs; n++, bvec++) { + size_t len = bvec->bv_len - offset; + offset = 0; + if (unlikely(!len)) /* empty segment */ + continue; + if (len > size) + len = size; + npages++; + if (npages >= maxpages) /* don't bother going further */ + return maxpages; + size -= len; + offset = 0; + } + return min(npages, maxpages); +} + +size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, + struct iov_iter *i) +{ + if (i->type & ITER_BVEC) + return copy_page_to_iter_bvec(page, offset, bytes, i); + else + return copy_page_to_iter_iovec(page, offset, bytes, i); +} +EXPORT_SYMBOL(copy_page_to_iter); + +size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes, + struct iov_iter *i) +{ + if (i->type & ITER_BVEC) + return copy_page_from_iter_bvec(page, offset, bytes, i); + else + return copy_page_from_iter_iovec(page, offset, bytes, i); +} +EXPORT_SYMBOL(copy_page_from_iter); + +size_t iov_iter_copy_from_user_atomic(struct page *page, + struct iov_iter *i, unsigned long offset, size_t bytes) +{ + if (i->type & ITER_BVEC) + return copy_from_user_bvec(page, i, offset, bytes); + else + return copy_from_user_atomic_iovec(page, i, offset, bytes); +} +EXPORT_SYMBOL(iov_iter_copy_from_user_atomic); + +void iov_iter_advance(struct iov_iter *i, size_t size) +{ + if (i->type & ITER_BVEC) + advance_bvec(i, size); + else + advance_iovec(i, size); +} +EXPORT_SYMBOL(iov_iter_advance); + +/* + * Return the count of just the current iov_iter segment. + */ +size_t iov_iter_single_seg_count(const struct iov_iter *i) +{ + if (i->nr_segs == 1) + return i->count; + else if (i->type & ITER_BVEC) + return min(i->count, i->iov->iov_len - i->iov_offset); + else + return min(i->count, i->bvec->bv_len - i->iov_offset); +} +EXPORT_SYMBOL(iov_iter_single_seg_count); + +unsigned long iov_iter_alignment(const struct iov_iter *i) +{ + if (i->type & ITER_BVEC) + return alignment_bvec(i); + else + return alignment_iovec(i); +} +EXPORT_SYMBOL(iov_iter_alignment); + +ssize_t iov_iter_get_pages(struct iov_iter *i, + struct page **pages, size_t maxsize, + size_t *start) +{ + if (i->type & ITER_BVEC) + return get_pages_bvec(i, pages, maxsize, start); + else + return get_pages_iovec(i, pages, maxsize, start); +} +EXPORT_SYMBOL(iov_iter_get_pages); + +ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, + struct page ***pages, size_t maxsize, + size_t *start) +{ + if (i->type & ITER_BVEC) + return get_pages_alloc_bvec(i, pages, maxsize, start); + else + return get_pages_alloc_iovec(i, pages, maxsize, start); +} +EXPORT_SYMBOL(iov_iter_get_pages_alloc); + +int iov_iter_npages(const struct iov_iter *i, int maxpages) +{ + if (i->type & ITER_BVEC) + return iov_iter_npages_bvec(i, maxpages); + else + return iov_iter_npages_iovec(i, maxpages); +} EXPORT_SYMBOL(iov_iter_npages); diff --git a/mm/page_io.c b/mm/page_io.c index 313bfedb75d1..33bb38c4aad7 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -259,23 +259,28 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc, struct kiocb kiocb; struct file *swap_file = sis->swap_file; struct address_space *mapping = swap_file->f_mapping; - struct iovec iov = { - .iov_base = kmap(page), - .iov_len = PAGE_SIZE, + struct bio_vec bv = { + .bv_page = page, + .bv_len = PAGE_SIZE, + .bv_offset = 0 + }; + struct iov_iter from = { + .type = ITER_BVEC | WRITE, + .count = PAGE_SIZE, + .iov_offset = 0, + .nr_segs = 1, + .bvec = &bv }; - struct iov_iter from; init_sync_kiocb(&kiocb, swap_file); kiocb.ki_pos = page_file_offset(page); kiocb.ki_nbytes = PAGE_SIZE; - iov_iter_init(&from, KERNEL_WRITE, &iov, 1, PAGE_SIZE); set_page_writeback(page); unlock_page(page); - ret = mapping->a_ops->direct_IO(KERNEL_WRITE, + ret = mapping->a_ops->direct_IO(ITER_BVEC | WRITE, &kiocb, &from, kiocb.ki_pos); - kunmap(page); if (ret == PAGE_SIZE) { count_vm_event(PSWPOUT); ret = 0; -- cgit v1.2.3 From 9c69de4c94fcb11db919160d5fa0b48f13d1757a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 6 May 2014 19:37:13 +0200 Subject: nfsd: remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only real user of this header is fs/nfsd/nfsfh.h, so merge the two. Various lockѕ source files used it to indirectly get other sunrpc or nfs headers, so fix those up. Signed-off-by: Christoph Hellwig Signed-off-by: J. Bruce Fields --- fs/lockd/clnt4xdr.c | 2 ++ fs/lockd/clntxdr.c | 2 ++ fs/lockd/svcsubs.c | 2 +- fs/lockd/xdr.c | 2 ++ fs/nfsd/nfsd.h | 1 + fs/nfsd/nfsfh.h | 59 ++++++++++++++++++++++++++++++++++++++---- fs/nfsd/state.h | 1 - include/linux/lockd/lockd.h | 2 +- include/linux/nfsd/export.h | 6 ++++- include/linux/nfsd/nfsfh.h | 63 --------------------------------------------- 10 files changed, 68 insertions(+), 72 deletions(-) delete mode 100644 include/linux/nfsd/nfsfh.h (limited to 'include') diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c index 00ec0b9c94d1..d3e40db28930 100644 --- a/fs/lockd/clnt4xdr.c +++ b/fs/lockd/clnt4xdr.c @@ -14,6 +14,8 @@ #include #include +#include + #define NLMDBG_FACILITY NLMDBG_XDR #if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ) diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c index 9a55797a1cd4..3e9f7874b975 100644 --- a/fs/lockd/clntxdr.c +++ b/fs/lockd/clntxdr.c @@ -15,6 +15,8 @@ #include #include +#include + #define NLMDBG_FACILITY NLMDBG_XDR #if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ) diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index dc5c75930f0f..7ec6b1074d8c 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -14,12 +14,12 @@ #include #include #include -#include #include #include #include #include #include +#include #define NLMDBG_FACILITY NLMDBG_SVCSUBS diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 964666c68a86..9340e7e10ef6 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -16,6 +16,8 @@ #include #include +#include + #define NLMDBG_FACILITY NLMDBG_XDR diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 479eb681c27c..7d5c310678d0 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h index ad67964d0bb1..2e89e70ac15c 100644 --- a/fs/nfsd/nfsfh.h +++ b/fs/nfsd/nfsfh.h @@ -1,9 +1,58 @@ -/* Copyright (C) 1995, 1996, 1997 Olaf Kirch */ +/* + * Copyright (C) 1995, 1996, 1997 Olaf Kirch + * + * This file describes the layout of the file handles as passed + * over the wire. + */ +#ifndef _LINUX_NFSD_NFSFH_H +#define _LINUX_NFSD_NFSFH_H + +#include +#include + +static inline __u32 ino_t_to_u32(ino_t ino) +{ + return (__u32) ino; +} + +static inline ino_t u32_to_ino_t(__u32 uino) +{ + return (ino_t) uino; +} -#ifndef _LINUX_NFSD_FH_INT_H -#define _LINUX_NFSD_FH_INT_H +/* + * This is the internal representation of an NFS handle used in knfsd. + * pre_mtime/post_version will be used to support wcc_attr's in NFSv3. + */ +typedef struct svc_fh { + struct knfsd_fh fh_handle; /* FH data */ + struct dentry * fh_dentry; /* validated dentry */ + struct svc_export * fh_export; /* export pointer */ + int fh_maxsize; /* max size for fh_handle */ + + unsigned char fh_locked; /* inode locked by us */ + unsigned char fh_want_write; /* remount protection taken */ + +#ifdef CONFIG_NFSD_V3 + unsigned char fh_post_saved; /* post-op attrs saved */ + unsigned char fh_pre_saved; /* pre-op attrs saved */ + + /* Pre-op attributes saved during fh_lock */ + __u64 fh_pre_size; /* size before operation */ + struct timespec fh_pre_mtime; /* mtime before oper */ + struct timespec fh_pre_ctime; /* ctime before oper */ + /* + * pre-op nfsv4 change attr: note must check IS_I_VERSION(inode) + * to find out if it is valid. + */ + u64 fh_pre_change; + + /* Post-op attributes saved in fh_unlock */ + struct kstat fh_post_attr; /* full attrs after operation */ + u64 fh_post_change; /* nfsv4 change; see above */ +#endif /* CONFIG_NFSD_V3 */ -#include +} svc_fh; enum nfsd_fsid { FSID_DEV = 0, @@ -215,4 +264,4 @@ fh_unlock(struct svc_fh *fhp) } } -#endif /* _LINUX_NFSD_FH_INT_H */ +#endif /* _LINUX_NFSD_NFSFH_H */ diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 424d8f5f2317..5b3bbf24097c 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -37,7 +37,6 @@ #include #include -#include #include "nfsfh.h" typedef struct { diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index dcaad79f54ed..219d79627c05 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -17,13 +17,13 @@ #include #include #include -#include #include #include #ifdef CONFIG_LOCKD_V4 #include #endif #include +#include /* * Version string diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 7898c997dfea..b12c4e526ef2 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -9,9 +9,13 @@ #ifndef NFSD_EXPORT_H #define NFSD_EXPORT_H -# include +#include #include +struct knfsd_fh; +struct svc_fh; +struct svc_rqst; + /* * FS Locations */ diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h deleted file mode 100644 index a93593f1fa4e..000000000000 --- a/include/linux/nfsd/nfsfh.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * include/linux/nfsd/nfsfh.h - * - * This file describes the layout of the file handles as passed - * over the wire. - * - * Earlier versions of knfsd used to sign file handles using keyed MD5 - * or SHA. I've removed this code, because it doesn't give you more - * security than blocking external access to port 2049 on your firewall. - * - * Copyright (C) 1995, 1996, 1997 Olaf Kirch - */ -#ifndef _LINUX_NFSD_FH_H -#define _LINUX_NFSD_FH_H - -# include -#include - -static inline __u32 ino_t_to_u32(ino_t ino) -{ - return (__u32) ino; -} - -static inline ino_t u32_to_ino_t(__u32 uino) -{ - return (ino_t) uino; -} - -/* - * This is the internal representation of an NFS handle used in knfsd. - * pre_mtime/post_version will be used to support wcc_attr's in NFSv3. - */ -typedef struct svc_fh { - struct knfsd_fh fh_handle; /* FH data */ - struct dentry * fh_dentry; /* validated dentry */ - struct svc_export * fh_export; /* export pointer */ - int fh_maxsize; /* max size for fh_handle */ - - unsigned char fh_locked; /* inode locked by us */ - unsigned char fh_want_write; /* remount protection taken */ - -#ifdef CONFIG_NFSD_V3 - unsigned char fh_post_saved; /* post-op attrs saved */ - unsigned char fh_pre_saved; /* pre-op attrs saved */ - - /* Pre-op attributes saved during fh_lock */ - __u64 fh_pre_size; /* size before operation */ - struct timespec fh_pre_mtime; /* mtime before oper */ - struct timespec fh_pre_ctime; /* ctime before oper */ - /* - * pre-op nfsv4 change attr: note must check IS_I_VERSION(inode) - * to find out if it is valid. - */ - u64 fh_pre_change; - - /* Post-op attributes saved in fh_unlock */ - struct kstat fh_post_attr; /* full attrs after operation */ - u64 fh_post_change; /* nfsv4 change; see above */ -#endif /* CONFIG_NFSD_V3 */ - -} svc_fh; - -#endif /* _LINUX_NFSD_FH_H */ -- cgit v1.2.3 From d430e8d530e900c923bf77718d72478b1c280592 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 6 May 2014 19:37:14 +0200 Subject: nfsd: move to fs/nfsd There are no legitimate users outside of fs/nfsd, so move it there. Signed-off-by: Christoph Hellwig Signed-off-by: J. Bruce Fields --- fs/lockd/svcsubs.c | 1 - fs/nfsd/export.h | 109 ++++++++++++++++++++++++++++++++++++++++++ fs/nfsd/nfsd.h | 3 +- include/linux/nfsd/export.h | 114 -------------------------------------------- 4 files changed, 111 insertions(+), 116 deletions(-) create mode 100644 fs/nfsd/export.h delete mode 100644 include/linux/nfsd/export.h (limited to 'include') diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 7ec6b1074d8c..b6f3b84b6e99 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h new file mode 100644 index 000000000000..d7939a62a0ae --- /dev/null +++ b/fs/nfsd/export.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 1995-1997 Olaf Kirch + */ +#ifndef NFSD_EXPORT_H +#define NFSD_EXPORT_H + +#include +#include + +struct knfsd_fh; +struct svc_fh; +struct svc_rqst; + +/* + * FS Locations + */ + +#define MAX_FS_LOCATIONS 128 + +struct nfsd4_fs_location { + char *hosts; /* colon separated list of hosts */ + char *path; /* slash separated list of path components */ +}; + +struct nfsd4_fs_locations { + uint32_t locations_count; + struct nfsd4_fs_location *locations; +/* If we're not actually serving this data ourselves (only providing a + * list of replicas that do serve it) then we set "migrated": */ + int migrated; +}; + +/* + * We keep an array of pseudoflavors with the export, in order from most + * to least preferred. For the foreseeable future, we don't expect more + * than the eight pseudoflavors null, unix, krb5, krb5i, krb5p, skpm3, + * spkm3i, and spkm3p (and using all 8 at once should be rare). + */ +#define MAX_SECINFO_LIST 8 + +struct exp_flavor_info { + u32 pseudoflavor; + u32 flags; +}; + +struct svc_export { + struct cache_head h; + struct auth_domain * ex_client; + int ex_flags; + struct path ex_path; + kuid_t ex_anon_uid; + kgid_t ex_anon_gid; + int ex_fsid; + unsigned char * ex_uuid; /* 16 byte fsid */ + struct nfsd4_fs_locations ex_fslocs; + int ex_nflavors; + struct exp_flavor_info ex_flavors[MAX_SECINFO_LIST]; + struct cache_detail *cd; +}; + +/* an "export key" (expkey) maps a filehandlefragement to an + * svc_export for a given client. There can be several per export, + * for the different fsid types. + */ +struct svc_expkey { + struct cache_head h; + + struct auth_domain * ek_client; + int ek_fsidtype; + u32 ek_fsid[6]; + + struct path ek_path; +}; + +#define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) +#define EX_NOHIDE(exp) ((exp)->ex_flags & NFSEXP_NOHIDE) +#define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES) + +int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp); +__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp); + +/* + * Function declarations + */ +int nfsd_export_init(struct net *); +void nfsd_export_shutdown(struct net *); +void nfsd_export_flush(struct net *); +struct svc_export * rqst_exp_get_by_name(struct svc_rqst *, + struct path *); +struct svc_export * rqst_exp_parent(struct svc_rqst *, + struct path *); +struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *); +int exp_rootfh(struct net *, struct auth_domain *, + char *path, struct knfsd_fh *, int maxsize); +__be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *); +__be32 nfserrno(int errno); + +static inline void exp_put(struct svc_export *exp) +{ + cache_put(&exp->h, exp->cd); +} + +static inline void exp_get(struct svc_export *exp) +{ + cache_get(&exp->h); +} +struct svc_export * rqst_exp_find(struct svc_rqst *, int, u32 *); + +#endif /* NFSD_EXPORT_H */ diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 7d5c310678d0..72004caad718 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -19,9 +19,10 @@ #include #include -#include #include +#include "export.h" + /* * nfsd version */ diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h deleted file mode 100644 index b12c4e526ef2..000000000000 --- a/include/linux/nfsd/export.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * include/linux/nfsd/export.h - * - * Public declarations for NFS exports. The definitions for the - * syscall interface are in nfsctl.h - * - * Copyright (C) 1995-1997 Olaf Kirch - */ -#ifndef NFSD_EXPORT_H -#define NFSD_EXPORT_H - -#include -#include - -struct knfsd_fh; -struct svc_fh; -struct svc_rqst; - -/* - * FS Locations - */ - -#define MAX_FS_LOCATIONS 128 - -struct nfsd4_fs_location { - char *hosts; /* colon separated list of hosts */ - char *path; /* slash separated list of path components */ -}; - -struct nfsd4_fs_locations { - uint32_t locations_count; - struct nfsd4_fs_location *locations; -/* If we're not actually serving this data ourselves (only providing a - * list of replicas that do serve it) then we set "migrated": */ - int migrated; -}; - -/* - * We keep an array of pseudoflavors with the export, in order from most - * to least preferred. For the foreseeable future, we don't expect more - * than the eight pseudoflavors null, unix, krb5, krb5i, krb5p, skpm3, - * spkm3i, and spkm3p (and using all 8 at once should be rare). - */ -#define MAX_SECINFO_LIST 8 - -struct exp_flavor_info { - u32 pseudoflavor; - u32 flags; -}; - -struct svc_export { - struct cache_head h; - struct auth_domain * ex_client; - int ex_flags; - struct path ex_path; - kuid_t ex_anon_uid; - kgid_t ex_anon_gid; - int ex_fsid; - unsigned char * ex_uuid; /* 16 byte fsid */ - struct nfsd4_fs_locations ex_fslocs; - int ex_nflavors; - struct exp_flavor_info ex_flavors[MAX_SECINFO_LIST]; - struct cache_detail *cd; -}; - -/* an "export key" (expkey) maps a filehandlefragement to an - * svc_export for a given client. There can be several per export, - * for the different fsid types. - */ -struct svc_expkey { - struct cache_head h; - - struct auth_domain * ek_client; - int ek_fsidtype; - u32 ek_fsid[6]; - - struct path ek_path; -}; - -#define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) -#define EX_NOHIDE(exp) ((exp)->ex_flags & NFSEXP_NOHIDE) -#define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES) - -int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp); -__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp); - -/* - * Function declarations - */ -int nfsd_export_init(struct net *); -void nfsd_export_shutdown(struct net *); -void nfsd_export_flush(struct net *); -struct svc_export * rqst_exp_get_by_name(struct svc_rqst *, - struct path *); -struct svc_export * rqst_exp_parent(struct svc_rqst *, - struct path *); -struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *); -int exp_rootfh(struct net *, struct auth_domain *, - char *path, struct knfsd_fh *, int maxsize); -__be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *); -__be32 nfserrno(int errno); - -static inline void exp_put(struct svc_export *exp) -{ - cache_put(&exp->h, exp->cd); -} - -static inline void exp_get(struct svc_export *exp) -{ - cache_get(&exp->h); -} -struct svc_export * rqst_exp_find(struct svc_rqst *, int, u32 *); - -#endif /* NFSD_EXPORT_H */ -- cgit v1.2.3 From 7f94423e8fcc1e0f3416be76d3da0982f586d565 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 6 May 2014 19:37:15 +0200 Subject: nfsd: move to fs/nfsd There are no legitimate users outside of fs/nfsd, so move it there. Signed-off-by: Christoph Hellwig Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsd.h | 2 +- fs/nfsd/stats.c | 1 - fs/nfsd/stats.h | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/nfsd/stats.h | 45 --------------------------------------------- 4 files changed, 44 insertions(+), 47 deletions(-) create mode 100644 fs/nfsd/stats.h delete mode 100644 include/linux/nfsd/stats.h (limited to 'include') diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 72004caad718..7a07f9c6ee78 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -19,8 +19,8 @@ #include #include -#include +#include "stats.h" #include "export.h" /* diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c index 6d4521feb6e3..cd90878a76aa 100644 --- a/fs/nfsd/stats.c +++ b/fs/nfsd/stats.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include "nfsd.h" diff --git a/fs/nfsd/stats.h b/fs/nfsd/stats.h new file mode 100644 index 000000000000..a5c944b771c6 --- /dev/null +++ b/fs/nfsd/stats.h @@ -0,0 +1,43 @@ +/* + * Statistics for NFS server. + * + * Copyright (C) 1995, 1996 Olaf Kirch + */ +#ifndef _NFSD_STATS_H +#define _NFSD_STATS_H + +#include + + +struct nfsd_stats { + unsigned int rchits; /* repcache hits */ + unsigned int rcmisses; /* repcache hits */ + unsigned int rcnocache; /* uncached reqs */ + unsigned int fh_stale; /* FH stale error */ + unsigned int fh_lookup; /* dentry cached */ + unsigned int fh_anon; /* anon file dentry returned */ + unsigned int fh_nocache_dir; /* filehandle not found in dcache */ + unsigned int fh_nocache_nondir; /* filehandle not found in dcache */ + unsigned int io_read; /* bytes returned to read requests */ + unsigned int io_write; /* bytes passed in write requests */ + unsigned int th_cnt; /* number of available threads */ + unsigned int th_usage[10]; /* number of ticks during which n perdeciles + * of available threads were in use */ + unsigned int th_fullcnt; /* number of times last free thread was used */ + unsigned int ra_size; /* size of ra cache */ + unsigned int ra_depth[11]; /* number of times ra entry was found that deep + * in the cache (10percentiles). [10] = not found */ +#ifdef CONFIG_NFSD_V4 + unsigned int nfs4_opcount[LAST_NFS4_OP + 1]; /* count of individual nfsv4 operations */ +#endif + +}; + + +extern struct nfsd_stats nfsdstats; +extern struct svc_stat nfsd_svcstats; + +void nfsd_stat_init(void); +void nfsd_stat_shutdown(void); + +#endif /* _NFSD_STATS_H */ diff --git a/include/linux/nfsd/stats.h b/include/linux/nfsd/stats.h deleted file mode 100644 index e75b2544ff12..000000000000 --- a/include/linux/nfsd/stats.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * linux/include/linux/nfsd/stats.h - * - * Statistics for NFS server. - * - * Copyright (C) 1995, 1996 Olaf Kirch - */ -#ifndef LINUX_NFSD_STATS_H -#define LINUX_NFSD_STATS_H - -#include - - -struct nfsd_stats { - unsigned int rchits; /* repcache hits */ - unsigned int rcmisses; /* repcache hits */ - unsigned int rcnocache; /* uncached reqs */ - unsigned int fh_stale; /* FH stale error */ - unsigned int fh_lookup; /* dentry cached */ - unsigned int fh_anon; /* anon file dentry returned */ - unsigned int fh_nocache_dir; /* filehandle not found in dcache */ - unsigned int fh_nocache_nondir; /* filehandle not found in dcache */ - unsigned int io_read; /* bytes returned to read requests */ - unsigned int io_write; /* bytes passed in write requests */ - unsigned int th_cnt; /* number of available threads */ - unsigned int th_usage[10]; /* number of ticks during which n perdeciles - * of available threads were in use */ - unsigned int th_fullcnt; /* number of times last free thread was used */ - unsigned int ra_size; /* size of ra cache */ - unsigned int ra_depth[11]; /* number of times ra entry was found that deep - * in the cache (10percentiles). [10] = not found */ -#ifdef CONFIG_NFSD_V4 - unsigned int nfs4_opcount[LAST_NFS4_OP + 1]; /* count of individual nfsv4 operations */ -#endif - -}; - - -extern struct nfsd_stats nfsdstats; -extern struct svc_stat nfsd_svcstats; - -void nfsd_stat_init(void); -void nfsd_stat_shutdown(void); - -#endif /* LINUX_NFSD_STATS_H */ -- cgit v1.2.3 From 6f226e2ab1b895c8685e868af0a5f797fcaaaf57 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 6 May 2014 19:37:16 +0200 Subject: nfsd: remove There is almost nothing left it in, just merge it into the only file that includes it. Signed-off-by: Christoph Hellwig Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsd.h | 9 ++++++++- include/linux/nfsd/debug.h | 19 ------------------- 2 files changed, 8 insertions(+), 20 deletions(-) delete mode 100644 include/linux/nfsd/debug.h (limited to 'include') diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 7a07f9c6ee78..e9f2fd42d184 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -18,11 +18,18 @@ #include #include -#include +#include #include "stats.h" #include "export.h" +#undef ifdebug +#ifdef NFSD_DEBUG +# define ifdebug(flag) if (nfsd_debug & NFSDDBG_##flag) +#else +# define ifdebug(flag) if (0) +#endif + /* * nfsd version */ diff --git a/include/linux/nfsd/debug.h b/include/linux/nfsd/debug.h deleted file mode 100644 index 19ef8375b577..000000000000 --- a/include/linux/nfsd/debug.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * linux/include/linux/nfsd/debug.h - * - * Debugging-related stuff for nfsd - * - * Copyright (C) 1995 Olaf Kirch - */ -#ifndef LINUX_NFSD_DEBUG_H -#define LINUX_NFSD_DEBUG_H - -#include - -# undef ifdebug -# ifdef NFSD_DEBUG -# define ifdebug(flag) if (nfsd_debug & NFSDDBG_##flag) -# else -# define ifdebug(flag) if (0) -# endif -#endif /* LINUX_NFSD_DEBUG_H */ -- cgit v1.2.3 From ca654dc3a93d3b47dddc0c24a98043060bbb256b Mon Sep 17 00:00:00 2001 From: "Srivatsa S. Bhat" Date: Mon, 5 May 2014 12:52:39 +0530 Subject: cpufreq: Catch double invocations of cpufreq_freq_transition_begin/end Some cpufreq drivers were redundantly invoking the _begin() and _end() APIs around frequency transitions, and this double invocation (one from the cpufreq core and the other from the cpufreq driver) used to result in a self-deadlock, leading to system hangs during boot. (The _begin() API makes contending callers wait until the previous invocation is complete. Hence, the cpufreq driver would end up waiting on itself!). Now all such drivers have been fixed, but debugging this issue was not very straight-forward (even lockdep didn't catch this). So let us add a debug infrastructure to the cpufreq core to catch such issues more easily in the future. We add a new field called 'transition_task' to the policy structure, to keep track of the task which is performing the frequency transition. Using this field, we make note of this task during _begin() and print a warning if we find a case where the same task is calling _begin() again, before completing the previous frequency transition using the corresponding _end(). We have left out ASYNC_NOTIFICATION drivers from this debug infrastructure for 2 reasons: 1. At the moment, we have no way to avoid a particular scenario where this debug infrastructure can emit false-positive warnings for such drivers. The scenario is depicted below: Task A Task B /* 1st freq transition */ Invoke _begin() { ... ... } Change the frequency /* 2nd freq transition */ Invoke _begin() { ... //waiting for B to ... //finish _end() for ... //the 1st transition ... | Got interrupt for successful ... | change of frequency (1st one). ... | ... | /* 1st freq transition */ ... | Invoke _end() { ... | ... ... V } ... ... } This scenario is actually deadlock-free because, once Task A changes the frequency, it is Task B's responsibility to invoke the corresponding _end() for the 1st frequency transition. Hence it is perfectly legal for Task A to go ahead and attempt another frequency transition in the meantime. (Of course it won't be able to proceed until Task B finishes the 1st _end(), but this doesn't cause a deadlock or a hang). The debug infrastructure cannot handle this scenario and will treat it as a deadlock and print a warning. To avoid this, we exclude such drivers from the purview of this code. 2. Luckily, we don't _need_ this infrastructure for ASYNC_NOTIFICATION drivers at all! The cpufreq core does not automatically invoke the _begin() and _end() APIs during frequency transitions in such drivers. Thus, the driver alone is responsible for invoking _begin()/_end() and hence there shouldn't be any conflicts which lead to double invocations. So, we can skip these drivers, since the probability that such drivers will hit this problem is extremely low, as outlined above. Signed-off-by: Srivatsa S. Bhat Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 14 ++++++++++++++ include/linux/cpufreq.h | 1 + 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a517da996aaf..bfe82b63875f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -365,6 +365,18 @@ static void cpufreq_notify_post_transition(struct cpufreq_policy *policy, void cpufreq_freq_transition_begin(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs) { + + /* + * Catch double invocations of _begin() which lead to self-deadlock. + * ASYNC_NOTIFICATION drivers are left out because the cpufreq core + * doesn't invoke _begin() on their behalf, and hence the chances of + * double invocations are very low. Moreover, there are scenarios + * where these checks can emit false-positive warnings in these + * drivers; so we avoid that by skipping them altogether. + */ + WARN_ON(!(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION) + && current == policy->transition_task); + wait: wait_event(policy->transition_wait, !policy->transition_ongoing); @@ -376,6 +388,7 @@ wait: } policy->transition_ongoing = true; + policy->transition_task = current; spin_unlock(&policy->transition_lock); @@ -392,6 +405,7 @@ void cpufreq_freq_transition_end(struct cpufreq_policy *policy, cpufreq_notify_post_transition(policy, freqs, transition_failed); policy->transition_ongoing = false; + policy->transition_task = NULL; wake_up(&policy->transition_wait); } diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 77a5fa191502..f3822f836e14 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -110,6 +110,7 @@ struct cpufreq_policy { bool transition_ongoing; /* Tracks transition status */ spinlock_t transition_lock; wait_queue_head_t transition_wait; + struct task_struct *transition_task; /* Task which is doing the transition */ }; /* Only for ACPI */ -- cgit v1.2.3 From a0dd7b79657bd6644b914d16ce7f23468c44a7b4 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 5 May 2014 08:33:50 -0500 Subject: PM / OPP: Move cpufreq specific OPP functions out of generic OPP library CPUFreq specific helper functions for OPP (Operating Performance Points) now use generic OPP functions that allow CPUFreq to be be moved back into CPUFreq framework. This allows for independent modifications or future enhancements as needed isolated to just CPUFreq framework alone. Here, we just move relevant code and documentation to make this part of CPUFreq infrastructure. Cc: Kevin Hilman Signed-off-by: Nishanth Menon Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- Documentation/cpu-freq/core.txt | 29 +++++++++++ Documentation/power/opp.txt | 40 ++------------- drivers/base/power/opp.c | 92 --------------------------------- drivers/cpufreq/Makefile | 2 + drivers/cpufreq/cpufreq_opp.c | 110 ++++++++++++++++++++++++++++++++++++++++ include/linux/cpufreq.h | 21 ++++++++ include/linux/pm_opp.h | 20 -------- 7 files changed, 167 insertions(+), 147 deletions(-) create mode 100644 drivers/cpufreq/cpufreq_opp.c (limited to 'include') diff --git a/Documentation/cpu-freq/core.txt b/Documentation/cpu-freq/core.txt index 0060d76b445f..70933eadc308 100644 --- a/Documentation/cpu-freq/core.txt +++ b/Documentation/cpu-freq/core.txt @@ -20,6 +20,7 @@ Contents: --------- 1. CPUFreq core and interfaces 2. CPUFreq notifiers +3. CPUFreq Table Generation with Operating Performance Point (OPP) 1. General Information ======================= @@ -92,3 +93,31 @@ values: cpu - number of the affected CPU old - old frequency new - new frequency + +3. CPUFreq Table Generation with Operating Performance Point (OPP) +================================================================== +For details about OPP, see Documentation/power/opp.txt + +dev_pm_opp_init_cpufreq_table - cpufreq framework typically is initialized with + cpufreq_frequency_table_cpuinfo which is provided with the list of + frequencies that are available for operation. This function provides + a ready to use conversion routine to translate the OPP layer's internal + information about the available frequencies into a format readily + providable to cpufreq. + + WARNING: Do not use this function in interrupt context. + + Example: + soc_pm_init() + { + /* Do things */ + r = dev_pm_opp_init_cpufreq_table(dev, &freq_table); + if (!r) + cpufreq_frequency_table_cpuinfo(policy, freq_table); + /* Do other things */ + } + + NOTE: This function is available only if CONFIG_CPU_FREQ is enabled in + addition to CONFIG_PM_OPP. + +dev_pm_opp_free_cpufreq_table - Free up the table allocated by dev_pm_opp_init_cpufreq_table diff --git a/Documentation/power/opp.txt b/Documentation/power/opp.txt index b8a907dc0169..a9adad828cdc 100644 --- a/Documentation/power/opp.txt +++ b/Documentation/power/opp.txt @@ -10,8 +10,7 @@ Contents 3. OPP Search Functions 4. OPP Availability Control Functions 5. OPP Data Retrieval Functions -6. Cpufreq Table Generation -7. Data Structures +6. Data Structures 1. Introduction =============== @@ -72,7 +71,6 @@ operations until that OPP could be re-enabled if possible. OPP library facilitates this concept in it's implementation. The following operational functions operate only on available opps: opp_find_freq_{ceil, floor}, dev_pm_opp_get_voltage, dev_pm_opp_get_freq, dev_pm_opp_get_opp_count -and dev_pm_opp_init_cpufreq_table dev_pm_opp_find_freq_exact is meant to be used to find the opp pointer which can then be used for dev_pm_opp_enable/disable functions to make an opp available as required. @@ -96,10 +94,9 @@ using RCU read locks. The opp_find_freq_{exact,ceil,floor}, opp_get_{voltage, freq, opp_count} fall into this category. opp_{add,enable,disable} are updaters which use mutex and implement it's own -RCU locking mechanisms. dev_pm_opp_init_cpufreq_table acts as an updater and uses -mutex to implment RCU updater strategy. These functions should *NOT* be called -under RCU locks and other contexts that prevent blocking functions in RCU or -mutex operations from working. +RCU locking mechanisms. These functions should *NOT* be called under RCU locks +and other contexts that prevent blocking functions in RCU or mutex operations +from working. 2. Initial OPP List Registration ================================ @@ -311,34 +308,7 @@ dev_pm_opp_get_opp_count - Retrieve the number of available opps for a device /* Do other things */ } -6. Cpufreq Table Generation -=========================== -dev_pm_opp_init_cpufreq_table - cpufreq framework typically is initialized with - cpufreq_frequency_table_cpuinfo which is provided with the list of - frequencies that are available for operation. This function provides - a ready to use conversion routine to translate the OPP layer's internal - information about the available frequencies into a format readily - providable to cpufreq. - - WARNING: Do not use this function in interrupt context. - - Example: - soc_pm_init() - { - /* Do things */ - r = dev_pm_opp_init_cpufreq_table(dev, &freq_table); - if (!r) - cpufreq_frequency_table_cpuinfo(policy, freq_table); - /* Do other things */ - } - - NOTE: This function is available only if CONFIG_CPU_FREQ is enabled in - addition to CONFIG_PM as power management feature is required to - dynamically scale voltage and frequency in a system. - -dev_pm_opp_free_cpufreq_table - Free up the table allocated by dev_pm_opp_init_cpufreq_table - -7. Data Structures +6. Data Structures ================== Typically an SoC contains multiple voltage domains which are variable. Each domain is represented by a device pointer. The relationship to OPP can be diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 38b43bb20878..d9e376a6d19d 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -596,97 +595,6 @@ int dev_pm_opp_disable(struct device *dev, unsigned long freq) } EXPORT_SYMBOL_GPL(dev_pm_opp_disable); -#ifdef CONFIG_CPU_FREQ -/** - * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device - * @dev: device for which we do this operation - * @table: Cpufreq table returned back to caller - * - * Generate a cpufreq table for a provided device- this assumes that the - * opp list is already initialized and ready for usage. - * - * This function allocates required memory for the cpufreq table. It is - * expected that the caller does the required maintenance such as freeing - * the table as required. - * - * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM - * if no memory available for the operation (table is not populated), returns 0 - * if successful and table is populated. - * - * WARNING: It is important for the callers to ensure refreshing their copy of - * the table if any of the mentioned functions have been invoked in the interim. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Since we just use the regular accessor functions to access the internal data - * structures, we use RCU read lock inside this function. As a result, users of - * this function DONOT need to use explicit locks for invoking. - */ -int dev_pm_opp_init_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table) -{ - struct dev_pm_opp *opp; - struct cpufreq_frequency_table *freq_table = NULL; - int i, max_opps, ret = 0; - unsigned long rate; - - rcu_read_lock(); - - max_opps = dev_pm_opp_get_opp_count(dev); - if (max_opps <= 0) { - ret = max_opps ? max_opps : -ENODATA; - goto out; - } - - freq_table = kzalloc(sizeof(*freq_table) * (max_opps + 1), GFP_KERNEL); - if (!freq_table) { - ret = -ENOMEM; - goto out; - } - - for (i = 0, rate = 0; i < max_opps; i++, rate++) { - /* find next rate */ - opp = dev_pm_opp_find_freq_ceil(dev, &rate); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - goto out; - } - freq_table[i].driver_data = i; - freq_table[i].frequency = rate / 1000; - } - - freq_table[i].driver_data = i; - freq_table[i].frequency = CPUFREQ_TABLE_END; - - *table = &freq_table[0]; - -out: - rcu_read_unlock(); - if (ret) - kfree(freq_table); - - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); - -/** - * dev_pm_opp_free_cpufreq_table() - free the cpufreq table - * @dev: device for which we do this operation - * @table: table to free - * - * Free up the table allocated by dev_pm_opp_init_cpufreq_table - */ -void dev_pm_opp_free_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table) -{ - if (!table) - return; - - kfree(*table); - *table = NULL; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); -#endif /* CONFIG_CPU_FREQ */ - /** * dev_pm_opp_get_notifier() - find notifier_head of the device with opp * @dev: device pointer used to lookup device OPPs. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 0dbb963c1aef..738c8b7b17dc 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -1,5 +1,7 @@ # CPUfreq core obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o +obj-$(CONFIG_PM_OPP) += cpufreq_opp.o + # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o diff --git a/drivers/cpufreq/cpufreq_opp.c b/drivers/cpufreq/cpufreq_opp.c new file mode 100644 index 000000000000..c0c6f4a4eccf --- /dev/null +++ b/drivers/cpufreq/cpufreq_opp.c @@ -0,0 +1,110 @@ +/* + * Generic OPP helper interface for CPUFreq drivers + * + * Copyright (C) 2009-2014 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta + * Kevin Hilman + * + * 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 + +/** + * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device + * @dev: device for which we do this operation + * @table: Cpufreq table returned back to caller + * + * Generate a cpufreq table for a provided device- this assumes that the + * opp list is already initialized and ready for usage. + * + * This function allocates required memory for the cpufreq table. It is + * expected that the caller does the required maintenance such as freeing + * the table as required. + * + * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM + * if no memory available for the operation (table is not populated), returns 0 + * if successful and table is populated. + * + * WARNING: It is important for the callers to ensure refreshing their copy of + * the table if any of the mentioned functions have been invoked in the interim. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Since we just use the regular accessor functions to access the internal data + * structures, we use RCU read lock inside this function. As a result, users of + * this function DONOT need to use explicit locks for invoking. + */ +int dev_pm_opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + struct dev_pm_opp *opp; + struct cpufreq_frequency_table *freq_table = NULL; + int i, max_opps, ret = 0; + unsigned long rate; + + rcu_read_lock(); + + max_opps = dev_pm_opp_get_opp_count(dev); + if (max_opps <= 0) { + ret = max_opps ? max_opps : -ENODATA; + goto out; + } + + freq_table = kzalloc(sizeof(*freq_table) * (max_opps + 1), GFP_KERNEL); + if (!freq_table) { + ret = -ENOMEM; + goto out; + } + + for (i = 0, rate = 0; i < max_opps; i++, rate++) { + /* find next rate */ + opp = dev_pm_opp_find_freq_ceil(dev, &rate); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + goto out; + } + freq_table[i].driver_data = i; + freq_table[i].frequency = rate / 1000; + } + + freq_table[i].driver_data = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; + + *table = &freq_table[0]; + +out: + rcu_read_unlock(); + if (ret) + kfree(freq_table); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); + +/** + * dev_pm_opp_free_cpufreq_table() - free the cpufreq table + * @dev: device for which we do this operation + * @table: table to free + * + * Free up the table allocated by dev_pm_opp_init_cpufreq_table + */ +void dev_pm_opp_free_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + if (!table) + return; + + kfree(*table); + *table = NULL; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index f3822f836e14..9d803b529ac2 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -469,6 +469,27 @@ struct cpufreq_frequency_table { * order */ }; +#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP) +int dev_pm_opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table); +void dev_pm_opp_free_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table); +#else +static inline int dev_pm_opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table + **table) +{ + return -EINVAL; +} + +static inline void dev_pm_opp_free_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table + **table) +{ +} +#endif + + bool cpufreq_next_valid(struct cpufreq_frequency_table **pos); /* diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 5151b0059585..0330217abfad 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -15,7 +15,6 @@ #define __LINUX_OPP_H__ #include -#include #include struct dev_pm_opp; @@ -117,23 +116,4 @@ static inline int of_init_opp_table(struct device *dev) } #endif -#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP) -int dev_pm_opp_init_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table); -void dev_pm_opp_free_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table); -#else -static inline int dev_pm_opp_init_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table) -{ - return -EINVAL; -} - -static inline -void dev_pm_opp_free_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table) -{ -} -#endif /* CONFIG_CPU_FREQ */ - #endif /* __LINUX_OPP_H__ */ -- cgit v1.2.3 From 3035ff70e6e6e15351e865fcc69dff182103b115 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 30 Apr 2014 10:03:52 +0800 Subject: ACPICA: Update global variable definitions. No functional change. Move all of the public globals to acpixf.h for the convenience of users. Also: Adds #ifndef/#endif conditions arround ACPI_GLOBAL and ACPI_INIT_GLOBAL definition so that OSPMs might be able to: 1. Redefine ACPI_GLOBAL/ACPI_INIT_GLOBAL into no-op, and 2. Redefine external global variables into immediates to implement stubs for them. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acglobal.h | 134 +------------------------------------- include/acpi/acpixf.h | 143 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 132 insertions(+), 145 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index e7d73eb57366..115eedcade1e 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -44,144 +44,14 @@ #ifndef __ACGLOBAL_H__ #define __ACGLOBAL_H__ -/* - * Ensure that the globals are actually defined and initialized only once. - * - * The use of these macros allows a single list of globals (here) in order - * to simplify maintenance of the code. - */ -#ifdef DEFINE_ACPI_GLOBALS -#define ACPI_GLOBAL(type,name) \ - extern type name; \ - type name - -#define ACPI_INIT_GLOBAL(type,name,value) \ - type name=value - -#else -#define ACPI_GLOBAL(type,name) \ - extern type name - -#define ACPI_INIT_GLOBAL(type,name,value) \ - extern type name -#endif - -#ifdef DEFINE_ACPI_GLOBALS - -/* Public globals, available from outside ACPICA subsystem */ - /***************************************************************************** * - * Runtime configuration (static defaults that can be overriden at runtime) + * Globals related to the ACPI tables * ****************************************************************************/ -/* - * Enable "slack" in the AML interpreter? Default is FALSE, and the - * interpreter strictly follows the ACPI specification. Setting to TRUE - * allows the interpreter to ignore certain errors and/or bad AML constructs. - * - * Currently, these features are enabled by this flag: - * - * 1) Allow "implicit return" of last value in a control method - * 2) Allow access beyond the end of an operation region - * 3) Allow access to uninitialized locals/args (auto-init to integer 0) - * 4) Allow ANY object type to be a source operand for the Store() operator - * 5) Allow unresolved references (invalid target name) in package objects - * 6) Enable warning messages for behavior that is not ACPI spec compliant - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_interpreter_slack, FALSE); - -/* - * Automatically serialize all methods that create named objects? Default - * is TRUE, meaning that all non_serialized methods are scanned once at - * table load time to determine those that create named objects. Methods - * that create named objects are marked Serialized in order to prevent - * possible run-time problems if they are entered by more than one thread. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_auto_serialize_methods, TRUE); - -/* - * Create the predefined _OSI method in the namespace? Default is TRUE - * because ACPICA is fully compatible with other ACPI implementations. - * Changing this will revert ACPICA (and machine ASL) to pre-OSI behavior. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_create_osi_method, TRUE); - -/* - * Optionally use default values for the ACPI register widths. Set this to - * TRUE to use the defaults, if an FADT contains incorrect widths/lengths. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_use_default_register_widths, TRUE); - -/* - * Optionally enable output from the AML Debug Object. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_aml_debug_object, FALSE); - -/* - * Optionally copy the entire DSDT to local memory (instead of simply - * mapping it.) There are some BIOSs that corrupt or replace the original - * DSDT, creating the need for this option. Default is FALSE, do not copy - * the DSDT. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_copy_dsdt_locally, FALSE); - -/* - * Optionally ignore an XSDT if present and use the RSDT instead. - * Although the ACPI specification requires that an XSDT be used instead - * of the RSDT, the XSDT has been found to be corrupt or ill-formed on - * some machines. Default behavior is to use the XSDT if present. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE); - -/* - * Optionally use 32-bit FADT addresses if and when there is a conflict - * (address mismatch) between the 32-bit and 64-bit versions of the - * address. Although ACPICA adheres to the ACPI specification which - * requires the use of the corresponding 64-bit address if it is non-zero, - * some machines have been found to have a corrupted non-zero 64-bit - * address. Default is FALSE, do not favor the 32-bit addresses. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, FALSE); - -/* - * Optionally truncate I/O addresses to 16 bits. Provides compatibility - * with other ACPI implementations. NOTE: During ACPICA initialization, - * this value is set to TRUE if any Windows OSI strings have been - * requested by the BIOS. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_truncate_io_addresses, FALSE); - -/* - * Disable runtime checking and repair of values returned by control methods. - * Use only if the repair is causing a problem on a particular machine. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_auto_repair, FALSE); +/* Master list of all ACPI tables that were found in the RSDT/XSDT */ -/* - * Optionally do not install any SSDTs from the RSDT/XSDT during initialization. - * This can be useful for debugging ACPI problems on some machines. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_install, FALSE); - -/* - * We keep track of the latest version of Windows that has been requested by - * the BIOS. - */ -ACPI_INIT_GLOBAL(u8, acpi_gbl_osi_data, 0); - -#endif /* DEFINE_ACPI_GLOBALS */ - -/***************************************************************************** - * - * ACPI Table globals - * - ****************************************************************************/ - -/* - * Master list of all ACPI tables that were found in the RSDT/XSDT. - */ ACPI_GLOBAL(struct acpi_table_list, acpi_gbl_root_table_list); /* DSDT information. Used to check for DSDT corruption */ diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 913d0765adbe..010816e24f5e 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -56,13 +56,141 @@ extern u8 acpi_gbl_permanent_mmap; /* - * Globals that are publically available + * Ensure that the globals are actually defined and initialized only once. + * + * The use of these macros allows a single list of globals (here) in order + * to simplify maintenance of the code. + */ +#ifdef DEFINE_ACPI_GLOBALS +#define ACPI_GLOBAL(type,name) \ + extern type name; \ + type name + +#define ACPI_INIT_GLOBAL(type,name,value) \ + type name=value + +#else +#ifndef ACPI_GLOBAL +#define ACPI_GLOBAL(type,name) \ + extern type name +#endif + +#ifndef ACPI_INIT_GLOBAL +#define ACPI_INIT_GLOBAL(type,name,value) \ + extern type name +#endif +#endif + +/* Public globals, available from outside ACPICA subsystem */ + +/***************************************************************************** + * + * Runtime configuration (static defaults that can be overriden at runtime) + * + ****************************************************************************/ + +/* + * Enable "slack" in the AML interpreter? Default is FALSE, and the + * interpreter strictly follows the ACPI specification. Setting to TRUE + * allows the interpreter to ignore certain errors and/or bad AML constructs. + * + * Currently, these features are enabled by this flag: + * + * 1) Allow "implicit return" of last value in a control method + * 2) Allow access beyond the end of an operation region + * 3) Allow access to uninitialized locals/args (auto-init to integer 0) + * 4) Allow ANY object type to be a source operand for the Store() operator + * 5) Allow unresolved references (invalid target name) in package objects + * 6) Enable warning messages for behavior that is not ACPI spec compliant + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_interpreter_slack, FALSE); + +/* + * Automatically serialize all methods that create named objects? Default + * is TRUE, meaning that all non_serialized methods are scanned once at + * table load time to determine those that create named objects. Methods + * that create named objects are marked Serialized in order to prevent + * possible run-time problems if they are entered by more than one thread. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_auto_serialize_methods, TRUE); + +/* + * Create the predefined _OSI method in the namespace? Default is TRUE + * because ACPICA is fully compatible with other ACPI implementations. + * Changing this will revert ACPICA (and machine ASL) to pre-OSI behavior. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_create_osi_method, TRUE); + +/* + * Optionally use default values for the ACPI register widths. Set this to + * TRUE to use the defaults, if an FADT contains incorrect widths/lengths. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_use_default_register_widths, TRUE); + +/* + * Optionally enable output from the AML Debug Object. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_aml_debug_object, FALSE); + +/* + * Optionally copy the entire DSDT to local memory (instead of simply + * mapping it.) There are some BIOSs that corrupt or replace the original + * DSDT, creating the need for this option. Default is FALSE, do not copy + * the DSDT. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_copy_dsdt_locally, FALSE); + +/* + * Optionally ignore an XSDT if present and use the RSDT instead. + * Although the ACPI specification requires that an XSDT be used instead + * of the RSDT, the XSDT has been found to be corrupt or ill-formed on + * some machines. Default behavior is to use the XSDT if present. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE); + +/* + * Optionally use 32-bit FADT addresses if and when there is a conflict + * (address mismatch) between the 32-bit and 64-bit versions of the + * address. Although ACPICA adheres to the ACPI specification which + * requires the use of the corresponding 64-bit address if it is non-zero, + * some machines have been found to have a corrupted non-zero 64-bit + * address. Default is FALSE, do not favor the 32-bit addresses. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, FALSE); + +/* + * Optionally truncate I/O addresses to 16 bits. Provides compatibility + * with other ACPI implementations. NOTE: During ACPICA initialization, + * this value is set to TRUE if any Windows OSI strings have been + * requested by the BIOS. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_truncate_io_addresses, FALSE); + +/* + * Disable runtime checking and repair of values returned by control methods. + * Use only if the repair is causing a problem on a particular machine. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_auto_repair, FALSE); + +/* + * Optionally do not install any SSDTs from the RSDT/XSDT during initialization. + * This can be useful for debugging ACPI problems on some machines. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_install, FALSE); + +/* + * We keep track of the latest version of Windows that has been requested by + * the BIOS. ACPI 5.0. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_osi_data, 0); + +/* + * Other miscellaneous public globals */ extern u32 acpi_current_gpe_count; extern struct acpi_table_fadt acpi_gbl_FADT; extern u8 acpi_gbl_system_awake_and_running; extern u8 acpi_gbl_reduced_hardware; /* ACPI 5.0 */ -extern u8 acpi_gbl_osi_data; /* Runtime configuration of debug print levels */ @@ -71,19 +199,8 @@ extern u32 acpi_dbg_layer; /* ACPICA runtime options */ -extern u8 acpi_gbl_auto_serialize_methods; -extern u8 acpi_gbl_copy_dsdt_locally; -extern u8 acpi_gbl_create_osi_method; -extern u8 acpi_gbl_disable_auto_repair; -extern u8 acpi_gbl_disable_ssdt_table_install; -extern u8 acpi_gbl_do_not_use_xsdt; -extern u8 acpi_gbl_enable_aml_debug_object; -extern u8 acpi_gbl_enable_interpreter_slack; extern u32 acpi_gbl_trace_flags; extern acpi_name acpi_gbl_trace_method_name; -extern u8 acpi_gbl_truncate_io_addresses; -extern u8 acpi_gbl_use32_bit_fadt_addresses; -extern u8 acpi_gbl_use_default_register_widths; /* * Hardware-reduced prototypes. All interfaces that use these macros will -- cgit v1.2.3 From d36d4e30bd44e2e447a2346edb437e842df8c753 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Apr 2014 10:04:06 +0800 Subject: ACPICA: Add support for LPIT table. Adds header, disassembler, table compiler, and template support for the Low Power Idle Table (LPIT). Note that the disassembler and table compiler are not shipped in the kernel. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'include') diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index c8adad9c6b6a..687426168b0e 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -70,6 +70,7 @@ #define ACPI_SIG_HPET "HPET" /* High Precision Event Timer table */ #define ACPI_SIG_IBFT "IBFT" /* iSCSI Boot Firmware Table */ #define ACPI_SIG_IVRS "IVRS" /* I/O Virtualization Reporting Structure */ +#define ACPI_SIG_LPIT "LPIT" /* Low Power Idle Table */ #define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */ #define ACPI_SIG_MCHI "MCHI" /* Management Controller Host Interface table */ #define ACPI_SIG_MTMR "MTMR" /* MID Timer table */ @@ -818,6 +819,70 @@ struct acpi_ivrs_memory { u64 memory_length; }; +/******************************************************************************* + * + * LPIT - Low Power Idle Table + * + * Conforms to "ACPI Low Power Idle Table (LPIT) and _LPD Proposal (DRAFT)" + * + ******************************************************************************/ + +struct acpi_table_lpit { + struct acpi_table_header header; /* Common ACPI table header */ +}; + +/* LPIT subtable header */ + +struct acpi_lpit_header { + u32 type; /* Subtable type */ + u32 length; /* Subtable length */ + u16 unique_id; + u16 reserved; + u32 flags; +}; + +/* Values for subtable Type above */ + +enum acpi_lpit_type { + ACPI_LPIT_TYPE_NATIVE_CSTATE = 0x00, + ACPI_LPIT_TYPE_SIMPLE_IO = 0x01 +}; + +/* Masks for Flags field above */ + +#define ACPI_LPIT_STATE_DISABLED (1) +#define ACPI_LPIT_NO_COUNTER (1<<1) + +/* + * LPIT subtables, correspond to Type in struct acpi_lpit_header + */ + +/* 0x00: Native C-state instruction based LPI structure */ + +struct acpi_lpit_native { + struct acpi_lpit_header header; + struct acpi_generic_address entry_trigger; + u32 residency; + u32 latency; + struct acpi_generic_address residency_counter; + u64 counter_frequency; +}; + +/* 0x01: Simple I/O based LPI structure */ + +struct acpi_lpit_io { + struct acpi_lpit_header header; + struct acpi_generic_address entry_trigger; + u32 trigger_action; + u64 trigger_value; + u64 trigger_mask; + struct acpi_generic_address minimum_idle_state; + u32 residency; + u32 latency; + struct acpi_generic_address residency_counter; + u64 counter_frequency; +}; + /******************************************************************************* * * MCFG - PCI Memory Mapped Configuration table and sub-table -- cgit v1.2.3 From 1a49b72c4e0c7097b9d3cf8650ff17f87e07d8bd Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Apr 2014 10:04:28 +0800 Subject: ACPICA: Comment updates - no functional change. Change all instances of "sub-table" to "subtable" for consistency. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl1.h | 4 ++-- include/acpi/actbl2.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 212c65de75df..4ad7da805180 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -675,7 +675,7 @@ enum acpi_madt_type { }; /* - * MADT Sub-tables, correspond to Type in struct acpi_subtable_header + * MADT Subtables, correspond to Type in struct acpi_subtable_header */ /* 0: Processor Local APIC */ @@ -918,7 +918,7 @@ enum acpi_srat_type { }; /* - * SRAT Sub-tables, correspond to Type in struct acpi_subtable_header + * SRAT Subtables, correspond to Type in struct acpi_subtable_header */ /* 0: Processor Local APIC/SAPIC Affinity */ diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 687426168b0e..860e5c883eb3 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -457,7 +457,7 @@ struct acpi_dmar_pci_path { }; /* - * DMAR Sub-tables, correspond to Type in struct acpi_dmar_header + * DMAR Subtables, correspond to Type in struct acpi_dmar_header */ /* 0: Hardware Unit Definition */ @@ -885,7 +885,7 @@ struct acpi_lpit_io { /******************************************************************************* * - * MCFG - PCI Memory Mapped Configuration table and sub-table + * MCFG - PCI Memory Mapped Configuration table and subtable * Version 1 * * Conforms to "PCI Firmware Specification", Revision 3.0, June 20, 2005 @@ -988,7 +988,7 @@ enum acpi_slic_type { }; /* - * SLIC Sub-tables, correspond to Type in struct acpi_slic_header + * SLIC Subtables, correspond to Type in struct acpi_slic_header */ /* 0: Public Key Structure */ -- cgit v1.2.3 From 3a2f3a3383f87412fa11b89290d592a24f618487 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 30 Apr 2014 10:04:35 +0800 Subject: ACPICA: OSL: Move external globals from utglobal.c to acpixf.h using ACPI_INIT_GLOBAL/ACPI_GLOBAL. OSPMs like Linux trend to include all header files but leave empty stub macros for a feature that is not configured during build. This patch cleans up global variables that are defined in utglobal.c using ACPI_INIT_GLOBAL mechanism. In Linux, such global variables are used by the subsystems external to ACPICA. This patch also cleans up global variables that are defined in utglobal.c using ACPI_GLOBAL mechanism. In Linux, such global variables are not used or should not be used by the subsystems external to ACPICA. External global variables can be redefined by OSPMs using ACPI_INIT_GLOBAL/ACPI_GLOBAL macros. Thus the ACPI_GLOBAL/ACPI_INIT_GLOBAL mechanisms can be used by OSPM to implement stubs for such external globals. This patch doesn't include code for Linux to use this new mechanism, thus no functional changes. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utglobal.c | 22 ---------------------- include/acpi/acpixf.h | 35 +++++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 825b064e21f3..d69be3cb3fae 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -55,28 +55,7 @@ ACPI_MODULE_NAME("utglobal") * Static global variable initialization. * ******************************************************************************/ -/* Debug output control masks */ -u32 acpi_dbg_level = ACPI_DEBUG_DEFAULT; - -u32 acpi_dbg_layer = 0; - -/* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */ - -struct acpi_table_fadt acpi_gbl_FADT; -u32 acpi_gbl_trace_flags; -acpi_name acpi_gbl_trace_method_name; -u8 acpi_gbl_system_awake_and_running; -u32 acpi_current_gpe_count; - -/* - * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning - * that the ACPI hardware is no longer required. A flag in the FADT indicates - * a reduced HW machine, and that flag is duplicated here for convenience. - */ -u8 acpi_gbl_reduced_hardware; - /* Various state name strings */ - const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = { "\\_S0_", "\\_S1_", @@ -337,7 +316,6 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_acpi_hardware_present = TRUE; acpi_gbl_last_owner_id_index = 0; acpi_gbl_next_owner_id_offset = 0; - acpi_gbl_trace_method_name = 0; acpi_gbl_trace_dbg_level = 0; acpi_gbl_trace_dbg_layer = 0; acpi_gbl_debugger_configuration = DEBUGGER_THREADING; diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 010816e24f5e..8255689c864b 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -185,22 +185,33 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_install, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_osi_data, 0); /* - * Other miscellaneous public globals + * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning + * that the ACPI hardware is no longer required. A flag in the FADT indicates + * a reduced HW machine, and that flag is duplicated here for convenience. */ -extern u32 acpi_current_gpe_count; -extern struct acpi_table_fadt acpi_gbl_FADT; -extern u8 acpi_gbl_system_awake_and_running; -extern u8 acpi_gbl_reduced_hardware; /* ACPI 5.0 */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_reduced_hardware, FALSE); -/* Runtime configuration of debug print levels */ - -extern u32 acpi_dbg_level; -extern u32 acpi_dbg_layer; +/* + * This mechanism is used to trace a specified AML method. The method is + * traced each time it is executed. + */ +ACPI_INIT_GLOBAL(u32, acpi_gbl_trace_flags, 0); +ACPI_INIT_GLOBAL(acpi_name, acpi_gbl_trace_method_name, 0); -/* ACPICA runtime options */ +/* + * Runtime configuration of debug output control masks. We want the debug + * switches statically initialized so they are already set when the debugger + * is entered. + */ +ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT); +ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0); -extern u32 acpi_gbl_trace_flags; -extern acpi_name acpi_gbl_trace_method_name; +/* + * Globals that are publically available + */ +ACPI_GLOBAL(u32, acpi_current_gpe_count); +ACPI_GLOBAL(struct acpi_table_fadt, acpi_gbl_FADT); +ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); /* * Hardware-reduced prototypes. All interfaces that use these macros will -- cgit v1.2.3 From d5caf1cdc41c311cb5d4d2c010f90809f319c7dd Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 30 Apr 2014 10:04:42 +0800 Subject: ACPICA: OSL: Add configurability for memory allocation macros. OSPMs like Linux trend to include all header files but leave empty stub macros for a feature that is not configured during build. For macros defined without other symbols referencesd it is safe to leave them without protections. By investigation, there are only the following internal/external symbols referenced by the ACPICA macros: 1. C library symbols, including string, ctype, stdarg APIs. Since such symbols are always accessbile in the kernel source tree, it is safe to leave macros referencing them without protected for Linux. 2. ACPICA OSL symbols, such symbols are designed to be used only by ACPICA internal APIs. And there are macros directly referencing mutex and memory allocation OSL symbols. We need to examine the external usages of such macros. For macros referencing the mutex OSL symbols, fortunately, there is no external user directly invoking such macros. ======================================================================== !! IMPORTANT !! ======================================================================== For macros referencing memory allocation OSL symbols - 1. 'free' - ACPI_FREE 2. 'alloc' - ACPI_ALLOCATE, ACPI_ALLOCATE_ZEROED, ACPI_ALLOCATE_BUFFER, ACPI_ALLOCATE_LOCAL_BUFFER there are external users directly invoking 'alloc' macros. And the more complicated situation is the reversals of such macros are not ACPI_FREE but acpi_os_free (or kfree) in Linux. Though we can define such macros into no-op, we in fact cannot define their reversals into no-op. This patch adds mechanism to protect ACPICA memory allocation APIs for Linux so that acpi_os_free (or kfree) invoked in Linux can have a zero address returned by 'alloc' macros to free. In this way, acpi_os_free (or kfree) can be converted into no-op. ======================================================================== 3. ACPI_OFFSET and other macros that would access structure members, we need to check if such structure members are not accessible under a specific configuration. Fortunately, currently Linux doesn't use such structure members when CONFIG_ACPI is disabled. This patch thus only adds mechanism useful for implementing stubs for ACPICA provided macros - the configurability of memory allocation APIs. This patch doesn't include code for Linux to use this new mechanism, thus no functional changes. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/actypes.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index e76356574374..19b26bb69a70 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -329,6 +329,15 @@ typedef u32 acpi_physical_address; * ******************************************************************************/ +#ifdef ACPI_NO_MEM_ALLOCATIONS + +#define ACPI_ALLOCATE(a) NULL +#define ACPI_ALLOCATE_ZEROED(a) NULL +#define ACPI_FREE(a) +#define ACPI_MEM_TRACKING(a) + +#else /* ACPI_NO_MEM_ALLOCATIONS */ + #ifdef ACPI_DBG_TRACK_ALLOCATIONS /* * Memory allocation tracking (used by acpi_exec to detect memory leaks) @@ -350,6 +359,8 @@ typedef u32 acpi_physical_address; #endif /* ACPI_DBG_TRACK_ALLOCATIONS */ +#endif /* ACPI_NO_MEM_ALLOCATIONS */ + /****************************************************************************** * * ACPI Specification constants (Do not change unless the specification changes) @@ -928,9 +939,19 @@ struct acpi_object_list { * Miscellaneous common Data Structures used by the interfaces */ #define ACPI_NO_BUFFER 0 + +#ifdef ACPI_NO_MEM_ALLOCATIONS + +#define ACPI_ALLOCATE_BUFFER (acpi_size) (0) +#define ACPI_ALLOCATE_LOCAL_BUFFER (acpi_size) (0) + +#else /* ACPI_NO_MEM_ALLOCATIONS */ + #define ACPI_ALLOCATE_BUFFER (acpi_size) (-1) /* Let ACPICA allocate buffer */ #define ACPI_ALLOCATE_LOCAL_BUFFER (acpi_size) (-2) /* For internal use only (enables tracking) */ +#endif /* ACPI_NO_MEM_ALLOCATIONS */ + struct acpi_buffer { acpi_size length; /* Length in bytes of the buffer */ void *pointer; /* pointer to buffer */ -- cgit v1.2.3 From 407e22afcf0701ec5543f39934c43b10082a1a97 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 30 Apr 2014 10:04:48 +0800 Subject: ACPICA: OSL: Add configurability for error message functions. This patch extends ACPI_HW_DEPENDENT_x mechanism to all error message related functions so that the OSPMs can have full control to configure them into stub functions. This patch doesn't include code for Linux to use this new mechanism, thus no functional change. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utxferror.c | 2 ++ include/acpi/acpixf.h | 73 ++++++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c index edd861102f1b..88ef77f3cf88 100644 --- a/drivers/acpi/acpica/utxferror.c +++ b/drivers/acpi/acpica/utxferror.c @@ -53,6 +53,7 @@ ACPI_MODULE_NAME("utxferror") * This module is used for the in-kernel ACPICA as well as the ACPICA * tools/applications. */ +#ifndef ACPI_NO_ERROR_MESSAGES /* Entire module */ /******************************************************************************* * * FUNCTION: acpi_error @@ -249,3 +250,4 @@ acpi_bios_warning(const char *module_name, } ACPI_EXPORT_SYMBOL(acpi_bios_warning) +#endif /* ACPI_NO_ERROR_MESSAGES */ diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 8255689c864b..39f432e513f2 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -240,6 +240,21 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); #endif /* !ACPI_REDUCED_HARDWARE */ +/* + * Error-message prototypes. All interfaces that use these macros will + * be configured out of the ACPICA build if the ACPI_NO_ERROR_MESSAGE flag + * is defined. + */ +#ifndef ACPI_NO_ERROR_MESSAGES +#define ACPI_MSG_DEPENDENT_RETURN_VOID(prototype) \ + prototype; + +#else +#define ACPI_MSG_DEPENDENT_RETURN_VOID(prototype) \ + static ACPI_INLINE prototype {return;} + +#endif /* ACPI_NO_ERROR_MESSAGES */ + /* * Initialization */ @@ -666,38 +681,42 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status /* * Error/Warning output */ -ACPI_PRINTF_LIKE(3) -void ACPI_INTERNAL_VAR_XFACE -acpi_error(const char *module_name, u32 line_number, const char *format, ...); - -ACPI_PRINTF_LIKE(4) -void ACPI_INTERNAL_VAR_XFACE -acpi_exception(const char *module_name, - u32 line_number, acpi_status status, const char *format, ...); - -ACPI_PRINTF_LIKE(3) -void ACPI_INTERNAL_VAR_XFACE -acpi_warning(const char *module_name, u32 line_number, const char *format, ...); - -ACPI_PRINTF_LIKE(3) -void ACPI_INTERNAL_VAR_XFACE -acpi_info(const char *module_name, u32 line_number, const char *format, ...); - -ACPI_PRINTF_LIKE(3) -void ACPI_INTERNAL_VAR_XFACE -acpi_bios_error(const char *module_name, - u32 line_number, const char *format, ...); - -ACPI_PRINTF_LIKE(3) -void ACPI_INTERNAL_VAR_XFACE -acpi_bios_warning(const char *module_name, - u32 line_number, const char *format, ...); +ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3) + void ACPI_INTERNAL_VAR_XFACE + acpi_error(const char *module_name, + u32 line_number, + const char *format, ...)) +ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(4) + void ACPI_INTERNAL_VAR_XFACE + acpi_exception(const char *module_name, + u32 line_number, + acpi_status status, + const char *format, ...)) +ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3) + void ACPI_INTERNAL_VAR_XFACE + acpi_warning(const char *module_name, + u32 line_number, + const char *format, ...)) +ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3) + void ACPI_INTERNAL_VAR_XFACE + acpi_info(const char *module_name, + u32 line_number, + const char *format, ...)) +ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3) + void ACPI_INTERNAL_VAR_XFACE + acpi_bios_error(const char *module_name, + u32 line_number, + const char *format, ...)) +ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3) + void ACPI_INTERNAL_VAR_XFACE + acpi_bios_warning(const char *module_name, + u32 line_number, + const char *format, ...)) /* * Debug output */ #ifdef ACPI_DEBUG_OUTPUT - ACPI_PRINTF_LIKE(6) void ACPI_INTERNAL_VAR_XFACE acpi_debug_print(u32 requested_debug_level, -- cgit v1.2.3 From 1ce28c32d8deeca3cd53802e8cd710b9b4799398 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 30 Apr 2014 10:04:55 +0800 Subject: ACPICA: OSL: Add configurability for debug output functions. This patch extends ACPI_HW_DEPENDENT_x mechanism to all debugging output related functions so that the OSPMs can have full control to configure them into stub functions. This patch doesn't include code for Linux to use this new mechanism, thus no functional change. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 39f432e513f2..923775a5e9e9 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -255,6 +255,21 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); #endif /* ACPI_NO_ERROR_MESSAGES */ +/* + * Debugging-output prototypes. All interfaces that use these macros will + * be configured out of the ACPICA build if the ACPI_DEBUG_OUTPUT flag is + * not defined. + */ +#ifdef ACPI_DEBUG_OUTPUT +#define ACPI_DBG_DEPENDENT_RETURN_VOID(prototype) \ + prototype; + +#else +#define ACPI_DBG_DEPENDENT_RETURN_VOID(prototype) \ + static ACPI_INLINE prototype {return;} + +#endif /* ACPI_DEBUG_OUTPUT */ + /* * Initialization */ @@ -716,22 +731,20 @@ ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3) /* * Debug output */ -#ifdef ACPI_DEBUG_OUTPUT -ACPI_PRINTF_LIKE(6) -void ACPI_INTERNAL_VAR_XFACE -acpi_debug_print(u32 requested_debug_level, - u32 line_number, - const char *function_name, - const char *module_name, - u32 component_id, const char *format, ...); - -ACPI_PRINTF_LIKE(6) -void ACPI_INTERNAL_VAR_XFACE -acpi_debug_print_raw(u32 requested_debug_level, - u32 line_number, - const char *function_name, - const char *module_name, - u32 component_id, const char *format, ...); -#endif - +ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6) + void ACPI_INTERNAL_VAR_XFACE + acpi_debug_print(u32 requested_debug_level, + u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, + const char *format, ...)) +ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6) + void ACPI_INTERNAL_VAR_XFACE + acpi_debug_print_raw(u32 requested_debug_level, + u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, + const char *format, ...)) #endif /* __ACXFACE_H__ */ -- cgit v1.2.3 From 0dedb3c43cae6968ef6c2351e081f38eced47bfa Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 30 Apr 2014 10:05:02 +0800 Subject: ACPICA: OSL: Add section to collect the divergence in acpixf.h. This patch re-orders the interface prototypes defined in acpixf.h, moving those having not back ported to ACPICA into a seperate section to reduce the source code differences between Linux and ACPICA. This can help to reduce the cost of linuxizing the follow up commits. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 923775a5e9e9..7980c87643a5 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -338,17 +338,10 @@ acpi_status __init acpi_reallocate_root_table(void); acpi_status __init acpi_find_root_pointer(acpi_size *rsdp_address); -acpi_status acpi_unload_table_id(acpi_owner_id id); - acpi_status acpi_get_table_header(acpi_string signature, u32 instance, struct acpi_table_header *out_table_header); -acpi_status -acpi_get_table_with_size(acpi_string signature, - u32 instance, struct acpi_table_header **out_table, - acpi_size *tbl_size); - acpi_status acpi_get_table(acpi_string signature, u32 instance, struct acpi_table_header **out_table); @@ -390,10 +383,6 @@ acpi_attach_data(acpi_handle object, acpi_object_handler handler, void *data); acpi_status acpi_detach_data(acpi_handle object, acpi_object_handler handler); -acpi_status -acpi_get_data_full(acpi_handle object, acpi_object_handler handler, void **data, - void (*callback)(void *)); - acpi_status acpi_get_data(acpi_handle object, acpi_object_handler handler, void **data); @@ -429,8 +418,6 @@ acpi_get_next_object(acpi_object_type type, acpi_status acpi_get_type(acpi_handle object, acpi_object_type * out_type); -acpi_status acpi_get_id(acpi_handle object, acpi_owner_id * out_type); - acpi_status acpi_get_parent(acpi_handle object, acpi_handle * out_handle); /* @@ -747,4 +734,21 @@ ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6) const char *module_name, u32 component_id, const char *format, ...)) + +/* + * Divergences + */ +acpi_status acpi_get_id(acpi_handle object, acpi_owner_id * out_type); + +acpi_status acpi_unload_table_id(acpi_owner_id id); + +acpi_status +acpi_get_table_with_size(acpi_string signature, + u32 instance, struct acpi_table_header **out_table, + acpi_size *tbl_size); + +acpi_status +acpi_get_data_full(acpi_handle object, acpi_object_handler handler, void **data, + void (*callback)(void *)); + #endif /* __ACXFACE_H__ */ -- cgit v1.2.3 From 8b9c1152a018883f6f0b841e12e17671f2c64c32 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 30 Apr 2014 10:05:08 +0800 Subject: ACPICA: OSL: Add configurability for generic external functions. OSPMs like Linux trend to include all header files but leave empty inline stub functions for a feature that is not configured during build. This patch adds wrappers mechanism to be used around ACPICA external interfaces to facilitate OSPM with such configurability. This patch doesn't include code for Linux to use this new mechanism, thus no functional change. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 483 +++++++++++++++++++++++++++++--------------------- 1 file changed, 283 insertions(+), 200 deletions(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 7980c87643a5..ac04985017c5 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -81,6 +81,33 @@ extern u8 acpi_gbl_permanent_mmap; #endif #endif +/* ACPICA prototypes */ + +#ifndef ACPI_EXTERNAL_RETURN_STATUS +#define ACPI_EXTERNAL_RETURN_STATUS(prototype) \ + prototype; +#endif + +#ifndef ACPI_EXTERNAL_RETURN_OK +#define ACPI_EXTERNAL_RETURN_OK(prototype) \ + prototype; +#endif + +#ifndef ACPI_EXTERNAL_RETURN_VOID +#define ACPI_EXTERNAL_RETURN_VOID(prototype) \ + prototype; +#endif + +#ifndef ACPI_EXTERNAL_RETURN_UINT32 +#define ACPI_EXTERNAL_RETURN_UINT32(prototype) \ + prototype; +#endif + +#ifndef ACPI_EXTERNAL_RETURN_PTR +#define ACPI_EXTERNAL_RETURN_PTR(prototype) \ + prototype; +#endif + /* Public globals, available from outside ACPICA subsystem */ /***************************************************************************** @@ -220,13 +247,13 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); */ #if (!ACPI_REDUCED_HARDWARE) #define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \ - prototype; + ACPI_EXTERNAL_RETURN_STATUS(prototype) #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \ - prototype; + ACPI_EXTERNAL_RETURN_OK(prototype) #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \ - prototype; + ACPI_EXTERNAL_RETURN_VOID(prototype) #else #define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \ @@ -273,17 +300,18 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); /* * Initialization */ -acpi_status __init -acpi_initialize_tables(struct acpi_table_desc *initial_storage, - u32 initial_table_count, u8 allow_resize); - -acpi_status __init acpi_initialize_subsystem(void); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init + acpi_initialize_tables(struct acpi_table_desc + *initial_storage, + u32 initial_table_count, + u8 allow_resize)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_initialize_subsystem(void)) -acpi_status __init acpi_enable_subsystem(u32 flags); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_enable_subsystem(u32 flags)) -acpi_status __init acpi_initialize_objects(u32 flags); - -acpi_status __init acpi_terminate(void); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init + acpi_initialize_objects(u32 flags)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_terminate(void)) /* * Miscellaneous global interfaces @@ -291,145 +319,170 @@ acpi_status __init acpi_terminate(void); ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable(void)) #ifdef ACPI_FUTURE_USAGE -acpi_status acpi_subsystem_status(void); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_subsystem_status(void)) #endif #ifdef ACPI_FUTURE_USAGE -acpi_status acpi_get_system_info(struct acpi_buffer *ret_buffer); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_system_info(struct acpi_buffer + *ret_buffer)) #endif - -acpi_status acpi_get_statistics(struct acpi_statistics *stats); - -const char *acpi_format_exception(acpi_status exception); - -acpi_status acpi_purge_cached_objects(void); - -acpi_status acpi_install_interface(acpi_string interface_name); - -acpi_status acpi_remove_interface(acpi_string interface_name); - -acpi_status acpi_update_interfaces(u8 action); - -u32 -acpi_check_address_range(acpi_adr_space_type space_id, - acpi_physical_address address, - acpi_size length, u8 warn); - -acpi_status -acpi_decode_pld_buffer(u8 *in_buffer, - acpi_size length, struct acpi_pld_info **return_buffer); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_statistics(struct acpi_statistics *stats)) +ACPI_EXTERNAL_RETURN_PTR(const char + *acpi_format_exception(acpi_status exception)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_purge_cached_objects(void)) + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_install_interface(acpi_string interface_name)) + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_remove_interface(acpi_string interface_name)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_update_interfaces(u8 action)) + +ACPI_EXTERNAL_RETURN_UINT32(u32 + acpi_check_address_range(acpi_adr_space_type + space_id, + acpi_physical_address + address, acpi_size length, + u8 warn)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_decode_pld_buffer(u8 *in_buffer, + acpi_size length, + struct acpi_pld_info + **return_buffer)) /* * ACPI table load/unload interfaces */ -acpi_status __init -acpi_install_table(acpi_physical_address address, u8 physical); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init + acpi_install_table(acpi_physical_address address, + u8 physical)) -acpi_status acpi_load_table(struct acpi_table_header *table); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_load_table(struct acpi_table_header *table)) -acpi_status acpi_unload_parent_table(acpi_handle object); - -acpi_status __init acpi_load_tables(void); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_unload_parent_table(acpi_handle object)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_load_tables(void)) /* * ACPI table manipulation interfaces */ -acpi_status __init acpi_reallocate_root_table(void); - -acpi_status __init acpi_find_root_pointer(acpi_size *rsdp_address); - -acpi_status -acpi_get_table_header(acpi_string signature, - u32 instance, struct acpi_table_header *out_table_header); - -acpi_status -acpi_get_table(acpi_string signature, - u32 instance, struct acpi_table_header **out_table); - -acpi_status -acpi_get_table_by_index(u32 table_index, struct acpi_table_header **out_table); - -acpi_status -acpi_install_table_handler(acpi_table_handler handler, void *context); - -acpi_status acpi_remove_table_handler(acpi_table_handler handler); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_reallocate_root_table(void)) + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init + acpi_find_root_pointer(acpi_size * rsdp_address)) + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_table_header(acpi_string signature, + u32 instance, + struct acpi_table_header + *out_table_header)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_table(acpi_string signature, u32 instance, + struct acpi_table_header + **out_table)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_table_by_index(u32 table_index, + struct acpi_table_header + **out_table)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_install_table_handler(acpi_table_handler + handler, void *context)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_remove_table_handler(acpi_table_handler + handler)) /* * Namespace and name interfaces */ -acpi_status -acpi_walk_namespace(acpi_object_type type, - acpi_handle start_object, - u32 max_depth, - acpi_walk_callback descending_callback, - acpi_walk_callback ascending_callback, - void *context, void **return_value); - -acpi_status -acpi_get_devices(const char *HID, - acpi_walk_callback user_function, - void *context, void **return_value); - -acpi_status -acpi_get_name(acpi_handle object, - u32 name_type, struct acpi_buffer *ret_path_ptr); - -acpi_status -acpi_get_handle(acpi_handle parent, - acpi_string pathname, acpi_handle * ret_handle); - -acpi_status -acpi_attach_data(acpi_handle object, acpi_object_handler handler, void *data); - -acpi_status acpi_detach_data(acpi_handle object, acpi_object_handler handler); - -acpi_status -acpi_get_data(acpi_handle object, acpi_object_handler handler, void **data); - -acpi_status -acpi_debug_trace(char *name, u32 debug_level, u32 debug_layer, u32 flags); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_walk_namespace(acpi_object_type type, + acpi_handle start_object, + u32 max_depth, + acpi_walk_callback + descending_callback, + acpi_walk_callback + ascending_callback, + void *context, + void **return_value)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_devices(const char *HID, + acpi_walk_callback user_function, + void *context, + void **return_value)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_name(acpi_handle object, u32 name_type, + struct acpi_buffer *ret_path_ptr)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_handle(acpi_handle parent, + acpi_string pathname, + acpi_handle * ret_handle)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_attach_data(acpi_handle object, + acpi_object_handler handler, + void *data)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_detach_data(acpi_handle object, + acpi_object_handler handler)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_data(acpi_handle object, + acpi_object_handler handler, + void **data)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_debug_trace(char *name, u32 debug_level, + u32 debug_layer, u32 flags)) /* * Object manipulation and enumeration */ -acpi_status -acpi_evaluate_object(acpi_handle object, - acpi_string pathname, - struct acpi_object_list *parameter_objects, - struct acpi_buffer *return_object_buffer); - -acpi_status -acpi_evaluate_object_typed(acpi_handle object, - acpi_string pathname, - struct acpi_object_list *external_params, - struct acpi_buffer *return_buffer, - acpi_object_type return_type); - -acpi_status -acpi_get_object_info(acpi_handle object, - struct acpi_device_info **return_buffer); - -acpi_status acpi_install_method(u8 *buffer); - -acpi_status -acpi_get_next_object(acpi_object_type type, - acpi_handle parent, - acpi_handle child, acpi_handle * out_handle); - -acpi_status acpi_get_type(acpi_handle object, acpi_object_type * out_type); - -acpi_status acpi_get_parent(acpi_handle object, acpi_handle * out_handle); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_evaluate_object(acpi_handle object, + acpi_string pathname, + struct acpi_object_list + *parameter_objects, + struct acpi_buffer + *return_object_buffer)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_evaluate_object_typed(acpi_handle object, + acpi_string pathname, + struct acpi_object_list + *external_params, + struct acpi_buffer + *return_buffer, + acpi_object_type + return_type)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_object_info(acpi_handle object, + struct acpi_device_info + **return_buffer)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_install_method(u8 *buffer)) + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_next_object(acpi_object_type type, + acpi_handle parent, + acpi_handle child, + acpi_handle * out_handle)) + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_type(acpi_handle object, + acpi_object_type * out_type)) + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_parent(acpi_handle object, + acpi_handle * out_handle)) /* * Handler interfaces */ -acpi_status -acpi_install_initialization_handler(acpi_init_handler handler, u32 function); - +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_install_initialization_handler + (acpi_init_handler handler, u32 function)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status - acpi_install_sci_handler(acpi_sci_handler - address, - void *context)) + acpi_install_sci_handler(acpi_sci_handler + address, + void *context)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_remove_sci_handler(acpi_sci_handler address)) @@ -461,30 +514,42 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status u32 gpe_number, acpi_gpe_handler address)) -acpi_status acpi_install_notify_handler(acpi_handle device, u32 handler_type, - acpi_notify_handler handler, - void *context); - -acpi_status -acpi_remove_notify_handler(acpi_handle device, - u32 handler_type, acpi_notify_handler handler); - -acpi_status -acpi_install_address_space_handler(acpi_handle device, - acpi_adr_space_type space_id, - acpi_adr_space_handler handler, - acpi_adr_space_setup setup, void *context); - -acpi_status -acpi_remove_address_space_handler(acpi_handle device, - acpi_adr_space_type space_id, - acpi_adr_space_handler handler); - +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_install_notify_handler(acpi_handle device, + u32 handler_type, + acpi_notify_handler + handler, + void *context)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_remove_notify_handler(acpi_handle device, + u32 handler_type, + acpi_notify_handler + handler)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_install_address_space_handler(acpi_handle + device, + acpi_adr_space_type + space_id, + acpi_adr_space_handler + handler, + acpi_adr_space_setup + setup, + void *context)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_remove_address_space_handler(acpi_handle + device, + acpi_adr_space_type + space_id, + acpi_adr_space_handler + handler)) #ifdef ACPI_FUTURE_USAGE -acpi_status acpi_install_exception_handler(acpi_exception_handler handler); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_install_exception_handler + (acpi_exception_handler handler)) #endif - -acpi_status acpi_install_interface_handler(acpi_interface_handler handler); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_install_interface_handler + (acpi_interface_handler handler)) /* * Global Lock interfaces @@ -499,10 +564,14 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status /* * Interfaces to AML mutex objects */ -acpi_status -acpi_acquire_mutex(acpi_handle handle, acpi_string pathname, u16 timeout); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_acquire_mutex(acpi_handle handle, + acpi_string pathname, + u16 timeout)) -acpi_status acpi_release_mutex(acpi_handle handle, acpi_string pathname); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_release_mutex(acpi_handle handle, + acpi_string pathname)) /* * Fixed Event interfaces @@ -582,57 +651,69 @@ typedef acpi_status(*acpi_walk_resource_callback) (struct acpi_resource * resource, void *context); -acpi_status -acpi_get_vendor_resource(acpi_handle device, - char *name, - struct acpi_vendor_uuid *uuid, - struct acpi_buffer *ret_buffer); - -acpi_status -acpi_get_current_resources(acpi_handle device, struct acpi_buffer *ret_buffer); - +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_vendor_resource(acpi_handle device, + char *name, + struct acpi_vendor_uuid + *uuid, + struct acpi_buffer + *ret_buffer)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_current_resources(acpi_handle device, + struct acpi_buffer + *ret_buffer)) #ifdef ACPI_FUTURE_USAGE -acpi_status -acpi_get_possible_resources(acpi_handle device, struct acpi_buffer *ret_buffer); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_possible_resources(acpi_handle device, + struct acpi_buffer + *ret_buffer)) #endif - -acpi_status -acpi_get_event_resources(acpi_handle device_handle, - struct acpi_buffer *ret_buffer); - -acpi_status -acpi_walk_resource_buffer(struct acpi_buffer *buffer, - acpi_walk_resource_callback user_function, - void *context); - -acpi_status -acpi_walk_resources(acpi_handle device, - char *name, - acpi_walk_resource_callback user_function, void *context); - -acpi_status -acpi_set_current_resources(acpi_handle device, struct acpi_buffer *in_buffer); - -acpi_status -acpi_get_irq_routing_table(acpi_handle device, struct acpi_buffer *ret_buffer); - -acpi_status -acpi_resource_to_address64(struct acpi_resource *resource, - struct acpi_resource_address64 *out); - -acpi_status -acpi_buffer_to_resource(u8 *aml_buffer, - u16 aml_buffer_length, - struct acpi_resource **resource_ptr); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_event_resources(acpi_handle device_handle, + struct acpi_buffer + *ret_buffer)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_walk_resource_buffer(struct acpi_buffer + *buffer, + acpi_walk_resource_callback + user_function, + void *context)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_walk_resources(acpi_handle device, char *name, + acpi_walk_resource_callback + user_function, void *context)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_set_current_resources(acpi_handle device, + struct acpi_buffer + *in_buffer)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_irq_routing_table(acpi_handle device, + struct acpi_buffer + *ret_buffer)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_resource_to_address64(struct acpi_resource + *resource, + struct + acpi_resource_address64 + *out)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_buffer_to_resource(u8 *aml_buffer, + u16 aml_buffer_length, + struct acpi_resource + **resource_ptr)) /* * Hardware (ACPI device) interfaces */ -acpi_status acpi_reset(void); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_reset(void)) -acpi_status acpi_read(u64 *value, struct acpi_generic_address *reg); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_read(u64 *value, + struct acpi_generic_address *reg)) -acpi_status acpi_write(u64 value, struct acpi_generic_address *reg); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_write(u64 value, + struct acpi_generic_address *reg)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_read_bit_register(u32 register_id, @@ -645,18 +726,20 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status /* * Sleep/Wake interfaces */ -acpi_status -acpi_get_sleep_type_data(u8 sleep_state, u8 *slp_typ_a, u8 *slp_typ_b); - -acpi_status acpi_enter_sleep_state_prep(u8 sleep_state); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_sleep_type_data(u8 sleep_state, + u8 *slp_typ_a, + u8 *slp_typ_b)) -acpi_status acpi_enter_sleep_state(u8 sleep_state); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_enter_sleep_state_prep(u8 sleep_state)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_enter_sleep_state(u8 sleep_state)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enter_sleep_state_s4bios(void)) -acpi_status acpi_leave_sleep_state_prep(u8 sleep_state); - -acpi_status acpi_leave_sleep_state(u8 sleep_state); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_leave_sleep_state_prep(u8 sleep_state)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_leave_sleep_state(u8 sleep_state)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_set_firmware_waking_vector(u32 -- cgit v1.2.3 From 42873a84a22f6ee089b720c817bb61aebeff6e0d Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 30 Apr 2014 10:05:15 +0800 Subject: ACPICA: Linux header: Add support for stubbed externals. Linux wants to include all header files but leave empty inline stub variables for a feature that is not configured during build. This patch configures ACPICA external globals/macros/functions out and defines them into no-op when CONFIG_ACPI is not enabled. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/platform/aclinux.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'include') diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index f2429094b9b9..4c2f9e78001c 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -73,6 +73,37 @@ #endif #include +#ifndef CONFIG_ACPI + +/* External globals for __KERNEL__, stubs is needed */ + +#define ACPI_GLOBAL(t,a) +#define ACPI_INIT_GLOBAL(t,a,b) + +/* Generating stubs for configurable ACPICA macros */ + +#define ACPI_NO_MEM_ALLOCATIONS + +/* Generating stubs for configurable ACPICA functions */ + +#define ACPI_NO_ERROR_MESSAGES +#undef ACPI_DEBUG_OUTPUT + +/* External interface for __KERNEL__, stub is needed */ + +#define ACPI_EXTERNAL_RETURN_STATUS(prototype) \ + static ACPI_INLINE prototype {return(AE_NOT_CONFIGURED);} +#define ACPI_EXTERNAL_RETURN_OK(prototype) \ + static ACPI_INLINE prototype {return(AE_OK);} +#define ACPI_EXTERNAL_RETURN_VOID(prototype) \ + static ACPI_INLINE prototype {return;} +#define ACPI_EXTERNAL_RETURN_UINT32(prototype) \ + static ACPI_INLINE prototype {return(0);} +#define ACPI_EXTERNAL_RETURN_PTR(prototype) \ + static ACPI_INLINE prototype {return(NULL);} + +#endif /* CONFIG_ACPI */ + /* Host-dependent types and defines for in-kernel ACPICA */ #define ACPI_MACHINE_WIDTH BITS_PER_LONG -- cgit v1.2.3 From e57db4093da567215bcfb10b98ac5f3fc210b1e3 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Apr 2014 10:05:27 +0800 Subject: ACPICA: Back port of _PRP update. This patch is the linuxize result of the following commit: Subject: ACPICA: Add check for _PRP/_HID dependency, with error message. _PRP requires that a _HID appears in the same scope. The iASL changes are not in this patch as iASL currently is not shipped in the kernel. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acnames.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h index 3dd6e838dc30..f0e713fadb1f 100644 --- a/include/acpi/acnames.h +++ b/include/acpi/acnames.h @@ -55,6 +55,7 @@ #define METHOD_NAME__HID "_HID" #define METHOD_NAME__INI "_INI" #define METHOD_NAME__PLD "_PLD" +#define METHOD_NAME__PRP "_PRP" #define METHOD_NAME__PRS "_PRS" #define METHOD_NAME__PRT "_PRT" #define METHOD_NAME__PRW "_PRW" -- cgit v1.2.3 From fea79bc01cb184432309148af9bf53a961a4d920 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Apr 2014 10:05:34 +0800 Subject: ACPICA: Back port of improvements on exception code. This is the linuxize result of the following commit: Subject: ACPICA: Improve handling of exception code blocks. Split exception codes into three distinct blocks; for the main ASL compiler, Table compiler, and the preprocessor. This allows easy addition of new codes into each block without disturbing the others. Adds one new file, aslmessages.c The iASL changes are not in this patch as iASL currently is not shipped in the kernel. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index ac04985017c5..2d074ba1db42 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -207,7 +207,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_install, FALSE); /* * We keep track of the latest version of Windows that has been requested by - * the BIOS. ACPI 5.0. + * the BIOS. ACPI 5.0. */ ACPI_INIT_GLOBAL(u8, acpi_gbl_osi_data, 0); -- cgit v1.2.3 From 1011080dd27ac61950afa43433c47e570e2146d7 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Apr 2014 10:06:22 +0800 Subject: ACPICA: Comment/format update, no functional change. Add some additional commenting the the public acpixf.h file. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 73 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 2d074ba1db42..b3b21087a158 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -55,11 +55,17 @@ extern u8 acpi_gbl_permanent_mmap; +/***************************************************************************** + * + * Macros used for ACPICA globals and configuration + * + ****************************************************************************/ + /* - * Ensure that the globals are actually defined and initialized only once. + * Ensure that global variables are defined and initialized only once. * - * The use of these macros allows a single list of globals (here) in order - * to simplify maintenance of the code. + * The use of these macros allows for a single list of globals (here) + * in order to simplify maintenance of the code. */ #ifdef DEFINE_ACPI_GLOBALS #define ACPI_GLOBAL(type,name) \ @@ -81,8 +87,11 @@ extern u8 acpi_gbl_permanent_mmap; #endif #endif -/* ACPICA prototypes */ - +/* + * These macros configure the various ACPICA interfaces. They are + * useful for generating stub inline functions for features that are + * configured out of the current kernel or ACPICA application. + */ #ifndef ACPI_EXTERNAL_RETURN_STATUS #define ACPI_EXTERNAL_RETURN_STATUS(prototype) \ prototype; @@ -108,16 +117,14 @@ extern u8 acpi_gbl_permanent_mmap; prototype; #endif -/* Public globals, available from outside ACPICA subsystem */ - /***************************************************************************** * - * Runtime configuration (static defaults that can be overriden at runtime) + * Public globals and runtime configuration options * ****************************************************************************/ /* - * Enable "slack" in the AML interpreter? Default is FALSE, and the + * Enable "slack mode" of the AML interpreter? Default is FALSE, and the * interpreter strictly follows the ACPI specification. Setting to TRUE * allows the interpreter to ignore certain errors and/or bad AML constructs. * @@ -234,16 +241,34 @@ ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT); ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0); /* - * Globals that are publically available + * Other miscellaneous globals */ -ACPI_GLOBAL(u32, acpi_current_gpe_count); ACPI_GLOBAL(struct acpi_table_fadt, acpi_gbl_FADT); +ACPI_GLOBAL(u32, acpi_current_gpe_count); ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); +/***************************************************************************** + * + * ACPICA public interface configuration. + * + * Interfaces that are configured out of the ACPICA build are replaced + * by inlined stubs by default. + * + ****************************************************************************/ + /* - * Hardware-reduced prototypes. All interfaces that use these macros will - * be configured out of the ACPICA build if the ACPI_REDUCED_HARDWARE flag + * Hardware-reduced prototypes (default: Not hardware reduced). + * + * All ACPICA hardware-related interfaces that use these macros will be + * configured out of the ACPICA build if the ACPI_REDUCED_HARDWARE flag * is set to TRUE. + * + * Note: This static build option for reduced hardware is intended to + * reduce ACPICA code size if desired or necessary. However, even if this + * option is not specified, the runtime behavior of ACPICA is dependent + * on the actual FADT reduced hardware flag (HW_REDUCED_ACPI). If set, + * the flag will enable similar behavior -- ACPICA will not attempt + * to access any ACPI-relate hardware (SCI, GPEs, Fixed Events, etc.) */ #if (!ACPI_REDUCED_HARDWARE) #define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \ @@ -268,9 +293,11 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); #endif /* !ACPI_REDUCED_HARDWARE */ /* - * Error-message prototypes. All interfaces that use these macros will - * be configured out of the ACPICA build if the ACPI_NO_ERROR_MESSAGE flag - * is defined. + * Error message prototypes (default: error messages enabled). + * + * All interfaces related to error and warning messages + * will be configured out of the ACPICA build if the + * ACPI_NO_ERROR_MESSAGE flag is defined. */ #ifndef ACPI_NO_ERROR_MESSAGES #define ACPI_MSG_DEPENDENT_RETURN_VOID(prototype) \ @@ -283,9 +310,11 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); #endif /* ACPI_NO_ERROR_MESSAGES */ /* - * Debugging-output prototypes. All interfaces that use these macros will - * be configured out of the ACPICA build if the ACPI_DEBUG_OUTPUT flag is - * not defined. + * Debugging output prototypes (default: no debug output). + * + * All interfaces related to debug output messages + * will be configured out of the ACPICA build unless the + * ACPI_DEBUG_OUTPUT flag is defined. */ #ifdef ACPI_DEBUG_OUTPUT #define ACPI_DBG_DEPENDENT_RETURN_VOID(prototype) \ @@ -297,6 +326,12 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); #endif /* ACPI_DEBUG_OUTPUT */ +/***************************************************************************** + * + * ACPICA public interface prototypes + * + ****************************************************************************/ + /* * Initialization */ -- cgit v1.2.3 From bc381eebd4b246d6cc29d97ab34d2c54ecc40a87 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Apr 2014 10:06:30 +0800 Subject: ACPICA: Update version to 20140424. Version 20140424. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index b3b21087a158..4e3044ca4855 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20140325 +#define ACPI_CA_VERSION 0x20140424 #include #include -- cgit v1.2.3 From 6b90f55f63c75c2c65454aea6703c2ea91b9e372 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 6 May 2014 11:24:30 +0800 Subject: ACPI / PCI: Stub out pci_acpi_crs_quirks() and make it x86 specific For pci_acpi_crs_quirks(), ia64 already doesn't use it, and we can not foresee it should be used in ARM64, so stub out pci_acpi_crs_quirks() to avoid introducing platform specific dummy stub function. Signed-off-by: Hanjun Guo Reviewed-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- arch/ia64/include/asm/acpi.h | 1 - include/acpi/acpi_drivers.h | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/ia64/include/asm/acpi.h b/arch/ia64/include/asm/acpi.h index d651102a4d45..97cb16086314 100644 --- a/arch/ia64/include/asm/acpi.h +++ b/arch/ia64/include/asm/acpi.h @@ -92,7 +92,6 @@ ia64_acpi_release_global_lock (unsigned int *lock) #endif #define acpi_processor_cstate_check(x) (x) /* no idle limits on IA64 :) */ static inline void disable_acpi(void) { } -static inline void pci_acpi_crs_quirks(void) { } #ifdef CONFIG_IA64_GENERIC const char *acpi_get_sysname (void); diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index d504613bbf80..ea6428b7dacb 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -96,7 +96,12 @@ struct pci_dev *acpi_get_pci_dev(acpi_handle); /* Arch-defined function to add a bus to the system */ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root); + +#ifdef CONFIG_X86 void pci_acpi_crs_quirks(void); +#else +static inline void pci_acpi_crs_quirks(void) { } +#endif /* -------------------------------------------------------------------------- Processor -- cgit v1.2.3 From a6220fc19afc07fe77cfd16f5b8e568615517091 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 5 May 2014 00:51:54 +0200 Subject: PM / suspend: Always use deepest C-state in the "freeze" sleep state If freeze_enter() is called, we want to bypass the current cpuidle governor and always use the deepest available (that is, not disabled) C-state, because we want to save as much energy as reasonably possible then and runtime latency constraints don't matter at that point, since the system is in a sleep state anyway. Signed-off-by: Rafael J. Wysocki Tested-by: Aubrey Li --- drivers/cpuidle/cpuidle.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/cpuidle.h | 2 ++ kernel/power/suspend.c | 2 ++ 3 files changed, 48 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index f38359f64cc6..cb7019977c50 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -32,6 +32,7 @@ LIST_HEAD(cpuidle_detected_devices); static int enabled_devices; static int off __read_mostly; static int initialized __read_mostly; +static bool use_deepest_state __read_mostly; int cpuidle_disabled(void) { @@ -64,6 +65,45 @@ int cpuidle_play_dead(void) return -ENODEV; } +/** + * cpuidle_use_deepest_state - Enable/disable the "deepest idle" mode. + * @enable: Whether enable or disable the feature. + * + * If the "deepest idle" mode is enabled, cpuidle will ignore the governor and + * always use the state with the greatest exit latency (out of the states that + * are not disabled). + * + * This function can only be called after cpuidle_pause() to avoid races. + */ +void cpuidle_use_deepest_state(bool enable) +{ + use_deepest_state = enable; +} + +/** + * cpuidle_find_deepest_state - Find the state of the greatest exit latency. + * @drv: cpuidle driver for a given CPU. + * @dev: cpuidle device for a given CPU. + */ +static int cpuidle_find_deepest_state(struct cpuidle_driver *drv, + struct cpuidle_device *dev) +{ + unsigned int latency_req = 0; + int i, ret = CPUIDLE_DRIVER_STATE_START - 1; + + for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { + struct cpuidle_state *s = &drv->states[i]; + struct cpuidle_state_usage *su = &dev->states_usage[i]; + + if (s->disabled || su->disable || s->exit_latency <= latency_req) + continue; + + latency_req = s->exit_latency; + ret = i; + } + return ret; +} + /** * cpuidle_enter_state - enter the state and update stats * @dev: cpuidle device for this cpu @@ -124,6 +164,9 @@ int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) if (!drv || !dev || !dev->enabled) return -EBUSY; + if (unlikely(use_deepest_state)) + return cpuidle_find_deepest_state(drv, dev); + return cpuidle_curr_governor->select(drv, dev); } @@ -155,7 +198,7 @@ int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev, */ void cpuidle_reflect(struct cpuidle_device *dev, int index) { - if (cpuidle_curr_governor->reflect) + if (cpuidle_curr_governor->reflect && !unlikely(use_deepest_state)) cpuidle_curr_governor->reflect(dev, index); } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index a8d5bd391a26..c51a436135c4 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -143,6 +143,7 @@ extern void cpuidle_resume(void); extern int cpuidle_enable_device(struct cpuidle_device *dev); extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_play_dead(void); +extern void cpuidle_use_deepest_state(bool enable); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); #else @@ -175,6 +176,7 @@ static inline int cpuidle_enable_device(struct cpuidle_device *dev) {return -ENODEV; } static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } static inline int cpuidle_play_dead(void) {return -ENODEV; } +static inline void cpuidle_use_deepest_state(bool enable) {} static inline struct cpuidle_driver *cpuidle_get_cpu_driver( struct cpuidle_device *dev) {return NULL; } #endif diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 8233cd4047d7..155721f7f909 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -54,9 +54,11 @@ static void freeze_begin(void) static void freeze_enter(void) { + cpuidle_use_deepest_state(true); cpuidle_resume(); wait_event(suspend_freeze_wait_head, suspend_freeze_wake); cpuidle_pause(); + cpuidle_use_deepest_state(false); } void freeze_wake(void) -- cgit v1.2.3 From 6403eb1f646a49cc92f25c08f8716f8870a4a865 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sat, 26 Apr 2014 19:59:52 +0800 Subject: f2fs: introduce help macro ADDRS_PER_PAGE() Introduce help macro ADDRS_PER_PAGE() to get the number of address pointers in direct node or inode. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 6 ++---- fs/f2fs/file.c | 5 +---- fs/f2fs/recovery.c | 5 +---- include/linux/f2fs_fs.h | 3 +++ 4 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0147de7e3973..273fe1631af9 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -652,8 +652,7 @@ static int get_data_block(struct inode *inode, sector_t iblock, goto put_out; } - end_offset = IS_INODE(dn.node_page) ? - ADDRS_PER_INODE(F2FS_I(inode)) : ADDRS_PER_BLOCK; + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); bh_result->b_size = (((size_t)1) << blkbits); dn.ofs_in_node++; pgofs++; @@ -675,8 +674,7 @@ get_next: if (dn.data_blkaddr == NEW_ADDR) goto put_out; - end_offset = IS_INODE(dn.node_page) ? - ADDRS_PER_INODE(F2FS_I(inode)) : ADDRS_PER_BLOCK; + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); } if (maxblocks > (bh_result->b_size >> blkbits)) { diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 60e7d5448a1d..bb365c932555 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -288,10 +288,7 @@ int truncate_blocks(struct inode *inode, u64 from) return err; } - if (IS_INODE(dn.node_page)) - count = ADDRS_PER_INODE(F2FS_I(inode)); - else - count = ADDRS_PER_BLOCK; + count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); count -= dn.ofs_in_node; f2fs_bug_on(count < 0); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 9eb6487f383d..be1e3e881725 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -301,10 +301,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, goto out; start = start_bidx_of_node(ofs_of_node(page), fi); - if (IS_INODE(page)) - end = start + ADDRS_PER_INODE(fi); - else - end = start + ADDRS_PER_BLOCK; + end = start + ADDRS_PER_PAGE(page, fi); f2fs_lock_op(sbi); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index df53e1753a76..8c03f71307c6 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -146,6 +146,9 @@ struct f2fs_extent { #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ +#define ADDRS_PER_PAGE(page, fi) \ + (IS_INODE(page) ? ADDRS_PER_INODE(fi) : ADDRS_PER_BLOCK) + #define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) #define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) #define NODE_IND1_BLOCK (DEF_ADDRS_PER_INODE + 3) -- cgit v1.2.3 From 62aed044ea4bef36ac3f6885611b013b11c9be2d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 6 May 2014 16:46:04 +0800 Subject: f2fs: add a tracepoint for f2fs_write_begin This patch adds a tracepoint for f2fs_write_begin to trace write op of user. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 ++ include/trace/events/f2fs.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a7bb98f5831a..cde0d69969b9 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -900,6 +900,8 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, struct dnode_of_data dn; int err = 0; + trace_f2fs_write_begin(inode, pos, len, flags); + f2fs_balance_fs(sbi); repeat: err = f2fs_convert_inline_data(inode, pos + len); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 67f38faac589..1e9375aa9340 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -659,6 +659,36 @@ DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_read_bio, TP_CONDITION(bio) ); +TRACE_EVENT(f2fs_write_begin, + + TP_PROTO(struct inode *inode, loff_t pos, unsigned int len, + unsigned int flags), + + TP_ARGS(inode, pos, len, flags), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, pos) + __field(unsigned int, len) + __field(unsigned int, flags) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pos = pos; + __entry->len = len; + __entry->flags = flags; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pos = %llu, len = %u, flags = %u", + show_dev_ino(__entry), + (unsigned long long)__entry->pos, + __entry->len, + __entry->flags) +); + DECLARE_EVENT_CLASS(f2fs__page, TP_PROTO(struct page *page, int type), -- cgit v1.2.3 From dfb2bf38bf89e15a65f9996566b2945921388a2f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 6 May 2014 16:47:23 +0800 Subject: f2fs: add a tracepoint for f2fs_write_end This patch adds a tracepoint for f2fs_write_end to trace write op of user. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 ++ include/trace/events/f2fs.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index cde0d69969b9..ea58fb698ac6 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -989,6 +989,8 @@ static int f2fs_write_end(struct file *file, { struct inode *inode = page->mapping->host; + trace_f2fs_write_end(inode, pos, len, copied); + SetPageUptodate(page); set_page_dirty(page); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 1e9375aa9340..483804b55742 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -689,6 +689,36 @@ TRACE_EVENT(f2fs_write_begin, __entry->flags) ); +TRACE_EVENT(f2fs_write_end, + + TP_PROTO(struct inode *inode, loff_t pos, unsigned int len, + unsigned int copied), + + TP_ARGS(inode, pos, len, copied), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, pos) + __field(unsigned int, len) + __field(unsigned int, copied) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pos = pos; + __entry->len = len; + __entry->copied = copied; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pos = %llu, len = %u, copied = %u", + show_dev_ino(__entry), + (unsigned long long)__entry->pos, + __entry->len, + __entry->copied) +); + DECLARE_EVENT_CLASS(f2fs__page, TP_PROTO(struct page *page, int type), -- cgit v1.2.3 From ecda0de3430455378f1c02523bf3ad71d91d613a Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 6 May 2014 16:48:26 +0800 Subject: f2fs: add a tracepoint for f2fs_write_{meta,node,data}_page This patch adds a tracepoint for f2fs_write_{meta,node,data}_page to trace when page is writting out. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 ++ fs/f2fs/data.c | 2 ++ fs/f2fs/node.c | 2 ++ include/trace/events/f2fs.h | 7 +++++++ 4 files changed, 13 insertions(+) (limited to 'include') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 4206f1664be5..c95d62281d7e 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -157,6 +157,8 @@ static int f2fs_write_meta_page(struct page *page, struct inode *inode = page->mapping->host; struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + trace_f2fs_writepage(page, META); + if (unlikely(sbi->por_doing)) goto redirty_out; if (wbc->for_reclaim) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ea58fb698ac6..b997d552880e 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -788,6 +788,8 @@ static int f2fs_write_data_page(struct page *page, .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, }; + trace_f2fs_writepage(page, DATA); + if (page->index < end_index) goto write; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 059aaf5dda2b..49bdddbcadea 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1199,6 +1199,8 @@ static int f2fs_write_node_page(struct page *page, .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, }; + trace_f2fs_writepage(page, NODE); + if (unlikely(sbi->por_doing)) goto redirty_out; diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 483804b55742..d70991e69e58 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -751,6 +751,13 @@ DECLARE_EVENT_CLASS(f2fs__page, __entry->dirty) ); +DEFINE_EVENT(f2fs__page, f2fs_writepage, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + DEFINE_EVENT(f2fs__page, f2fs_set_page_dirty, TP_PROTO(struct page *page, int type), -- cgit v1.2.3 From e57484343898094bb8f72a2aa1a50929d27aa027 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 6 May 2014 16:51:24 +0800 Subject: f2fs: add a tracepoint for f2fs_write_{meta,node,data}_pages This patch adds a tracepoint for f2fs_write_{meta,node,data}_pages to trace when pages are fsyncing/flushing. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 ++ fs/f2fs/data.c | 2 ++ fs/f2fs/node.c | 2 ++ include/trace/events/f2fs.h | 64 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+) (limited to 'include') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index c95d62281d7e..fe968c7bfc90 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -186,6 +186,8 @@ static int f2fs_write_meta_pages(struct address_space *mapping, struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb); long diff, written; + trace_f2fs_writepages(mapping->host, wbc, META); + /* collect a number of dirty meta pages and write together */ if (wbc->for_kupdate || get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META)) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b997d552880e..21bfafaafe83 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -860,6 +860,8 @@ static int f2fs_write_data_pages(struct address_space *mapping, int ret; long diff; + trace_f2fs_writepages(mapping->host, wbc, DATA); + /* deal with chardevs and other special file */ if (!mapping->a_ops->writepage) return 0; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 49bdddbcadea..3d60d3d34ed2 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1242,6 +1242,8 @@ static int f2fs_write_node_pages(struct address_space *mapping, struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb); long diff; + trace_f2fs_writepages(mapping->host, wbc, NODE); + /* balancing f2fs's metadata in background */ f2fs_balance_fs_bg(sbi); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index d70991e69e58..91b1fcc5ec93 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -772,6 +772,70 @@ DEFINE_EVENT(f2fs__page, f2fs_vm_page_mkwrite, TP_ARGS(page, type) ); +TRACE_EVENT(f2fs_writepages, + + TP_PROTO(struct inode *inode, struct writeback_control *wbc, int type), + + TP_ARGS(inode, wbc, type), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(int, type) + __field(int, dir) + __field(long, nr_to_write) + __field(long, pages_skipped) + __field(loff_t, range_start) + __field(loff_t, range_end) + __field(pgoff_t, writeback_index) + __field(int, sync_mode) + __field(char, for_kupdate) + __field(char, for_background) + __field(char, tagged_writepages) + __field(char, for_reclaim) + __field(char, range_cyclic) + __field(char, for_sync) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->type = type; + __entry->dir = S_ISDIR(inode->i_mode); + __entry->nr_to_write = wbc->nr_to_write; + __entry->pages_skipped = wbc->pages_skipped; + __entry->range_start = wbc->range_start; + __entry->range_end = wbc->range_end; + __entry->writeback_index = inode->i_mapping->writeback_index; + __entry->sync_mode = wbc->sync_mode; + __entry->for_kupdate = wbc->for_kupdate; + __entry->for_background = wbc->for_background; + __entry->tagged_writepages = wbc->tagged_writepages; + __entry->for_reclaim = wbc->for_reclaim; + __entry->range_cyclic = wbc->range_cyclic; + __entry->for_sync = wbc->for_sync; + ), + + TP_printk("dev = (%d,%d), ino = %lu, %s, %s, nr_to_write %ld, " + "skipped %ld, start %lld, end %lld, wb_idx %lu, sync_mode %d, " + "kupdate %u background %u tagged %u reclaim %u cyclic %u sync %u", + show_dev_ino(__entry), + show_block_type(__entry->type), + show_file_type(__entry->dir), + __entry->nr_to_write, + __entry->pages_skipped, + __entry->range_start, + __entry->range_end, + (unsigned long)__entry->writeback_index, + __entry->sync_mode, + __entry->for_kupdate, + __entry->for_background, + __entry->tagged_writepages, + __entry->for_reclaim, + __entry->range_cyclic, + __entry->for_sync) +); + TRACE_EVENT(f2fs_submit_page_mbio, TP_PROTO(struct page *page, int rw, int type, block_t blk_addr), -- cgit v1.2.3 From c20e89cde669799eff62bf8c00ca9a4819c4e11f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 6 May 2014 16:53:08 +0800 Subject: f2fs: add a tracepoint for f2fs_read_data_page This patch adds a tracepoint for f2fs_read_data_page to trace when page is readed by user. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 ++ include/trace/events/f2fs.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 21bfafaafe83..8c250a5d6f26 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -713,6 +713,8 @@ static int f2fs_read_data_page(struct file *file, struct page *page) struct inode *inode = page->mapping->host; int ret; + trace_f2fs_readpage(page, DATA); + /* If the file has inline data, try to read it directlly */ if (f2fs_has_inline_data(inode)) ret = f2fs_read_inline_data(inode, page); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 91b1fcc5ec93..b983990b4a9f 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -732,6 +732,7 @@ DECLARE_EVENT_CLASS(f2fs__page, __field(int, dir) __field(pgoff_t, index) __field(int, dirty) + __field(int, uptodate) ), TP_fast_assign( @@ -741,14 +742,17 @@ DECLARE_EVENT_CLASS(f2fs__page, __entry->dir = S_ISDIR(page->mapping->host->i_mode); __entry->index = page->index; __entry->dirty = PageDirty(page); + __entry->uptodate = PageUptodate(page); ), - TP_printk("dev = (%d,%d), ino = %lu, %s, %s, index = %lu, dirty = %d", + TP_printk("dev = (%d,%d), ino = %lu, %s, %s, index = %lu, " + "dirty = %d, uptodate = %d", show_dev_ino(__entry), show_block_type(__entry->type), show_file_type(__entry->dir), (unsigned long)__entry->index, - __entry->dirty) + __entry->dirty, + __entry->uptodate) ); DEFINE_EVENT(f2fs__page, f2fs_writepage, @@ -758,6 +762,13 @@ DEFINE_EVENT(f2fs__page, f2fs_writepage, TP_ARGS(page, type) ); +DEFINE_EVENT(f2fs__page, f2fs_readpage, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + DEFINE_EVENT(f2fs__page, f2fs_set_page_dirty, TP_PROTO(struct page *page, int type), -- cgit v1.2.3 From 257462dbf3ed233de0dc2e489dcc58579535b33f Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 27 Feb 2014 14:53:34 +0900 Subject: pwm-backlight: switch to gpiod interface Switch to the new gpiod interface, which allows to handle GPIO properties such as active low transparently and removes a whole bunch of code. There are still a couple of users of this driver that rely on passing the enable GPIO number through platform data, so a fallback mechanism using a GPIO number is still available to avoid breaking them. It will be removed once current users have switched to the GPIO lookup tables provided by the gpiod interface. Signed-off-by: Alexandre Courbot Signed-off-by: Thierry Reding --- drivers/video/backlight/pwm_bl.c | 69 +++++++++++++++++----------------------- include/linux/pwm_backlight.h | 5 +-- 2 files changed, 30 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index fa7f5c35b7fb..cc49347bbcc0 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -10,8 +10,8 @@ * published by the Free Software Foundation. */ +#include #include -#include #include #include #include @@ -32,8 +32,7 @@ struct pwm_bl_data { unsigned int *levels; bool enabled; struct regulator *power_supply; - int enable_gpio; - unsigned long enable_gpio_flags; + struct gpio_desc *enable_gpio; unsigned int scale; int (*notify)(struct device *, int brightness); @@ -54,12 +53,8 @@ static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness) if (err < 0) dev_err(pb->dev, "failed to enable power supply\n"); - if (gpio_is_valid(pb->enable_gpio)) { - if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW) - gpio_set_value(pb->enable_gpio, 0); - else - gpio_set_value(pb->enable_gpio, 1); - } + if (pb->enable_gpio) + gpiod_set_value(pb->enable_gpio, 1); pwm_enable(pb->pwm); pb->enabled = true; @@ -73,12 +68,8 @@ static void pwm_backlight_power_off(struct pwm_bl_data *pb) pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); - if (gpio_is_valid(pb->enable_gpio)) { - if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW) - gpio_set_value(pb->enable_gpio, 1); - else - gpio_set_value(pb->enable_gpio, 0); - } + if (pb->enable_gpio) + gpiod_set_value(pb->enable_gpio, 0); regulator_disable(pb->power_supply); pb->enabled = false; @@ -148,7 +139,6 @@ static int pwm_backlight_parse_dt(struct device *dev, struct platform_pwm_backlight_data *data) { struct device_node *node = dev->of_node; - enum of_gpio_flags flags; struct property *prop; int length; u32 value; @@ -189,14 +179,6 @@ static int pwm_backlight_parse_dt(struct device *dev, data->max_brightness--; } - data->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0, - &flags); - if (data->enable_gpio == -EPROBE_DEFER) - return -EPROBE_DEFER; - - if (gpio_is_valid(data->enable_gpio) && (flags & OF_GPIO_ACTIVE_LOW)) - data->enable_gpio_flags |= PWM_BACKLIGHT_GPIO_ACTIVE_LOW; - return 0; } @@ -256,8 +238,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) } else pb->scale = data->max_brightness; - pb->enable_gpio = data->enable_gpio; - pb->enable_gpio_flags = data->enable_gpio_flags; pb->notify = data->notify; pb->notify_after = data->notify_after; pb->check_fb = data->check_fb; @@ -265,26 +245,38 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->dev = &pdev->dev; pb->enabled = false; - if (gpio_is_valid(pb->enable_gpio)) { - unsigned long flags; - - if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW) - flags = GPIOF_OUT_INIT_HIGH; + pb->enable_gpio = devm_gpiod_get(&pdev->dev, "enable"); + if (IS_ERR(pb->enable_gpio)) { + ret = PTR_ERR(pb->enable_gpio); + if (ret == -ENOENT) + pb->enable_gpio = NULL; else - flags = GPIOF_OUT_INIT_LOW; + goto err_alloc; + } - ret = gpio_request_one(pb->enable_gpio, flags, "enable"); + /* + * Compatibility fallback for drivers still using the integer GPIO + * platform data. Must go away soon. + */ + if (!pb->enable_gpio && gpio_is_valid(data->enable_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, data->enable_gpio, + GPIOF_OUT_INIT_HIGH, "enable"); if (ret < 0) { dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n", - pb->enable_gpio, ret); + data->enable_gpio, ret); goto err_alloc; } + + pb->enable_gpio = gpio_to_desc(data->enable_gpio); } + if (pb->enable_gpio) + gpiod_direction_output(pb->enable_gpio, 1); + pb->power_supply = devm_regulator_get(&pdev->dev, "power"); if (IS_ERR(pb->power_supply)) { ret = PTR_ERR(pb->power_supply); - goto err_gpio; + goto err_alloc; } pb->pwm = devm_pwm_get(&pdev->dev, NULL); @@ -295,7 +287,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (IS_ERR(pb->pwm)) { dev_err(&pdev->dev, "unable to request legacy PWM\n"); ret = PTR_ERR(pb->pwm); - goto err_gpio; + goto err_alloc; } } @@ -320,7 +312,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); ret = PTR_ERR(bl); - goto err_gpio; + goto err_alloc; } if (data->dft_brightness > data->max_brightness) { @@ -336,9 +328,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); return 0; -err_gpio: - if (gpio_is_valid(pb->enable_gpio)) - gpio_free(pb->enable_gpio); err_alloc: if (data->exit) data->exit(&pdev->dev); diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h index 2de2e275b2cb..efdd9227a49c 100644 --- a/include/linux/pwm_backlight.h +++ b/include/linux/pwm_backlight.h @@ -6,9 +6,6 @@ #include -/* TODO: convert to gpiod_*() API once it has been merged */ -#define PWM_BACKLIGHT_GPIO_ACTIVE_LOW (1 << 0) - struct platform_pwm_backlight_data { int pwm_id; unsigned int max_brightness; @@ -16,8 +13,8 @@ struct platform_pwm_backlight_data { unsigned int lth_brightness; unsigned int pwm_period_ns; unsigned int *levels; + /* TODO remove once all users are switched to gpiod_* API */ int enable_gpio; - unsigned long enable_gpio_flags; int (*init)(struct device *dev); int (*notify)(struct device *dev, int brightness); void (*notify_after)(struct device *dev, int brightness); -- cgit v1.2.3 From 24faf7656800afa0e0d882f950502c5c03f4b7f0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 May 2014 09:39:37 +0200 Subject: ASoC: Remove card's DAI list Commit f0fba2ad1 ("ASoC: multi-component - ASoC Multi-Component Support") added a per card list that keeps track of all the DAIs that have been registered with the card, but the list has never been used. This patch removes it again. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 1 - include/sound/soc.h | 2 -- sound/soc/soc-core.c | 5 ----- 3 files changed, 8 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index fad76769f153..e2c3e45d0656 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -277,7 +277,6 @@ struct snd_soc_dai { struct snd_soc_card *card; struct list_head list; - struct list_head card_list; }; static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, diff --git a/include/sound/soc.h b/include/sound/soc.h index e84f22f89765..96d073fe7cd2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1021,7 +1021,6 @@ struct snd_soc_card { /* lists of probed devices belonging to this card */ struct list_head codec_dev_list; struct list_head platform_dev_list; - struct list_head dai_dev_list; struct list_head widgets; struct list_head paths; @@ -1207,7 +1206,6 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) { - INIT_LIST_HEAD(&card->dai_dev_list); INIT_LIST_HEAD(&card->codec_dev_list); INIT_LIST_HEAD(&card->platform_dev_list); INIT_LIST_HEAD(&card->widgets); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 520d2489d23c..ada7cbc9fb94 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1017,7 +1017,6 @@ static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, int order) codec_dai->name, err); } codec_dai->probed = 0; - list_del(&codec_dai->card_list); } } @@ -1049,7 +1048,6 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) cpu_dai->name, err); } cpu_dai->probed = 0; - list_del(&cpu_dai->card_list); if (!cpu_dai->codec) { snd_soc_dapm_free(&cpu_dai->dapm); @@ -1405,7 +1403,6 @@ static int soc_probe_codec_dai(struct snd_soc_card *card, /* mark codec_dai as probed and add to card dai list */ codec_dai->probed = 1; - list_add(&codec_dai->card_list, &card->dai_dev_list); } return 0; @@ -1490,8 +1487,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } } cpu_dai->probed = 1; - /* mark cpu_dai as probed and add to card dai list */ - list_add(&cpu_dai->card_list, &card->dai_dev_list); } /* probe the CODEC DAI */ -- cgit v1.2.3 From af0881ffbd7bfc825c2871c79798d66e3608a50c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 May 2014 09:39:38 +0200 Subject: ASoC: Remove unused 'list' field form card The global card list was removed in commit b19e6e7b7 ("ASoC: core: Use driver core probe deferral"). The 'list' field of the snd_soc_card struct has been unused since then. This patch removes the field. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - sound/soc/soc-core.c | 1 - 2 files changed, 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 96d073fe7cd2..7287c6e0cf71 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -958,7 +958,6 @@ struct snd_soc_card { struct snd_card *snd_card; struct module *owner; - struct list_head list; struct mutex mutex; struct mutex dapm_mutex; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ada7cbc9fb94..0a2c0720fc2d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3745,7 +3745,6 @@ int snd_soc_register_card(struct snd_soc_card *card) for (i = 0; i < card->num_links; i++) card->rtd[i].dai_link = &card->dai_link[i]; - INIT_LIST_HEAD(&card->list); INIT_LIST_HEAD(&card->dapm_dirty); card->instantiated = 0; mutex_init(&card->mutex); -- cgit v1.2.3 From db88a8e3ca38b840c768e65a3ddb952cf1bf258f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 May 2014 09:39:39 +0200 Subject: ASoC: Remove unused num_dai field from CODEC Commit d191bd8de8 ("ASoC: snd_soc_codec includes snd_soc_component") removed the last user of the num_dai field. Also remove the field itself. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - sound/soc/soc-core.c | 1 - 2 files changed, 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 7287c6e0cf71..b9ee22018352 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -701,7 +701,6 @@ struct snd_soc_codec { struct snd_soc_card *card; struct list_head list; struct list_head card_list; - int num_dai; /* runtime */ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0a2c0720fc2d..a675eec64756 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4277,7 +4277,6 @@ int snd_soc_register_codec(struct device *dev, codec->dapm.stream_event = codec_drv->stream_event; codec->dev = dev; codec->driver = codec_drv; - codec->num_dai = num_dai; codec->component.val_bytes = codec_drv->reg_word_size; mutex_init(&codec->mutex); -- cgit v1.2.3 From 5bfd126e80dca70431aef8fdbc1cf14535f3c338 Mon Sep 17 00:00:00 2001 From: Juri Lelli Date: Tue, 15 Apr 2014 13:49:04 +0200 Subject: sched/deadline: Fix sched_yield() behavior yield_task_dl() is broken: o it forces current to be throttled setting its runtime to zero; o it sets current's dl_se->dl_new to one, expecting that dl_task_timer() will queue it back with proper parameters at replenish time. Unfortunately, dl_task_timer() has this check at the very beginning: if (!dl_task(p) || dl_se->dl_new) goto unlock; So, it just bails out and the task is never replenished. It actually yielded forever. To fix this, introduce a new flag indicating that the task properly yielded the CPU before its current runtime expired. While this is a little overdoing at the moment, the flag would be useful in the future to discriminate between "good" jobs (of which remaining runtime could be reclaimed, i.e. recycled) and "bad" jobs (for which dl_throttled task has been set) that needed to be stopped. Reported-by: yjay.kim Signed-off-by: Juri Lelli Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20140429103953.e68eba1b2ac3309214e3dc5a@gmail.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 7 +++++-- kernel/sched/core.c | 1 + kernel/sched/deadline.c | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 25f54c79f757..2a4298fb0d85 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1153,9 +1153,12 @@ struct sched_dl_entity { * * @dl_boosted tells if we are boosted due to DI. If so we are * outside bandwidth enforcement mechanism (but only until we - * exit the critical section). + * exit the critical section); + * + * @dl_yielded tells if task gave up the cpu before consuming + * all its available runtime during the last job. */ - int dl_throttled, dl_new, dl_boosted; + int dl_throttled, dl_new, dl_boosted, dl_yielded; /* * Bandwidth enforcement timer. Each -deadline task has its diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 9fe2190005cb..e62c65a12d5b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3124,6 +3124,7 @@ __setparam_dl(struct task_struct *p, const struct sched_attr *attr) dl_se->dl_bw = to_ratio(dl_se->dl_period, dl_se->dl_runtime); dl_se->dl_throttled = 0; dl_se->dl_new = 1; + dl_se->dl_yielded = 0; } static void __setscheduler_params(struct task_struct *p, diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index b08095786cb8..800e99b99075 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -528,6 +528,7 @@ static enum hrtimer_restart dl_task_timer(struct hrtimer *timer) sched_clock_tick(); update_rq_clock(rq); dl_se->dl_throttled = 0; + dl_se->dl_yielded = 0; if (p->on_rq) { enqueue_task_dl(rq, p, ENQUEUE_REPLENISH); if (task_has_dl_policy(rq->curr)) @@ -893,10 +894,10 @@ static void yield_task_dl(struct rq *rq) * We make the task go to sleep until its current deadline by * forcing its runtime to zero. This way, update_curr_dl() stops * it and the bandwidth timer will wake it up and will give it - * new scheduling parameters (thanks to dl_new=1). + * new scheduling parameters (thanks to dl_yielded=1). */ if (p->dl.runtime > 0) { - rq->curr->dl.dl_new = 1; + rq->curr->dl.dl_yielded = 1; p->dl.runtime = 0; } update_curr_dl(rq); -- cgit v1.2.3 From 143e1e28cb40bed836b0a06567208bd7347c9672 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Fri, 11 Apr 2014 11:44:37 +0200 Subject: sched: Rework sched_domain topology definition We replace the old way to configure the scheduler topology with a new method which enables a platform to declare additionnal level (if needed). We still have a default topology table definition that can be used by platform that don't want more level than the SMT, MC, CPU and NUMA ones. This table can be overwritten by an arch which either wants to add new level where a load balance make sense like BOOK or powergating level or wants to change the flags configuration of some levels. For each level, we need a function pointer that returns cpumask for each cpu, a function pointer that returns the flags for the level and a name. Only flags that describe topology, can be set by an architecture. The current topology flags are: SD_SHARE_CPUPOWER SD_SHARE_PKG_RESOURCES SD_NUMA SD_ASYM_PACKING Then, each level must be a subset on the next one. The build sequence of the sched_domain will take care of removing useless levels like those with 1 CPU and those with the same CPU span and no more relevant information for load balancing than its children. Signed-off-by: Vincent Guittot Tested-by: Dietmar Eggemann Reviewed-by: Preeti U Murthy Reviewed-by: Dietmar Eggemann Signed-off-by: Peter Zijlstra Cc: Andrew Morton Cc: Benjamin Herrenschmidt Cc: Bjorn Helgaas Cc: Chris Metcalf Cc: Christoph Lameter Cc: David S. Miller Cc: Fenghua Yu Cc: Greg Kroah-Hartman Cc: Hanjun Guo Cc: Heiko Carstens Cc: Jason Low Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Tony Luck Cc: linux390@de.ibm.com Cc: linux-ia64@vger.kernel.org Cc: linux-s390@vger.kernel.org Link: http://lkml.kernel.org/r/1397209481-28542-2-git-send-email-vincent.guittot@linaro.org Signed-off-by: Ingo Molnar --- arch/ia64/include/asm/topology.h | 24 ---- arch/s390/include/asm/topology.h | 2 - arch/tile/include/asm/topology.h | 33 ------ include/linux/sched.h | 53 +++++++++ include/linux/topology.h | 128 +++------------------ kernel/sched/core.c | 233 ++++++++++++++++++++------------------- 6 files changed, 186 insertions(+), 287 deletions(-) (limited to 'include') diff --git a/arch/ia64/include/asm/topology.h b/arch/ia64/include/asm/topology.h index 5cb55a1e606b..3202aa74e0d6 100644 --- a/arch/ia64/include/asm/topology.h +++ b/arch/ia64/include/asm/topology.h @@ -46,30 +46,6 @@ void build_cpu_to_node_map(void); -#define SD_CPU_INIT (struct sched_domain) { \ - .parent = NULL, \ - .child = NULL, \ - .groups = NULL, \ - .min_interval = 1, \ - .max_interval = 4, \ - .busy_factor = 64, \ - .imbalance_pct = 125, \ - .cache_nice_tries = 2, \ - .busy_idx = 2, \ - .idle_idx = 1, \ - .newidle_idx = 0, \ - .wake_idx = 0, \ - .forkexec_idx = 0, \ - .flags = SD_LOAD_BALANCE \ - | SD_BALANCE_NEWIDLE \ - | SD_BALANCE_EXEC \ - | SD_BALANCE_FORK \ - | SD_WAKE_AFFINE, \ - .last_balance = jiffies, \ - .balance_interval = 1, \ - .nr_balance_failed = 0, \ -} - #endif /* CONFIG_NUMA */ #ifdef CONFIG_SMP diff --git a/arch/s390/include/asm/topology.h b/arch/s390/include/asm/topology.h index 05425b18c0aa..07763bdb408d 100644 --- a/arch/s390/include/asm/topology.h +++ b/arch/s390/include/asm/topology.h @@ -64,8 +64,6 @@ static inline void s390_init_cpu_topology(void) }; #endif -#define SD_BOOK_INIT SD_CPU_INIT - #include #endif /* _ASM_S390_TOPOLOGY_H */ diff --git a/arch/tile/include/asm/topology.h b/arch/tile/include/asm/topology.h index d15c0d8d550f..938311844233 100644 --- a/arch/tile/include/asm/topology.h +++ b/arch/tile/include/asm/topology.h @@ -44,39 +44,6 @@ static inline const struct cpumask *cpumask_of_node(int node) /* For now, use numa node -1 for global allocation. */ #define pcibus_to_node(bus) ((void)(bus), -1) -/* - * TILE architecture has many cores integrated in one processor, so we need - * setup bigger balance_interval for both CPU/NODE scheduling domains to - * reduce process scheduling costs. - */ - -/* sched_domains SD_CPU_INIT for TILE architecture */ -#define SD_CPU_INIT (struct sched_domain) { \ - .min_interval = 4, \ - .max_interval = 128, \ - .busy_factor = 64, \ - .imbalance_pct = 125, \ - .cache_nice_tries = 1, \ - .busy_idx = 2, \ - .idle_idx = 1, \ - .newidle_idx = 0, \ - .wake_idx = 0, \ - .forkexec_idx = 0, \ - \ - .flags = 1*SD_LOAD_BALANCE \ - | 1*SD_BALANCE_NEWIDLE \ - | 1*SD_BALANCE_EXEC \ - | 1*SD_BALANCE_FORK \ - | 0*SD_BALANCE_WAKE \ - | 0*SD_WAKE_AFFINE \ - | 0*SD_SHARE_CPUPOWER \ - | 0*SD_SHARE_PKG_RESOURCES \ - | 0*SD_SERIALIZE \ - , \ - .last_balance = jiffies, \ - .balance_interval = 32, \ -} - /* By definition, we create nodes based on online memory. */ #define node_has_online_mem(nid) 1 diff --git a/include/linux/sched.h b/include/linux/sched.h index 2a4298fb0d85..656b035c30e5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -879,6 +879,27 @@ enum cpu_idle_type { extern int __weak arch_sd_sibiling_asym_packing(void); +#ifdef CONFIG_SCHED_SMT +static inline const int cpu_smt_flags(void) +{ + return SD_SHARE_CPUPOWER | SD_SHARE_PKG_RESOURCES; +} +#endif + +#ifdef CONFIG_SCHED_MC +static inline const int cpu_core_flags(void) +{ + return SD_SHARE_PKG_RESOURCES; +} +#endif + +#ifdef CONFIG_NUMA +static inline const int cpu_numa_flags(void) +{ + return SD_NUMA; +} +#endif + struct sched_domain_attr { int relax_domain_level; }; @@ -985,6 +1006,38 @@ void free_sched_domains(cpumask_var_t doms[], unsigned int ndoms); bool cpus_share_cache(int this_cpu, int that_cpu); +typedef const struct cpumask *(*sched_domain_mask_f)(int cpu); +typedef const int (*sched_domain_flags_f)(void); + +#define SDTL_OVERLAP 0x01 + +struct sd_data { + struct sched_domain **__percpu sd; + struct sched_group **__percpu sg; + struct sched_group_power **__percpu sgp; +}; + +struct sched_domain_topology_level { + sched_domain_mask_f mask; + sched_domain_flags_f sd_flags; + int flags; + int numa_level; + struct sd_data data; +#ifdef CONFIG_SCHED_DEBUG + char *name; +#endif +}; + +extern struct sched_domain_topology_level *sched_domain_topology; + +extern void set_sched_topology(struct sched_domain_topology_level *tl); + +#ifdef CONFIG_SCHED_DEBUG +# define SD_INIT_NAME(type) .name = #type +#else +# define SD_INIT_NAME(type) +#endif + #else /* CONFIG_SMP */ struct sched_domain_attr; diff --git a/include/linux/topology.h b/include/linux/topology.h index 7062330a1329..973671ff9e7d 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -66,121 +66,6 @@ int arch_update_cpu_topology(void); #define PENALTY_FOR_NODE_WITH_CPUS (1) #endif -/* - * Below are the 3 major initializers used in building sched_domains: - * SD_SIBLING_INIT, for SMT domains - * SD_CPU_INIT, for SMP domains - * - * Any architecture that cares to do any tuning to these values should do so - * by defining their own arch-specific initializer in include/asm/topology.h. - * A definition there will automagically override these default initializers - * and allow arch-specific performance tuning of sched_domains. - * (Only non-zero and non-null fields need be specified.) - */ - -#ifdef CONFIG_SCHED_SMT -/* MCD - Do we really need this? It is always on if CONFIG_SCHED_SMT is, - * so can't we drop this in favor of CONFIG_SCHED_SMT? - */ -#define ARCH_HAS_SCHED_WAKE_IDLE -/* Common values for SMT siblings */ -#ifndef SD_SIBLING_INIT -#define SD_SIBLING_INIT (struct sched_domain) { \ - .min_interval = 1, \ - .max_interval = 2, \ - .busy_factor = 64, \ - .imbalance_pct = 110, \ - \ - .flags = 1*SD_LOAD_BALANCE \ - | 1*SD_BALANCE_NEWIDLE \ - | 1*SD_BALANCE_EXEC \ - | 1*SD_BALANCE_FORK \ - | 0*SD_BALANCE_WAKE \ - | 1*SD_WAKE_AFFINE \ - | 1*SD_SHARE_CPUPOWER \ - | 1*SD_SHARE_PKG_RESOURCES \ - | 0*SD_SERIALIZE \ - | 0*SD_PREFER_SIBLING \ - | arch_sd_sibling_asym_packing() \ - , \ - .last_balance = jiffies, \ - .balance_interval = 1, \ - .smt_gain = 1178, /* 15% */ \ - .max_newidle_lb_cost = 0, \ - .next_decay_max_lb_cost = jiffies, \ -} -#endif -#endif /* CONFIG_SCHED_SMT */ - -#ifdef CONFIG_SCHED_MC -/* Common values for MC siblings. for now mostly derived from SD_CPU_INIT */ -#ifndef SD_MC_INIT -#define SD_MC_INIT (struct sched_domain) { \ - .min_interval = 1, \ - .max_interval = 4, \ - .busy_factor = 64, \ - .imbalance_pct = 125, \ - .cache_nice_tries = 1, \ - .busy_idx = 2, \ - .wake_idx = 0, \ - .forkexec_idx = 0, \ - \ - .flags = 1*SD_LOAD_BALANCE \ - | 1*SD_BALANCE_NEWIDLE \ - | 1*SD_BALANCE_EXEC \ - | 1*SD_BALANCE_FORK \ - | 0*SD_BALANCE_WAKE \ - | 1*SD_WAKE_AFFINE \ - | 0*SD_SHARE_CPUPOWER \ - | 1*SD_SHARE_PKG_RESOURCES \ - | 0*SD_SERIALIZE \ - , \ - .last_balance = jiffies, \ - .balance_interval = 1, \ - .max_newidle_lb_cost = 0, \ - .next_decay_max_lb_cost = jiffies, \ -} -#endif -#endif /* CONFIG_SCHED_MC */ - -/* Common values for CPUs */ -#ifndef SD_CPU_INIT -#define SD_CPU_INIT (struct sched_domain) { \ - .min_interval = 1, \ - .max_interval = 4, \ - .busy_factor = 64, \ - .imbalance_pct = 125, \ - .cache_nice_tries = 1, \ - .busy_idx = 2, \ - .idle_idx = 1, \ - .newidle_idx = 0, \ - .wake_idx = 0, \ - .forkexec_idx = 0, \ - \ - .flags = 1*SD_LOAD_BALANCE \ - | 1*SD_BALANCE_NEWIDLE \ - | 1*SD_BALANCE_EXEC \ - | 1*SD_BALANCE_FORK \ - | 0*SD_BALANCE_WAKE \ - | 1*SD_WAKE_AFFINE \ - | 0*SD_SHARE_CPUPOWER \ - | 0*SD_SHARE_PKG_RESOURCES \ - | 0*SD_SERIALIZE \ - | 1*SD_PREFER_SIBLING \ - , \ - .last_balance = jiffies, \ - .balance_interval = 1, \ - .max_newidle_lb_cost = 0, \ - .next_decay_max_lb_cost = jiffies, \ -} -#endif - -#ifdef CONFIG_SCHED_BOOK -#ifndef SD_BOOK_INIT -#error Please define an appropriate SD_BOOK_INIT in include/asm/topology.h!!! -#endif -#endif /* CONFIG_SCHED_BOOK */ - #ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID DECLARE_PER_CPU(int, numa_node); @@ -295,4 +180,17 @@ static inline int cpu_to_mem(int cpu) #define topology_core_cpumask(cpu) cpumask_of(cpu) #endif +#ifdef CONFIG_SCHED_SMT +static inline const struct cpumask *cpu_smt_mask(int cpu) +{ + return topology_thread_cpumask(cpu); +} +#endif + +static inline const struct cpumask *cpu_cpu_mask(int cpu) +{ + return cpumask_of_node(cpu_to_node(cpu)); +} + + #endif /* _LINUX_TOPOLOGY_H */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 13584f1cccfc..7d332b7899cc 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5566,17 +5566,6 @@ static int __init isolated_cpu_setup(char *str) __setup("isolcpus=", isolated_cpu_setup); -static const struct cpumask *cpu_cpu_mask(int cpu) -{ - return cpumask_of_node(cpu_to_node(cpu)); -} - -struct sd_data { - struct sched_domain **__percpu sd; - struct sched_group **__percpu sg; - struct sched_group_power **__percpu sgp; -}; - struct s_data { struct sched_domain ** __percpu sd; struct root_domain *rd; @@ -5589,21 +5578,6 @@ enum s_alloc { sa_none, }; -struct sched_domain_topology_level; - -typedef struct sched_domain *(*sched_domain_init_f)(struct sched_domain_topology_level *tl, int cpu); -typedef const struct cpumask *(*sched_domain_mask_f)(int cpu); - -#define SDTL_OVERLAP 0x01 - -struct sched_domain_topology_level { - sched_domain_init_f init; - sched_domain_mask_f mask; - int flags; - int numa_level; - struct sd_data data; -}; - /* * Build an iteration mask that can exclude certain CPUs from the upwards * domain traversal. @@ -5832,34 +5806,6 @@ int __weak arch_sd_sibling_asym_packing(void) * Non-inlined to reduce accumulated stack pressure in build_sched_domains() */ -#ifdef CONFIG_SCHED_DEBUG -# define SD_INIT_NAME(sd, type) sd->name = #type -#else -# define SD_INIT_NAME(sd, type) do { } while (0) -#endif - -#define SD_INIT_FUNC(type) \ -static noinline struct sched_domain * \ -sd_init_##type(struct sched_domain_topology_level *tl, int cpu) \ -{ \ - struct sched_domain *sd = *per_cpu_ptr(tl->data.sd, cpu); \ - *sd = SD_##type##_INIT; \ - SD_INIT_NAME(sd, type); \ - sd->private = &tl->data; \ - return sd; \ -} - -SD_INIT_FUNC(CPU) -#ifdef CONFIG_SCHED_SMT - SD_INIT_FUNC(SIBLING) -#endif -#ifdef CONFIG_SCHED_MC - SD_INIT_FUNC(MC) -#endif -#ifdef CONFIG_SCHED_BOOK - SD_INIT_FUNC(BOOK) -#endif - static int default_relax_domain_level = -1; int sched_domain_level_max; @@ -5947,99 +5893,156 @@ static void claim_allocations(int cpu, struct sched_domain *sd) *per_cpu_ptr(sdd->sgp, cpu) = NULL; } -#ifdef CONFIG_SCHED_SMT -static const struct cpumask *cpu_smt_mask(int cpu) -{ - return topology_thread_cpumask(cpu); -} -#endif - -/* - * Topology list, bottom-up. - */ -static struct sched_domain_topology_level default_topology[] = { -#ifdef CONFIG_SCHED_SMT - { sd_init_SIBLING, cpu_smt_mask, }, -#endif -#ifdef CONFIG_SCHED_MC - { sd_init_MC, cpu_coregroup_mask, }, -#endif -#ifdef CONFIG_SCHED_BOOK - { sd_init_BOOK, cpu_book_mask, }, -#endif - { sd_init_CPU, cpu_cpu_mask, }, - { NULL, }, -}; - -static struct sched_domain_topology_level *sched_domain_topology = default_topology; - -#define for_each_sd_topology(tl) \ - for (tl = sched_domain_topology; tl->init; tl++) - #ifdef CONFIG_NUMA - static int sched_domains_numa_levels; static int *sched_domains_numa_distance; static struct cpumask ***sched_domains_numa_masks; static int sched_domains_curr_level; +#endif -static inline int sd_local_flags(int level) -{ - if (sched_domains_numa_distance[level] > RECLAIM_DISTANCE) - return 0; - - return SD_BALANCE_EXEC | SD_BALANCE_FORK | SD_WAKE_AFFINE; -} +/* + * SD_flags allowed in topology descriptions. + * + * SD_SHARE_CPUPOWER - describes SMT topologies + * SD_SHARE_PKG_RESOURCES - describes shared caches + * SD_NUMA - describes NUMA topologies + * + * Odd one out: + * SD_ASYM_PACKING - describes SMT quirks + */ +#define TOPOLOGY_SD_FLAGS \ + (SD_SHARE_CPUPOWER | \ + SD_SHARE_PKG_RESOURCES | \ + SD_NUMA | \ + SD_ASYM_PACKING) static struct sched_domain * -sd_numa_init(struct sched_domain_topology_level *tl, int cpu) +sd_init(struct sched_domain_topology_level *tl, int cpu) { struct sched_domain *sd = *per_cpu_ptr(tl->data.sd, cpu); - int level = tl->numa_level; - int sd_weight = cpumask_weight( - sched_domains_numa_masks[level][cpu_to_node(cpu)]); + int sd_weight, sd_flags = 0; + +#ifdef CONFIG_NUMA + /* + * Ugly hack to pass state to sd_numa_mask()... + */ + sched_domains_curr_level = tl->numa_level; +#endif + + sd_weight = cpumask_weight(tl->mask(cpu)); + + if (tl->sd_flags) + sd_flags = (*tl->sd_flags)(); + if (WARN_ONCE(sd_flags & ~TOPOLOGY_SD_FLAGS, + "wrong sd_flags in topology description\n")) + sd_flags &= ~TOPOLOGY_SD_FLAGS; *sd = (struct sched_domain){ .min_interval = sd_weight, .max_interval = 2*sd_weight, .busy_factor = 32, .imbalance_pct = 125, - .cache_nice_tries = 2, - .busy_idx = 3, - .idle_idx = 2, + + .cache_nice_tries = 0, + .busy_idx = 0, + .idle_idx = 0, .newidle_idx = 0, .wake_idx = 0, .forkexec_idx = 0, .flags = 1*SD_LOAD_BALANCE | 1*SD_BALANCE_NEWIDLE - | 0*SD_BALANCE_EXEC - | 0*SD_BALANCE_FORK + | 1*SD_BALANCE_EXEC + | 1*SD_BALANCE_FORK | 0*SD_BALANCE_WAKE - | 0*SD_WAKE_AFFINE + | 1*SD_WAKE_AFFINE | 0*SD_SHARE_CPUPOWER | 0*SD_SHARE_PKG_RESOURCES - | 1*SD_SERIALIZE + | 0*SD_SERIALIZE | 0*SD_PREFER_SIBLING - | 1*SD_NUMA - | sd_local_flags(level) + | 0*SD_NUMA + | sd_flags , + .last_balance = jiffies, .balance_interval = sd_weight, + .smt_gain = 0, .max_newidle_lb_cost = 0, .next_decay_max_lb_cost = jiffies, +#ifdef CONFIG_SCHED_DEBUG + .name = tl->name, +#endif }; - SD_INIT_NAME(sd, NUMA); - sd->private = &tl->data; /* - * Ugly hack to pass state to sd_numa_mask()... + * Convert topological properties into behaviour. */ - sched_domains_curr_level = tl->numa_level; + + if (sd->flags & SD_SHARE_CPUPOWER) { + sd->imbalance_pct = 110; + sd->smt_gain = 1178; /* ~15% */ + sd->flags |= arch_sd_sibling_asym_packing(); + + } else if (sd->flags & SD_SHARE_PKG_RESOURCES) { + sd->imbalance_pct = 117; + sd->cache_nice_tries = 1; + sd->busy_idx = 2; + +#ifdef CONFIG_NUMA + } else if (sd->flags & SD_NUMA) { + sd->cache_nice_tries = 2; + sd->busy_idx = 3; + sd->idle_idx = 2; + + sd->flags |= SD_SERIALIZE; + if (sched_domains_numa_distance[tl->numa_level] > RECLAIM_DISTANCE) { + sd->flags &= ~(SD_BALANCE_EXEC | + SD_BALANCE_FORK | + SD_WAKE_AFFINE); + } + +#endif + } else { + sd->flags |= SD_PREFER_SIBLING; + sd->cache_nice_tries = 1; + sd->busy_idx = 2; + sd->idle_idx = 1; + } + + sd->private = &tl->data; return sd; } +/* + * Topology list, bottom-up. + */ +static struct sched_domain_topology_level default_topology[] = { +#ifdef CONFIG_SCHED_SMT + { cpu_smt_mask, cpu_smt_flags, SD_INIT_NAME(SMT) }, +#endif +#ifdef CONFIG_SCHED_MC + { cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) }, +#endif +#ifdef CONFIG_SCHED_BOOK + { cpu_book_mask, SD_INIT_NAME(BOOK) }, +#endif + { cpu_cpu_mask, SD_INIT_NAME(DIE) }, + { NULL, }, +}; + +struct sched_domain_topology_level *sched_domain_topology = default_topology; + +#define for_each_sd_topology(tl) \ + for (tl = sched_domain_topology; tl->mask; tl++) + +void set_sched_topology(struct sched_domain_topology_level *tl) +{ + sched_domain_topology = tl; +} + +#ifdef CONFIG_NUMA + static const struct cpumask *sd_numa_mask(int cpu) { return sched_domains_numa_masks[sched_domains_curr_level][cpu_to_node(cpu)]; @@ -6183,7 +6186,10 @@ static void sched_init_numa(void) } } - tl = kzalloc((ARRAY_SIZE(default_topology) + level) * + /* Compute default topology size */ + for (i = 0; sched_domain_topology[i].mask; i++); + + tl = kzalloc((i + level) * sizeof(struct sched_domain_topology_level), GFP_KERNEL); if (!tl) return; @@ -6191,18 +6197,19 @@ static void sched_init_numa(void) /* * Copy the default topology bits.. */ - for (i = 0; default_topology[i].init; i++) - tl[i] = default_topology[i]; + for (i = 0; sched_domain_topology[i].mask; i++) + tl[i] = sched_domain_topology[i]; /* * .. and append 'j' levels of NUMA goodness. */ for (j = 0; j < level; i++, j++) { tl[i] = (struct sched_domain_topology_level){ - .init = sd_numa_init, .mask = sd_numa_mask, + .sd_flags = cpu_numa_flags, .flags = SDTL_OVERLAP, .numa_level = j, + SD_INIT_NAME(NUMA) }; } @@ -6360,7 +6367,7 @@ struct sched_domain *build_sched_domain(struct sched_domain_topology_level *tl, const struct cpumask *cpu_map, struct sched_domain_attr *attr, struct sched_domain *child, int cpu) { - struct sched_domain *sd = tl->init(tl, cpu); + struct sched_domain *sd = sd_init(tl, cpu); if (!sd) return child; -- cgit v1.2.3 From 607b45e9a216e89a63351556e488eea06be0ff48 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Fri, 11 Apr 2014 11:44:39 +0200 Subject: sched, powerpc: Create a dedicated topology table Create a dedicated topology table for handling asymetric feature of powerpc. Signed-off-by: Vincent Guittot Reviewed-by: Preeti U Murthy Signed-off-by: Peter Zijlstra Cc: Andy Fleming Cc: Anton Blanchard Cc: Benjamin Herrenschmidt Cc: Grant Likely Cc: Linus Torvalds Cc: Michael Ellerman Cc: Paul Gortmaker Cc: Paul Mackerras Cc: Preeti U. Murthy Cc: Rob Herring Cc: Srivatsa S. Bhat Cc: Toshi Kani Cc: Vasant Hegde Cc: tony.luck@intel.com Cc: fenghua.yu@intel.com Cc: schwidefsky@de.ibm.com Cc: cmetcalf@tilera.com Cc: dietmar.eggemann@arm.com Cc: devicetree@vger.kernel.org Cc: linuxppc-dev@lists.ozlabs.org Link: http://lkml.kernel.org/r/1397209481-28542-4-git-send-email-vincent.guittot@linaro.org Signed-off-by: Ingo Molnar --- arch/powerpc/kernel/smp.c | 31 +++++++++++++++++++++++-------- include/linux/sched.h | 2 -- kernel/sched/core.c | 6 ------ 3 files changed, 23 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index e2a4232c5871..10ffffef0414 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -766,6 +766,28 @@ int setup_profiling_timer(unsigned int multiplier) return 0; } +#ifdef CONFIG_SCHED_SMT +/* cpumask of CPUs with asymetric SMT dependancy */ +static const int powerpc_smt_flags(void) +{ + int flags = SD_SHARE_CPUPOWER | SD_SHARE_PKG_RESOURCES; + + if (cpu_has_feature(CPU_FTR_ASYM_SMT)) { + printk_once(KERN_INFO "Enabling Asymmetric SMT scheduling\n"); + flags |= SD_ASYM_PACKING; + } + return flags; +} +#endif + +static struct sched_domain_topology_level powerpc_topology[] = { +#ifdef CONFIG_SCHED_SMT + { cpu_smt_mask, powerpc_smt_flags, SD_INIT_NAME(SMT) }, +#endif + { cpu_cpu_mask, SD_INIT_NAME(DIE) }, + { NULL, }, +}; + void __init smp_cpus_done(unsigned int max_cpus) { cpumask_var_t old_mask; @@ -790,15 +812,8 @@ void __init smp_cpus_done(unsigned int max_cpus) dump_numa_cpu_topology(); -} + set_sched_topology(powerpc_topology); -int arch_sd_sibling_asym_packing(void) -{ - if (cpu_has_feature(CPU_FTR_ASYM_SMT)) { - printk_once(KERN_INFO "Enabling Asymmetric SMT scheduling\n"); - return SD_ASYM_PACKING; - } - return 0; } #ifdef CONFIG_HOTPLUG_CPU diff --git a/include/linux/sched.h b/include/linux/sched.h index 656b035c30e5..439a153b8403 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -877,8 +877,6 @@ enum cpu_idle_type { #define SD_OVERLAP 0x2000 /* sched_domains of this level overlap */ #define SD_NUMA 0x4000 /* cross-node balancing */ -extern int __weak arch_sd_sibiling_asym_packing(void); - #ifdef CONFIG_SCHED_SMT static inline const int cpu_smt_flags(void) { diff --git a/kernel/sched/core.c b/kernel/sched/core.c index e59e5aec745a..7e348e238bf1 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5796,11 +5796,6 @@ static void init_sched_groups_power(int cpu, struct sched_domain *sd) atomic_set(&sg->sgp->nr_busy_cpus, sg->group_weight); } -int __weak arch_sd_sibling_asym_packing(void) -{ - return 0*SD_ASYM_PACKING; -} - /* * Initializers for schedule domains * Non-inlined to reduce accumulated stack pressure in build_sched_domains() @@ -5981,7 +5976,6 @@ sd_init(struct sched_domain_topology_level *tl, int cpu) if (sd->flags & SD_SHARE_CPUPOWER) { sd->imbalance_pct = 110; sd->smt_gain = 1178; /* ~15% */ - sd->flags |= arch_sd_sibling_asym_packing(); } else if (sd->flags & SD_SHARE_PKG_RESOURCES) { sd->imbalance_pct = 117; -- cgit v1.2.3 From d77b3ed5c9f8ebedf154b52b5e943c461f3d37e6 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Fri, 11 Apr 2014 11:44:40 +0200 Subject: sched: Add a new SD_SHARE_POWERDOMAIN for sched_domain A new flag SD_SHARE_POWERDOMAIN is created to reflect whether groups of CPUs in a sched_domain level can or not reach different power state. As an example, the flag should be cleared at CPU level if groups of cores can be power gated independently. This information can be used in the load balance decision or to add load balancing level between group of CPUs that can power gate independantly. This flag is part of the topology flags that can be set by arch. Reviewed-by: Dietmar Eggemann Tested-by: Dietmar Eggemann Signed-off-by: Vincent Guittot Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: tony.luck@intel.com Cc: fenghua.yu@intel.com Cc: schwidefsky@de.ibm.com Cc: cmetcalf@tilera.com Cc: benh@kernel.crashing.org Cc: preeti@linux.vnet.ibm.com Link: http://lkml.kernel.org/r/1397209481-28542-5-git-send-email-vincent.guittot@linaro.org Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 + kernel/sched/core.c | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 439a153b8403..accb66bfd722 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -870,6 +870,7 @@ enum cpu_idle_type { #define SD_BALANCE_WAKE 0x0010 /* Balance on wakeup */ #define SD_WAKE_AFFINE 0x0020 /* Wake task to waking CPU */ #define SD_SHARE_CPUPOWER 0x0080 /* Domain members share cpu power */ +#define SD_SHARE_POWERDOMAIN 0x0100 /* Domain members share power domain */ #define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share cpu pkg resources */ #define SD_SERIALIZE 0x0400 /* Only a single load balancing instance */ #define SD_ASYM_PACKING 0x0800 /* Place busy groups earlier in the domain */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 7e348e238bf1..1c9c3b7b26af 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -5261,7 +5261,8 @@ static int sd_degenerate(struct sched_domain *sd) SD_BALANCE_FORK | SD_BALANCE_EXEC | SD_SHARE_CPUPOWER | - SD_SHARE_PKG_RESOURCES)) { + SD_SHARE_PKG_RESOURCES | + SD_SHARE_POWERDOMAIN)) { if (sd->groups != sd->groups->next) return 0; } @@ -5292,7 +5293,8 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) SD_BALANCE_EXEC | SD_SHARE_CPUPOWER | SD_SHARE_PKG_RESOURCES | - SD_PREFER_SIBLING); + SD_PREFER_SIBLING | + SD_SHARE_POWERDOMAIN); if (nr_node_ids == 1) pflags &= ~SD_SERIALIZE; } @@ -5901,6 +5903,7 @@ static int sched_domains_curr_level; * SD_SHARE_CPUPOWER - describes SMT topologies * SD_SHARE_PKG_RESOURCES - describes shared caches * SD_NUMA - describes NUMA topologies + * SD_SHARE_POWERDOMAIN - describes shared power domain * * Odd one out: * SD_ASYM_PACKING - describes SMT quirks @@ -5909,7 +5912,8 @@ static int sched_domains_curr_level; (SD_SHARE_CPUPOWER | \ SD_SHARE_PKG_RESOURCES | \ SD_NUMA | \ - SD_ASYM_PACKING) + SD_ASYM_PACKING | \ + SD_SHARE_POWERDOMAIN) static struct sched_domain * sd_init(struct sched_domain_topology_level *tl, int cpu) -- cgit v1.2.3 From 591c1ee465ce5372385dbc41e7d3e36cbb477bd8 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 24 Apr 2014 11:30:04 -0400 Subject: of: configure the platform device dma parameters Retrieve DMA configuration from DT and setup platform device's DMA parameters. The DMA configuration in DT has to be specified using "dma-ranges" and "dma-coherent" properties if supported. We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops using "dma-coherent" device tree properties. The set_arch_dma_coherent_ops macro has to be defined by arch if it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is declared as nop. Cc: Greg Kroah-Hartman Cc: Russell King Cc: Arnd Bergmann Cc: Olof Johansson Cc: Grant Likely Cc: Catalin Marinas Cc: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Grygorii Strashko Signed-off-by: Santosh Shilimkar --- drivers/of/platform.c | 65 ++++++++++++++++++++++++++++++++++++++++----- include/linux/dma-mapping.h | 7 +++++ 2 files changed, 66 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 404d1daebefa..91fa9838b56f 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -186,6 +186,64 @@ struct platform_device *of_device_alloc(struct device_node *np, } EXPORT_SYMBOL(of_device_alloc); +/** + * of_dma_configure - Setup DMA configuration + * @dev: Device to apply DMA configuration + * + * Try to get devices's DMA configuration from DT and update it + * accordingly. + * + * In case if platform code need to use own special DMA configuration,it + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event + * to fix up DMA configuration. + */ +static void of_dma_configure(struct platform_device *pdev) +{ + u64 dma_addr, paddr, size; + int ret; + struct device *dev = &pdev->dev; + +#if defined(CONFIG_MICROBLAZE) + pdev->archdata.dma_mask = 0xffffffffUL; +#endif + + /* + * Set default dma-mask to 32 bit. Drivers are expected to setup + * the correct supported dma_mask. + */ + dev->coherent_dma_mask = DMA_BIT_MASK(32); + + /* + * Set it to coherent_dma_mask by default if the architecture + * code has not set it. + */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + + /* + * if dma-coherent property exist, call arch hook to setup + * dma coherent operations. + */ + if (of_dma_is_coherent(dev->of_node)) { + set_arch_dma_coherent_ops(dev); + dev_dbg(dev, "device is dma coherent\n"); + } + + /* + * if dma-ranges property doesn't exist - just return else + * setup the dma offset + */ + ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size); + if (ret < 0) { + dev_dbg(dev, "no dma range information to setup\n"); + return; + } + + /* DMA ranges found. Calculate and set dma_pfn_offset */ + dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr); + dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset); +} + /** * of_platform_device_create_pdata - Alloc, initialize and register an of_device * @np: pointer to node to create device for @@ -211,12 +269,7 @@ static struct platform_device *of_platform_device_create_pdata( if (!dev) return NULL; -#if defined(CONFIG_MICROBLAZE) - dev->archdata.dma_mask = 0xffffffffUL; -#endif - dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - if (!dev->dev.dma_mask) - dev->dev.dma_mask = &dev->dev.coherent_dma_mask; + of_dma_configure(dev); dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index fd4aee29ad10..c7d9b1b14ce7 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -123,6 +123,13 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask) extern u64 dma_get_required_mask(struct device *dev); +#ifndef set_arch_dma_coherent_ops +static inline int set_arch_dma_coherent_ops(struct device *dev) +{ + return 0; +} +#endif + static inline unsigned int dma_get_max_seg_size(struct device *dev) { return dev->dma_parms ? dev->dma_parms->max_segment_size : 65536; -- cgit v1.2.3 From 2b53f41fa8604845f4f7c538723694a453088b15 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 7 May 2014 09:21:56 -0400 Subject: cgroup: remove unused CGRP_SANE_BEHAVIOR This cgroup flag has never been used. Only CGRP_ROOT_SANE_BEHAVIOR is used. Remove it. Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 2dfabb3b749a..f482f95c2c72 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -140,8 +140,6 @@ enum { * specified at mount time and thus is implemented here. */ CGRP_CPUSET_CLONE_CHILDREN, - /* see the comment above CGRP_ROOT_SANE_BEHAVIOR for details */ - CGRP_SANE_BEHAVIOR, }; struct cgroup { -- cgit v1.2.3 From 7c65bbc7dcface00b295bbd18bce82fe1db3d633 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 6 May 2014 09:26:30 -0400 Subject: tracing: Add trace__enabled() function There are some code paths in the kernel that need to do some preparations before it calls a tracepoint. As that code is worthless overhead when the tracepoint is not enabled, it would be prudent to have that code only run when the tracepoint is active. To accomplish this, all tracepoints now get a static inline function called "trace__enabled()" which returns true when the tracepoint is enabled and false otherwise. As an added bonus, that function uses the static_key of the tracepoint such that no branch is needed. if (trace_mytracepoint_enabled()) { arg = process_tp_arg(); trace_mytracepoint(arg); } Will keep the "process_tp_arg()" (which may be expensive to run) from being executed when the tracepoint isn't enabled. It's best to encapsulate the tracepoint itself in the if statement just to keep races. For example, if you had: if (trace_mytracepoint_enabled()) arg = process_tp_arg(); trace_mytracepoint(arg); There's a chance that the tracepoint could be enabled just after the if statement, and arg will be undefined when calling the tracepoint. Link: http://lkml.kernel.org/r/20140506094407.507b6435@gandalf.local.home Acked-by: Mathieu Desnoyers Signed-off-by: Steven Rostedt --- Documentation/trace/tracepoints.txt | 24 ++++++++++++++++++++++++ include/linux/tracepoint.h | 10 ++++++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/Documentation/trace/tracepoints.txt b/Documentation/trace/tracepoints.txt index 6b018b53177a..a3efac621c5a 100644 --- a/Documentation/trace/tracepoints.txt +++ b/Documentation/trace/tracepoints.txt @@ -115,6 +115,30 @@ If the tracepoint has to be used in kernel modules, an EXPORT_TRACEPOINT_SYMBOL_GPL() or EXPORT_TRACEPOINT_SYMBOL() can be used to export the defined tracepoints. +If you need to do a bit of work for a tracepoint parameter, and +that work is only used for the tracepoint, that work can be encapsulated +within an if statement with the following: + + if (trace_foo_bar_enabled()) { + int i; + int tot = 0; + + for (i = 0; i < count; i++) + tot += calculate_nuggets(); + + trace_foo_bar(tot); + } + +All trace_() calls have a matching trace__enabled() +function defined that returns true if the tracepoint is enabled and +false otherwise. The trace_() should always be within the +block of the if (trace__enabled()) to prevent races between +the tracepoint being enabled and the check being seen. + +The advantage of using the trace__enabled() is that it uses +the static_key of the tracepoint to allow the if statement to be implemented +with jump labels and avoid conditional branches. + Note: The convenience macro TRACE_EVENT provides an alternative way to define tracepoints. Check http://lwn.net/Articles/379903, http://lwn.net/Articles/381064 and http://lwn.net/Articles/383362 diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 9d30ee469c2a..2e2a5f7717e5 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -185,6 +185,11 @@ extern void syscall_unregfunc(void); static inline void \ check_trace_callback_type_##name(void (*cb)(data_proto)) \ { \ + } \ + static inline bool \ + trace_##name##_enabled(void) \ + { \ + return static_key_false(&__tracepoint_##name.key); \ } /* @@ -230,6 +235,11 @@ extern void syscall_unregfunc(void); } \ static inline void check_trace_callback_type_##name(void (*cb)(data_proto)) \ { \ + } \ + static inline bool \ + trace_##name##_enabled(void) \ + { \ + return false; \ } #define DEFINE_TRACE_FN(name, reg, unreg) -- cgit v1.2.3 From 506e931f92defdc60c1dc4aa2ff4a19a5dcd8618 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 7 May 2014 10:26:44 -0600 Subject: blk-mq: add basic round-robin of what CPU to queue workqueue work on Right now we just pick the first CPU in the mask, but that can easily overload that one. Add some basic batching and round-robin all the entries in the mask instead. Signed-off-by: Jens Axboe --- block/blk-mq.c | 45 +++++++++++++++++++++++++++++++-------------- include/linux/blk-mq.h | 4 ++++ 2 files changed, 35 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 0d379830a278..2410e0cb7aef 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -670,6 +670,30 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) } } +/* + * It'd be great if the workqueue API had a way to pass + * in a mask and had some smarts for more clever placement. + * For now we just round-robin here, switching for every + * BLK_MQ_CPU_WORK_BATCH queued items. + */ +static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx) +{ + int cpu = hctx->next_cpu; + + if (--hctx->next_cpu_batch <= 0) { + int next_cpu; + + next_cpu = cpumask_next(hctx->next_cpu, hctx->cpumask); + if (next_cpu >= nr_cpu_ids) + next_cpu = cpumask_first(hctx->cpumask); + + hctx->next_cpu = next_cpu; + hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH; + } + + return cpu; +} + void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async) { if (unlikely(test_bit(BLK_MQ_S_STOPPED, &hctx->state))) @@ -682,13 +706,7 @@ void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async) else { unsigned int cpu; - /* - * It'd be great if the workqueue API had a way to pass - * in a mask and had some smarts for more clever placement - * than the first CPU. Or we could round-robin here. For now, - * just queue on the first CPU. - */ - cpu = cpumask_first(hctx->cpumask); + cpu = blk_mq_hctx_next_cpu(hctx); kblockd_schedule_delayed_work_on(cpu, &hctx->run_work, 0); } } @@ -795,13 +813,7 @@ void blk_mq_delay_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs) else { unsigned int cpu; - /* - * It'd be great if the workqueue API had a way to pass - * in a mask and had some smarts for more clever placement - * than the first CPU. Or we could round-robin here. For now, - * just queue on the first CPU. - */ - cpu = cpumask_first(hctx->cpumask); + cpu = blk_mq_hctx_next_cpu(hctx); kblockd_schedule_delayed_work_on(cpu, &hctx->delay_work, tmo); } } @@ -1378,6 +1390,11 @@ static void blk_mq_map_swqueue(struct request_queue *q) ctx->index_hw = hctx->nr_ctx; hctx->ctxs[hctx->nr_ctx++] = ctx; } + + queue_for_each_hw_ctx(q, hctx, i) { + hctx->next_cpu = cpumask_first(hctx->cpumask); + hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH; + } } struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 3b561d651a02..5bd677e2dcb7 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -21,6 +21,8 @@ struct blk_mq_hw_ctx { struct delayed_work run_work; struct delayed_work delay_work; cpumask_var_t cpumask; + int next_cpu; + int next_cpu_batch; unsigned long flags; /* BLK_MQ_F_* flags */ @@ -126,6 +128,8 @@ enum { BLK_MQ_S_STOPPED = 0, BLK_MQ_MAX_DEPTH = 2048, + + BLK_MQ_CPU_WORK_BATCH = 8, }; struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *); -- cgit v1.2.3 From 80eded6ce8bb8bade60955660c6957d6166c44c1 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 7 May 2014 18:02:15 +0200 Subject: clk: at91: add slow clks driver AT91 slow clk is a clk multiplexer. In some SoCs (sam9x5, sama5, sam9g45 families) this multiplexer can choose among 2 sources: an internal RC oscillator circuit and an oscillator using an external crystal. In other Socs (sam9260 family) the multiplexer source is hardcoded with the OSCSEL signal. Signed-off-by: Boris BREZILLON Acked-by: Mike Turquette Signed-off-by: Nicolas Ferre --- drivers/clk/at91/Makefile | 4 +- drivers/clk/at91/clk-slow.c | 467 +++++++++++++++++++++++++++++++++++++++++++ drivers/clk/at91/pmc.c | 5 + drivers/clk/at91/pmc.h | 3 + drivers/clk/at91/sckc.c | 57 ++++++ drivers/clk/at91/sckc.h | 22 ++ include/linux/clk/at91_pmc.h | 1 + 7 files changed, 557 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/at91/clk-slow.c create mode 100644 drivers/clk/at91/sckc.c create mode 100644 drivers/clk/at91/sckc.h (limited to 'include') diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 46c1d3d0d66b..4998aee59267 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -2,8 +2,8 @@ # Makefile for at91 specific clk # -obj-y += pmc.o -obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o +obj-y += pmc.o sckc.o +obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o obj-y += clk-system.o clk-peripheral.o clk-programmable.o obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c new file mode 100644 index 000000000000..0300c46ee247 --- /dev/null +++ b/drivers/clk/at91/clk-slow.c @@ -0,0 +1,467 @@ +/* + * drivers/clk/at91/clk-slow.c + * + * Copyright (C) 2013 Boris BREZILLON + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmc.h" +#include "sckc.h" + +#define SLOW_CLOCK_FREQ 32768 +#define SLOWCK_SW_CYCLES 5 +#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ + SLOW_CLOCK_FREQ) + +#define AT91_SCKC_CR 0x00 +#define AT91_SCKC_RCEN (1 << 0) +#define AT91_SCKC_OSC32EN (1 << 1) +#define AT91_SCKC_OSC32BYP (1 << 2) +#define AT91_SCKC_OSCSEL (1 << 3) + +struct clk_slow_osc { + struct clk_hw hw; + void __iomem *sckcr; + unsigned long startup_usec; +}; + +#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) + +struct clk_slow_rc_osc { + struct clk_hw hw; + void __iomem *sckcr; + unsigned long frequency; + unsigned long accuracy; + unsigned long startup_usec; +}; + +#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) + +struct clk_sam9260_slow { + struct clk_hw hw; + struct at91_pmc *pmc; +}; + +#define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw) + +struct clk_sam9x5_slow { + struct clk_hw hw; + void __iomem *sckcr; + u8 parent; +}; + +#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) + + +static int clk_slow_osc_prepare(struct clk_hw *hw) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(hw); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & AT91_SCKC_OSC32BYP) + return 0; + + writel(tmp | AT91_SCKC_OSC32EN, sckcr); + + usleep_range(osc->startup_usec, osc->startup_usec + 1); + + return 0; +} + +static void clk_slow_osc_unprepare(struct clk_hw *hw) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(hw); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & AT91_SCKC_OSC32BYP) + return; + + writel(tmp & ~AT91_SCKC_OSC32EN, sckcr); +} + +static int clk_slow_osc_is_prepared(struct clk_hw *hw) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(hw); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & AT91_SCKC_OSC32BYP) + return 1; + + return !!(tmp & AT91_SCKC_OSC32EN); +} + +static const struct clk_ops slow_osc_ops = { + .prepare = clk_slow_osc_prepare, + .unprepare = clk_slow_osc_unprepare, + .is_prepared = clk_slow_osc_is_prepared, +}; + +static struct clk * __init +at91_clk_register_slow_osc(void __iomem *sckcr, + const char *name, + const char *parent_name, + unsigned long startup, + bool bypass) +{ + struct clk_slow_osc *osc; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!sckcr || !name || !parent_name) + return ERR_PTR(-EINVAL); + + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &slow_osc_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_IGNORE_UNUSED; + + osc->hw.init = &init; + osc->sckcr = sckcr; + osc->startup_usec = startup; + + if (bypass) + writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP, + sckcr); + + clk = clk_register(NULL, &osc->hw); + if (IS_ERR(clk)) + kfree(osc); + + return clk; +} + +void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, + void __iomem *sckcr) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + u32 startup; + bool bypass; + + parent_name = of_clk_get_parent_name(np, 0); + of_property_read_string(np, "clock-output-names", &name); + of_property_read_u32(np, "atmel,startup-time-usec", &startup); + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup, + bypass); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + + return osc->frequency; +} + +static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, + unsigned long parent_acc) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + + return osc->accuracy; +} + +static int clk_slow_rc_osc_prepare(struct clk_hw *hw) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + void __iomem *sckcr = osc->sckcr; + + writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr); + + usleep_range(osc->startup_usec, osc->startup_usec + 1); + + return 0; +} + +static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + void __iomem *sckcr = osc->sckcr; + + writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr); +} + +static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); + + return !!(readl(osc->sckcr) & AT91_SCKC_RCEN); +} + +static const struct clk_ops slow_rc_osc_ops = { + .prepare = clk_slow_rc_osc_prepare, + .unprepare = clk_slow_rc_osc_unprepare, + .is_prepared = clk_slow_rc_osc_is_prepared, + .recalc_rate = clk_slow_rc_osc_recalc_rate, + .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, +}; + +static struct clk * __init +at91_clk_register_slow_rc_osc(void __iomem *sckcr, + const char *name, + unsigned long frequency, + unsigned long accuracy, + unsigned long startup) +{ + struct clk_slow_rc_osc *osc; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!sckcr || !name) + return ERR_PTR(-EINVAL); + + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &slow_rc_osc_ops; + init.parent_names = NULL; + init.num_parents = 0; + init.flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED; + + osc->hw.init = &init; + osc->sckcr = sckcr; + osc->frequency = frequency; + osc->accuracy = accuracy; + osc->startup_usec = startup; + + clk = clk_register(NULL, &osc->hw); + if (IS_ERR(clk)) + kfree(osc); + + return clk; +} + +void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, + void __iomem *sckcr) +{ + struct clk *clk; + u32 frequency = 0; + u32 accuracy = 0; + u32 startup = 0; + const char *name = np->name; + + of_property_read_string(np, "clock-output-names", &name); + of_property_read_u32(np, "clock-frequency", &frequency); + of_property_read_u32(np, "clock-accuracy", &accuracy); + of_property_read_u32(np, "atmel,startup-time-usec", &startup); + + clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy, + startup); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); + void __iomem *sckcr = slowck->sckcr; + u32 tmp; + + if (index > 1) + return -EINVAL; + + tmp = readl(sckcr); + + if ((!index && !(tmp & AT91_SCKC_OSCSEL)) || + (index && (tmp & AT91_SCKC_OSCSEL))) + return 0; + + if (index) + tmp |= AT91_SCKC_OSCSEL; + else + tmp &= ~AT91_SCKC_OSCSEL; + + writel(tmp, sckcr); + + usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); + + return 0; +} + +static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) +{ + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); + + return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL); +} + +static const struct clk_ops sam9x5_slow_ops = { + .set_parent = clk_sam9x5_slow_set_parent, + .get_parent = clk_sam9x5_slow_get_parent, +}; + +static struct clk * __init +at91_clk_register_sam9x5_slow(void __iomem *sckcr, + const char *name, + const char **parent_names, + int num_parents) +{ + struct clk_sam9x5_slow *slowck; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!sckcr || !name || !parent_names || !num_parents) + return ERR_PTR(-EINVAL); + + slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); + if (!slowck) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &sam9x5_slow_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = 0; + + slowck->hw.init = &init; + slowck->sckcr = sckcr; + slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL); + + clk = clk_register(NULL, &slowck->hw); + if (IS_ERR(clk)) + kfree(slowck); + + return clk; +} + +void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, + void __iomem *sckcr) +{ + struct clk *clk; + const char *parent_names[2]; + int num_parents; + const char *name = np->name; + int i; + + num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (num_parents <= 0 || num_parents > 2) + return; + + for (i = 0; i < num_parents; ++i) { + parent_names[i] = of_clk_get_parent_name(np, i); + if (!parent_names[i]) + return; + } + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names, + num_parents); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw) +{ + struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw); + + return !!(pmc_read(slowck->pmc, AT91_PMC_SR) & AT91_PMC_OSCSEL); +} + +static const struct clk_ops sam9260_slow_ops = { + .get_parent = clk_sam9260_slow_get_parent, +}; + +static struct clk * __init +at91_clk_register_sam9260_slow(struct at91_pmc *pmc, + const char *name, + const char **parent_names, + int num_parents) +{ + struct clk_sam9260_slow *slowck; + struct clk *clk = NULL; + struct clk_init_data init; + + if (!pmc || !name) + return ERR_PTR(-EINVAL); + + if (!parent_names || !num_parents) + return ERR_PTR(-EINVAL); + + slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); + if (!slowck) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &sam9260_slow_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = 0; + + slowck->hw.init = &init; + slowck->pmc = pmc; + + clk = clk_register(NULL, &slowck->hw); + if (IS_ERR(clk)) + kfree(slowck); + + return clk; +} + +void __init of_at91sam9260_clk_slow_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + struct clk *clk; + const char *parent_names[2]; + int num_parents; + const char *name = np->name; + int i; + + num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (num_parents <= 0 || num_parents > 1) + return; + + for (i = 0; i < num_parents; ++i) { + parent_names[i] = of_clk_get_parent_name(np, i); + if (!parent_names[i]) + return; + } + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91_clk_register_sam9260_slow(pmc, name, parent_names, + num_parents); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index dc5fdde98e1a..524196bb35a5 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -229,6 +229,11 @@ out_free_pmc: } static const struct of_device_id pmc_clk_ids[] __initconst = { + /* Slow oscillator */ + { + .compatible = "atmel,at91sam9260-clk-slow", + .data = of_at91sam9260_clk_slow_setup, + }, /* Main clock */ { .compatible = "atmel,at91rm9200-clk-main-osc", diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 42cc7cc5e1d3..6c7625976113 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -58,6 +58,9 @@ static inline void pmc_write(struct at91_pmc *pmc, int offset, u32 value) int of_at91_get_clk_range(struct device_node *np, const char *propname, struct clk_range *range); +extern void __init of_at91sam9260_clk_slow_setup(struct device_node *np, + struct at91_pmc *pmc); + extern void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np, struct at91_pmc *pmc); extern void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np, diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c new file mode 100644 index 000000000000..1184d76a7ab7 --- /dev/null +++ b/drivers/clk/at91/sckc.c @@ -0,0 +1,57 @@ +/* + * drivers/clk/at91/sckc.c + * + * Copyright (C) 2013 Boris BREZILLON + * + * 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 +#include +#include +#include + +#include "sckc.h" + +static const struct of_device_id sckc_clk_ids[] __initconst = { + /* Slow clock */ + { + .compatible = "atmel,at91sam9x5-clk-slow-osc", + .data = of_at91sam9x5_clk_slow_osc_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-slow-rc-osc", + .data = of_at91sam9x5_clk_slow_rc_osc_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-slow", + .data = of_at91sam9x5_clk_slow_setup, + }, + { /*sentinel*/ } +}; + +static void __init of_at91sam9x5_sckc_setup(struct device_node *np) +{ + struct device_node *childnp; + void (*clk_setup)(struct device_node *, void __iomem *); + const struct of_device_id *clk_id; + void __iomem *regbase = of_iomap(np, 0); + + if (!regbase) + return; + + for_each_child_of_node(np, childnp) { + clk_id = of_match_node(sckc_clk_ids, childnp); + if (!clk_id) + continue; + clk_setup = clk_id->data; + clk_setup(childnp, regbase); + } +} +CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", + of_at91sam9x5_sckc_setup); diff --git a/drivers/clk/at91/sckc.h b/drivers/clk/at91/sckc.h new file mode 100644 index 000000000000..836fcf59820f --- /dev/null +++ b/drivers/clk/at91/sckc.h @@ -0,0 +1,22 @@ +/* + * drivers/clk/at91/sckc.h + * + * Copyright (C) 2013 Boris BREZILLON + * + * 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 __AT91_SCKC_H_ +#define __AT91_SCKC_H_ + +extern void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, + void __iomem *sckcr); +extern void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, + void __iomem *sckcr); +extern void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, + void __iomem *sckcr); + +#endif /* __AT91_SCKC_H_ */ diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h index a6911ebbd02a..de4268d4987a 100644 --- a/include/linux/clk/at91_pmc.h +++ b/include/linux/clk/at91_pmc.h @@ -155,6 +155,7 @@ extern void __iomem *at91_pmc_base; #define AT91_PMC_LOCKB (1 << 2) /* PLLB Lock */ #define AT91_PMC_MCKRDY (1 << 3) /* Master Clock */ #define AT91_PMC_LOCKU (1 << 6) /* UPLL Lock [some SAM9] */ +#define AT91_PMC_OSCSEL (1 << 7) /* Slow Oscillator Selection [some SAM9] */ #define AT91_PMC_PCK0RDY (1 << 8) /* Programmable Clock 0 */ #define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */ #define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */ -- cgit v1.2.3 From 2de0c019f34ffbe49744c453628afb270aa9adb6 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 15 Apr 2014 12:27:58 +0200 Subject: iio: adc: at91: cleanup platform_data num_channels and registers are not used anymore since they are defined inside the driver and assigned by matching the id_table. Also, struct at91_adc_reg_desc is now only used inside the driver. Signed-off-by: Alexandre Belloni Acked-by: Jonathan Cameron Signed-off-by: Nicolas Ferre --- drivers/iio/adc/at91_adc.c | 19 +++++++++++++++++++ include/linux/platform_data/at91_adc.h | 23 ----------------------- 2 files changed, 19 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 89777ed9abd8..1beae65aef2c 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -46,6 +46,25 @@ #define TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ #define TOUCH_PEN_DETECT_DEBOUNCE_US 200 +/** + * struct at91_adc_reg_desc - Various informations relative to registers + * @channel_base: Base offset for the channel data registers + * @drdy_mask: Mask of the DRDY field in the relevant registers + (Interruptions registers mostly) + * @status_register: Offset of the Interrupt Status Register + * @trigger_register: Offset of the Trigger setup register + * @mr_prescal_mask: Mask of the PRESCAL field in the adc MR register + * @mr_startup_mask: Mask of the STARTUP field in the adc MR register + */ +struct at91_adc_reg_desc { + u8 channel_base; + u32 drdy_mask; + u8 status_register; + u8 trigger_register; + u32 mr_prescal_mask; + u32 mr_startup_mask; +}; + struct at91_adc_caps { bool has_ts; /* Support touch screen */ bool has_tsmr; /* only at91sam9x5, sama5d3 have TSMR reg */ diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h index b3ca1e94e0c8..fcf73879dbfe 100644 --- a/include/linux/platform_data/at91_adc.h +++ b/include/linux/platform_data/at91_adc.h @@ -7,25 +7,6 @@ #ifndef _AT91_ADC_H_ #define _AT91_ADC_H_ -/** - * struct at91_adc_reg_desc - Various informations relative to registers - * @channel_base: Base offset for the channel data registers - * @drdy_mask: Mask of the DRDY field in the relevant registers - (Interruptions registers mostly) - * @status_register: Offset of the Interrupt Status Register - * @trigger_register: Offset of the Trigger setup register - * @mr_prescal_mask: Mask of the PRESCAL field in the adc MR register - * @mr_startup_mask: Mask of the STARTUP field in the adc MR register - */ -struct at91_adc_reg_desc { - u8 channel_base; - u32 drdy_mask; - u8 status_register; - u8 trigger_register; - u32 mr_prescal_mask; - u32 mr_startup_mask; -}; - /** * struct at91_adc_trigger - description of triggers * @name: name of the trigger advertised to the user @@ -42,8 +23,6 @@ struct at91_adc_trigger { /** * struct at91_adc_data - platform data for ADC driver * @channels_used: channels in use on the board as a bitmask - * @num_channels: global number of channels available on the board - * @registers: Registers definition on the board * @startup_time: startup time of the ADC in microseconds * @trigger_list: Triggers available in the ADC * @trigger_number: Number of triggers available in the ADC @@ -52,8 +31,6 @@ struct at91_adc_trigger { */ struct at91_adc_data { unsigned long channels_used; - u8 num_channels; - struct at91_adc_reg_desc *registers; u8 startup_time; struct at91_adc_trigger *trigger_list; u8 trigger_number; -- cgit v1.2.3 From 84882b060301c35ab7e2c1ef355b0bd06b764195 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 15 Apr 2014 12:27:59 +0200 Subject: iio: adc: at91_adc: Add support for touchscreens without TSMR Old ADCs, as present on the sam9rl and the sam9g45 don't have a TSMR register and the touchscreen support should be handled differently. Signed-off-by: Alexandre Belloni Acked-by: Jonathan Cameron Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/include/mach/at91_adc.h | 13 ++ drivers/iio/adc/at91_adc.c | 200 ++++++++++++++++++++++------- include/linux/platform_data/at91_adc.h | 8 ++ 3 files changed, 174 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-at91/include/mach/at91_adc.h b/arch/arm/mach-at91/include/mach/at91_adc.h index c287307b9a3b..7d80396346b2 100644 --- a/arch/arm/mach-at91/include/mach/at91_adc.h +++ b/arch/arm/mach-at91/include/mach/at91_adc.h @@ -20,6 +20,9 @@ #define AT91_ADC_START (1 << 1) /* Start Conversion */ #define AT91_ADC_MR 0x04 /* Mode Register */ +#define AT91_ADC_TSAMOD (3 << 0) /* ADC mode */ +#define AT91_ADC_TSAMOD_ADC_ONLY_MODE (0 << 0) /* ADC Mode */ +#define AT91_ADC_TSAMOD_TS_ONLY_MODE (1 << 0) /* Touch Screen Only Mode */ #define AT91_ADC_TRGEN (1 << 0) /* Trigger Enable */ #define AT91_ADC_TRGSEL (7 << 1) /* Trigger Selection */ #define AT91_ADC_TRGSEL_TC0 (0 << 1) @@ -28,6 +31,7 @@ #define AT91_ADC_TRGSEL_EXTERNAL (6 << 1) #define AT91_ADC_LOWRES (1 << 4) /* Low Resolution */ #define AT91_ADC_SLEEP (1 << 5) /* Sleep Mode */ +#define AT91_ADC_PENDET (1 << 6) /* Pen contact detection enable */ #define AT91_ADC_PRESCAL_9260 (0x3f << 8) /* Prescalar Rate Selection */ #define AT91_ADC_PRESCAL_9G45 (0xff << 8) #define AT91_ADC_PRESCAL_(x) ((x) << 8) @@ -37,6 +41,12 @@ #define AT91_ADC_STARTUP_(x) ((x) << 16) #define AT91_ADC_SHTIM (0xf << 24) /* Sample & Hold Time */ #define AT91_ADC_SHTIM_(x) ((x) << 24) +#define AT91_ADC_PENDBC (0x0f << 28) /* Pen Debounce time */ +#define AT91_ADC_PENDBC_(x) ((x) << 28) + +#define AT91_ADC_TSR 0x0C +#define AT91_ADC_TSR_SHTIM (0xf << 24) /* Sample & Hold Time */ +#define AT91_ADC_TSR_SHTIM_(x) ((x) << 24) #define AT91_ADC_CHER 0x10 /* Channel Enable Register */ #define AT91_ADC_CHDR 0x14 /* Channel Disable Register */ @@ -60,6 +70,8 @@ #define AT91_ADC_IER 0x24 /* Interrupt Enable Register */ #define AT91_ADC_IDR 0x28 /* Interrupt Disable Register */ #define AT91_ADC_IMR 0x2C /* Interrupt Mask Register */ +#define AT91RL_ADC_IER_PEN (1 << 20) +#define AT91RL_ADC_IER_NOPEN (1 << 21) #define AT91_ADC_IER_PEN (1 << 29) #define AT91_ADC_IER_NOPEN (1 << 30) #define AT91_ADC_IER_XRDY (1 << 20) @@ -102,6 +114,7 @@ #define AT91_ADC_TRGR_TRGPER (0xffff << 16) #define AT91_ADC_TRGR_TRGPER_(x) ((x) << 16) #define AT91_ADC_TRGR_TRGMOD (0x7 << 0) +#define AT91_ADC_TRGR_NONE (0 << 0) #define AT91_ADC_TRGR_MOD_PERIOD_TRIG (5 << 0) #endif diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 1beae65aef2c..c0e4206e34e5 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -46,6 +46,10 @@ #define TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ #define TOUCH_PEN_DETECT_DEBOUNCE_US 200 +#define MAX_RLPOS_BITS 10 +#define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */ +#define TOUCH_SHTIM 0xa + /** * struct at91_adc_reg_desc - Various informations relative to registers * @channel_base: Base offset for the channel data registers @@ -83,12 +87,6 @@ struct at91_adc_caps { struct at91_adc_reg_desc registers; }; -enum atmel_adc_ts_type { - ATMEL_ADC_TOUCHSCREEN_NONE = 0, - ATMEL_ADC_TOUCHSCREEN_4WIRE = 4, - ATMEL_ADC_TOUCHSCREEN_5WIRE = 5, -}; - struct at91_adc_state { struct clk *adc_clk; u16 *buffer; @@ -133,6 +131,11 @@ struct at91_adc_state { u16 ts_sample_period_val; u32 ts_pressure_threshold; + u16 ts_pendbc; + + bool ts_bufferedmeasure; + u32 ts_prev_absx; + u32 ts_prev_absy; }; static irqreturn_t at91_adc_trigger_handler(int irq, void *p) @@ -239,7 +242,72 @@ static int at91_ts_sample(struct at91_adc_state *st) return 0; } -static irqreturn_t at91_adc_interrupt(int irq, void *private) +static irqreturn_t at91_adc_rl_interrupt(int irq, void *private) +{ + struct iio_dev *idev = private; + struct at91_adc_state *st = iio_priv(idev); + u32 status = at91_adc_readl(st, st->registers->status_register); + unsigned int reg; + + status &= at91_adc_readl(st, AT91_ADC_IMR); + if (status & st->registers->drdy_mask) + handle_adc_eoc_trigger(irq, idev); + + if (status & AT91RL_ADC_IER_PEN) { + /* Disabling pen debounce is required to get a NOPEN irq */ + reg = at91_adc_readl(st, AT91_ADC_MR); + reg &= ~AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN); + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_NOPEN + | AT91_ADC_EOC(3)); + /* Set up period trigger for sampling */ + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_MOD_PERIOD_TRIG | + AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val)); + } else if (status & AT91RL_ADC_IER_NOPEN) { + reg = at91_adc_readl(st, AT91_ADC_MR); + reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_NONE); + + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_NOPEN + | AT91_ADC_EOC(3)); + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN); + st->ts_bufferedmeasure = false; + input_report_key(st->ts_input, BTN_TOUCH, 0); + input_sync(st->ts_input); + } else if (status & AT91_ADC_EOC(3)) { + /* Conversion finished */ + if (st->ts_bufferedmeasure) { + /* + * Last measurement is always discarded, since it can + * be erroneous. + * Always report previous measurement + */ + input_report_abs(st->ts_input, ABS_X, st->ts_prev_absx); + input_report_abs(st->ts_input, ABS_Y, st->ts_prev_absy); + input_report_key(st->ts_input, BTN_TOUCH, 1); + input_sync(st->ts_input); + } else + st->ts_bufferedmeasure = true; + + /* Now make new measurement */ + st->ts_prev_absx = at91_adc_readl(st, AT91_ADC_CHAN(st, 3)) + << MAX_RLPOS_BITS; + st->ts_prev_absx /= at91_adc_readl(st, AT91_ADC_CHAN(st, 2)); + + st->ts_prev_absy = at91_adc_readl(st, AT91_ADC_CHAN(st, 1)) + << MAX_RLPOS_BITS; + st->ts_prev_absy /= at91_adc_readl(st, AT91_ADC_CHAN(st, 0)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t at91_adc_9x5_interrupt(int irq, void *private) { struct iio_dev *idev = private; struct at91_adc_state *st = iio_priv(idev); @@ -672,6 +740,8 @@ static int at91_adc_probe_dt_ts(struct device_node *node, return -EINVAL; } + if (!st->caps->has_tsmr) + return 0; prop = 0; of_property_read_u32(node, "atmel,adc-ts-pressure-threshold", &prop); st->ts_pressure_threshold = prop; @@ -795,6 +865,7 @@ static int at91_adc_probe_pdata(struct at91_adc_state *st, st->trigger_number = pdata->trigger_number; st->trigger_list = pdata->trigger_list; st->registers = &st->caps->registers; + st->touchscreen_type = pdata->touchscreen_type; return 0; } @@ -809,7 +880,10 @@ static int atmel_ts_open(struct input_dev *dev) { struct at91_adc_state *st = input_get_drvdata(dev); - at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + if (st->caps->has_tsmr) + at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + else + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN); return 0; } @@ -817,45 +891,61 @@ static void atmel_ts_close(struct input_dev *dev) { struct at91_adc_state *st = input_get_drvdata(dev); - at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); + if (st->caps->has_tsmr) + at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); + else + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN); } static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz) { - u32 reg = 0, pendbc; + u32 reg = 0; int i = 0; - if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) - reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS; - else - reg = AT91_ADC_TSMR_TSMODE_5WIRE; - /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid * pen detect noise. * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock */ - pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz / 1000, 1); + st->ts_pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz / + 1000, 1); - while (pendbc >> ++i) + while (st->ts_pendbc >> ++i) ; /* Empty! Find the shift offset */ - if (abs(pendbc - (1 << i)) < abs(pendbc - (1 << (i - 1)))) - pendbc = i; + if (abs(st->ts_pendbc - (1 << i)) < abs(st->ts_pendbc - (1 << (i - 1)))) + st->ts_pendbc = i; else - pendbc = i - 1; + st->ts_pendbc = i - 1; - if (st->caps->has_tsmr) { - reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average) - & AT91_ADC_TSMR_TSAV; - reg |= AT91_ADC_TSMR_PENDBC_(pendbc) & AT91_ADC_TSMR_PENDBC; - reg |= AT91_ADC_TSMR_NOTSDMA; - reg |= AT91_ADC_TSMR_PENDET_ENA; - reg |= 0x03 << 8; /* TSFREQ, need bigger than TSAV */ - - at91_adc_writel(st, AT91_ADC_TSMR, reg); - } else { - /* TODO: for 9g45 which has no TSMR */ + if (!st->caps->has_tsmr) { + reg = at91_adc_readl(st, AT91_ADC_MR); + reg |= AT91_ADC_TSAMOD_TS_ONLY_MODE | AT91_ADC_PENDET; + + reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + + reg = AT91_ADC_TSR_SHTIM_(TOUCH_SHTIM) & AT91_ADC_TSR_SHTIM; + at91_adc_writel(st, AT91_ADC_TSR, reg); + + st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US_RL * + adc_clk_khz / 1000) - 1, 1); + + return 0; } + if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) + reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS; + else + reg = AT91_ADC_TSMR_TSMODE_5WIRE; + + reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average) + & AT91_ADC_TSMR_TSAV; + reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC; + reg |= AT91_ADC_TSMR_NOTSDMA; + reg |= AT91_ADC_TSMR_PENDET_ENA; + reg |= 0x03 << 8; /* TSFREQ, needs to be bigger than TSAV */ + + at91_adc_writel(st, AT91_ADC_TSMR, reg); + /* Change adc internal resistor value for better pen detection, * default value is 100 kOhm. * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm @@ -864,7 +954,7 @@ static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz) at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity & AT91_ADC_ACR_PENDETSENS); - /* Sample Peroid Time = (TRGPER + 1) / ADCClock */ + /* Sample Period Time = (TRGPER + 1) / ADCClock */ st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US * adc_clk_khz / 1000) - 1, 1); @@ -893,17 +983,37 @@ static int at91_ts_register(struct at91_adc_state *st, __set_bit(EV_ABS, input->evbit); __set_bit(EV_KEY, input->evbit); __set_bit(BTN_TOUCH, input->keybit); - input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, 0, 0); - input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, 0, 0); - input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0); + if (st->caps->has_tsmr) { + input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0); + } else { + if (st->touchscreen_type != ATMEL_ADC_TOUCHSCREEN_4WIRE) { + dev_err(&pdev->dev, + "This touchscreen controller only support 4 wires\n"); + ret = -EINVAL; + goto err; + } + + input_set_abs_params(input, ABS_X, 0, (1 << MAX_RLPOS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_Y, 0, (1 << MAX_RLPOS_BITS) - 1, + 0, 0); + } st->ts_input = input; input_set_drvdata(input, st); ret = input_register_device(input); if (ret) - input_free_device(st->ts_input); + goto err; + + return ret; +err: + input_free_device(st->ts_input); return ret; } @@ -962,11 +1072,13 @@ static int at91_adc_probe(struct platform_device *pdev) */ at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST); at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF); - ret = request_irq(st->irq, - at91_adc_interrupt, - 0, - pdev->dev.driver->name, - idev); + + if (st->caps->has_tsmr) + ret = request_irq(st->irq, at91_adc_9x5_interrupt, 0, + pdev->dev.driver->name, idev); + else + ret = request_irq(st->irq, at91_adc_rl_interrupt, 0, + pdev->dev.driver->name, idev); if (ret) { dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); return ret; @@ -1070,12 +1182,6 @@ static int at91_adc_probe(struct platform_device *pdev) goto error_disable_adc_clk; } } else { - if (!st->caps->has_tsmr) { - dev_err(&pdev->dev, "We don't support non-TSMR adc\n"); - ret = -ENODEV; - goto error_disable_adc_clk; - } - ret = at91_ts_register(st, pdev); if (ret) goto error_disable_adc_clk; diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h index fcf73879dbfe..7819fc787731 100644 --- a/include/linux/platform_data/at91_adc.h +++ b/include/linux/platform_data/at91_adc.h @@ -7,6 +7,12 @@ #ifndef _AT91_ADC_H_ #define _AT91_ADC_H_ +enum atmel_adc_ts_type { + ATMEL_ADC_TOUCHSCREEN_NONE = 0, + ATMEL_ADC_TOUCHSCREEN_4WIRE = 4, + ATMEL_ADC_TOUCHSCREEN_5WIRE = 5, +}; + /** * struct at91_adc_trigger - description of triggers * @name: name of the trigger advertised to the user @@ -28,6 +34,7 @@ struct at91_adc_trigger { * @trigger_number: Number of triggers available in the ADC * @use_external_triggers: does the board has external triggers availables * @vref: Reference voltage for the ADC in millivolts + * @touchscreen_type: If a touchscreen is connected, its type (4 or 5 wires) */ struct at91_adc_data { unsigned long channels_used; @@ -36,6 +43,7 @@ struct at91_adc_data { u8 trigger_number; bool use_external_triggers; u16 vref; + enum atmel_adc_ts_type touchscreen_type; }; extern void __init at91_add_device_adc(struct at91_adc_data *data); -- cgit v1.2.3 From 03a3f53b965aaf1eb4f9423c1a55b41b3b4895b2 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 15 Apr 2014 12:28:09 +0200 Subject: ARM: at91: remove atmel_tsadcc platform_data Signed-off-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/board.h | 3 --- include/linux/platform_data/atmel.h | 7 ------- 2 files changed, 10 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-at91/board.h b/arch/arm/mach-at91/board.h index 6c08b341167d..4e773b55bc2d 100644 --- a/arch/arm/mach-at91/board.h +++ b/arch/arm/mach-at91/board.h @@ -118,9 +118,6 @@ struct isi_platform_data; extern void __init at91_add_device_isi(struct isi_platform_data *data, bool use_pck_as_mck); - /* Touchscreen Controller */ -extern void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data); - /* CAN */ extern void __init at91_add_device_can(struct at91_can_data *data); diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index e26b0c14edea..1466443797d7 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -87,13 +87,6 @@ struct atmel_uart_data { int rts_gpio; /* optional RTS GPIO */ }; - /* Touchscreen Controller */ -struct at91_tsadcc_data { - unsigned int adc_clock; - u8 pendet_debounce; - u8 ts_sample_hold_time; -}; - /* CAN */ struct at91_can_data { void (*transceiver_switch)(int on); -- cgit v1.2.3 From 69902c718c0b476e94ed7fccd3cf29ca39fe433a Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Thu, 1 May 2014 10:56:44 +0530 Subject: kprobes: Ensure blacklist data is aligned ARC Linux (not supporting native unaligned access) was failing to boot because __start_kprobe_blacklist was not aligned. This was because per generated vmlinux.lds it was emitted right next to .rodata with strings etc hence could be randomly unaligned. Fix that by ensuring a word alignment. While 4 would suffice for 32bit arches and problem at hand, it is probably better to put 8. | Path: (null) CPU: 0 PID: 1 Comm: swapper Not tainted | 3.15.0-rc3-next-20140430 #2 | task: 8f044000 ti: 8f01e000 task.ti: 8f01e000 | | [ECR ]: 0x00230400 => Misaligned r/w from 0x800fb0d3 | [EFA ]: 0x800fb0d3 | [BLINK ]: do_one_initcall+0x86/0x1bc | [ERET ]: init_kprobes+0x52/0x120 Signed-off-by: Vineet Gupta Cc: Cc: Cc: Cc: Cc: Cc: Cc: Cc: Cc: Cc: Cc: Cc: Cc: Cc: anton Kolesov Link: http://lkml.kernel.org/r/5361DB14.7010406@synopsys.com Signed-off-by: Ingo Molnar --- include/asm-generic/vmlinux.lds.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 40ceb3ceba79..8e0204a68c74 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -110,7 +110,8 @@ #endif #ifdef CONFIG_KPROBES -#define KPROBE_BLACKLIST() VMLINUX_SYMBOL(__start_kprobe_blacklist) = .; \ +#define KPROBE_BLACKLIST() . = ALIGN(8); \ + VMLINUX_SYMBOL(__start_kprobe_blacklist) = .; \ *(_kprobe_blacklist) \ VMLINUX_SYMBOL(__stop_kprobe_blacklist) = .; #else -- cgit v1.2.3 From d28071d102f232d92e52af06d242d041074b54b6 Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Sun, 4 May 2014 20:55:39 -0400 Subject: tunnel: fix RFC number in comment for INET_ECN_decapsulate() The quoted text and figure are from RFC 6040 ("Tunnelling of Explicit Congestion Notification"). Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/inet_ecn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h index 3bd22795c3e2..84b20835b736 100644 --- a/include/net/inet_ecn.h +++ b/include/net/inet_ecn.h @@ -150,7 +150,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb) } /* - * RFC 6080 4.2 + * RFC 6040 4.2 * To decapsulate the inner header at the tunnel egress, a compliant * tunnel egress MUST set the outgoing ECN field to the codepoint at the * intersection of the appropriate arriving inner header (row) and outer -- cgit v1.2.3 From c1e756bfcbcac838a86a23f3e4501b556a961e3c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 5 May 2014 15:00:44 +0200 Subject: Revert "net: core: introduce netif_skb_dev_features" This reverts commit d206940319c41df4299db75ed56142177bb2e5f6, there are no more callers. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/netdevice.h | 7 +------ net/core/dev.c | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7ed3a3aa6604..20e99efb1ca6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3180,12 +3180,7 @@ void netdev_change_features(struct net_device *dev); void netif_stacked_transfer_operstate(const struct net_device *rootdev, struct net_device *dev); -netdev_features_t netif_skb_dev_features(struct sk_buff *skb, - const struct net_device *dev); -static inline netdev_features_t netif_skb_features(struct sk_buff *skb) -{ - return netif_skb_dev_features(skb, skb->dev); -} +netdev_features_t netif_skb_features(struct sk_buff *skb); static inline bool net_gso_ok(netdev_features_t features, int gso_type) { diff --git a/net/core/dev.c b/net/core/dev.c index d2c8a06b3a98..c619b8641337 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2418,7 +2418,7 @@ EXPORT_SYMBOL(netdev_rx_csum_fault); * 2. No high memory really exists on this machine. */ -static int illegal_highdma(const struct net_device *dev, struct sk_buff *skb) +static int illegal_highdma(struct net_device *dev, struct sk_buff *skb) { #ifdef CONFIG_HIGHMEM int i; @@ -2493,38 +2493,36 @@ static int dev_gso_segment(struct sk_buff *skb, netdev_features_t features) } static netdev_features_t harmonize_features(struct sk_buff *skb, - const struct net_device *dev, - netdev_features_t features) + netdev_features_t features) { int tmp; if (skb->ip_summed != CHECKSUM_NONE && !can_checksum_protocol(features, skb_network_protocol(skb, &tmp))) { features &= ~NETIF_F_ALL_CSUM; - } else if (illegal_highdma(dev, skb)) { + } else if (illegal_highdma(skb->dev, skb)) { features &= ~NETIF_F_SG; } return features; } -netdev_features_t netif_skb_dev_features(struct sk_buff *skb, - const struct net_device *dev) +netdev_features_t netif_skb_features(struct sk_buff *skb) { __be16 protocol = skb->protocol; - netdev_features_t features = dev->features; + netdev_features_t features = skb->dev->features; - if (skb_shinfo(skb)->gso_segs > dev->gso_max_segs) + if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs) features &= ~NETIF_F_GSO_MASK; if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) { struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; protocol = veh->h_vlan_encapsulated_proto; } else if (!vlan_tx_tag_present(skb)) { - return harmonize_features(skb, dev, features); + return harmonize_features(skb, features); } - features &= (dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX | + features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX); if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) @@ -2532,9 +2530,9 @@ netdev_features_t netif_skb_dev_features(struct sk_buff *skb, NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; - return harmonize_features(skb, dev, features); + return harmonize_features(skb, features); } -EXPORT_SYMBOL(netif_skb_dev_features); +EXPORT_SYMBOL(netif_skb_features); int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq) -- cgit v1.2.3 From 698365fa1874aa7635d51667a34a2842228e9837 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Mon, 5 May 2014 15:55:55 -0700 Subject: net: clean up snmp stats code commit 8f0ea0fe3a036a47767f9c80e (snmp: reduce percpu needs by 50%) reduced snmp array size to 1, so technically it doesn't have to be an array any more. What's more, after the following commit: commit 933393f58fef9963eac61db8093689544e29a600 Date: Thu Dec 22 11:58:51 2011 -0600 percpu: Remove irqsafe_cpu_xxx variants We simply say that regular this_cpu use must be safe regardless of preemption and interrupt state. That has no material change for x86 and s390 implementations of this_cpu operations. However, arches that do not provide their own implementation for this_cpu operations will now get code generated that disables interrupts instead of preemption. probably no arch wants to have SNMP_ARRAY_SZ == 2. At least after almost 3 years, no one complains. So, just convert the array to a single pointer and remove snmp_mib_init() and snmp_mib_free() as well. Cc: Christoph Lameter Cc: Eric Dumazet Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/ip.h | 18 ++-------- include/net/snmp.h | 32 ++++++++--------- net/dccp/proto.c | 9 ++--- net/ipv4/af_inet.c | 93 ++++++++++++++++-------------------------------- net/ipv4/proc.c | 24 ++++++------- net/ipv6/addrconf.c | 17 ++++----- net/ipv6/addrconf_core.c | 2 +- net/ipv6/af_inet6.c | 42 +++++++++------------- net/ipv6/proc.c | 6 ++-- net/sctp/protocol.c | 9 ++--- net/xfrm/xfrm_policy.c | 10 +++--- net/xfrm/xfrm_proc.c | 3 +- 12 files changed, 103 insertions(+), 162 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 1988cefdbb70..16146b667ddb 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -196,27 +196,15 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, #define NET_ADD_STATS_BH(net, field, adnd) SNMP_ADD_STATS_BH((net)->mib.net_statistics, field, adnd) #define NET_ADD_STATS_USER(net, field, adnd) SNMP_ADD_STATS_USER((net)->mib.net_statistics, field, adnd) -unsigned long snmp_fold_field(void __percpu *mib[], int offt); +unsigned long snmp_fold_field(void __percpu *mib, int offt); #if BITS_PER_LONG==32 -u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t sync_off); +u64 snmp_fold_field64(void __percpu *mib, int offt, size_t sync_off); #else -static inline u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_off) +static inline u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_off) { return snmp_fold_field(mib, offt); } #endif -int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align); - -static inline void snmp_mib_free(void __percpu *ptr[SNMP_ARRAY_SZ]) -{ - int i; - - BUG_ON(ptr == NULL); - for (i = 0; i < SNMP_ARRAY_SZ; i++) { - free_percpu(ptr[i]); - ptr[i] = NULL; - } -} void inet_get_local_port_range(struct net *net, int *low, int *high); diff --git a/include/net/snmp.h b/include/net/snmp.h index 71596261fa99..f1f27fdbb0d5 100644 --- a/include/net/snmp.h +++ b/include/net/snmp.h @@ -116,51 +116,49 @@ struct linux_xfrm_mib { unsigned long mibs[LINUX_MIB_XFRMMAX]; }; -#define SNMP_ARRAY_SZ 1 - #define DEFINE_SNMP_STAT(type, name) \ - __typeof__(type) __percpu *name[SNMP_ARRAY_SZ] + __typeof__(type) __percpu *name #define DEFINE_SNMP_STAT_ATOMIC(type, name) \ __typeof__(type) *name #define DECLARE_SNMP_STAT(type, name) \ - extern __typeof__(type) __percpu *name[SNMP_ARRAY_SZ] + extern __typeof__(type) __percpu *name #define SNMP_INC_STATS_BH(mib, field) \ - __this_cpu_inc(mib[0]->mibs[field]) + __this_cpu_inc(mib->mibs[field]) #define SNMP_INC_STATS_USER(mib, field) \ - this_cpu_inc(mib[0]->mibs[field]) + this_cpu_inc(mib->mibs[field]) #define SNMP_INC_STATS_ATOMIC_LONG(mib, field) \ atomic_long_inc(&mib->mibs[field]) #define SNMP_INC_STATS(mib, field) \ - this_cpu_inc(mib[0]->mibs[field]) + this_cpu_inc(mib->mibs[field]) #define SNMP_DEC_STATS(mib, field) \ - this_cpu_dec(mib[0]->mibs[field]) + this_cpu_dec(mib->mibs[field]) #define SNMP_ADD_STATS_BH(mib, field, addend) \ - __this_cpu_add(mib[0]->mibs[field], addend) + __this_cpu_add(mib->mibs[field], addend) #define SNMP_ADD_STATS_USER(mib, field, addend) \ - this_cpu_add(mib[0]->mibs[field], addend) + this_cpu_add(mib->mibs[field], addend) #define SNMP_ADD_STATS(mib, field, addend) \ - this_cpu_add(mib[0]->mibs[field], addend) + this_cpu_add(mib->mibs[field], addend) /* - * Use "__typeof__(*mib[0]) *ptr" instead of "__typeof__(mib[0]) ptr" + * Use "__typeof__(*mib) *ptr" instead of "__typeof__(mib) ptr" * to make @ptr a non-percpu pointer. */ #define SNMP_UPD_PO_STATS(mib, basefield, addend) \ do { \ - __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs; \ + __typeof__(*mib->mibs) *ptr = mib->mibs; \ this_cpu_inc(ptr[basefield##PKTS]); \ this_cpu_add(ptr[basefield##OCTETS], addend); \ } while (0) #define SNMP_UPD_PO_STATS_BH(mib, basefield, addend) \ do { \ - __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs; \ + __typeof__(*mib->mibs) *ptr = mib->mibs; \ __this_cpu_inc(ptr[basefield##PKTS]); \ __this_cpu_add(ptr[basefield##OCTETS], addend); \ } while (0) @@ -170,7 +168,7 @@ struct linux_xfrm_mib { #define SNMP_ADD_STATS64_BH(mib, field, addend) \ do { \ - __typeof__(*mib[0]) *ptr = __this_cpu_ptr((mib)[0]); \ + __typeof__(*mib) *ptr = __this_cpu_ptr(mib); \ u64_stats_update_begin(&ptr->syncp); \ ptr->mibs[field] += addend; \ u64_stats_update_end(&ptr->syncp); \ @@ -191,8 +189,8 @@ struct linux_xfrm_mib { #define SNMP_INC_STATS64(mib, field) SNMP_ADD_STATS64(mib, field, 1) #define SNMP_UPD_PO_STATS64_BH(mib, basefield, addend) \ do { \ - __typeof__(*mib[0]) *ptr; \ - ptr = __this_cpu_ptr((mib)[0]); \ + __typeof__(*mib) *ptr; \ + ptr = __this_cpu_ptr(mib); \ u64_stats_update_begin(&ptr->syncp); \ ptr->mibs[basefield##PKTS]++; \ ptr->mibs[basefield##OCTETS] += addend; \ diff --git a/net/dccp/proto.c b/net/dccp/proto.c index eb892b4f4814..de2c1e719305 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -1084,14 +1084,15 @@ EXPORT_SYMBOL_GPL(dccp_shutdown); static inline int dccp_mib_init(void) { - return snmp_mib_init((void __percpu **)dccp_statistics, - sizeof(struct dccp_mib), - __alignof__(struct dccp_mib)); + dccp_statistics = alloc_percpu(struct dccp_mib); + if (!dccp_statistics) + return -ENOMEM; + return 0; } static inline void dccp_mib_exit(void) { - snmp_mib_free((void __percpu **)dccp_statistics); + free_percpu(dccp_statistics); } static int thash_entries; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 8c54870db792..6765d91b8e75 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1476,22 +1476,20 @@ int inet_ctl_sock_create(struct sock **sk, unsigned short family, } EXPORT_SYMBOL_GPL(inet_ctl_sock_create); -unsigned long snmp_fold_field(void __percpu *mib[], int offt) +unsigned long snmp_fold_field(void __percpu *mib, int offt) { unsigned long res = 0; - int i, j; + int i; - for_each_possible_cpu(i) { - for (j = 0; j < SNMP_ARRAY_SZ; j++) - res += *(((unsigned long *) per_cpu_ptr(mib[j], i)) + offt); - } + for_each_possible_cpu(i) + res += *(((unsigned long *) per_cpu_ptr(mib, i)) + offt); return res; } EXPORT_SYMBOL_GPL(snmp_fold_field); #if BITS_PER_LONG==32 -u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset) +u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_offset) { u64 res = 0; int cpu; @@ -1502,7 +1500,7 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset) u64 v; unsigned int start; - bhptr = per_cpu_ptr(mib[0], cpu); + bhptr = per_cpu_ptr(mib, cpu); syncp = (struct u64_stats_sync *)(bhptr + syncp_offset); do { start = u64_stats_fetch_begin_irq(syncp); @@ -1516,25 +1514,6 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset) EXPORT_SYMBOL_GPL(snmp_fold_field64); #endif -int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align) -{ - BUG_ON(ptr == NULL); - ptr[0] = __alloc_percpu(mibsize, align); - if (!ptr[0]) - return -ENOMEM; - -#if SNMP_ARRAY_SZ == 2 - ptr[1] = __alloc_percpu(mibsize, align); - if (!ptr[1]) { - free_percpu(ptr[0]); - ptr[0] = NULL; - return -ENOMEM; - } -#endif - return 0; -} -EXPORT_SYMBOL_GPL(snmp_mib_init); - #ifdef CONFIG_IP_MULTICAST static const struct net_protocol igmp_protocol = { .handler = igmp_rcv, @@ -1570,40 +1549,30 @@ static __net_init int ipv4_mib_init_net(struct net *net) { int i; - if (snmp_mib_init((void __percpu **)net->mib.tcp_statistics, - sizeof(struct tcp_mib), - __alignof__(struct tcp_mib)) < 0) + net->mib.tcp_statistics = alloc_percpu(struct tcp_mib); + if (!net->mib.tcp_statistics) goto err_tcp_mib; - if (snmp_mib_init((void __percpu **)net->mib.ip_statistics, - sizeof(struct ipstats_mib), - __alignof__(struct ipstats_mib)) < 0) + net->mib.ip_statistics = alloc_percpu(struct ipstats_mib); + if (!net->mib.ip_statistics) goto err_ip_mib; for_each_possible_cpu(i) { struct ipstats_mib *af_inet_stats; - af_inet_stats = per_cpu_ptr(net->mib.ip_statistics[0], i); + af_inet_stats = per_cpu_ptr(net->mib.ip_statistics, i); u64_stats_init(&af_inet_stats->syncp); -#if SNMP_ARRAY_SZ == 2 - af_inet_stats = per_cpu_ptr(net->mib.ip_statistics[1], i); - u64_stats_init(&af_inet_stats->syncp); -#endif } - if (snmp_mib_init((void __percpu **)net->mib.net_statistics, - sizeof(struct linux_mib), - __alignof__(struct linux_mib)) < 0) + net->mib.net_statistics = alloc_percpu(struct linux_mib); + if (!net->mib.net_statistics) goto err_net_mib; - if (snmp_mib_init((void __percpu **)net->mib.udp_statistics, - sizeof(struct udp_mib), - __alignof__(struct udp_mib)) < 0) + net->mib.udp_statistics = alloc_percpu(struct udp_mib); + if (!net->mib.udp_statistics) goto err_udp_mib; - if (snmp_mib_init((void __percpu **)net->mib.udplite_statistics, - sizeof(struct udp_mib), - __alignof__(struct udp_mib)) < 0) + net->mib.udplite_statistics = alloc_percpu(struct udp_mib); + if (!net->mib.udplite_statistics) goto err_udplite_mib; - if (snmp_mib_init((void __percpu **)net->mib.icmp_statistics, - sizeof(struct icmp_mib), - __alignof__(struct icmp_mib)) < 0) + net->mib.icmp_statistics = alloc_percpu(struct icmp_mib); + if (!net->mib.icmp_statistics) goto err_icmp_mib; net->mib.icmpmsg_statistics = kzalloc(sizeof(struct icmpmsg_mib), GFP_KERNEL); @@ -1614,17 +1583,17 @@ static __net_init int ipv4_mib_init_net(struct net *net) return 0; err_icmpmsg_mib: - snmp_mib_free((void __percpu **)net->mib.icmp_statistics); + free_percpu(net->mib.icmp_statistics); err_icmp_mib: - snmp_mib_free((void __percpu **)net->mib.udplite_statistics); + free_percpu(net->mib.udplite_statistics); err_udplite_mib: - snmp_mib_free((void __percpu **)net->mib.udp_statistics); + free_percpu(net->mib.udp_statistics); err_udp_mib: - snmp_mib_free((void __percpu **)net->mib.net_statistics); + free_percpu(net->mib.net_statistics); err_net_mib: - snmp_mib_free((void __percpu **)net->mib.ip_statistics); + free_percpu(net->mib.ip_statistics); err_ip_mib: - snmp_mib_free((void __percpu **)net->mib.tcp_statistics); + free_percpu(net->mib.tcp_statistics); err_tcp_mib: return -ENOMEM; } @@ -1632,12 +1601,12 @@ err_tcp_mib: static __net_exit void ipv4_mib_exit_net(struct net *net) { kfree(net->mib.icmpmsg_statistics); - snmp_mib_free((void __percpu **)net->mib.icmp_statistics); - snmp_mib_free((void __percpu **)net->mib.udplite_statistics); - snmp_mib_free((void __percpu **)net->mib.udp_statistics); - snmp_mib_free((void __percpu **)net->mib.net_statistics); - snmp_mib_free((void __percpu **)net->mib.ip_statistics); - snmp_mib_free((void __percpu **)net->mib.tcp_statistics); + free_percpu(net->mib.icmp_statistics); + free_percpu(net->mib.udplite_statistics); + free_percpu(net->mib.udp_statistics); + free_percpu(net->mib.net_statistics); + free_percpu(net->mib.ip_statistics); + free_percpu(net->mib.tcp_statistics); } static __net_initdata struct pernet_operations ipv4_mib_ops = { diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index ad737fad6d8b..ae0af9386f7c 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -345,15 +345,15 @@ static void icmp_put(struct seq_file *seq) for (i = 0; icmpmibmap[i].name != NULL; i++) seq_printf(seq, " Out%s", icmpmibmap[i].name); seq_printf(seq, "\nIcmp: %lu %lu %lu", - snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_INMSGS), - snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_INERRORS), - snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_CSUMERRORS)); + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_INMSGS), + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_INERRORS), + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_CSUMERRORS)); for (i = 0; icmpmibmap[i].name != NULL; i++) seq_printf(seq, " %lu", atomic_long_read(ptr + icmpmibmap[i].index)); seq_printf(seq, " %lu %lu", - snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_OUTMSGS), - snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_OUTERRORS)); + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTMSGS), + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTERRORS)); for (i = 0; icmpmibmap[i].name != NULL; i++) seq_printf(seq, " %lu", atomic_long_read(ptr + (icmpmibmap[i].index | 0x100))); @@ -379,7 +379,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v) BUILD_BUG_ON(offsetof(struct ipstats_mib, mibs) != 0); for (i = 0; snmp4_ipstats_list[i].name != NULL; i++) seq_printf(seq, " %llu", - snmp_fold_field64((void __percpu **)net->mib.ip_statistics, + snmp_fold_field64(net->mib.ip_statistics, snmp4_ipstats_list[i].entry, offsetof(struct ipstats_mib, syncp))); @@ -395,11 +395,11 @@ static int snmp_seq_show(struct seq_file *seq, void *v) /* MaxConn field is signed, RFC 2012 */ if (snmp4_tcp_list[i].entry == TCP_MIB_MAXCONN) seq_printf(seq, " %ld", - snmp_fold_field((void __percpu **)net->mib.tcp_statistics, + snmp_fold_field(net->mib.tcp_statistics, snmp4_tcp_list[i].entry)); else seq_printf(seq, " %lu", - snmp_fold_field((void __percpu **)net->mib.tcp_statistics, + snmp_fold_field(net->mib.tcp_statistics, snmp4_tcp_list[i].entry)); } @@ -410,7 +410,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v) seq_puts(seq, "\nUdp:"); for (i = 0; snmp4_udp_list[i].name != NULL; i++) seq_printf(seq, " %lu", - snmp_fold_field((void __percpu **)net->mib.udp_statistics, + snmp_fold_field(net->mib.udp_statistics, snmp4_udp_list[i].entry)); /* the UDP and UDP-Lite MIBs are the same */ @@ -421,7 +421,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v) seq_puts(seq, "\nUdpLite:"); for (i = 0; snmp4_udp_list[i].name != NULL; i++) seq_printf(seq, " %lu", - snmp_fold_field((void __percpu **)net->mib.udplite_statistics, + snmp_fold_field(net->mib.udplite_statistics, snmp4_udp_list[i].entry)); seq_putc(seq, '\n'); @@ -458,7 +458,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v) seq_puts(seq, "\nTcpExt:"); for (i = 0; snmp4_net_list[i].name != NULL; i++) seq_printf(seq, " %lu", - snmp_fold_field((void __percpu **)net->mib.net_statistics, + snmp_fold_field(net->mib.net_statistics, snmp4_net_list[i].entry)); seq_puts(seq, "\nIpExt:"); @@ -468,7 +468,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v) seq_puts(seq, "\nIpExt:"); for (i = 0; snmp4_ipextstats_list[i].name != NULL; i++) seq_printf(seq, " %llu", - snmp_fold_field64((void __percpu **)net->mib.ip_statistics, + snmp_fold_field64(net->mib.ip_statistics, snmp4_ipextstats_list[i].entry, offsetof(struct ipstats_mib, syncp))); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 1ac13c0300b7..5667b3003af9 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -275,19 +275,14 @@ static int snmp6_alloc_dev(struct inet6_dev *idev) { int i; - if (snmp_mib_init((void __percpu **)idev->stats.ipv6, - sizeof(struct ipstats_mib), - __alignof__(struct ipstats_mib)) < 0) + idev->stats.ipv6 = alloc_percpu(struct ipstats_mib); + if (!idev->stats.ipv6) goto err_ip; for_each_possible_cpu(i) { struct ipstats_mib *addrconf_stats; - addrconf_stats = per_cpu_ptr(idev->stats.ipv6[0], i); + addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i); u64_stats_init(&addrconf_stats->syncp); -#if SNMP_ARRAY_SZ == 2 - addrconf_stats = per_cpu_ptr(idev->stats.ipv6[1], i); - u64_stats_init(&addrconf_stats->syncp); -#endif } @@ -305,7 +300,7 @@ static int snmp6_alloc_dev(struct inet6_dev *idev) err_icmpmsg: kfree(idev->stats.icmpv6dev); err_icmp: - snmp_mib_free((void __percpu **)idev->stats.ipv6); + free_percpu(idev->stats.ipv6); err_ip: return -ENOMEM; } @@ -4363,7 +4358,7 @@ static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib, memset(&stats[items], 0, pad); } -static inline void __snmp6_fill_stats64(u64 *stats, void __percpu **mib, +static inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib, int items, int bytes, size_t syncpoff) { int i; @@ -4383,7 +4378,7 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, { switch (attrtype) { case IFLA_INET6_STATS: - __snmp6_fill_stats64(stats, (void __percpu **)idev->stats.ipv6, + __snmp6_fill_stats64(stats, idev->stats.ipv6, IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp)); break; case IFLA_INET6_ICMP6STATS: diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c index 4c11cbcf8308..e6960457f625 100644 --- a/net/ipv6/addrconf_core.c +++ b/net/ipv6/addrconf_core.c @@ -123,7 +123,7 @@ static void snmp6_free_dev(struct inet6_dev *idev) { kfree(idev->stats.icmpv6msgdev); kfree(idev->stats.icmpv6dev); - snmp_mib_free((void __percpu **)idev->stats.ipv6); + free_percpu(idev->stats.ipv6); } /* Nobody refers to this device, we may destroy it. */ diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index d935889f1008..dc47cc757b80 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -715,33 +715,25 @@ static int __net_init ipv6_init_mibs(struct net *net) { int i; - if (snmp_mib_init((void __percpu **)net->mib.udp_stats_in6, - sizeof(struct udp_mib), - __alignof__(struct udp_mib)) < 0) + net->mib.udp_stats_in6 = alloc_percpu(struct udp_mib); + if (!net->mib.udp_stats_in6) return -ENOMEM; - if (snmp_mib_init((void __percpu **)net->mib.udplite_stats_in6, - sizeof(struct udp_mib), - __alignof__(struct udp_mib)) < 0) + net->mib.udplite_stats_in6 = alloc_percpu(struct udp_mib); + if (!net->mib.udplite_stats_in6) goto err_udplite_mib; - if (snmp_mib_init((void __percpu **)net->mib.ipv6_statistics, - sizeof(struct ipstats_mib), - __alignof__(struct ipstats_mib)) < 0) + net->mib.ipv6_statistics = alloc_percpu(struct ipstats_mib); + if (!net->mib.ipv6_statistics) goto err_ip_mib; for_each_possible_cpu(i) { struct ipstats_mib *af_inet6_stats; - af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics[0], i); + af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics, i); u64_stats_init(&af_inet6_stats->syncp); -#if SNMP_ARRAY_SZ == 2 - af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics[1], i); - u64_stats_init(&af_inet6_stats->syncp); -#endif } - if (snmp_mib_init((void __percpu **)net->mib.icmpv6_statistics, - sizeof(struct icmpv6_mib), - __alignof__(struct icmpv6_mib)) < 0) + net->mib.icmpv6_statistics = alloc_percpu(struct icmpv6_mib); + if (!net->mib.icmpv6_statistics) goto err_icmp_mib; net->mib.icmpv6msg_statistics = kzalloc(sizeof(struct icmpv6msg_mib), GFP_KERNEL); @@ -750,22 +742,22 @@ static int __net_init ipv6_init_mibs(struct net *net) return 0; err_icmpmsg_mib: - snmp_mib_free((void __percpu **)net->mib.icmpv6_statistics); + free_percpu(net->mib.icmpv6_statistics); err_icmp_mib: - snmp_mib_free((void __percpu **)net->mib.ipv6_statistics); + free_percpu(net->mib.ipv6_statistics); err_ip_mib: - snmp_mib_free((void __percpu **)net->mib.udplite_stats_in6); + free_percpu(net->mib.udplite_stats_in6); err_udplite_mib: - snmp_mib_free((void __percpu **)net->mib.udp_stats_in6); + free_percpu(net->mib.udp_stats_in6); return -ENOMEM; } static void ipv6_cleanup_mibs(struct net *net) { - snmp_mib_free((void __percpu **)net->mib.udp_stats_in6); - snmp_mib_free((void __percpu **)net->mib.udplite_stats_in6); - snmp_mib_free((void __percpu **)net->mib.ipv6_statistics); - snmp_mib_free((void __percpu **)net->mib.icmpv6_statistics); + free_percpu(net->mib.udp_stats_in6); + free_percpu(net->mib.udplite_stats_in6); + free_percpu(net->mib.ipv6_statistics); + free_percpu(net->mib.icmpv6_statistics); kfree(net->mib.icmpv6msg_statistics); } diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 091d066a57b3..69cb47625df7 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -201,7 +201,7 @@ static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib, } } -static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu **mib, +static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu *mib, const struct snmp_mib *itemlist, size_t syncpoff) { int i; @@ -215,7 +215,7 @@ static int snmp6_seq_show(struct seq_file *seq, void *v) { struct net *net = (struct net *)seq->private; - snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics, + snmp6_seq_show_item64(seq, net->mib.ipv6_statistics, snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp)); snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics, NULL, snmp6_icmp6_list); @@ -245,7 +245,7 @@ static int snmp6_dev_seq_show(struct seq_file *seq, void *v) struct inet6_dev *idev = (struct inet6_dev *)seq->private; seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex); - snmp6_seq_show_item64(seq, (void __percpu **)idev->stats.ipv6, + snmp6_seq_show_item64(seq, idev->stats.ipv6, snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp)); snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs, snmp6_icmp6_list); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index c09757fbf803..074b60e2faab 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1100,14 +1100,15 @@ int sctp_register_pf(struct sctp_pf *pf, sa_family_t family) static inline int init_sctp_mibs(struct net *net) { - return snmp_mib_init((void __percpu **)net->sctp.sctp_statistics, - sizeof(struct sctp_mib), - __alignof__(struct sctp_mib)); + net->sctp.sctp_statistics = alloc_percpu(struct sctp_mib); + if (!net->sctp.sctp_statistics) + return -ENOMEM; + return 0; } static inline void cleanup_sctp_mibs(struct net *net) { - snmp_mib_free((void __percpu **)net->sctp.sctp_statistics); + free_percpu(net->sctp.sctp_statistics); } static void sctp_v4_pf_init(void) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c08fbd11ceff..e63f242ae03e 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2783,21 +2783,19 @@ static struct notifier_block xfrm_dev_notifier = { static int __net_init xfrm_statistics_init(struct net *net) { int rv; - - if (snmp_mib_init((void __percpu **)net->mib.xfrm_statistics, - sizeof(struct linux_xfrm_mib), - __alignof__(struct linux_xfrm_mib)) < 0) + net->mib.xfrm_statistics = alloc_percpu(struct linux_xfrm_mib); + if (!net->mib.xfrm_statistics) return -ENOMEM; rv = xfrm_proc_init(net); if (rv < 0) - snmp_mib_free((void __percpu **)net->mib.xfrm_statistics); + free_percpu(net->mib.xfrm_statistics); return rv; } static void xfrm_statistics_fini(struct net *net) { xfrm_proc_fini(net); - snmp_mib_free((void __percpu **)net->mib.xfrm_statistics); + free_percpu(net->mib.xfrm_statistics); } #else static int __net_init xfrm_statistics_init(struct net *net) diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index fc5abd0b456f..9c4fbd8935f4 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -54,8 +54,7 @@ static int xfrm_statistics_seq_show(struct seq_file *seq, void *v) int i; for (i = 0; xfrm_mib_list[i].name; i++) seq_printf(seq, "%-24s\t%lu\n", xfrm_mib_list[i].name, - snmp_fold_field((void __percpu **) - net->mib.xfrm_statistics, + snmp_fold_field(net->mib.xfrm_statistics, xfrm_mib_list[i].entry)); return 0; } -- cgit v1.2.3 From 552a515707a5caf9fa9f3620d68fc28146c6b1b9 Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Wed, 7 May 2014 09:28:31 +0200 Subject: ath9k: Allow platform override without EEPROM override Add a new platform data flag "use_eeprom" that indicates that the eeprom found on the card itself should be used instead of the one present in the platform data. This allows to override the MAC address of a PCI card while preserving the eeprom data from the card itself. The default behavior is preserved. Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 2 +- include/linux/ath9k_platform.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index a6e273a2c44b..bcc7cfb1866d 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -508,7 +508,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->tx99_power = MAX_RATE_POWER + 1; init_waitqueue_head(&sc->tx_wait); - if (!pdata) { + if (!pdata || pdata->use_eeprom) { ah->ah_flags |= AH_USE_EEPROM; sc->sc_ah->led_pin = -1; } else { diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h index 8598f8eacb20..a495a959e8a7 100644 --- a/include/linux/ath9k_platform.h +++ b/include/linux/ath9k_platform.h @@ -36,6 +36,8 @@ struct ath9k_platform_data { int (*get_mac_revision)(void); int (*external_reset)(void); + + bool use_eeprom; }; #endif /* _LINUX_ATH9K_PLATFORM_H */ -- cgit v1.2.3 From 23a456f05353035d1a2b3f1b9a92707acdc036e0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 6 May 2014 18:52:16 +0200 Subject: net: mdio: of_mdiobus_register(): fall back to mdiobus_register() for !CONFIG_OF If CONFIG_OF is not set, make of_mdiobus_register() call mdiobus_register() instead of returning -ENOSYS. This way, we can just call of_mdiobus_register() from all DT-enabled drivers to handle the compat cases. Signed-off-by: Daniel Mack Suggested-by: Florian Fainelli Acked-by: Florian Fainelli Acked-by: Mugunthan V N Signed-off-by: David S. Miller --- include/linux/of_mdio.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index 6fe8464ed767..881a7c3571f4 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -31,7 +31,12 @@ extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np); #else /* CONFIG_OF */ static inline int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) { - return -ENOSYS; + /* + * Fall back to the non-DT function to register a bus. + * This way, we don't have to keep compat bits around in drivers. + */ + + return mdiobus_register(mdio); } static inline struct phy_device *of_phy_find_device(struct device_node *phy_np) -- cgit v1.2.3 From db6d8cc00773d8ef5a8b421b42a5ded235307b10 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Wed, 26 Mar 2014 02:27:02 -0700 Subject: dell-led: add mic mute led interface This patch provides similar led functional of 420f973 thinkpad-acpi: Add mute and mic-mute LED functionality Signed-off-by: Alex Hung Signed-off-by: Bryan Wu --- drivers/leds/dell-led.c | 171 +++++++++++++++++++++++++++++++++++++++++++++-- include/linux/dell-led.h | 10 +++ 2 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 include/linux/dell-led.h (limited to 'include') diff --git a/drivers/leds/dell-led.c b/drivers/leds/dell-led.c index e5c57389efd6..c36acaf566a6 100644 --- a/drivers/leds/dell-led.c +++ b/drivers/leds/dell-led.c @@ -15,12 +15,15 @@ #include #include #include +#include +#include MODULE_AUTHOR("Louis Davis/Jim Dailey"); MODULE_DESCRIPTION("Dell LED Control Driver"); MODULE_LICENSE("GPL"); #define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396" +#define DELL_APP_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); /* Error Result Codes: */ @@ -39,6 +42,149 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); #define CMD_LED_OFF 17 #define CMD_LED_BLINK 18 +struct app_wmi_args { + u16 class; + u16 selector; + u32 arg1; + u32 arg2; + u32 arg3; + u32 arg4; + u32 res1; + u32 res2; + u32 res3; + u32 res4; + char dummy[92]; +}; + +#define GLOBAL_MIC_MUTE_ENABLE 0x364 +#define GLOBAL_MIC_MUTE_DISABLE 0x365 + +struct dell_bios_data_token { + u16 tokenid; + u16 location; + u16 value; +}; + +struct __attribute__ ((__packed__)) dell_bios_calling_interface { + struct dmi_header header; + u16 cmd_io_addr; + u8 cmd_io_code; + u32 supported_cmds; + struct dell_bios_data_token damap[]; +}; + +static struct dell_bios_data_token dell_mic_tokens[2]; + +static int dell_wmi_perform_query(struct app_wmi_args *args) +{ + struct app_wmi_args *bios_return; + union acpi_object *obj; + struct acpi_buffer input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + u32 rc = -EINVAL; + + input.length = 128; + input.pointer = args; + + status = wmi_evaluate_method(DELL_APP_GUID, 0, 1, &input, &output); + if (!ACPI_SUCCESS(status)) + goto err_out0; + + obj = output.pointer; + if (!obj) + goto err_out0; + + if (obj->type != ACPI_TYPE_BUFFER) + goto err_out1; + + bios_return = (struct app_wmi_args *)obj->buffer.pointer; + rc = bios_return->res1; + if (rc) + goto err_out1; + + memcpy(args, bios_return, sizeof(struct app_wmi_args)); + rc = 0; + + err_out1: + kfree(obj); + err_out0: + return rc; +} + +static void __init find_micmute_tokens(const struct dmi_header *dm, void *dummy) +{ + struct dell_bios_calling_interface *calling_interface; + struct dell_bios_data_token *token; + int token_size = sizeof(struct dell_bios_data_token); + int i = 0; + + if (dm->type == 0xda && dm->length > 17) { + calling_interface = container_of(dm, + struct dell_bios_calling_interface, header); + + token = &calling_interface->damap[i]; + while (token->tokenid != 0xffff) { + if (token->tokenid == GLOBAL_MIC_MUTE_DISABLE) + memcpy(&dell_mic_tokens[0], token, token_size); + else if (token->tokenid == GLOBAL_MIC_MUTE_ENABLE) + memcpy(&dell_mic_tokens[1], token, token_size); + + i++; + token = &calling_interface->damap[i]; + } + } +} + +static int dell_micmute_led_set(int state) +{ + struct app_wmi_args args; + struct dell_bios_data_token *token; + + if (!wmi_has_guid(DELL_APP_GUID)) + return -ENODEV; + + if (state == 0 || state == 1) + token = &dell_mic_tokens[state]; + else + return -EINVAL; + + memset(&args, 0, sizeof(struct app_wmi_args)); + + args.class = 1; + args.arg1 = token->location; + args.arg2 = token->value; + + dell_wmi_perform_query(&args); + + return state; +} + +int dell_app_wmi_led_set(int whichled, int on) +{ + int state = 0; + + switch (whichled) { + case DELL_LED_MICMUTE: + state = dell_micmute_led_set(on); + break; + default: + pr_warn("led type %x is not supported\n", whichled); + break; + } + + return state; +} +EXPORT_SYMBOL_GPL(dell_app_wmi_led_set); + +static int __init dell_micmute_led_init(void) +{ + memset(dell_mic_tokens, 0, sizeof(struct dell_bios_data_token) * 2); + dmi_walk(find_micmute_tokens, NULL); + + return 0; +} + struct bios_args { unsigned char length; unsigned char result_code; @@ -181,21 +327,32 @@ static int __init dell_led_init(void) { int error = 0; - if (!wmi_has_guid(DELL_LED_BIOS_GUID)) + if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID)) return -ENODEV; - error = led_off(); - if (error != 0) - return -ENODEV; + if (wmi_has_guid(DELL_APP_GUID)) + error = dell_micmute_led_init(); - return led_classdev_register(NULL, &dell_led); + if (wmi_has_guid(DELL_LED_BIOS_GUID)) { + error = led_off(); + if (error != 0) + return -ENODEV; + + error = led_classdev_register(NULL, &dell_led); + } + + return error; } static void __exit dell_led_exit(void) { - led_classdev_unregister(&dell_led); + int error = 0; - led_off(); + if (wmi_has_guid(DELL_LED_BIOS_GUID)) { + error = led_off(); + if (error == 0) + led_classdev_unregister(&dell_led); + } } module_init(dell_led_init); diff --git a/include/linux/dell-led.h b/include/linux/dell-led.h new file mode 100644 index 000000000000..7009b8bec77b --- /dev/null +++ b/include/linux/dell-led.h @@ -0,0 +1,10 @@ +#ifndef __DELL_LED_H__ +#define __DELL_LED_H__ + +enum { + DELL_LED_MICMUTE, +}; + +int dell_app_wmi_led_set(int whichled, int on); + +#endif -- cgit v1.2.3 From 2ce112f1e788a695630e2c275f47d57a801673d3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 5 Apr 2014 20:16:34 -0700 Subject: leds: pca9685: Remove leds-pca9685 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver is replaced by pwm-pca9685 driver and there is no user uses this driver in current tree. So remove it. Signed-off-by: Axel Lin Acked-by: Steffen Trumtrar Acked-by: Maximilian Güntner Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 10 -- drivers/leds/Makefile | 1 - drivers/leds/leds-pca9685.c | 213 ----------------------------- include/linux/platform_data/leds-pca9685.h | 35 ----- 4 files changed, 259 deletions(-) delete mode 100644 drivers/leds/leds-pca9685.c delete mode 100644 include/linux/platform_data/leds-pca9685.h (limited to 'include') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 6de9dfbf61c1..6713dbb6bfda 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -300,16 +300,6 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 -config LEDS_PCA9685 - tristate "LED support for PCA9685 I2C chip" - depends on LEDS_CLASS - depends on I2C - help - This option enables support for LEDs connected to the PCA9685 - LED driver chip accessed via the I2C bus. - The PCA9685 offers 12-bit PWM (4095 levels of brightness) on - 16 individual channels. - config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3cd76dbd9be2..8979b0b2c85e 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -36,7 +36,6 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o -obj-$(CONFIG_LEDS_PCA9685) += leds-pca9685.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o diff --git a/drivers/leds/leds-pca9685.c b/drivers/leds/leds-pca9685.c deleted file mode 100644 index 6e1ef3a9d6ef..000000000000 --- a/drivers/leds/leds-pca9685.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2013 Maximilian Güntner - * - * This file is subject to the terms and conditions of version 2 of - * the GNU General Public License. See the file COPYING in the main - * directory of this archive for more details. - * - * Based on leds-pca963x.c driver by - * Peter Meerwald - * - * Driver for the NXP PCA9685 12-Bit PWM LED driver chip. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Register Addresses */ -#define PCA9685_MODE1 0x00 -#define PCA9685_MODE2 0x01 -#define PCA9685_LED0_ON_L 0x06 -#define PCA9685_ALL_LED_ON_L 0xFA - -/* MODE1 Register */ -#define PCA9685_ALLCALL 0x00 -#define PCA9685_SLEEP 0x04 -#define PCA9685_AI 0x05 - -/* MODE2 Register */ -#define PCA9685_INVRT 0x04 -#define PCA9685_OUTDRV 0x02 - -static const struct i2c_device_id pca9685_id[] = { - { "pca9685", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pca9685_id); - -struct pca9685_led { - struct i2c_client *client; - struct work_struct work; - u16 brightness; - struct led_classdev led_cdev; - int led_num; /* 0-15 */ - char name[32]; -}; - -static void pca9685_write_msg(struct i2c_client *client, u8 *buf, u8 len) -{ - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0x00, - .len = len, - .buf = buf - }; - i2c_transfer(client->adapter, &msg, 1); -} - -static void pca9685_all_off(struct i2c_client *client) -{ - u8 i2c_buffer[5] = {PCA9685_ALL_LED_ON_L, 0x00, 0x00, 0x00, 0x10}; - pca9685_write_msg(client, i2c_buffer, 5); -} - -static void pca9685_led_work(struct work_struct *work) -{ - struct pca9685_led *pca9685; - u8 i2c_buffer[5]; - - pca9685 = container_of(work, struct pca9685_led, work); - i2c_buffer[0] = PCA9685_LED0_ON_L + 4 * pca9685->led_num; - /* - * 4095 is the maximum brightness, so we set the ON time to 0x1000 - * which disables the PWM generator for that LED - */ - if (pca9685->brightness == 4095) - *((__le16 *)(i2c_buffer+1)) = cpu_to_le16(0x1000); - else - *((__le16 *)(i2c_buffer+1)) = 0x0000; - - if (pca9685->brightness == 0) - *((__le16 *)(i2c_buffer+3)) = cpu_to_le16(0x1000); - else if (pca9685->brightness == 4095) - *((__le16 *)(i2c_buffer+3)) = 0x0000; - else - *((__le16 *)(i2c_buffer+3)) = cpu_to_le16(pca9685->brightness); - - pca9685_write_msg(pca9685->client, i2c_buffer, 5); -} - -static void pca9685_led_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct pca9685_led *pca9685; - pca9685 = container_of(led_cdev, struct pca9685_led, led_cdev); - pca9685->brightness = value; - - schedule_work(&pca9685->work); -} - -static int pca9685_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pca9685_led *pca9685; - struct pca9685_platform_data *pdata; - int err; - u8 i; - - pdata = dev_get_platdata(&client->dev); - if (pdata) { - if (pdata->leds.num_leds < 1 || pdata->leds.num_leds > 15) { - dev_err(&client->dev, "board info must claim 1-16 LEDs"); - return -EINVAL; - } - } - - pca9685 = devm_kzalloc(&client->dev, 16 * sizeof(*pca9685), GFP_KERNEL); - if (!pca9685) - return -ENOMEM; - - i2c_set_clientdata(client, pca9685); - pca9685_all_off(client); - - for (i = 0; i < 16; i++) { - pca9685[i].client = client; - pca9685[i].led_num = i; - pca9685[i].name[0] = '\0'; - if (pdata && i < pdata->leds.num_leds) { - if (pdata->leds.leds[i].name) - strncpy(pca9685[i].name, - pdata->leds.leds[i].name, - sizeof(pca9685[i].name)-1); - if (pdata->leds.leds[i].default_trigger) - pca9685[i].led_cdev.default_trigger = - pdata->leds.leds[i].default_trigger; - } - if (strlen(pca9685[i].name) == 0) { - /* - * Write adapter and address to the name as well. - * Otherwise multiple chips attached to one host would - * not work. - */ - snprintf(pca9685[i].name, sizeof(pca9685[i].name), - "pca9685:%d:x%.2x:%d", - client->adapter->nr, client->addr, i); - } - pca9685[i].led_cdev.name = pca9685[i].name; - pca9685[i].led_cdev.max_brightness = 0xfff; - pca9685[i].led_cdev.brightness_set = pca9685_led_set; - - INIT_WORK(&pca9685[i].work, pca9685_led_work); - err = led_classdev_register(&client->dev, &pca9685[i].led_cdev); - if (err < 0) - goto exit; - } - - if (pdata) - i2c_smbus_write_byte_data(client, PCA9685_MODE2, - pdata->outdrv << PCA9685_OUTDRV | - pdata->inverted << PCA9685_INVRT); - else - i2c_smbus_write_byte_data(client, PCA9685_MODE2, - PCA9685_TOTEM_POLE << PCA9685_OUTDRV); - /* Enable Auto-Increment, enable oscillator, ALLCALL/SUBADDR disabled */ - i2c_smbus_write_byte_data(client, PCA9685_MODE1, BIT(PCA9685_AI)); - - return 0; - -exit: - while (i--) { - led_classdev_unregister(&pca9685[i].led_cdev); - cancel_work_sync(&pca9685[i].work); - } - return err; -} - -static int pca9685_remove(struct i2c_client *client) -{ - struct pca9685_led *pca9685 = i2c_get_clientdata(client); - u8 i; - - for (i = 0; i < 16; i++) { - led_classdev_unregister(&pca9685[i].led_cdev); - cancel_work_sync(&pca9685[i].work); - } - pca9685_all_off(client); - return 0; -} - -static struct i2c_driver pca9685_driver = { - .driver = { - .name = "leds-pca9685", - .owner = THIS_MODULE, - }, - .probe = pca9685_probe, - .remove = pca9685_remove, - .id_table = pca9685_id, -}; - -module_i2c_driver(pca9685_driver); - -MODULE_AUTHOR("Maximilian Güntner "); -MODULE_DESCRIPTION("PCA9685 LED Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/leds-pca9685.h b/include/linux/platform_data/leds-pca9685.h deleted file mode 100644 index 778e9e4249cc..000000000000 --- a/include/linux/platform_data/leds-pca9685.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2013 Maximilian Güntner - * - * This file is subject to the terms and conditions of version 2 of - * the GNU General Public License. See the file COPYING in the main - * directory of this archive for more details. - * - * Based on leds-pca963x.h by Peter Meerwald - * - * LED driver for the NXP PCA9685 PWM chip - * - */ - -#ifndef __LINUX_PCA9685_H -#define __LINUX_PCA9685_H - -#include - -enum pca9685_outdrv { - PCA9685_OPEN_DRAIN, - PCA9685_TOTEM_POLE, -}; - -enum pca9685_inverted { - PCA9685_NOT_INVERTED, - PCA9685_INVERTED, -}; - -struct pca9685_platform_data { - struct led_platform_data leds; - enum pca9685_outdrv outdrv; - enum pca9685_inverted inverted; -}; - -#endif /* __LINUX_PCA9685_H */ -- cgit v1.2.3 From 69dd0f848879328ae6c6f54c2ec80e49eef042d8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 9 Apr 2014 14:30:10 +0200 Subject: sched/idle: Remove TS_POLLING support Now that there are no architectures left using it, kill the support for TS_POLLING. Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andy Lutomirski Link: http://lkml.kernel.org/n/tip-6yurip2tfix2f4bfc5agu2s0@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/sched.h | 46 ++-------------------------------------------- 1 file changed, 2 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index accb66bfd722..725eef121c9f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2775,51 +2775,9 @@ static inline int spin_needbreak(spinlock_t *lock) /* * Idle thread specific functions to determine the need_resched - * polling state. We have two versions, one based on TS_POLLING in - * thread_info.status and one based on TIF_POLLING_NRFLAG in - * thread_info.flags + * polling state. */ -#ifdef TS_POLLING -static inline int tsk_is_polling(struct task_struct *p) -{ - return task_thread_info(p)->status & TS_POLLING; -} -static inline void __current_set_polling(void) -{ - current_thread_info()->status |= TS_POLLING; -} - -static inline bool __must_check current_set_polling_and_test(void) -{ - __current_set_polling(); - - /* - * Polling state must be visible before we test NEED_RESCHED, - * paired by resched_task() - */ - smp_mb(); - - return unlikely(tif_need_resched()); -} - -static inline void __current_clr_polling(void) -{ - current_thread_info()->status &= ~TS_POLLING; -} - -static inline bool __must_check current_clr_polling_and_test(void) -{ - __current_clr_polling(); - - /* - * Polling state must be visible before we test NEED_RESCHED, - * paired by resched_task() - */ - smp_mb(); - - return unlikely(tif_need_resched()); -} -#elif defined(TIF_POLLING_NRFLAG) +#ifdef TIF_POLLING_NRFLAG static inline int tsk_is_polling(struct task_struct *p) { return test_tsk_thread_flag(p, TIF_POLLING_NRFLAG); -- cgit v1.2.3 From 0567f5facbdf04806a37ef521dd0893159fde715 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 1 May 2014 17:49:33 +0200 Subject: asm-generic: allow generic unaligned access if the arch supports it Switch the default unaligned access method to 'hardware implemented' if HAVE_EFFICIENT_UNALIGNED_ACCESS is set. Signed-off-by: Ard Biesheuvel Acked-by: Arnd Bergmann --- include/asm-generic/unaligned.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/asm-generic/unaligned.h b/include/asm-generic/unaligned.h index 03cf5936bad6..1ac097279db1 100644 --- a/include/asm-generic/unaligned.h +++ b/include/asm-generic/unaligned.h @@ -4,22 +4,27 @@ /* * This is the most generic implementation of unaligned accesses * and should work almost anywhere. - * - * If an architecture can handle unaligned accesses in hardware, - * it may want to use the linux/unaligned/access_ok.h implementation - * instead. */ #include +/* Set by the arch if it can handle unaligned accesses in hardware. */ +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +# include +#endif + #if defined(__LITTLE_ENDIAN) -# include -# include +# ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +# include +# include +# endif # include # define get_unaligned __get_unaligned_le # define put_unaligned __put_unaligned_le #elif defined(__BIG_ENDIAN) -# include -# include +# ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +# include +# include +# endif # include # define get_unaligned __get_unaligned_be # define put_unaligned __put_unaligned_be -- cgit v1.2.3 From c3d620362dfe9218c7637354c7bce344ea771a31 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 7 May 2014 19:07:05 +0300 Subject: cfg80211: fix docbook warning When trying to generate documentation, at least xmldocs, we get the following warning: Warning(include/net/cfg80211.h:461): No description found for parameter 'nl80211_iftype' Fix it by adding the iftype argument name to the cfg80211_chandef_dfs_required() function declaration. Reported-and-tested-by: Masanari Iida Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0631230b01eb..28f6f1a5b445 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -458,7 +458,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, */ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, - enum nl80211_iftype); + enum nl80211_iftype iftype); /** * ieee80211_chandef_rate_flags - returns rate flags for a channel -- cgit v1.2.3 From 5eeaf1f1897372590105f155c6a7110b3fa36aef Mon Sep 17 00:00:00 2001 From: Stratos Karafotis Date: Wed, 7 May 2014 19:33:33 +0300 Subject: cpufreq: Fix build error on some platforms that use cpufreq_for_each_* On platforms that use cpufreq_for_each_* macros, build fails if CONFIG_CPU_FREQ=n, e.g. ARM/shmobile/koelsch/non-multiplatform: drivers/built-in.o: In function `clk_round_parent': clkdev.c:(.text+0xcf168): undefined reference to `cpufreq_next_valid' drivers/built-in.o: In function `clk_rate_table_find': clkdev.c:(.text+0xcf820): undefined reference to `cpufreq_next_valid' make[3]: *** [vmlinux] Error 1 Fix this making cpufreq_next_valid function inline and move it to cpufreq.h. Fixes: 27e289dce297 (cpufreq: Introduce macros for cpufreq_frequency_table iteration) Reported-and-tested-by: Geert Uytterhoeven Signed-off-by: Stratos Karafotis Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 11 ----------- include/linux/cpufreq.h | 11 +++++++++-- 2 files changed, 9 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index bfe82b63875f..a05c92198b9f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -237,17 +237,6 @@ void cpufreq_cpu_put(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(cpufreq_cpu_put); -bool cpufreq_next_valid(struct cpufreq_frequency_table **pos) -{ - while ((*pos)->frequency != CPUFREQ_TABLE_END) - if ((*pos)->frequency != CPUFREQ_ENTRY_INVALID) - return true; - else - (*pos)++; - return false; -} -EXPORT_SYMBOL_GPL(cpufreq_next_valid); - /********************************************************************* * EXTERNALLY AFFECTING FREQUENCY CHANGES * *********************************************************************/ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 9d803b529ac2..3f458896d45c 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -489,8 +489,15 @@ static inline void dev_pm_opp_free_cpufreq_table(struct device *dev, } #endif - -bool cpufreq_next_valid(struct cpufreq_frequency_table **pos); +static inline bool cpufreq_next_valid(struct cpufreq_frequency_table **pos) +{ + while ((*pos)->frequency != CPUFREQ_TABLE_END) + if ((*pos)->frequency != CPUFREQ_ENTRY_INVALID) + return true; + else + (*pos)++; + return false; +} /* * cpufreq_for_each_entry - iterate over a cpufreq_frequency_table -- cgit v1.2.3 From 29e69fd2cd6f55233f64f600ad55ce2b661784d1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 8 May 2014 01:59:26 -0700 Subject: ASoC: rsnd: remove compatibility code Now, all platform is using new style rsnd_dai_platform_info. Keeping compatibility is no longer needed. We can cleanup code. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 22 +++------------ sound/soc/sh/rcar/core.c | 54 ++++++++----------------------------- sound/soc/sh/rcar/rsnd.h | 3 --- sound/soc/sh/rcar/src.c | 69 +++++++++++++++--------------------------------- sound/soc/sh/rcar/ssi.c | 51 +---------------------------------- 5 files changed, 36 insertions(+), 163 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 34a3c02a4576..a03268ec59c3 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -34,40 +34,24 @@ * B : SSI direction */ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) -#define RSND_SSI_PLAY (1 << 24) #define RSND_SSI(_dma_id, _pio_irq, _flags) \ { .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } -#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ -{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } #define RSND_SSI_UNUSED \ -{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 } +{ .dma_id = -1, .pio_irq = -1, .flags = 0 } struct rsnd_ssi_platform_info { - int dai_id; /* will be removed */ int dma_id; int pio_irq; u32 flags; }; -/* - * flags - */ -#define RSND_SCU_USE_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ - #define RSND_SRC(rate, _dma_id) \ -{ .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, } -#define RSND_SRC_SET(rate, _dma_id) \ - { .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, } +{ .convert_rate = rate, .dma_id = _dma_id, } #define RSND_SRC_UNUSED \ - { .flags = 0, .convert_rate = 0, .dma_id = 0, } - -#define rsnd_scu_platform_info rsnd_src_platform_info -#define src_info scu_info -#define src_info_nr scu_info_nr +{ .convert_rate = 0, .dma_id = -1, } struct rsnd_src_platform_info { - u32 flags; u32 convert_rate; /* sampling rate convert */ int dma_id; /* for Gen2 SCU */ }; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 215b668166be..8ed84703b280 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -468,10 +468,7 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv, - rsnd_dai_id(priv, rdai), - rsnd_dai_is_play(rdai, io)); - int ssi_id = rsnd_mod_id(mod); + int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io)); int ret; unsigned long flags; @@ -584,7 +581,6 @@ static int rsnd_path_init(struct rsnd_priv *priv, struct rsnd_dai_stream *io) { struct rsnd_mod *mod; - struct rsnd_dai_platform_info *dai_info = rdai->info; int ret; int ssi_id = -1; int src_id = -1; @@ -599,20 +595,10 @@ static int rsnd_path_init(struct rsnd_priv *priv, * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is * using fixed path. */ - if (dai_info) { - if (rsnd_is_enable_path(io, ssi)) - ssi_id = rsnd_info_id(priv, io, ssi); - if (rsnd_is_enable_path(io, src)) - src_id = rsnd_info_id(priv, io, src); - } else { - /* get SSI's ID */ - mod = rsnd_ssi_mod_get_frm_dai(priv, - rsnd_dai_id(priv, rdai), - rsnd_dai_is_play(rdai, io)); - if (!mod) - return 0; - ssi_id = src_id = rsnd_mod_id(mod); - } + if (rsnd_is_enable_path(io, ssi)) + ssi_id = rsnd_info_id(priv, io, ssi); + if (rsnd_is_enable_path(io, src)) + src_id = rsnd_info_id(priv, io, src); ret = 0; @@ -726,29 +712,14 @@ static int rsnd_dai_probe(struct platform_device *pdev, struct snd_soc_dai_driver *drv; struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_dai *rdai; - struct rsnd_mod *pmod, *cmod; + struct rsnd_ssi_platform_info *pmod, *cmod; struct device *dev = rsnd_priv_to_dev(priv); int dai_nr; int i; rsnd_of_parse_dai(pdev, of_data, priv); - /* - * dai_nr should be set via dai_info_nr, - * but allow it to keeping compatible - */ dai_nr = info->dai_info_nr; - if (!dai_nr) { - /* get max dai nr */ - for (dai_nr = 0; dai_nr < 32; dai_nr++) { - pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); - cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); - - if (!pmod && !cmod) - break; - } - } - if (!dai_nr) { dev_err(dev, "no dai\n"); return -EIO; @@ -766,11 +737,10 @@ static int rsnd_dai_probe(struct platform_device *pdev, priv->rdai = rdai; for (i = 0; i < dai_nr; i++) { - if (info->dai_info) - rdai[i].info = &info->dai_info[i]; + rdai[i].info = &info->dai_info[i]; - pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); - cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); + pmod = rdai[i].info->playback.ssi; + cmod = rdai[i].info->capture.ssi; /* * init rsnd_dai @@ -788,8 +758,7 @@ static int rsnd_dai_probe(struct platform_device *pdev, drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; - if (info->dai_info) - rdai[i].playback.info = &info->dai_info[i].playback; + rdai[i].playback.info = &info->dai_info[i].playback; rsnd_path_init(priv, &rdai[i], &rdai[i].playback); } if (cmod) { @@ -798,8 +767,7 @@ static int rsnd_dai_probe(struct platform_device *pdev, drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; - if (info->dai_info) - rdai[i].capture.info = &info->dai_info[i].capture; + rdai[i].capture.info = &info->dai_info[i].capture; rsnd_path_init(priv, &rdai[i], &rdai[i].capture); } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 619d198c7d2e..061b04cbd7d5 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -379,9 +379,6 @@ int rsnd_ssi_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, - int dai_id, int is_play); int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); -int rsnd_ssi_is_play(struct rsnd_mod *mod); #endif diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 6232b7d307aa..d51c59bf7c47 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -27,12 +27,9 @@ struct rsnd_src { #define OTBL_18 (6 << 16) #define OTBL_16 (8 << 16) -#define rsnd_src_mode_flags(p) ((p)->info->flags) #define rsnd_src_convert_rate(p) ((p)->info->convert_rate) #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) -#define rsnd_src_hpbif_is_enable(src) \ - (rsnd_src_mode_flags(src) & RSND_SCU_USE_HPBIF) #define rsnd_src_dma_available(src) \ rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) @@ -80,34 +77,35 @@ struct rsnd_src { * * This driver request * struct rsnd_src_platform_info { - * u32 flags; * u32 convert_rate; + * int dma_id; * } * - * rsnd_src_hpbif_is_enable() will be true - * if flags had RSND_SRC_USE_HPBIF, - * and it controls whether SSIU is used or not. - * * rsnd_src_convert_rate() indicates * above convert_rate, and it controls * whether SRC is used or not. * * ex) doesn't use SRC - * struct rsnd_src_platform_info info = { - * .flags = 0, - * .convert_rate = 0, + * static struct rsnd_dai_platform_info rsnd_dai = { + * .playback = { .ssi = &rsnd_ssi[0], }, * }; * * ex) uses SRC - * struct rsnd_src_platform_info info = { - * .flags = RSND_SRC_USE_HPBIF, - * .convert_rate = 48000, + * static struct rsnd_src_platform_info rsnd_src[] = { + * RSND_SCU(48000, 0), + * ... + * }; + * static struct rsnd_dai_platform_info rsnd_dai = { + * .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] }, * }; * * ex) uses SRC bypass mode - * struct rsnd_src_platform_info info = { - * .flags = RSND_SRC_USE_HPBIF, - * .convert_rate = 0, + * static struct rsnd_src_platform_info rsnd_src[] = { + * RSND_SCU(0, 0), + * ... + * }; + * static struct rsnd_dai_platform_info rsnd_dai = { + * .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] }, * }; * */ @@ -119,24 +117,14 @@ int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct rcar_snd_info *info = rsnd_priv_to_info(priv); int ssi_id = rsnd_mod_id(ssi_mod); - int has_src = 0; /* * SSI_MODE0 */ - if (info->dai_info) { - has_src = !!src_mod; - } else { - struct rsnd_src *src = rsnd_mod_to_src(src_mod); - has_src = rsnd_src_hpbif_is_enable(src); - } - rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), - has_src ? 0 : (1 << ssi_id)); + src_mod ? 0 : (1 << ssi_id)); /* * SSI_MODE1 @@ -534,21 +522,13 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_src *src = rsnd_mod_to_src(mod); - struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, rsnd_mod_id(mod)); struct device *dev = rsnd_priv_to_dev(priv); int ret; - int is_play; - - if (info->dai_info) - is_play = rsnd_info_is_playback(priv, src); - else - is_play = rsnd_ssi_is_play(ssi); ret = rsnd_dma_init(priv, rsnd_mod_to_dma(mod), - is_play, + rsnd_info_is_playback(priv, src), src->info->dma_id); if (ret < 0) dev_err(dev, "SRC DMA failed\n"); @@ -699,11 +679,6 @@ int rsnd_src_probe(struct platform_device *pdev, snprintf(name, RSND_SRC_NAME_SIZE, "src.%d", i); clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - snprintf(name, RSND_SRC_NAME_SIZE, "scu.%d", i); - clk = devm_clk_get(dev, name); - } - if (IS_ERR(clk)) return PTR_ERR(clk); @@ -711,12 +686,10 @@ int rsnd_src_probe(struct platform_device *pdev, src->clk = clk; ops = &rsnd_src_non_ops; - if (rsnd_src_hpbif_is_enable(src)) { - if (rsnd_is_gen1(priv)) - ops = &rsnd_src_gen1_ops; - if (rsnd_is_gen2(priv)) - ops = &rsnd_src_gen2_ops; - } + if (rsnd_is_gen1(priv)) + ops = &rsnd_src_gen1_ops; + if (rsnd_is_gen2(priv)) + ops = &rsnd_src_gen2_ops; rsnd_mod_init(priv, &src->mod, ops, RSND_MOD_SRC, i); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 4b7e20603dd7..77b7b60325c1 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -422,20 +422,13 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct device *dev = rsnd_priv_to_dev(priv); int dma_id = ssi->info->dma_id; - int is_play; int ret; - if (info->dai_info) - is_play = rsnd_info_is_playback(priv, ssi); - else - is_play = rsnd_ssi_is_play(&ssi->mod); - ret = rsnd_dma_init( priv, rsnd_mod_to_dma(mod), - is_play, + rsnd_info_is_playback(priv, ssi), dma_id); if (ret < 0) @@ -512,41 +505,6 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { /* * ssi mod function */ -struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, - int dai_id, int is_play) -{ - struct rsnd_dai_platform_info *dai_info = NULL; - struct rsnd_dai_path_info *path_info = NULL; - struct rsnd_ssi_platform_info *target_info = NULL; - struct rsnd_ssi *ssi; - int i, has_play; - - if (priv->rdai) - dai_info = priv->rdai[dai_id].info; - if (dai_info) - path_info = (is_play) ? &dai_info->playback : &dai_info->capture; - if (path_info) - target_info = path_info->ssi; - - is_play = !!is_play; - - for_each_rsnd_ssi(ssi, priv, i) { - if (target_info == ssi->info) - return &ssi->mod; - - /* for compatible */ - if (rsnd_ssi_dai_id(ssi) != dai_id) - continue; - - has_play = rsnd_ssi_is_play(&ssi->mod); - - if (is_play == has_play) - return &ssi->mod; - } - - return NULL; -} - struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) @@ -562,13 +520,6 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } -int rsnd_ssi_is_play(struct rsnd_mod *mod) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); -} - static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { if (!rsnd_ssi_is_pin_sharing(&ssi->mod)) -- cgit v1.2.3 From 5ae76a94150c86a6e0ee84eb74e7f7e1909b8d39 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Thu, 8 May 2014 15:32:08 +0200 Subject: Bluetooth: Store RSSI for connection This patch adds support to store RSSI for connection when reply for HCI_Read_RSSI is received. Signed-off-by: Andrzej Kaczmarek Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 10 ++++++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 23 +++++++++++++++++++++++ 3 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4261a67682c0..ad2ecc92380d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1064,6 +1064,16 @@ struct hci_rp_read_page_scan_type { #define PAGE_SCAN_TYPE_STANDARD 0x00 #define PAGE_SCAN_TYPE_INTERLACED 0x01 +#define HCI_OP_READ_RSSI 0x1405 +struct hci_cp_read_rssi { + __le16 handle; +} __packed; +struct hci_rp_read_rssi { + __u8 status; + __le16 handle; + __s8 rssi; +} __packed; + #define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 struct hci_rp_read_local_amp_info { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d73f41855ada..0318d5263837 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -374,6 +374,7 @@ struct hci_conn { __u16 setting; __u16 le_conn_min_interval; __u16 le_conn_max_interval; + __s8 rssi; unsigned long flags; __u8 remote_cap; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 07c37d0cecb2..2bb0053d4c45 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1245,6 +1245,25 @@ static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, amp_write_rem_assoc_continue(hdev, rp->phy_handle); } +static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_rssi *rp = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn) + conn->rssi = rp->rssi; + + hci_dev_unlock(hdev); +} + static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -2637,6 +2656,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_remote_amp_assoc(hdev, skb); break; + case HCI_OP_READ_RSSI: + hci_cc_read_rssi(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; -- cgit v1.2.3 From 5409e46f1bcf960c651f3fff35f2f25e539655cf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 May 2014 13:49:44 +0200 Subject: nfsd: clean up fh_auth usage Use fh_fsid when reffering to the fsid part of the filehandle. The variable length auth field envisioned in nfsfh wasn't ever implemented. Also clean up some lose ends around this and document the file handle format better. Btw, why do we even export nfsfh.h to userspace? The file handle very much is kernel private, and nothing in nfs-utils include the header either. Signed-off-by: Christoph Hellwig Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsfh.c | 20 +++++++++----------- include/uapi/linux/nfsd/nfsfh.h | 32 +++++++------------------------- 2 files changed, 16 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 3c37b160dcad..a337106d6875 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -169,8 +169,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) data_left -= len; if (data_left < 0) return error; - exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_auth); - fid = (struct fid *)(fh->fh_auth + len); + exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_fsid); + fid = (struct fid *)(fh->fh_fsid + len); } else { __u32 tfh[2]; dev_t xdev; @@ -385,7 +385,7 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp, { if (dentry != exp->ex_path.dentry) { struct fid *fid = (struct fid *) - (fhp->fh_handle.fh_auth + fhp->fh_handle.fh_size/4 - 1); + (fhp->fh_handle.fh_fsid + fhp->fh_handle.fh_size/4 - 1); int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK); @@ -513,7 +513,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, */ struct inode * inode = dentry->d_inode; - __u32 *datap; dev_t ex_dev = exp_sb(exp)->s_dev; dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %pd2, ino=%ld)\n", @@ -557,17 +556,16 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, if (inode) _fh_update_old(dentry, exp, &fhp->fh_handle); } else { - int len; + fhp->fh_handle.fh_size = + key_len(fhp->fh_handle.fh_fsid_type) + 4; fhp->fh_handle.fh_auth_type = 0; - datap = fhp->fh_handle.fh_auth+0; - mk_fsid(fhp->fh_handle.fh_fsid_type, datap, ex_dev, + + mk_fsid(fhp->fh_handle.fh_fsid_type, + fhp->fh_handle.fh_fsid, + ex_dev, exp->ex_path.dentry->d_inode->i_ino, exp->ex_fsid, exp->ex_uuid); - len = key_len(fhp->fh_handle.fh_fsid_type); - datap += len/4; - fhp->fh_handle.fh_size = 4 + len; - if (inode) _fh_update(fhp, exp, dentry); if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) { diff --git a/include/uapi/linux/nfsd/nfsfh.h b/include/uapi/linux/nfsd/nfsfh.h index 616e3b396476..20391235d088 100644 --- a/include/uapi/linux/nfsd/nfsfh.h +++ b/include/uapi/linux/nfsd/nfsfh.h @@ -1,13 +1,7 @@ /* - * include/linux/nfsd/nfsfh.h - * * This file describes the layout of the file handles as passed * over the wire. * - * Earlier versions of knfsd used to sign file handles using keyed MD5 - * or SHA. I've removed this code, because it doesn't give you more - * security than blocking external access to port 2049 on your firewall. - * * Copyright (C) 1995, 1996, 1997 Olaf Kirch */ @@ -37,7 +31,7 @@ struct nfs_fhbase_old { }; /* - * This is the new flexible, extensible style NFSv2/v3 file handle. + * This is the new flexible, extensible style NFSv2/v3/v4 file handle. * by Neil Brown - March 2000 * * The file handle starts with a sequence of four-byte words. @@ -47,14 +41,7 @@ struct nfs_fhbase_old { * * All four-byte values are in host-byte-order. * - * The auth_type field specifies how the filehandle can be authenticated - * This might allow a file to be confirmed to be in a writable part of a - * filetree without checking the path from it up to the root. - * Current values: - * 0 - No authentication. fb_auth is 0 bytes long - * Possible future values: - * 1 - 4 bytes taken from MD5 hash of the remainer of the file handle - * prefixed by a secret and with the important export flags. + * The auth_type field is deprecated and must be set to 0. * * The fsid_type identifies how the filesystem (or export point) is * encoded. @@ -71,14 +58,9 @@ struct nfs_fhbase_old { * 7 - 8 byte inode number and 16 byte uuid * * The fileid_type identified how the file within the filesystem is encoded. - * This is (will be) passed to, and set by, the underlying filesystem if it supports - * filehandle operations. The filesystem must not use the value '0' or '0xff' and may - * only use the values 1 and 2 as defined below: - * Current values: - * 0 - The root, or export point, of the filesystem. fb_fileid is 0 bytes. - * 1 - 32bit inode number, 32 bit generation number. - * 2 - 32bit inode number, 32 bit generation number, 32 bit parent directory inode number. - * + * The values for this field are filesystem specific, exccept that + * filesystems must not use the values '0' or '0xff'. 'See enum fid_type' + * in include/linux/exportfs.h for currently registered values. */ struct nfs_fhbase_new { __u8 fb_version; /* == 1, even => nfs_fhbase_old */ @@ -114,9 +96,9 @@ struct knfsd_fh { #define fh_fsid_type fh_base.fh_new.fb_fsid_type #define fh_auth_type fh_base.fh_new.fb_auth_type #define fh_fileid_type fh_base.fh_new.fb_fileid_type -#define fh_auth fh_base.fh_new.fb_auth #define fh_fsid fh_base.fh_new.fb_auth - +/* Do not use, provided for userspace compatiblity. */ +#define fh_auth fh_base.fh_new.fb_auth #endif /* _UAPI_LINUX_NFSD_FH_H */ -- cgit v1.2.3 From c9d8f1a64225dfcc2f721d73a5984a2444920744 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 6 May 2014 11:02:49 -0700 Subject: ipv4: move local_port_range out of CONFIG_SYSCTL When CONFIG_SYSCTL is not set, ip_local_port_range should still work, just that no one can change it. Therefore we should move it out of sysctl_inet.c. Also, rename it to ->ip_local_ports instead. Cc: David S. Miller Cc: Francois Romieu Reported-by: Stefan de Konink Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 2 +- net/ipv4/af_inet.c | 28 ++++++++++++++++++++++++++++ net/ipv4/inet_connection_sock.c | 8 ++++---- net/ipv4/ping.c | 4 ++-- net/ipv4/sysctl_net_ipv4.c | 29 +++++++++++------------------ 5 files changed, 46 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 80f500a29498..3d95cd475316 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -66,7 +66,7 @@ struct netns_ipv4 { int sysctl_icmp_ratemask; int sysctl_icmp_errors_use_inbound_ifaddr; - struct local_ports sysctl_local_ports; + struct local_ports ip_local_ports; int sysctl_tcp_ecn; int sysctl_ip_no_pmtu_disc; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 8c54870db792..cccc8e487c7e 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1650,6 +1650,31 @@ static int __init init_ipv4_mibs(void) return register_pernet_subsys(&ipv4_mib_ops); } +static __net_init int inet_init_net(struct net *net) +{ + /* + * Set defaults for local port range + */ + seqlock_init(&net->ipv4.ip_local_ports.lock); + net->ipv4.ip_local_ports.range[0] = 32768; + net->ipv4.ip_local_ports.range[1] = 61000; + return 0; +} + +static __net_exit void inet_exit_net(struct net *net) +{ +} + +static __net_initdata struct pernet_operations af_inet_ops = { + .init = inet_init_net, + .exit = inet_exit_net, +}; + +static int __init init_inet_pernet_ops(void) +{ + return register_pernet_subsys(&af_inet_ops); +} + static int ipv4_proc_init(void); /* @@ -1794,6 +1819,9 @@ static int __init inet_init(void) if (ip_mr_init()) pr_crit("%s: Cannot init ipv4 mroute\n", __func__); #endif + + if (init_inet_pernet_ops()) + pr_crit("%s: Cannot init ipv4 inet pernet ops\n", __func__); /* * Initialise per-cpu ipv4 mibs */ diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 0d1e2cb877ec..a56b8e6e866a 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -37,11 +37,11 @@ void inet_get_local_port_range(struct net *net, int *low, int *high) unsigned int seq; do { - seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock); + seq = read_seqbegin(&net->ipv4.ip_local_ports.lock); - *low = net->ipv4.sysctl_local_ports.range[0]; - *high = net->ipv4.sysctl_local_ports.range[1]; - } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq)); + *low = net->ipv4.ip_local_ports.range[0]; + *high = net->ipv4.ip_local_ports.range[1]; + } while (read_seqretry(&net->ipv4.ip_local_ports.lock, seq)); } EXPORT_SYMBOL(inet_get_local_port_range); diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 8210964a9f19..347bdde9a585 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -240,11 +240,11 @@ static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, unsigned int seq; do { - seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock); + seq = read_seqbegin(&net->ipv4.ip_local_ports.lock); *low = data[0]; *high = data[1]; - } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq)); + } while (read_seqretry(&net->ipv4.ip_local_ports.lock, seq)); } diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 44eba052b43d..a116c41b05dd 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -45,10 +45,10 @@ static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX }; /* Update system visible IP port range */ static void set_local_port_range(struct net *net, int range[2]) { - write_seqlock(&net->ipv4.sysctl_local_ports.lock); - net->ipv4.sysctl_local_ports.range[0] = range[0]; - net->ipv4.sysctl_local_ports.range[1] = range[1]; - write_sequnlock(&net->ipv4.sysctl_local_ports.lock); + write_seqlock(&net->ipv4.ip_local_ports.lock); + net->ipv4.ip_local_ports.range[0] = range[0]; + net->ipv4.ip_local_ports.range[1] = range[1]; + write_sequnlock(&net->ipv4.ip_local_ports.lock); } /* Validate changes from /proc interface. */ @@ -57,7 +57,7 @@ static int ipv4_local_port_range(struct ctl_table *table, int write, size_t *lenp, loff_t *ppos) { struct net *net = - container_of(table->data, struct net, ipv4.sysctl_local_ports.range); + container_of(table->data, struct net, ipv4.ip_local_ports.range); int ret; int range[2]; struct ctl_table tmp = { @@ -90,11 +90,11 @@ static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low container_of(table->data, struct net, ipv4.sysctl_ping_group_range); unsigned int seq; do { - seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock); + seq = read_seqbegin(&net->ipv4.ip_local_ports.lock); *low = data[0]; *high = data[1]; - } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq)); + } while (read_seqretry(&net->ipv4.ip_local_ports.lock, seq)); } /* Update system visible IP port range */ @@ -103,10 +103,10 @@ static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t hig kgid_t *data = table->data; struct net *net = container_of(table->data, struct net, ipv4.sysctl_ping_group_range); - write_seqlock(&net->ipv4.sysctl_local_ports.lock); + write_seqlock(&net->ipv4.ip_local_ports.lock); data[0] = low; data[1] = high; - write_sequnlock(&net->ipv4.sysctl_local_ports.lock); + write_sequnlock(&net->ipv4.ip_local_ports.lock); } /* Validate changes from /proc interface. */ @@ -819,8 +819,8 @@ static struct ctl_table ipv4_net_table[] = { }, { .procname = "ip_local_port_range", - .maxlen = sizeof(init_net.ipv4.sysctl_local_ports.range), - .data = &init_net.ipv4.sysctl_local_ports.range, + .maxlen = sizeof(init_net.ipv4.ip_local_ports.range), + .data = &init_net.ipv4.ip_local_ports.range, .mode = 0644, .proc_handler = ipv4_local_port_range, }, @@ -865,13 +865,6 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) net->ipv4.sysctl_ping_group_range[0] = make_kgid(&init_user_ns, 1); net->ipv4.sysctl_ping_group_range[1] = make_kgid(&init_user_ns, 0); - /* - * Set defaults for local port range - */ - seqlock_init(&net->ipv4.sysctl_local_ports.lock); - net->ipv4.sysctl_local_ports.range[0] = 32768; - net->ipv4.sysctl_local_ports.range[1] = 61000; - net->ipv4.ipv4_hdr = register_net_sysctl(net, "net/ipv4", table); if (net->ipv4.ipv4_hdr == NULL) goto err_reg; -- cgit v1.2.3 From ba6b918ab234186d3aa1663e296586a1b526b77a Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 6 May 2014 11:02:50 -0700 Subject: ping: move ping_group_range out of CONFIG_SYSCTL Similarly, when CONFIG_SYSCTL is not set, ping_group_range should still work, just that no one can change it. Therefore we should move it out of sysctl_net_ipv4.c. And, it should not share the same seqlock with ip_local_port_range. BTW, rename it to ->ping_group_range instead. Cc: David S. Miller Cc: Francois Romieu Reported-by: Stefan de Konink Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 7 ++++++- net/ipv4/af_inet.c | 8 ++++++++ net/ipv4/ping.c | 6 +++--- net/ipv4/sysctl_net_ipv4.c | 13 +++---------- 4 files changed, 20 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 3d95cd475316..b2704fd0ec80 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -20,6 +20,11 @@ struct local_ports { int range[2]; }; +struct ping_group_range { + seqlock_t lock; + kgid_t range[2]; +}; + struct netns_ipv4 { #ifdef CONFIG_SYSCTL struct ctl_table_header *forw_hdr; @@ -72,7 +77,7 @@ struct netns_ipv4 { int sysctl_ip_no_pmtu_disc; int sysctl_ip_fwd_use_pmtu; - kgid_t sysctl_ping_group_range[2]; + struct ping_group_range ping_group_range; atomic_t dev_addr_genid; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index cccc8e487c7e..6d6dd345bc4d 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1658,6 +1658,14 @@ static __net_init int inet_init_net(struct net *net) seqlock_init(&net->ipv4.ip_local_ports.lock); net->ipv4.ip_local_ports.range[0] = 32768; net->ipv4.ip_local_ports.range[1] = 61000; + + seqlock_init(&net->ipv4.ping_group_range.lock); + /* + * Sane defaults - nobody may create ping sockets. + * Boot scripts should set this to distro-specific group. + */ + net->ipv4.ping_group_range.range[0] = make_kgid(&init_user_ns, 1); + net->ipv4.ping_group_range.range[1] = make_kgid(&init_user_ns, 0); return 0; } diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 347bdde9a585..044a0ddf6a79 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -236,15 +236,15 @@ exit: static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, kgid_t *high) { - kgid_t *data = net->ipv4.sysctl_ping_group_range; + kgid_t *data = net->ipv4.ping_group_range.range; unsigned int seq; do { - seq = read_seqbegin(&net->ipv4.ip_local_ports.lock); + seq = read_seqbegin(&net->ipv4.ping_group_range.lock); *low = data[0]; *high = data[1]; - } while (read_seqretry(&net->ipv4.ip_local_ports.lock, seq)); + } while (read_seqretry(&net->ipv4.ping_group_range.lock, seq)); } diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index a116c41b05dd..5cde8f263d40 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -87,7 +87,7 @@ static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low { kgid_t *data = table->data; struct net *net = - container_of(table->data, struct net, ipv4.sysctl_ping_group_range); + container_of(table->data, struct net, ipv4.ping_group_range.range); unsigned int seq; do { seq = read_seqbegin(&net->ipv4.ip_local_ports.lock); @@ -102,7 +102,7 @@ static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t hig { kgid_t *data = table->data; struct net *net = - container_of(table->data, struct net, ipv4.sysctl_ping_group_range); + container_of(table->data, struct net, ipv4.ping_group_range.range); write_seqlock(&net->ipv4.ip_local_ports.lock); data[0] = low; data[1] = high; @@ -805,7 +805,7 @@ static struct ctl_table ipv4_net_table[] = { }, { .procname = "ping_group_range", - .data = &init_net.ipv4.sysctl_ping_group_range, + .data = &init_net.ipv4.ping_group_range.range, .maxlen = sizeof(gid_t)*2, .mode = 0644, .proc_handler = ipv4_ping_group_range, @@ -858,13 +858,6 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) table[i].data += (void *)net - (void *)&init_net; } - /* - * Sane defaults - nobody may create ping sockets. - * Boot scripts should set this to distro-specific group. - */ - net->ipv4.sysctl_ping_group_range[0] = make_kgid(&init_user_ns, 1); - net->ipv4.sysctl_ping_group_range[1] = make_kgid(&init_user_ns, 0); - net->ipv4.ipv4_hdr = register_net_sysctl(net, "net/ipv4", table); if (net->ipv4.ipv4_hdr == NULL) goto err_reg; -- cgit v1.2.3 From 9c9e321455fb806108f9dbb1872bacfd42c6002b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 8 May 2014 23:16:35 +0200 Subject: mfd: stmpe: add optional regulators The STMPE has VCC and VIO supply lines, and sometimes (as on Ux500) this comes from a software-controlled regulator. Make it possible to supply the STMPE with power from these regulators. Signed-off-by: Linus Walleij --- drivers/mfd/stmpe.c | 18 ++++++++++++++++++ include/linux/mfd/stmpe.h | 5 +++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 9fa2dd6d38bd..294731be1a15 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "stmpe.h" static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks) @@ -1186,6 +1187,18 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum) stmpe->variant = stmpe_variant_info[partnum]; stmpe->regs = stmpe->variant->regs; stmpe->num_gpios = stmpe->variant->num_gpios; + stmpe->vcc = devm_regulator_get_optional(ci->dev, "vcc"); + if (!IS_ERR(stmpe->vcc)) { + ret = regulator_enable(stmpe->vcc); + if (ret) + dev_warn(ci->dev, "failed to enable VCC supply\n"); + } + stmpe->vio = devm_regulator_get_optional(ci->dev, "vio"); + if (!IS_ERR(stmpe->vio)) { + ret = regulator_enable(stmpe->vio); + if (ret) + dev_warn(ci->dev, "failed to enable VIO supply\n"); + } dev_set_drvdata(stmpe->dev, stmpe); if (ci->init) @@ -1252,6 +1265,11 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum) int stmpe_remove(struct stmpe *stmpe) { + if (!IS_ERR(stmpe->vio)) + regulator_disable(stmpe->vio); + if (!IS_ERR(stmpe->vcc)) + regulator_disable(stmpe->vcc); + mfd_remove_devices(stmpe->dev); return 0; diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 48395a69a7e9..980898620e57 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -11,6 +11,7 @@ #include struct device; +struct regulator; enum stmpe_block { STMPE_BLOCK_GPIO = 1 << 0, @@ -62,6 +63,8 @@ struct stmpe_client_info; /** * struct stmpe - STMPE MFD structure + * @vcc: optional VCC regulator + * @vio: optional VIO regulator * @lock: lock protecting I/O operations * @irq_lock: IRQ bus lock * @dev: device, mostly for dev_dbg() @@ -80,6 +83,8 @@ struct stmpe_client_info; * @pdata: platform data */ struct stmpe { + struct regulator *vcc; + struct regulator *vio; struct mutex lock; struct mutex irq_lock; struct device *dev; -- cgit v1.2.3 From 24f13a6679c9b75687f3ae48994e42071d4e9fce Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 24 Apr 2014 13:28:18 +0100 Subject: video: omap2dss: fix LPAE warnings If LPAE is enabled, dma_addr_t is 64 bit, so we have to change a few type for everything in this driver to match again. Signed-off-by: Arnd Bergmann Signed-off-by: Peter Griffin Cc: Jean-Christophe Plagniol-Villard Cc: Tomi Valkeinen Cc: linux-fbdev@vger.kernel.org Cc: linux-omap@vger.kernel.org Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/omap2/dss/dispc.c | 6 +++--- include/video/omapdss.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c index f18397c33e8f..4fe143166b74 100644 --- a/drivers/video/fbdev/omap2/dss/dispc.c +++ b/drivers/video/fbdev/omap2/dss/dispc.c @@ -2577,9 +2577,9 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi, channel = dispc_ovl_get_channel_out(plane); - DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> " - "%dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n", - plane, oi->paddr, oi->p_uv_addr, oi->screen_width, oi->pos_x, + DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->" + " %dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n", + plane, &oi->paddr, &oi->p_uv_addr, oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height, oi->color_mode, oi->rotation, oi->mirror, channel, replication); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 6adb44534606..ded61a9e5219 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -388,8 +388,8 @@ struct omap_dss_cpr_coefs { }; struct omap_overlay_info { - u32 paddr; - u32 p_uv_addr; /* for NV12 format */ + dma_addr_t paddr; + dma_addr_t p_uv_addr; /* for NV12 format */ u16 screen_width; u16 width; u16 height; -- cgit v1.2.3 From 81c44c2b2ce358b1c5fe0065dc5d2e2010f39f1b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 24 Apr 2014 13:28:20 +0100 Subject: video/omap: fix modular build The framebuffer layer can be a loadable module, which forces omapfb to be a module as well. However, this breaks the lcd drivers, which are linked into the omapfb driver but each have their own module_init() function. To solve this, we split out the lcd drivers into separate modules and export omapfb_register_panel, which is the only interface required between the main omapfb driver and the lcd panel drivers. We also have to introduce a new Kconfig symbol for H3, since that lcd driver has a dependency on TPS65010, which we can express better in Kconfig than Makefile syntax. Signed-off-by: Arnd Bergmann Signed-off-by: Peter Griffin Cc: Jean-Christophe Plagniol-Villard Cc: Tomi Valkeinen Cc: linux-fbdev@vger.kernel.org Cc: linux-omap@vger.kernel.org Signed-off-by: Tomi Valkeinen --- drivers/video/fbdev/omap/Kconfig | 9 +++++++++ drivers/video/fbdev/omap/Makefile | 23 ++++++++++++----------- drivers/video/fbdev/omap/omapfb_main.c | 1 + include/linux/omap-dma.h | 2 +- 4 files changed, 23 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/video/fbdev/omap/Kconfig b/drivers/video/fbdev/omap/Kconfig index 0bc3a936ce2b..18c4cb0d5690 100644 --- a/drivers/video/fbdev/omap/Kconfig +++ b/drivers/video/fbdev/omap/Kconfig @@ -39,6 +39,15 @@ config FB_OMAP_LCD_MIPID the Mobile Industry Processor Interface DBI-C/DCS specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3) +config FB_OMAP_LCD_H3 + bool "TPS65010 LCD controller on OMAP-H3" + depends on MACH_OMAP_H3 + depends on TPS65010 + default y + help + Say Y here if you want to have support for the LCD on the + H3 board. + config FB_OMAP_DMA_TUNE bool "Set DMA SDRAM access priority high" depends on FB_OMAP diff --git a/drivers/video/fbdev/omap/Makefile b/drivers/video/fbdev/omap/Makefile index 1927faffb5bc..732e0718be53 100644 --- a/drivers/video/fbdev/omap/Makefile +++ b/drivers/video/fbdev/omap/Makefile @@ -10,17 +10,18 @@ objs-y$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o -objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o -objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o -objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o -objs-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o -objs-y$(CONFIG_MACH_OMAP_PALMZ71) += lcd_palmz71.o -objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o -objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o -objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o - -objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o -objs-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o +lcds-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o +lcds-y$(CONFIG_FB_OMAP_LCD_H3) += lcd_h3.o +lcds-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o +lcds-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o +lcds-y$(CONFIG_MACH_OMAP_PALMZ71) += lcd_palmz71.o +lcds-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o +lcds-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o +lcds-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o + +lcds-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o +lcds-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o omapfb-objs := $(objs-yy) +obj-$(CONFIG_FB_OMAP) += $(lcds-yy) diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c index e4fc6d9b5371..d8d028d98711 100644 --- a/drivers/video/fbdev/omap/omapfb_main.c +++ b/drivers/video/fbdev/omap/omapfb_main.c @@ -1823,6 +1823,7 @@ void omapfb_register_panel(struct lcd_panel *panel) if (fbdev_pdev != NULL) omapfb_do_probe(fbdev_pdev, fbdev_panel); } +EXPORT_SYMBOL_GPL(omapfb_register_panel); /* Called when the device is being detached from the driver */ static int omapfb_remove(struct platform_device *pdev) diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h index 41a13e70f41f..0a1a2e2d5c21 100644 --- a/include/linux/omap-dma.h +++ b/include/linux/omap-dma.h @@ -393,7 +393,7 @@ extern int omap_modify_dma_chain_params(int chain_id, extern int omap_dma_chain_status(int chain_id); #endif -#if defined(CONFIG_ARCH_OMAP1) && defined(CONFIG_FB_OMAP) +#if defined(CONFIG_ARCH_OMAP1) && IS_ENABLED(CONFIG_FB_OMAP) #include #else static inline int omap_lcd_dma_running(void) -- cgit v1.2.3 From f6837ba8c98afcf28ec25f6863a8597274aeefd6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 Apr 2014 14:19:04 +0200 Subject: mac80211: handle failed restart/resume better When the driver fails during HW restart or resume, the whole stack goes into a very confused state with interfaces being up while the hardware is down etc. Address this by shutting down everything; we'll run into a lot of warnings in the process but that's better than having the whole stack get messed up. Reviewed-by: Arik Nemtsov Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 14 ++++++ net/mac80211/driver-ops.h | 108 +++++++++++++++++++++++++++++---------------- net/mac80211/ieee80211_i.h | 1 + net/mac80211/scan.c | 15 ++++--- net/mac80211/util.c | 46 +++++++++++++++++-- net/wireless/core.c | 20 ++++++--- 6 files changed, 153 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 28f6f1a5b445..5c7169b0ac57 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4771,6 +4771,20 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, gfp_t gfp); +/** + * cfg80211_shutdown_all_interfaces - shut down all interfaces for a wiphy + * @wiphy: the wiphy to shut down + * + * This function shuts down all interfaces belonging to this wiphy by + * calling dev_close() (and treating non-netdev interfaces as needed). + * It shouldn't really be used unless there are some fatal device errors + * that really can't be recovered in any other way. + * + * Callers must hold the RTNL and be able to deal with callbacks into + * the driver while the function is running. + */ +void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 5331582a2c81..df1d50291344 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -5,11 +5,11 @@ #include "ieee80211_i.h" #include "trace.h" -static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) +static inline bool check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) { - WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), - "%s: Failed check-sdata-in-driver check, flags: 0x%x\n", - sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); + return !WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), + "%s: Failed check-sdata-in-driver check, flags: 0x%x\n", + sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); } static inline struct ieee80211_sub_if_data * @@ -168,7 +168,8 @@ static inline int drv_change_interface(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_change_interface(local, sdata, type, p2p); ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); @@ -181,7 +182,8 @@ static inline void drv_remove_interface(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_remove_interface(local, sdata); local->ops->remove_interface(&local->hw, &sdata->vif); @@ -219,7 +221,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, sdata->vif.type == NL80211_IFTYPE_MONITOR)) return; - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_bss_info_changed(local, sdata, info, changed); if (local->ops->bss_info_changed) @@ -278,7 +281,8 @@ static inline int drv_set_key(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_set_key(local, cmd, sdata, sta, key); ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); @@ -298,7 +302,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local, ista = &sta->sta; sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_update_tkip_key(local, sdata, conf, ista, iv32); if (local->ops->update_tkip_key) @@ -315,7 +320,8 @@ static inline int drv_hw_scan(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_hw_scan(local, sdata); ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); @@ -328,7 +334,8 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_cancel_hw_scan(local, sdata); local->ops->cancel_hw_scan(&local->hw, &sdata->vif); @@ -345,7 +352,8 @@ drv_sched_scan_start(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_sched_scan_start(local, sdata); ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, @@ -361,7 +369,8 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_sched_scan_stop(local, sdata); ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif); @@ -462,7 +471,8 @@ static inline void drv_sta_notify(struct ieee80211_local *local, struct ieee80211_sta *sta) { sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_sta_notify(local, sdata, cmd, sta); if (local->ops->sta_notify) @@ -479,7 +489,8 @@ static inline int drv_sta_add(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_sta_add(local, sdata, sta); if (local->ops->sta_add) @@ -497,7 +508,8 @@ static inline void drv_sta_remove(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_sta_remove(local, sdata, sta); if (local->ops->sta_remove) @@ -515,7 +527,8 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; if (local->ops->sta_add_debugfs) local->ops->sta_add_debugfs(&local->hw, &sdata->vif, @@ -545,7 +558,8 @@ static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta); if (local->ops->sta_pre_rcu_remove) @@ -566,7 +580,8 @@ int drv_sta_state(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); if (local->ops->sta_state) { @@ -590,7 +605,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local, struct ieee80211_sta *sta, u32 changed) { sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && (sdata->vif.type != NL80211_IFTYPE_ADHOC && @@ -612,7 +628,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_conf_tx(local, sdata, ac, params); if (local->ops->conf_tx) @@ -629,7 +646,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return ret; trace_drv_get_tsf(local, sdata); if (local->ops->get_tsf) @@ -644,7 +662,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_set_tsf(local, sdata, tsf); if (local->ops->set_tsf) @@ -657,7 +676,8 @@ static inline void drv_reset_tsf(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_reset_tsf(local, sdata); if (local->ops->reset_tsf) @@ -689,7 +709,8 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, might_sleep(); sdata = get_bss_sdata(sdata); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); @@ -733,8 +754,8 @@ static inline void drv_flush(struct ieee80211_local *local, might_sleep(); - if (sdata) - check_sdata_in_driver(sdata); + if (sdata && !check_sdata_in_driver(sdata)) + return; trace_drv_flush(local, queues, drop); if (local->ops->flush) @@ -854,7 +875,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local, might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_set_bitrate_mask(local, sdata, mask); if (local->ops->set_bitrate_mask) @@ -869,7 +891,8 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_gtk_rekey_data *data) { - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_set_rekey_data(local, sdata, data); if (local->ops->set_rekey_data) @@ -937,7 +960,8 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); trace_drv_mgd_prepare_tx(local, sdata); @@ -964,6 +988,9 @@ static inline int drv_add_chanctx(struct ieee80211_local *local, static inline void drv_remove_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { + if (WARN_ON(!ctx->driver_present)) + return; + trace_drv_remove_chanctx(local, ctx); if (local->ops->remove_chanctx) local->ops->remove_chanctx(&local->hw, &ctx->conf); @@ -989,7 +1016,8 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local, { int ret = 0; - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_assign_vif_chanctx(local, sdata, ctx); if (local->ops->assign_vif_chanctx) { @@ -1007,7 +1035,8 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx *ctx) { - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_unassign_vif_chanctx(local, sdata, ctx); if (local->ops->unassign_vif_chanctx) { @@ -1024,7 +1053,8 @@ static inline int drv_start_ap(struct ieee80211_local *local, { int ret = 0; - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf); if (local->ops->start_ap) @@ -1036,7 +1066,8 @@ static inline int drv_start_ap(struct ieee80211_local *local, static inline void drv_stop_ap(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_stop_ap(local, sdata); if (local->ops->stop_ap) @@ -1059,7 +1090,8 @@ drv_set_default_unicast_key(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, int key_idx) { - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; WARN_ON_ONCE(key_idx < -1 || key_idx > 3); @@ -1101,7 +1133,8 @@ static inline int drv_join_ibss(struct ieee80211_local *local, int ret = 0; might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return -EIO; trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf); if (local->ops->join_ibss) @@ -1114,7 +1147,8 @@ static inline void drv_leave_ibss(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); - check_sdata_in_driver(sdata); + if (!check_sdata_in_driver(sdata)) + return; trace_drv_leave_ibss(local, sdata); if (local->ops->leave_ibss) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f4ba0c4a4314..4668ce9a1d3f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1459,6 +1459,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req); int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); +void ieee80211_sched_scan_end(struct ieee80211_local *local); void ieee80211_sched_scan_stopped_work(struct work_struct *work); /* off-channel helpers */ diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 28185c8dc19a..f40661eb75b5 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1076,12 +1076,8 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_sched_scan_results); -void ieee80211_sched_scan_stopped_work(struct work_struct *work) +void ieee80211_sched_scan_end(struct ieee80211_local *local) { - struct ieee80211_local *local = - container_of(work, struct ieee80211_local, - sched_scan_stopped_work); - mutex_lock(&local->mtx); if (!rcu_access_pointer(local->sched_scan_sdata)) { @@ -1099,6 +1095,15 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work) cfg80211_sched_scan_stopped(local->hw.wiphy); } +void ieee80211_sched_scan_stopped_work(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, + sched_scan_stopped_work); + + ieee80211_sched_scan_end(local); +} + void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ad058759e85e..7e0dd4be8097 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1457,6 +1457,44 @@ void ieee80211_stop_device(struct ieee80211_local *local) drv_stop(local); } +static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_chanctx *ctx; + + /* + * We get here if during resume the device can't be restarted properly. + * We might also get here if this happens during HW reset, which is a + * slightly different situation and we need to drop all connections in + * the latter case. + * + * Ask cfg80211 to turn off all interfaces, this will result in more + * warnings but at least we'll then get into a clean stopped state. + */ + + local->resuming = false; + local->suspended = false; + local->started = false; + + /* scheduled scan clearly can't be running any more, but tell + * cfg80211 and clear local state + */ + ieee80211_sched_scan_end(local); + + list_for_each_entry(sdata, &local->interfaces, list) + sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; + + /* Mark channel contexts as not being in the driver any more to avoid + * removing them from the driver during the shutdown process... + */ + mutex_lock(&local->chanctx_mtx); + list_for_each_entry(ctx, &local->chanctx_list, list) + ctx->driver_present = false; + mutex_unlock(&local->chanctx_mtx); + + cfg80211_shutdown_all_interfaces(local->hw.wiphy); +} + static void ieee80211_assign_chanctx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { @@ -1520,9 +1558,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) */ res = drv_start(local); if (res) { - WARN(local->suspended, "Hardware became unavailable " - "upon resume. This could be a software issue " - "prior to suspend or a hardware issue.\n"); + if (local->suspended) + WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); + else + WARN(1, "Hardware became unavailable during restart.\n"); + ieee80211_handle_reconfig_failure(local); return res; } diff --git a/net/wireless/core.c b/net/wireless/core.c index 7e023b74f009..39788711ce6e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -210,15 +210,12 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, } } -static int cfg80211_rfkill_set_block(void *data, bool blocked) +void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy) { - struct cfg80211_registered_device *rdev = data; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct wireless_dev *wdev; - if (!blocked) - return 0; - - rtnl_lock(); + ASSERT_RTNL(); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (wdev->netdev) { @@ -234,7 +231,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) break; } } +} +EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces); +static int cfg80211_rfkill_set_block(void *data, bool blocked) +{ + struct cfg80211_registered_device *rdev = data; + + if (!blocked) + return 0; + + rtnl_lock(); + cfg80211_shutdown_all_interfaces(&rdev->wiphy); rtnl_unlock(); return 0; -- cgit v1.2.3 From 80823ca1ba243d145c963212734e359e099051d8 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Fri, 9 May 2014 13:25:41 +0300 Subject: OMAPDSS: remove unused macros Macros to_dss_driver() and to_dss_device() are no longer used, and the latter doesn't even work. Remove them. Signed-off-by: Tomi Valkeinen --- include/video/omapdss.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/video/omapdss.h b/include/video/omapdss.h index ded61a9e5219..f7b9903ee154 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -964,9 +964,6 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi, bool replication, const struct omap_video_timings *mgr_timings, bool mem_to_mem); -#define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver) -#define to_dss_device(x) container_of((x), struct omap_dss_device, old_dev) - int omapdss_compat_init(void); void omapdss_compat_uninit(void); -- cgit v1.2.3 From 29a1f2333e07bbbecb920cc78fd035fe8f53207a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 25 Apr 2014 17:10:06 +0200 Subject: gpio: Add helpers for optional GPIOs Introduce gpiod_get_optional() and gpiod_get_index_optional() helpers that make it easier for drivers to handle optional GPIOs. Currently in order to handle optional GPIOs, a driver needs to special case error handling for -ENOENT, such as this: gpio = gpiod_get(dev, "foo"); if (IS_ERR(gpio)) { if (PTR_ERR(gpio) != -ENOENT) return PTR_ERR(gpio); gpio = NULL; } if (gpio) { /* set up GPIO */ } With these new helpers the above is reduced to: gpio = gpiod_get_optional(dev, "foo"); if (IS_ERR(gpio)) return PTR_ERR(gpio); if (gpio) { /* set up GPIO */ } While at it, device-managed variants of these functions are also provided. Signed-off-by: Thierry Reding Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- Documentation/driver-model/devres.txt | 2 ++ drivers/gpio/devres.c | 43 +++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 43 +++++++++++++++++++++++++++++++++++ include/linux/gpio/consumer.h | 40 ++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+) (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 8ff1167cfedf..10b8c5d2c797 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -312,4 +312,6 @@ SPI GPIO devm_gpiod_get() devm_gpiod_get_index() + devm_gpiod_get_optional() + devm_gpiod_get_index_optional() devm_gpiod_put() diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 307464fd015f..65978cf85f79 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -51,6 +51,22 @@ struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, } EXPORT_SYMBOL(devm_gpiod_get); +/** + * devm_gpiod_get_optional - Resource-managed gpiod_get_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * + * Managed gpiod_get_optional(). GPIO descriptors returned from this function + * are automatically disposed on driver detach. See gpiod_get_optional() for + * detailed information about behavior and return values. + */ +struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev, + const char *con_id) +{ + return devm_gpiod_get_index_optional(dev, con_id, 0); +} +EXPORT_SYMBOL(devm_gpiod_get_optional); + /** * devm_gpiod_get_index - Resource-managed gpiod_get_index() * @dev: GPIO consumer @@ -86,6 +102,33 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, } EXPORT_SYMBOL(devm_gpiod_get_index); +/** + * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer + * + * Managed gpiod_get_index_optional(). GPIO descriptors returned from this + * function are automatically disposed on driver detach. See + * gpiod_get_index_optional() for detailed information about behavior and + * return values. + */ +struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev, + const char *con_id, + unsigned int index) +{ + struct gpio_desc *desc; + + desc = devm_gpiod_get_index(dev, con_id, index); + if (IS_ERR(desc)) { + if (PTR_ERR(desc) == -ENOENT) + return NULL; + } + + return desc; +} +EXPORT_SYMBOL(devm_gpiod_get_index_optional); + /** * devm_gpiod_put - Resource-managed gpiod_put() * @desc: GPIO descriptor to dispose of diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4ad110e793c5..d9c9cb4665db 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2737,6 +2737,22 @@ struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id) } EXPORT_SYMBOL_GPL(gpiod_get); +/** + * gpiod_get_optional - obtain an optional GPIO for a given GPIO function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * + * This is equivalent to gpiod_get(), except that when no GPIO was assigned to + * the requested function it will return NULL. This is convenient for drivers + * that need to handle optional GPIOs. + */ +struct gpio_desc *__must_check gpiod_get_optional(struct device *dev, + const char *con_id) +{ + return gpiod_get_index_optional(dev, con_id, 0); +} +EXPORT_SYMBOL_GPL(gpiod_get_optional); + /** * gpiod_get_index - obtain a GPIO from a multi-index GPIO function * @dev: GPIO consumer, can be NULL for system-global GPIOs @@ -2799,6 +2815,33 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, } EXPORT_SYMBOL_GPL(gpiod_get_index); +/** + * gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO + * function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer + * + * This is equivalent to gpiod_get_index(), except that when no GPIO with the + * specified index was assigned to the requested function it will return NULL. + * This is convenient for drivers that need to handle optional GPIOs. + */ +struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, + const char *con_id, + unsigned int index) +{ + struct gpio_desc *desc; + + desc = gpiod_get_index(dev, con_id, index); + if (IS_ERR(desc)) { + if (PTR_ERR(desc) == -ENOENT) + return NULL; + } + + return desc; +} +EXPORT_SYMBOL_GPL(gpiod_get_index_optional); + /** * gpiod_put - dispose of a GPIO descriptor * @desc: GPIO descriptor to dispose of diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index bed128e8f4b1..6a37ef0dc59c 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -23,6 +23,12 @@ struct gpio_desc *__must_check gpiod_get(struct device *dev, struct gpio_desc *__must_check gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx); +struct gpio_desc *__must_check gpiod_get_optional(struct device *dev, + const char *con_id); +struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, + const char *con_id, + unsigned int index); + void gpiod_put(struct gpio_desc *desc); struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, @@ -30,6 +36,12 @@ struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx); +struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev, + const char *con_id); +struct gpio_desc *__must_check +devm_gpiod_get_index_optional(struct device *dev, const char *con_id, + unsigned int index); + void devm_gpiod_put(struct device *dev, struct gpio_desc *desc); int gpiod_get_direction(const struct gpio_desc *desc); @@ -73,6 +85,20 @@ static inline struct gpio_desc *__must_check gpiod_get_index(struct device *dev, { return ERR_PTR(-ENOSYS); } + +static inline struct gpio_desc *__must_check +gpiod_get_optional(struct device *dev, const char *con_id) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct gpio_desc *__must_check +gpiod_get_index_optional(struct device *dev, const char *con_id, + unsigned int index) +{ + return ERR_PTR(-ENOSYS); +} + static inline void gpiod_put(struct gpio_desc *desc) { might_sleep(); @@ -93,6 +119,20 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, { return ERR_PTR(-ENOSYS); } + +static inline struct gpio_desc *__must_check +devm_gpiod_get_optional(struct device *dev, const char *con_id) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct gpio_desc *__must_check +devm_gpiod_get_index_optional(struct device *dev, const char *con_id, + unsigned int index) +{ + return ERR_PTR(-ENOSYS); +} + static inline void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) { might_sleep(); -- cgit v1.2.3 From d6279d4ae18d4248c90dfcf02fa8c579b81ce43b Mon Sep 17 00:00:00 2001 From: Sathya Prakash M R Date: Mon, 24 Mar 2014 16:31:51 +0530 Subject: OMAPDSS: Add DSS features for AM43xx Add DSS features for AM43xx. Signed-off-by: Sathya Prakash M R Acked-by: Tony Lindgren Signed-off-by: Tomi Valkeinen --- arch/arm/mach-omap2/display.c | 2 + drivers/video/fbdev/omap2/dss/dispc.c | 1 + drivers/video/fbdev/omap2/dss/dpi.c | 2 + drivers/video/fbdev/omap2/dss/dsi.c | 1 + drivers/video/fbdev/omap2/dss/dss.c | 11 +++++ drivers/video/fbdev/omap2/dss/dss_features.c | 67 ++++++++++++++++++++++++++++ include/video/omapdss.h | 1 + 7 files changed, 85 insertions(+) (limited to 'include') diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index 66a2ee030b21..c3f2c66e61f3 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c @@ -279,6 +279,8 @@ static enum omapdss_version __init omap_display_get_version(void) return OMAPDSS_VER_OMAP4; else if (soc_is_omap54xx()) return OMAPDSS_VER_OMAP5; + else if (soc_is_am43xx()) + return OMAPDSS_VER_AM43xx; else return OMAPDSS_VER_UNKNOWN; } diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c index 4fe143166b74..3f612a0895d3 100644 --- a/drivers/video/fbdev/omap2/dss/dispc.c +++ b/drivers/video/fbdev/omap2/dss/dispc.c @@ -3656,6 +3656,7 @@ static int __init dispc_init_features(struct platform_device *pdev) case OMAPDSS_VER_OMAP34xx_ES3: case OMAPDSS_VER_OMAP3630: case OMAPDSS_VER_AM35xx: + case OMAPDSS_VER_AM43xx: src = &omap34xx_rev3_0_dispc_feats; break; diff --git a/drivers/video/fbdev/omap2/dss/dpi.c b/drivers/video/fbdev/omap2/dss/dpi.c index 157921db447a..10745261be52 100644 --- a/drivers/video/fbdev/omap2/dss/dpi.c +++ b/drivers/video/fbdev/omap2/dss/dpi.c @@ -67,6 +67,7 @@ static struct platform_device *dpi_get_dsidev(enum omap_channel channel) case OMAPDSS_VER_OMAP34xx_ES3: case OMAPDSS_VER_OMAP3630: case OMAPDSS_VER_AM35xx: + case OMAPDSS_VER_AM43xx: return NULL; case OMAPDSS_VER_OMAP4430_ES1: @@ -595,6 +596,7 @@ static enum omap_channel dpi_get_channel(void) case OMAPDSS_VER_OMAP34xx_ES3: case OMAPDSS_VER_OMAP3630: case OMAPDSS_VER_AM35xx: + case OMAPDSS_VER_AM43xx: return OMAP_DSS_CHANNEL_LCD; case OMAPDSS_VER_OMAP4430_ES1: diff --git a/drivers/video/fbdev/omap2/dss/dsi.c b/drivers/video/fbdev/omap2/dss/dsi.c index 57f120896b43..90e0eff84867 100644 --- a/drivers/video/fbdev/omap2/dss/dsi.c +++ b/drivers/video/fbdev/omap2/dss/dsi.c @@ -5132,6 +5132,7 @@ static enum omap_channel dsi_get_channel(int module_id) { switch (omapdss_get_version()) { case OMAPDSS_VER_OMAP24xx: + case OMAPDSS_VER_AM43xx: DSSWARN("DSI not supported\n"); return OMAP_DSS_CHANNEL_LCD; diff --git a/drivers/video/fbdev/omap2/dss/dss.c b/drivers/video/fbdev/omap2/dss/dss.c index 71524cb1b423..a6ef4c424ffa 100644 --- a/drivers/video/fbdev/omap2/dss/dss.c +++ b/drivers/video/fbdev/omap2/dss/dss.c @@ -728,6 +728,13 @@ static const struct dss_features omap54xx_dss_feats __initconst = { .dpi_select_source = &dss_dpi_select_source_omap5, }; +static const struct dss_features am43xx_dss_feats __initconst = { + .fck_div_max = 0, + .dss_fck_multiplier = 0, + .parent_clk_name = NULL, + .dpi_select_source = &dss_dpi_select_source_omap2_omap3, +}; + static int __init dss_init_features(struct platform_device *pdev) { const struct dss_features *src; @@ -764,6 +771,10 @@ static int __init dss_init_features(struct platform_device *pdev) src = &omap54xx_dss_feats; break; + case OMAPDSS_VER_AM43xx: + src = &am43xx_dss_feats; + break; + default: return -ENODEV; } diff --git a/drivers/video/fbdev/omap2/dss/dss_features.c b/drivers/video/fbdev/omap2/dss/dss_features.c index 7f8969191dc6..46542f82faef 100644 --- a/drivers/video/fbdev/omap2/dss/dss_features.c +++ b/drivers/video/fbdev/omap2/dss/dss_features.c @@ -93,6 +93,17 @@ static const struct dss_reg_field omap3_dss_reg_fields[] = { [FEAT_REG_DSIPLL_REGM_DSI] = { 26, 23 }, }; +static const struct dss_reg_field am43xx_dss_reg_fields[] = { + [FEAT_REG_FIRHINC] = { 12, 0 }, + [FEAT_REG_FIRVINC] = { 28, 16 }, + [FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 }, + [FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 }, + [FEAT_REG_FIFOSIZE] = { 10, 0 }, + [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, + [FEAT_REG_VERTICALACCU] = { 25, 16 }, + [FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 }, +}; + static const struct dss_reg_field omap4_dss_reg_fields[] = { [FEAT_REG_FIRHINC] = { 12, 0 }, [FEAT_REG_FIRVINC] = { 28, 16 }, @@ -149,6 +160,11 @@ static const enum omap_display_type omap3630_dss_supported_displays[] = { OMAP_DISPLAY_TYPE_VENC, }; +static const enum omap_display_type am43xx_dss_supported_displays[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI, +}; + static const enum omap_display_type omap4_dss_supported_displays[] = { /* OMAP_DSS_CHANNEL_LCD */ OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI, @@ -200,6 +216,11 @@ static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = { OMAP_DSS_OUTPUT_VENC, }; +static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = { + /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI, +}; + static const enum omap_dss_output_id omap4_dss_supported_outputs[] = { /* OMAP_DSS_CHANNEL_LCD */ OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1, @@ -444,6 +465,13 @@ static const struct dss_param_range omap3_dss_param_range[] = { [FEAT_PARAM_LINEWIDTH] = { 1, 1024 }, }; +static const struct dss_param_range am43xx_dss_param_range[] = { + [FEAT_PARAM_DSS_FCK] = { 0, 200000000 }, + [FEAT_PARAM_DSS_PCD] = { 2, 255 }, + [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, + [FEAT_PARAM_LINEWIDTH] = { 1, 1024 }, +}; + static const struct dss_param_range omap4_dss_param_range[] = { [FEAT_PARAM_DSS_FCK] = { 0, 186000000 }, [FEAT_PARAM_DSS_PCD] = { 1, 255 }, @@ -520,6 +548,21 @@ static const enum dss_feat_id am35xx_dss_feat_list[] = { FEAT_OMAP3_DSI_FIFO_BUG, }; +static const enum dss_feat_id am43xx_dss_feat_list[] = { + FEAT_LCDENABLEPOL, + FEAT_LCDENABLESIGNAL, + FEAT_PCKFREEENABLE, + FEAT_FUNCGATED, + FEAT_LINEBUFFERSPLIT, + FEAT_ROWREPEATENABLE, + FEAT_RESIZECONF, + FEAT_CPR, + FEAT_PRELOAD, + FEAT_FIR_COEF_V, + FEAT_ALPHA_FIXED_ZORDER, + FEAT_FIFO_MERGE, +}; + static const enum dss_feat_id omap3630_dss_feat_list[] = { FEAT_LCDENABLEPOL, FEAT_LCDENABLESIGNAL, @@ -682,6 +725,26 @@ static const struct omap_dss_features am35xx_dss_features = { .burst_size_unit = 8, }; +static const struct omap_dss_features am43xx_dss_features = { + .reg_fields = am43xx_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(am43xx_dss_reg_fields), + + .features = am43xx_dss_feat_list, + .num_features = ARRAY_SIZE(am43xx_dss_feat_list), + + .num_mgrs = 1, + .num_ovls = 3, + .supported_displays = am43xx_dss_supported_displays, + .supported_outputs = am43xx_dss_supported_outputs, + .supported_color_modes = omap3_dss_supported_color_modes, + .overlay_caps = omap3430_dss_overlay_caps, + .clksrc_names = omap2_dss_clk_source_names, + .dss_params = am43xx_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA, + .buffer_size_unit = 1, + .burst_size_unit = 8, +}; + static const struct omap_dss_features omap3630_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), @@ -928,6 +991,10 @@ void dss_features_init(enum omapdss_version version) omap_current_dss_features = &am35xx_dss_features; break; + case OMAPDSS_VER_AM43xx: + omap_current_dss_features = &am43xx_dss_features; + break; + default: DSSWARN("Unsupported OMAP version"); break; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index f7b9903ee154..fc06c5b5f12a 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -319,6 +319,7 @@ enum omapdss_version { OMAPDSS_VER_OMAP4430_ES2, /* OMAP4430 ES2.0, 2.1, 2.2 */ OMAPDSS_VER_OMAP4, /* All other OMAP4s */ OMAPDSS_VER_OMAP5, + OMAPDSS_VER_AM43xx, }; /* Board specific data */ -- cgit v1.2.3 From af76e555e5e29e08eb8ac1f7878e23dbf0d6741f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 6 May 2014 12:12:45 +0200 Subject: blk-mq: initialize struct request fields individually This allows us to avoid a non-atomic memset over ->atomic_flags as well as killing lots of duplicate initializations. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-mq.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- include/linux/blkdev.h | 7 ++++--- 2 files changed, 49 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 3fdb097ebe5e..492f49f96459 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -82,9 +82,7 @@ static struct request *__blk_mq_alloc_request(struct blk_mq_hw_ctx *hctx, tag = blk_mq_get_tag(hctx->tags, gfp, reserved); if (tag != BLK_MQ_TAG_FAIL) { rq = hctx->tags->rqs[tag]; - blk_rq_init(hctx->queue, rq); rq->tag = tag; - return rq; } @@ -187,10 +185,54 @@ static void blk_mq_rq_ctx_init(struct request_queue *q, struct blk_mq_ctx *ctx, if (blk_queue_io_stat(q)) rw_flags |= REQ_IO_STAT; + INIT_LIST_HEAD(&rq->queuelist); + /* csd/requeue_work/fifo_time is initialized before use */ + rq->q = q; rq->mq_ctx = ctx; rq->cmd_flags = rw_flags; + rq->cmd_type = 0; + /* do not touch atomic flags, it needs atomic ops against the timer */ + rq->cpu = -1; + rq->__data_len = 0; + rq->__sector = (sector_t) -1; + rq->bio = NULL; + rq->biotail = NULL; + INIT_HLIST_NODE(&rq->hash); + RB_CLEAR_NODE(&rq->rb_node); + memset(&rq->flush, 0, max(sizeof(rq->flush), sizeof(rq->elv))); + rq->rq_disk = NULL; + rq->part = NULL; rq->start_time = jiffies; +#ifdef CONFIG_BLK_CGROUP + rq->rl = NULL; set_start_time_ns(rq); + rq->io_start_time_ns = 0; +#endif + rq->nr_phys_segments = 0; +#if defined(CONFIG_BLK_DEV_INTEGRITY) + rq->nr_integrity_segments = 0; +#endif + rq->ioprio = 0; + rq->special = NULL; + /* tag was already set */ + rq->errors = 0; + memset(rq->__cmd, 0, sizeof(rq->__cmd)); + rq->cmd = rq->__cmd; + rq->cmd_len = BLK_MAX_CDB; + + rq->extra_len = 0; + rq->sense_len = 0; + rq->resid_len = 0; + rq->sense = NULL; + + rq->deadline = 0; + INIT_LIST_HEAD(&rq->timeout_list); + rq->timeout = 0; + rq->retries = 0; + rq->end_io = NULL; + rq->end_io_data = NULL; + rq->next_rq = NULL; + ctx->rq_dispatched[rw_is_sync(rw_flags)]++; } @@ -258,6 +300,7 @@ static void __blk_mq_free_request(struct blk_mq_hw_ctx *hctx, const int tag = rq->tag; struct request_queue *q = rq->q; + clear_bit(REQ_ATOM_STARTED, &rq->atomic_flags); blk_mq_put_tag(hctx->tags, tag); blk_mq_queue_exit(q); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 20b26d4e53a2..94b27210641b 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -90,9 +90,10 @@ enum rq_cmd_type_bits { #define BLK_MAX_CDB 16 /* - * try to put the fields that are referenced together in the same cacheline. - * if you modify this structure, be sure to check block/blk-core.c:blk_rq_init() - * as well! + * Try to put the fields that are referenced together in the same cacheline. + * + * If you modify this structure, make sure to update blk_rq_init() and + * especially blk_mq_rq_ctx_init() to take care of the added fields. */ struct request { struct list_head queuelist; -- cgit v1.2.3 From 4bb659b156996f2993dc16fad71fec9ee070153c Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 9 May 2014 09:36:49 -0600 Subject: blk-mq: implement new and more efficient tagging scheme blk-mq currently uses percpu_ida for tag allocation. But that only works well if the ratio between tag space and number of CPUs is sufficiently high. For most devices and systems, that is not the case. The end result if that we either only utilize the tag space partially, or we end up attempting to fully exhaust it and run into lots of lock contention with stealing between CPUs. This is not optimal. This new tagging scheme is a hybrid bitmap allocator. It uses two tricks to both be SMP friendly and allow full exhaustion of the space: 1) We cache the last allocated (or freed) tag on a per blk-mq software context basis. This allows us to limit the space we have to search. The key element here is not caching it in the shared tag structure, otherwise we end up dirtying more shared cache lines on each allocate/free operation. 2) The tag space is split into cache line sized groups, and each context will start off randomly in that space. Even up to full utilization of the space, this divides the tag users efficiently into cache line groups, avoiding dirtying the same one both between allocators and between allocator and freeer. This scheme shows drastically better behaviour, both on small tag spaces but on large ones as well. It has been tested extensively to show better performance for all the cases blk-mq cares about. Signed-off-by: Jens Axboe --- block/blk-mq-tag.c | 415 ++++++++++++++++++++++++++++++++++++++++--------- block/blk-mq-tag.h | 42 ++++- block/blk-mq.c | 23 ++- block/blk-mq.h | 4 +- include/linux/blk-mq.h | 6 +- 5 files changed, 391 insertions(+), 99 deletions(-) (limited to 'include') diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index 1f43d6ee956f..467f3a20b355 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -1,64 +1,257 @@ #include #include +#include #include #include "blk.h" #include "blk-mq.h" #include "blk-mq-tag.h" -void blk_mq_wait_for_tags(struct blk_mq_tags *tags, bool reserved) +void blk_mq_wait_for_tags(struct blk_mq_tags *tags, struct blk_mq_hw_ctx *hctx, + bool reserved) { - int tag = blk_mq_get_tag(tags, __GFP_WAIT, reserved); - blk_mq_put_tag(tags, tag); + int tag, zero = 0; + + tag = blk_mq_get_tag(tags, hctx, &zero, __GFP_WAIT, reserved); + blk_mq_put_tag(tags, tag, &zero); +} + +static bool bt_has_free_tags(struct blk_mq_bitmap_tags *bt) +{ + int i; + + for (i = 0; i < bt->map_nr; i++) { + struct blk_mq_bitmap *bm = &bt->map[i]; + int ret; + + ret = find_first_zero_bit(&bm->word, bm->depth); + if (ret < bm->depth) + return true; + } + + return false; } bool blk_mq_has_free_tags(struct blk_mq_tags *tags) { - return !tags || - percpu_ida_free_tags(&tags->free_tags, nr_cpu_ids) != 0; + if (!tags) + return true; + + return bt_has_free_tags(&tags->bitmap_tags); +} + +static int __bt_get_word(struct blk_mq_bitmap *bm, unsigned int last_tag) +{ + int tag, org_last_tag, end; + + org_last_tag = last_tag = TAG_TO_BIT(last_tag); + end = bm->depth; + do { +restart: + tag = find_next_zero_bit(&bm->word, end, last_tag); + if (unlikely(tag >= end)) { + /* + * We started with an offset, start from 0 to + * exhaust the map. + */ + if (org_last_tag && last_tag) { + end = last_tag; + last_tag = 0; + goto restart; + } + return -1; + } + last_tag = tag + 1; + } while (test_and_set_bit_lock(tag, &bm->word)); + + return tag; +} + +/* + * Straight forward bitmap tag implementation, where each bit is a tag + * (cleared == free, and set == busy). The small twist is using per-cpu + * last_tag caches, which blk-mq stores in the blk_mq_ctx software queue + * contexts. This enables us to drastically limit the space searched, + * without dirtying an extra shared cacheline like we would if we stored + * the cache value inside the shared blk_mq_bitmap_tags structure. On top + * of that, each word of tags is in a separate cacheline. This means that + * multiple users will tend to stick to different cachelines, at least + * until the map is exhausted. + */ +static int __bt_get(struct blk_mq_bitmap_tags *bt, unsigned int *tag_cache) +{ + unsigned int last_tag, org_last_tag; + int index, i, tag; + + last_tag = org_last_tag = *tag_cache; + index = TAG_TO_INDEX(last_tag); + + for (i = 0; i < bt->map_nr; i++) { + tag = __bt_get_word(&bt->map[index], last_tag); + if (tag != -1) { + tag += index * BITS_PER_LONG; + goto done; + } + + last_tag = 0; + if (++index >= bt->map_nr) + index = 0; + } + + *tag_cache = 0; + return -1; + + /* + * Only update the cache from the allocation path, if we ended + * up using the specific cached tag. + */ +done: + if (tag == org_last_tag) { + last_tag = tag + 1; + if (last_tag >= bt->depth - 1) + last_tag = 0; + + *tag_cache = last_tag; + } + + return tag; +} + +static inline void bt_index_inc(unsigned int *index) +{ + *index = (*index + 1) & (BT_WAIT_QUEUES - 1); +} + +static struct bt_wait_state *bt_wait_ptr(struct blk_mq_bitmap_tags *bt, + struct blk_mq_hw_ctx *hctx) +{ + struct bt_wait_state *bs; + + if (!hctx) + return &bt->bs[0]; + + bs = &bt->bs[hctx->wait_index]; + bt_index_inc(&hctx->wait_index); + return bs; } -static unsigned int __blk_mq_get_tag(struct blk_mq_tags *tags, gfp_t gfp) +static int bt_get(struct blk_mq_bitmap_tags *bt, struct blk_mq_hw_ctx *hctx, + unsigned int *last_tag, gfp_t gfp) { + struct bt_wait_state *bs; + DEFINE_WAIT(wait); int tag; - tag = percpu_ida_alloc(&tags->free_tags, (gfp & __GFP_WAIT) ? - TASK_UNINTERRUPTIBLE : TASK_RUNNING); - if (tag < 0) - return BLK_MQ_TAG_FAIL; - return tag + tags->nr_reserved_tags; + tag = __bt_get(bt, last_tag); + if (tag != -1) + return tag; + + if (!(gfp & __GFP_WAIT)) + return -1; + + bs = bt_wait_ptr(bt, hctx); + do { + bool was_empty; + + was_empty = list_empty(&wait.task_list); + prepare_to_wait(&bs->wait, &wait, TASK_UNINTERRUPTIBLE); + + tag = __bt_get(bt, last_tag); + if (tag != -1) + break; + + if (was_empty) + atomic_set(&bs->wait_cnt, bt->wake_cnt); + + io_schedule(); + } while (1); + + finish_wait(&bs->wait, &wait); + return tag; +} + +static unsigned int __blk_mq_get_tag(struct blk_mq_tags *tags, + struct blk_mq_hw_ctx *hctx, + unsigned int *last_tag, gfp_t gfp) +{ + int tag; + + tag = bt_get(&tags->bitmap_tags, hctx, last_tag, gfp); + if (tag >= 0) + return tag + tags->nr_reserved_tags; + + return BLK_MQ_TAG_FAIL; } static unsigned int __blk_mq_get_reserved_tag(struct blk_mq_tags *tags, gfp_t gfp) { - int tag; + int tag, zero = 0; if (unlikely(!tags->nr_reserved_tags)) { WARN_ON_ONCE(1); return BLK_MQ_TAG_FAIL; } - tag = percpu_ida_alloc(&tags->reserved_tags, (gfp & __GFP_WAIT) ? - TASK_UNINTERRUPTIBLE : TASK_RUNNING); + tag = bt_get(&tags->breserved_tags, NULL, &zero, gfp); if (tag < 0) return BLK_MQ_TAG_FAIL; + return tag; } -unsigned int blk_mq_get_tag(struct blk_mq_tags *tags, gfp_t gfp, bool reserved) +unsigned int blk_mq_get_tag(struct blk_mq_tags *tags, + struct blk_mq_hw_ctx *hctx, unsigned int *last_tag, + gfp_t gfp, bool reserved) { if (!reserved) - return __blk_mq_get_tag(tags, gfp); + return __blk_mq_get_tag(tags, hctx, last_tag, gfp); return __blk_mq_get_reserved_tag(tags, gfp); } +static struct bt_wait_state *bt_wake_ptr(struct blk_mq_bitmap_tags *bt) +{ + int i, wake_index; + + wake_index = bt->wake_index; + for (i = 0; i < BT_WAIT_QUEUES; i++) { + struct bt_wait_state *bs = &bt->bs[wake_index]; + + if (waitqueue_active(&bs->wait)) { + if (wake_index != bt->wake_index) + bt->wake_index = wake_index; + + return bs; + } + + bt_index_inc(&wake_index); + } + + return NULL; +} + +static void bt_clear_tag(struct blk_mq_bitmap_tags *bt, unsigned int tag) +{ + const int index = TAG_TO_INDEX(tag); + struct bt_wait_state *bs; + + clear_bit(TAG_TO_BIT(tag), &bt->map[index].word); + + bs = bt_wake_ptr(bt); + if (bs && atomic_dec_and_test(&bs->wait_cnt)) { + smp_mb__after_clear_bit(); + atomic_set(&bs->wait_cnt, bt->wake_cnt); + bt_index_inc(&bt->wake_index); + wake_up(&bs->wait); + } +} + static void __blk_mq_put_tag(struct blk_mq_tags *tags, unsigned int tag) { BUG_ON(tag >= tags->nr_tags); - percpu_ida_free(&tags->free_tags, tag - tags->nr_reserved_tags); + bt_clear_tag(&tags->bitmap_tags, tag); } static void __blk_mq_put_reserved_tag(struct blk_mq_tags *tags, @@ -66,22 +259,41 @@ static void __blk_mq_put_reserved_tag(struct blk_mq_tags *tags, { BUG_ON(tag >= tags->nr_reserved_tags); - percpu_ida_free(&tags->reserved_tags, tag); + bt_clear_tag(&tags->breserved_tags, tag); } -void blk_mq_put_tag(struct blk_mq_tags *tags, unsigned int tag) +void blk_mq_put_tag(struct blk_mq_tags *tags, unsigned int tag, + unsigned int *last_tag) { - if (tag >= tags->nr_reserved_tags) - __blk_mq_put_tag(tags, tag); - else + if (tag >= tags->nr_reserved_tags) { + const int real_tag = tag - tags->nr_reserved_tags; + + __blk_mq_put_tag(tags, real_tag); + *last_tag = real_tag; + } else __blk_mq_put_reserved_tag(tags, tag); } -static int __blk_mq_tag_iter(unsigned id, void *data) +static void bt_for_each_free(struct blk_mq_bitmap_tags *bt, + unsigned long *free_map, unsigned int off) { - unsigned long *tag_map = data; - __set_bit(id, tag_map); - return 0; + int i; + + for (i = 0; i < bt->map_nr; i++) { + struct blk_mq_bitmap *bm = &bt->map[i]; + int bit = 0; + + do { + bit = find_next_zero_bit(&bm->word, bm->depth, bit); + if (bit >= bm->depth) + break; + + __set_bit(bit + off, free_map); + bit++; + } while (1); + + off += BITS_PER_LONG; + } } void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, @@ -95,21 +307,98 @@ void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, if (!tag_map) return; - percpu_ida_for_each_free(&tags->free_tags, __blk_mq_tag_iter, tag_map); + bt_for_each_free(&tags->bitmap_tags, tag_map, tags->nr_reserved_tags); if (tags->nr_reserved_tags) - percpu_ida_for_each_free(&tags->reserved_tags, __blk_mq_tag_iter, - tag_map); + bt_for_each_free(&tags->breserved_tags, tag_map, 0); fn(data, tag_map); kfree(tag_map); } +static unsigned int bt_unused_tags(struct blk_mq_bitmap_tags *bt) +{ + unsigned int i, used; + + for (i = 0, used = 0; i < bt->map_nr; i++) { + struct blk_mq_bitmap *bm = &bt->map[i]; + + used += bitmap_weight(&bm->word, bm->depth); + } + + return bt->depth - used; +} + +static int bt_alloc(struct blk_mq_bitmap_tags *bt, unsigned int depth, + int node, bool reserved) +{ + int i; + + /* + * Depth can be zero for reserved tags, that's not a failure + * condition. + */ + if (depth) { + int nr, i, map_depth; + + nr = ALIGN(depth, BITS_PER_LONG) / BITS_PER_LONG; + bt->map = kzalloc_node(nr * sizeof(struct blk_mq_bitmap), + GFP_KERNEL, node); + if (!bt->map) + return -ENOMEM; + + bt->map_nr = nr; + map_depth = depth; + for (i = 0; i < nr; i++) { + bt->map[i].depth = min(map_depth, BITS_PER_LONG); + map_depth -= BITS_PER_LONG; + } + } + + bt->bs = kzalloc(BT_WAIT_QUEUES * sizeof(*bt->bs), GFP_KERNEL); + if (!bt->bs) { + kfree(bt->map); + return -ENOMEM; + } + + for (i = 0; i < BT_WAIT_QUEUES; i++) + init_waitqueue_head(&bt->bs[i].wait); + + bt->wake_cnt = BT_WAIT_BATCH; + if (bt->wake_cnt > depth / 4) + bt->wake_cnt = max(1U, depth / 4); + + bt->depth = depth; + return 0; +} + +static void bt_free(struct blk_mq_bitmap_tags *bt) +{ + kfree(bt->map); + kfree(bt->bs); +} + +static struct blk_mq_tags *blk_mq_init_bitmap_tags(struct blk_mq_tags *tags, + int node) +{ + unsigned int depth = tags->nr_tags - tags->nr_reserved_tags; + + if (bt_alloc(&tags->bitmap_tags, depth, node, false)) + goto enomem; + if (bt_alloc(&tags->breserved_tags, tags->nr_reserved_tags, node, true)) + goto enomem; + + return tags; +enomem: + bt_free(&tags->bitmap_tags); + kfree(tags); + return NULL; +} + struct blk_mq_tags *blk_mq_init_tags(unsigned int total_tags, unsigned int reserved_tags, int node) { unsigned int nr_tags, nr_cache; struct blk_mq_tags *tags; - int ret; if (total_tags > BLK_MQ_TAG_MAX) { pr_err("blk-mq: tag depth too large\n"); @@ -121,72 +410,46 @@ struct blk_mq_tags *blk_mq_init_tags(unsigned int total_tags, return NULL; nr_tags = total_tags - reserved_tags; - nr_cache = nr_tags / num_possible_cpus(); - - if (nr_cache < BLK_MQ_TAG_CACHE_MIN) - nr_cache = BLK_MQ_TAG_CACHE_MIN; - else if (nr_cache > BLK_MQ_TAG_CACHE_MAX) - nr_cache = BLK_MQ_TAG_CACHE_MAX; + nr_cache = nr_tags / num_online_cpus(); tags->nr_tags = total_tags; tags->nr_reserved_tags = reserved_tags; - tags->nr_max_cache = nr_cache; - tags->nr_batch_move = max(1u, nr_cache / 2); - - ret = __percpu_ida_init(&tags->free_tags, tags->nr_tags - - tags->nr_reserved_tags, - tags->nr_max_cache, - tags->nr_batch_move); - if (ret) - goto err_free_tags; - - if (reserved_tags) { - /* - * With max_cahe and batch set to 1, the allocator fallbacks to - * no cached. It's fine reserved tags allocation is slow. - */ - ret = __percpu_ida_init(&tags->reserved_tags, reserved_tags, - 1, 1); - if (ret) - goto err_reserved_tags; - } - return tags; - -err_reserved_tags: - percpu_ida_destroy(&tags->free_tags); -err_free_tags: - kfree(tags); - return NULL; + return blk_mq_init_bitmap_tags(tags, node); } void blk_mq_free_tags(struct blk_mq_tags *tags) { - percpu_ida_destroy(&tags->free_tags); - percpu_ida_destroy(&tags->reserved_tags); + bt_free(&tags->bitmap_tags); + bt_free(&tags->breserved_tags); kfree(tags); } +void blk_mq_tag_init_last_tag(struct blk_mq_tags *tags, unsigned int *tag) +{ + unsigned int depth = tags->nr_tags - tags->nr_reserved_tags; + + if (depth > 1) + *tag = prandom_u32() % (depth - 1); + else + *tag = 0; +} + ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page) { char *orig_page = page; - unsigned int cpu; + unsigned int free, res; if (!tags) return 0; - page += sprintf(page, "nr_tags=%u, reserved_tags=%u, batch_move=%u," - " max_cache=%u\n", tags->nr_tags, tags->nr_reserved_tags, - tags->nr_batch_move, tags->nr_max_cache); + page += sprintf(page, "nr_tags=%u, reserved_tags=%u\n", + tags->nr_tags, tags->nr_reserved_tags); - page += sprintf(page, "nr_free=%u, nr_reserved=%u\n", - percpu_ida_free_tags(&tags->free_tags, nr_cpu_ids), - percpu_ida_free_tags(&tags->reserved_tags, nr_cpu_ids)); + free = bt_unused_tags(&tags->bitmap_tags); + res = bt_unused_tags(&tags->breserved_tags); - for_each_possible_cpu(cpu) { - page += sprintf(page, " cpu%02u: nr_free=%u\n", cpu, - percpu_ida_free_tags(&tags->free_tags, cpu)); - } + page += sprintf(page, "nr_free=%u, nr_reserved=%u\n", free, res); return page - orig_page; } diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h index c8e0645ea331..06d4a2f0f7a0 100644 --- a/block/blk-mq-tag.h +++ b/block/blk-mq-tag.h @@ -1,7 +1,34 @@ #ifndef INT_BLK_MQ_TAG_H #define INT_BLK_MQ_TAG_H -#include +enum { + BT_WAIT_QUEUES = 8, + BT_WAIT_BATCH = 8, +}; + +struct bt_wait_state { + atomic_t wait_cnt; + wait_queue_head_t wait; +} ____cacheline_aligned_in_smp; + +#define TAG_TO_INDEX(tag) ((tag) / BITS_PER_LONG) +#define TAG_TO_BIT(tag) ((tag) & (BITS_PER_LONG - 1)) + +struct blk_mq_bitmap { + unsigned long word; + unsigned long depth; +} ____cacheline_aligned_in_smp; + +struct blk_mq_bitmap_tags { + unsigned int depth; + unsigned int wake_cnt; + + struct blk_mq_bitmap *map; + unsigned int map_nr; + + unsigned int wake_index; + struct bt_wait_state *bs; +}; /* * Tag address space map. @@ -9,11 +36,9 @@ struct blk_mq_tags { unsigned int nr_tags; unsigned int nr_reserved_tags; - unsigned int nr_batch_move; - unsigned int nr_max_cache; - struct percpu_ida free_tags; - struct percpu_ida reserved_tags; + struct blk_mq_bitmap_tags bitmap_tags; + struct blk_mq_bitmap_tags breserved_tags; struct request **rqs; struct list_head page_list; @@ -23,12 +48,13 @@ struct blk_mq_tags { extern struct blk_mq_tags *blk_mq_init_tags(unsigned int nr_tags, unsigned int reserved_tags, int node); extern void blk_mq_free_tags(struct blk_mq_tags *tags); -extern unsigned int blk_mq_get_tag(struct blk_mq_tags *tags, gfp_t gfp, bool reserved); -extern void blk_mq_wait_for_tags(struct blk_mq_tags *tags, bool reserved); -extern void blk_mq_put_tag(struct blk_mq_tags *tags, unsigned int tag); +extern unsigned int blk_mq_get_tag(struct blk_mq_tags *tags, struct blk_mq_hw_ctx *hctx, unsigned int *last_tag, gfp_t gfp, bool reserved); +extern void blk_mq_wait_for_tags(struct blk_mq_tags *tags, struct blk_mq_hw_ctx *hctx, bool reserved); +extern void blk_mq_put_tag(struct blk_mq_tags *tags, unsigned int tag, unsigned int *last_tag); extern void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, void (*fn)(void *data, unsigned long *), void *data); extern bool blk_mq_has_free_tags(struct blk_mq_tags *tags); extern ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page); +extern void blk_mq_tag_init_last_tag(struct blk_mq_tags *tags, unsigned int *last_tag); enum { BLK_MQ_TAG_CACHE_MIN = 1, diff --git a/block/blk-mq.c b/block/blk-mq.c index 492f49f96459..9f07a266f7ab 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -74,12 +74,13 @@ static void blk_mq_hctx_mark_pending(struct blk_mq_hw_ctx *hctx, } static struct request *__blk_mq_alloc_request(struct blk_mq_hw_ctx *hctx, + struct blk_mq_ctx *ctx, gfp_t gfp, bool reserved) { struct request *rq; unsigned int tag; - tag = blk_mq_get_tag(hctx->tags, gfp, reserved); + tag = blk_mq_get_tag(hctx->tags, hctx, &ctx->last_tag, gfp, reserved); if (tag != BLK_MQ_TAG_FAIL) { rq = hctx->tags->rqs[tag]; rq->tag = tag; @@ -246,7 +247,8 @@ static struct request *blk_mq_alloc_request_pinned(struct request_queue *q, struct blk_mq_ctx *ctx = blk_mq_get_ctx(q); struct blk_mq_hw_ctx *hctx = q->mq_ops->map_queue(q, ctx->cpu); - rq = __blk_mq_alloc_request(hctx, gfp & ~__GFP_WAIT, reserved); + rq = __blk_mq_alloc_request(hctx, ctx, gfp & ~__GFP_WAIT, + reserved); if (rq) { blk_mq_rq_ctx_init(q, ctx, rq, rw); break; @@ -260,7 +262,7 @@ static struct request *blk_mq_alloc_request_pinned(struct request_queue *q, break; } - blk_mq_wait_for_tags(hctx->tags, reserved); + blk_mq_wait_for_tags(hctx->tags, hctx, reserved); } while (1); return rq; @@ -278,6 +280,7 @@ struct request *blk_mq_alloc_request(struct request_queue *q, int rw, gfp_t gfp) blk_mq_put_ctx(rq->mq_ctx); return rq; } +EXPORT_SYMBOL(blk_mq_alloc_request); struct request *blk_mq_alloc_reserved_request(struct request_queue *q, int rw, gfp_t gfp) @@ -301,7 +304,7 @@ static void __blk_mq_free_request(struct blk_mq_hw_ctx *hctx, struct request_queue *q = rq->q; clear_bit(REQ_ATOM_STARTED, &rq->atomic_flags); - blk_mq_put_tag(hctx->tags, tag); + blk_mq_put_tag(hctx->tags, tag, &ctx->last_tag); blk_mq_queue_exit(q); } @@ -677,11 +680,6 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) queued++; continue; case BLK_MQ_RQ_QUEUE_BUSY: - /* - * FIXME: we should have a mechanism to stop the queue - * like blk_stop_queue, otherwise we will waste cpu - * time - */ list_add(&rq->queuelist, &rq_list); __blk_mq_requeue_request(rq); break; @@ -873,6 +871,7 @@ static void __blk_mq_insert_request(struct blk_mq_hw_ctx *hctx, list_add(&rq->queuelist, &ctx->rq_list); else list_add_tail(&rq->queuelist, &ctx->rq_list); + blk_mq_hctx_mark_pending(hctx, ctx); /* @@ -1046,7 +1045,7 @@ static void blk_mq_make_request(struct request_queue *q, struct bio *bio) if (is_sync) rw |= REQ_SYNC; trace_block_getrq(q, bio, rw); - rq = __blk_mq_alloc_request(hctx, GFP_ATOMIC, false); + rq = __blk_mq_alloc_request(hctx, ctx, GFP_ATOMIC, false); if (likely(rq)) blk_mq_rq_ctx_init(q, ctx, rq, rw); else { @@ -1130,8 +1129,8 @@ EXPORT_SYMBOL(blk_mq_map_queue); struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_tag_set *set, unsigned int hctx_index) { - return kmalloc_node(sizeof(struct blk_mq_hw_ctx), - GFP_KERNEL | __GFP_ZERO, set->numa_node); + return kzalloc_node(sizeof(struct blk_mq_hw_ctx), GFP_KERNEL, + set->numa_node); } EXPORT_SYMBOL(blk_mq_alloc_single_hw_queue); diff --git a/block/blk-mq.h b/block/blk-mq.h index 1ae364ceaf8b..97cfab9c092f 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -12,6 +12,8 @@ struct blk_mq_ctx { unsigned int cpu; unsigned int index_hw; + unsigned int last_tag ____cacheline_aligned_in_smp; + /* incremented at dispatch time */ unsigned long rq_dispatched[2]; unsigned long rq_merged; @@ -21,7 +23,7 @@ struct blk_mq_ctx { struct request_queue *queue; struct kobject kobj; -}; +} ____cacheline_aligned_in_smp; void __blk_mq_complete_request(struct request *rq); void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async); diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 5bd677e2dcb7..f83d15f6e1c1 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -31,10 +31,12 @@ struct blk_mq_hw_ctx { void *driver_data; - unsigned int nr_ctx; - struct blk_mq_ctx **ctxs; unsigned int nr_ctx_map; unsigned long *ctx_map; + unsigned int nr_ctx; + struct blk_mq_ctx **ctxs; + + unsigned int wait_index; struct blk_mq_tags *tags; -- cgit v1.2.3 From 2070d50e1cbe3d7f157cbf8e63279c893f375d7f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 9 May 2014 15:11:53 -0400 Subject: percpu-refcount: rename percpu_ref_tryget() to percpu_ref_tryget_live() percpu_ref_tryget() is different from the usual tryget semantics in that it fails if the refcnt is in its dying stage even if the refcnt hasn't reached zero yet. We're about to introduce the more conventional tryget and the current one has only one user. Let's rename it to percpu_ref_tryget_live() so that it explicitly signifies the peculiarities of its semantics. This is pure rename. Signed-off-by: Tejun Heo Acked-by: Kent Overstreet --- include/linux/cgroup.h | 2 +- include/linux/percpu-refcount.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c2515851c1aa..549aed8de32b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -101,7 +101,7 @@ static inline bool css_tryget(struct cgroup_subsys_state *css) { if (css->flags & CSS_ROOT) return true; - return percpu_ref_tryget(&css->refcnt); + return percpu_ref_tryget_live(&css->refcnt); } /** diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index 95961f0bf62d..e22d15597cc3 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -118,7 +118,7 @@ static inline void percpu_ref_get(struct percpu_ref *ref) } /** - * percpu_ref_tryget - try to increment a percpu refcount + * percpu_ref_tryget_live - try to increment a live percpu refcount * @ref: percpu_ref to try-get * * Increment a percpu refcount unless it has already been killed. Returns @@ -129,7 +129,7 @@ static inline void percpu_ref_get(struct percpu_ref *ref) * used. After the confirm_kill callback is invoked, it's guaranteed that * no new reference will be given out by percpu_ref_tryget(). */ -static inline bool percpu_ref_tryget(struct percpu_ref *ref) +static inline bool percpu_ref_tryget_live(struct percpu_ref *ref) { unsigned __percpu *pcpu_count; int ret = false; -- cgit v1.2.3 From 4fb6e25049cb6fa0accc7f1b7c192b952fad7ac8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 9 May 2014 15:11:53 -0400 Subject: percpu-refcount: implement percpu_ref_tryget() Implement percpu_ref_tryget() which fails if the refcnt already reached zero. Note that this is different from the recently renamed percpu_ref_tryget_live() which fails if the refcnt has been killed and is draining the remaining references. percpu_ref_tryget() succeeds on a killed refcnt as long as its current refcnt is above zero. Signed-off-by: Tejun Heo Acked-by: Kent Overstreet --- include/linux/percpu-refcount.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'include') diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index e22d15597cc3..dba35c411e8c 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -117,6 +117,36 @@ static inline void percpu_ref_get(struct percpu_ref *ref) rcu_read_unlock_sched(); } +/** + * percpu_ref_tryget - try to increment a percpu refcount + * @ref: percpu_ref to try-get + * + * Increment a percpu refcount unless its count already reached zero. + * Returns %true on success; %false on failure. + * + * The caller is responsible for ensuring that @ref stays accessible. + */ +static inline bool percpu_ref_tryget(struct percpu_ref *ref) +{ + unsigned __percpu *pcpu_count; + int ret = false; + + rcu_read_lock_sched(); + + pcpu_count = ACCESS_ONCE(ref->pcpu_count); + + if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) { + __this_cpu_inc(*pcpu_count); + ret = true; + } else { + ret = atomic_inc_not_zero(&ref->count); + } + + rcu_read_unlock_sched(); + + return ret; +} + /** * percpu_ref_tryget_live - try to increment a live percpu refcount * @ref: percpu_ref to try-get @@ -128,6 +158,8 @@ static inline void percpu_ref_get(struct percpu_ref *ref) * will fail. For such guarantee, percpu_ref_kill_and_confirm() should be * used. After the confirm_kill callback is invoked, it's guaranteed that * no new reference will be given out by percpu_ref_tryget(). + * + * The caller is responsible for ensuring that @ref stays accessible. */ static inline bool percpu_ref_tryget_live(struct percpu_ref *ref) { -- cgit v1.2.3 From 49c50b97b5522a987b80fbbf9d9869deee8d23b0 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 6 May 2014 16:02:19 -0700 Subject: mtd: nand: refactor erase_cmd() to return chip status The nand_chip::erase_cmd callback previously served a dual purpose; for one, it allowed a per-flash-chip override, so that AG-AND devices could use a different erase command than other NAND. These AND devices were dropped in commit 14c6578683367b1e7af0c3c09e872b45a45183a7 (mtd: nand: remove AG-AND support). On the other hand, some drivers (denali and doc-g4) need to use this sort of callback to implement controller-specific erase operations. To make the latter operation easier for some drivers (e.g., ST's new BCH NAND driver), it helps if the command dispatch and wait functions can be lumped together, rather than called separately. This patch does two things: 1. Pull the call to chip->waitfunc() into chip->erase_cmd(), and return the status from this callback 2. Rename erase_cmd() to just erase(), since this callback does a little more than just send a command Signed-off-by: Brian Norris Tested-by: Lee Jones --- drivers/mtd/nand/denali.c | 7 +++---- drivers/mtd/nand/docg4.c | 6 ++++-- drivers/mtd/nand/nand_base.c | 14 +++++++------- include/linux/mtd/nand.h | 5 ++--- 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index c07cd573ad3a..9f2012a3e764 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1233,7 +1233,7 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) return status; } -static void denali_erase(struct mtd_info *mtd, int page) +static int denali_erase(struct mtd_info *mtd, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); @@ -1250,8 +1250,7 @@ static void denali_erase(struct mtd_info *mtd, int page) irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP | INTR_STATUS__ERASE_FAIL); - denali->status = (irq_status & INTR_STATUS__ERASE_FAIL) ? - NAND_STATUS_FAIL : PASS; + return (irq_status & INTR_STATUS__ERASE_FAIL) ? NAND_STATUS_FAIL : PASS; } static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, @@ -1584,7 +1583,7 @@ int denali_init(struct denali_nand_info *denali) denali->nand.ecc.write_page_raw = denali_write_page_raw; denali->nand.ecc.read_oob = denali_read_oob; denali->nand.ecc.write_oob = denali_write_oob; - denali->nand.erase_cmd = denali_erase; + denali->nand.erase = denali_erase; if (nand_scan_tail(&denali->mtd)) { ret = -ENXIO; diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 1b0265e85a06..ce24637e14f1 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -872,7 +872,7 @@ static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, return 0; } -static void docg4_erase_block(struct mtd_info *mtd, int page) +static int docg4_erase_block(struct mtd_info *mtd, int page) { struct nand_chip *nand = mtd->priv; struct docg4_priv *doc = nand->priv; @@ -916,6 +916,8 @@ static void docg4_erase_block(struct mtd_info *mtd, int page) write_nop(docptr); poll_status(doc); write_nop(docptr); + + return nand->waitfunc(mtd, nand); } static int write_page(struct mtd_info *mtd, struct nand_chip *nand, @@ -1236,7 +1238,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->block_markbad = docg4_block_markbad; nand->read_buf = docg4_read_buf; nand->write_buf = docg4_write_buf16; - nand->erase_cmd = docg4_erase_block; + nand->erase = docg4_erase_block; nand->ecc.read_page = docg4_read_page; nand->ecc.write_page = docg4_write_page; nand->ecc.read_page_raw = docg4_read_page_raw; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index f6c5685b79a6..7853b9b0a05e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2617,18 +2617,20 @@ out: } /** - * single_erase_cmd - [GENERIC] NAND standard block erase command function + * single_erase - [GENERIC] NAND standard block erase command function * @mtd: MTD device structure * @page: the page address of the block which will be erased * - * Standard erase command for NAND chips. + * Standard erase command for NAND chips. Returns NAND status. */ -static void single_erase_cmd(struct mtd_info *mtd, int page) +static int single_erase(struct mtd_info *mtd, int page) { struct nand_chip *chip = mtd->priv; /* Send commands to erase a block */ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + + return chip->waitfunc(mtd, chip); } /** @@ -2709,9 +2711,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, (page + pages_per_block)) chip->pagebuf = -1; - chip->erase_cmd(mtd, page & chip->pagemask); - - status = chip->waitfunc(mtd, chip); + status = chip->erase(mtd, page & chip->pagemask); /* * See if operation failed and additional status checks are @@ -3684,7 +3684,7 @@ ident_done: } chip->badblockbits = 8; - chip->erase_cmd = single_erase_cmd; + chip->erase = single_erase; /* Do not replace user supplied command function! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 450d61ec7f06..7a922e6c4e4b 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -552,8 +552,7 @@ struct nand_buffers { * @ecc: [BOARDSPECIFIC] ECC control structure * @buffers: buffer structure for read/write * @hwcontrol: platform-specific hardware control structure - * @erase_cmd: [INTERN] erase command write function, selectable due - * to AND support. + * @erase: [REPLACEABLE] erase function * @scan_bbt: [REPLACEABLE] function to scan bad block table * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring * data from array to read regs (tR). @@ -637,7 +636,7 @@ struct nand_chip { void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); - void (*erase_cmd)(struct mtd_info *mtd, int page); + int (*erase)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); -- cgit v1.2.3 From 5a134faeef82b46ff4ad244d11d8c6be41679834 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Fri, 9 May 2014 21:35:28 +0200 Subject: Bluetooth: Store TX power level for connection This patch adds support to store local TX power level for connection when reply for HCI_Read_Transmit_Power_Level is received. Signed-off-by: Andrzej Kaczmarek Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 11 +++++++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 1 + net/bluetooth/hci_event.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ad2ecc92380d..16587dcd6a91 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1054,6 +1054,17 @@ struct hci_cp_write_page_scan_activity { __le16 window; } __packed; +#define HCI_OP_READ_TX_POWER 0x0c2d +struct hci_cp_read_tx_power { + __le16 handle; + __u8 type; +} __packed; +struct hci_rp_read_tx_power { + __u8 status; + __le16 handle; + __s8 tx_power; +} __packed; + #define HCI_OP_READ_PAGE_SCAN_TYPE 0x0c46 struct hci_rp_read_page_scan_type { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0318d5263837..211bad6a3366 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -375,6 +375,7 @@ struct hci_conn { __u16 le_conn_min_interval; __u16 le_conn_max_interval; __s8 rssi; + __s8 tx_power; unsigned long flags; __u8 remote_cap; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 55a174317925..74b368bfe102 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -407,6 +407,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->io_capability = hdev->io_capability; conn->remote_auth = 0xff; conn->key_type = 0xff; + conn->tx_power = HCI_TX_POWER_INVALID; set_bit(HCI_CONN_POWER_SAVE, &conn->flags); conn->disc_timeout = HCI_DISCONN_TIMEOUT; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2bb0053d4c45..fa614e3430a7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1264,6 +1264,30 @@ static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); } +static void hci_cc_read_tx_power(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_cp_read_tx_power *sent; + struct hci_rp_read_tx_power *rp = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + sent = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER); + if (!sent) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn && sent->type == 0x00) + conn->tx_power = rp->tx_power; + + hci_dev_unlock(hdev); +} + static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -2660,6 +2684,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_rssi(hdev, skb); break; + case HCI_OP_READ_TX_POWER: + hci_cc_read_tx_power(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; -- cgit v1.2.3 From f2727f7eb9132803d06309839a95de3dad82d237 Mon Sep 17 00:00:00 2001 From: Dimitri John Ledkov Date: Wed, 7 May 2014 20:55:30 +0100 Subject: NVMe: Update namespace and controller identify structures to the 1.1a spec Controller: add CNTLID, AVSCC, APSTA, NVSCC, ACWU, SGLS fields. Namespace: add NMIC, RESCAP, EUI64 fields. EUI64 is specifically interesting, since it can be used to construct an UEFI NVMe device path for a boot entry. As per NVM Express 1.1a spec: http://www.nvmexpress.org/wp-content/uploads/NVM-Express-1_1a.pdf Signed-off-by: Dimitri John Ledkov Signed-off-by: Matthew Wilcox --- include/uapi/linux/nvme.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nvme.h b/include/uapi/linux/nvme.h index f090336d5bad..e74da782d69c 100644 --- a/include/uapi/linux/nvme.h +++ b/include/uapi/linux/nvme.h @@ -45,7 +45,8 @@ struct nvme_id_ctrl { __u8 ieee[3]; __u8 mic; __u8 mdts; - __u8 rsvd78[178]; + __u16 cntlid; + __u8 rsvd80[176]; __le16 oacs; __u8 acl; __u8 aerl; @@ -53,7 +54,9 @@ struct nvme_id_ctrl { __u8 lpa; __u8 elpe; __u8 npss; - __u8 rsvd264[248]; + __u8 avscc; + __u8 apsta; + __u8 rsvd266[246]; __u8 sqes; __u8 cqes; __u8 rsvd514[2]; @@ -64,7 +67,12 @@ struct nvme_id_ctrl { __u8 vwc; __le16 awun; __le16 awupf; - __u8 rsvd530[1518]; + __u8 nvscc; + __u8 rsvd531; + __le16 acwu; + __u8 rsvd534[2]; + __le32 sgls; + __u8 rsvd540[1508]; struct nvme_id_power_state psd[32]; __u8 vs[1024]; }; @@ -92,7 +100,10 @@ struct nvme_id_ns { __u8 mc; __u8 dpc; __u8 dps; - __u8 rsvd30[98]; + __u8 nmic; + __u8 rescap; + __u8 rsvd32[88]; + __u8 eui64[8]; struct nvme_lbaf lbaf[16]; __u8 rsvd192[192]; __u8 vs[3712]; -- cgit v1.2.3 From 5efaf09021a5817e5a274aa2d2fad8d92d12ed92 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 21 Apr 2014 12:07:27 +0800 Subject: clk: hisi: add clk-hix5hd2.c Signed-off-by: Haifeng Yan Signed-off-by: Zhangfei Gao Signed-off-by: Haojian Zhuang --- .../devicetree/bindings/clock/hix5hd2-clock.txt | 31 +++++++ drivers/clk/Makefile | 1 + drivers/clk/hisilicon/Makefile | 1 + drivers/clk/hisilicon/clk-hix5hd2.c | 101 +++++++++++++++++++++ include/dt-bindings/clock/hix5hd2-clock.h | 58 ++++++++++++ 5 files changed, 192 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/hix5hd2-clock.txt create mode 100644 drivers/clk/hisilicon/clk-hix5hd2.c create mode 100644 include/dt-bindings/clock/hix5hd2-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/hix5hd2-clock.txt b/Documentation/devicetree/bindings/clock/hix5hd2-clock.txt new file mode 100644 index 000000000000..7894a64887cb --- /dev/null +++ b/Documentation/devicetree/bindings/clock/hix5hd2-clock.txt @@ -0,0 +1,31 @@ +* Hisilicon Hix5hd2 Clock Controller + +The hix5hd2 clock controller generates and supplies clock to various +controllers within the hix5hd2 SoC. + +Required Properties: + +- compatible: should be "hisilicon,hix5hd2-clock" +- reg: Address and length of the register set +- #clock-cells: Should be <1> + +Each clock is assigned an identifier and client nodes use this identifier +to specify the clock which they consume. + +All these identifier could be found in . + +Examples: + clock: clock@f8a22000 { + compatible = "hisilicon,hix5hd2-clock"; + reg = <0xf8a22000 0x1000>; + #clock-cells = <1>; + }; + + uart0: uart@f8b00000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xf8b00000 0x1000>; + interrupts = <0 49 4>; + clocks = <&clock HIX5HD2_FIXED_83M>; + clock-names = "apb_pclk"; + status = "disabled"; + }; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 5f8a28735c96..3e6e577c01db 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ +obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 40b33c6a8257..038c02f4d0e7 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -6,3 +6,4 @@ obj-y += clk.o clkgate-separated.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o +obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c new file mode 100644 index 000000000000..e5fcfb4e32ef --- /dev/null +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include "clk.h" + +static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = { + { HIX5HD2_FIXED_1200M, "1200m", NULL, CLK_IS_ROOT, 1200000000, }, + { HIX5HD2_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, + { HIX5HD2_FIXED_48M, "48m", NULL, CLK_IS_ROOT, 48000000, }, + { HIX5HD2_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, + { HIX5HD2_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, + { HIX5HD2_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, + { HIX5HD2_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, + { HIX5HD2_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, + { HIX5HD2_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, + { HIX5HD2_FIXED_40M, "40m", NULL, CLK_IS_ROOT, 40000000, }, + { HIX5HD2_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, + { HIX5HD2_FIXED_1728M, "1728m", NULL, CLK_IS_ROOT, 1728000000, }, + { HIX5HD2_FIXED_28P8M, "28p8m", NULL, CLK_IS_ROOT, 28000000, }, + { HIX5HD2_FIXED_432M, "432m", NULL, CLK_IS_ROOT, 432000000, }, + { HIX5HD2_FIXED_345P6M, "345p6m", NULL, CLK_IS_ROOT, 345000000, }, + { HIX5HD2_FIXED_288M, "288m", NULL, CLK_IS_ROOT, 288000000, }, + { HIX5HD2_FIXED_60M, "60m", NULL, CLK_IS_ROOT, 60000000, }, + { HIX5HD2_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, + { HIX5HD2_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, + { HIX5HD2_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, + { HIX5HD2_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 27000000, }, + { HIX5HD2_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000, }, + { HIX5HD2_FIXED_375M, "375m", NULL, CLK_IS_ROOT, 375000000, }, + { HIX5HD2_FIXED_187M, "187m", NULL, CLK_IS_ROOT, 187000000, }, + { HIX5HD2_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, + { HIX5HD2_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, + { HIX5HD2_FIXED_2P02M, "2m", NULL, CLK_IS_ROOT, 2000000, }, + { HIX5HD2_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, + { HIX5HD2_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, }, + { HIX5HD2_FIXED_83M, "83m", NULL, CLK_IS_ROOT, 83333333, }, +}; + +static const char *sfc_mux_p[] __initconst = { + "24m", "150m", "200m", "100m", "75m", }; +static u32 sfc_mux_table[] = {0, 4, 5, 6, 7}; + +static const char *sdio1_mux_p[] __initconst = { + "75m", "100m", "50m", "15m", }; +static u32 sdio1_mux_table[] = {0, 1, 2, 3}; + +static const char *fephy_mux_p[] __initconst = { "25m", "125m"}; +static u32 fephy_mux_table[] = {0, 1}; + + +static struct hisi_mux_clock hix5hd2_mux_clks[] __initdata = { + { HIX5HD2_SFC_MUX, "sfc_mux", sfc_mux_p, ARRAY_SIZE(sfc_mux_p), + CLK_SET_RATE_PARENT, 0x5c, 8, 3, 0, sfc_mux_table, }, + { HIX5HD2_MMC_MUX, "mmc_mux", sdio1_mux_p, ARRAY_SIZE(sdio1_mux_p), + CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio1_mux_table, }, + { HIX5HD2_FEPHY_MUX, "fephy_mux", + fephy_mux_p, ARRAY_SIZE(fephy_mux_p), + CLK_SET_RATE_PARENT, 0x120, 8, 2, 0, fephy_mux_table, }, +}; + +static struct hisi_gate_clock hix5hd2_gate_clks[] __initdata = { + /*sfc*/ + { HIX5HD2_SFC_CLK, "clk_sfc", "sfc_mux", + CLK_SET_RATE_PARENT, 0x5c, 0, 0, }, + { HIX5HD2_SFC_RST, "rst_sfc", "clk_sfc", + CLK_SET_RATE_PARENT, 0x5c, 4, CLK_GATE_SET_TO_DISABLE, }, + /*sdio1*/ + { HIX5HD2_MMC_BIU_CLK, "clk_mmc_biu", "200m", + CLK_SET_RATE_PARENT, 0xa0, 0, 0, }, + { HIX5HD2_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux", + CLK_SET_RATE_PARENT, 0xa0, 1, 0, }, + { HIX5HD2_MMC_CIU_RST, "rst_mmc_ciu", "clk_mmc_ciu", + CLK_SET_RATE_PARENT, 0xa0, 4, CLK_GATE_SET_TO_DISABLE, }, +}; + +static void __init hix5hd2_clk_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HIX5HD2_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_fixed_rate(hix5hd2_fixed_rate_clks, + ARRAY_SIZE(hix5hd2_fixed_rate_clks), + clk_data); + hisi_clk_register_mux(hix5hd2_mux_clks, ARRAY_SIZE(hix5hd2_mux_clks), + clk_data); + hisi_clk_register_gate(hix5hd2_gate_clks, + ARRAY_SIZE(hix5hd2_gate_clks), clk_data); +} + +CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init); diff --git a/include/dt-bindings/clock/hix5hd2-clock.h b/include/dt-bindings/clock/hix5hd2-clock.h new file mode 100644 index 000000000000..aad579a75802 --- /dev/null +++ b/include/dt-bindings/clock/hix5hd2-clock.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef __DTS_HIX5HD2_CLOCK_H +#define __DTS_HIX5HD2_CLOCK_H + +/* fixed rate */ +#define HIX5HD2_FIXED_1200M 1 +#define HIX5HD2_FIXED_400M 2 +#define HIX5HD2_FIXED_48M 3 +#define HIX5HD2_FIXED_24M 4 +#define HIX5HD2_FIXED_600M 5 +#define HIX5HD2_FIXED_300M 6 +#define HIX5HD2_FIXED_75M 7 +#define HIX5HD2_FIXED_200M 8 +#define HIX5HD2_FIXED_100M 9 +#define HIX5HD2_FIXED_40M 10 +#define HIX5HD2_FIXED_150M 11 +#define HIX5HD2_FIXED_1728M 12 +#define HIX5HD2_FIXED_28P8M 13 +#define HIX5HD2_FIXED_432M 14 +#define HIX5HD2_FIXED_345P6M 15 +#define HIX5HD2_FIXED_288M 16 +#define HIX5HD2_FIXED_60M 17 +#define HIX5HD2_FIXED_750M 18 +#define HIX5HD2_FIXED_500M 19 +#define HIX5HD2_FIXED_54M 20 +#define HIX5HD2_FIXED_27M 21 +#define HIX5HD2_FIXED_1500M 22 +#define HIX5HD2_FIXED_375M 23 +#define HIX5HD2_FIXED_187M 24 +#define HIX5HD2_FIXED_250M 25 +#define HIX5HD2_FIXED_125M 26 +#define HIX5HD2_FIXED_2P02M 27 +#define HIX5HD2_FIXED_50M 28 +#define HIX5HD2_FIXED_25M 29 +#define HIX5HD2_FIXED_83M 30 + +/* mux clocks */ +#define HIX5HD2_SFC_MUX 64 +#define HIX5HD2_MMC_MUX 65 +#define HIX5HD2_FEPHY_MUX 66 + +/* gate clocks */ +#define HIX5HD2_SFC_RST 128 +#define HIX5HD2_SFC_CLK 129 +#define HIX5HD2_MMC_CIU_CLK 130 +#define HIX5HD2_MMC_BIU_CLK 131 +#define HIX5HD2_MMC_CIU_RST 132 + +#define HIX5HD2_NR_CLKS 256 +#endif /* __DTS_HIX5HD2_CLOCK_H */ -- cgit v1.2.3 From 9739eef13c926645fbf88bcb77e66442fa75d688 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 8 May 2014 14:10:51 -0700 Subject: net: filter: make BPF conversion more readable Introduce BPF helper macros to define instructions (similar to old BPF_STMT/BPF_JUMP macros) Use them while converting classic BPF to internal and in BPF testsuite later. Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 51 ++++++++++++++++++ net/core/filter.c | 142 +++++++++++++++++-------------------------------- 2 files changed, 101 insertions(+), 92 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index ed1efab10b8f..4457b383961c 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -79,6 +79,57 @@ enum { /* BPF program can access up to 512 bytes of stack space. */ #define MAX_BPF_STACK 512 +/* bpf_add|sub|...: a += x, bpf_mov: a = x */ +#define BPF_ALU64_REG(op, a, x) \ + ((struct sock_filter_int) {BPF_ALU64|BPF_OP(op)|BPF_X, a, x, 0, 0}) +#define BPF_ALU32_REG(op, a, x) \ + ((struct sock_filter_int) {BPF_ALU|BPF_OP(op)|BPF_X, a, x, 0, 0}) + +/* bpf_add|sub|...: a += imm, bpf_mov: a = imm */ +#define BPF_ALU64_IMM(op, a, imm) \ + ((struct sock_filter_int) {BPF_ALU64|BPF_OP(op)|BPF_K, a, 0, 0, imm}) +#define BPF_ALU32_IMM(op, a, imm) \ + ((struct sock_filter_int) {BPF_ALU|BPF_OP(op)|BPF_K, a, 0, 0, imm}) + +/* R0 = *(uint *) (skb->data + off) */ +#define BPF_LD_ABS(size, off) \ + ((struct sock_filter_int) {BPF_LD|BPF_SIZE(size)|BPF_ABS, 0, 0, 0, off}) + +/* R0 = *(uint *) (skb->data + x + off) */ +#define BPF_LD_IND(size, x, off) \ + ((struct sock_filter_int) {BPF_LD|BPF_SIZE(size)|BPF_IND, 0, x, 0, off}) + +/* a = *(uint *) (x + off) */ +#define BPF_LDX_MEM(sz, a, x, off) \ + ((struct sock_filter_int) {BPF_LDX|BPF_SIZE(sz)|BPF_MEM, a, x, off, 0}) + +/* if (a 'op' x) goto pc+off */ +#define BPF_JMP_REG(op, a, x, off) \ + ((struct sock_filter_int) {BPF_JMP|BPF_OP(op)|BPF_X, a, x, off, 0}) + +/* if (a 'op' imm) goto pc+off */ +#define BPF_JMP_IMM(op, a, imm, off) \ + ((struct sock_filter_int) {BPF_JMP|BPF_OP(op)|BPF_K, a, 0, off, imm}) + +#define BPF_EXIT_INSN() \ + ((struct sock_filter_int) {BPF_JMP|BPF_EXIT, 0, 0, 0, 0}) + +static inline int size_to_bpf(int size) +{ + switch (size) { + case 1: + return BPF_B; + case 2: + return BPF_H; + case 4: + return BPF_W; + case 8: + return BPF_DW; + default: + return -EINVAL; + } +} + /* Macro to invoke filter function. */ #define SK_RUN_FILTER(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) diff --git a/net/core/filter.c b/net/core/filter.c index eb020a7d6f55..9aaa05ad8fe3 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -668,10 +668,9 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_PROTOCOL: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); - insn->code = BPF_LDX | BPF_MEM | BPF_H; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_CTX; - insn->off = offsetof(struct sk_buff, protocol); + /* A = *(u16 *) (ctx + offsetof(protocol)) */ + *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, + offsetof(struct sk_buff, protocol)); insn++; /* A = ntohs(A) [emitting a nop or swap16] */ @@ -681,37 +680,27 @@ static bool convert_bpf_extensions(struct sock_filter *fp, break; case SKF_AD_OFF + SKF_AD_PKTTYPE: - insn->code = BPF_LDX | BPF_MEM | BPF_B; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_CTX; - insn->off = pkt_type_offset(); + *insn = BPF_LDX_MEM(BPF_B, BPF_REG_A, BPF_REG_CTX, + pkt_type_offset()); if (insn->off < 0) return false; insn++; - insn->code = BPF_ALU | BPF_AND | BPF_K; - insn->a_reg = BPF_REG_A; - insn->imm = PKT_TYPE_MAX; + *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, PKT_TYPE_MAX); break; case SKF_AD_OFF + SKF_AD_IFINDEX: case SKF_AD_OFF + SKF_AD_HATYPE: - if (FIELD_SIZEOF(struct sk_buff, dev) == 8) - insn->code = BPF_LDX | BPF_MEM | BPF_DW; - else - insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = BPF_REG_TMP; - insn->x_reg = BPF_REG_CTX; - insn->off = offsetof(struct sk_buff, dev); + *insn = BPF_LDX_MEM(size_to_bpf(FIELD_SIZEOF(struct sk_buff, dev)), + BPF_REG_TMP, BPF_REG_CTX, + offsetof(struct sk_buff, dev)); insn++; - insn->code = BPF_JMP | BPF_JNE | BPF_K; - insn->a_reg = BPF_REG_TMP; - insn->imm = 0; - insn->off = 1; + /* if (tmp != 0) goto pc+1 */ + *insn = BPF_JMP_IMM(BPF_JNE, BPF_REG_TMP, 0, 1); insn++; - insn->code = BPF_JMP | BPF_EXIT; + *insn = BPF_EXIT_INSN(); insn++; BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4); @@ -732,55 +721,45 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_MARK: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); - insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_CTX; - insn->off = offsetof(struct sk_buff, mark); + *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, + offsetof(struct sk_buff, mark)); break; case SKF_AD_OFF + SKF_AD_RXHASH: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); - insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_CTX; - insn->off = offsetof(struct sk_buff, hash); + *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, + offsetof(struct sk_buff, hash)); break; case SKF_AD_OFF + SKF_AD_QUEUE: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); - insn->code = BPF_LDX | BPF_MEM | BPF_H; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_CTX; - insn->off = offsetof(struct sk_buff, queue_mapping); + *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, + offsetof(struct sk_buff, queue_mapping)); break; case SKF_AD_OFF + SKF_AD_VLAN_TAG: case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT: BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); - insn->code = BPF_LDX | BPF_MEM | BPF_H; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_CTX; - insn->off = offsetof(struct sk_buff, vlan_tci); + /* A = *(u16 *) (ctx + offsetof(vlan_tci)) */ + *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, + offsetof(struct sk_buff, vlan_tci)); insn++; BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000); if (fp->k == SKF_AD_OFF + SKF_AD_VLAN_TAG) { - insn->code = BPF_ALU | BPF_AND | BPF_K; - insn->a_reg = BPF_REG_A; - insn->imm = ~VLAN_TAG_PRESENT; + *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, + ~VLAN_TAG_PRESENT); } else { - insn->code = BPF_ALU | BPF_RSH | BPF_K; - insn->a_reg = BPF_REG_A; - insn->imm = 12; + /* A >>= 12 */ + *insn = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 12); insn++; - insn->code = BPF_ALU | BPF_AND | BPF_K; - insn->a_reg = BPF_REG_A; - insn->imm = 1; + /* A &= 1 */ + *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 1); } break; @@ -790,21 +769,15 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_CPU: case SKF_AD_OFF + SKF_AD_RANDOM: /* arg1 = ctx */ - insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = BPF_REG_ARG1; - insn->x_reg = BPF_REG_CTX; + *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_ARG1, BPF_REG_CTX); insn++; /* arg2 = A */ - insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = BPF_REG_ARG2; - insn->x_reg = BPF_REG_A; + *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_ARG2, BPF_REG_A); insn++; /* arg3 = X */ - insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = BPF_REG_ARG3; - insn->x_reg = BPF_REG_X; + *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_ARG3, BPF_REG_X); insn++; /* Emit call(ctx, arg2=A, arg3=X) */ @@ -829,9 +802,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, break; case SKF_AD_OFF + SKF_AD_ALU_XOR_X: - insn->code = BPF_ALU | BPF_XOR | BPF_X; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_X; + /* A ^= X */ + *insn = BPF_ALU32_REG(BPF_XOR, BPF_REG_A, BPF_REG_X); break; default: @@ -897,9 +869,7 @@ do_pass: fp = prog; if (new_insn) { - new_insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - new_insn->a_reg = BPF_REG_CTX; - new_insn->x_reg = BPF_REG_ARG1; + *new_insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_CTX, BPF_REG_ARG1); } new_insn++; @@ -1027,34 +997,28 @@ do_pass: /* ldxb 4 * ([14] & 0xf) is remaped into 6 insns. */ case BPF_LDX | BPF_MSH | BPF_B: - insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = BPF_REG_TMP; - insn->x_reg = BPF_REG_A; + /* tmp = A */ + *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_TMP, BPF_REG_A); insn++; - insn->code = BPF_LD | BPF_ABS | BPF_B; - insn->a_reg = BPF_REG_A; - insn->imm = fp->k; + /* A = R0 = *(u8 *) (skb->data + K) */ + *insn = BPF_LD_ABS(BPF_B, fp->k); insn++; - insn->code = BPF_ALU | BPF_AND | BPF_K; - insn->a_reg = BPF_REG_A; - insn->imm = 0xf; + /* A &= 0xf */ + *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 0xf); insn++; - insn->code = BPF_ALU | BPF_LSH | BPF_K; - insn->a_reg = BPF_REG_A; - insn->imm = 2; + /* A <<= 2 */ + *insn = BPF_ALU32_IMM(BPF_LSH, BPF_REG_A, 2); insn++; - insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = BPF_REG_X; - insn->x_reg = BPF_REG_A; + /* X = A */ + *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_X, BPF_REG_A); insn++; - insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_TMP; + /* A = tmp */ + *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_A, BPF_REG_TMP); break; /* RET_K, RET_A are remaped into 2 insns. */ @@ -1068,7 +1032,7 @@ do_pass: insn->imm = fp->k; insn++; - insn->code = BPF_JMP | BPF_EXIT; + *insn = BPF_EXIT_INSN(); break; /* Store to stack. */ @@ -1102,16 +1066,12 @@ do_pass: /* X = A */ case BPF_MISC | BPF_TAX: - insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = BPF_REG_X; - insn->x_reg = BPF_REG_A; + *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_X, BPF_REG_A); break; /* A = X */ case BPF_MISC | BPF_TXA: - insn->code = BPF_ALU64 | BPF_MOV | BPF_X; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_X; + *insn = BPF_ALU64_REG(BPF_MOV, BPF_REG_A, BPF_REG_X); break; /* A = skb->len or X = skb->len */ @@ -1126,10 +1086,8 @@ do_pass: /* access seccomp_data fields */ case BPF_LDX | BPF_ABS | BPF_W: - insn->code = BPF_LDX | BPF_MEM | BPF_W; - insn->a_reg = BPF_REG_A; - insn->x_reg = BPF_REG_CTX; - insn->off = fp->k; + /* A = *(u32 *) (ctx + K) */ + *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, fp->k); break; default: -- cgit v1.2.3 From 4593df29b94b31de931dc20d7da2e6c468c8d473 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 21 Mar 2014 10:13:05 +0100 Subject: mmc: mmci: Enforce DT for signal direction and feedback clock Remove the option to provide signal direction configuration and feeback clock as platform data, enforce it through DT. Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 34 ++++++++++++++-------------------- drivers/mmc/host/mmci.h | 11 +++++++++++ include/linux/amba/mmci.h | 16 ---------------- 3 files changed, 25 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 0d3ee08662a9..c0353f84d5be 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1287,7 +1287,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * indicating signal direction for the signals in * the SD/MMC bus and feedback-clock usage. */ - pwr |= host->plat->sigdir; + pwr |= host->pwr_reg_add; if (ios->bus_width == MMC_BUS_WIDTH_4) pwr &= ~MCI_ST_DATA74DIREN; @@ -1386,29 +1386,26 @@ static struct mmc_host_ops mmci_ops = { .start_signal_voltage_switch = mmci_sig_volt_switch, }; -static void mmci_dt_populate_generic_pdata(struct device_node *np, - struct mmci_platform_data *pdata) +static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc) { + struct mmci_host *host = mmc_priv(mmc); + int ret = mmc_of_parse(mmc); + + if (ret) + return ret; + if (of_get_property(np, "st,sig-dir-dat0", NULL)) - pdata->sigdir |= MCI_ST_DATA0DIREN; + host->pwr_reg_add |= MCI_ST_DATA0DIREN; if (of_get_property(np, "st,sig-dir-dat2", NULL)) - pdata->sigdir |= MCI_ST_DATA2DIREN; + host->pwr_reg_add |= MCI_ST_DATA2DIREN; if (of_get_property(np, "st,sig-dir-dat31", NULL)) - pdata->sigdir |= MCI_ST_DATA31DIREN; + host->pwr_reg_add |= MCI_ST_DATA31DIREN; if (of_get_property(np, "st,sig-dir-dat74", NULL)) - pdata->sigdir |= MCI_ST_DATA74DIREN; + host->pwr_reg_add |= MCI_ST_DATA74DIREN; if (of_get_property(np, "st,sig-dir-cmd", NULL)) - pdata->sigdir |= MCI_ST_CMDDIREN; + host->pwr_reg_add |= MCI_ST_CMDDIREN; if (of_get_property(np, "st,sig-pin-fbclk", NULL)) - pdata->sigdir |= MCI_ST_FBCLKEN; -} - -static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc) -{ - int ret = mmc_of_parse(mmc); - - if (ret) - return ret; + host->pwr_reg_add |= MCI_ST_FBCLKEN; if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL)) mmc->caps |= MMC_CAP_MMC_HIGHSPEED; @@ -1440,9 +1437,6 @@ static int mmci_probe(struct amba_device *dev, return -ENOMEM; } - if (np) - mmci_dt_populate_generic_pdata(np, plat); - mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); if (!mmc) return -ENOMEM; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 8fc5814f938a..347d942d740b 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -13,6 +13,16 @@ #define MCI_PWR_ON 0x03 #define MCI_OD (1 << 6) #define MCI_ROD (1 << 7) +/* + * The ST Micro version does not have ROD and reuse the voltage registers for + * direction settings. + */ +#define MCI_ST_DATA2DIREN (1 << 2) +#define MCI_ST_CMDDIREN (1 << 3) +#define MCI_ST_DATA0DIREN (1 << 4) +#define MCI_ST_DATA31DIREN (1 << 5) +#define MCI_ST_FBCLKEN (1 << 7) +#define MCI_ST_DATA74DIREN (1 << 8) #define MMCICLOCK 0x004 #define MCI_CLK_ENABLE (1 << 8) @@ -183,6 +193,7 @@ struct mmci_host { unsigned int mclk; unsigned int cclk; u32 pwr_reg; + u32 pwr_reg_add; u32 clk_reg; u32 datactrl_reg; u32 busy_status; diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 32a89cf5ec45..0d3ff95b3b4c 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -6,19 +6,6 @@ #include - -/* - * These defines is places here due to access is needed from machine - * configuration files. The ST Micro version does not have ROD and - * reuse the voltage registers for direction settings. - */ -#define MCI_ST_DATA2DIREN (1 << 2) -#define MCI_ST_CMDDIREN (1 << 3) -#define MCI_ST_DATA0DIREN (1 << 4) -#define MCI_ST_DATA31DIREN (1 << 5) -#define MCI_ST_FBCLKEN (1 << 7) -#define MCI_ST_DATA74DIREN (1 << 8) - /* Just some dummy forwarding */ struct dma_chan; @@ -45,8 +32,6 @@ struct dma_chan; * @capabilities: the capabilities of the block as implemented in * this platform, signify anything MMC_CAP_* from mmc/host.h * @capabilities2: more capabilities, MMC_CAP2_* from mmc/host.h - * @sigdir: a bit field indicating for what bits in the MMC bus the host - * should enable signal direction indication. * @dma_filter: function used to select an appropriate RX and TX * DMA channel to be used for DMA, if and only if you're deploying the * generic DMA engine @@ -69,7 +54,6 @@ struct mmci_platform_data { bool cd_invert; unsigned long capabilities; unsigned long capabilities2; - u32 sigdir; bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; -- cgit v1.2.3 From 3faf80dfa342e98b5780e0b78b7a670c7b61a9be Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 21 Mar 2014 10:29:10 +0100 Subject: mmc: mmci: Enforce mmc capabilities through DT Remove the option to provide the flags for mmc capabilities as platform data, enforce it through DT. Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 2 -- include/linux/amba/mmci.h | 5 ----- 2 files changed, 7 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c0353f84d5be..9c60325f1a30 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1519,8 +1519,6 @@ static int mmci_probe(struct amba_device *dev, dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); /* DT takes precedence over platform data. */ - mmc->caps = np ? mmc->caps : plat->capabilities; - mmc->caps2 = np ? mmc->caps2 : plat->capabilities2; if (!np) { if (!plat->cd_invert) mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 0d3ff95b3b4c..b992fc931295 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -29,9 +29,6 @@ struct dma_chan; * @gpio_wp: read this GPIO pin to see if the card is write protected * @gpio_cd: read this GPIO pin to detect card insertion * @cd_invert: true if the gpio_cd pin value is active low - * @capabilities: the capabilities of the block as implemented in - * this platform, signify anything MMC_CAP_* from mmc/host.h - * @capabilities2: more capabilities, MMC_CAP2_* from mmc/host.h * @dma_filter: function used to select an appropriate RX and TX * DMA channel to be used for DMA, if and only if you're deploying the * generic DMA engine @@ -52,8 +49,6 @@ struct mmci_platform_data { int gpio_wp; int gpio_cd; bool cd_invert; - unsigned long capabilities; - unsigned long capabilities2; bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; -- cgit v1.2.3 From 1c8349a17137b93f0a83f276c764a6df1b9a116e Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 12 May 2014 08:12:25 -0400 Subject: ext4: fix data integrity sync in ordered mode When we perform a data integrity sync we tag all the dirty pages with PAGECACHE_TAG_TOWRITE at start of ext4_da_writepages. Later we check for this tag in write_cache_pages_da and creates a struct mpage_da_data containing contiguously indexed pages tagged with this tag and sync these pages with a call to mpage_da_map_and_submit. This process is done in while loop until all the PAGECACHE_TAG_TOWRITE pages are synced. We also do journal start and stop in each iteration. journal_stop could initiate journal commit which would call ext4_writepage which in turn will call ext4_bio_write_page even for delayed OR unwritten buffers. When ext4_bio_write_page is called for such buffers, even though it does not sync them but it clears the PAGECACHE_TAG_TOWRITE of the corresponding page and hence these pages are also not synced by the currently running data integrity sync. We will end up with dirty pages although sync is completed. This could cause a potential data loss when the sync call is followed by a truncate_pagecache call, which is exactly the case in collapse_range. (It will cause generic/127 failure in xfstests) To avoid this issue, we can use set_page_writeback_keepwrite instead of set_page_writeback, which doesn't clear TOWRITE tag. Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon Signed-off-by: Ashish Sangwan Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/ext4.h | 3 ++- fs/ext4/inode.c | 6 ++++-- fs/ext4/page-io.c | 8 ++++++-- include/linux/page-flags.h | 12 +++++++++++- mm/page-writeback.c | 11 ++++++----- 5 files changed, 29 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 1d08a1b51bdd..aeda5061a59a 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2772,7 +2772,8 @@ extern void ext4_io_submit(struct ext4_io_submit *io); extern int ext4_bio_write_page(struct ext4_io_submit *io, struct page *page, int len, - struct writeback_control *wbc); + struct writeback_control *wbc, + bool keep_towrite); /* mmp.c */ extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9b3c52fbe86d..04dd2de10796 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1846,6 +1846,7 @@ static int ext4_writepage(struct page *page, struct buffer_head *page_bufs = NULL; struct inode *inode = page->mapping->host; struct ext4_io_submit io_submit; + bool keep_towrite = false; trace_ext4_writepage(page); size = i_size_read(inode); @@ -1876,6 +1877,7 @@ static int ext4_writepage(struct page *page, unlock_page(page); return 0; } + keep_towrite = true; } if (PageChecked(page) && ext4_should_journal_data(inode)) @@ -1892,7 +1894,7 @@ static int ext4_writepage(struct page *page, unlock_page(page); return -ENOMEM; } - ret = ext4_bio_write_page(&io_submit, page, len, wbc); + ret = ext4_bio_write_page(&io_submit, page, len, wbc, keep_towrite); ext4_io_submit(&io_submit); /* Drop io_end reference we got from init */ ext4_put_io_end_defer(io_submit.io_end); @@ -1911,7 +1913,7 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page) else len = PAGE_CACHE_SIZE; clear_page_dirty_for_io(page); - err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc); + err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false); if (!err) mpd->wbc->nr_to_write--; mpd->first_page++; diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index c18d95b50540..4cb2743cb2e3 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -401,7 +401,8 @@ submit_and_retry: int ext4_bio_write_page(struct ext4_io_submit *io, struct page *page, int len, - struct writeback_control *wbc) + struct writeback_control *wbc, + bool keep_towrite) { struct inode *inode = page->mapping->host; unsigned block_start, blocksize; @@ -414,7 +415,10 @@ int ext4_bio_write_page(struct ext4_io_submit *io, BUG_ON(!PageLocked(page)); BUG_ON(PageWriteback(page)); - set_page_writeback(page); + if (keep_towrite) + set_page_writeback_keepwrite(page); + else + set_page_writeback(page); ClearPageError(page); /* diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index d1fe1a761047..ca71a1d347a0 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -317,13 +317,23 @@ CLEARPAGEFLAG(Uptodate, uptodate) extern void cancel_dirty_page(struct page *page, unsigned int account_size); int test_clear_page_writeback(struct page *page); -int test_set_page_writeback(struct page *page); +int __test_set_page_writeback(struct page *page, bool keep_write); + +#define test_set_page_writeback(page) \ + __test_set_page_writeback(page, false) +#define test_set_page_writeback_keepwrite(page) \ + __test_set_page_writeback(page, true) static inline void set_page_writeback(struct page *page) { test_set_page_writeback(page); } +static inline void set_page_writeback_keepwrite(struct page *page) +{ + test_set_page_writeback_keepwrite(page); +} + #ifdef CONFIG_PAGEFLAGS_EXTENDED /* * System with lots of page flags available. This allows separate diff --git a/mm/page-writeback.c b/mm/page-writeback.c index ef413492a149..d8691d9de3c4 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -2398,7 +2398,7 @@ int test_clear_page_writeback(struct page *page) return ret; } -int test_set_page_writeback(struct page *page) +int __test_set_page_writeback(struct page *page, bool keep_write) { struct address_space *mapping = page_mapping(page); int ret; @@ -2423,9 +2423,10 @@ int test_set_page_writeback(struct page *page) radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); - radix_tree_tag_clear(&mapping->page_tree, - page_index(page), - PAGECACHE_TAG_TOWRITE); + if (!keep_write) + radix_tree_tag_clear(&mapping->page_tree, + page_index(page), + PAGECACHE_TAG_TOWRITE); spin_unlock_irqrestore(&mapping->tree_lock, flags); } else { ret = TestSetPageWriteback(page); @@ -2436,7 +2437,7 @@ int test_set_page_writeback(struct page *page) return ret; } -EXPORT_SYMBOL(test_set_page_writeback); +EXPORT_SYMBOL(__test_set_page_writeback); /* * Return true if any of the pages in the mapping are marked with the -- cgit v1.2.3 From aa8532c32216ae07c3813b9aeb774517878a7573 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 8 May 2014 11:09:23 +0100 Subject: xen: refactor suspend pre/post hooks New architectures currently have to provide implementations of 5 different functions: xen_arch_pre_suspend(), xen_arch_post_suspend(), xen_arch_hvm_post_suspend(), xen_mm_pin_all(), and xen_mm_unpin_all(). Refactor the suspend code to only require xen_arch_pre_suspend() and xen_arch_post_suspend(). Signed-off-by: David Vrabel Reviewed-by: Boris Ostrovsky --- arch/x86/xen/suspend.c | 23 ++++++++++++++++++++--- arch/x86/xen/xen-ops.h | 2 ++ drivers/xen/manage.c | 45 +++++++-------------------------------------- include/xen/xen-ops.h | 4 ---- 4 files changed, 29 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c index 45329c8c226e..c4df9dbd63b7 100644 --- a/arch/x86/xen/suspend.c +++ b/arch/x86/xen/suspend.c @@ -12,8 +12,10 @@ #include "xen-ops.h" #include "mmu.h" -void xen_arch_pre_suspend(void) +static void xen_pv_pre_suspend(void) { + xen_mm_pin_all(); + xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn); xen_start_info->console.domU.mfn = mfn_to_pfn(xen_start_info->console.domU.mfn); @@ -26,7 +28,7 @@ void xen_arch_pre_suspend(void) BUG(); } -void xen_arch_hvm_post_suspend(int suspend_cancelled) +static void xen_hvm_post_suspend(int suspend_cancelled) { #ifdef CONFIG_XEN_PVHVM int cpu; @@ -41,7 +43,7 @@ void xen_arch_hvm_post_suspend(int suspend_cancelled) #endif } -void xen_arch_post_suspend(int suspend_cancelled) +static void xen_pv_post_suspend(int suspend_cancelled) { xen_build_mfn_list_list(); @@ -60,6 +62,21 @@ void xen_arch_post_suspend(int suspend_cancelled) xen_vcpu_restore(); } + xen_mm_unpin_all(); +} + +void xen_arch_pre_suspend(void) +{ + if (xen_pv_domain()) + xen_pv_pre_suspend(); +} + +void xen_arch_post_suspend(int cancelled) +{ + if (xen_pv_domain()) + xen_pv_post_suspend(cancelled); + else + xen_hvm_post_suspend(cancelled); } static void xen_vcpu_notify_restore(void *data) diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index 1cb6f4c37300..c834d4b231f0 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -31,6 +31,8 @@ void xen_setup_kernel_pagetable(pgd_t *pgd, unsigned long max_pfn); void xen_reserve_top(void); extern unsigned long xen_max_p2m_pfn; +void xen_mm_pin_all(void); +void xen_mm_unpin_all(void); void xen_set_pat(u64); char * __init xen_memory_setup(void); diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 32f9236c959f..c3667b202f2f 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -41,9 +41,6 @@ static enum shutdown_state shutting_down = SHUTDOWN_INVALID; struct suspend_info { int cancelled; - unsigned long arg; /* extra hypercall argument */ - void (*pre)(void); - void (*post)(int cancelled); }; static RAW_NOTIFIER_HEAD(xen_resume_notifier); @@ -61,26 +58,6 @@ void xen_resume_notifier_unregister(struct notifier_block *nb) EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister); #ifdef CONFIG_HIBERNATE_CALLBACKS -static void xen_hvm_post_suspend(int cancelled) -{ - xen_arch_hvm_post_suspend(cancelled); - gnttab_resume(); -} - -static void xen_pre_suspend(void) -{ - xen_mm_pin_all(); - gnttab_suspend(); - xen_arch_pre_suspend(); -} - -static void xen_post_suspend(int cancelled) -{ - xen_arch_post_suspend(cancelled); - gnttab_resume(); - xen_mm_unpin_all(); -} - static int xen_suspend(void *data) { struct suspend_info *si = data; @@ -94,18 +71,20 @@ static int xen_suspend(void *data) return err; } - if (si->pre) - si->pre(); + gnttab_suspend(); + xen_arch_pre_suspend(); /* * This hypercall returns 1 if suspend was cancelled * or the domain was merely checkpointed, and 0 if it * is resuming in a new domain. */ - si->cancelled = HYPERVISOR_suspend(si->arg); + si->cancelled = HYPERVISOR_suspend(xen_pv_domain() + ? virt_to_mfn(xen_start_info) + : 0); - if (si->post) - si->post(si->cancelled); + xen_arch_post_suspend(si->cancelled); + gnttab_resume(); if (!si->cancelled) { xen_irq_resume(); @@ -154,16 +133,6 @@ static void do_suspend(void) si.cancelled = 1; - if (xen_hvm_domain()) { - si.arg = 0UL; - si.pre = NULL; - si.post = &xen_hvm_post_suspend; - } else { - si.arg = virt_to_mfn(xen_start_info); - si.pre = &xen_pre_suspend; - si.post = &xen_post_suspend; - } - err = stop_machine(xen_suspend, &si, cpumask_of(0)); raw_notifier_call_chain(&xen_resume_notifier, 0, NULL); diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 2cf47175b12b..0b3149ed7eaa 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -9,10 +9,6 @@ DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu); void xen_arch_pre_suspend(void); void xen_arch_post_suspend(int suspend_cancelled); -void xen_arch_hvm_post_suspend(int suspend_cancelled); - -void xen_mm_pin_all(void); -void xen_mm_unpin_all(void); void xen_timer_resume(void); void xen_arch_resume(void); -- cgit v1.2.3 From 7d87a7f709650bde4d7d63117f25ee1c095da5dd Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 9 Apr 2014 18:19:04 +0300 Subject: srm/i915/chv: Add Cherryview PCI IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v2: Update to also fill in the new num_pipes field. v3: Rebase on top of the pciid extraction. v4: Switch from info->has*ring to info->ring mask. Also add VEBOX support whiel at it. v5: s/CHV_PCI_IDS/CHV_IDS/, and drop the trailing '\' Signed-off-by: Ville Syrjälä Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 12 +++++++++++- include/drm/i915_pciids.h | 6 ++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 4024e16ae63d..6868bc0eabd3 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -299,6 +299,15 @@ static const struct intel_device_info intel_broadwell_gt3m_info = { GEN_DEFAULT_PIPEOFFSETS, }; +static const struct intel_device_info intel_cherryview_info = { + .is_preliminary = 1, + .gen = 8, .num_pipes = 2, + .need_gfx_hws = 1, .has_hotplug = 1, + .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, + .is_valleyview = 1, + .display_mmio_offset = VLV_DISPLAY_BASE, +}; + /* * Make sure any device matches here are from most specific to most * general. For example, since the Quanta match is based on the subsystem @@ -334,7 +343,8 @@ static const struct intel_device_info intel_broadwell_gt3m_info = { INTEL_BDW_GT12M_IDS(&intel_broadwell_m_info), \ INTEL_BDW_GT12D_IDS(&intel_broadwell_d_info), \ INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info), \ - INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info) + INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info), \ + INTEL_CHV_IDS(&intel_cherryview_info) static const struct pci_device_id pciidlist[] = { /* aka */ INTEL_PCI_IDS, diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 24f3cad045db..d18f31a77987 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -245,4 +245,10 @@ INTEL_BDW_GT12D_IDS(info), \ INTEL_BDW_GT3D_IDS(info) +#define INTEL_CHV_IDS(info) \ + INTEL_VGA_DEVICE(0x22b0, info), \ + INTEL_VGA_DEVICE(0x22b1, info), \ + INTEL_VGA_DEVICE(0x22b2, info), \ + INTEL_VGA_DEVICE(0x22b3, info) + #endif /* _I915_PCIIDS_H */ -- cgit v1.2.3 From 60ff746739bf805a912484643c720b6124826140 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 4 May 2014 16:39:18 -0700 Subject: net: rename local_df to ignore_df MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As suggested by several people, rename local_df to ignore_df, since it means "ignore df bit if it is set". Cc: Maciej Żenczykowski Cc: Florian Westphal Cc: David S. Miller Cc: Eric Dumazet Signed-off-by: Cong Wang Acked-by: Maciej Żenczykowski Signed-off-by: David S. Miller --- include/linux/skbuff.h | 4 ++-- include/net/ip.h | 6 +++--- include/net/ip6_route.h | 2 +- net/core/skbuff.c | 4 ++-- net/ipv4/ip_forward.c | 2 +- net/ipv4/ip_output.c | 12 ++++++------ net/ipv4/netfilter/nf_defrag_ipv4.c | 2 +- net/ipv4/xfrm4_output.c | 2 +- net/ipv6/ip6_output.c | 12 ++++++------ net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/ipv6/xfrm6_output.c | 6 +++--- net/l2tp/l2tp_core.c | 2 +- net/netfilter/ipvs/ip_vs_xmit.c | 20 ++++++++++---------- net/openvswitch/vport-gre.c | 2 +- net/openvswitch/vport-vxlan.c | 2 +- net/sctp/ipv6.c | 2 +- net/sctp/output.c | 2 +- 17 files changed, 42 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 3ca0dda5a42e..7a9beeb1c458 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -426,7 +426,7 @@ static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1, * @csum_start: Offset from skb->head where checksumming should start * @csum_offset: Offset from csum_start where checksum should be stored * @priority: Packet queueing priority - * @local_df: allow local fragmentation + * @ignore_df: allow local fragmentation * @cloned: Head may be cloned (check refcnt to be sure) * @ip_summed: Driver fed us an IP checksum * @nohdr: Payload reference only, must not modify header @@ -514,7 +514,7 @@ struct sk_buff { }; __u32 priority; kmemcheck_bitfield_begin(flags1); - __u8 local_df:1, + __u8 ignore_df:1, cloned:1, ip_summed:2, nohdr:1, diff --git a/include/net/ip.h b/include/net/ip.h index 16146b667ddb..55752985c144 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -269,7 +269,7 @@ static inline bool ip_sk_use_pmtu(const struct sock *sk) return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE; } -static inline bool ip_sk_local_df(const struct sock *sk) +static inline bool ip_sk_ignore_df(const struct sock *sk) { return inet_sk(sk)->pmtudisc < IP_PMTUDISC_DO || inet_sk(sk)->pmtudisc == IP_PMTUDISC_OMIT; @@ -304,7 +304,7 @@ static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, s { struct iphdr *iph = ip_hdr(skb); - if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) { + if ((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) { /* This is only to work around buggy Windows95/2000 * VJ compression implementations. If the ID field * does not change, they drop every other packet in @@ -320,7 +320,7 @@ static inline void ip_select_ident_more(struct sk_buff *skb, struct dst_entry *d { struct iphdr *iph = ip_hdr(skb); - if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) { + if ((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) { if (sk && inet_sk(sk)->inet_daddr) { iph->id = htons(inet_sk(sk)->inet_id); inet_sk(sk)->inet_id += 1 + more; diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 6c4f5eac98e7..38e41e4d0998 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -185,7 +185,7 @@ static inline bool ip6_sk_accept_pmtu(const struct sock *sk) inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_OMIT; } -static inline bool ip6_sk_local_df(const struct sock *sk) +static inline bool ip6_sk_ignore_df(const struct sock *sk) { return inet6_sk(sk)->pmtudisc < IPV6_PMTUDISC_DO || inet6_sk(sk)->pmtudisc == IPV6_PMTUDISC_OMIT; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1b62343f5837..3d74530ae82b 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -694,7 +694,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) #endif memcpy(new->cb, old->cb, sizeof(old->cb)); new->csum = old->csum; - new->local_df = old->local_df; + new->ignore_df = old->ignore_df; new->pkt_type = old->pkt_type; new->ip_summed = old->ip_summed; skb_copy_queue_mapping(new, old); @@ -3913,7 +3913,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet) skb->tstamp.tv64 = 0; skb->pkt_type = PACKET_HOST; skb->skb_iif = 0; - skb->local_df = 0; + skb->ignore_df = 0; skb_dst_drop(skb); skb->mark = 0; secpath_reset(skb); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 6f111e48e11c..3a83ce5efa80 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -42,7 +42,7 @@ static bool ip_may_fragment(const struct sk_buff *skb) { return unlikely((ip_hdr(skb)->frag_off & htons(IP_DF)) == 0) || - skb->local_df; + skb->ignore_df; } static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index a52f50187b54..6aa4380fde1a 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -415,7 +415,7 @@ packet_routed: skb_reset_network_header(skb); iph = ip_hdr(skb); *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff)); - if (ip_dont_fragment(sk, &rt->dst) && !skb->local_df) + if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df) iph->frag_off = htons(IP_DF); else iph->frag_off = 0; @@ -501,7 +501,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) iph = ip_hdr(skb); mtu = ip_skb_dst_mtu(skb); - if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->local_df) || + if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) || (IPCB(skb)->frag_max_size && IPCB(skb)->frag_max_size > mtu))) { IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS); @@ -866,7 +866,7 @@ static int __ip_append_data(struct sock *sk, fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; - maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu; + maxnonfragsize = ip_sk_ignore_df(sk) ? 0xFFFF : mtu; if (cork->length + length > maxnonfragsize - fragheaderlen) { ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, @@ -1189,7 +1189,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; - maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu; + maxnonfragsize = ip_sk_ignore_df(sk) ? 0xFFFF : mtu; if (cork->length + size > maxnonfragsize - fragheaderlen) { ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, @@ -1350,10 +1350,10 @@ struct sk_buff *__ip_make_skb(struct sock *sk, * to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ - skb->local_df = ip_sk_local_df(sk); + skb->ignore_df = ip_sk_ignore_df(sk); /* DF bit is set when we want to see DF on outgoing frames. - * If local_df is set too, we still allow to fragment this frame + * If ignore_df is set too, we still allow to fragment this frame * locally. */ if (inet->pmtudisc == IP_PMTUDISC_DO || inet->pmtudisc == IP_PMTUDISC_PROBE || diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c index f40f321b41fc..b8f6381c7d0b 100644 --- a/net/ipv4/netfilter/nf_defrag_ipv4.c +++ b/net/ipv4/netfilter/nf_defrag_ipv4.c @@ -34,7 +34,7 @@ static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user) if (!err) { ip_send_check(ip_hdr(skb)); - skb->local_df = 1; + skb->ignore_df = 1; } return err; diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 40e701f2e1e0..8e8c018d9d2d 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -25,7 +25,7 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb) if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE) goto out; - if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df) + if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df) goto out; mtu = dst_mtu(skb_dst(skb)); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 31a38bde69ef..ab0cc57f779c 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -219,7 +219,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, skb->mark = sk->sk_mark; mtu = dst_mtu(dst); - if ((skb->len <= mtu) || skb->local_df || skb_is_gso(skb)) { + if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) { IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUT, skb->len); return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, @@ -347,11 +347,11 @@ static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu) if (skb->len <= mtu) return false; - /* ipv6 conntrack defrag sets max_frag_size + local_df */ + /* ipv6 conntrack defrag sets max_frag_size + ignore_df */ if (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu) return true; - if (skb->local_df) + if (skb->ignore_df) return false; if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu) @@ -559,7 +559,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) /* We must not fragment if the socket is set to force MTU discovery * or if the skb it not generated by a local socket. */ - if (unlikely(!skb->local_df && skb->len > mtu) || + if (unlikely(!skb->ignore_df && skb->len > mtu) || (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu)) { if (skb->sk && dst_allfrag(skb_dst(skb))) @@ -1234,7 +1234,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, sizeof(struct frag_hdr) : 0) + rt->rt6i_nfheader_len; - if (ip6_sk_local_df(sk)) + if (ip6_sk_ignore_df(sk)) maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN; else maxnonfragsize = mtu; @@ -1544,7 +1544,7 @@ int ip6_push_pending_frames(struct sock *sk) } /* Allow local fragmentation. */ - skb->local_df = ip6_sk_local_df(sk); + skb->ignore_df = ip6_sk_ignore_df(sk); *final_dst = fl6->daddr; __skb_pull(skb, skb_network_header_len(skb)); diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 767ab8da8218..0d5279fd852a 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -451,7 +451,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) } sub_frag_mem_limit(&fq->q, head->truesize); - head->local_df = 1; + head->ignore_df = 1; head->next = NULL; head->dev = dev; head->tstamp = fq->q.stamp; diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 19ef329bdbf8..f47c8b153dd3 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -78,7 +78,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb) if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; - if (!skb->local_df && skb->len > mtu) { + if (!skb->ignore_df && skb->len > mtu) { skb->dev = dst->dev; if (xfrm6_local_dontfrag(skb)) @@ -120,7 +120,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb) #endif skb->protocol = htons(ETH_P_IPV6); - skb->local_df = 1; + skb->ignore_df = 1; return x->outer_mode->output2(x, skb); } @@ -150,7 +150,7 @@ static int __xfrm6_output(struct sk_buff *skb) if (skb->len > mtu && xfrm6_local_dontfrag(skb)) { xfrm6_local_rxpmtu(skb, mtu); return -EMSGSIZE; - } else if (!skb->local_df && skb->len > mtu && skb->sk) { + } else if (!skb->ignore_df && skb->len > mtu && skb->sk) { xfrm_local_error(skb, mtu); return -EMSGSIZE; } diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index aa1a9d44c107..ed0716a075ba 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1073,7 +1073,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, } /* Queue the packet to IP for output */ - skb->local_df = 1; + skb->ignore_df = 1; #if IS_ENABLED(CONFIG_IPV6) if (tunnel->sock->sk_family == PF_INET6 && !tunnel->v4mapped) error = inet6_csk_xmit(tunnel->sock, skb, NULL); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index c47444e4cf8c..487b55e04337 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -562,7 +562,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_send_check(iph); /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0); rcu_read_unlock(); @@ -590,7 +590,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0); rcu_read_unlock(); @@ -684,7 +684,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, MTU problem. */ /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local); rcu_read_unlock(); @@ -774,7 +774,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, MTU problem. */ /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local); rcu_read_unlock(); @@ -886,7 +886,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_select_ident(skb, &rt->dst, NULL); /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) @@ -974,7 +974,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, iph->hop_limit = old_iph->hop_limit; /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; ret = ip_vs_tunnel_xmit_prepare(skb, cp); if (ret == NF_ACCEPT) @@ -1023,7 +1023,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_send_check(ip_hdr(skb)); /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0); rcu_read_unlock(); @@ -1060,7 +1060,7 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, } /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0); rcu_read_unlock(); @@ -1157,7 +1157,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_nat_icmp(skb, pp, cp, 0); /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local); rcu_read_unlock(); @@ -1249,7 +1249,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_nat_icmp_v6(skb, pp, cp, 0); /* Another hack: avoid icmp_send in ip_fragment */ - skb->local_df = 1; + skb->ignore_df = 1; rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local); rcu_read_unlock(); diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index ebb6e2442554..0856f014d7a9 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -172,7 +172,7 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb) df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; - skb->local_df = 1; + skb->ignore_df = 1; return iptunnel_xmit(skb->sk, rt, skb, fl.saddr, OVS_CB(skb)->tun_key->ipv4_dst, IPPROTO_GRE, diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 21cceb3bdf78..a93efa3f64c3 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -170,7 +170,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; - skb->local_df = 1; + skb->ignore_df = 1; inet_get_local_port_range(net, &port_min, &port_max); src_port = vxlan_src_port(port_min, port_max, skb); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 2b1738ef9394..4dc5d9e08311 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -216,7 +216,7 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport) IP6_ECN_flow_xmit(sk, fl6->flowlabel); if (!(transport->param_flags & SPP_PMTUD_ENABLE)) - skb->local_df = 1; + skb->ignore_df = 1; SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS); diff --git a/net/sctp/output.c b/net/sctp/output.c index 0f4d15fc2627..01ab8e0723f0 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -591,7 +591,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) pr_debug("***sctp_transmit_packet*** skb->len:%d\n", nskb->len); - nskb->local_df = packet->ipfragok; + nskb->ignore_df = packet->ipfragok; tp->af_specific->sctp_xmit(nskb, tp); out: -- cgit v1.2.3 From f06c7f9f92295faf701a9628b383156c4efb6119 Mon Sep 17 00:00:00 2001 From: dingtianhong Date: Fri, 9 May 2014 14:58:05 +0800 Subject: vlan: rename __vlan_find_dev_deep() to __vlan_find_dev_deep_rcu() The __vlan_find_dev_deep should always called in RCU, according David's suggestion, rename to __vlan_find_dev_deep_rcu looks more reasonable. Signed-off-by: Ding Tianhong Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 2 +- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +- drivers/net/usb/cdc_mbim.c | 4 ++-- drivers/s390/net/qeth_l3_main.c | 10 +++++----- include/linux/if_vlan.h | 4 ++-- net/8021q/vlan_core.c | 6 +++--- net/bridge/br_netfilter.c | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index c0a9dd55f4e5..b0cbb2b7fd48 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -185,7 +185,7 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter, if (ether_addr_equal(dev->dev_addr, mac)) { rcu_read_lock(); if (vlan && vlan != VLAN_VID_MASK) { - dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), vlan); + dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), vlan); } else if (netif_is_bond_slave(dev)) { struct net_device *upper_dev; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 8efeed3325b5..0f1e886d89e3 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -4068,7 +4068,7 @@ static int update_root_dev_clip(struct net_device *dev) /* Parse all bond and vlan devices layered on top of the physical dev */ for (i = 0; i < VLAN_N_VID; i++) { - root_dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), i); + root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i); if (!root_dev) continue; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index d2e18b52caba..8a2aeb85e320 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -4146,7 +4146,7 @@ void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event) rcu_read_lock(); for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) { - dev = __vlan_find_dev_deep(netdev, htons(ETH_P_8021Q), vid); + dev = __vlan_find_dev_deep_rcu(netdev, htons(ETH_P_8021Q), vid); if (!dev) continue; qlcnic_config_indev_addr(adapter, dev, event); diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 2e025ddcef21..0ab79fca822c 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -223,8 +223,8 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) /* need to send the NA on the VLAN dev, if any */ rcu_read_lock(); if (tci) { - netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q), - tci); + netdev = __vlan_find_dev_deep_rcu(dev->net, htons(ETH_P_8021Q), + tci); if (!netdev) { rcu_read_unlock(); return; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index c8d91d797ec8..bc2499a24884 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1659,7 +1659,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card) for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { struct net_device *netdev; - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), + netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); if (netdev == NULL || !(netdev->flags & IFF_UP)) @@ -1721,7 +1721,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card) for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { struct net_device *netdev; - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), + netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); if (netdev == NULL || !(netdev->flags & IFF_UP)) @@ -1766,7 +1766,7 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card, QETH_CARD_TEXT(card, 4, "frvaddr4"); - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid); + netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); if (!netdev) return; in_dev = in_dev_get(netdev); @@ -1796,7 +1796,7 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card, QETH_CARD_TEXT(card, 4, "frvaddr6"); - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid); + netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); if (!netdev) return; in6_dev = in6_dev_get(netdev); @@ -2089,7 +2089,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev, struct net_device *netdev; rcu_read_lock(); - netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), + netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid); rcu_read_unlock(); if (netdev == dev) { diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 13bbbde00e68..8c0fb7f3a9a5 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -106,7 +106,7 @@ struct vlan_pcpu_stats { #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) -extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, +extern struct net_device *__vlan_find_dev_deep_rcu(struct net_device *real_dev, __be16 vlan_proto, u16 vlan_id); extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); extern u16 vlan_dev_vlan_id(const struct net_device *dev); @@ -199,7 +199,7 @@ extern void vlan_vids_del_by_dev(struct net_device *dev, extern bool vlan_uses_dev(const struct net_device *dev); #else static inline struct net_device * -__vlan_find_dev_deep(struct net_device *real_dev, +__vlan_find_dev_deep_rcu(struct net_device *real_dev, __be16 vlan_proto, u16 vlan_id) { return NULL; diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 3c32bd257b73..9012b1c922b6 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -63,7 +63,7 @@ bool vlan_do_receive(struct sk_buff **skbp) } /* Must be invoked with rcu_read_lock. */ -struct net_device *__vlan_find_dev_deep(struct net_device *dev, +struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, __be16 vlan_proto, u16 vlan_id) { struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info); @@ -81,13 +81,13 @@ struct net_device *__vlan_find_dev_deep(struct net_device *dev, upper_dev = netdev_master_upper_dev_get_rcu(dev); if (upper_dev) - return __vlan_find_dev_deep(upper_dev, + return __vlan_find_dev_deep_rcu(upper_dev, vlan_proto, vlan_id); } return NULL; } -EXPORT_SYMBOL(__vlan_find_dev_deep); +EXPORT_SYMBOL(__vlan_find_dev_deep_rcu); struct net_device *vlan_dev_real_dev(const struct net_device *dev) { diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 2acf7fa1fec6..a615264cf01a 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -535,7 +535,7 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct if (brnf_pass_vlan_indev == 0 || !vlan_tx_tag_present(skb)) return br; - vlan = __vlan_find_dev_deep(br, skb->vlan_proto, + vlan = __vlan_find_dev_deep_rcu(br, skb->vlan_proto, vlan_tx_tag_get(skb) & VLAN_VID_MASK); return vlan ? vlan : br; -- cgit v1.2.3 From 94986198f5e1f949b2b78373b8a4da803cdcc7f3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 11 May 2014 19:27:47 +0200 Subject: ASoC: dapm: Handle SND_SOC_DAPM_REG() generically Commit commit de9ba98b6d ("ASoC: dapm: Make widget power register settings more flexible") added generic support for on_val/off_val in the DAPM core. With this in place there is no need anymore for having a special event callback for SND_SOC_DAPM_REG() widgets. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 7 ++----- sound/soc/soc-dapm.c | 20 -------------------- 2 files changed, 2 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 75020f52acdd..5932d66fd654 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -256,9 +256,8 @@ struct device; /* generic widgets */ #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ { .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \ - .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \ - .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ - .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} + .reg = wreg, .shift = wshift, .mask = wmask, \ + .on_val = won_val, .off_val = woff_val, } #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \ { .id = snd_soc_dapm_supply, .name = wname, \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ @@ -362,8 +361,6 @@ struct regulator; struct snd_soc_dapm_widget_list; struct snd_soc_dapm_update; -int dapm_reg_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); int dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int dapm_clock_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d0c61c120ac1..98e20de50e2d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1058,26 +1058,6 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, return paths; } -/* - * Handler for generic register modifier widget. - */ -int dapm_reg_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - unsigned int val; - - if (SND_SOC_DAPM_EVENT_ON(event)) - val = w->on_val; - else - val = w->off_val; - - soc_widget_update_bits(w, -(w->reg + 1), - w->mask << w->shift, val << w->shift); - - return 0; -} -EXPORT_SYMBOL_GPL(dapm_reg_event); - /* * Handler for regulator supply widget. */ -- cgit v1.2.3 From 797f283b61e43c54f37d20c1ef3f9682837ef67a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 May 2014 09:39:41 +0200 Subject: ASoC: Remove runtime field from DAI This was initially removed in commit 6423c1875 ("ASoC: Remove runtime field from DAI"), but was, presumably by accident, brought back in commit f0fba2ad1 ("ASoC: multi-component - ASoC Multi-Component Support"). But has never been initialized to anything but NULL ever since. This commit removes it again. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 1 - sound/soc/soc-compress.c | 1 - sound/soc/soc-pcm.c | 1 - 3 files changed, 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index e2c3e45d0656..688f2ba8009f 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -252,7 +252,6 @@ struct snd_soc_dai { unsigned int symmetric_rates:1; unsigned int symmetric_channels:1; unsigned int symmetric_samplebits:1; - struct snd_pcm_runtime *runtime; unsigned int active; unsigned char probed:1; diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 91083e6a6b38..5320e8664a59 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -203,7 +203,6 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); - cpu_dai->runtime = NULL; if (cstream->direction == SND_COMPRESS_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2cedf09f6d96..cc002522fd5e 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -555,7 +555,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); - cpu_dai->runtime = NULL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { -- cgit v1.2.3 From 907abd51012b9b0b0b687e97d5ebadbddbc682fe Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 3 Mar 2014 11:36:43 +0900 Subject: mmc: dw_mmc: remove unused member variable. Since using the device-tree, didn't use the callback pointer. So removed the unused callback pointer. When the set_power callback is used, it should be added in future. Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 33 +++------------------------------ include/linux/mmc/dw_mmc.h | 14 -------------- 2 files changed, 3 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 55c34cb702d4..81991eca5671 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -850,8 +850,6 @@ static void __dw_mci_start_request(struct dw_mci *host, u32 cmdflags; mrq = slot->mrq; - if (host->pdata->select_slot) - host->pdata->select_slot(slot->id); host->cur_slot = slot; host->mrq = mrq; @@ -985,17 +983,11 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - /* Power up slot */ - if (slot->host->pdata->setpower) - slot->host->pdata->setpower(slot->id, mmc->ocr_avail); regs = mci_readl(slot->host, PWREN); regs |= (1 << slot->id); mci_writel(slot->host, PWREN, regs); break; case MMC_POWER_OFF: - /* Power down slot */ - if (slot->host->pdata->setpower) - slot->host->pdata->setpower(slot->id, 0); regs = mci_readl(slot->host, PWREN); regs &= ~(1 << slot->id); mci_writel(slot->host, PWREN, regs); @@ -1009,13 +1001,10 @@ static int dw_mci_get_ro(struct mmc_host *mmc) { int read_only; struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci_board *brd = slot->host->pdata; /* Use platform get_ro function, else try on board write protect */ if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) read_only = 0; - else if (brd->get_ro) - read_only = brd->get_ro(slot->id); else if (gpio_is_valid(slot->wp_gpio)) read_only = gpio_get_value(slot->wp_gpio); else @@ -1039,8 +1028,6 @@ static int dw_mci_get_cd(struct mmc_host *mmc) /* Use platform get_cd function, else try onboard card detect */ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) present = 1; - else if (brd->get_cd) - present = !brd->get_cd(slot->id); else if (!IS_ERR_VALUE(gpio_cd)) present = gpio_cd; else @@ -2138,17 +2125,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->f_max = freq[1]; } - if (host->pdata->get_ocr) - mmc->ocr_avail = host->pdata->get_ocr(id); - else - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - - /* - * Start with slot power disabled, it will be enabled when a card - * is detected. - */ - if (host->pdata->setpower) - host->pdata->setpower(id, 0); + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; if (host->pdata->caps) mmc->caps = host->pdata->caps; @@ -2217,10 +2194,6 @@ err_setup_bus: static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) { - /* Shutdown detect IRQ */ - if (slot->host->pdata->exit) - slot->host->pdata->exit(id); - /* Debugfs stuff is cleaned up by mmc core */ mmc_remove_host(slot->mmc); slot->host->slot[id] = NULL; @@ -2395,9 +2368,9 @@ int dw_mci_probe(struct dw_mci *host) } } - if (!host->pdata->select_slot && host->pdata->num_slots > 1) { + if (host->pdata->num_slots > 1) { dev_err(host->dev, - "Platform data must supply select_slot function\n"); + "Platform data must supply num_slots.\n"); return -ENODEV; } diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 6ce7d2cd3c7a..babaea93bca6 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -248,20 +248,6 @@ struct dw_mci_board { /* delay in mS before detecting cards after interrupt */ u32 detect_delay_ms; - int (*init)(u32 slot_id, irq_handler_t , void *); - int (*get_ro)(u32 slot_id); - int (*get_cd)(u32 slot_id); - int (*get_ocr)(u32 slot_id); - int (*get_bus_wd)(u32 slot_id); - /* - * Enable power to selected slot and set voltage to desired level. - * Voltage levels are specified using MMC_VDD_xxx defines defined - * in linux/mmc/host.h file. - */ - void (*setpower)(u32 slot_id, u32 volt); - void (*exit)(u32 slot_id); - void (*select_slot)(u32 slot_id); - struct dw_mci_dma_ops *dma_ops; struct dma_pdata *data; struct block_settings *blk_settings; -- cgit v1.2.3 From cdc991790c51c693d0c347a5286af017826a5d01 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 23 Apr 2014 17:07:35 +0900 Subject: mmc: drop the speed mode of card's state Timing mode identifier has same role and can take the place of speed mode. This change removes all related speed mode. Signed-off-by: Seungwon Jeon Tested-by: Jaehoon Chung Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/bus.c | 8 ++++---- drivers/mmc/core/core.c | 3 +-- drivers/mmc/core/mmc.c | 11 +++-------- drivers/mmc/core/sd.c | 16 +++------------- drivers/mmc/core/sd.h | 1 - drivers/mmc/core/sdio.c | 8 ++------ drivers/net/wireless/rsi/rsi_91x_sdio.c | 4 +--- include/linux/mmc/card.h | 23 ++++++----------------- include/linux/mmc/host.h | 23 +++++++++++++++++++++++ 9 files changed, 43 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 824644875d41..f37e9d6af84a 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -341,16 +341,16 @@ int mmc_add_card(struct mmc_card *card) if (mmc_host_is_spi(card->host)) { pr_info("%s: new %s%s%s card on SPI\n", mmc_hostname(card->host), - mmc_card_highspeed(card) ? "high speed " : "", - mmc_card_ddr_mode(card) ? "DDR " : "", + mmc_card_hs(card) ? "high speed " : "", + mmc_card_ddr52(card) ? "DDR " : "", type); } else { pr_info("%s: new %s%s%s%s%s card at address %04x\n", mmc_hostname(card->host), mmc_card_uhs(card) ? "ultra high speed " : - (mmc_card_highspeed(card) ? "high speed " : ""), + (mmc_card_hs(card) ? "high speed " : ""), (mmc_card_hs200(card) ? "HS200 " : ""), - mmc_card_ddr_mode(card) ? "DDR " : "", + mmc_card_ddr52(card) ? "DDR " : "", uhs_bus_speed_mode, type, card->rca); } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d97dff5fab62..02baa30653fa 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2192,7 +2192,7 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) { struct mmc_command cmd = {0}; - if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card)) + if (mmc_card_blockaddr(card) || mmc_card_ddr52(card)) return 0; cmd.opcode = MMC_SET_BLOCKLEN; @@ -2272,7 +2272,6 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check) } } - host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_DDR); if (mmc_host_is_spi(host)) { host->ios.chip_select = MMC_CS_HIGH; host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index fbcf93d81858..31220529e171 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1083,11 +1083,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } else { if (card->ext_csd.hs_max_dtr > 52000000 && host->caps2 & MMC_CAP2_HS200) { - mmc_card_set_hs200(card); mmc_set_timing(card->host, MMC_TIMING_MMC_HS200); } else { - mmc_card_set_highspeed(card); mmc_set_timing(card->host, MMC_TIMING_MMC_HS); } } @@ -1098,10 +1096,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ max_dtr = (unsigned int)-1; - if (mmc_card_highspeed(card) || mmc_card_hs200(card)) { + if (mmc_card_hs(card) || mmc_card_hs200(card)) { if (max_dtr > card->ext_csd.hs_max_dtr) max_dtr = card->ext_csd.hs_max_dtr; - if (mmc_card_highspeed(card) && (max_dtr > 52000000)) + if (mmc_card_hs(card) && (max_dtr > 52000000)) max_dtr = 52000000; } else if (max_dtr > card->csd.max_dtr) { max_dtr = card->csd.max_dtr; @@ -1112,7 +1110,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, /* * Indicate DDR mode (if supported). */ - if (mmc_card_highspeed(card)) { + if (mmc_card_hs(card)) { if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) && (host->caps & MMC_CAP_1_8V_DDR)) ddr = MMC_1_8V_DDR_MODE; @@ -1255,7 +1253,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (err) goto err; } - mmc_card_set_ddr_mode(card); mmc_set_timing(card->host, MMC_TIMING_MMC_DDR52); mmc_set_bus_width(card->host, bus_width); } @@ -1499,7 +1496,6 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) err = mmc_sleep(host); else if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); - host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); if (!err) { mmc_power_off(host); @@ -1629,7 +1625,6 @@ static int mmc_power_restore(struct mmc_host *host) { int ret; - host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); mmc_claim_host(host); ret = mmc_init_card(host, host->card->ocr, host->card); mmc_release_host(host); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index aef515755e5b..0c44510bf717 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -887,7 +887,7 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card) { unsigned max_dtr = (unsigned int)-1; - if (mmc_card_highspeed(card)) { + if (mmc_card_hs(card)) { if (max_dtr > card->sw_caps.hs_max_dtr) max_dtr = card->sw_caps.hs_max_dtr; } else if (max_dtr > card->csd.max_dtr) { @@ -897,12 +897,6 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card) return max_dtr; } -void mmc_sd_go_highspeed(struct mmc_card *card) -{ - mmc_card_set_highspeed(card); - mmc_set_timing(card->host, MMC_TIMING_SD_HS); -} - /* * Handle the detection and initialisation of a card. * @@ -977,16 +971,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, err = mmc_sd_init_uhs_card(card); if (err) goto free_card; - - /* Card is an ultra-high-speed card */ - mmc_card_set_uhs(card); } else { /* * Attempt to change to high-speed (if supported) */ err = mmc_sd_switch_hs(card); if (err > 0) - mmc_sd_go_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_SD_HS); else if (err) goto free_card; @@ -1081,7 +1072,7 @@ static int _mmc_sd_suspend(struct mmc_host *host) if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); - host->card->state &= ~MMC_STATE_HIGHSPEED; + if (!err) { mmc_power_off(host); mmc_card_set_suspended(host->card); @@ -1190,7 +1181,6 @@ static int mmc_sd_power_restore(struct mmc_host *host) { int ret; - host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_claim_host(host); ret = mmc_sd_init_card(host, host->card->ocr, host->card); mmc_release_host(host); diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h index 4b34b24f3f76..aab824a9a7f3 100644 --- a/drivers/mmc/core/sd.h +++ b/drivers/mmc/core/sd.h @@ -12,6 +12,5 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, bool reinit); unsigned mmc_sd_get_max_clock(struct mmc_card *card); int mmc_sd_switch_hs(struct mmc_card *card); -void mmc_sd_go_highspeed(struct mmc_card *card); #endif diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 9933e426bc36..e636d9e99e4a 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -363,7 +363,7 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) { unsigned max_dtr; - if (mmc_card_highspeed(card)) { + if (mmc_card_hs(card)) { /* * The SDIO specification doesn't mention how * the CIS transfer speed register relates to @@ -733,7 +733,6 @@ try_again: mmc_set_clock(host, card->cis.max_dtr); if (card->cccr.high_speed) { - mmc_card_set_highspeed(card); mmc_set_timing(card->host, MMC_TIMING_SD_HS); } @@ -792,16 +791,13 @@ try_again: err = mmc_sdio_init_uhs_card(card); if (err) goto remove; - - /* Card is an ultra-high-speed card */ - mmc_card_set_uhs(card); } else { /* * Switch to high-speed (if supported). */ err = sdio_enable_hs(card); if (err > 0) - mmc_sd_go_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_SD_HS); else if (err) goto remove; diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c index 2e39d38d6a9e..46e7af446f01 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -285,7 +285,6 @@ static void rsi_reset_card(struct sdio_func *pfunction) if (err) { rsi_dbg(ERR_ZONE, "%s: CCCR speed reg read failed: %d\n", __func__, err); - card->state &= ~MMC_STATE_HIGHSPEED; } else { err = rsi_cmd52writebyte(card, SDIO_CCCR_SPEED, @@ -296,14 +295,13 @@ static void rsi_reset_card(struct sdio_func *pfunction) __func__, err); return; } - mmc_card_set_highspeed(card); host->ios.timing = MMC_TIMING_SD_HS; host->ops->set_ios(host, &host->ios); } } /* Set clock */ - if (mmc_card_highspeed(card)) + if (mmc_card_hs(card)) clock = 50000000; else clock = card->cis.max_dtr; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index aa7e57f60fb2..aadeaf155d0e 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -194,6 +194,7 @@ struct sdio_cis { }; struct mmc_host; +struct mmc_ios; struct sdio_func; struct sdio_func_tuple; @@ -250,15 +251,11 @@ struct mmc_card { unsigned int state; /* (our) card state */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_READONLY (1<<1) /* card is read-only */ -#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */ -#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */ -#define MMC_STATE_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */ -#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */ -#define MMC_CARD_SDXC (1<<6) /* card is SDXC */ -#define MMC_CARD_REMOVED (1<<7) /* card has been removed */ -#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ -#define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */ -#define MMC_STATE_SUSPENDED (1<<11) /* card is suspended */ +#define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */ +#define MMC_CARD_SDXC (1<<3) /* card is SDXC */ +#define MMC_CARD_REMOVED (1<<4) /* card has been removed */ +#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */ +#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -418,11 +415,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) -#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) -#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200) #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) -#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR) -#define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) #define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS) @@ -430,11 +423,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) -#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) -#define mmc_card_set_hs200(c) ((c)->state |= MMC_STATE_HIGHSPEED_200) #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) -#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR) -#define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) #define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0cf705c83998..a43853779799 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -17,6 +17,7 @@ #include #include +#include #include struct mmc_ios { @@ -478,4 +479,26 @@ static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) return host->ios.clock; } #endif + +static inline int mmc_card_hs(struct mmc_card *card) +{ + return card->host->ios.timing == MMC_TIMING_SD_HS || + card->host->ios.timing == MMC_TIMING_MMC_HS; +} + +static inline int mmc_card_uhs(struct mmc_card *card) +{ + return card->host->ios.timing >= MMC_TIMING_UHS_SDR12 && + card->host->ios.timing <= MMC_TIMING_UHS_DDR50; +} + +static inline bool mmc_card_hs200(struct mmc_card *card) +{ + return card->host->ios.timing == MMC_TIMING_MMC_HS200; +} + +static inline bool mmc_card_ddr52(struct mmc_card *card) +{ + return card->host->ios.timing == MMC_TIMING_MMC_DDR52; +} #endif /* LINUX_MMC_HOST_H */ -- cgit v1.2.3 From 2415c0ef618b3cd95581c7f633cbab78b29b7ab0 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 23 Apr 2014 17:07:58 +0900 Subject: mmc: identify available device type to select Device types which are supported by both host and device can be identified when EXT_CSD is read. There is no need to check host's capability anymore. Signed-off-by: Seungwon Jeon Tested-by: Jaehoon Chung Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 72 ++++++++++++++++++++++++++---------------------- include/linux/mmc/card.h | 2 +- include/linux/mmc/host.h | 6 ---- include/linux/mmc/mmc.h | 12 +++++--- 4 files changed, 48 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 31220529e171..b5691fee9629 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -243,28 +243,46 @@ static void mmc_select_card_type(struct mmc_card *card) u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; u32 caps = host->caps, caps2 = host->caps2; unsigned int hs_max_dtr = 0; + unsigned int avail_type = 0; - if (card_type & EXT_CSD_CARD_TYPE_26) + if (caps & MMC_CAP_MMC_HIGHSPEED && + card_type & EXT_CSD_CARD_TYPE_HS_26) { hs_max_dtr = MMC_HIGH_26_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_HS_26; + } if (caps & MMC_CAP_MMC_HIGHSPEED && - card_type & EXT_CSD_CARD_TYPE_52) + card_type & EXT_CSD_CARD_TYPE_HS_52) { hs_max_dtr = MMC_HIGH_52_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_HS_52; + } - if ((caps & MMC_CAP_1_8V_DDR && - card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) || - (caps & MMC_CAP_1_2V_DDR && - card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)) + if (caps & MMC_CAP_1_8V_DDR && + card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) { hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; + } + + if (caps & MMC_CAP_1_2V_DDR && + card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { + hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_2V; + } - if ((caps2 & MMC_CAP2_HS200_1_8V_SDR && - card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) || - (caps2 & MMC_CAP2_HS200_1_2V_SDR && - card_type & EXT_CSD_CARD_TYPE_SDR_1_2V)) + if (caps2 & MMC_CAP2_HS200_1_8V_SDR && + card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) { hs_max_dtr = MMC_HS200_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V; + } + + if (caps2 & MMC_CAP2_HS200_1_2V_SDR && + card_type & EXT_CSD_CARD_TYPE_HS200_1_2V) { + hs_max_dtr = MMC_HS200_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V; + } card->ext_csd.hs_max_dtr = hs_max_dtr; - card->ext_csd.card_type = card_type; + card->mmc_avail_type = avail_type; } /* @@ -800,12 +818,10 @@ static int mmc_select_hs200(struct mmc_card *card) host = card->host; - if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && - host->caps2 & MMC_CAP2_HS200_1_2V_SDR) + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); - if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V && - host->caps2 & MMC_CAP2_HS200_1_8V_SDR) + if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V) err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); /* If fails try again during next card power cycle */ @@ -1064,10 +1080,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ if (card->ext_csd.hs_max_dtr != 0) { err = 0; - if (card->ext_csd.hs_max_dtr > 52000000 && - host->caps2 & MMC_CAP2_HS200) + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) err = mmc_select_hs200(card); - else if (host->caps & MMC_CAP_MMC_HIGHSPEED) + else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1, card->ext_csd.generic_cmd6_time, @@ -1081,13 +1096,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, mmc_hostname(card->host)); err = 0; } else { - if (card->ext_csd.hs_max_dtr > 52000000 && - host->caps2 & MMC_CAP2_HS200) { + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) mmc_set_timing(card->host, MMC_TIMING_MMC_HS200); - } else { + else mmc_set_timing(card->host, MMC_TIMING_MMC_HS); - } } } @@ -1110,14 +1123,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, /* * Indicate DDR mode (if supported). */ - if (mmc_card_hs(card)) { - if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) - && (host->caps & MMC_CAP_1_8V_DDR)) - ddr = MMC_1_8V_DDR_MODE; - else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) - && (host->caps & MMC_CAP_1_2V_DDR)) - ddr = MMC_1_2V_DDR_MODE; - } + if (mmc_card_hs(card)) + ddr = card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52; /* * Indicate HS200 SDR mode (if supported). @@ -1137,8 +1144,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * 3. set the clock to > 52Mhz <=200MHz and * 4. execute tuning for HS200 */ - if ((host->caps2 & MMC_CAP2_HS200) && - card->host->ops->execute_tuning) { + if (card->host->ops->execute_tuning) { mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK_HS200); @@ -1247,7 +1253,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * * WARNING: eMMC rules are NOT the same as SD DDR */ - if (ddr == MMC_1_2V_DDR_MODE) { + if (ddr & EXT_CSD_CARD_TYPE_DDR_1_2V) { err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); if (err) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index aadeaf155d0e..fe31f8d89a03 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -68,7 +68,6 @@ struct mmc_ext_csd { #define MMC_HIGH_DDR_MAX_DTR 52000000 #define MMC_HS200_MAX_DTR 200000000 unsigned int sectors; - unsigned int card_type; unsigned int hc_erase_size; /* In sectors */ unsigned int hc_erase_timeout; /* In milliseconds */ unsigned int sec_trim_mult; /* Secure trim multiplier */ @@ -298,6 +297,7 @@ struct mmc_card { struct sdio_func_tuple *tuples; /* unknown common tuples */ unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ + unsigned int mmc_avail_type; /* supported device type by both host and card */ struct dentry *debugfs_root; struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index a43853779799..6b1e9ee6ca10 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -62,12 +62,6 @@ struct mmc_ios { #define MMC_TIMING_MMC_DDR52 8 #define MMC_TIMING_MMC_HS200 9 -#define MMC_SDR_MODE 0 -#define MMC_1_2V_DDR_MODE 1 -#define MMC_1_8V_DDR_MODE 2 -#define MMC_1_2V_SDR_MODE 3 -#define MMC_1_8V_SDR_MODE 4 - unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ #define MMC_SIGNAL_VOLTAGE_330 0 diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 50bcde3677ca..f734c0c64575 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -354,18 +354,22 @@ struct _mmc_csd { #define EXT_CSD_CMD_SET_SECURE (1<<1) #define EXT_CSD_CMD_SET_CPSECURE (1<<2) -#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */ -#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */ #define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */ +#define EXT_CSD_CARD_TYPE_HS_26 (1<<0) /* Card can run at 26MHz */ +#define EXT_CSD_CARD_TYPE_HS_52 (1<<1) /* Card can run at 52MHz */ +#define EXT_CSD_CARD_TYPE_HS (EXT_CSD_CARD_TYPE_HS_26 | \ + EXT_CSD_CARD_TYPE_HS_52) #define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */ /* DDR mode @1.8V or 3V I/O */ #define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */ /* DDR mode @1.2V I/O */ #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \ | EXT_CSD_CARD_TYPE_DDR_1_2V) -#define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at 200MHz */ -#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */ +#define EXT_CSD_CARD_TYPE_HS200_1_8V (1<<4) /* Card can run at 200MHz */ +#define EXT_CSD_CARD_TYPE_HS200_1_2V (1<<5) /* Card can run at 200MHz */ /* SDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ + EXT_CSD_CARD_TYPE_HS200_1_2V) #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ -- cgit v1.2.3 From 577fb13199b11d8cd75609183649be4b5561243f Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 23 Apr 2014 17:08:44 +0900 Subject: mmc: rework selection of bus speed mode Current implementation for bus speed mode selection is too complicated. This patch is to simplify the codes and remove some duplicate parts. The following changes are including: * Adds functions for each mode selection(HS, HS-DDR, HS200 and etc) * Rearranged the mode selection sequence with supported device type * Adds maximum speed for HS200 mode(hs200_max_dtr) * Adds field definition for HS_TIMING of EXT_CSD Signed-off-by: Seungwon Jeon Tested-by: Jaehoon Chung Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/debugfs.c | 2 +- drivers/mmc/core/mmc.c | 431 ++++++++++++++++++++++++--------------------- include/linux/mmc/card.h | 1 + include/linux/mmc/mmc.h | 4 + 4 files changed, 238 insertions(+), 200 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 509229b48b55..1f730dbfaeea 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -139,7 +139,7 @@ static int mmc_ios_show(struct seq_file *s, void *data) str = "mmc DDR52"; break; case MMC_TIMING_MMC_HS200: - str = "mmc high-speed SDR200"; + str = "mmc HS200"; break; default: str = "invalid"; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4538541ac5ab..bec6786efd19 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -242,7 +242,7 @@ static void mmc_select_card_type(struct mmc_card *card) struct mmc_host *host = card->host; u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; u32 caps = host->caps, caps2 = host->caps2; - unsigned int hs_max_dtr = 0; + unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; unsigned int avail_type = 0; if (caps & MMC_CAP_MMC_HIGHSPEED && @@ -271,17 +271,18 @@ static void mmc_select_card_type(struct mmc_card *card) if (caps2 & MMC_CAP2_HS200_1_8V_SDR && card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) { - hs_max_dtr = MMC_HS200_MAX_DTR; + hs200_max_dtr = MMC_HS200_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V; } if (caps2 & MMC_CAP2_HS200_1_2V_SDR && card_type & EXT_CSD_CARD_TYPE_HS200_1_2V) { - hs_max_dtr = MMC_HS200_MAX_DTR; + hs200_max_dtr = MMC_HS200_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V; } card->ext_csd.hs_max_dtr = hs_max_dtr; + card->ext_csd.hs200_max_dtr = hs200_max_dtr; card->mmc_avail_type = avail_type; } @@ -825,37 +826,46 @@ static int mmc_select_powerclass(struct mmc_card *card) } /* - * Selects the desired buswidth and switch to the HS200 mode - * if bus width set without error + * Set the bus speed for the selected speed mode. */ -static int mmc_select_hs200(struct mmc_card *card) +static void mmc_set_bus_speed(struct mmc_card *card) +{ + unsigned int max_dtr = (unsigned int)-1; + + if (mmc_card_hs200(card) && max_dtr > card->ext_csd.hs200_max_dtr) + max_dtr = card->ext_csd.hs200_max_dtr; + else if (mmc_card_hs(card) && max_dtr > card->ext_csd.hs_max_dtr) + max_dtr = card->ext_csd.hs_max_dtr; + else if (max_dtr > card->csd.max_dtr) + max_dtr = card->csd.max_dtr; + + mmc_set_clock(card->host, max_dtr); +} + +/* + * Select the bus width amoung 4-bit and 8-bit(SDR). + * If the bus width is changed successfully, return the selected width value. + * Zero is returned instead of error value if the wide width is not supported. + */ +static int mmc_select_bus_width(struct mmc_card *card) { - int idx, err = -EINVAL; - struct mmc_host *host; static unsigned ext_csd_bits[] = { - EXT_CSD_BUS_WIDTH_4, EXT_CSD_BUS_WIDTH_8, + EXT_CSD_BUS_WIDTH_4, }; static unsigned bus_widths[] = { - MMC_BUS_WIDTH_4, MMC_BUS_WIDTH_8, + MMC_BUS_WIDTH_4, }; + struct mmc_host *host = card->host; + unsigned idx, bus_width = 0; + int err = 0; - BUG_ON(!card); - - host = card->host; - - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); - - if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); - - /* If fails try again during next card power cycle */ - if (err) - goto err; + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) && + !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) + return 0; - idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0; + idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 0 : 1; /* * Unlike SD, MMC cards dont have a configuration register to notify @@ -863,8 +873,7 @@ static int mmc_select_hs200(struct mmc_card *card) * the supported bus width or compare the ext csd values of current * bus width and ext csd values of 1 bit mode read earlier. */ - for (; idx >= 0; idx--) { - + for (; idx < ARRAY_SIZE(bus_widths); idx++) { /* * Host is capable of 8bit transfer, then switch * the device to work in 8bit transfer mode. If the @@ -879,26 +888,201 @@ static int mmc_select_hs200(struct mmc_card *card) if (err) continue; - mmc_set_bus_width(card->host, bus_widths[idx]); + bus_width = bus_widths[idx]; + mmc_set_bus_width(host, bus_width); + /* + * If controller can't handle bus width test, + * compare ext_csd previously read in 1 bit mode + * against ext_csd at new bus width + */ if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) - err = mmc_compare_ext_csds(card, bus_widths[idx]); + err = mmc_compare_ext_csds(card, bus_width); else - err = mmc_bus_test(card, bus_widths[idx]); - if (!err) + err = mmc_bus_test(card, bus_width); + + if (!err) { + err = bus_width; break; + } else { + pr_warn("%s: switch to bus width %d failed\n", + mmc_hostname(host), ext_csd_bits[idx]); + } } - /* switch to HS200 mode if bus width set successfully */ + return err; +} + +/* + * Switch to the high-speed mode + */ +static int mmc_select_hs(struct mmc_card *card) +{ + int err; + + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, + card->ext_csd.generic_cmd6_time, + true, true, true); if (!err) + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + + return err; +} + +/* + * Activate wide bus and DDR if supported. + */ +static int mmc_select_hs_ddr(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + u32 bus_width, ext_csd_bits; + int err = 0; + + if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52)) + return 0; + + bus_width = host->ios.bus_width; + if (bus_width == MMC_BUS_WIDTH_1) + return 0; + + ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + ext_csd_bits, + card->ext_csd.generic_cmd6_time); + if (err) { + pr_warn("%s: switch to bus width %d ddr failed\n", + mmc_hostname(host), 1 << bus_width); + return err; + } + + /* + * eMMC cards can support 3.3V to 1.2V i/o (vccq) + * signaling. + * + * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. + * + * 1.8V vccq at 3.3V core voltage (vcc) is not required + * in the JEDEC spec for DDR. + * + * Do not force change in vccq since we are obviously + * working and no change to vccq is needed. + * + * WARNING: eMMC rules are NOT the same as SD DDR + */ + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { + err = __mmc_set_signal_voltage(host, + MMC_SIGNAL_VOLTAGE_120); + if (err) + return err; + } + + mmc_set_timing(host, MMC_TIMING_MMC_DDR52); + + return err; +} + +/* + * For device supporting HS200 mode, the following sequence + * should be done before executing the tuning process. + * 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported) + * 2. switch to HS200 mode + * 3. set the clock to > 52Mhz and <=200MHz + */ +static int mmc_select_hs200(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = -EINVAL; + + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); + + if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + + /* If fails try again during next card power cycle */ + if (err) + goto err; + + /* + * Set the bus width(4 or 8) with host's support and + * switch to HS200 mode if bus width is set successfully. + */ + err = mmc_select_bus_width(card); + if (!IS_ERR_VALUE(err)) { err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 2, - card->ext_csd.generic_cmd6_time, - true, true, true); + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, + card->ext_csd.generic_cmd6_time, + true, true, true); + if (!err) + mmc_set_timing(host, MMC_TIMING_MMC_HS200); + } err: return err; } +/* + * Activate High Speed or HS200 mode if supported. + */ +static int mmc_select_timing(struct mmc_card *card) +{ + int err = 0; + + if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 && + card->ext_csd.hs_max_dtr == 0)) + goto bus_speed; + + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) + err = mmc_select_hs200(card); + else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) + err = mmc_select_hs(card); + + if (err && err != -EBADMSG) + return err; + + if (err) { + pr_warn("%s: switch to %s failed\n", + mmc_card_hs(card) ? "high-speed" : + (mmc_card_hs200(card) ? "hs200" : ""), + mmc_hostname(card->host)); + err = 0; + } + +bus_speed: + /* + * Set the bus speed to the selected bus timing. + * If timing is not selected, backward compatible is the default. + */ + mmc_set_bus_speed(card); + return err; +} + +/* + * Execute tuning sequence to seek the proper bus operating + * conditions for HS200, which sends CMD21 to the device. + */ +static int mmc_hs200_tuning(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + + if (host->ops->execute_tuning) { + mmc_host_clk_hold(host); + err = host->ops->execute_tuning(host, + MMC_SEND_TUNING_BLOCK_HS200); + mmc_host_clk_release(host); + + if (err) + pr_warn("%s: tuning execution failed\n", + mmc_hostname(host)); + } + + return err; +} + /* * Handle the detection and initialisation of a card. * @@ -909,9 +1093,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card; - int err, ddr = 0; + int err; u32 cid[4]; - unsigned int max_dtr; u32 rocr; u8 *ext_csd = NULL; @@ -1103,173 +1286,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* - * Activate high speed (if supported) - */ - if (card->ext_csd.hs_max_dtr != 0) { - err = 0; - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) - err = mmc_select_hs200(card); - else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) - err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1, - card->ext_csd.generic_cmd6_time, - true, true, true); - - if (err && err != -EBADMSG) - goto free_card; - - if (err) { - pr_warning("%s: switch to highspeed failed\n", - mmc_hostname(card->host)); - err = 0; - } else { - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) - mmc_set_timing(card->host, - MMC_TIMING_MMC_HS200); - else - mmc_set_timing(card->host, MMC_TIMING_MMC_HS); - } - } - - /* - * Compute bus speed. - */ - max_dtr = (unsigned int)-1; - - if (mmc_card_hs(card) || mmc_card_hs200(card)) { - if (max_dtr > card->ext_csd.hs_max_dtr) - max_dtr = card->ext_csd.hs_max_dtr; - if (mmc_card_hs(card) && (max_dtr > 52000000)) - max_dtr = 52000000; - } else if (max_dtr > card->csd.max_dtr) { - max_dtr = card->csd.max_dtr; - } - - mmc_set_clock(host, max_dtr); - - /* - * Indicate DDR mode (if supported). + * Select timing interface */ - if (mmc_card_hs(card)) - ddr = card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52; + err = mmc_select_timing(card); + if (err) + goto free_card; - /* - * Indicate HS200 SDR mode (if supported). - */ if (mmc_card_hs200(card)) { - u32 ext_csd_bits; - u32 bus_width = card->host->ios.bus_width; - - /* - * For devices supporting HS200 mode, the bus width has - * to be set before executing the tuning function. If - * set before tuning, then device will respond with CRC - * errors for responses on CMD line. So for HS200 the - * sequence will be - * 1. set bus width 4bit / 8 bit (1 bit not supported) - * 2. switch to HS200 mode - * 3. set the clock to > 52Mhz <=200MHz and - * 4. execute tuning for HS200 - */ - if (card->host->ops->execute_tuning) { - mmc_host_clk_hold(card->host); - err = card->host->ops->execute_tuning(card->host, - MMC_SEND_TUNING_BLOCK_HS200); - mmc_host_clk_release(card->host); - } - if (err) { - pr_warning("%s: tuning execution failed\n", - mmc_hostname(card->host)); + err = mmc_hs200_tuning(card); + if (err) goto err; - } - - ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? - EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; - } - - /* - * Activate wide bus and DDR (if supported). - */ - if (!mmc_card_hs200(card) && - (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && - (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { - static unsigned ext_csd_bits[][2] = { - { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, - { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, - { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, - }; - static unsigned bus_widths[] = { - MMC_BUS_WIDTH_8, - MMC_BUS_WIDTH_4, - MMC_BUS_WIDTH_1 - }; - unsigned idx, bus_width = 0; - - if (host->caps & MMC_CAP_8_BIT_DATA) - idx = 0; - else - idx = 1; - for (; idx < ARRAY_SIZE(bus_widths); idx++) { - bus_width = bus_widths[idx]; - if (bus_width == MMC_BUS_WIDTH_1) - ddr = 0; /* no DDR for 1-bit width */ - - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - ext_csd_bits[idx][0], - card->ext_csd.generic_cmd6_time); - if (!err) { - mmc_set_bus_width(card->host, bus_width); - - /* - * If controller can't handle bus width test, - * compare ext_csd previously read in 1 bit mode - * against ext_csd at new bus width - */ - if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) - err = mmc_compare_ext_csds(card, - bus_width); - else - err = mmc_bus_test(card, bus_width); - if (!err) - break; - } - } - - if (!err && ddr) { - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - ext_csd_bits[idx][1], - card->ext_csd.generic_cmd6_time); - } - if (err) { - pr_warning("%s: switch to bus width %d ddr %d " - "failed\n", mmc_hostname(card->host), - 1 << bus_width, ddr); - goto free_card; - } else if (ddr) { - /* - * eMMC cards can support 3.3V to 1.2V i/o (vccq) - * signaling. - * - * EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq. - * - * 1.8V vccq at 3.3V core voltage (vcc) is not required - * in the JEDEC spec for DDR. - * - * Do not force change in vccq since we are obviously - * working and no change to vccq is needed. - * - * WARNING: eMMC rules are NOT the same as SD DDR - */ - if (ddr & EXT_CSD_CARD_TYPE_DDR_1_2V) { - err = __mmc_set_signal_voltage(host, - MMC_SIGNAL_VOLTAGE_120); - if (err) - goto err; - } - mmc_set_timing(card->host, MMC_TIMING_MMC_DDR52); - mmc_set_bus_width(card->host, bus_width); + } else if (mmc_card_hs(card)) { + /* Select the desired bus width optionally */ + err = mmc_select_bus_width(card); + if (!IS_ERR_VALUE(err)) { + err = mmc_select_hs_ddr(card); + if (err) + goto err; } } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index fe31f8d89a03..176073692872 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -63,6 +63,7 @@ struct mmc_ext_csd { unsigned int power_off_longtime; /* Units: ms */ u8 power_off_notification; /* state */ unsigned int hs_max_dtr; + unsigned int hs200_max_dtr; #define MMC_HIGH_26_MAX_DTR 26000000 #define MMC_HIGH_52_MAX_DTR 52000000 #define MMC_HIGH_DDR_MAX_DTR 52000000 diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index f734c0c64575..f429f13be433 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -377,6 +377,10 @@ struct _mmc_csd { #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ +#define EXT_CSD_TIMING_HS 1 /* High speed */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ + #define EXT_CSD_SEC_ER_EN BIT(0) #define EXT_CSD_SEC_BD_BLK_EN BIT(2) #define EXT_CSD_SEC_GB_CL_EN BIT(4) -- cgit v1.2.3 From 0a5b6438ee482696360bb013e67b8488f63d3e9e Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 23 Apr 2014 17:14:58 +0900 Subject: mmc: add support for HS400 mode of eMMC5.0 This patch adds HS400 mode support for eMMC5.0 device. HS400 mode is high speed DDR interface timing from HS200. Clock frequency is up to 200MHz and only 8-bit bus width is supported. In addition, tuning process of HS200 is required to synchronize the command response on the CMD line because CMD input timing for HS400 mode is the same as HS200 mode. Signed-off-by: Seungwon Jeon Reviewed-by: Jackey Shen Tested-by: Jaehoon Chung Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/bus.c | 1 + drivers/mmc/core/debugfs.c | 3 ++ drivers/mmc/core/mmc.c | 98 +++++++++++++++++++++++++++++++++++++++++++--- include/linux/mmc/card.h | 1 + include/linux/mmc/host.h | 14 +++++++ include/linux/mmc/mmc.h | 7 +++- 6 files changed, 118 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index f37e9d6af84a..d2dbf02022bd 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -349,6 +349,7 @@ int mmc_add_card(struct mmc_card *card) mmc_hostname(card->host), mmc_card_uhs(card) ? "ultra high speed " : (mmc_card_hs(card) ? "high speed " : ""), + mmc_card_hs400(card) ? "HS400 " : (mmc_card_hs200(card) ? "HS200 " : ""), mmc_card_ddr52(card) ? "DDR " : "", uhs_bus_speed_mode, type, card->rca); diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 1f730dbfaeea..91eb16223246 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -141,6 +141,9 @@ static int mmc_ios_show(struct seq_file *s, void *data) case MMC_TIMING_MMC_HS200: str = "mmc HS200"; break; + case MMC_TIMING_MMC_HS400: + str = "mmc HS400"; + break; default: str = "invalid"; break; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index bec6786efd19..793c6f7ddb04 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -240,7 +240,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) static void mmc_select_card_type(struct mmc_card *card) { struct mmc_host *host = card->host; - u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; + u8 card_type = card->ext_csd.raw_card_type; u32 caps = host->caps, caps2 = host->caps2; unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; unsigned int avail_type = 0; @@ -281,6 +281,18 @@ static void mmc_select_card_type(struct mmc_card *card) avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V; } + if (caps2 & MMC_CAP2_HS400_1_8V && + card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) { + hs200_max_dtr = MMC_HS200_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_HS400_1_8V; + } + + if (caps2 & MMC_CAP2_HS400_1_2V && + card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) { + hs200_max_dtr = MMC_HS200_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V; + } + card->ext_csd.hs_max_dtr = hs_max_dtr; card->ext_csd.hs200_max_dtr = hs200_max_dtr; card->mmc_avail_type = avail_type; @@ -499,6 +511,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_PWR_CL_DDR_52_195]; card->ext_csd.raw_pwr_cl_ddr_52_360 = ext_csd[EXT_CSD_PWR_CL_DDR_52_360]; + card->ext_csd.raw_pwr_cl_ddr_200_360 = + ext_csd[EXT_CSD_PWR_CL_DDR_200_360]; } if (card->ext_csd.rev >= 5) { @@ -665,7 +679,10 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) (card->ext_csd.raw_pwr_cl_ddr_52_195 == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) && (card->ext_csd.raw_pwr_cl_ddr_52_360 == - bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360])); + bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) && + (card->ext_csd.raw_pwr_cl_ddr_200_360 == + bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360])); + if (err) err = -EINVAL; @@ -768,7 +785,9 @@ static int __mmc_select_powerclass(struct mmc_card *card, ext_csd->raw_pwr_cl_52_360 : ext_csd->raw_pwr_cl_ddr_52_360; else if (host->ios.clock <= MMC_HS200_MAX_DTR) - pwrclass_val = ext_csd->raw_pwr_cl_200_360; + pwrclass_val = (bus_width == EXT_CSD_DDR_BUS_WIDTH_8) ? + ext_csd->raw_pwr_cl_ddr_200_360 : + ext_csd->raw_pwr_cl_200_360; break; default: pr_warning("%s: Voltage range not supported " @@ -832,7 +851,8 @@ static void mmc_set_bus_speed(struct mmc_card *card) { unsigned int max_dtr = (unsigned int)-1; - if (mmc_card_hs200(card) && max_dtr > card->ext_csd.hs200_max_dtr) + if ((mmc_card_hs200(card) || mmc_card_hs400(card)) && + max_dtr > card->ext_csd.hs200_max_dtr) max_dtr = card->ext_csd.hs200_max_dtr; else if (mmc_card_hs(card) && max_dtr > card->ext_csd.hs_max_dtr) max_dtr = card->ext_csd.hs_max_dtr; @@ -985,6 +1005,61 @@ static int mmc_select_hs_ddr(struct mmc_card *card) return err; } +static int mmc_select_hs400(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + + /* + * HS400 mode requires 8-bit bus width + */ + if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 && + host->ios.bus_width == MMC_BUS_WIDTH_8)) + return 0; + + /* + * Before switching to dual data rate operation for HS400, + * it is required to convert from HS200 mode to HS mode. + */ + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + mmc_set_bus_speed(card); + + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, + card->ext_csd.generic_cmd6_time, + true, true, true); + if (err) { + pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n", + mmc_hostname(host), err); + return err; + } + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + EXT_CSD_DDR_BUS_WIDTH_8, + card->ext_csd.generic_cmd6_time); + if (err) { + pr_warn("%s: switch to bus width for hs400 failed, err:%d\n", + mmc_hostname(host), err); + return err; + } + + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, + card->ext_csd.generic_cmd6_time, + true, true, true); + if (err) { + pr_warn("%s: switch to hs400 failed, err:%d\n", + mmc_hostname(host), err); + return err; + } + + mmc_set_timing(host, MMC_TIMING_MMC_HS400); + mmc_set_bus_speed(card); + + return 0; +} + /* * For device supporting HS200 mode, the following sequence * should be done before executing the tuning process. @@ -1062,13 +1137,22 @@ bus_speed: /* * Execute tuning sequence to seek the proper bus operating - * conditions for HS200, which sends CMD21 to the device. + * conditions for HS200 and HS400, which sends CMD21 to the device. */ static int mmc_hs200_tuning(struct mmc_card *card) { struct mmc_host *host = card->host; int err = 0; + /* + * Timing should be adjusted to the HS400 target + * operation frequency for tuning process + */ + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 && + host->ios.bus_width == MMC_BUS_WIDTH_8) + if (host->ops->prepare_hs400_tuning) + host->ops->prepare_hs400_tuning(host, &host->ios); + if (host->ops->execute_tuning) { mmc_host_clk_hold(host); err = host->ops->execute_tuning(host, @@ -1296,6 +1380,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_hs200_tuning(card); if (err) goto err; + + err = mmc_select_hs400(card); + if (err) + goto err; } else if (mmc_card_hs(card)) { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 176073692872..d424b9de3aff 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -110,6 +110,7 @@ struct mmc_ext_csd { u8 raw_pwr_cl_200_360; /* 237 */ u8 raw_pwr_cl_ddr_52_195; /* 238 */ u8 raw_pwr_cl_ddr_52_360; /* 239 */ + u8 raw_pwr_cl_ddr_200_360; /* 253 */ u8 raw_bkops_status; /* 246 */ u8 raw_sectors[4]; /* 212 - 4 bytes */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 6b1e9ee6ca10..183087374215 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -61,6 +61,7 @@ struct mmc_ios { #define MMC_TIMING_UHS_DDR50 7 #define MMC_TIMING_MMC_DDR52 8 #define MMC_TIMING_MMC_HS200 9 +#define MMC_TIMING_MMC_HS400 10 unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ @@ -132,6 +133,9 @@ struct mmc_host_ops { /* The tuning command opcode value is different for SD and eMMC cards */ int (*execute_tuning)(struct mmc_host *host, u32 opcode); + + /* Prepare HS400 target operating frequency depending host driver */ + int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); void (*hw_reset)(struct mmc_host *host); void (*card_event)(struct mmc_host *host); @@ -274,6 +278,10 @@ struct mmc_host { #define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \ MMC_CAP2_PACKED_WR) #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ +#define MMC_CAP2_HS400_1_8V (1 << 15) /* Can support HS400 1.8V */ +#define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */ +#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \ + MMC_CAP2_HS400_1_2V) mmc_pm_flag_t pm_caps; /* supported pm features */ @@ -495,4 +503,10 @@ static inline bool mmc_card_ddr52(struct mmc_card *card) { return card->host->ios.timing == MMC_TIMING_MMC_DDR52; } + +static inline bool mmc_card_hs400(struct mmc_card *card) +{ + return card->host->ios.timing == MMC_TIMING_MMC_HS400; +} + #endif /* LINUX_MMC_HOST_H */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index f429f13be433..64ec963ed347 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -325,6 +325,7 @@ struct _mmc_csd { #define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ #define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ +#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */ #define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */ #define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */ #define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */ @@ -354,7 +355,6 @@ struct _mmc_csd { #define EXT_CSD_CMD_SET_SECURE (1<<1) #define EXT_CSD_CMD_SET_CPSECURE (1<<2) -#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */ #define EXT_CSD_CARD_TYPE_HS_26 (1<<0) /* Card can run at 26MHz */ #define EXT_CSD_CARD_TYPE_HS_52 (1<<1) /* Card can run at 52MHz */ #define EXT_CSD_CARD_TYPE_HS (EXT_CSD_CARD_TYPE_HS_26 | \ @@ -370,6 +370,10 @@ struct _mmc_csd { /* SDR mode @1.2V I/O */ #define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ EXT_CSD_CARD_TYPE_HS200_1_2V) +#define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz DDR, 1.8V */ +#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */ +#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \ + EXT_CSD_CARD_TYPE_HS400_1_2V) #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ @@ -380,6 +384,7 @@ struct _mmc_csd { #define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ #define EXT_CSD_TIMING_HS 1 /* High speed */ #define EXT_CSD_TIMING_HS200 2 /* HS200 */ +#define EXT_CSD_TIMING_HS400 3 /* HS400 */ #define EXT_CSD_SEC_ER_EN BIT(0) #define EXT_CSD_SEC_BD_BLK_EN BIT(2) -- cgit v1.2.3 From 4d1f52f9a9f9a63371dba589093b3ae90fc80c3d Mon Sep 17 00:00:00 2001 From: Tim Kryger Date: Tue, 6 May 2014 15:57:01 -0700 Subject: mmc: core: Improve support for deferred regulators Callers of mmc_regulator_get_supply could benefit from knowing if either of the regulators are present but not yet available. Since callers do not currently examine the return value, modify this function to return zero or -EPROBE_DEFER if either regulator get returns the same. Furthermore, since callers check vmmc/vqmmc using IS_ERR and can deal with absent regulators, switch to devm_regulator_get_optional. This has the added benefit of allowing this function to behave correctly even in the !CONFIG_REGULATOR case such that the stub can be removed. Signed-off-by: Tim Kryger Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 31 +++++++++++++++++++------------ include/linux/mmc/host.h | 8 ++------ 2 files changed, 21 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 02baa30653fa..7dc0c85fdb60 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1314,31 +1314,38 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, } EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr); +#endif /* CONFIG_REGULATOR */ + int mmc_regulator_get_supply(struct mmc_host *mmc) { struct device *dev = mmc_dev(mmc); - struct regulator *supply; int ret; - supply = devm_regulator_get(dev, "vmmc"); - mmc->supply.vmmc = supply; + mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc"); mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc"); - if (IS_ERR(supply)) - return PTR_ERR(supply); + if (IS_ERR(mmc->supply.vmmc)) { + if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "No vmmc regulator found\n"); + } else { + ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc); + if (ret > 0) + mmc->ocr_avail = ret; + else + dev_warn(dev, "Failed getting OCR mask: %d\n", ret); + } - ret = mmc_regulator_get_ocrmask(supply); - if (ret > 0) - mmc->ocr_avail = ret; - else - dev_warn(mmc_dev(mmc), "Failed getting OCR mask: %d\n", ret); + if (IS_ERR(mmc->supply.vqmmc)) { + if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "No vqmmc regulator found\n"); + } return 0; } EXPORT_SYMBOL_GPL(mmc_regulator_get_supply); -#endif /* CONFIG_REGULATOR */ - /* * Mask off any voltages we don't support and select * the lowest voltage diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 183087374215..cd595275e118 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -402,7 +402,6 @@ int mmc_regulator_get_ocrmask(struct regulator *supply); int mmc_regulator_set_ocr(struct mmc_host *mmc, struct regulator *supply, unsigned short vdd_bit); -int mmc_regulator_get_supply(struct mmc_host *mmc); #else static inline int mmc_regulator_get_ocrmask(struct regulator *supply) { @@ -415,13 +414,10 @@ static inline int mmc_regulator_set_ocr(struct mmc_host *mmc, { return 0; } - -static inline int mmc_regulator_get_supply(struct mmc_host *mmc) -{ - return 0; -} #endif +int mmc_regulator_get_supply(struct mmc_host *mmc); + int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *); static inline int mmc_card_is_removable(struct mmc_host *host) -- cgit v1.2.3 From 3f7c01ade226e7006d76e42f8c9b99ada7085312 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 9 May 2014 05:49:10 +0900 Subject: clk: samsung: add clock controller driver for s3c2410, s3c2440 and s3c2442 This driver can handle the clock controllers of the socs mentioned above, as they share a common clock tree with only small differences. The clock structure is built according to the manuals of the included SoCs and might include changes in comparison to the previous clock structure. As pll-rate-tables only the 12mhz variants are currently included. The original code was wrongly checking for 169mhz xti values [a 0 to much at the end], so the original 16mhz pll table would have never been included and its values are so obscure that I have no possibility to at least check their sane-ness. When using the formula from the manual the resulting frequency is near the table value but still slightly off. Signed-off-by: Heiko Stuebner Acked-by: Mike Turquette Signed-off-by: Kukjin Kim --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-s3c2410.c | 477 ++++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/s3c2410.h | 62 +++++ 3 files changed, 540 insertions(+) create mode 100644 drivers/clk/samsung/clk-s3c2410.c create mode 100644 include/dt-bindings/clock/s3c2410.h (limited to 'include') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 9892de4978e3..2cb62f87e068 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c new file mode 100644 index 000000000000..7b4182186a30 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2410.c @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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. + * + * Common Clock Framework support for S3C2410 and following SoCs. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +#define LOCKTIME 0x00 +#define MPLLCON 0x04 +#define UPLLCON 0x08 +#define CLKCON 0x0c +#define CLKSLOW 0x10 +#define CLKDIVN 0x14 +#define CAMDIVN 0x18 + +/* the soc types */ +enum supported_socs { + S3C2410, + S3C2440, + S3C2442, +}; + +/* list of PLLs to be registered */ +enum s3c2410_plls { + mpll, upll, +}; + +static void __iomem *reg_base; + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *s3c2410_save; + +/* + * list of controller registers to be saved and restored during a + * suspend/resume cycle. + */ +static unsigned long s3c2410_clk_regs[] __initdata = { + LOCKTIME, + MPLLCON, + UPLLCON, + CLKCON, + CLKSLOW, + CLKDIVN, + CAMDIVN, +}; + +static int s3c2410_clk_suspend(void) +{ + samsung_clk_save(reg_base, s3c2410_save, + ARRAY_SIZE(s3c2410_clk_regs)); + + return 0; +} + +static void s3c2410_clk_resume(void) +{ + samsung_clk_restore(reg_base, s3c2410_save, + ARRAY_SIZE(s3c2410_clk_regs)); +} + +static struct syscore_ops s3c2410_clk_syscore_ops = { + .suspend = s3c2410_clk_suspend, + .resume = s3c2410_clk_resume, +}; + +static void s3c2410_clk_sleep_init(void) +{ + s3c2410_save = samsung_clk_alloc_reg_dump(s3c2410_clk_regs, + ARRAY_SIZE(s3c2410_clk_regs)); + if (!s3c2410_save) { + pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", + __func__); + return; + } + + register_syscore_ops(&s3c2410_clk_syscore_ops); + return; +} +#else +static void s3c2410_clk_sleep_init(void) {} +#endif + +PNAME(fclk_p) = { "mpll", "div_slow" }; + +struct samsung_mux_clock s3c2410_common_muxes[] __initdata = { + MUX(FCLK, "fclk", fclk_p, CLKSLOW, 4, 1), +}; + +static struct clk_div_table divslow_d[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c2410_common_dividers[] __initdata = { + DIV_T(0, "div_slow", "xti", CLKSLOW, 0, 3, divslow_d), + DIV(PCLK, "pclk", "hclk", CLKDIVN, 0, 1), +}; + +struct samsung_gate_clock s3c2410_common_gates[] __initdata = { + GATE(PCLK_SPI, "spi", "pclk", CLKCON, 18, 0, 0), + GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 17, 0, 0), + GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 16, 0, 0), + GATE(PCLK_ADC, "adc", "pclk", CLKCON, 15, 0, 0), + GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 14, 0, 0), + GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 13, CLK_IGNORE_UNUSED, 0), + GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 12, 0, 0), + GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 11, 0, 0), + GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 10, 0, 0), + GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 9, 0, 0), + GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 8, 0, 0), + GATE(HCLK_USBD, "usb-device", "hclk", CLKCON, 7, 0, 0), + GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0), + GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0), + GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0), +}; + +/* should be added _after_ the soc-specific clocks are created */ +struct samsung_clock_alias s3c2410_common_aliases[] __initdata = { + ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"), + ALIAS(PCLK_ADC, NULL, "adc"), + ALIAS(PCLK_RTC, NULL, "rtc"), + ALIAS(PCLK_PWM, NULL, "timers"), + ALIAS(HCLK_LCD, NULL, "lcd"), + ALIAS(HCLK_USBD, NULL, "usb-device"), + ALIAS(HCLK_USBH, NULL, "usb-host"), + ALIAS(UCLK, NULL, "usb-bus-host"), + ALIAS(UCLK, NULL, "usb-bus-gadget"), + ALIAS(ARMCLK, NULL, "armclk"), + ALIAS(UCLK, NULL, "uclk"), + ALIAS(HCLK, NULL, "hclk"), + ALIAS(MPLL, NULL, "mpll"), + ALIAS(FCLK, NULL, "fclk"), +}; + +/* S3C2410 specific clocks */ + +static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = { + /* sorted in descending order */ + /* 2410A extras */ + PLL_35XX_RATE(270000000, 127, 1, 1), + PLL_35XX_RATE(268000000, 126, 1, 1), + PLL_35XX_RATE(266000000, 125, 1, 1), + PLL_35XX_RATE(226000000, 105, 1, 1), + PLL_35XX_RATE(210000000, 132, 2, 1), + /* 2410 common */ + PLL_35XX_RATE(203000000, 161, 3, 1), + PLL_35XX_RATE(192000000, 88, 1, 1), + PLL_35XX_RATE(186000000, 85, 1, 1), + PLL_35XX_RATE(180000000, 82, 1, 1), + PLL_35XX_RATE(170000000, 77, 1, 1), + PLL_35XX_RATE(158000000, 71, 1, 1), + PLL_35XX_RATE(152000000, 68, 1, 1), + PLL_35XX_RATE(147000000, 90, 2, 1), + PLL_35XX_RATE(135000000, 82, 2, 1), + PLL_35XX_RATE(124000000, 116, 1, 2), + PLL_35XX_RATE(118000000, 150, 2, 2), + PLL_35XX_RATE(113000000, 105, 1, 2), + PLL_35XX_RATE(101000000, 127, 2, 2), + PLL_35XX_RATE(90000000, 112, 2, 2), + PLL_35XX_RATE(85000000, 105, 2, 2), + PLL_35XX_RATE(79000000, 71, 1, 2), + PLL_35XX_RATE(68000000, 82, 2, 2), + PLL_35XX_RATE(56000000, 142, 2, 3), + PLL_35XX_RATE(48000000, 120, 2, 3), + PLL_35XX_RATE(51000000, 161, 3, 3), + PLL_35XX_RATE(45000000, 82, 1, 3), + PLL_35XX_RATE(34000000, 82, 2, 3), + { /* sentinel */ }, +}; + +static struct samsung_pll_clock s3c2410_plls[] __initdata = { + [mpll] = PLL(pll_s3c2410_mpll, MPLL, "mpll", "xti", + LOCKTIME, MPLLCON, NULL), + [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti", + LOCKTIME, UPLLCON, NULL), +}; + +struct samsung_div_clock s3c2410_dividers[] __initdata = { + DIV(HCLK, "hclk", "mpll", CLKDIVN, 1, 1), +}; + +struct samsung_fixed_factor_clock s3c2410_ffactor[] __initdata = { + /* + * armclk is directly supplied by the fclk, without + * switching possibility like on the s3c244x below. + */ + FFACTOR(ARMCLK, "armclk", "fclk", 1, 1, 0), + + /* uclk is fed from the unmodified upll */ + FFACTOR(UCLK, "uclk", "upll", 1, 1, 0), +}; + +struct samsung_clock_alias s3c2410_aliases[] __initdata = { + ALIAS(PCLK_UART0, "s3c2410-uart.0", "uart"), + ALIAS(PCLK_UART1, "s3c2410-uart.1", "uart"), + ALIAS(PCLK_UART2, "s3c2410-uart.2", "uart"), + ALIAS(PCLK_UART0, "s3c2410-uart.0", "clk_uart_baud0"), + ALIAS(PCLK_UART1, "s3c2410-uart.1", "clk_uart_baud0"), + ALIAS(PCLK_UART2, "s3c2410-uart.2", "clk_uart_baud0"), + ALIAS(UCLK, NULL, "clk_uart_baud1"), +}; + +/* S3C244x specific clocks */ + +static struct samsung_pll_rate_table pll_s3c244x_12mhz_tbl[] __initdata = { + /* sorted in descending order */ + PLL_35XX_RATE(400000000, 0x5c, 1, 1), + PLL_35XX_RATE(390000000, 0x7a, 2, 1), + PLL_35XX_RATE(380000000, 0x57, 1, 1), + PLL_35XX_RATE(370000000, 0xb1, 4, 1), + PLL_35XX_RATE(360000000, 0x70, 2, 1), + PLL_35XX_RATE(350000000, 0xa7, 4, 1), + PLL_35XX_RATE(340000000, 0x4d, 1, 1), + PLL_35XX_RATE(330000000, 0x66, 2, 1), + PLL_35XX_RATE(320000000, 0x98, 4, 1), + PLL_35XX_RATE(310000000, 0x93, 4, 1), + PLL_35XX_RATE(300000000, 0x75, 3, 1), + PLL_35XX_RATE(240000000, 0x70, 1, 2), + PLL_35XX_RATE(230000000, 0x6b, 1, 2), + PLL_35XX_RATE(220000000, 0x66, 1, 2), + PLL_35XX_RATE(210000000, 0x84, 2, 2), + PLL_35XX_RATE(200000000, 0x5c, 1, 2), + PLL_35XX_RATE(190000000, 0x57, 1, 2), + PLL_35XX_RATE(180000000, 0x70, 2, 2), + PLL_35XX_RATE(170000000, 0x4d, 1, 2), + PLL_35XX_RATE(160000000, 0x98, 4, 2), + PLL_35XX_RATE(150000000, 0x75, 3, 2), + PLL_35XX_RATE(120000000, 0x70, 1, 3), + PLL_35XX_RATE(110000000, 0x66, 1, 3), + PLL_35XX_RATE(100000000, 0x5c, 1, 3), + PLL_35XX_RATE(90000000, 0x70, 2, 3), + PLL_35XX_RATE(80000000, 0x98, 4, 3), + PLL_35XX_RATE(75000000, 0x75, 3, 3), + { /* sentinel */ }, +}; + +static struct samsung_pll_clock s3c244x_common_plls[] __initdata = { + [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", + LOCKTIME, MPLLCON, NULL), + [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti", + LOCKTIME, UPLLCON, NULL), +}; + +PNAME(hclk_p) = { "fclk", "div_hclk_2", "div_hclk_4", "div_hclk_3" }; +PNAME(armclk_p) = { "fclk", "hclk" }; + +struct samsung_mux_clock s3c244x_common_muxes[] __initdata = { + MUX(HCLK, "hclk", hclk_p, CLKDIVN, 1, 2), + MUX(ARMCLK, "armclk", armclk_p, CAMDIVN, 12, 1), +}; + +struct samsung_fixed_factor_clock s3c244x_common_ffactor[] __initdata = { + FFACTOR(0, "div_hclk_2", "fclk", 1, 2, 0), + FFACTOR(0, "ff_cam", "div_cam", 2, 1, CLK_SET_RATE_PARENT), +}; + +static struct clk_div_table div_hclk_4_d[] = { + { .val = 0, .div = 4 }, + { .val = 1, .div = 8 }, + { /* sentinel */ }, +}; + +static struct clk_div_table div_hclk_3_d[] = { + { .val = 0, .div = 3 }, + { .val = 1, .div = 6 }, + { /* sentinel */ }, +}; + +struct samsung_div_clock s3c244x_common_dividers[] __initdata = { + DIV(UCLK, "uclk", "upll", CLKDIVN, 3, 1), + DIV(0, "div_hclk", "fclk", CLKDIVN, 1, 1), + DIV_T(0, "div_hclk_4", "fclk", CAMDIVN, 9, 1, div_hclk_4_d), + DIV_T(0, "div_hclk_3", "fclk", CAMDIVN, 8, 1, div_hclk_3_d), + DIV(0, "div_cam", "upll", CAMDIVN, 0, 3), +}; + +struct samsung_gate_clock s3c244x_common_gates[] __initdata = { + GATE(HCLK_CAM, "cam", "hclk", CLKCON, 19, 0, 0), +}; + +struct samsung_clock_alias s3c244x_common_aliases[] __initdata = { + ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"), + ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"), + ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"), + ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"), + ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"), + ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"), + ALIAS(HCLK_CAM, NULL, "camif"), + ALIAS(CAMIF, NULL, "camif-upll"), +}; + +/* S3C2440 specific clocks */ + +PNAME(s3c2440_camif_p) = { "upll", "ff_cam" }; + +struct samsung_mux_clock s3c2440_muxes[] __initdata = { + MUX(CAMIF, "camif", s3c2440_camif_p, CAMDIVN, 4, 1), +}; + +struct samsung_gate_clock s3c2440_gates[] __initdata = { + GATE(PCLK_AC97, "ac97", "pclk", CLKCON, 20, 0, 0), +}; + +/* S3C2442 specific clocks */ + +struct samsung_fixed_factor_clock s3c2442_ffactor[] __initdata = { + FFACTOR(0, "upll_3", "upll", 1, 3, 0), +}; + +PNAME(s3c2442_camif_p) = { "upll", "ff_cam", "upll", "upll_3" }; + +struct samsung_mux_clock s3c2442_muxes[] __initdata = { + MUX(CAMIF, "camif", s3c2442_camif_p, CAMDIVN, 4, 2), +}; + +/* + * fixed rate clocks generated outside the soc + * Only necessary until the devicetree-move is complete + */ +#define XTI 1 +struct samsung_fixed_rate_clock s3c2410_common_frate_clks[] __initdata = { + FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0), +}; + +static void __init s3c2410_common_clk_register_fixed_ext(unsigned long xti_f) +{ + struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal"); + + s3c2410_common_frate_clks[0].fixed_rate = xti_f; + samsung_clk_register_fixed_rate(s3c2410_common_frate_clks, + ARRAY_SIZE(s3c2410_common_frate_clks)); + + samsung_clk_register_alias(&xti_alias, 1); +} + +void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f, + int current_soc, + void __iomem *base) +{ + reg_base = base; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + samsung_clk_init(np, reg_base, NR_CLKS); + + /* Register external clocks only in non-dt cases */ + if (!np) + s3c2410_common_clk_register_fixed_ext(xti_f); + + if (current_soc == 2410) { + if (_get_rate("xti") == 12 * MHZ) { + s3c2410_plls[mpll].rate_table = pll_s3c2410_12mhz_tbl; + s3c2410_plls[upll].rate_table = pll_s3c2410_12mhz_tbl; + } + + /* Register PLLs. */ + samsung_clk_register_pll(s3c2410_plls, + ARRAY_SIZE(s3c2410_plls), reg_base); + + } else { /* S3C2440, S3C2442 */ + if (_get_rate("xti") == 12 * MHZ) { + /* + * plls follow different calculation schemes, with the + * upll following the same scheme as the s3c2410 plls + */ + s3c244x_common_plls[mpll].rate_table = + pll_s3c244x_12mhz_tbl; + s3c244x_common_plls[upll].rate_table = + pll_s3c2410_12mhz_tbl; + } + + /* Register PLLs. */ + samsung_clk_register_pll(s3c244x_common_plls, + ARRAY_SIZE(s3c244x_common_plls), reg_base); + } + + /* Register common internal clocks. */ + samsung_clk_register_mux(s3c2410_common_muxes, + ARRAY_SIZE(s3c2410_common_muxes)); + samsung_clk_register_div(s3c2410_common_dividers, + ARRAY_SIZE(s3c2410_common_dividers)); + samsung_clk_register_gate(s3c2410_common_gates, + ARRAY_SIZE(s3c2410_common_gates)); + + if (current_soc == S3C2440 || current_soc == S3C2442) { + samsung_clk_register_div(s3c244x_common_dividers, + ARRAY_SIZE(s3c244x_common_dividers)); + samsung_clk_register_gate(s3c244x_common_gates, + ARRAY_SIZE(s3c244x_common_gates)); + samsung_clk_register_mux(s3c244x_common_muxes, + ARRAY_SIZE(s3c244x_common_muxes)); + samsung_clk_register_fixed_factor(s3c244x_common_ffactor, + ARRAY_SIZE(s3c244x_common_ffactor)); + } + + /* Register SoC-specific clocks. */ + switch (current_soc) { + case S3C2410: + samsung_clk_register_div(s3c2410_dividers, + ARRAY_SIZE(s3c2410_dividers)); + samsung_clk_register_fixed_factor(s3c2410_ffactor, + ARRAY_SIZE(s3c2410_ffactor)); + samsung_clk_register_alias(s3c2410_aliases, + ARRAY_SIZE(s3c2410_common_aliases)); + break; + case S3C2440: + samsung_clk_register_mux(s3c2440_muxes, + ARRAY_SIZE(s3c2440_muxes)); + samsung_clk_register_gate(s3c2440_gates, + ARRAY_SIZE(s3c2440_gates)); + break; + case S3C2442: + samsung_clk_register_mux(s3c2442_muxes, + ARRAY_SIZE(s3c2442_muxes)); + samsung_clk_register_fixed_factor(s3c2442_ffactor, + ARRAY_SIZE(s3c2442_ffactor)); + break; + } + + /* + * Register common aliases at the end, as some of the aliased clocks + * are SoC specific. + */ + samsung_clk_register_alias(s3c2410_common_aliases, + ARRAY_SIZE(s3c2410_common_aliases)); + + if (current_soc == S3C2440 || current_soc == S3C2442) { + samsung_clk_register_alias(s3c244x_common_aliases, + ARRAY_SIZE(s3c244x_common_aliases)); + } + + s3c2410_clk_sleep_init(); +} + +static void __init s3c2410_clk_init(struct device_node *np) +{ + s3c2410_common_clk_init(np, 0, S3C2410, 0); +} +CLK_OF_DECLARE(s3c2410_clk, "samsung,s3c2410-clock", s3c2410_clk_init); + +static void __init s3c2440_clk_init(struct device_node *np) +{ + s3c2410_common_clk_init(np, 0, S3C2440, 0); +} +CLK_OF_DECLARE(s3c2440_clk, "samsung,s3c2440-clock", s3c2440_clk_init); + +static void __init s3c2442_clk_init(struct device_node *np) +{ + s3c2410_common_clk_init(np, 0, S3C2442, 0); +} +CLK_OF_DECLARE(s3c2442_clk, "samsung,s3c2442-clock", s3c2442_clk_init); diff --git a/include/dt-bindings/clock/s3c2410.h b/include/dt-bindings/clock/s3c2410.h new file mode 100644 index 000000000000..352a7673fc69 --- /dev/null +++ b/include/dt-bindings/clock/s3c2410.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Heiko Stuebner + * + * 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. + * + * Device Tree binding constants clock controllers of Samsung S3C2410 and later. + */ + +#ifndef _DT_BINDINGS_CLOCK_SAMSUNG_S3C2410_CLOCK_H +#define _DT_BINDINGS_CLOCK_SAMSUNG_S3C2410_CLOCK_H + +/* + * Let each exported clock get a unique index, which is used on DT-enabled + * platforms to lookup the clock from a clock specifier. These indices are + * therefore considered an ABI and so must not be changed. This implies + * that new clocks should be added either in free spaces between clock groups + * or at the end. + */ + +/* Core clocks. */ + +/* id 1 is reserved */ +#define MPLL 2 +#define UPLL 3 +#define FCLK 4 +#define HCLK 5 +#define PCLK 6 +#define UCLK 7 +#define ARMCLK 8 + +/* pclk-gates */ +#define PCLK_UART0 16 +#define PCLK_UART1 17 +#define PCLK_UART2 18 +#define PCLK_I2C 19 +#define PCLK_SDI 20 +#define PCLK_SPI 21 +#define PCLK_ADC 22 +#define PCLK_AC97 23 +#define PCLK_I2S 24 +#define PCLK_PWM 25 +#define PCLK_RTC 26 +#define PCLK_GPIO 27 + + +/* hclk-gates */ +#define HCLK_LCD 32 +#define HCLK_USBH 33 +#define HCLK_USBD 34 +#define HCLK_NAND 35 +#define HCLK_CAM 36 + + +#define CAMIF 40 + + +/* Total number of clocks. */ +#define NR_CLKS (CAMIF + 1) + +#endif /* _DT_BINDINGS_CLOCK_SAMSUNG_S3C2443_CLOCK_H */ -- cgit v1.2.3 From 725b418b43d2ddcb94b413cd25c74c1175d1c5f0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Apr 2014 15:11:41 +0200 Subject: clk: Fixup spacing in comments - Remove spaces in front of TABs, - Correct indentation for some CLK_* flag descriptions. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mike Turquette --- include/linux/clk-provider.h | 88 ++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 59e2eb58f555..397f98505bd4 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -40,14 +40,14 @@ struct dentry; * through the clk_* api. * * @prepare: Prepare the clock for enabling. This must not return until - * the clock is fully prepared, and it's safe to call clk_enable. - * This callback is intended to allow clock implementations to - * do any initialisation that may sleep. Called with - * prepare_lock held. + * the clock is fully prepared, and it's safe to call clk_enable. + * This callback is intended to allow clock implementations to + * do any initialisation that may sleep. Called with + * prepare_lock held. * * @unprepare: Release the clock from its prepared state. This will typically - * undo any work done in the @prepare callback. Called with - * prepare_lock held. + * undo any work done in the @prepare callback. Called with + * prepare_lock held. * * @is_prepared: Queries the hardware to determine if the clock is prepared. * This function is allowed to sleep. Optional, if this op is not @@ -58,16 +58,16 @@ struct dentry; * Called with prepare mutex held. This function may sleep. * * @enable: Enable the clock atomically. This must not return until the - * clock is generating a valid clock signal, usable by consumer - * devices. Called with enable_lock held. This function must not - * sleep. + * clock is generating a valid clock signal, usable by consumer + * devices. Called with enable_lock held. This function must not + * sleep. * * @disable: Disable the clock atomically. Called with enable_lock held. - * This function must not sleep. + * This function must not sleep. * * @is_enabled: Queries the hardware to determine if the clock is enabled. - * This function must not sleep. Optional, if this op is not - * set then the enable count will be used. + * This function must not sleep. Optional, if this op is not + * set then the enable count will be used. * * @disable_unused: Disable the clock atomically. Only called from * clk_disable_unused for gate clocks with special needs. @@ -75,34 +75,34 @@ struct dentry; * sleep. * * @recalc_rate Recalculate the rate of this clock, by querying hardware. The - * parent rate is an input parameter. It is up to the caller to - * ensure that the prepare_mutex is held across this call. - * Returns the calculated rate. Optional, but recommended - if - * this op is not set then clock rate will be initialized to 0. + * parent rate is an input parameter. It is up to the caller to + * ensure that the prepare_mutex is held across this call. + * Returns the calculated rate. Optional, but recommended - if + * this op is not set then clock rate will be initialized to 0. * * @round_rate: Given a target rate as input, returns the closest rate actually - * supported by the clock. + * supported by the clock. * * @determine_rate: Given a target rate as input, returns the closest rate * actually supported by the clock, and optionally the parent clock * that should be used to provide the clock rate. * * @get_parent: Queries the hardware to determine the parent of a clock. The - * return value is a u8 which specifies the index corresponding to - * the parent clock. This index can be applied to either the - * .parent_names or .parents arrays. In short, this function - * translates the parent value read from hardware into an array - * index. Currently only called when the clock is initialized by - * __clk_init. This callback is mandatory for clocks with - * multiple parents. It is optional (and unnecessary) for clocks - * with 0 or 1 parents. + * return value is a u8 which specifies the index corresponding to + * the parent clock. This index can be applied to either the + * .parent_names or .parents arrays. In short, this function + * translates the parent value read from hardware into an array + * index. Currently only called when the clock is initialized by + * __clk_init. This callback is mandatory for clocks with + * multiple parents. It is optional (and unnecessary) for clocks + * with 0 or 1 parents. * * @set_parent: Change the input source of this clock; for clocks with multiple - * possible parents specify a new parent by passing in the index - * as a u8 corresponding to the parent in either the .parent_names - * or .parents arrays. This function in affect translates an - * array index into the value programmed into the hardware. - * Returns 0 on success, -EERROR otherwise. + * possible parents specify a new parent by passing in the index + * as a u8 corresponding to the parent in either the .parent_names + * or .parents arrays. This function in affect translates an + * array index into the value programmed into the hardware. + * Returns 0 on success, -EERROR otherwise. * * @set_rate: Change the rate of this clock. The requested rate is specified * by the second argument, which should typically be the return @@ -254,12 +254,12 @@ void of_fixed_clk_setup(struct device_node *np); * * Flags: * CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to - * enable the clock. Setting this flag does the opposite: setting the bit - * disable the clock and clearing it enables the clock + * enable the clock. Setting this flag does the opposite: setting the bit + * disable the clock and clearing it enables the clock * CLK_GATE_HIWORD_MASK - The gate settings are only in lower 16-bit - * of this register, and mask of gate bits are in higher 16-bit of this - * register. While setting the gate bits, higher 16-bit should also be - * updated to indicate changing gate bits. + * of this register, and mask of gate bits are in higher 16-bit of this + * register. While setting the gate bits, higher 16-bit should also be + * updated to indicate changing gate bits. */ struct clk_gate { struct clk_hw hw; @@ -298,20 +298,20 @@ struct clk_div_table { * * Flags: * CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the - * register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is - * the raw value read from the register, with the value of zero considered + * register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is + * the raw value read from the register, with the value of zero considered * invalid, unless CLK_DIVIDER_ALLOW_ZERO is set. * CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from - * the hardware register + * the hardware register * CLK_DIVIDER_ALLOW_ZERO - Allow zero divisors. For dividers which have * CLK_DIVIDER_ONE_BASED set, it is possible to end up with a zero divisor. * Some hardware implementations gracefully handle this case and allow a * zero divisor by not modifying their input clock * (divide by one / bypass). * CLK_DIVIDER_HIWORD_MASK - The divider settings are only in lower 16-bit - * of this register, and mask of divider bits are in higher 16-bit of this - * register. While setting the divider bits, higher 16-bit should also be - * updated to indicate changing divider bits. + * of this register, and mask of divider bits are in higher 16-bit of this + * register. While setting the divider bits, higher 16-bit should also be + * updated to indicate changing divider bits. * CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded * to the closest integer instead of the up one. */ @@ -359,9 +359,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, * CLK_MUX_INDEX_ONE - register index starts at 1, not 0 * CLK_MUX_INDEX_BIT - register index is a single bit (power of two) * CLK_MUX_HIWORD_MASK - The mux settings are only in lower 16-bit of this - * register, and mask of mux bits are in higher 16-bit of this register. - * While setting the mux bits, higher 16-bit should also be updated to - * indicate changing mux bits. + * register, and mask of mux bits are in higher 16-bit of this register. + * While setting the mux bits, higher 16-bit should also be updated to + * indicate changing mux bits. */ struct clk_mux { struct clk_hw hw; -- cgit v1.2.3 From 54e73016dd217be915ed83353d296f2a133d1ad5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Apr 2014 15:11:42 +0200 Subject: clk: Improve clk_ops documentation General: - Add parameter names to .round_rate() and .set_rate(). Documentation/clk.txt: - Add missing parameter for .set_rate(), - Add missing .debug_init(). include/linux/clk-provider.h: - Add parent rate documentation for .round_rate(), - Reorder documentation to match implementation order, - Add missing documentation for .init(). Signed-off-by: Geert Uytterhoeven Signed-off-by: Mike Turquette --- Documentation/clk.txt | 16 +++++++++++----- include/linux/clk-provider.h | 44 +++++++++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/Documentation/clk.txt b/Documentation/clk.txt index c9c399af7c08..1fee72f4d331 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -68,21 +68,27 @@ the operations defined in clk.h: int (*is_enabled)(struct clk_hw *hw); unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate); - long (*round_rate)(struct clk_hw *hw, unsigned long, - unsigned long *); + long (*round_rate)(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate); long (*determine_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate, struct clk **best_parent_clk); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, unsigned long); + int (*set_rate)(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate); int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate, u8 index); + unsigned long parent_rate, + u8 index); unsigned long (*recalc_accuracy)(struct clk_hw *hw, - unsigned long parent_accuracy); + unsigned long parent_accuracy); void (*init)(struct clk_hw *hw); + int (*debug_init)(struct clk_hw *hw, + struct dentry *dentry); }; Part 3 - hardware clk implementations diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 397f98505bd4..40809431641e 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -81,12 +81,20 @@ struct dentry; * this op is not set then clock rate will be initialized to 0. * * @round_rate: Given a target rate as input, returns the closest rate actually - * supported by the clock. + * supported by the clock. The parent rate is an input/output + * parameter. * * @determine_rate: Given a target rate as input, returns the closest rate * actually supported by the clock, and optionally the parent clock * that should be used to provide the clock rate. * + * @set_parent: Change the input source of this clock; for clocks with multiple + * possible parents specify a new parent by passing in the index + * as a u8 corresponding to the parent in either the .parent_names + * or .parents arrays. This function in affect translates an + * array index into the value programmed into the hardware. + * Returns 0 on success, -EERROR otherwise. + * * @get_parent: Queries the hardware to determine the parent of a clock. The * return value is a u8 which specifies the index corresponding to * the parent clock. This index can be applied to either the @@ -97,26 +105,12 @@ struct dentry; * multiple parents. It is optional (and unnecessary) for clocks * with 0 or 1 parents. * - * @set_parent: Change the input source of this clock; for clocks with multiple - * possible parents specify a new parent by passing in the index - * as a u8 corresponding to the parent in either the .parent_names - * or .parents arrays. This function in affect translates an - * array index into the value programmed into the hardware. - * Returns 0 on success, -EERROR otherwise. - * * @set_rate: Change the rate of this clock. The requested rate is specified * by the second argument, which should typically be the return * of .round_rate call. The third argument gives the parent rate * which is likely helpful for most .set_rate implementation. * Returns 0 on success, -EERROR otherwise. * - * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy - * is expressed in ppb (parts per billion). The parent accuracy is - * an input parameter. - * Returns the calculated accuracy. Optional - if this op is not - * set then clock accuracy will be initialized to parent accuracy - * or 0 (perfect clock) if clock has no parent. - * * @set_rate_and_parent: Change the rate and the parent of this clock. The * requested rate is specified by the second argument, which * should typically be the return of .round_rate call. The @@ -128,6 +122,18 @@ struct dentry; * separately via calls to .set_parent and .set_rate. * Returns 0 on success, -EERROR otherwise. * + * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy + * is expressed in ppb (parts per billion). The parent accuracy is + * an input parameter. + * Returns the calculated accuracy. Optional - if this op is not + * set then clock accuracy will be initialized to parent accuracy + * or 0 (perfect clock) if clock has no parent. + * + * @init: Perform platform-specific initialization magic. + * This is not not used by any of the basic clock types. + * Please consider other ways of solving initialization problems + * before using this callback, as its use is discouraged. + * * @debug_init: Set up type-specific debugfs entries for this clock. This * is called once, after the debugfs directory entry for this * clock has been created. The dentry pointer representing that @@ -157,15 +163,15 @@ struct clk_ops { void (*disable_unused)(struct clk_hw *hw); unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate); - long (*round_rate)(struct clk_hw *hw, unsigned long, - unsigned long *); + long (*round_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate); long (*determine_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate, struct clk **best_parent_clk); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, unsigned long, - unsigned long); + int (*set_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); -- cgit v1.2.3 From a505daa501dc3f02b08d0b48b049bce719299b74 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 12 May 2014 20:49:33 +0200 Subject: ARM: shmobile: r8a7791: Correct SYS-DMAC clock defines R-Car M2 has two MSTP bits for SYS-DMAC, not one. Also bring the naming in sync with the documentation. This issue was introduced in v3.14, in commit 4d8864c9e94ec727f1c675b9f6921525c360334b ("ARM: shmobile: r8a7791: Add clock index macros for DT sources"). Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Simon Horman --- include/dt-bindings/clock/r8a7791-clock.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/dt-bindings/clock/r8a7791-clock.h b/include/dt-bindings/clock/r8a7791-clock.h index f069bc6627cb..c3b0130f56c8 100644 --- a/include/dt-bindings/clock/r8a7791-clock.h +++ b/include/dt-bindings/clock/r8a7791-clock.h @@ -43,7 +43,8 @@ #define R8A7791_CLK_SCIFB1 7 #define R8A7791_CLK_MSIOF1 8 #define R8A7791_CLK_SCIFB2 16 -#define R8A7791_CLK_DMAC 18 +#define R8A7791_CLK_SYS_DMAC1 18 +#define R8A7791_CLK_SYS_DMAC0 19 /* MSTP3 */ #define R8A7791_CLK_TPU0 4 -- cgit v1.2.3 From 7b42a997bfb93c6ae0709f34ec8e2860757804b5 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 18 Apr 2014 08:05:50 +0900 Subject: clk: shmobile: r8a7779: Add clocks support The R8A7779 SoC has several clocks that are too custom to be supported in a generic driver. Those clocks are all fixed rate clocks with multiplier and divisor set according to boot mode configuration. Based on work for R-Car Gen2 SoCs by Laurent Pinchart. Cc: devicetree@vger.kernel.org Acked-by: Laurent Pinchart Signed-off-by: Simon Horman Signed-off-by: Mike Turquette --- .../bindings/clock/renesas,r8a7779-cpg-clocks.txt | 27 ++++ drivers/clk/shmobile/Makefile | 1 + drivers/clk/shmobile/clk-r8a7779.c | 180 +++++++++++++++++++++ include/linux/clk/shmobile.h | 3 + 4 files changed, 211 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt create mode 100644 drivers/clk/shmobile/clk-r8a7779.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt new file mode 100644 index 000000000000..ed3c8cb12f4e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt @@ -0,0 +1,27 @@ +* Renesas R8A7779 Clock Pulse Generator (CPG) + +The CPG generates core clocks for the R8A7779. It includes one PLL and +several fixed ratio dividers + +Required Properties: + + - compatible: Must be "renesas,r8a7779-cpg-clocks" + - reg: Base address and length of the memory resource used by the CPG + + - clocks: Reference to the parent clock + - #clock-cells: Must be 1 + - clock-output-names: The names of the clocks. Supported clocks are "plla", + "z", "zs", "s", "s1", "p", "b", "out". + + +Example +------- + + cpg_clocks: cpg_clocks@ffc80000 { + compatible = "renesas,r8a7779-cpg-clocks"; + reg = <0 0xffc80000 0 0x30>; + clocks = <&extal_clk>; + #clock-cells = <1>; + clock-output-names = "plla", "z", "zs", "s", "s1", "p", + "b", "out"; + }; diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile index 5404cb931ebf..bdf342daefa5 100644 --- a/drivers/clk/shmobile/Makefile +++ b/drivers/clk/shmobile/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o +obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o diff --git a/drivers/clk/shmobile/clk-r8a7779.c b/drivers/clk/shmobile/clk-r8a7779.c new file mode 100644 index 000000000000..652ecacb6daf --- /dev/null +++ b/drivers/clk/shmobile/clk-r8a7779.c @@ -0,0 +1,180 @@ +/* + * r8a7779 Core CPG Clocks + * + * Copyright (C) 2013, 2014 Horms Solutions Ltd. + * + * Contact: Simon Horman + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CPG_NUM_CLOCKS (R8A7779_CLK_OUT + 1) + +struct r8a7779_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +/* ----------------------------------------------------------------------------- + * CPG Clock Data + */ + +/* + * MD1 = 1 MD1 = 0 + * (PLLA = 1500) (PLLA = 1600) + * (MHz) (MHz) + *------------------------------------------------+-------------------- + * clkz 1000 (2/3) 800 (1/2) + * clkzs 250 (1/6) 200 (1/8) + * clki 750 (1/2) 800 (1/2) + * clks 250 (1/6) 200 (1/8) + * clks1 125 (1/12) 100 (1/16) + * clks3 187.5 (1/8) 200 (1/8) + * clks4 93.7 (1/16) 100 (1/16) + * clkp 62.5 (1/24) 50 (1/32) + * clkg 62.5 (1/24) 66.6 (1/24) + * clkb, CLKOUT + * (MD2 = 0) 62.5 (1/24) 66.6 (1/24) + * (MD2 = 1) 41.6 (1/36) 50 (1/32) + */ + +#define CPG_CLK_CONFIG_INDEX(md) (((md) & (BIT(2)|BIT(1))) >> 1) + +struct cpg_clk_config { + unsigned int z_mult; + unsigned int z_div; + unsigned int zs_and_s_div; + unsigned int s1_div; + unsigned int p_div; + unsigned int b_and_out_div; +}; + +static const struct cpg_clk_config cpg_clk_configs[4] __initconst = { + { 1, 2, 8, 16, 32, 24 }, + { 2, 3, 6, 12, 24, 24 }, + { 1, 2, 8, 16, 32, 32 }, + { 2, 3, 6, 12, 24, 36 }, +}; + +/* + * MD PLLA Ratio + * 12 11 + *------------------------ + * 0 0 x42 + * 0 1 x48 + * 1 0 x56 + * 1 1 x64 + */ + +#define CPG_PLLA_MULT_INDEX(md) (((md) & (BIT(12)|BIT(11))) >> 11) + +static const unsigned int cpg_plla_mult[4] __initconst = { 42, 48, 56, 64 }; + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +static u32 cpg_mode __initdata; + +static struct clk * __init +r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg, + const struct cpg_clk_config *config, + unsigned int plla_mult, const char *name) +{ + const char *parent_name = "plla"; + unsigned int mult = 1; + unsigned int div = 1; + + if (!strcmp(name, "plla")) { + parent_name = of_clk_get_parent_name(np, 0); + mult = plla_mult; + } else if (!strcmp(name, "z")) { + div = config->z_div; + mult = config->z_mult; + } else if (!strcmp(name, "zs") || !strcmp(name, "s")) { + div = config->zs_and_s_div; + } else if (!strcmp(name, "s1")) { + div = config->s1_div; + } else if (!strcmp(name, "p")) { + div = config->p_div; + } else if (!strcmp(name, "b") || !strcmp(name, "out")) { + div = config->b_and_out_div; + } else { + return ERR_PTR(-EINVAL); + } + + return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, div); +} + +static void __init r8a7779_cpg_clocks_init(struct device_node *np) +{ + const struct cpg_clk_config *config; + struct r8a7779_cpg *cpg; + struct clk **clks; + unsigned int i, plla_mult; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kzalloc(CPG_NUM_CLOCKS * sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(cpg_mode)]; + plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(cpg_mode)]; + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = r8a7779_cpg_register_clock(np, cpg, config, + plla_mult, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); +} +CLK_OF_DECLARE(r8a7779_cpg_clks, "renesas,r8a7779-cpg-clocks", + r8a7779_cpg_clocks_init); + +void __init r8a7779_clocks_init(u32 mode) +{ + cpg_mode = mode; + + of_clk_init(NULL); +} diff --git a/include/linux/clk/shmobile.h b/include/linux/clk/shmobile.h index f9bf080a1123..9f8a14041dd5 100644 --- a/include/linux/clk/shmobile.h +++ b/include/linux/clk/shmobile.h @@ -1,7 +1,9 @@ /* * Copyright 2013 Ideas On Board SPRL + * Copyright 2013, 2014 Horms Solutions Ltd. * * Contact: Laurent Pinchart + * Contact: Simon Horman * * 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 @@ -14,6 +16,7 @@ #include +void r8a7779_clocks_init(u32 mode); void rcar_gen2_clocks_init(u32 mode); #endif -- cgit v1.2.3 From 59025887fb08a8b913605fb20f8a62eb0bb69b36 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 13 May 2014 15:30:16 +0530 Subject: phy: Add new Exynos5 USB 3.0 PHY driver Add a new driver for the USB 3.0 PHY on Exynos5 series of SoCs. The new driver uses the generic PHY framework and will interact with DWC3 controller present on Exynos5 series of SoCs. Also, created a new header file in linux/mfd/syscon/ for Exynos5 SoCs and put the required PMU offset definitions for the basic available PHYs. Signed-off-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 11 + drivers/phy/Makefile | 1 + drivers/phy/phy-exynos5-usbdrd.c | 644 +++++++++++++++++++++++++++++++++ include/linux/mfd/syscon/exynos5-pmu.h | 44 +++ 4 files changed, 700 insertions(+) create mode 100644 drivers/phy/phy-exynos5-usbdrd.c create mode 100644 include/linux/mfd/syscon/exynos5-pmu.h (limited to 'include') diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 071b7633bf03..16a2f067c242 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -160,6 +160,17 @@ config PHY_EXYNOS5250_USB2 particular SoC is compiled in the driver. In case of Exynos 5250 four phys are available - device, host, HSIC0 and HSIC. +config PHY_EXYNOS5_USBDRD + tristate "Exynos5 SoC series USB DRD PHY driver" + depends on ARCH_EXYNOS5 && OF + depends on HAS_IOMEM + select GENERIC_PHY + select MFD_SYSCON + help + Enable USB DRD PHY support for Exynos 5 SoC series. + This driver provides PHY interface for USB 3.0 DRD controller + present on Exynos5 SoC series. + config PHY_XGENE tristate "APM X-Gene 15Gbps PHY support" depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST) diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 7728518572a4..b4f1d5770601 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -18,4 +18,5 @@ phy-exynos-usb2-y += phy-samsung-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o +obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c new file mode 100644 index 000000000000..8fcdd9434346 --- /dev/null +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -0,0 +1,644 @@ +/* + * Samsung EXYNOS5 SoC series USB DRD PHY driver + * + * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series + * + * Copyright (C) 2014 Samsung Electronics Co., Ltd. + * Author: Vivek Gautam + * + * 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 +#include +#include + +/* Exynos USB PHY registers */ +#define EXYNOS5_FSEL_9MHZ6 0x0 +#define EXYNOS5_FSEL_10MHZ 0x1 +#define EXYNOS5_FSEL_12MHZ 0x2 +#define EXYNOS5_FSEL_19MHZ2 0x3 +#define EXYNOS5_FSEL_20MHZ 0x4 +#define EXYNOS5_FSEL_24MHZ 0x5 +#define EXYNOS5_FSEL_50MHZ 0x7 + +/* EXYNOS5: USB 3.0 DRD PHY registers */ +#define EXYNOS5_DRD_LINKSYSTEM 0x04 + +#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) +#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) +#define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) + +#define EXYNOS5_DRD_PHYUTMI 0x08 + +#define PHYUTMI_OTGDISABLE BIT(6) +#define PHYUTMI_FORCESUSPEND BIT(1) +#define PHYUTMI_FORCESLEEP BIT(0) + +#define EXYNOS5_DRD_PHYPIPE 0x0c + +#define EXYNOS5_DRD_PHYCLKRST 0x10 + +#define PHYCLKRST_EN_UTMISUSPEND BIT(31) + +#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) +#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) + +#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) +#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) + +#define PHYCLKRST_SSC_EN BIT(20) +#define PHYCLKRST_REF_SSP_EN BIT(19) +#define PHYCLKRST_REF_CLKDIV2 BIT(18) + +#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x32 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) + +#define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) +#define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) +#define PHYCLKRST_FSEL(_x) ((_x) << 5) +#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) +#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) +#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) +#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) + +#define PHYCLKRST_RETENABLEN BIT(4) + +#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) + +#define PHYCLKRST_PORTRESET BIT(1) +#define PHYCLKRST_COMMONONN BIT(0) + +#define EXYNOS5_DRD_PHYREG0 0x14 +#define EXYNOS5_DRD_PHYREG1 0x18 + +#define EXYNOS5_DRD_PHYPARAM0 0x1c + +#define PHYPARAM0_REF_USE_PAD BIT(31) +#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) +#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) + +#define EXYNOS5_DRD_PHYPARAM1 0x20 + +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH (0x1c) + +#define EXYNOS5_DRD_PHYTERM 0x24 + +#define EXYNOS5_DRD_PHYTEST 0x28 + +#define PHYTEST_POWERDOWN_SSP BIT(3) +#define PHYTEST_POWERDOWN_HSP BIT(2) + +#define EXYNOS5_DRD_PHYADP 0x2c + +#define EXYNOS5_DRD_PHYUTMICLKSEL 0x30 + +#define PHYUTMICLKSEL_UTMI_CLKSEL BIT(2) + +#define EXYNOS5_DRD_PHYRESUME 0x34 +#define EXYNOS5_DRD_LINKPORT 0x44 + +#define KHZ 1000 +#define MHZ (KHZ * KHZ) + +enum exynos5_usbdrd_phy_id { + EXYNOS5_DRDPHY_UTMI, + EXYNOS5_DRDPHY_PIPE3, + EXYNOS5_DRDPHYS_NUM, +}; + +struct phy_usb_instance; +struct exynos5_usbdrd_phy; + +struct exynos5_usbdrd_phy_config { + u32 id; + void (*phy_isol)(struct phy_usb_instance *inst, u32 on); + void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd); + unsigned int (*set_refclk)(struct phy_usb_instance *inst); +}; + +struct exynos5_usbdrd_phy_drvdata { + const struct exynos5_usbdrd_phy_config *phy_cfg; + u32 pmu_offset_usbdrd0_phy; + u32 pmu_offset_usbdrd1_phy; +}; + +/** + * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY + * @dev: pointer to device instance of this platform device + * @reg_phy: usb phy controller register memory base + * @clk: phy clock for register access + * @drv_data: pointer to SoC level driver data structure + * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY + * instances each with its 'phy' and 'phy_cfg'. + * @extrefclk: frequency select settings when using 'separate + * reference clocks' for SS and HS operations + * @ref_clk: reference clock to PHY block from which PHY's + * operational clocks are derived + * @ref_rate: rate of above reference clock + */ +struct exynos5_usbdrd_phy { + struct device *dev; + void __iomem *reg_phy; + struct clk *clk; + const struct exynos5_usbdrd_phy_drvdata *drv_data; + struct phy_usb_instance { + struct phy *phy; + u32 index; + struct regmap *reg_pmu; + u32 pmu_offset; + const struct exynos5_usbdrd_phy_config *phy_cfg; + } phys[EXYNOS5_DRDPHYS_NUM]; + u32 extrefclk; + struct clk *ref_clk; +}; + +static inline +struct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst) +{ + return container_of((inst), struct exynos5_usbdrd_phy, + phys[(inst)->index]); +} + +/* + * exynos5_rate_to_clk() converts the supplied clock rate to the value that + * can be written to the phy register. + */ +static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg) +{ + /* EXYNOS5_FSEL_MASK */ + + switch (rate) { + case 9600 * KHZ: + *reg = EXYNOS5_FSEL_9MHZ6; + break; + case 10 * MHZ: + *reg = EXYNOS5_FSEL_10MHZ; + break; + case 12 * MHZ: + *reg = EXYNOS5_FSEL_12MHZ; + break; + case 19200 * KHZ: + *reg = EXYNOS5_FSEL_19MHZ2; + break; + case 20 * MHZ: + *reg = EXYNOS5_FSEL_20MHZ; + break; + case 24 * MHZ: + *reg = EXYNOS5_FSEL_24MHZ; + break; + case 50 * MHZ: + *reg = EXYNOS5_FSEL_50MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst, + unsigned int on) +{ + unsigned int val; + + if (!inst->reg_pmu) + return; + + val = on ? 0 : EXYNOS5_PHY_ENABLE; + + regmap_update_bits(inst->reg_pmu, inst->pmu_offset, + EXYNOS5_PHY_ENABLE, val); +} + +/* + * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock + * from clock core. Further sets multiplier values and spread spectrum + * clock settings for SuperSpeed operations. + */ +static unsigned int +exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst) +{ + static u32 reg; + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + /* restore any previous reference clock settings */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + /* Use EXTREFCLK as ref clock */ + reg &= ~PHYCLKRST_REFCLKSEL_MASK; + reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; + + /* FSEL settings corresponding to reference clock */ + reg &= ~PHYCLKRST_FSEL_PIPE_MASK | + PHYCLKRST_MPLL_MULTIPLIER_MASK | + PHYCLKRST_SSC_REFCLKSEL_MASK; + switch (phy_drd->extrefclk) { + case EXYNOS5_FSEL_50MHZ: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case EXYNOS5_FSEL_24MHZ: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + case EXYNOS5_FSEL_20MHZ: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case EXYNOS5_FSEL_19MHZ2: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + default: + dev_dbg(phy_drd->dev, "unsupported ref clk\n"); + break; + } + + return reg; +} + +/* + * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock + * from clock core. Further sets the FSEL values for HighSpeed operations. + */ +static unsigned int +exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst) +{ + static u32 reg; + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + /* restore any previous reference clock settings */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + reg &= ~PHYCLKRST_REFCLKSEL_MASK; + reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; + + reg &= ~PHYCLKRST_FSEL_UTMI_MASK | + PHYCLKRST_MPLL_MULTIPLIER_MASK | + PHYCLKRST_SSC_REFCLKSEL_MASK; + reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); + + return reg; +} + +static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd) +{ + u32 reg; + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); + /* Set Tx De-Emphasis level */ + reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; + reg |= PHYPARAM1_PCS_TXDEEMPH; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); + reg &= ~PHYTEST_POWERDOWN_SSP; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +} + +static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) +{ + u32 reg; + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); + /* Set Loss-of-Signal Detector sensitivity */ + reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK; + reg |= PHYPARAM0_REF_LOSLEVEL; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); + /* Set Tx De-Emphasis level */ + reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; + reg |= PHYPARAM1_PCS_TXDEEMPH; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); + + /* UTMI Power Control */ + writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); + reg &= ~PHYTEST_POWERDOWN_HSP; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +} + +static int exynos5_usbdrd_phy_init(struct phy *phy) +{ + int ret; + u32 reg; + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + /* Reset USB 3.0 PHY */ + writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); + writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME); + + /* + * Setting the Frame length Adj value[6:1] to default 0x20 + * See xHCI 1.0 spec, 5.2.4 + */ + reg = LINKSYSTEM_XHCI_VERSION_CONTROL | + LINKSYSTEM_FLADJ(0x20); + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); + /* Select PHY CLK source */ + reg &= ~PHYPARAM0_REF_USE_PAD; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); + + /* This bit must be set for both HS and SS operations */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); + reg |= PHYUTMICLKSEL_UTMI_CLKSEL; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); + + /* UTMI or PIPE3 specific init */ + inst->phy_cfg->phy_init(phy_drd); + + /* reference clock settings */ + reg = inst->phy_cfg->set_refclk(inst); + + /* Digital power supply in normal operating mode */ + reg |= PHYCLKRST_RETENABLEN | + /* Enable ref clock for SS function */ + PHYCLKRST_REF_SSP_EN | + /* Enable spread spectrum */ + PHYCLKRST_SSC_EN | + /* Power down HS Bias and PLL blocks in suspend mode */ + PHYCLKRST_COMMONONN | + /* Reset the port */ + PHYCLKRST_PORTRESET; + + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + udelay(10); + + reg &= ~PHYCLKRST_PORTRESET; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos5_usbdrd_phy_exit(struct phy *phy) +{ + int ret; + u32 reg; + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + reg = PHYUTMI_OTGDISABLE | + PHYUTMI_FORCESUSPEND | + PHYUTMI_FORCESLEEP; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); + + /* Resetting the PHYCLKRST enable bits to reduce leakage current */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + reg &= ~(PHYCLKRST_REF_SSP_EN | + PHYCLKRST_SSC_EN | + PHYCLKRST_COMMONONN); + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + /* Control PHYTEST to remove leakage current */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); + reg |= PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos5_usbdrd_phy_power_on(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); + + clk_prepare_enable(phy_drd->ref_clk); + + /* Power-on PHY*/ + inst->phy_cfg->phy_isol(inst, 0); + + return 0; +} + +static int exynos5_usbdrd_phy_power_off(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); + + /* Power-off the PHY */ + inst->phy_cfg->phy_isol(inst, 1); + + clk_disable_unprepare(phy_drd->ref_clk); + + return 0; +} + +static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev); + + if (WARN_ON(args->args[0] > EXYNOS5_DRDPHYS_NUM)) + return ERR_PTR(-ENODEV); + + return phy_drd->phys[args->args[0]].phy; +} + +static struct phy_ops exynos5_usbdrd_phy_ops = { + .init = exynos5_usbdrd_phy_init, + .exit = exynos5_usbdrd_phy_exit, + .power_on = exynos5_usbdrd_phy_power_on, + .power_off = exynos5_usbdrd_phy_power_off, + .owner = THIS_MODULE, +}; + +const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { + { + .id = EXYNOS5_DRDPHY_UTMI, + .phy_isol = exynos5_usbdrd_phy_isol, + .phy_init = exynos5_usbdrd_utmi_init, + .set_refclk = exynos5_usbdrd_utmi_set_refclk, + }, + { + .id = EXYNOS5_DRDPHY_PIPE3, + .phy_isol = exynos5_usbdrd_phy_isol, + .phy_init = exynos5_usbdrd_pipe3_init, + .set_refclk = exynos5_usbdrd_pipe3_set_refclk, + }, +}; + +const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos5, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, +}; + +const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos5, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, +}; + +static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { + { + .compatible = "samsung,exynos5250-usbdrd-phy", + .data = &exynos5250_usbdrd_phy + }, { + .compatible = "samsung,exynos5420-usbdrd-phy", + .data = &exynos5420_usbdrd_phy + }, + { }, +}; + +static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct exynos5_usbdrd_phy *phy_drd; + struct phy_provider *phy_provider; + struct resource *res; + const struct of_device_id *match; + const struct exynos5_usbdrd_phy_drvdata *drv_data; + struct regmap *reg_pmu; + u32 pmu_offset; + unsigned long ref_rate; + int i, ret; + int channel; + + phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL); + if (!phy_drd) + return -ENOMEM; + + dev_set_drvdata(dev, phy_drd); + phy_drd->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy_drd->reg_phy = devm_ioremap_resource(dev, res); + if (IS_ERR(phy_drd->reg_phy)) + return PTR_ERR(phy_drd->reg_phy); + + match = of_match_node(exynos5_usbdrd_phy_of_match, pdev->dev.of_node); + + drv_data = match->data; + phy_drd->drv_data = drv_data; + + phy_drd->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(phy_drd->clk)) { + dev_err(dev, "Failed to get clock of phy controller\n"); + return PTR_ERR(phy_drd->clk); + } + + phy_drd->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(phy_drd->ref_clk)) { + dev_err(dev, "Failed to get reference clock of usbdrd phy\n"); + return PTR_ERR(phy_drd->ref_clk); + } + ref_rate = clk_get_rate(phy_drd->ref_clk); + + ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); + if (ret) { + dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", + ref_rate); + return ret; + } + + reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,pmu-syscon"); + if (IS_ERR(reg_pmu)) { + dev_err(dev, "Failed to lookup PMU regmap\n"); + return PTR_ERR(reg_pmu); + } + + /* + * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with + * each having separate power control registers. + * 'channel' facilitates to set such registers. + */ + channel = of_alias_get_id(node, "usbdrdphy"); + if (channel < 0) + dev_dbg(dev, "Not a multi-controller usbdrd phy\n"); + + switch (channel) { + case 1: + pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy; + break; + case 0: + default: + pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy; + break; + } + + dev_vdbg(dev, "Creating usbdrd_phy phy\n"); + + for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { + struct phy *phy = devm_phy_create(dev, &exynos5_usbdrd_phy_ops, + NULL); + if (IS_ERR(phy)) { + dev_err(dev, "Failed to create usbdrd_phy phy\n"); + return PTR_ERR(phy); + } + + phy_drd->phys[i].phy = phy; + phy_drd->phys[i].index = i; + phy_drd->phys[i].reg_pmu = reg_pmu; + phy_drd->phys[i].pmu_offset = pmu_offset; + phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; + phy_set_drvdata(phy, &phy_drd->phys[i]); + } + + phy_provider = devm_of_phy_provider_register(dev, + exynos5_usbdrd_phy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(phy_drd->dev, "Failed to register phy provider\n"); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static struct platform_driver exynos5_usb3drd_phy = { + .probe = exynos5_usbdrd_phy_probe, + .driver = { + .of_match_table = exynos5_usbdrd_phy_of_match, + .name = "exynos5_usb3drd_phy", + .owner = THIS_MODULE, + } +}; + +module_platform_driver(exynos5_usb3drd_phy); +MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver"); +MODULE_AUTHOR("Vivek Gautam "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:exynos5_usb3drd_phy"); diff --git a/include/linux/mfd/syscon/exynos5-pmu.h b/include/linux/mfd/syscon/exynos5-pmu.h new file mode 100644 index 000000000000..00ef24bf6ede --- /dev/null +++ b/include/linux/mfd/syscon/exynos5-pmu.h @@ -0,0 +1,44 @@ +/* + * Exynos5 SoC series Power Management Unit (PMU) register offsets + * and bit definitions. + * + * 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 version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_MFD_SYSCON_PMU_EXYNOS5_H_ +#define _LINUX_MFD_SYSCON_PMU_EXYNOS5_H_ + +/* Exynos5 PMU register definitions */ +#define EXYNOS5_HDMI_PHY_CONTROL (0x700) +#define EXYNOS5_USBDRD_PHY_CONTROL (0x704) + +/* Exynos5250 specific register definitions */ +#define EXYNOS5_USBHOST_PHY_CONTROL (0x708) +#define EXYNOS5_EFNAND_PHY_CONTROL (0x70c) +#define EXYNOS5_MIPI_PHY0_CONTROL (0x710) +#define EXYNOS5_MIPI_PHY1_CONTROL (0x714) +#define EXYNOS5_ADC_PHY_CONTROL (0x718) +#define EXYNOS5_MTCADC_PHY_CONTROL (0x71c) +#define EXYNOS5_DPTX_PHY_CONTROL (0x720) +#define EXYNOS5_SATA_PHY_CONTROL (0x724) + +/* Exynos5420 specific register definitions */ +#define EXYNOS5420_USBDRD1_PHY_CONTROL (0x708) +#define EXYNOS5420_USBHOST_PHY_CONTROL (0x70c) +#define EXYNOS5420_MIPI_PHY0_CONTROL (0x714) +#define EXYNOS5420_MIPI_PHY1_CONTROL (0x718) +#define EXYNOS5420_MIPI_PHY2_CONTROL (0x71c) +#define EXYNOS5420_ADC_PHY_CONTROL (0x720) +#define EXYNOS5420_MTCADC_PHY_CONTROL (0x724) +#define EXYNOS5420_DPTX_PHY_CONTROL (0x728) + +#define EXYNOS5_PHY_ENABLE BIT(0) + +#define EXYNOS5_MIPI_PHY_S_RESETN BIT(1) +#define EXYNOS5_MIPI_PHY_M_RESETN BIT(2) + +#endif /* _LINUX_MFD_SYSCON_PMU_EXYNOS5_H_ */ -- cgit v1.2.3 From f5651986fe438c1ba05fdafa383d38bd86713d13 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 13 May 2014 15:45:55 +0200 Subject: nl80211: fix NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL API My commit removing that also removed it from the header file which can break compilation of userspace that needed it, add it back for API/ABI compatibility purposes (but no code to implement anything for it.) Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1ba9d626aa83..194c1eab04d8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3856,6 +3856,8 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested * to work properly to suppport receiving regulatory hints from * cellular base stations. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only + * here to reserve the value for API/ABI compatibility) * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station * mode @@ -3897,7 +3899,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_HT_IBSS = 1 << 1, NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, - /* bit 4 is reserved - don't use */ + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, NL80211_FEATURE_SAE = 1 << 5, NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, NL80211_FEATURE_SCAN_FLUSH = 1 << 7, -- cgit v1.2.3 From 8c48b50a1a888ac5511fe856d63f72fb688c6bb4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 5 May 2014 11:48:40 +0200 Subject: cfg80211: allow restricting supported dfs regions At the moment, the ath9k/ath10k DFS module only supports detecting ETSI radar patterns. Add a bitmap in the interface combinations, indicating which DFS regions are supported by the detector. If unset, support for all regions is assumed. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 3 +++ net/wireless/nl80211.c | 6 ++++-- net/wireless/util.c | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5c7169b0ac57..e3a48b0a2b3b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2638,6 +2638,7 @@ struct ieee80211_iface_limit { * between infrastructure and AP types must match. This is required * only in special cases. * @radar_detect_widths: bitmap of channel widths supported for radar detection + * @radar_detect_regions: bitmap of regions supported for radar detection * * With this structure the driver can describe which interface * combinations it supports concurrently. @@ -2695,6 +2696,7 @@ struct ieee80211_iface_combination { u8 n_limits; bool beacon_int_infra_match; u8 radar_detect_widths; + u8 radar_detect_regions; }; struct ieee80211_txrx_stypes { diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 406010d4def0..b65095a85dee 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3688,6 +3688,8 @@ enum nl80211_iface_limit_attrs { * different channels may be used within this group. * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap * of supported channel widths for radar detection. + * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap + * of supported regulatory regions for radar detection. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -3721,6 +3723,7 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, /* keep last */ NUM_NL80211_IFACE_COMB, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0f1b18f209d6..c0833830cfe7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -970,8 +970,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy, c->max_interfaces)) goto nla_put_failure; if (large && - nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, - c->radar_detect_widths)) + (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + c->radar_detect_widths) || + nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, + c->radar_detect_regions))) goto nla_put_failure; nla_nest_end(msg, nl_combi); diff --git a/net/wireless/util.c b/net/wireless/util.c index a756429b3a0a..8c61d5c6fad3 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1274,10 +1274,20 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, void *data), void *data) { + const struct ieee80211_regdomain *regdom; + enum nl80211_dfs_regions region = 0; int i, j, iftype; int num_interfaces = 0; u32 used_iftypes = 0; + if (radar_detect) { + rcu_read_lock(); + regdom = rcu_dereference(cfg80211_regdomain); + if (regdom) + region = regdom->dfs_region; + rcu_read_unlock(); + } + for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { num_interfaces += iftype_num[iftype]; if (iftype_num[iftype] > 0 && @@ -1318,6 +1328,10 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if (radar_detect != (c->radar_detect_widths & radar_detect)) goto cont; + if (radar_detect && c->radar_detect_regions && + !(c->radar_detect_regions & BIT(region))) + goto cont; + /* Finally check that all iftypes that we're currently * using are actually part of this combination. If they * aren't then we can't use this combination and have -- cgit v1.2.3 From ec903c0c858e4963a9e0724bdcadfa837253341c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 May 2014 12:11:01 -0400 Subject: cgroup: rename css_tryget*() to css_tryget_online*() Unlike the more usual refcnting, what css_tryget() provides is the distinction between online and offline csses instead of protection against upping a refcnt which already reached zero. cgroup is planning to provide actual tryget which fails if the refcnt already reached zero. Let's rename the existing trygets so that they clearly indicate that they're onliness. I thought about keeping the existing names as-are and introducing new names for the planned actual tryget; however, given that each controller participates in the synchronization of the online state, it seems worthwhile to make it explicit that these functions are about on/offline state. Rename css_tryget() to css_tryget_online() and css_tryget_from_dir() to css_tryget_online_from_dir(). This is pure rename. v2: cgroup_freezer grew new usages of css_tryget(). Update accordingly. Signed-off-by: Tejun Heo Acked-by: Johannes Weiner Acked-by: Michal Hocko Acked-by: Li Zefan Cc: Vivek Goyal Cc: Jens Axboe Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo --- block/blk-cgroup.c | 2 +- fs/bio.c | 2 +- include/linux/cgroup.h | 14 +++++++------- kernel/cgroup.c | 40 ++++++++++++++++++++-------------------- kernel/cgroup_freezer.c | 4 ++-- kernel/cpuset.c | 6 +++--- kernel/events/core.c | 3 ++- mm/hugetlb_cgroup.c | 2 +- mm/memcontrol.c | 46 ++++++++++++++++++++++++---------------------- 9 files changed, 61 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 1039fb9ff5f5..9f5bce33e6fe 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -185,7 +185,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, lockdep_assert_held(q->queue_lock); /* blkg holds a reference to blkcg */ - if (!css_tryget(&blkcg->css)) { + if (!css_tryget_online(&blkcg->css)) { ret = -EINVAL; goto err_free_blkg; } diff --git a/fs/bio.c b/fs/bio.c index 6f0362b77806..0608ef9422bd 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1970,7 +1970,7 @@ int bio_associate_current(struct bio *bio) /* associate blkcg if exists */ rcu_read_lock(); css = task_css(current, blkio_cgrp_id); - if (css && css_tryget(css)) + if (css && css_tryget_online(css)) bio->bi_css = css; rcu_read_unlock(); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index bde44618d8c2..c5f3684ef557 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -95,16 +95,16 @@ static inline void css_get(struct cgroup_subsys_state *css) } /** - * css_tryget - try to obtain a reference on the specified css + * css_tryget_online - try to obtain a reference on the specified css if online * @css: target css * - * Obtain a reference on @css if it's alive. The caller naturally needs to - * ensure that @css is accessible but doesn't have to be holding a + * Obtain a reference on @css if it's online. The caller naturally needs + * to ensure that @css is accessible but doesn't have to be holding a * reference on it - IOW, RCU protected access is good enough for this * function. Returns %true if a reference count was successfully obtained; * %false otherwise. */ -static inline bool css_tryget(struct cgroup_subsys_state *css) +static inline bool css_tryget_online(struct cgroup_subsys_state *css) { if (css->flags & CSS_ROOT) return true; @@ -115,7 +115,7 @@ static inline bool css_tryget(struct cgroup_subsys_state *css) * css_put - put a css reference * @css: target css * - * Put a reference obtained via css_get() and css_tryget(). + * Put a reference obtained via css_get() and css_tryget_online(). */ static inline void css_put(struct cgroup_subsys_state *css) { @@ -905,8 +905,8 @@ void css_task_iter_end(struct css_task_iter *it); int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from); -struct cgroup_subsys_state *css_tryget_from_dir(struct dentry *dentry, - struct cgroup_subsys *ss); +struct cgroup_subsys_state *css_tryget_online_from_dir(struct dentry *dentry, + struct cgroup_subsys *ss); #else /* !CONFIG_CGROUPS */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 7633703e9baf..671d8a6dae37 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3771,7 +3771,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) /* * We aren't being called from kernfs and there's no guarantee on - * @kn->priv's validity. For this and css_tryget_from_dir(), + * @kn->priv's validity. For this and css_tryget_online_from_dir(), * @kn->priv is RCU safe. Let's do the RCU dancing. */ rcu_read_lock(); @@ -4060,9 +4060,9 @@ err: * Implemented in kill_css(). * * 2. When the percpu_ref is confirmed to be visible as killed on all CPUs - * and thus css_tryget() is guaranteed to fail, the css can be offlined - * by invoking offline_css(). After offlining, the base ref is put. - * Implemented in css_killed_work_fn(). + * and thus css_tryget_online() is guaranteed to fail, the css can be + * offlined by invoking offline_css(). After offlining, the base ref is + * put. Implemented in css_killed_work_fn(). * * 3. When the percpu_ref reaches zero, the only possible remaining * accessors are inside RCU read sections. css_release() schedules the @@ -4386,7 +4386,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, /* * This is called when the refcnt of a css is confirmed to be killed. - * css_tryget() is now guaranteed to fail. + * css_tryget_online() is now guaranteed to fail. */ static void css_killed_work_fn(struct work_struct *work) { @@ -4398,8 +4398,8 @@ static void css_killed_work_fn(struct work_struct *work) mutex_lock(&cgroup_mutex); /* - * css_tryget() is guaranteed to fail now. Tell subsystems to - * initate destruction. + * css_tryget_online() is guaranteed to fail now. Tell subsystems + * to initate destruction. */ offline_css(css); @@ -4440,8 +4440,8 @@ static void css_killed_ref_fn(struct percpu_ref *ref) * * This function initiates destruction of @css by removing cgroup interface * files and putting its base reference. ->css_offline() will be invoked - * asynchronously once css_tryget() is guaranteed to fail and when the - * reference count reaches zero, @css will be released. + * asynchronously once css_tryget_online() is guaranteed to fail and when + * the reference count reaches zero, @css will be released. */ static void kill_css(struct cgroup_subsys_state *css) { @@ -4462,7 +4462,7 @@ static void kill_css(struct cgroup_subsys_state *css) /* * cgroup core guarantees that, by the time ->css_offline() is * invoked, no new css reference will be given out via - * css_tryget(). We can't simply call percpu_ref_kill() and + * css_tryget_online(). We can't simply call percpu_ref_kill() and * proceed to offlining css's because percpu_ref_kill() doesn't * guarantee that the ref is seen as killed on all CPUs on return. * @@ -4478,9 +4478,9 @@ static void kill_css(struct cgroup_subsys_state *css) * * css's make use of percpu refcnts whose killing latency shouldn't be * exposed to userland and are RCU protected. Also, cgroup core needs to - * guarantee that css_tryget() won't succeed by the time ->css_offline() is - * invoked. To satisfy all the requirements, destruction is implemented in - * the following two steps. + * guarantee that css_tryget_online() won't succeed by the time + * ->css_offline() is invoked. To satisfy all the requirements, + * destruction is implemented in the following two steps. * * s1. Verify @cgrp can be destroyed and mark it dying. Remove all * userland visible parts and start killing the percpu refcnts of @@ -4574,9 +4574,9 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) /* * There are two control paths which try to determine cgroup from * dentry without going through kernfs - cgroupstats_build() and - * css_tryget_from_dir(). Those are supported by RCU protecting - * clearing of cgrp->kn->priv backpointer, which should happen - * after all files under it have been removed. + * css_tryget_online_from_dir(). Those are supported by RCU + * protecting clearing of cgrp->kn->priv backpointer, which should + * happen after all files under it have been removed. */ kernfs_remove(cgrp->kn); /* @cgrp has an extra ref on its kn */ RCU_INIT_POINTER(*(void __rcu __force **)&cgrp->kn->priv, NULL); @@ -5173,7 +5173,7 @@ static int __init cgroup_disable(char *str) __setup("cgroup_disable=", cgroup_disable); /** - * css_tryget_from_dir - get corresponding css from the dentry of a cgroup dir + * css_tryget_online_from_dir - get corresponding css from a cgroup dentry * @dentry: directory dentry of interest * @ss: subsystem of interest * @@ -5181,8 +5181,8 @@ __setup("cgroup_disable=", cgroup_disable); * to get the corresponding css and return it. If such css doesn't exist * or can't be pinned, an ERR_PTR value is returned. */ -struct cgroup_subsys_state *css_tryget_from_dir(struct dentry *dentry, - struct cgroup_subsys *ss) +struct cgroup_subsys_state *css_tryget_online_from_dir(struct dentry *dentry, + struct cgroup_subsys *ss) { struct kernfs_node *kn = kernfs_node_from_dentry(dentry); struct cgroup_subsys_state *css = NULL; @@ -5204,7 +5204,7 @@ struct cgroup_subsys_state *css_tryget_from_dir(struct dentry *dentry, if (cgrp) css = cgroup_css(cgrp, ss); - if (!css || !css_tryget(css)) + if (!css || !css_tryget_online(css)) css = ERR_PTR(-ENOENT); rcu_read_unlock(); diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 345628c78b5b..0398f7e9ac81 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -304,7 +304,7 @@ static int freezer_read(struct seq_file *m, void *v) /* update states bottom-up */ css_for_each_descendant_post(pos, css) { - if (!css_tryget(pos)) + if (!css_tryget_online(pos)) continue; rcu_read_unlock(); @@ -404,7 +404,7 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) struct freezer *pos_f = css_freezer(pos); struct freezer *parent = parent_freezer(pos_f); - if (!css_tryget(pos)) + if (!css_tryget_online(pos)) continue; rcu_read_unlock(); diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 7c0e8da59e26..37ca0a5c226d 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -872,7 +872,7 @@ static void update_tasks_cpumask_hier(struct cpuset *root_cs, bool update_root) continue; } } - if (!css_tryget(&cp->css)) + if (!css_tryget_online(&cp->css)) continue; rcu_read_unlock(); @@ -1108,7 +1108,7 @@ static void update_tasks_nodemask_hier(struct cpuset *root_cs, bool update_root) continue; } } - if (!css_tryget(&cp->css)) + if (!css_tryget_online(&cp->css)) continue; rcu_read_unlock(); @@ -2153,7 +2153,7 @@ static void cpuset_hotplug_workfn(struct work_struct *work) rcu_read_lock(); cpuset_for_each_descendant_pre(cs, pos_css, &top_cpuset) { - if (cs == &top_cpuset || !css_tryget(&cs->css)) + if (cs == &top_cpuset || !css_tryget_online(&cs->css)) continue; rcu_read_unlock(); diff --git a/kernel/events/core.c b/kernel/events/core.c index f83a71a3e46d..077968d19b8a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -607,7 +607,8 @@ static inline int perf_cgroup_connect(int fd, struct perf_event *event, if (!f.file) return -EBADF; - css = css_tryget_from_dir(f.file->f_dentry, &perf_event_cgrp_subsys); + css = css_tryget_online_from_dir(f.file->f_dentry, + &perf_event_cgrp_subsys); if (IS_ERR(css)) { ret = PTR_ERR(css); goto out; diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 595d7fd795e1..372f1adca491 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -181,7 +181,7 @@ int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, again: rcu_read_lock(); h_cg = hugetlb_cgroup_from_task(current); - if (!css_tryget(&h_cg->css)) { + if (!css_tryget_online(&h_cg->css)) { rcu_read_unlock(); goto again; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c3f82f69ef58..5cf3246314a2 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -567,7 +567,8 @@ void sock_update_memcg(struct sock *sk) memcg = mem_cgroup_from_task(current); cg_proto = sk->sk_prot->proto_cgroup(memcg); if (!mem_cgroup_is_root(memcg) && - memcg_proto_active(cg_proto) && css_tryget(&memcg->css)) { + memcg_proto_active(cg_proto) && + css_tryget_online(&memcg->css)) { sk->sk_cgrp = cg_proto; } rcu_read_unlock(); @@ -834,7 +835,7 @@ retry: */ __mem_cgroup_remove_exceeded(mz->memcg, mz, mctz); if (!res_counter_soft_limit_excess(&mz->memcg->res) || - !css_tryget(&mz->memcg->css)) + !css_tryget_online(&mz->memcg->css)) goto retry; done: return mz; @@ -1076,7 +1077,7 @@ static struct mem_cgroup *get_mem_cgroup_from_mm(struct mm_struct *mm) memcg = mem_cgroup_from_task(rcu_dereference(mm->owner)); if (unlikely(!memcg)) memcg = root_mem_cgroup; - } while (!css_tryget(&memcg->css)); + } while (!css_tryget_online(&memcg->css)); rcu_read_unlock(); return memcg; } @@ -1113,7 +1114,8 @@ skip_node: */ if (next_css) { if ((next_css == &root->css) || - ((next_css->flags & CSS_ONLINE) && css_tryget(next_css))) + ((next_css->flags & CSS_ONLINE) && + css_tryget_online(next_css))) return mem_cgroup_from_css(next_css); prev_css = next_css; @@ -1159,7 +1161,7 @@ mem_cgroup_iter_load(struct mem_cgroup_reclaim_iter *iter, * would be returned all the time. */ if (position && position != root && - !css_tryget(&position->css)) + !css_tryget_online(&position->css)) position = NULL; } return position; @@ -2785,9 +2787,9 @@ static void __mem_cgroup_cancel_local_charge(struct mem_cgroup *memcg, /* * A helper function to get mem_cgroup from ID. must be called under - * rcu_read_lock(). The caller is responsible for calling css_tryget if - * the mem_cgroup is used for charging. (dropping refcnt from swap can be - * called against removed memcg.) + * rcu_read_lock(). The caller is responsible for calling + * css_tryget_online() if the mem_cgroup is used for charging. (dropping + * refcnt from swap can be called against removed memcg.) */ static struct mem_cgroup *mem_cgroup_lookup(unsigned short id) { @@ -2810,14 +2812,14 @@ struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page) lock_page_cgroup(pc); if (PageCgroupUsed(pc)) { memcg = pc->mem_cgroup; - if (memcg && !css_tryget(&memcg->css)) + if (memcg && !css_tryget_online(&memcg->css)) memcg = NULL; } else if (PageSwapCache(page)) { ent.val = page_private(page); id = lookup_swap_cgroup_id(ent); rcu_read_lock(); memcg = mem_cgroup_lookup(id); - if (memcg && !css_tryget(&memcg->css)) + if (memcg && !css_tryget_online(&memcg->css)) memcg = NULL; rcu_read_unlock(); } @@ -3473,7 +3475,7 @@ struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep, } /* The corresponding put will be done in the workqueue. */ - if (!css_tryget(&memcg->css)) + if (!css_tryget_online(&memcg->css)) goto out; rcu_read_unlock(); @@ -4246,8 +4248,8 @@ void mem_cgroup_uncharge_swap(swp_entry_t ent) memcg = mem_cgroup_lookup(id); if (memcg) { /* - * We uncharge this because swap is freed. - * This memcg can be obsolete one. We avoid calling css_tryget + * We uncharge this because swap is freed. This memcg can + * be obsolete one. We avoid calling css_tryget_online(). */ if (!mem_cgroup_is_root(memcg)) res_counter_uncharge(&memcg->memsw, PAGE_SIZE); @@ -5840,10 +5842,10 @@ static void kmem_cgroup_css_offline(struct mem_cgroup *memcg) * which is then paired with css_put during uncharge resp. here. * * Although this might sound strange as this path is called from - * css_offline() when the referencemight have dropped down to 0 - * and shouldn't be incremented anymore (css_tryget would fail) - * we do not have other options because of the kmem allocations - * lifetime. + * css_offline() when the referencemight have dropped down to 0 and + * shouldn't be incremented anymore (css_tryget_online() would + * fail) we do not have other options because of the kmem + * allocations lifetime. */ css_get(&memcg->css); @@ -6051,8 +6053,8 @@ static int memcg_write_event_control(struct cgroup_subsys_state *css, * automatically removed on cgroup destruction but the removal is * asynchronous, so take an extra ref on @css. */ - cfile_css = css_tryget_from_dir(cfile.file->f_dentry->d_parent, - &memory_cgrp_subsys); + cfile_css = css_tryget_online_from_dir(cfile.file->f_dentry->d_parent, + &memory_cgrp_subsys); ret = -EINVAL; if (IS_ERR(cfile_css)) goto out_put_cfile; @@ -6496,7 +6498,7 @@ static void mem_cgroup_css_free(struct cgroup_subsys_state *css) /* * XXX: css_offline() would be where we should reparent all * memory to prepare the cgroup for destruction. However, - * memcg does not do css_tryget() and res_counter charging + * memcg does not do css_tryget_online() and res_counter charging * under the same RCU lock region, which means that charging * could race with offlining. Offlining only happens to * cgroups with no tasks in them but charges can show up @@ -6510,9 +6512,9 @@ static void mem_cgroup_css_free(struct cgroup_subsys_state *css) * lookup_swap_cgroup_id() * rcu_read_lock() * mem_cgroup_lookup() - * css_tryget() + * css_tryget_online() * rcu_read_unlock() - * disable css_tryget() + * disable css_tryget_online() * call_rcu() * offline_css() * reparent_charges() -- cgit v1.2.3 From b41686401e501430ffe93b575ef7959d2ecc6f2e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 May 2014 12:16:21 -0400 Subject: cgroup: implement cftype->write() During the recent conversion to kernfs, cftype's seq_file operations are updated so that they are directly mapped to kernfs operations and thus can fully access the associated kernfs and cgroup contexts; however, write path hasn't seen similar updates and none of the existing write operations has access to, for example, the associated kernfs_open_file. Let's introduce a new operation cftype->write() which maps directly to the kernfs write operation and has access to all the arguments and contexts. This will replace ->write_string() and ->trigger() and ease manipulation of kernfs active protection from cgroup file operations. Two accessors - of_cft() and of_css() - are introduced to enable accessing the associated cgroup context from cftype->write() which only takes kernfs_open_file for the context information. The accessors for seq_file operations - seq_cft() and seq_css() - are rewritten to wrap the of_ accessors. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 27 +++++++++++++++++++++++---- kernel/cgroup.c | 14 ++++++++------ 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c5f3684ef557..c5a170ca4a48 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -515,6 +515,15 @@ struct cftype { */ int (*trigger)(struct cgroup_subsys_state *css, unsigned int event); + /* + * write() is the generic write callback which maps directly to + * kernfs write operation and overrides all other operations. + * Maximum write size is determined by ->max_write_len. Use + * of_css/cft() to access the associated css and cft. + */ + ssize_t (*write)(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off); + #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lock_class_key lockdep_key; #endif @@ -552,14 +561,24 @@ static inline ino_t cgroup_ino(struct cgroup *cgrp) return 0; } -static inline struct cftype *seq_cft(struct seq_file *seq) +/* cft/css accessors for cftype->write() operation */ +static inline struct cftype *of_cft(struct kernfs_open_file *of) { - struct kernfs_open_file *of = seq->private; - return of->kn->priv; } -struct cgroup_subsys_state *seq_css(struct seq_file *seq); +struct cgroup_subsys_state *of_css(struct kernfs_open_file *of); + +/* cft/css accessors for cftype->seq_*() operations */ +static inline struct cftype *seq_cft(struct seq_file *seq) +{ + return of_cft(seq->private); +} + +static inline struct cgroup_subsys_state *seq_css(struct seq_file *seq) +{ + return of_css(seq->private); +} /* * Name / path handling functions. All are thin wrappers around the kernfs diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 671d8a6dae37..a16f91d12f4e 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -283,11 +283,10 @@ static inline bool cgroup_is_dead(const struct cgroup *cgrp) return test_bit(CGRP_DEAD, &cgrp->flags); } -struct cgroup_subsys_state *seq_css(struct seq_file *seq) +struct cgroup_subsys_state *of_css(struct kernfs_open_file *of) { - struct kernfs_open_file *of = seq->private; struct cgroup *cgrp = of->kn->parent->priv; - struct cftype *cft = seq_cft(seq); + struct cftype *cft = of_cft(of); /* * This is open and unprotected implementation of cgroup_css(). @@ -302,7 +301,7 @@ struct cgroup_subsys_state *seq_css(struct seq_file *seq) else return &cgrp->dummy_css; } -EXPORT_SYMBOL_GPL(seq_css); +EXPORT_SYMBOL_GPL(of_css); /** * cgroup_is_descendant - test ancestry @@ -1035,8 +1034,8 @@ static umode_t cgroup_file_mode(const struct cftype *cft) if (cft->read_u64 || cft->read_s64 || cft->seq_show) mode |= S_IRUGO; - if (cft->write_u64 || cft->write_s64 || cft->write_string || - cft->trigger) + if (cft->write_u64 || cft->write_s64 || cft->write || + cft->write_string || cft->trigger) mode |= S_IWUSR; return mode; @@ -2726,6 +2725,9 @@ static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf, struct cgroup_subsys_state *css; int ret; + if (cft->write) + return cft->write(of, buf, nbytes, off); + /* * kernfs guarantees that a file isn't deleted with operations in * flight, which means that the matching css is and stays alive and -- cgit v1.2.3 From 451af504df0c62f695a69b83c250486e77c66378 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 May 2014 12:16:21 -0400 Subject: cgroup: replace cftype->write_string() with cftype->write() Convert all cftype->write_string() users to the new cftype->write() which maps directly to kernfs write operation and has full access to kernfs and cgroup contexts. The conversions are mostly mechanical. * @css and @cft are accessed using of_css() and of_cft() accessors respectively instead of being specified as arguments. * Should return @nbytes on success instead of 0. * @buf is not trimmed automatically. Trim if necessary. Note that blkcg and netprio don't need this as the parsers already handle whitespaces. cftype->write_string() has no user left after the conversions and removed. While at it, remove unnecessary local variable @p in cgroup_subtree_control_write() and stale comment about CGROUP_LOCAL_BUFFER_SIZE in cgroup_freezer.c. This patch doesn't introduce any visible behavior changes. v2: netprio was missing from conversion. Converted. Signed-off-by: Tejun Heo Acked-by: Aristeu Rozanski Acked-by: Vivek Goyal Acked-by: Li Zefan Cc: Jens Axboe Cc: Johannes Weiner Cc: Michal Hocko Cc: Neil Horman Cc: "David S. Miller" --- block/blk-throttle.c | 32 ++++++++++++++++---------------- block/cfq-iosched.c | 28 ++++++++++++++-------------- include/linux/cgroup.h | 10 +--------- kernel/cgroup.c | 38 +++++++++++++++++++------------------- kernel/cgroup_freezer.c | 20 +++++++++----------- kernel/cpuset.c | 16 +++++++++------- mm/hugetlb_cgroup.c | 17 +++++++++-------- mm/memcontrol.c | 46 +++++++++++++++++++++++++--------------------- net/core/netprio_cgroup.c | 12 ++++++------ net/ipv4/tcp_memcontrol.c | 16 +++++++++------- security/device_cgroup.c | 14 +++++++------- 11 files changed, 124 insertions(+), 125 deletions(-) (limited to 'include') diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 033745cd7fba..5e8fd1bace98 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1346,10 +1346,10 @@ static int tg_print_conf_uint(struct seq_file *sf, void *v) return 0; } -static int tg_set_conf(struct cgroup_subsys_state *css, struct cftype *cft, - const char *buf, bool is_u64) +static ssize_t tg_set_conf(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off, bool is_u64) { - struct blkcg *blkcg = css_to_blkcg(css); + struct blkcg *blkcg = css_to_blkcg(of_css(of)); struct blkg_conf_ctx ctx; struct throtl_grp *tg; struct throtl_service_queue *sq; @@ -1368,9 +1368,9 @@ static int tg_set_conf(struct cgroup_subsys_state *css, struct cftype *cft, ctx.v = -1; if (is_u64) - *(u64 *)((void *)tg + cft->private) = ctx.v; + *(u64 *)((void *)tg + of_cft(of)->private) = ctx.v; else - *(unsigned int *)((void *)tg + cft->private) = ctx.v; + *(unsigned int *)((void *)tg + of_cft(of)->private) = ctx.v; throtl_log(&tg->service_queue, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u", @@ -1404,19 +1404,19 @@ static int tg_set_conf(struct cgroup_subsys_state *css, struct cftype *cft, } blkg_conf_finish(&ctx); - return 0; + return nbytes; } -static int tg_set_conf_u64(struct cgroup_subsys_state *css, struct cftype *cft, - char *buf) +static ssize_t tg_set_conf_u64(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { - return tg_set_conf(css, cft, buf, true); + return tg_set_conf(of, buf, nbytes, off, true); } -static int tg_set_conf_uint(struct cgroup_subsys_state *css, struct cftype *cft, - char *buf) +static ssize_t tg_set_conf_uint(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { - return tg_set_conf(css, cft, buf, false); + return tg_set_conf(of, buf, nbytes, off, false); } static struct cftype throtl_files[] = { @@ -1424,25 +1424,25 @@ static struct cftype throtl_files[] = { .name = "throttle.read_bps_device", .private = offsetof(struct throtl_grp, bps[READ]), .seq_show = tg_print_conf_u64, - .write_string = tg_set_conf_u64, + .write = tg_set_conf_u64, }, { .name = "throttle.write_bps_device", .private = offsetof(struct throtl_grp, bps[WRITE]), .seq_show = tg_print_conf_u64, - .write_string = tg_set_conf_u64, + .write = tg_set_conf_u64, }, { .name = "throttle.read_iops_device", .private = offsetof(struct throtl_grp, iops[READ]), .seq_show = tg_print_conf_uint, - .write_string = tg_set_conf_uint, + .write = tg_set_conf_uint, }, { .name = "throttle.write_iops_device", .private = offsetof(struct throtl_grp, iops[WRITE]), .seq_show = tg_print_conf_uint, - .write_string = tg_set_conf_uint, + .write = tg_set_conf_uint, }, { .name = "throttle.io_service_bytes", diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index e0985f1955e7..a73020b8c9af 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1670,11 +1670,11 @@ static int cfq_print_leaf_weight(struct seq_file *sf, void *v) return 0; } -static int __cfqg_set_weight_device(struct cgroup_subsys_state *css, - struct cftype *cft, const char *buf, - bool is_leaf_weight) +static ssize_t __cfqg_set_weight_device(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off, + bool is_leaf_weight) { - struct blkcg *blkcg = css_to_blkcg(css); + struct blkcg *blkcg = css_to_blkcg(of_css(of)); struct blkg_conf_ctx ctx; struct cfq_group *cfqg; int ret; @@ -1697,19 +1697,19 @@ static int __cfqg_set_weight_device(struct cgroup_subsys_state *css, } blkg_conf_finish(&ctx); - return ret; + return ret ?: nbytes; } -static int cfqg_set_weight_device(struct cgroup_subsys_state *css, - struct cftype *cft, char *buf) +static ssize_t cfqg_set_weight_device(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { - return __cfqg_set_weight_device(css, cft, buf, false); + return __cfqg_set_weight_device(of, buf, nbytes, off, false); } -static int cfqg_set_leaf_weight_device(struct cgroup_subsys_state *css, - struct cftype *cft, char *buf) +static ssize_t cfqg_set_leaf_weight_device(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { - return __cfqg_set_weight_device(css, cft, buf, true); + return __cfqg_set_weight_device(of, buf, nbytes, off, true); } static int __cfq_set_weight(struct cgroup_subsys_state *css, struct cftype *cft, @@ -1837,7 +1837,7 @@ static struct cftype cfq_blkcg_files[] = { .name = "weight_device", .flags = CFTYPE_ONLY_ON_ROOT, .seq_show = cfqg_print_leaf_weight_device, - .write_string = cfqg_set_leaf_weight_device, + .write = cfqg_set_leaf_weight_device, }, { .name = "weight", @@ -1851,7 +1851,7 @@ static struct cftype cfq_blkcg_files[] = { .name = "weight_device", .flags = CFTYPE_NOT_ON_ROOT, .seq_show = cfqg_print_weight_device, - .write_string = cfqg_set_weight_device, + .write = cfqg_set_weight_device, }, { .name = "weight", @@ -1863,7 +1863,7 @@ static struct cftype cfq_blkcg_files[] = { { .name = "leaf_weight_device", .seq_show = cfqg_print_leaf_weight_device, - .write_string = cfqg_set_leaf_weight_device, + .write = cfqg_set_leaf_weight_device, }, { .name = "leaf_weight", diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c5a170ca4a48..aecdc84fe128 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -453,8 +453,7 @@ struct cftype { /* * The maximum length of string, excluding trailing nul, that can - * be passed to write_string. If < PAGE_SIZE-1, PAGE_SIZE-1 is - * assumed. + * be passed to write. If < PAGE_SIZE-1, PAGE_SIZE-1 is assumed. */ size_t max_write_len; @@ -500,13 +499,6 @@ struct cftype { int (*write_s64)(struct cgroup_subsys_state *css, struct cftype *cft, s64 val); - /* - * write_string() is passed a nul-terminated kernelspace - * buffer of maximum length determined by max_write_len. - * Returns 0 or -ve error code. - */ - int (*write_string)(struct cgroup_subsys_state *css, struct cftype *cft, - char *buffer); /* * trigger() callback can be used to get some kick from the * userspace, when the actual string written is not important diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a16f91d12f4e..2a88ce7b24b6 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1035,7 +1035,7 @@ static umode_t cgroup_file_mode(const struct cftype *cft) mode |= S_IRUGO; if (cft->write_u64 || cft->write_s64 || cft->write || - cft->write_string || cft->trigger) + cft->trigger) mode |= S_IWUSR; return mode; @@ -2352,20 +2352,21 @@ static int cgroup_procs_write(struct cgroup_subsys_state *css, return attach_task_by_pid(css->cgroup, tgid, true); } -static int cgroup_release_agent_write(struct cgroup_subsys_state *css, - struct cftype *cft, char *buffer) +static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { - struct cgroup_root *root = css->cgroup->root; + struct cgroup *cgrp = of_css(of)->cgroup; + struct cgroup_root *root = cgrp->root; BUILD_BUG_ON(sizeof(root->release_agent_path) < PATH_MAX); - if (!cgroup_lock_live_group(css->cgroup)) + if (!cgroup_lock_live_group(cgrp)) return -ENODEV; spin_lock(&release_agent_path_lock); - strlcpy(root->release_agent_path, buffer, + strlcpy(root->release_agent_path, strstrip(buf), sizeof(root->release_agent_path)); spin_unlock(&release_agent_path_lock); mutex_unlock(&cgroup_mutex); - return 0; + return nbytes; } static int cgroup_release_agent_show(struct seq_file *seq, void *v) @@ -2530,21 +2531,22 @@ out_finish: } /* change the enabled child controllers for a cgroup in the default hierarchy */ -static int cgroup_subtree_control_write(struct cgroup_subsys_state *dummy_css, - struct cftype *cft, char *buffer) +static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, + loff_t off) { unsigned int enable = 0, disable = 0; - struct cgroup *cgrp = dummy_css->cgroup, *child; + struct cgroup *cgrp = of_css(of)->cgroup, *child; struct cgroup_subsys *ss; - char *tok, *p; + char *tok; int ssid, ret; /* * Parse input - space separated list of subsystem names prefixed * with either + or -. */ - p = buffer; - while ((tok = strsep(&p, " "))) { + buf = strstrip(buf); + while ((tok = strsep(&buf, " "))) { if (tok[0] == '\0') continue; for_each_subsys(ss, ssid) { @@ -2692,7 +2694,7 @@ out_unlock_tree: out_unbreak: kernfs_unbreak_active_protection(cgrp->control_kn); cgroup_put(cgrp); - return ret; + return ret ?: nbytes; err_undo_css: cgrp->child_subsys_mask &= ~enable; @@ -2738,9 +2740,7 @@ static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf, css = cgroup_css(cgrp, cft->ss); rcu_read_unlock(); - if (cft->write_string) { - ret = cft->write_string(css, cft, strstrip(buf)); - } else if (cft->write_u64) { + if (cft->write_u64) { unsigned long long v; ret = kstrtoull(buf, 0, &v); if (!ret) @@ -3984,7 +3984,7 @@ static struct cftype cgroup_base_files[] = { .name = "cgroup.subtree_control", .flags = CFTYPE_ONLY_ON_DFL, .seq_show = cgroup_subtree_control_show, - .write_string = cgroup_subtree_control_write, + .write = cgroup_subtree_control_write, }, { .name = "cgroup.populated", @@ -4018,7 +4018,7 @@ static struct cftype cgroup_base_files[] = { .name = "release_agent", .flags = CFTYPE_INSANE | CFTYPE_ONLY_ON_ROOT, .seq_show = cgroup_release_agent_show, - .write_string = cgroup_release_agent_write, + .write = cgroup_release_agent_write, .max_write_len = PATH_MAX - 1, }, { } /* terminate */ diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 0398f7e9ac81..6b4e60e33a9a 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -73,10 +73,6 @@ bool cgroup_freezing(struct task_struct *task) return ret; } -/* - * cgroups_write_string() limits the size of freezer state strings to - * CGROUP_LOCAL_BUFFER_SIZE - */ static const char *freezer_state_strs(unsigned int state) { if (state & CGROUP_FROZEN) @@ -423,20 +419,22 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) mutex_unlock(&freezer_mutex); } -static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft, - char *buffer) +static ssize_t freezer_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { bool freeze; - if (strcmp(buffer, freezer_state_strs(0)) == 0) + buf = strstrip(buf); + + if (strcmp(buf, freezer_state_strs(0)) == 0) freeze = false; - else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0) + else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0) freeze = true; else return -EINVAL; - freezer_change_state(css_freezer(css), freeze); - return 0; + freezer_change_state(css_freezer(of_css(of)), freeze); + return nbytes; } static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css, @@ -460,7 +458,7 @@ static struct cftype files[] = { .name = "state", .flags = CFTYPE_NOT_ON_ROOT, .seq_show = freezer_read, - .write_string = freezer_write, + .write = freezer_write, }, { .name = "self_freezing", diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 37ca0a5c226d..2f4b08b8db24 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1603,13 +1603,15 @@ out_unlock: /* * Common handling for a write to a "cpus" or "mems" file. */ -static int cpuset_write_resmask(struct cgroup_subsys_state *css, - struct cftype *cft, char *buf) +static ssize_t cpuset_write_resmask(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { - struct cpuset *cs = css_cs(css); + struct cpuset *cs = css_cs(of_css(of)); struct cpuset *trialcs; int retval = -ENODEV; + buf = strstrip(buf); + /* * CPU or memory hotunplug may leave @cs w/o any execution * resources, in which case the hotplug code asynchronously updates @@ -1633,7 +1635,7 @@ static int cpuset_write_resmask(struct cgroup_subsys_state *css, goto out_unlock; } - switch (cft->private) { + switch (of_cft(of)->private) { case FILE_CPULIST: retval = update_cpumask(cs, trialcs, buf); break; @@ -1648,7 +1650,7 @@ static int cpuset_write_resmask(struct cgroup_subsys_state *css, free_trial_cpuset(trialcs); out_unlock: mutex_unlock(&cpuset_mutex); - return retval; + return retval ?: nbytes; } /* @@ -1750,7 +1752,7 @@ static struct cftype files[] = { { .name = "cpus", .seq_show = cpuset_common_seq_show, - .write_string = cpuset_write_resmask, + .write = cpuset_write_resmask, .max_write_len = (100U + 6 * NR_CPUS), .private = FILE_CPULIST, }, @@ -1758,7 +1760,7 @@ static struct cftype files[] = { { .name = "mems", .seq_show = cpuset_common_seq_show, - .write_string = cpuset_write_resmask, + .write = cpuset_write_resmask, .max_write_len = (100U + 6 * MAX_NUMNODES), .private = FILE_MEMLIST, }, diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 372f1adca491..191de26b0148 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -253,15 +253,16 @@ static u64 hugetlb_cgroup_read_u64(struct cgroup_subsys_state *css, return res_counter_read_u64(&h_cg->hugepage[idx], name); } -static int hugetlb_cgroup_write(struct cgroup_subsys_state *css, - struct cftype *cft, char *buffer) +static ssize_t hugetlb_cgroup_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { int idx, name, ret; unsigned long long val; - struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css); + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of)); - idx = MEMFILE_IDX(cft->private); - name = MEMFILE_ATTR(cft->private); + buf = strstrip(buf); + idx = MEMFILE_IDX(of_cft(of)->private); + name = MEMFILE_ATTR(of_cft(of)->private); switch (name) { case RES_LIMIT: @@ -271,7 +272,7 @@ static int hugetlb_cgroup_write(struct cgroup_subsys_state *css, break; } /* This function does all necessary parse...reuse it */ - ret = res_counter_memparse_write_strategy(buffer, &val); + ret = res_counter_memparse_write_strategy(buf, &val); if (ret) break; ret = res_counter_set_limit(&h_cg->hugepage[idx], val); @@ -280,7 +281,7 @@ static int hugetlb_cgroup_write(struct cgroup_subsys_state *css, ret = -EINVAL; break; } - return ret; + return ret ?: nbytes; } static int hugetlb_cgroup_reset(struct cgroup_subsys_state *css, @@ -331,7 +332,7 @@ static void __init __hugetlb_cgroup_file_init(int idx) snprintf(cft->name, MAX_CFTYPE_NAME, "%s.limit_in_bytes", buf); cft->private = MEMFILE_PRIVATE(idx, RES_LIMIT); cft->read_u64 = hugetlb_cgroup_read_u64; - cft->write_string = hugetlb_cgroup_write; + cft->write = hugetlb_cgroup_write; /* Add the usage file */ cft = &h->cgroup_files[1]; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5cf3246314a2..7098a43f7447 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5143,17 +5143,18 @@ static int memcg_update_kmem_limit(struct mem_cgroup *memcg, * The user of this function is... * RES_LIMIT. */ -static int mem_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, - char *buffer) +static ssize_t mem_cgroup_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { - struct mem_cgroup *memcg = mem_cgroup_from_css(css); + struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); enum res_type type; int name; unsigned long long val; int ret; - type = MEMFILE_TYPE(cft->private); - name = MEMFILE_ATTR(cft->private); + buf = strstrip(buf); + type = MEMFILE_TYPE(of_cft(of)->private); + name = MEMFILE_ATTR(of_cft(of)->private); switch (name) { case RES_LIMIT: @@ -5162,7 +5163,7 @@ static int mem_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, break; } /* This function does all necessary parse...reuse it */ - ret = res_counter_memparse_write_strategy(buffer, &val); + ret = res_counter_memparse_write_strategy(buf, &val); if (ret) break; if (type == _MEM) @@ -5175,7 +5176,7 @@ static int mem_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, return -EINVAL; break; case RES_SOFT_LIMIT: - ret = res_counter_memparse_write_strategy(buffer, &val); + ret = res_counter_memparse_write_strategy(buf, &val); if (ret) break; /* @@ -5192,7 +5193,7 @@ static int mem_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, ret = -EINVAL; /* should be BUG() ? */ break; } - return ret; + return ret ?: nbytes; } static void memcg_get_hierarchical_limit(struct mem_cgroup *memcg, @@ -5964,9 +5965,10 @@ static void memcg_event_ptable_queue_proc(struct file *file, * Input must be in format ' '. * Interpretation of args is defined by control file implementation. */ -static int memcg_write_event_control(struct cgroup_subsys_state *css, - struct cftype *cft, char *buffer) +static ssize_t memcg_write_event_control(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { + struct cgroup_subsys_state *css = of_css(of); struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup_event *event; struct cgroup_subsys_state *cfile_css; @@ -5977,15 +5979,17 @@ static int memcg_write_event_control(struct cgroup_subsys_state *css, char *endp; int ret; - efd = simple_strtoul(buffer, &endp, 10); + buf = strstrip(buf); + + efd = simple_strtoul(buf, &endp, 10); if (*endp != ' ') return -EINVAL; - buffer = endp + 1; + buf = endp + 1; - cfd = simple_strtoul(buffer, &endp, 10); + cfd = simple_strtoul(buf, &endp, 10); if ((*endp != ' ') && (*endp != '\0')) return -EINVAL; - buffer = endp + 1; + buf = endp + 1; event = kzalloc(sizeof(*event), GFP_KERNEL); if (!event) @@ -6063,7 +6067,7 @@ static int memcg_write_event_control(struct cgroup_subsys_state *css, goto out_put_cfile; } - ret = event->register_event(memcg, event->eventfd, buffer); + ret = event->register_event(memcg, event->eventfd, buf); if (ret) goto out_put_css; @@ -6076,7 +6080,7 @@ static int memcg_write_event_control(struct cgroup_subsys_state *css, fdput(cfile); fdput(efile); - return 0; + return nbytes; out_put_css: css_put(css); @@ -6107,13 +6111,13 @@ static struct cftype mem_cgroup_files[] = { { .name = "limit_in_bytes", .private = MEMFILE_PRIVATE(_MEM, RES_LIMIT), - .write_string = mem_cgroup_write, + .write = mem_cgroup_write, .read_u64 = mem_cgroup_read_u64, }, { .name = "soft_limit_in_bytes", .private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT), - .write_string = mem_cgroup_write, + .write = mem_cgroup_write, .read_u64 = mem_cgroup_read_u64, }, { @@ -6138,7 +6142,7 @@ static struct cftype mem_cgroup_files[] = { }, { .name = "cgroup.event_control", /* XXX: for compat */ - .write_string = memcg_write_event_control, + .write = memcg_write_event_control, .flags = CFTYPE_NO_PREFIX, .mode = S_IWUGO, }, @@ -6171,7 +6175,7 @@ static struct cftype mem_cgroup_files[] = { { .name = "kmem.limit_in_bytes", .private = MEMFILE_PRIVATE(_KMEM, RES_LIMIT), - .write_string = mem_cgroup_write, + .write = mem_cgroup_write, .read_u64 = mem_cgroup_read_u64, }, { @@ -6217,7 +6221,7 @@ static struct cftype memsw_cgroup_files[] = { { .name = "memsw.limit_in_bytes", .private = MEMFILE_PRIVATE(_MEMSWAP, RES_LIMIT), - .write_string = mem_cgroup_write, + .write = mem_cgroup_write, .read_u64 = mem_cgroup_read_u64, }, { diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 3825f669147b..b990cefd906b 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -185,15 +185,15 @@ static int read_priomap(struct seq_file *sf, void *v) return 0; } -static int write_priomap(struct cgroup_subsys_state *css, struct cftype *cft, - char *buffer) +static ssize_t write_priomap(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { char devname[IFNAMSIZ + 1]; struct net_device *dev; u32 prio; int ret; - if (sscanf(buffer, "%"__stringify(IFNAMSIZ)"s %u", devname, &prio) != 2) + if (sscanf(buf, "%"__stringify(IFNAMSIZ)"s %u", devname, &prio) != 2) return -EINVAL; dev = dev_get_by_name(&init_net, devname); @@ -202,11 +202,11 @@ static int write_priomap(struct cgroup_subsys_state *css, struct cftype *cft, rtnl_lock(); - ret = netprio_set_prio(css, dev, prio); + ret = netprio_set_prio(of_css(of), dev, prio); rtnl_unlock(); dev_put(dev); - return ret; + return ret ?: nbytes; } static int update_netprio(const void *v, struct file *file, unsigned n) @@ -239,7 +239,7 @@ static struct cftype ss_files[] = { { .name = "ifpriomap", .seq_show = read_priomap, - .write_string = write_priomap, + .write = write_priomap, }, { } /* terminate */ }; diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c index d4f015ad6c84..841fd3fa937a 100644 --- a/net/ipv4/tcp_memcontrol.c +++ b/net/ipv4/tcp_memcontrol.c @@ -102,17 +102,19 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) return 0; } -static int tcp_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, - char *buffer) +static ssize_t tcp_cgroup_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { - struct mem_cgroup *memcg = mem_cgroup_from_css(css); + struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); unsigned long long val; int ret = 0; - switch (cft->private) { + buf = strstrip(buf); + + switch (of_cft(of)->private) { case RES_LIMIT: /* see memcontrol.c */ - ret = res_counter_memparse_write_strategy(buffer, &val); + ret = res_counter_memparse_write_strategy(buf, &val); if (ret) break; ret = tcp_update_limit(memcg, val); @@ -121,7 +123,7 @@ static int tcp_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, ret = -EINVAL; break; } - return ret; + return ret ?: nbytes; } static u64 tcp_read_stat(struct mem_cgroup *memcg, int type, u64 default_val) @@ -193,7 +195,7 @@ static int tcp_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event) static struct cftype tcp_files[] = { { .name = "kmem.tcp.limit_in_bytes", - .write_string = tcp_cgroup_write, + .write = tcp_cgroup_write, .read_u64 = tcp_cgroup_read, .private = RES_LIMIT, }, diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 9134dbf70d3e..7dbac4061b1c 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -767,27 +767,27 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, return rc; } -static int devcgroup_access_write(struct cgroup_subsys_state *css, - struct cftype *cft, char *buffer) +static ssize_t devcgroup_access_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { int retval; mutex_lock(&devcgroup_mutex); - retval = devcgroup_update_access(css_to_devcgroup(css), - cft->private, buffer); + retval = devcgroup_update_access(css_to_devcgroup(of_css(of)), + of_cft(of)->private, strstrip(buf)); mutex_unlock(&devcgroup_mutex); - return retval; + return retval ?: nbytes; } static struct cftype dev_cgroup_files[] = { { .name = "allow", - .write_string = devcgroup_access_write, + .write = devcgroup_access_write, .private = DEVCG_ALLOW, }, { .name = "deny", - .write_string = devcgroup_access_write, + .write = devcgroup_access_write, .private = DEVCG_DENY, }, { -- cgit v1.2.3 From 6770c64e5c8da4705d1f0973bdeb5c2bf4f3a404 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 May 2014 12:16:21 -0400 Subject: cgroup: replace cftype->trigger() with cftype->write() cftype->trigger() is pointless. It's trivial to ignore the input buffer from a regular ->write() operation. Convert all ->trigger() users to ->write() and remove ->trigger(). This patch doesn't introduce any visible behavior changes. Signed-off-by: Tejun Heo Acked-by: Li Zefan Cc: Johannes Weiner Cc: Michal Hocko --- include/linux/cgroup.h | 8 -------- kernel/cgroup.c | 5 +---- mm/hugetlb_cgroup.c | 16 ++++++++-------- mm/memcontrol.c | 34 ++++++++++++++++++---------------- net/ipv4/tcp_memcontrol.c | 15 ++++++++------- 5 files changed, 35 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index aecdc84fe128..08eb71ee600b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -499,14 +499,6 @@ struct cftype { int (*write_s64)(struct cgroup_subsys_state *css, struct cftype *cft, s64 val); - /* - * trigger() callback can be used to get some kick from the - * userspace, when the actual string written is not important - * at all. The private field can be used to determine the - * kick type for multiplexing. - */ - int (*trigger)(struct cgroup_subsys_state *css, unsigned int event); - /* * write() is the generic write callback which maps directly to * kernfs write operation and overrides all other operations. diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 2a88ce7b24b6..2f16aab03493 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1034,8 +1034,7 @@ static umode_t cgroup_file_mode(const struct cftype *cft) if (cft->read_u64 || cft->read_s64 || cft->seq_show) mode |= S_IRUGO; - if (cft->write_u64 || cft->write_s64 || cft->write || - cft->trigger) + if (cft->write_u64 || cft->write_s64 || cft->write) mode |= S_IWUSR; return mode; @@ -2750,8 +2749,6 @@ static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf, ret = kstrtoll(buf, 0, &v); if (!ret) ret = cft->write_s64(css, cft, v); - } else if (cft->trigger) { - ret = cft->trigger(css, (unsigned int)cft->private); } else { ret = -EINVAL; } diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 191de26b0148..a380681ab3cf 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -284,14 +284,14 @@ static ssize_t hugetlb_cgroup_write(struct kernfs_open_file *of, return ret ?: nbytes; } -static int hugetlb_cgroup_reset(struct cgroup_subsys_state *css, - unsigned int event) +static ssize_t hugetlb_cgroup_reset(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { int idx, name, ret = 0; - struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css); + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of)); - idx = MEMFILE_IDX(event); - name = MEMFILE_ATTR(event); + idx = MEMFILE_IDX(of_cft(of)->private); + name = MEMFILE_ATTR(of_cft(of)->private); switch (name) { case RES_MAX_USAGE: @@ -304,7 +304,7 @@ static int hugetlb_cgroup_reset(struct cgroup_subsys_state *css, ret = -EINVAL; break; } - return ret; + return ret ?: nbytes; } static char *mem_fmt(char *buf, int size, unsigned long hsize) @@ -344,14 +344,14 @@ static void __init __hugetlb_cgroup_file_init(int idx) cft = &h->cgroup_files[2]; snprintf(cft->name, MAX_CFTYPE_NAME, "%s.max_usage_in_bytes", buf); cft->private = MEMFILE_PRIVATE(idx, RES_MAX_USAGE); - cft->trigger = hugetlb_cgroup_reset; + cft->write = hugetlb_cgroup_reset; cft->read_u64 = hugetlb_cgroup_read_u64; /* Add the failcntfile */ cft = &h->cgroup_files[3]; snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf); cft->private = MEMFILE_PRIVATE(idx, RES_FAILCNT); - cft->trigger = hugetlb_cgroup_reset; + cft->write = hugetlb_cgroup_reset; cft->read_u64 = hugetlb_cgroup_read_u64; /* NULL terminate the last cft */ diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 7098a43f7447..b638a79209ee 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4887,14 +4887,15 @@ static int mem_cgroup_force_empty(struct mem_cgroup *memcg) return 0; } -static int mem_cgroup_force_empty_write(struct cgroup_subsys_state *css, - unsigned int event) +static ssize_t mem_cgroup_force_empty_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, + loff_t off) { - struct mem_cgroup *memcg = mem_cgroup_from_css(css); + struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); if (mem_cgroup_is_root(memcg)) return -EINVAL; - return mem_cgroup_force_empty(memcg); + return mem_cgroup_force_empty(memcg) ?: nbytes; } static u64 mem_cgroup_hierarchy_read(struct cgroup_subsys_state *css, @@ -5220,14 +5221,15 @@ out: *memsw_limit = min_memsw_limit; } -static int mem_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event) +static ssize_t mem_cgroup_reset(struct kernfs_open_file *of, char *buf, + size_t nbytes, loff_t off) { - struct mem_cgroup *memcg = mem_cgroup_from_css(css); + struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); int name; enum res_type type; - type = MEMFILE_TYPE(event); - name = MEMFILE_ATTR(event); + type = MEMFILE_TYPE(of_cft(of)->private); + name = MEMFILE_ATTR(of_cft(of)->private); switch (name) { case RES_MAX_USAGE: @@ -5252,7 +5254,7 @@ static int mem_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event) break; } - return 0; + return nbytes; } static u64 mem_cgroup_move_charge_read(struct cgroup_subsys_state *css, @@ -6105,7 +6107,7 @@ static struct cftype mem_cgroup_files[] = { { .name = "max_usage_in_bytes", .private = MEMFILE_PRIVATE(_MEM, RES_MAX_USAGE), - .trigger = mem_cgroup_reset, + .write = mem_cgroup_reset, .read_u64 = mem_cgroup_read_u64, }, { @@ -6123,7 +6125,7 @@ static struct cftype mem_cgroup_files[] = { { .name = "failcnt", .private = MEMFILE_PRIVATE(_MEM, RES_FAILCNT), - .trigger = mem_cgroup_reset, + .write = mem_cgroup_reset, .read_u64 = mem_cgroup_read_u64, }, { @@ -6132,7 +6134,7 @@ static struct cftype mem_cgroup_files[] = { }, { .name = "force_empty", - .trigger = mem_cgroup_force_empty_write, + .write = mem_cgroup_force_empty_write, }, { .name = "use_hierarchy", @@ -6186,13 +6188,13 @@ static struct cftype mem_cgroup_files[] = { { .name = "kmem.failcnt", .private = MEMFILE_PRIVATE(_KMEM, RES_FAILCNT), - .trigger = mem_cgroup_reset, + .write = mem_cgroup_reset, .read_u64 = mem_cgroup_read_u64, }, { .name = "kmem.max_usage_in_bytes", .private = MEMFILE_PRIVATE(_KMEM, RES_MAX_USAGE), - .trigger = mem_cgroup_reset, + .write = mem_cgroup_reset, .read_u64 = mem_cgroup_read_u64, }, #ifdef CONFIG_SLABINFO @@ -6215,7 +6217,7 @@ static struct cftype memsw_cgroup_files[] = { { .name = "memsw.max_usage_in_bytes", .private = MEMFILE_PRIVATE(_MEMSWAP, RES_MAX_USAGE), - .trigger = mem_cgroup_reset, + .write = mem_cgroup_reset, .read_u64 = mem_cgroup_read_u64, }, { @@ -6227,7 +6229,7 @@ static struct cftype memsw_cgroup_files[] = { { .name = "memsw.failcnt", .private = MEMFILE_PRIVATE(_MEMSWAP, RES_FAILCNT), - .trigger = mem_cgroup_reset, + .write = mem_cgroup_reset, .read_u64 = mem_cgroup_read_u64, }, { }, /* terminate */ diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c index 841fd3fa937a..f7a2ec3ac584 100644 --- a/net/ipv4/tcp_memcontrol.c +++ b/net/ipv4/tcp_memcontrol.c @@ -170,17 +170,18 @@ static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft) return val; } -static int tcp_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event) +static ssize_t tcp_cgroup_reset(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) { struct mem_cgroup *memcg; struct cg_proto *cg_proto; - memcg = mem_cgroup_from_css(css); + memcg = mem_cgroup_from_css(of_css(of)); cg_proto = tcp_prot.proto_cgroup(memcg); if (!cg_proto) - return 0; + return nbytes; - switch (event) { + switch (of_cft(of)->private) { case RES_MAX_USAGE: res_counter_reset_max(&cg_proto->memory_allocated); break; @@ -189,7 +190,7 @@ static int tcp_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event) break; } - return 0; + return nbytes; } static struct cftype tcp_files[] = { @@ -207,13 +208,13 @@ static struct cftype tcp_files[] = { { .name = "kmem.tcp.failcnt", .private = RES_FAILCNT, - .trigger = tcp_cgroup_reset, + .write = tcp_cgroup_reset, .read_u64 = tcp_cgroup_read, }, { .name = "kmem.tcp.max_usage_in_bytes", .private = RES_MAX_USAGE, - .trigger = tcp_cgroup_reset, + .write = tcp_cgroup_reset, .read_u64 = tcp_cgroup_read, }, { } /* terminate */ -- cgit v1.2.3 From b7fc5ad235936379fae67a9f7b50bb53487a1a3a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 May 2014 12:16:22 -0400 Subject: cgroup: remove cgroup->control_kn Now that cgroup_subtree_control_write() has access to the associated kernfs_open_file and thus the kernfs_node, there's no need to cache it in cgroup->control_kn on creation. Remove cgroup->control_kn and use @of->kn directly. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 1 - kernel/cgroup.c | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 08eb71ee600b..aa7353deaaf3 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -177,7 +177,6 @@ struct cgroup { struct cgroup *parent; /* my parent */ struct kernfs_node *kn; /* cgroup kernfs entry */ - struct kernfs_node *control_kn; /* kn for "cgroup.subtree_control" */ struct kernfs_node *populated_kn; /* kn for "cgroup.subtree_populated" */ /* diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 9a48c117ebf1..94d259bcd2b9 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2580,7 +2580,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, * active_ref protection. */ cgroup_get(cgrp); - kernfs_break_active_protection(cgrp->control_kn); + kernfs_break_active_protection(of->kn); mutex_lock(&cgroup_tree_mutex); @@ -2697,7 +2697,7 @@ out_unlock: out_unlock_tree: mutex_unlock(&cgroup_tree_mutex); out_unbreak: - kernfs_unbreak_active_protection(cgrp->control_kn); + kernfs_unbreak_active_protection(of->kn); cgroup_put(cgrp); return ret ?: nbytes; @@ -2887,9 +2887,7 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft) return ret; } - if (cft->seq_show == cgroup_subtree_control_show) - cgrp->control_kn = kn; - else if (cft->seq_show == cgroup_populated_show) + if (cft->seq_show == cgroup_populated_show) cgrp->populated_kn = kn; return 0; } -- cgit v1.2.3 From 85de721c46ba8ad9b283b2b3e307c9a3e8425042 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Dec 2013 12:38:17 -0300 Subject: [media] media: Use a better owner for the media device mdev->fops->owner is actually the owner of the very same module which implements media_device_register(), so it can't be unloaded anyway. Instead, use THIS_MODULE through a macro as does video_register_device(). Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-device.c | 7 ++++--- drivers/media/media-devnode.c | 5 +++-- include/media/media-device.h | 4 +++- include/media/media-devnode.h | 3 ++- 4 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index d5a7a135f75d..51217f00a668 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -372,7 +372,8 @@ static void media_device_release(struct media_devnode *mdev) * - dev must point to the parent device * - model must be filled with the device model name */ -int __must_check media_device_register(struct media_device *mdev) +int __must_check __media_device_register(struct media_device *mdev, + struct module *owner) { int ret; @@ -388,7 +389,7 @@ int __must_check media_device_register(struct media_device *mdev) mdev->devnode.fops = &media_device_fops; mdev->devnode.parent = mdev->dev; mdev->devnode.release = media_device_release; - ret = media_devnode_register(&mdev->devnode); + ret = media_devnode_register(&mdev->devnode, owner); if (ret < 0) return ret; @@ -400,7 +401,7 @@ int __must_check media_device_register(struct media_device *mdev) return 0; } -EXPORT_SYMBOL_GPL(media_device_register); +EXPORT_SYMBOL_GPL(__media_device_register); /** * media_device_unregister - unregister a media device diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index fb0f0469fad7..7acd19c881de 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -232,7 +232,8 @@ static const struct file_operations media_devnode_fops = { * the media_devnode structure is *not* called, so the caller is responsible for * freeing any data. */ -int __must_check media_devnode_register(struct media_devnode *mdev) +int __must_check media_devnode_register(struct media_devnode *mdev, + struct module *owner) { int minor; int ret; @@ -253,7 +254,7 @@ int __must_check media_devnode_register(struct media_devnode *mdev) /* Part 2: Initialize and register the character device */ cdev_init(&mdev->cdev, &media_devnode_fops); - mdev->cdev.owner = mdev->fops->owner; + mdev->cdev.owner = owner; ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); if (ret < 0) { diff --git a/include/media/media-device.h b/include/media/media-device.h index 12155a9596c4..6e6db78f1ee2 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -87,7 +87,9 @@ struct media_device { /* media_devnode to media_device */ #define to_media_device(node) container_of(node, struct media_device, devnode) -int __must_check media_device_register(struct media_device *mdev); +int __must_check __media_device_register(struct media_device *mdev, + struct module *owner); +#define media_device_register(mdev) __media_device_register(mdev, THIS_MODULE) void media_device_unregister(struct media_device *mdev); int __must_check media_device_register_entity(struct media_device *mdev, diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index 3446af279fca..0dc7060f9625 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -82,7 +82,8 @@ struct media_devnode { /* dev to media_devnode */ #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) -int __must_check media_devnode_register(struct media_devnode *mdev); +int __must_check media_devnode_register(struct media_devnode *mdev, + struct module *owner); void media_devnode_unregister(struct media_devnode *mdev); static inline struct media_devnode *media_devnode_data(struct file *filp) -- cgit v1.2.3 From b2a06aecb24329e16edc3108b8192d65ace8da75 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Dec 2013 09:36:46 -0300 Subject: [media] v4l: Only get module if it's different than the driver for v4l2_dev When the sub-device is registered, increment the use count of the sub-device owner only if it's different from the owner of the driver for the media device. This avoids increasing the use count by the module itself and thus making it possible to unload it when it's not in use. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-device.c | 18 +++++++++++++++--- include/media/v4l2-subdev.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 02d1b6327117..015f92aab44a 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -158,7 +158,17 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, /* Warn if we apparently re-register a subdev */ WARN_ON(sd->v4l2_dev != NULL); - if (!try_module_get(sd->owner)) + /* + * The reason to acquire the module here is to avoid unloading + * a module of sub-device which is registered to a media + * device. To make it possible to unload modules for media + * devices that also register sub-devices, do not + * try_module_get() such sub-device owners. + */ + sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver && + sd->owner == v4l2_dev->dev->driver->owner; + + if (!sd->owner_v4l2_dev && !try_module_get(sd->owner)) return -ENODEV; sd->v4l2_dev = v4l2_dev; @@ -192,7 +202,8 @@ error_unregister: if (sd->internal_ops && sd->internal_ops->unregistered) sd->internal_ops->unregistered(sd); error_module: - module_put(sd->owner); + if (!sd->owner_v4l2_dev) + module_put(sd->owner); sd->v4l2_dev = NULL; return err; } @@ -280,6 +291,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) } #endif video_unregister_device(sd->devnode); - module_put(sd->owner); + if (!sd->owner_v4l2_dev) + module_put(sd->owner); } EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index ee1cb2d354a8..ee0ae769ca94 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -584,6 +584,7 @@ struct v4l2_subdev { #endif struct list_head list; struct module *owner; + bool owner_v4l2_dev; u32 flags; struct v4l2_device *v4l2_dev; const struct v4l2_subdev_ops *ops; -- cgit v1.2.3 From 174d6a39b07f51212c23a8e30a0440598d18392c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 18 Dec 2013 08:40:28 -0300 Subject: [media] v4l: V4L2_MBUS_FRAME_DESC_FL_BLOB is about 1D DMA V4L2_MBUS_FRAME_DESC_FL_BLOB intends to say the receiver must use 1D DMA to receive the image, as the format does not have line offsets. This typically includes all compressed formats. Signed-off-by: Sakari Ailus Cc: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index ee0ae769ca94..7653194e821a 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -234,15 +234,18 @@ struct v4l2_subdev_audio_ops { /* Indicates the @length field specifies maximum data length. */ #define V4L2_MBUS_FRAME_DESC_FL_LEN_MAX (1U << 0) -/* Indicates user defined data format, i.e. non standard frame format. */ +/* + * Indicates that the format does not have line offsets, i.e. the + * receiver should use 1D DMA. + */ #define V4L2_MBUS_FRAME_DESC_FL_BLOB (1U << 1) /** * struct v4l2_mbus_frame_desc_entry - media bus frame description structure * @flags: V4L2_MBUS_FRAME_DESC_FL_* flags * @pixelcode: media bus pixel code, valid if FRAME_DESC_FL_BLOB is not set - * @length: number of octets per frame, valid for compressed or unspecified - * formats + * @length: number of octets per frame, valid if V4L2_MBUS_FRAME_DESC_FL_BLOB + * is set */ struct v4l2_mbus_frame_desc_entry { u16 flags; -- cgit v1.2.3 From 1ab2b3c0af10a81ad687258c00a1cec93c5c075b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 20 Dec 2013 10:20:26 -0300 Subject: [media] v4l: Remove documentation for nonexistend input field in v4l2_buffer The input field in struct v4l2_buffer no longer exists but has been replaced by a reserved field. Remove the field documentation. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index ea468ee8fe21..db4aebd8baba 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -649,7 +649,6 @@ struct v4l2_plane { * @length: size in bytes of the buffer (NOT its payload) for single-plane * buffers (when type != *_MPLANE); number of elements in the * planes array for multi-plane buffers - * @input: input number from which the video data has has been captured * * Contains data exchanged by application and driver using one of the Streaming * I/O methods. -- cgit v1.2.3 From aa9ba84b088a42f0ce522115675298e763815663 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 12:32:46 -0300 Subject: [media] v4l: Add UYVY10_2X10 and VYUY10_2X10 media bus pixel codes Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 128 +++++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 4 +- 2 files changed, 131 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 7331ce116f4c..6fb58de9466a 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -1898,6 +1898,134 @@ y1 y0 + + V4L2_MBUS_FMT_UYVY10_2X10 + 0x2018 + + &dash-ent-22; + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-22; + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-22; + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-22; + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY10_2X10 + 0x2019 + + &dash-ent-22; + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-22; + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-22; + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-22; + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + V4L2_MBUS_FMT_YUYV10_2X10 0x200b diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index b5c3aab6e82c..20a99b11c65c 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x2018 */ + /* YUV (including grey) - next is 0x201a */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -64,6 +64,8 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YUYV8_2X8 = 0x2008, V4L2_MBUS_FMT_YVYU8_2X8 = 0x2009, V4L2_MBUS_FMT_Y10_1X10 = 0x200a, + V4L2_MBUS_FMT_UYVY10_2X10 = 0x2018, + V4L2_MBUS_FMT_VYUY10_2X10 = 0x2019, V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b, V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c, V4L2_MBUS_FMT_Y12_1X12 = 0x2013, -- cgit v1.2.3 From a678a198fdeab3368f1a3171453a7d43837e587b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 12:32:46 -0300 Subject: [media] v4l: Add UYVY10_1X20 and VYUY10_1X20 media bus pixel codes Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 104 +++++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 4 +- 2 files changed, 107 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 6fb58de9466a..e3cbbb4fbd53 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -2436,6 +2436,110 @@ v1 v0 + + V4L2_MBUS_FMT_UYVY10_1X20 + 0x201a + + &dash-ent-12; + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-12; + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY10_1X20 + 0x201b + + &dash-ent-12; + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-12; + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + V4L2_MBUS_FMT_YUYV10_1X20 0x200d diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 20a99b11c65c..43707b2c17c1 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x201a */ + /* YUV (including grey) - next is 0x201c */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -74,6 +74,8 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011, V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012, V4L2_MBUS_FMT_YDYUYDYV8_1X16 = 0x2014, + V4L2_MBUS_FMT_UYVY10_1X20 = 0x201a, + V4L2_MBUS_FMT_VYUY10_1X20 = 0x201b, V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, -- cgit v1.2.3 From 39d39af6093038491137decdef855c703a09f983 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 12:32:46 -0300 Subject: [media] v4l: Add 12-bit YUV 4:2:0 media bus pixel codes Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 288 +++++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 6 +- 2 files changed, 293 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index e3cbbb4fbd53..a0fa7e0759c8 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -2718,6 +2718,294 @@ v1 v0 + + V4L2_MBUS_FMT_UYVY12_2X12 + 0x201c + + &dash-ent-20; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY12_2X12 + 0x201d + + &dash-ent-20; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_YUYV12_2X12 + 0x201e + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU12_2X12 + 0x201f + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 43707b2c17c1..70a732b7e47b 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x201c */ + /* YUV (including grey) - next is 0x2020 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -80,6 +80,10 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, V4L2_MBUS_FMT_AYUV8_1X32 = 0x2017, + V4L2_MBUS_FMT_UYVY12_2X12 = 0x201c, + V4L2_MBUS_FMT_VYUY12_2X12 = 0x201d, + V4L2_MBUS_FMT_YUYV12_2X12 = 0x201e, + V4L2_MBUS_FMT_YVYU12_2X12 = 0x201f, /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, -- cgit v1.2.3 From 7ffd58ddab76969019098e97d687711451d32a3d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 12:32:46 -0300 Subject: [media] v4l: Add 12-bit YUV 4:2:2 media bus pixel codes Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 240 +++++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 6 +- 2 files changed, 245 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index a0fa7e0759c8..b2d5a0363cba 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -3006,6 +3006,246 @@ u1 u0 + + V4L2_MBUS_FMT_UYVY12_1X24 + 0x2020 + + &dash-ent-8; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-8; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY12_1X24 + 0x2021 + + &dash-ent-8; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-8; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_YUYV12_1X24 + 0x2022 + + &dash-ent-8; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-8; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU12_1X24 + 0x2023 + + &dash-ent-8; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-8; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 70a732b7e47b..1445e858854f 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x2020 */ + /* YUV (including grey) - next is 0x2024 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -84,6 +84,10 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_VYUY12_2X12 = 0x201d, V4L2_MBUS_FMT_YUYV12_2X12 = 0x201e, V4L2_MBUS_FMT_YVYU12_2X12 = 0x201f, + V4L2_MBUS_FMT_UYVY12_1X24 = 0x2020, + V4L2_MBUS_FMT_VYUY12_1X24 = 0x2021, + V4L2_MBUS_FMT_YUYV12_1X24 = 0x2022, + V4L2_MBUS_FMT_YVYU12_1X24 = 0x2023, /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, -- cgit v1.2.3 From bff58ea4f43d9b4a9fd6fb05fabc8f50f68131f5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 8 May 2014 17:44:49 -0700 Subject: ASoC: rsnd: add DVC support This patch adds DVC (Digital Volume Controller) support which is member of CMD unit. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 10 ++ sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/adg.c | 18 +++ sound/soc/sh/rcar/core.c | 20 ++++ sound/soc/sh/rcar/dvc.c | 273 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/gen.c | 11 ++ sound/soc/sh/rcar/rsnd.h | 40 +++++++ sound/soc/sh/rcar/src.c | 4 +- 8 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 sound/soc/sh/rcar/dvc.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index a03268ec59c3..f4a706f82cb7 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -56,9 +56,17 @@ struct rsnd_src_platform_info { int dma_id; /* for Gen2 SCU */ }; +/* + * flags + */ +struct rsnd_dvc_platform_info { + u32 flags; +}; + struct rsnd_dai_path_info { struct rsnd_ssi_platform_info *ssi; struct rsnd_src_platform_info *src; + struct rsnd_dvc_platform_info *dvc; }; struct rsnd_dai_platform_info { @@ -83,6 +91,8 @@ struct rcar_snd_info { int ssi_info_nr; struct rsnd_src_platform_info *src_info; int src_info_nr; + struct rsnd_dvc_platform_info *dvc_info; + int dvc_info_nr; struct rsnd_dai_platform_info *dai_info; int dai_info_nr; int (*start)(int id); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 7d0051ced838..9ac536429800 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o +snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o dvc.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 41556b2ef21e..fc41a0e8b09f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -57,6 +57,24 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) return (0x6 + ws) << 8; } +int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_dai *rdai, + struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + int shift = (id % 2) ? 16 : 0; + u32 mask, val; + + val = rsnd_adg_ssi_ws_timing_gen2(io); + + val = val << shift; + mask = 0xffff << shift; + + rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val); + + return 0; +} + static int rsnd_adg_set_src_timsel_gen2(struct rsnd_dai *rdai, struct rsnd_mod *mod, struct rsnd_dai_stream *io, diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 378985b028ef..1f6981a56354 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -645,6 +645,11 @@ static int rsnd_path_init(struct rsnd_priv *priv, if (ret < 0) return ret; + /* DVC */ + ret = rsnd_path_parse(priv, io, dvc); + if (ret < 0) + return ret; + return ret; } @@ -869,6 +874,20 @@ static struct snd_pcm_ops rsnd_pcm_ops = { static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) { + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct rsnd_dai *rdai; + int i, ret; + + for_each_rsnd_dai(rdai, priv, i) { + ret = rsnd_dai_call(pcm_new, &rdai->playback, rdai, rtd); + if (ret) + return ret; + + ret = rsnd_dai_call(pcm_new, &rdai->capture, rdai, rtd); + if (ret) + return ret; + } + return snd_pcm_lib_preallocate_pages_for_all( rtd->pcm, SNDRV_DMA_TYPE_DEV, @@ -908,6 +927,7 @@ static int rsnd_probe(struct platform_device *pdev) rsnd_gen_probe, rsnd_ssi_probe, rsnd_src_probe, + rsnd_dvc_probe, rsnd_adg_probe, rsnd_dai_probe, }; diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c new file mode 100644 index 000000000000..74769b1be005 --- /dev/null +++ b/sound/soc/sh/rcar/dvc.c @@ -0,0 +1,273 @@ +/* + * Renesas R-Car DVC support + * + * Copyright (C) 2014 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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 "rsnd.h" + +#define RSND_DVC_NAME_SIZE 16 +#define RSND_DVC_VOLUME_MAX 100 +#define RSND_DVC_VOLUME_NUM 2 +struct rsnd_dvc { + struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; + struct clk *clk; + long volume[RSND_DVC_VOLUME_NUM]; +}; + +#define rsnd_mod_to_dvc(_mod) \ + container_of((_mod), struct rsnd_dvc, mod) + +#define for_each_rsnd_dvc(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_dvc_nr(priv)) && \ + ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ + i++) + +static void rsnd_dvc_volume_update(struct rsnd_mod *mod) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + u32 max = (0x00800000 - 1); + u32 vol[RSND_DVC_VOLUME_NUM]; + int i; + + for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) + vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i]; + + rsnd_mod_write(mod, DVC_VOL0R, vol[0]); + rsnd_mod_write(mod, DVC_VOL1R, vol[1]); +} + +static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, + struct rsnd_dai *rdai) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(dvc_mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(dvc_mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(dvc_mod); + struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct device *dev = rsnd_priv_to_dev(priv); + int dvc_id = rsnd_mod_id(dvc_mod); + int src_id = rsnd_mod_id(src_mod); + u32 route[] = { + [0] = 0x30000, + [1] = 0x30001, + [2] = 0x40000, + [3] = 0x10000, + [4] = 0x20000, + [5] = 0x40100 + }; + + if (src_id >= ARRAY_SIZE(route)) { + dev_err(dev, "DVC%d isn't connected to SRC%d\n", dvc_id, src_id); + return -EINVAL; + } + + clk_prepare_enable(dvc->clk); + + /* + * fixme + * it doesn't support CTU/MIX + */ + rsnd_mod_write(dvc_mod, CMD_ROUTE_SLCT, route[src_id]); + + rsnd_mod_write(dvc_mod, DVC_SWRSR, 0); + rsnd_mod_write(dvc_mod, DVC_SWRSR, 1); + + rsnd_mod_write(dvc_mod, DVC_DVUIR, 1); + + rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod)); + + /* enable Volume */ + rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x100); + + /* ch0/ch1 Volume */ + rsnd_dvc_volume_update(dvc_mod); + + rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); + + rsnd_mod_write(dvc_mod, DVC_DVUER, 1); + + rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io); + + return 0; +} + +static int rsnd_dvc_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + + clk_disable_unprepare(dvc->clk); + + return 0; +} + +static int rsnd_dvc_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ + rsnd_mod_write(mod, CMD_CTRL, 0x10); + + return 0; +} + +static int rsnd_dvc_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai) +{ + rsnd_mod_write(mod, CMD_CTRL, 0); + + return 0; +} + +static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = RSND_DVC_VOLUME_NUM; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = RSND_DVC_VOLUME_MAX; + + return 0; +} + +static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + int i; + + for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) + ucontrol->value.integer.value[i] = dvc->volume[i]; + + return 0; +} + +static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *ucontrol) +{ + struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + int i, change = 0; + + for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) { + if (ucontrol->value.integer.value[i] < 0 || + ucontrol->value.integer.value[i] > RSND_DVC_VOLUME_MAX) + return -EINVAL; + + change |= (ucontrol->value.integer.value[i] != dvc->volume[i]); + } + + if (change) { + for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) + dvc->volume[i] = ucontrol->value.integer.value[i]; + + rsnd_dvc_volume_update(mod); + } + + return change; +} + +static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_card *card = rtd->card->snd_card; + struct snd_kcontrol *kctrl; + static struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Playback Volume", + .info = rsnd_dvc_volume_info, + .get = rsnd_dvc_volume_get, + .put = rsnd_dvc_volume_put, + }; + int ret; + + if (!rsnd_dai_is_play(rdai, io)) { + dev_err(dev, "DVC%d is connected to Capture DAI\n", + rsnd_mod_id(mod)); + return -EINVAL; + } + + kctrl = snd_ctl_new1(&knew, mod); + if (!kctrl) + return -ENOMEM; + + ret = snd_ctl_add(card, kctrl); + if (ret < 0) + return ret; + + return 0; +} + +static struct rsnd_mod_ops rsnd_dvc_ops = { + .name = "dvc (gen2)", + .init = rsnd_dvc_init, + .quit = rsnd_dvc_quit, + .start = rsnd_dvc_start, + .stop = rsnd_dvc_stop, + .pcm_new = rsnd_dvc_pcm_new, +}; + +struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) + id = 0; + + return &((struct rsnd_dvc *)(priv->dvc) + id)->mod; +} + +int rsnd_dvc_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dvc *dvc; + struct clk *clk; + char name[RSND_DVC_NAME_SIZE]; + int i, nr; + + nr = info->dvc_info_nr; + if (!nr) + return 0; + + /* This driver doesn't support Gen1 at this point */ + if (rsnd_is_gen1(priv)) { + dev_warn(dev, "CMD is not supported on Gen1\n"); + return -EINVAL; + } + + dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); + if (!dvc) { + dev_err(dev, "CMD allocate failed\n"); + return -ENOMEM; + } + + priv->dvc_nr = nr; + priv->dvc = dvc; + + for_each_rsnd_dvc(dvc, priv, i) { + snprintf(name, RSND_DVC_NAME_SIZE, "dvc.%d", i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + dvc->info = &info->dvc_info[i]; + dvc->clk = clk; + + rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops, RSND_MOD_DVC, i); + + dev_dbg(dev, "CMD%d probed\n", i); + } + + return 0; +} diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 50a1ef3eb1c6..a135d4c9d080 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -181,6 +181,8 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) RSND_GEN2_M_REG(gen, SCU, SRC_BUSIF_MODE, 0x0, 0x20), RSND_GEN2_M_REG(gen, SCU, SRC_ROUTE_MODE0,0xc, 0x20), RSND_GEN2_M_REG(gen, SCU, SRC_CTRL, 0x10, 0x20), + RSND_GEN2_M_REG(gen, SCU, CMD_ROUTE_SLCT, 0x18c, 0x20), + RSND_GEN2_M_REG(gen, SCU, CMD_CTRL, 0x190, 0x20), RSND_GEN2_M_REG(gen, SCU, SRC_SWRSR, 0x200, 0x40), RSND_GEN2_M_REG(gen, SCU, SRC_SRCIR, 0x204, 0x40), RSND_GEN2_M_REG(gen, SCU, SRC_ADINR, 0x214, 0x40), @@ -189,6 +191,14 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) RSND_GEN2_M_REG(gen, SCU, SRC_SRCCR, 0x224, 0x40), RSND_GEN2_M_REG(gen, SCU, SRC_BSDSR, 0x22c, 0x40), RSND_GEN2_M_REG(gen, SCU, SRC_BSISR, 0x238, 0x40), + RSND_GEN2_M_REG(gen, SCU, DVC_SWRSR, 0xe00, 0x100), + RSND_GEN2_M_REG(gen, SCU, DVC_DVUIR, 0xe04, 0x100), + RSND_GEN2_M_REG(gen, SCU, DVC_ADINR, 0xe08, 0x100), + RSND_GEN2_M_REG(gen, SCU, DVC_DVUCR, 0xe10, 0x100), + RSND_GEN2_M_REG(gen, SCU, DVC_ZCMCR, 0xe14, 0x100), + RSND_GEN2_M_REG(gen, SCU, DVC_VOL0R, 0xe28, 0x100), + RSND_GEN2_M_REG(gen, SCU, DVC_VOL1R, 0xe2c, 0x100), + RSND_GEN2_M_REG(gen, SCU, DVC_DVUER, 0xe48, 0x100), RSND_GEN2_S_REG(gen, ADG, BRRA, 0x00), RSND_GEN2_S_REG(gen, ADG, BRRB, 0x04), @@ -207,6 +217,7 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL2, 0x50), RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL3, 0x54), RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL4, 0x58), + RSND_GEN2_S_REG(gen, ADG, CMDOUT_TIMSEL, 0x5c), RSND_GEN2_M_REG(gen, SSI, SSICR, 0x00, 0x40), RSND_GEN2_M_REG(gen, SSI, SSISR, 0x04, 0x40), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 6156ceebd58b..5aa790170b01 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -44,6 +44,15 @@ enum rsnd_reg { RSND_REG_SRC_IFSCR, RSND_REG_SRC_IFSVR, RSND_REG_SRC_SRCCR, + RSND_REG_CMD_ROUTE_SLCT, + RSND_REG_DVC_SWRSR, + RSND_REG_DVC_DVUIR, + RSND_REG_DVC_ADINR, + RSND_REG_DVC_DVUCR, + RSND_REG_DVC_ZCMCR, + RSND_REG_DVC_VOL0R, + RSND_REG_DVC_VOL1R, + RSND_REG_DVC_DVUER, /* ADG */ RSND_REG_BRRA, @@ -79,6 +88,8 @@ enum rsnd_reg { RSND_REG_SHARE17, RSND_REG_SHARE18, RSND_REG_SHARE19, + RSND_REG_SHARE20, + RSND_REG_SHARE21, RSND_REG_MAX, }; @@ -114,6 +125,8 @@ enum rsnd_reg { #define RSND_REG_SRCOUT_TIMSEL3 RSND_REG_SHARE17 #define RSND_REG_SRCOUT_TIMSEL4 RSND_REG_SHARE18 #define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19 +#define RSND_REG_CMD_CTRL RSND_REG_SHARE20 +#define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21 struct rsnd_of_data; struct rsnd_priv; @@ -166,6 +179,7 @@ void rsnd_dma_quit(struct rsnd_priv *priv, enum rsnd_mod_type { RSND_MOD_SRC = 0, RSND_MOD_SSI, + RSND_MOD_DVC, RSND_MOD_MAX, }; @@ -183,6 +197,9 @@ struct rsnd_mod_ops { struct rsnd_dai *rdai); int (*stop)(struct rsnd_mod *mod, struct rsnd_dai *rdai); + int (*pcm_new)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct snd_soc_pcm_runtime *rtd); }; struct rsnd_dai_stream; @@ -223,6 +240,7 @@ struct rsnd_dai_stream { }; #define rsnd_io_to_mod_ssi(io) ((io)->mod[RSND_MOD_SSI]) #define rsnd_io_to_mod_src(io) ((io)->mod[RSND_MOD_SRC]) +#define rsnd_io_to_mod_dvc(io) ((io)->mod[RSND_MOD_DVC]) struct rsnd_dai { char name[RSND_DAI_NAME_SIZE]; @@ -286,6 +304,9 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_dai *rdai, + struct rsnd_mod *mod, + struct rsnd_dai_stream *io); /* * R-Car sound priv @@ -322,6 +343,12 @@ struct rsnd_priv { void *ssi; int ssi_nr; + /* + * below value will be filled on rsnd_dvc_probe() + */ + void *dvc; + int dvc_nr; + /* * below value will be filled on rsnd_dai_probe() */ @@ -374,4 +401,17 @@ int rsnd_ssi_probe(struct platform_device *pdev, struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); +/* + * R-Car DVC + */ +int rsnd_dvc_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv); +void rsnd_dvc_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); + +#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) + + #endif diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 035eda2de239..7da87cde0bc3 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -536,12 +536,14 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, static int rsnd_src_start_gen2(struct rsnd_mod *mod, struct rsnd_dai *rdai) { + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); rsnd_mod_write(mod, SSI_CTRL, 0x1); - rsnd_mod_write(mod, SRC_CTRL, 0x11); + rsnd_mod_write(mod, SRC_CTRL, val); return rsnd_src_start(mod, rdai); } -- cgit v1.2.3 From ad0dc7f94dbf417b1c7d42e1f0b250f045b27f8f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 19 Feb 2014 10:51:42 -0800 Subject: rcutorture: Add forward-progress checking for writer The rcutorture output currently does not distinguish between stalls in the RCU implementation and stalls in the rcu_torture_writer() kthreads. This commit therefore adds some diagnostics to help distinguish between these two conditions, at least for the non-SRCU implementations. (SRCU does not provide evidence of update-side forward progress by design.) Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 19 +++++++++++++++++++ kernel/rcu/rcutorture.c | 37 +++++++++++++++++++++++++++++++++++++ kernel/rcu/tree.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 00a7fd61b3c6..82973738125b 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -51,7 +51,17 @@ extern int rcu_expedited; /* for sysctl */ extern int rcutorture_runnable; /* for sysctl */ #endif /* #ifdef CONFIG_RCU_TORTURE_TEST */ +enum rcutorture_type { + RCU_FLAVOR, + RCU_BH_FLAVOR, + RCU_SCHED_FLAVOR, + SRCU_FLAVOR, + INVALID_RCU_FLAVOR +}; + #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) +void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags, + unsigned long *gpnum, unsigned long *completed); void rcutorture_record_test_transition(void); void rcutorture_record_progress(unsigned long vernum); void do_trace_rcu_torture_read(const char *rcutorturename, @@ -60,6 +70,15 @@ void do_trace_rcu_torture_read(const char *rcutorturename, unsigned long c_old, unsigned long c); #else +static inline void rcutorture_get_gp_data(enum rcutorture_type test_type, + int *flags, + unsigned long *gpnum, + unsigned long *completed) +{ + *flags = 0; + *gpnum = 0; + *completed = 0; +} static inline void rcutorture_record_test_transition(void) { } diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index bd30bc61bc05..0d739e3797e3 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -138,6 +138,15 @@ static long n_barrier_attempts; static long n_barrier_successes; static struct list_head rcu_torture_removed; +static int rcu_torture_writer_state; +#define RTWS_FIXED_DELAY 0 +#define RTWS_DELAY 1 +#define RTWS_REPLACE 2 +#define RTWS_DEF_FREE 3 +#define RTWS_EXP_SYNC 4 +#define RTWS_STUTTER 5 +#define RTWS_STOPPING 6 + #if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) #define RCUTORTURE_RUNNABLE_INIT 1 #else @@ -214,6 +223,7 @@ rcu_torture_free(struct rcu_torture *p) */ struct rcu_torture_ops { + int ttype; void (*init)(void); int (*readlock)(void); void (*read_delay)(struct torture_random_state *rrsp); @@ -312,6 +322,7 @@ static void rcu_sync_torture_init(void) } static struct rcu_torture_ops rcu_ops = { + .ttype = RCU_FLAVOR, .init = rcu_sync_torture_init, .readlock = rcu_torture_read_lock, .read_delay = rcu_read_delay, @@ -355,6 +366,7 @@ static void rcu_bh_torture_deferred_free(struct rcu_torture *p) } static struct rcu_torture_ops rcu_bh_ops = { + .ttype = RCU_BH_FLAVOR, .init = rcu_sync_torture_init, .readlock = rcu_bh_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ @@ -397,6 +409,7 @@ call_rcu_busted(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) } static struct rcu_torture_ops rcu_busted_ops = { + .ttype = INVALID_RCU_FLAVOR, .init = rcu_sync_torture_init, .readlock = rcu_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ @@ -492,6 +505,7 @@ static void srcu_torture_synchronize_expedited(void) } static struct rcu_torture_ops srcu_ops = { + .ttype = SRCU_FLAVOR, .init = rcu_sync_torture_init, .readlock = srcu_torture_read_lock, .read_delay = srcu_read_delay, @@ -527,6 +541,7 @@ static void rcu_sched_torture_deferred_free(struct rcu_torture *p) } static struct rcu_torture_ops sched_ops = { + .ttype = RCU_SCHED_FLAVOR, .init = rcu_sync_torture_init, .readlock = sched_torture_read_lock, .read_delay = rcu_read_delay, /* just reuse rcu's version. */ @@ -699,12 +714,15 @@ rcu_torture_writer(void *arg) set_user_nice(current, MAX_NICE); do { + rcu_torture_writer_state = RTWS_FIXED_DELAY; schedule_timeout_uninterruptible(1); rp = rcu_torture_alloc(); if (rp == NULL) continue; rp->rtort_pipe_count = 0; + rcu_torture_writer_state = RTWS_DELAY; udelay(torture_random(&rand) & 0x3ff); + rcu_torture_writer_state = RTWS_REPLACE; old_rp = rcu_dereference_check(rcu_torture_current, current == writer_task); rp->rtort_mbtest = 1; @@ -721,8 +739,10 @@ rcu_torture_writer(void *arg) else exp = gp_exp; if (!exp) { + rcu_torture_writer_state = RTWS_DEF_FREE; cur_ops->deferred_free(old_rp); } else { + rcu_torture_writer_state = RTWS_EXP_SYNC; cur_ops->exp_sync(); list_add(&old_rp->rtort_free, &rcu_torture_removed); @@ -743,8 +763,10 @@ rcu_torture_writer(void *arg) } } rcutorture_record_progress(++rcu_torture_current_version); + rcu_torture_writer_state = RTWS_STUTTER; stutter_wait("rcu_torture_writer"); } while (!torture_must_stop()); + rcu_torture_writer_state = RTWS_STOPPING; torture_kthread_stopping("rcu_torture_writer"); return 0; } @@ -937,6 +959,7 @@ rcu_torture_printk(char *page) int i; long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; + static unsigned long rtcv_snap = ULONG_MAX; for_each_possible_cpu(cpu) { for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { @@ -997,6 +1020,20 @@ rcu_torture_printk(char *page) page += sprintf(page, "\n"); if (cur_ops->stats) cur_ops->stats(page); + if (rtcv_snap == rcu_torture_current_version && + rcu_torture_current != NULL) { + int __maybe_unused flags; + unsigned long __maybe_unused gpnum; + unsigned long __maybe_unused completed; + + rcutorture_get_gp_data(cur_ops->ttype, + &flags, &gpnum, &completed); + page += sprintf(page, + "??? Writer stall state %d g%lu c%lu f%#x\n", + rcu_torture_writer_state, + gpnum, completed, flags); + } + rtcv_snap = rcu_torture_current_version; } /* diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 0c47e300210a..3d15b5a82ae8 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -293,6 +293,39 @@ void rcutorture_record_test_transition(void) } EXPORT_SYMBOL_GPL(rcutorture_record_test_transition); +/* + * Send along grace-period-related data for rcutorture diagnostics. + */ +void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags, + unsigned long *gpnum, unsigned long *completed) +{ + struct rcu_state *rsp = NULL; + + switch (test_type) { + case RCU_FLAVOR: + rsp = rcu_state; + break; + case RCU_BH_FLAVOR: + rsp = &rcu_bh_state; + break; + case RCU_SCHED_FLAVOR: + rsp = &rcu_sched_state; + break; + default: + break; + } + if (rsp != NULL) { + *flags = ACCESS_ONCE(rsp->gp_flags); + *gpnum = ACCESS_ONCE(rsp->gpnum); + *completed = ACCESS_ONCE(rsp->completed); + return; + } + *flags = 0; + *gpnum = 0; + *completed = 0; +} +EXPORT_SYMBOL_GPL(rcutorture_get_gp_data); + /* * Record the number of writer passes through the current rcutorture test. * This is also used to correlate debugfs tracing stats with the rcutorture -- cgit v1.2.3 From d9c6866be8a145e32da616d8dcbae806032d75b5 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 7 May 2014 15:23:56 -0500 Subject: of: kill off of_can_translate_address of_can_translate_address only checks some conditions for address translation, but does not check other conditions like having range properties. The checks it does do are redundant with __of_address_translate. The only difference is printing a message or not. Since we only have a single caller that does the full translation anyway, just remove of_can_translate_address and quiet the error message. Cc: Grant Likely Signed-off-by: Rob Herring Tested-by: Frank Rowand Reviewed-by: Frank Rowand --- drivers/of/address.c | 22 +--------------------- drivers/of/platform.c | 5 ++--- include/linux/of_address.h | 1 - 3 files changed, 3 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/of/address.c b/drivers/of/address.c index cb4242a69cd5..95351b2a112c 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -498,8 +498,7 @@ static u64 __of_translate_address(struct device_node *dev, /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { - printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - of_node_full_name(dev)); + pr_debug("OF: Bad cell count for %s\n", of_node_full_name(dev)); goto bail; } memcpy(addr, in_addr, na * 4); @@ -564,25 +563,6 @@ u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) } EXPORT_SYMBOL(of_translate_dma_address); -bool of_can_translate_address(struct device_node *dev) -{ - struct device_node *parent; - struct of_bus *bus; - int na, ns; - - parent = of_get_parent(dev); - if (parent == NULL) - return false; - - bus = of_match_bus(parent); - bus->count_cells(dev, &na, &ns); - - of_node_put(parent); - - return OF_CHECK_COUNTS(na, ns); -} -EXPORT_SYMBOL(of_can_translate_address); - const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags) { diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 0602eb5b1be2..d0009b3614af 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -140,9 +140,8 @@ struct platform_device *of_device_alloc(struct device_node *np, return NULL; /* count the io and irq resources */ - if (of_can_translate_address(np)) - while (of_address_to_resource(np, num_reg, &temp_res) == 0) - num_reg++; + while (of_address_to_resource(np, num_reg, &temp_res) == 0) + num_reg++; num_irq = of_irq_count(np); /* Populate the resource table */ diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 5f6ed6b182b8..906ca7681756 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -40,7 +40,6 @@ extern u64 of_translate_dma_address(struct device_node *dev, #ifdef CONFIG_OF_ADDRESS extern u64 of_translate_address(struct device_node *np, const __be32 *addr); -extern bool of_can_translate_address(struct device_node *dev); extern int of_address_to_resource(struct device_node *dev, int index, struct resource *r); extern struct device_node *of_find_matching_node_by_address( -- cgit v1.2.3 From 0d2602ca30e410e84e8bdf05c84ed5688e0a5a44 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 13 May 2014 15:10:52 -0600 Subject: blk-mq: improve support for shared tags maps This adds support for active queue tracking, meaning that the blk-mq tagging maintains a count of active users of a tag set. This allows us to maintain a notion of fairness between users, so that we can distribute the tag depth evenly without starving some users while allowing others to try unfair deep queues. If sharing of a tag set is detected, each hardware queue will track the depth of its own queue. And if this exceeds the total depth divided by the number of active queues, the user is actively throttled down. The active queue count is done lazily to avoid bouncing that data between submitter and completer. Each hardware queue gets marked active when it allocates its first tag, and gets marked inactive when 1) the last tag is cleared, and 2) the queue timeout grace period has passed. Signed-off-by: Jens Axboe --- block/blk-mq-sysfs.c | 10 +++++ block/blk-mq-tag.c | 112 +++++++++++++++++++++++++++++++++++++++------- block/blk-mq-tag.h | 27 +++++++++-- block/blk-mq.c | 85 ++++++++++++++++++++++++++++++++--- block/blk-timeout.c | 13 +++++- block/blk.h | 4 ++ include/linux/blk-mq.h | 7 +++ include/linux/blk_types.h | 2 + include/linux/blkdev.h | 3 ++ 9 files changed, 236 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/block/blk-mq-sysfs.c b/block/blk-mq-sysfs.c index 8145b5b25b4b..99a60a829e69 100644 --- a/block/blk-mq-sysfs.c +++ b/block/blk-mq-sysfs.c @@ -208,6 +208,11 @@ static ssize_t blk_mq_hw_sysfs_tags_show(struct blk_mq_hw_ctx *hctx, char *page) return blk_mq_tag_sysfs_show(hctx->tags, page); } +static ssize_t blk_mq_hw_sysfs_active_show(struct blk_mq_hw_ctx *hctx, char *page) +{ + return sprintf(page, "%u\n", atomic_read(&hctx->nr_active)); +} + static ssize_t blk_mq_hw_sysfs_cpus_show(struct blk_mq_hw_ctx *hctx, char *page) { unsigned int i, first = 1; @@ -267,6 +272,10 @@ static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_dispatched = { .attr = {.name = "dispatched", .mode = S_IRUGO }, .show = blk_mq_hw_sysfs_dispatched_show, }; +static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_active = { + .attr = {.name = "active", .mode = S_IRUGO }, + .show = blk_mq_hw_sysfs_active_show, +}; static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_pending = { .attr = {.name = "pending", .mode = S_IRUGO }, .show = blk_mq_hw_sysfs_rq_list_show, @@ -287,6 +296,7 @@ static struct attribute *default_hw_ctx_attrs[] = { &blk_mq_hw_sysfs_pending.attr, &blk_mq_hw_sysfs_tags.attr, &blk_mq_hw_sysfs_cpus.attr, + &blk_mq_hw_sysfs_active.attr, NULL, }; diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index 8d526a3e02f6..c80086c9c064 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -7,13 +7,12 @@ #include "blk-mq.h" #include "blk-mq-tag.h" -void blk_mq_wait_for_tags(struct blk_mq_tags *tags, struct blk_mq_hw_ctx *hctx, - bool reserved) +void blk_mq_wait_for_tags(struct blk_mq_hw_ctx *hctx, bool reserved) { int tag, zero = 0; - tag = blk_mq_get_tag(tags, hctx, &zero, __GFP_WAIT, reserved); - blk_mq_put_tag(tags, tag, &zero); + tag = blk_mq_get_tag(hctx, &zero, __GFP_WAIT, reserved); + blk_mq_put_tag(hctx, tag, &zero); } static bool bt_has_free_tags(struct blk_mq_bitmap_tags *bt) @@ -40,6 +39,84 @@ bool blk_mq_has_free_tags(struct blk_mq_tags *tags) return bt_has_free_tags(&tags->bitmap_tags); } +static inline void bt_index_inc(unsigned int *index) +{ + *index = (*index + 1) & (BT_WAIT_QUEUES - 1); +} + +/* + * If a previously inactive queue goes active, bump the active user count. + */ +bool __blk_mq_tag_busy(struct blk_mq_hw_ctx *hctx) +{ + if (!test_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state) && + !test_and_set_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state)) + atomic_inc(&hctx->tags->active_queues); + + return true; +} + +/* + * If a previously busy queue goes inactive, potential waiters could now + * be allowed to queue. Wake them up and check. + */ +void __blk_mq_tag_idle(struct blk_mq_hw_ctx *hctx) +{ + struct blk_mq_tags *tags = hctx->tags; + struct blk_mq_bitmap_tags *bt; + int i, wake_index; + + if (!test_and_clear_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state)) + return; + + atomic_dec(&tags->active_queues); + + /* + * Will only throttle depth on non-reserved tags + */ + bt = &tags->bitmap_tags; + wake_index = bt->wake_index; + for (i = 0; i < BT_WAIT_QUEUES; i++) { + struct bt_wait_state *bs = &bt->bs[wake_index]; + + if (waitqueue_active(&bs->wait)) + wake_up(&bs->wait); + + bt_index_inc(&wake_index); + } +} + +/* + * For shared tag users, we track the number of currently active users + * and attempt to provide a fair share of the tag depth for each of them. + */ +static inline bool hctx_may_queue(struct blk_mq_hw_ctx *hctx, + struct blk_mq_bitmap_tags *bt) +{ + unsigned int depth, users; + + if (!hctx || !(hctx->flags & BLK_MQ_F_TAG_SHARED)) + return true; + if (!test_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state)) + return true; + + /* + * Don't try dividing an ant + */ + if (bt->depth == 1) + return true; + + users = atomic_read(&hctx->tags->active_queues); + if (!users) + return true; + + /* + * Allow at least some tags + */ + depth = max((bt->depth + users - 1) / users, 4U); + return atomic_read(&hctx->nr_active) < depth; +} + static int __bt_get_word(struct blk_mq_bitmap *bm, unsigned int last_tag) { int tag, org_last_tag, end; @@ -78,11 +155,15 @@ restart: * multiple users will tend to stick to different cachelines, at least * until the map is exhausted. */ -static int __bt_get(struct blk_mq_bitmap_tags *bt, unsigned int *tag_cache) +static int __bt_get(struct blk_mq_hw_ctx *hctx, struct blk_mq_bitmap_tags *bt, + unsigned int *tag_cache) { unsigned int last_tag, org_last_tag; int index, i, tag; + if (!hctx_may_queue(hctx, bt)) + return -1; + last_tag = org_last_tag = *tag_cache; index = TAG_TO_INDEX(bt, last_tag); @@ -117,11 +198,6 @@ done: return tag; } -static inline void bt_index_inc(unsigned int *index) -{ - *index = (*index + 1) & (BT_WAIT_QUEUES - 1); -} - static struct bt_wait_state *bt_wait_ptr(struct blk_mq_bitmap_tags *bt, struct blk_mq_hw_ctx *hctx) { @@ -142,7 +218,7 @@ static int bt_get(struct blk_mq_bitmap_tags *bt, struct blk_mq_hw_ctx *hctx, DEFINE_WAIT(wait); int tag; - tag = __bt_get(bt, last_tag); + tag = __bt_get(hctx, bt, last_tag); if (tag != -1) return tag; @@ -156,7 +232,7 @@ static int bt_get(struct blk_mq_bitmap_tags *bt, struct blk_mq_hw_ctx *hctx, was_empty = list_empty(&wait.task_list); prepare_to_wait(&bs->wait, &wait, TASK_UNINTERRUPTIBLE); - tag = __bt_get(bt, last_tag); + tag = __bt_get(hctx, bt, last_tag); if (tag != -1) break; @@ -200,14 +276,13 @@ static unsigned int __blk_mq_get_reserved_tag(struct blk_mq_tags *tags, return tag; } -unsigned int blk_mq_get_tag(struct blk_mq_tags *tags, - struct blk_mq_hw_ctx *hctx, unsigned int *last_tag, +unsigned int blk_mq_get_tag(struct blk_mq_hw_ctx *hctx, unsigned int *last_tag, gfp_t gfp, bool reserved) { if (!reserved) - return __blk_mq_get_tag(tags, hctx, last_tag, gfp); + return __blk_mq_get_tag(hctx->tags, hctx, last_tag, gfp); - return __blk_mq_get_reserved_tag(tags, gfp); + return __blk_mq_get_reserved_tag(hctx->tags, gfp); } static struct bt_wait_state *bt_wake_ptr(struct blk_mq_bitmap_tags *bt) @@ -265,9 +340,11 @@ static void __blk_mq_put_reserved_tag(struct blk_mq_tags *tags, bt_clear_tag(&tags->breserved_tags, tag); } -void blk_mq_put_tag(struct blk_mq_tags *tags, unsigned int tag, +void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, unsigned int tag, unsigned int *last_tag) { + struct blk_mq_tags *tags = hctx->tags; + if (tag >= tags->nr_reserved_tags) { const int real_tag = tag - tags->nr_reserved_tags; @@ -465,6 +542,7 @@ ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page) res = bt_unused_tags(&tags->breserved_tags); page += sprintf(page, "nr_free=%u, nr_reserved=%u\n", free, res); + page += sprintf(page, "active_queues=%u\n", atomic_read(&tags->active_queues)); return page - orig_page; } diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h index 7aa9f0665489..0f5ec8b50ef3 100644 --- a/block/blk-mq-tag.h +++ b/block/blk-mq-tag.h @@ -38,6 +38,8 @@ struct blk_mq_tags { unsigned int nr_tags; unsigned int nr_reserved_tags; + atomic_t active_queues; + struct blk_mq_bitmap_tags bitmap_tags; struct blk_mq_bitmap_tags breserved_tags; @@ -49,9 +51,9 @@ struct blk_mq_tags { extern struct blk_mq_tags *blk_mq_init_tags(unsigned int nr_tags, unsigned int reserved_tags, int node); extern void blk_mq_free_tags(struct blk_mq_tags *tags); -extern unsigned int blk_mq_get_tag(struct blk_mq_tags *tags, struct blk_mq_hw_ctx *hctx, unsigned int *last_tag, gfp_t gfp, bool reserved); -extern void blk_mq_wait_for_tags(struct blk_mq_tags *tags, struct blk_mq_hw_ctx *hctx, bool reserved); -extern void blk_mq_put_tag(struct blk_mq_tags *tags, unsigned int tag, unsigned int *last_tag); +extern unsigned int blk_mq_get_tag(struct blk_mq_hw_ctx *hctx, unsigned int *last_tag, gfp_t gfp, bool reserved); +extern void blk_mq_wait_for_tags(struct blk_mq_hw_ctx *hctx, bool reserved); +extern void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, unsigned int tag, unsigned int *last_tag); extern void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, void (*fn)(void *data, unsigned long *), void *data); extern bool blk_mq_has_free_tags(struct blk_mq_tags *tags); extern ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page); @@ -68,4 +70,23 @@ enum { BLK_MQ_TAG_MAX = BLK_MQ_TAG_FAIL - 1, }; +extern bool __blk_mq_tag_busy(struct blk_mq_hw_ctx *); +extern void __blk_mq_tag_idle(struct blk_mq_hw_ctx *); + +static inline bool blk_mq_tag_busy(struct blk_mq_hw_ctx *hctx) +{ + if (!(hctx->flags & BLK_MQ_F_TAG_SHARED)) + return false; + + return __blk_mq_tag_busy(hctx); +} + +static inline void blk_mq_tag_idle(struct blk_mq_hw_ctx *hctx) +{ + if (!(hctx->flags & BLK_MQ_F_TAG_SHARED)) + return; + + __blk_mq_tag_idle(hctx); +} + #endif diff --git a/block/blk-mq.c b/block/blk-mq.c index 9f07a266f7ab..3c4f1fceef8e 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -80,9 +80,16 @@ static struct request *__blk_mq_alloc_request(struct blk_mq_hw_ctx *hctx, struct request *rq; unsigned int tag; - tag = blk_mq_get_tag(hctx->tags, hctx, &ctx->last_tag, gfp, reserved); + tag = blk_mq_get_tag(hctx, &ctx->last_tag, gfp, reserved); if (tag != BLK_MQ_TAG_FAIL) { rq = hctx->tags->rqs[tag]; + + rq->cmd_flags = 0; + if (blk_mq_tag_busy(hctx)) { + rq->cmd_flags = REQ_MQ_INFLIGHT; + atomic_inc(&hctx->nr_active); + } + rq->tag = tag; return rq; } @@ -190,7 +197,7 @@ static void blk_mq_rq_ctx_init(struct request_queue *q, struct blk_mq_ctx *ctx, /* csd/requeue_work/fifo_time is initialized before use */ rq->q = q; rq->mq_ctx = ctx; - rq->cmd_flags = rw_flags; + rq->cmd_flags |= rw_flags; rq->cmd_type = 0; /* do not touch atomic flags, it needs atomic ops against the timer */ rq->cpu = -1; @@ -262,7 +269,7 @@ static struct request *blk_mq_alloc_request_pinned(struct request_queue *q, break; } - blk_mq_wait_for_tags(hctx->tags, hctx, reserved); + blk_mq_wait_for_tags(hctx, reserved); } while (1); return rq; @@ -303,8 +310,11 @@ static void __blk_mq_free_request(struct blk_mq_hw_ctx *hctx, const int tag = rq->tag; struct request_queue *q = rq->q; + if (rq->cmd_flags & REQ_MQ_INFLIGHT) + atomic_dec(&hctx->nr_active); + clear_bit(REQ_ATOM_STARTED, &rq->atomic_flags); - blk_mq_put_tag(hctx->tags, tag, &ctx->last_tag); + blk_mq_put_tag(hctx, tag, &ctx->last_tag); blk_mq_queue_exit(q); } @@ -571,8 +581,13 @@ static void blk_mq_rq_timer(unsigned long data) queue_for_each_hw_ctx(q, hctx, i) blk_mq_hw_ctx_check_timeout(hctx, &next, &next_set); - if (next_set) - mod_timer(&q->timeout, round_jiffies_up(next)); + if (next_set) { + next = blk_rq_timeout(round_jiffies_up(next)); + mod_timer(&q->timeout, next); + } else { + queue_for_each_hw_ctx(q, hctx, i) + blk_mq_tag_idle(hctx); + } } /* @@ -1439,6 +1454,56 @@ static void blk_mq_map_swqueue(struct request_queue *q) } } +static void blk_mq_update_tag_set_depth(struct blk_mq_tag_set *set) +{ + struct blk_mq_hw_ctx *hctx; + struct request_queue *q; + bool shared; + int i; + + if (set->tag_list.next == set->tag_list.prev) + shared = false; + else + shared = true; + + list_for_each_entry(q, &set->tag_list, tag_set_list) { + blk_mq_freeze_queue(q); + + queue_for_each_hw_ctx(q, hctx, i) { + if (shared) + hctx->flags |= BLK_MQ_F_TAG_SHARED; + else + hctx->flags &= ~BLK_MQ_F_TAG_SHARED; + } + blk_mq_unfreeze_queue(q); + } +} + +static void blk_mq_del_queue_tag_set(struct request_queue *q) +{ + struct blk_mq_tag_set *set = q->tag_set; + + blk_mq_freeze_queue(q); + + mutex_lock(&set->tag_list_lock); + list_del_init(&q->tag_set_list); + blk_mq_update_tag_set_depth(set); + mutex_unlock(&set->tag_list_lock); + + blk_mq_unfreeze_queue(q); +} + +static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, + struct request_queue *q) +{ + q->tag_set = set; + + mutex_lock(&set->tag_list_lock); + list_add_tail(&q->tag_set_list, &set->tag_list); + blk_mq_update_tag_set_depth(set); + mutex_unlock(&set->tag_list_lock); +} + struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) { struct blk_mq_hw_ctx **hctxs; @@ -1464,6 +1529,7 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) if (!zalloc_cpumask_var(&hctxs[i]->cpumask, GFP_KERNEL)) goto err_hctxs; + atomic_set(&hctxs[i]->nr_active, 0); hctxs[i]->numa_node = NUMA_NO_NODE; hctxs[i]->queue_num = i; } @@ -1516,6 +1582,8 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) list_add_tail(&q->all_q_node, &all_q_list); mutex_unlock(&all_q_mutex); + blk_mq_add_queue_tag_set(set, q); + return q; err_flush_rq: @@ -1543,6 +1611,8 @@ void blk_mq_free_queue(struct request_queue *q) struct blk_mq_hw_ctx *hctx; int i; + blk_mq_del_queue_tag_set(q); + queue_for_each_hw_ctx(q, hctx, i) { kfree(hctx->ctx_map); kfree(hctx->ctxs); @@ -1635,6 +1705,9 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set) goto out_unwind; } + mutex_init(&set->tag_list_lock); + INIT_LIST_HEAD(&set->tag_list); + return 0; out_unwind: diff --git a/block/blk-timeout.c b/block/blk-timeout.c index 448745683d28..43e8b515806f 100644 --- a/block/blk-timeout.c +++ b/block/blk-timeout.c @@ -166,6 +166,17 @@ void blk_abort_request(struct request *req) } EXPORT_SYMBOL_GPL(blk_abort_request); +unsigned long blk_rq_timeout(unsigned long timeout) +{ + unsigned long maxt; + + maxt = round_jiffies_up(jiffies + BLK_MAX_TIMEOUT); + if (time_after(timeout, maxt)) + timeout = maxt; + + return timeout; +} + /** * blk_add_timer - Start timeout timer for a single request * @req: request that is about to start running. @@ -200,7 +211,7 @@ void blk_add_timer(struct request *req) * than an existing one, modify the timer. Round up to next nearest * second. */ - expiry = round_jiffies_up(req->deadline); + expiry = blk_rq_timeout(round_jiffies_up(req->deadline)); if (!timer_pending(&q->timeout) || time_before(expiry, q->timeout.expires)) { diff --git a/block/blk.h b/block/blk.h index 79be2cbce7fd..95cab70000e3 100644 --- a/block/blk.h +++ b/block/blk.h @@ -9,6 +9,9 @@ /* Number of requests a "batching" process may submit */ #define BLK_BATCH_REQ 32 +/* Max future timer expiry for timeouts */ +#define BLK_MAX_TIMEOUT (5 * HZ) + extern struct kmem_cache *blk_requestq_cachep; extern struct kmem_cache *request_cachep; extern struct kobj_type blk_queue_ktype; @@ -37,6 +40,7 @@ bool __blk_end_bidi_request(struct request *rq, int error, void blk_rq_timed_out_timer(unsigned long data); void blk_rq_check_expired(struct request *rq, unsigned long *next_timeout, unsigned int *next_set); +unsigned long blk_rq_timeout(unsigned long timeout); void blk_add_timer(struct request *req); void blk_delete_timer(struct request *); diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index f83d15f6e1c1..379f88d5c44d 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -48,6 +48,8 @@ struct blk_mq_hw_ctx { unsigned int numa_node; unsigned int cmd_size; /* per-request extra data */ + atomic_t nr_active; + struct blk_mq_cpu_notifier cpu_notifier; struct kobject kobj; }; @@ -64,6 +66,9 @@ struct blk_mq_tag_set { void *driver_data; struct blk_mq_tags **tags; + + struct mutex tag_list_lock; + struct list_head tag_list; }; typedef int (queue_rq_fn)(struct blk_mq_hw_ctx *, struct request *); @@ -126,8 +131,10 @@ enum { BLK_MQ_F_SHOULD_MERGE = 1 << 0, BLK_MQ_F_SHOULD_SORT = 1 << 1, + BLK_MQ_F_TAG_SHARED = 1 << 2, BLK_MQ_S_STOPPED = 0, + BLK_MQ_S_TAG_ACTIVE = 1, BLK_MQ_MAX_DEPTH = 2048, diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index aa0eaa2d0bd8..d8e4cea23a25 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -190,6 +190,7 @@ enum rq_flag_bits { __REQ_PM, /* runtime pm request */ __REQ_END, /* last of chain of requests */ __REQ_HASHED, /* on IO scheduler merge hash */ + __REQ_MQ_INFLIGHT, /* track inflight for MQ */ __REQ_NR_BITS, /* stops here */ }; @@ -243,5 +244,6 @@ enum rq_flag_bits { #define REQ_PM (1ULL << __REQ_PM) #define REQ_END (1ULL << __REQ_END) #define REQ_HASHED (1ULL << __REQ_HASHED) +#define REQ_MQ_INFLIGHT (1ULL << __REQ_MQ_INFLIGHT) #endif /* __LINUX_BLK_TYPES_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 94b27210641b..6bc011a09e82 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -481,6 +481,9 @@ struct request_queue { wait_queue_head_t mq_freeze_wq; struct percpu_counter mq_usage_counter; struct list_head all_q_node; + + struct blk_mq_tag_set *tag_set; + struct list_head tag_set_list; }; #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */ -- cgit v1.2.3 From 7ad24ea4bf620a32631d7b3069c3e30c078b0c3e Mon Sep 17 00:00:00 2001 From: Wilfried Klaebe Date: Sun, 11 May 2014 00:12:32 +0000 Subject: net: get rid of SET_ETHTOOL_OPS net: get rid of SET_ETHTOOL_OPS Dave Miller mentioned he'd like to see SET_ETHTOOL_OPS gone. This does that. Mostly done via coccinelle script: @@ struct ethtool_ops *ops; struct net_device *dev; @@ - SET_ETHTOOL_OPS(dev, ops); + dev->ethtool_ops = ops; Compile tested only, but I'd seriously wonder if this broke anything. Suggested-by: Dave Miller Signed-off-by: Wilfried Klaebe Acked-by: Felipe Balbi Signed-off-by: David S. Miller --- drivers/infiniband/ulp/ipoib/ipoib_ethtool.c | 2 +- drivers/net/ethernet/3com/3c509.c | 2 +- drivers/net/ethernet/3com/3c589_cs.c | 2 +- drivers/net/ethernet/3com/typhoon.c | 2 +- drivers/net/ethernet/adaptec/starfire.c | 2 +- drivers/net/ethernet/alteon/acenic.c | 2 +- drivers/net/ethernet/altera/altera_tse_ethtool.c | 2 +- drivers/net/ethernet/amd/amd8111e.c | 2 +- drivers/net/ethernet/amd/au1000_eth.c | 2 +- drivers/net/ethernet/amd/nmclan_cs.c | 2 +- drivers/net/ethernet/atheros/alx/main.c | 2 +- drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c | 2 +- drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c | 2 +- drivers/net/ethernet/atheros/atlx/atl2.c | 2 +- drivers/net/ethernet/broadcom/b44.c | 2 +- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 4 ++-- drivers/net/ethernet/broadcom/bcmsysport.c | 2 +- drivers/net/ethernet/broadcom/bgmac.c | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 6 ++---- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 2 +- drivers/net/ethernet/brocade/bna/bnad_ethtool.c | 2 +- drivers/net/ethernet/calxeda/xgmac.c | 2 +- drivers/net/ethernet/chelsio/cxgb/cxgb2.c | 2 +- drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c | 2 +- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 2 +- drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c | 2 +- drivers/net/ethernet/cisco/enic/enic_ethtool.c | 2 +- drivers/net/ethernet/dec/tulip/tulip_core.c | 2 +- drivers/net/ethernet/dlink/dl2k.c | 2 +- drivers/net/ethernet/dlink/sundance.c | 2 +- drivers/net/ethernet/emulex/benet/be_main.c | 2 +- drivers/net/ethernet/faraday/ftgmac100.c | 2 +- drivers/net/ethernet/faraday/ftmac100.c | 2 +- drivers/net/ethernet/freescale/ucc_geth_ethtool.c | 2 +- drivers/net/ethernet/fujitsu/fmvj18x_cs.c | 2 +- drivers/net/ethernet/ibm/ehea/ehea_ethtool.c | 2 +- drivers/net/ethernet/ibm/emac/core.c | 2 +- drivers/net/ethernet/icplus/ipg.c | 2 +- drivers/net/ethernet/intel/e100.c | 2 +- drivers/net/ethernet/intel/e1000/e1000_ethtool.c | 2 +- drivers/net/ethernet/intel/e1000e/ethtool.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 2 +- drivers/net/ethernet/intel/igb/igb_ethtool.c | 2 +- drivers/net/ethernet/intel/igbvf/ethtool.c | 2 +- drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 2 +- drivers/net/ethernet/intel/ixgbevf/ethtool.c | 2 +- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- drivers/net/ethernet/marvell/mvneta.c | 2 +- drivers/net/ethernet/marvell/pxa168_eth.c | 2 +- drivers/net/ethernet/marvell/sky2.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +- drivers/net/ethernet/micrel/ks8695net.c | 6 +++--- drivers/net/ethernet/micrel/ks8851.c | 2 +- drivers/net/ethernet/micrel/ksz884x.c | 2 +- drivers/net/ethernet/microchip/enc28j60.c | 2 +- drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 2 +- drivers/net/ethernet/natsemi/natsemi.c | 2 +- drivers/net/ethernet/natsemi/ns83820.c | 2 +- drivers/net/ethernet/neterion/s2io.c | 2 +- drivers/net/ethernet/neterion/vxge/vxge-ethtool.c | 2 +- drivers/net/ethernet/nvidia/forcedeth.c | 2 +- drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c | 2 +- drivers/net/ethernet/packetengines/hamachi.c | 6 ++---- drivers/net/ethernet/packetengines/yellowfin.c | 2 +- drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c | 2 +- drivers/net/ethernet/qlogic/qla3xxx.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 8 +++----- drivers/net/ethernet/qlogic/qlge/qlge_main.c | 2 +- drivers/net/ethernet/realtek/r8169.c | 2 +- drivers/net/ethernet/renesas/sh_eth.c | 2 +- drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c | 2 +- drivers/net/ethernet/sfc/efx.c | 2 +- drivers/net/ethernet/sis/sis190.c | 2 +- drivers/net/ethernet/smsc/smc91c92_cs.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 +- drivers/net/ethernet/tehuti/tehuti.c | 2 +- drivers/net/ethernet/ti/cpsw.c | 4 ++-- drivers/net/ethernet/ti/davinci_emac.c | 2 +- drivers/net/hyperv/netvsc_drv.c | 2 +- drivers/net/ntb_netdev.c | 2 +- drivers/net/rionet.c | 2 +- drivers/net/usb/catc.c | 2 +- drivers/net/usb/hso.c | 2 +- drivers/net/usb/ipheth.c | 2 +- drivers/net/usb/kaweth.c | 2 +- drivers/net/usb/pegasus.c | 2 +- drivers/net/usb/r8152.c | 2 +- drivers/net/usb/rtl8150.c | 2 +- drivers/net/virtio_net.c | 2 +- drivers/net/vmxnet3/vmxnet3_ethtool.c | 2 +- drivers/net/vxlan.c | 2 +- drivers/net/wireless/hostap/hostap_main.c | 2 +- drivers/net/xen-netback/interface.c | 2 +- drivers/net/xen-netfront.c | 2 +- drivers/s390/net/qeth_l2_main.c | 7 +++---- drivers/s390/net/qeth_l3_main.c | 2 +- drivers/staging/et131x/et131x.c | 2 +- drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c | 2 +- drivers/staging/netlogic/xlr_net.c | 2 +- drivers/staging/octeon/ethernet.c | 2 +- drivers/usb/gadget/u_ether.c | 4 ++-- include/linux/netdevice.h | 3 --- net/batman-adv/soft-interface.c | 2 +- net/bridge/br_device.c | 2 +- net/dsa/slave.c | 2 +- net/openvswitch/vport-internal_dev.c | 2 +- 108 files changed, 118 insertions(+), 128 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c index c4b3940845e6..078cadd6c797 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c @@ -105,5 +105,5 @@ static const struct ethtool_ops ipoib_ethtool_ops = { void ipoib_set_ethtool_ops(struct net_device *dev) { - SET_ETHTOOL_OPS(dev, &ipoib_ethtool_ops); + dev->ethtool_ops = &ipoib_ethtool_ops; } diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index 35df0b9e6848..a968654b631d 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -534,7 +534,7 @@ static int el3_common_init(struct net_device *dev) /* The EL3-specific entries in the device structure. */ dev->netdev_ops = &netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(dev, ðtool_ops); + dev->ethtool_ops = ðtool_ops; err = register_netdev(dev); if (err) { diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index 063557e037f2..f18647c23559 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -218,7 +218,7 @@ static int tc589_probe(struct pcmcia_device *link) dev->netdev_ops = &el3_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); + dev->ethtool_ops = &netdev_ethtool_ops; return tc589_config(link); } diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index 465cc7108d8a..e13b04624ded 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -2435,7 +2435,7 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netif_napi_add(dev, &tp->napi, typhoon_poll, 16); dev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(dev, &typhoon_ethtool_ops); + dev->ethtool_ops = &typhoon_ethtool_ops; /* We can handle scatter gather, up to 16 entries, and * we can do IP checksumming (only version 4, doh...) diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c index 171d73c1d3c2..40dbbf740331 100644 --- a/drivers/net/ethernet/adaptec/starfire.c +++ b/drivers/net/ethernet/adaptec/starfire.c @@ -784,7 +784,7 @@ static int starfire_init_one(struct pci_dev *pdev, dev->netdev_ops = &netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(dev, ðtool_ops); + dev->ethtool_ops = ðtool_ops; netif_napi_add(dev, &np->napi, netdev_poll, max_interrupt_work); diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c index 1517e9df5ba1..9a6991be9749 100644 --- a/drivers/net/ethernet/alteon/acenic.c +++ b/drivers/net/ethernet/alteon/acenic.c @@ -476,7 +476,7 @@ static int acenic_probe_one(struct pci_dev *pdev, dev->watchdog_timeo = 5*HZ; dev->netdev_ops = &ace_netdev_ops; - SET_ETHTOOL_OPS(dev, &ace_ethtool_ops); + dev->ethtool_ops = &ace_ethtool_ops; /* we only display this string ONCE */ if (!boards_found) diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c index 76133caffa78..d817e285b266 100644 --- a/drivers/net/ethernet/altera/altera_tse_ethtool.c +++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c @@ -237,5 +237,5 @@ static const struct ethtool_ops tse_ethtool_ops = { void altera_tse_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &tse_ethtool_ops); + netdev->ethtool_ops = &tse_ethtool_ops; } diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index 26efaaa5e73f..068dc7cad5fa 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -1900,7 +1900,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev, /* Initialize driver entry points */ dev->netdev_ops = &amd8111e_netdev_ops; - SET_ETHTOOL_OPS(dev, &ops); + dev->ethtool_ops = &ops; dev->irq =pdev->irq; dev->watchdog_timeo = AMD8111E_TX_TIMEOUT; netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32); diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index a2bd91e3d302..a78e4c136959 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -1229,7 +1229,7 @@ static int au1000_probe(struct platform_device *pdev) dev->base_addr = base->start; dev->irq = irq; dev->netdev_ops = &au1000_netdev_ops; - SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops); + dev->ethtool_ops = &au1000_ethtool_ops; dev->watchdog_timeo = ETH_TX_TIMEOUT; /* diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c index 08569fe2b182..abf3b1581c82 100644 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -457,7 +457,7 @@ static int nmclan_probe(struct pcmcia_device *link) lp->tx_free_frames=AM2150_MAX_TX_FRAMES; dev->netdev_ops = &mace_netdev_ops; - SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); + dev->ethtool_ops = &netdev_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; return nmclan_config(link); diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 17bb9ce96260..49faa97a30c3 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -1302,7 +1302,7 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } netdev->netdev_ops = &alx_netdev_ops; - SET_ETHTOOL_OPS(netdev, &alx_ethtool_ops); + netdev->ethtool_ops = &alx_ethtool_ops; netdev->irq = pdev->irq; netdev->watchdog_timeo = ALX_WATCHDOG_TIME; diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c index 859ea844ba0f..ecacaaeb2b92 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c @@ -305,5 +305,5 @@ static const struct ethtool_ops atl1c_ethtool_ops = { void atl1c_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops); + netdev->ethtool_ops = &atl1c_ethtool_ops; } diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c index 82b23861bf55..206e9b7be431 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c @@ -388,5 +388,5 @@ static const struct ethtool_ops atl1e_ethtool_ops = { void atl1e_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops); + netdev->ethtool_ops = &atl1e_ethtool_ops; } diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index 78befb522a52..2587fed7b02c 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -1396,7 +1396,7 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) atl2_setup_pcicmd(pdev); netdev->netdev_ops = &atl2_netdev_ops; - SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops); + netdev->ethtool_ops = &atl2_ethtool_ops; netdev->watchdog_timeo = 5 * HZ; strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 05ba62589017..ca5a20a48b14 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2380,7 +2380,7 @@ static int b44_init_one(struct ssb_device *sdev, netif_napi_add(dev, &bp->napi, b44_poll, 64); dev->watchdog_timeo = B44_TX_TIMEOUT; dev->irq = sdev->irq; - SET_ETHTOOL_OPS(dev, &b44_ethtool_ops); + dev->ethtool_ops = &b44_ethtool_ops; err = ssb_bus_powerup(sdev->bus, 0); if (err) { diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 8db34d389675..3e8d1a88ed3d 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1897,7 +1897,7 @@ static int bcm_enet_probe(struct platform_device *pdev) dev->netdev_ops = &bcm_enet_ops; netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16); - SET_ETHTOOL_OPS(dev, &bcm_enet_ethtool_ops); + dev->ethtool_ops = &bcm_enet_ethtool_ops; SET_NETDEV_DEV(dev, &pdev->dev); ret = register_netdev(dev); @@ -2783,7 +2783,7 @@ static int bcm_enetsw_probe(struct platform_device *pdev) /* register netdevice */ dev->netdev_ops = &bcm_enetsw_ops; netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16); - SET_ETHTOOL_OPS(dev, &bcm_enetsw_ethtool_ops); + dev->ethtool_ops = &bcm_enetsw_ethtool_ops; SET_NETDEV_DEV(dev, &pdev->dev); spin_lock_init(&priv->enetsw_mdio_lock); diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 4dc8d1e9829b..56b74a495181 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -1540,7 +1540,7 @@ static int bcm_sysport_probe(struct platform_device *pdev) SET_NETDEV_DEV(dev, &pdev->dev); dev_set_drvdata(&pdev->dev, dev); - SET_ETHTOOL_OPS(dev, &bcm_sysport_ethtool_ops); + dev->ethtool_ops = &bcm_sysport_ethtool_ops; dev->netdev_ops = &bcm_sysport_netdev_ops; netif_napi_add(dev, &priv->napi, bcm_sysport_poll, 64); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 0297a79a38e1..05c6af6c418f 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1436,7 +1436,7 @@ static int bgmac_probe(struct bcma_device *core) return -ENOMEM; net_dev->netdev_ops = &bgmac_netdev_ops; net_dev->irq = core->irq; - SET_ETHTOOL_OPS(net_dev, &bgmac_ethtool_ops); + net_dev->ethtool_ops = &bgmac_ethtool_ops; bgmac = netdev_priv(net_dev); bgmac->net_dev = net_dev; bgmac->core = core; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index b6de05e3149b..03224090ecf9 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -3506,8 +3506,6 @@ static const struct ethtool_ops bnx2x_vf_ethtool_ops = { void bnx2x_set_ethtool_ops(struct bnx2x *bp, struct net_device *netdev) { - if (IS_PF(bp)) - SET_ETHTOOL_OPS(netdev, &bnx2x_ethtool_ops); - else /* vf */ - SET_ETHTOOL_OPS(netdev, &bnx2x_vf_ethtool_ops); + netdev->ethtool_ops = (IS_PF(bp)) ? + &bnx2x_ethtool_ops : &bnx2x_vf_ethtool_ops; } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 0966bd04375f..5ba1cfbd60da 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2481,7 +2481,7 @@ static int bcmgenet_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, dev); ether_addr_copy(dev->dev_addr, macaddr); dev->watchdog_timeo = 2 * HZ; - SET_ETHTOOL_OPS(dev, &bcmgenet_ethtool_ops); + dev->ethtool_ops = &bcmgenet_ethtool_ops; dev->netdev_ops = &bcmgenet_netdev_ops; netif_napi_add(dev, &priv->napi, bcmgenet_poll, 64); diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index f9e150825bb5..adca62b72837 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -1137,5 +1137,5 @@ static const struct ethtool_ops bnad_ethtool_ops = { void bnad_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &bnad_ethtool_ops); + netdev->ethtool_ops = &bnad_ethtool_ops; } diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 521dfea44b83..25d6b2a10e4e 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -1737,7 +1737,7 @@ static int xgmac_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ndev); ether_setup(ndev); ndev->netdev_ops = &xgmac_netdev_ops; - SET_ETHTOOL_OPS(ndev, &xgmac_ethtool_ops); + ndev->ethtool_ops = &xgmac_ethtool_ops; spin_lock_init(&priv->stats_lock); INIT_WORK(&priv->tx_timeout_work, xgmac_tx_timeout_work); diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index 0fe7ff750d77..c1b2c1dbf015 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -1100,7 +1100,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netif_napi_add(netdev, &adapter->napi, t1_poll, 64); - SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops); + netdev->ethtool_ops = &t1_ethtool_ops; } if (t1_init_sw_modules(adapter, bi) < 0) { diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 07bbb711b7e5..3ed507947248 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -3291,7 +3291,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->features |= NETIF_F_HIGHDMA; netdev->netdev_ops = &cxgb_netdev_ops; - SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops); + netdev->ethtool_ops = &cxgb_ethtool_ops; } pci_set_drvdata(pdev, adapter); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 0f1e886d89e3..266a5bc6aedf 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -6083,7 +6083,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->priv_flags |= IFF_UNICAST_FLT; netdev->netdev_ops = &cxgb4_netdev_ops; - SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops); + netdev->ethtool_ops = &cxgb_ethtool_ops; } pci_set_drvdata(pdev, adapter); diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 52859288de7b..ff1cdd1788b5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -2664,7 +2664,7 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev, netdev->priv_flags |= IFF_UNICAST_FLT; netdev->netdev_ops = &cxgb4vf_netdev_ops; - SET_ETHTOOL_OPS(netdev, &cxgb4vf_ethtool_ops); + netdev->ethtool_ops = &cxgb4vf_ethtool_ops; /* * Initialize the hardware/software state for the port. diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index 47e3562f4866..58a8c67638e3 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -253,5 +253,5 @@ static const struct ethtool_ops enic_ethtool_ops = { void enic_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &enic_ethtool_ops); + netdev->ethtool_ops = &enic_ethtool_ops; } diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 1642de78aac8..861660841ce2 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -1703,7 +1703,7 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #ifdef CONFIG_TULIP_NAPI netif_napi_add(dev, &tp->napi, tulip_poll, 16); #endif - SET_ETHTOOL_OPS(dev, &ops); + dev->ethtool_ops = &ops; if (register_netdev(dev)) goto err_out_free_ring; diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index 4fb756d219f7..2324f2ddfd48 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -227,7 +227,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) } dev->netdev_ops = &netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(dev, ðtool_ops); + dev->ethtool_ops = ðtool_ops; #if 0 dev->features = NETIF_F_IP_CSUM; #endif diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c index d9e5ca0d48c1..433c1e185442 100644 --- a/drivers/net/ethernet/dlink/sundance.c +++ b/drivers/net/ethernet/dlink/sundance.c @@ -577,7 +577,7 @@ static int sundance_probe1(struct pci_dev *pdev, /* The chip-specific entries in the device structure. */ dev->netdev_ops = &netdev_ops; - SET_ETHTOOL_OPS(dev, ðtool_ops); + dev->ethtool_ops = ðtool_ops; dev->watchdog_timeo = TX_TIMEOUT; pci_set_drvdata(pdev, dev); diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 3f04356afa82..dcc5e5c69743 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4304,7 +4304,7 @@ static void be_netdev_init(struct net_device *netdev) netdev->netdev_ops = &be_netdev_ops; - SET_ETHTOOL_OPS(netdev, &be_ethtool_ops); + netdev->ethtool_ops = &be_ethtool_ops; } static void be_unmap_pci_bars(struct be_adapter *adapter) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 68069eabc4f8..c77fa4a69844 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1210,7 +1210,7 @@ static int ftgmac100_probe(struct platform_device *pdev) SET_NETDEV_DEV(netdev, &pdev->dev); - SET_ETHTOOL_OPS(netdev, &ftgmac100_ethtool_ops); + netdev->ethtool_ops = &ftgmac100_ethtool_ops; netdev->netdev_ops = &ftgmac100_netdev_ops; netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO; diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index 8be5b40c0a12..4ff1adc6bfca 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -1085,7 +1085,7 @@ static int ftmac100_probe(struct platform_device *pdev) } SET_NETDEV_DEV(netdev, &pdev->dev); - SET_ETHTOOL_OPS(netdev, &ftmac100_ethtool_ops); + netdev->ethtool_ops = &ftmac100_ethtool_ops; netdev->netdev_ops = &ftmac100_netdev_ops; platform_set_drvdata(pdev, netdev); diff --git a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c index 413329eff2ff..cc83350d56ba 100644 --- a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c +++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c @@ -417,5 +417,5 @@ static const struct ethtool_ops uec_ethtool_ops = { void uec_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &uec_ethtool_ops); + netdev->ethtool_ops = &uec_ethtool_ops; } diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c index 7becab1aa3e4..cfe7a7431730 100644 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c @@ -256,7 +256,7 @@ static int fmvj18x_probe(struct pcmcia_device *link) dev->netdev_ops = &fjn_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); + dev->ethtool_ops = &netdev_ethtool_ops; return fmvj18x_config(link); } /* fmvj18x_attach */ diff --git a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c index 95837b99a464..6055e3eaf49c 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c @@ -278,5 +278,5 @@ static const struct ethtool_ops ehea_ethtool_ops = { void ehea_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &ehea_ethtool_ops); + netdev->ethtool_ops = &ehea_ethtool_ops; } diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index ae342fdb42c8..87bd953cc2ee 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -2879,7 +2879,7 @@ static int emac_probe(struct platform_device *ofdev) dev->commac.ops = &emac_commac_sg_ops; } else ndev->netdev_ops = &emac_netdev_ops; - SET_ETHTOOL_OPS(ndev, &emac_ethtool_ops); + ndev->ethtool_ops = &emac_ethtool_ops; netif_carrier_off(ndev); diff --git a/drivers/net/ethernet/icplus/ipg.c b/drivers/net/ethernet/icplus/ipg.c index 25045ae07171..5727779a7df2 100644 --- a/drivers/net/ethernet/icplus/ipg.c +++ b/drivers/net/ethernet/icplus/ipg.c @@ -2245,7 +2245,7 @@ static int ipg_probe(struct pci_dev *pdev, const struct pci_device_id *id) */ dev->netdev_ops = &ipg_netdev_ops; SET_NETDEV_DEV(dev, &pdev->dev); - SET_ETHTOOL_OPS(dev, &ipg_ethtool_ops); + dev->ethtool_ops = &ipg_ethtool_ops; rc = pci_request_regions(pdev, DRV_NAME); if (rc) diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index b56461ce674c..9d979d7debef 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -2854,7 +2854,7 @@ static int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->hw_features |= NETIF_F_RXALL; netdev->netdev_ops = &e100_netdev_ops; - SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops); + netdev->ethtool_ops = &e100_ethtool_ops; netdev->watchdog_timeo = E100_WATCHDOG_PERIOD; strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index 73a8aeefb92a..341889a4ef7f 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -1905,5 +1905,5 @@ static const struct ethtool_ops e1000_ethtool_ops = { void e1000_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &e1000_ethtool_ops); + netdev->ethtool_ops = &e1000_ethtool_ops; } diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 4e5ad7ebe1f2..e9a48bb5caac 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -2318,5 +2318,5 @@ static const struct ethtool_ops e1000_ethtool_ops = { void e1000e_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &e1000_ethtool_ops); + netdev->ethtool_ops = &e1000_ethtool_ops; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 0cf47c958081..f62929419a09 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1700,5 +1700,5 @@ static const struct ethtool_ops i40e_ethtool_ops = { void i40e_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &i40e_ethtool_ops); + netdev->ethtool_ops = &i40e_ethtool_ops; } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index a46be016039e..77e786d2d0e0 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -705,5 +705,5 @@ static struct ethtool_ops i40evf_ethtool_ops = { **/ void i40evf_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &i40evf_ethtool_ops); + netdev->ethtool_ops = &i40evf_ethtool_ops; } diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 333a2b0bbada..a84297c85fb1 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -3035,5 +3035,5 @@ static const struct ethtool_ops igb_ethtool_ops = { void igb_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &igb_ethtool_ops); + netdev->ethtool_ops = &igb_ethtool_ops; } diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index 90eef07943f4..f58170bae18b 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -476,5 +476,5 @@ static const struct ethtool_ops igbvf_ethtool_ops = { void igbvf_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &igbvf_ethtool_ops); + netdev->ethtool_ops = &igbvf_ethtool_ops; } diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c index dbb7dd2f8e36..1da2d987d370 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c @@ -656,5 +656,5 @@ static const struct ethtool_ops ixgb_ethtool_ops = { void ixgb_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &ixgb_ethtool_ops); + netdev->ethtool_ops = &ixgb_ethtool_ops; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 6c55c14d082a..31d7268401e7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -3099,5 +3099,5 @@ static const struct ethtool_ops ixgbe_ethtool_ops = { void ixgbe_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &ixgbe_ethtool_ops); + netdev->ethtool_ops = &ixgbe_ethtool_ops; } diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index 1baecb60f065..a757f0734719 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -813,5 +813,5 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = { void ixgbevf_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &ixgbevf_ethtool_ops); + netdev->ethtool_ops = &ixgbevf_ethtool_ops; } diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index b7b8d74c22d9..df1d1b97187f 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2889,7 +2889,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) if (err) goto out; - SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); + dev->ethtool_ops = &mv643xx_eth_ethtool_ops; init_pscr(mp, pd->speed, pd->duplex); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 14786c8bf99e..72bc47f2d2e9 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2813,7 +2813,7 @@ static int mvneta_probe(struct platform_device *pdev) dev->watchdog_timeo = 5 * HZ; dev->netdev_ops = &mvneta_netdev_ops; - SET_ETHTOOL_OPS(dev, &mvneta_eth_tool_ops); + dev->ethtool_ops = &mvneta_eth_tool_ops; pp = netdev_priv(dev); diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index b358c2f6f4bd..8f5aa7c62b18 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1488,7 +1488,7 @@ static int pxa168_eth_probe(struct platform_device *pdev) dev->netdev_ops = &pxa168_eth_netdev_ops; dev->watchdog_timeo = 2 * HZ; dev->base_addr = 0; - SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops); + dev->ethtool_ops = &pxa168_ethtool_ops; INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task); diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index b81106451a0a..69693384b58c 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4760,7 +4760,7 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port, SET_NETDEV_DEV(dev, &hw->pdev->dev); dev->irq = hw->pdev->irq; - SET_ETHTOOL_OPS(dev, &sky2_ethtool_ops); + dev->ethtool_ops = &sky2_ethtool_ops; dev->watchdog_timeo = TX_WATCHDOG; dev->netdev_ops = &sky2_netdev_ops[port]; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index fba3c8e77626..79c6f467d17e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2539,7 +2539,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, netif_set_real_num_tx_queues(dev, priv->tx_ring_num); netif_set_real_num_rx_queues(dev, priv->rx_ring_num); - SET_ETHTOOL_OPS(dev, &mlx4_en_ethtool_ops); + dev->ethtool_ops = &mlx4_en_ethtool_ops; /* * Set driver features diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c index 16435b3cfa9f..6c7c78baedca 100644 --- a/drivers/net/ethernet/micrel/ks8695net.c +++ b/drivers/net/ethernet/micrel/ks8695net.c @@ -1504,15 +1504,15 @@ ks8695_probe(struct platform_device *pdev) if (ksp->phyiface_regs && ksp->link_irq == -1) { ks8695_init_switch(ksp); ksp->dtype = KS8695_DTYPE_LAN; - SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops); + ndev->ethtool_ops = &ks8695_ethtool_ops; } else if (ksp->phyiface_regs && ksp->link_irq != -1) { ks8695_init_wan_phy(ksp); ksp->dtype = KS8695_DTYPE_WAN; - SET_ETHTOOL_OPS(ndev, &ks8695_wan_ethtool_ops); + ndev->ethtool_ops = &ks8695_wan_ethtool_ops; } else { /* No initialisation since HPNA does not have a PHY */ ksp->dtype = KS8695_DTYPE_HPNA; - SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops); + ndev->ethtool_ops = &ks8695_ethtool_ops; } /* And bring up the net_device with the net core */ diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index e0c92e0e5e1d..13767eb36a48 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -1471,7 +1471,7 @@ static int ks8851_probe(struct spi_device *spi) skb_queue_head_init(&ks->txq); - SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops); + ndev->ethtool_ops = &ks8851_ethtool_ops; SET_NETDEV_DEV(ndev, &spi->dev); spi_set_drvdata(spi, ks); diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 14ac0e2bc09f..4b9592c1fb40 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -7106,7 +7106,7 @@ static int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id) } dev->netdev_ops = &netdev_ops; - SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); + dev->ethtool_ops = &netdev_ethtool_ops; if (register_netdev(dev)) goto pcidev_init_reg_err; port_set_power_saving(port, true); diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c index c7b40aa21f22..b1b5f66b8b69 100644 --- a/drivers/net/ethernet/microchip/enc28j60.c +++ b/drivers/net/ethernet/microchip/enc28j60.c @@ -1593,7 +1593,7 @@ static int enc28j60_probe(struct spi_device *spi) dev->irq = spi->irq; dev->netdev_ops = &enc28j60_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops); + dev->ethtool_ops = &enc28j60_ethtool_ops; enc28j60_lowpower(priv, true); diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 130f6b204efa..f3d5d79f1cd1 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -4112,7 +4112,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) setup_timer(&mgp->watchdog_timer, myri10ge_watchdog_timer, (unsigned long)mgp); - SET_ETHTOOL_OPS(netdev, &myri10ge_ethtool_ops); + netdev->ethtool_ops = &myri10ge_ethtool_ops; INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog); status = register_netdev(netdev); if (status != 0) { diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c index 64ec2a437f46..291fba8b9f07 100644 --- a/drivers/net/ethernet/natsemi/natsemi.c +++ b/drivers/net/ethernet/natsemi/natsemi.c @@ -927,7 +927,7 @@ static int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent) dev->netdev_ops = &natsemi_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(dev, ðtool_ops); + dev->ethtool_ops = ðtool_ops; if (mtu) dev->mtu = mtu; diff --git a/drivers/net/ethernet/natsemi/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c index dbccf1de49ec..19bb8244b9e3 100644 --- a/drivers/net/ethernet/natsemi/ns83820.c +++ b/drivers/net/ethernet/natsemi/ns83820.c @@ -2030,7 +2030,7 @@ static int ns83820_init_one(struct pci_dev *pci_dev, pci_dev->subsystem_vendor, pci_dev->subsystem_device); ndev->netdev_ops = &netdev_ops; - SET_ETHTOOL_OPS(ndev, &ops); + ndev->ethtool_ops = &ops; ndev->watchdog_timeo = 5 * HZ; pci_set_drvdata(pci_dev, ndev); diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index e900c1abdef7..e3cf38e6ce3c 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -7910,7 +7910,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) /* Driver entry points */ dev->netdev_ops = &s2io_netdev_ops; - SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); + dev->ethtool_ops = &netdev_ethtool_ops; dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_RXCSUM | NETIF_F_LRO; diff --git a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c index f8f073880f84..ddcc81ad1ae1 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c @@ -1128,5 +1128,5 @@ static const struct ethtool_ops vxge_ethtool_ops = { void vxge_initialize_ethtool_ops(struct net_device *ndev) { - SET_ETHTOOL_OPS(ndev, &vxge_ethtool_ops); + ndev->ethtool_ops = &vxge_ethtool_ops; } diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index fddb464aeab3..e8235c5c5e69 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -5766,7 +5766,7 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) dev->netdev_ops = &nv_netdev_ops_optimized; netif_napi_add(dev, &np->napi, nv_napi_poll, RX_WORK_PER_LOOP); - SET_ETHTOOL_OPS(dev, &ops); + dev->ethtool_ops = &ops; dev->watchdog_timeo = NV_WATCHDOG_TIMEO; pci_set_drvdata(pci_dev, dev); diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c index 826f0ccdc23c..114d2fe52cc2 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c @@ -508,5 +508,5 @@ static const struct ethtool_ops pch_gbe_ethtool_ops = { void pch_gbe_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &pch_gbe_ethtool_ops); + netdev->ethtool_ops = &pch_gbe_ethtool_ops; } diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c index b6bdeb3c1971..9a997e4c3e08 100644 --- a/drivers/net/ethernet/packetengines/hamachi.c +++ b/drivers/net/ethernet/packetengines/hamachi.c @@ -724,10 +724,8 @@ static int hamachi_init_one(struct pci_dev *pdev, /* The Hamachi-specific entries in the device structure. */ dev->netdev_ops = &hamachi_netdev_ops; - if (chip_tbl[hmp->chip_id].flags & CanHaveMII) - SET_ETHTOOL_OPS(dev, ðtool_ops); - else - SET_ETHTOOL_OPS(dev, ðtool_ops_no_mii); + dev->ethtool_ops = (chip_tbl[hmp->chip_id].flags & CanHaveMII) ? + ðtool_ops : ðtool_ops_no_mii; dev->watchdog_timeo = TX_TIMEOUT; if (mtu) dev->mtu = mtu; diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c index 9a6cb482dcd0..69a8dc095072 100644 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ b/drivers/net/ethernet/packetengines/yellowfin.c @@ -472,7 +472,7 @@ static int yellowfin_init_one(struct pci_dev *pdev, /* The Yellowfin-specific entries in the device structure. */ dev->netdev_ops = &netdev_ops; - SET_ETHTOOL_OPS(dev, ðtool_ops); + dev->ethtool_ops = ðtool_ops; dev->watchdog_timeo = TX_TIMEOUT; if (mtu) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index f09c35d669b3..5bf05818a12c 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -1373,7 +1373,7 @@ netxen_setup_netdev(struct netxen_adapter *adapter, netxen_nic_change_mtu(netdev, netdev->mtu); - SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops); + netdev->ethtool_ops = &netxen_nic_ethtool_ops; netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_RXCSUM; diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 2eabd44f8914..b5d6bc1a8b00 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -3838,7 +3838,7 @@ static int ql3xxx_probe(struct pci_dev *pdev, /* Set driver entry points */ ndev->netdev_ops = &ql3xxx_netdev_ops; - SET_ETHTOOL_OPS(ndev, &ql3xxx_ethtool_ops); + ndev->ethtool_ops = &ql3xxx_ethtool_ops; ndev->watchdog_timeo = 5 * HZ; netif_napi_add(ndev, &qdev->napi, ql_poll, 64); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 8a2aeb85e320..f0a285359e66 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -2265,10 +2265,8 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, qlcnic_change_mtu(netdev, netdev->mtu); - if (qlcnic_sriov_vf_check(adapter)) - SET_ETHTOOL_OPS(netdev, &qlcnic_sriov_vf_ethtool_ops); - else - SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops); + netdev->ethtool_ops = (qlcnic_sriov_vf_check(adapter)) ? + &qlcnic_sriov_vf_ethtool_ops : &qlcnic_ethtool_ops; netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_IPV6_CSUM | NETIF_F_GRO | @@ -2682,7 +2680,7 @@ err_out_disable_pdev: err_out_maintenance_mode: set_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state); netdev->netdev_ops = &qlcnic_netdev_failed_ops; - SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_failed_ops); + netdev->ethtool_ops = &qlcnic_ethtool_failed_ops; ahw->port_type = QLCNIC_XGBE; if (qlcnic_83xx_check(adapter)) diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index 6e36fe14b848..b40050e03a56 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4770,7 +4770,7 @@ static int qlge_probe(struct pci_dev *pdev, ndev->irq = pdev->irq; ndev->netdev_ops = &qlge_netdev_ops; - SET_ETHTOOL_OPS(ndev, &qlge_ethtool_ops); + ndev->ethtool_ops = &qlge_ethtool_ops; ndev->watchdog_timeo = 10 * HZ; err = register_netdev(ndev); diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index aa1c079f231d..be425ad5e824 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -7125,7 +7125,7 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) for (i = 0; i < ETH_ALEN; i++) dev->dev_addr[i] = RTL_R8(MAC0 + i); - SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops); + dev->ethtool_ops = &rtl8169_ethtool_ops; dev->watchdog_timeo = RTL8169_TX_TIMEOUT; netif_napi_add(dev, &tp->napi, rtl8169_poll, R8169_NAPI_WEIGHT); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 6a9509ccd33b..967314cade95 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2843,7 +2843,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) ndev->netdev_ops = &sh_eth_netdev_ops_tsu; else ndev->netdev_ops = &sh_eth_netdev_ops; - SET_ETHTOOL_OPS(ndev, &sh_eth_ethtool_ops); + ndev->ethtool_ops = &sh_eth_ethtool_ops; ndev->watchdog_timeo = TX_TIMEOUT; /* debug message level */ diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c index 0415fa50eeb7..c0981ae45874 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c @@ -520,5 +520,5 @@ static const struct ethtool_ops sxgbe_ethtool_ops = { void sxgbe_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &sxgbe_ethtool_ops); + netdev->ethtool_ops = &sxgbe_ethtool_ops; } diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 63d595fd3cc5..1e274045970f 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -2248,7 +2248,7 @@ static int efx_register_netdev(struct efx_nic *efx) } else { net_dev->netdev_ops = &efx_farch_netdev_ops; } - SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops); + net_dev->ethtool_ops = &efx_ethtool_ops; net_dev->gso_max_segs = EFX_TSO_MAX_SEGS; rtnl_lock(); diff --git a/drivers/net/ethernet/sis/sis190.c b/drivers/net/ethernet/sis/sis190.c index acbbe48a519c..a86339903b9b 100644 --- a/drivers/net/ethernet/sis/sis190.c +++ b/drivers/net/ethernet/sis/sis190.c @@ -1877,7 +1877,7 @@ static int sis190_init_one(struct pci_dev *pdev, dev->netdev_ops = &sis190_netdev_ops; - SET_ETHTOOL_OPS(dev, &sis190_ethtool_ops); + dev->ethtool_ops = &sis190_ethtool_ops; dev->watchdog_timeo = SIS190_TX_TIMEOUT; spin_lock_init(&tp->lock); diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index c7a4868571f9..6b33127ab352 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -318,7 +318,7 @@ static int smc91c92_probe(struct pcmcia_device *link) /* The SMC91c92-specific entries in the device structure. */ dev->netdev_ops = &smc_netdev_ops; - SET_ETHTOOL_OPS(dev, ðtool_ops); + dev->ethtool_ops = ðtool_ops; dev->watchdog_timeo = TX_TIMEOUT; smc->mii_if.dev = dev; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index c5f9cb85c8ef..c963394ded6c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -784,5 +784,5 @@ static const struct ethtool_ops stmmac_ethtool_ops = { void stmmac_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &stmmac_ethtool_ops); + netdev->ethtool_ops = &stmmac_ethtool_ops; } diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index 2ead87759ab4..38da73a2a886 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -2413,7 +2413,7 @@ static void bdx_set_ethtool_ops(struct net_device *netdev) .get_ethtool_stats = bdx_get_ethtool_stats, }; - SET_ETHTOOL_OPS(netdev, &bdx_ethtool_ops); + netdev->ethtool_ops = &bdx_ethtool_ops; } /** diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 91499be03c6f..e3d871055d63 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1998,7 +1998,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev, ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; ndev->netdev_ops = &cpsw_netdev_ops; - SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops); + ndev->ethtool_ops = &cpsw_ethtool_ops; netif_napi_add(ndev, &priv_sl2->napi, cpsw_poll, CPSW_POLL_WEIGHT); /* register the network device */ @@ -2227,7 +2227,7 @@ static int cpsw_probe(struct platform_device *pdev) ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; ndev->netdev_ops = &cpsw_netdev_ops; - SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops); + ndev->ethtool_ops = &cpsw_ethtool_ops; netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT); /* register the network device */ diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 8f0e69ce07ca..e76eae541151 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1980,7 +1980,7 @@ static int davinci_emac_probe(struct platform_device *pdev) } ndev->netdev_ops = &emac_netdev_ops; - SET_ETHTOOL_OPS(ndev, ðtool_ops); + ndev->ethtool_ops = ðtool_ops; netif_napi_add(ndev, &priv->napi, emac_poll, EMAC_POLL_WEIGHT); /* register the network device */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 1de3ef5dd5d2..2e967a7bdb33 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -810,7 +810,7 @@ static int netvsc_probe(struct hv_device *dev, net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_TSO; - SET_ETHTOOL_OPS(net, ðtool_ops); + net->ethtool_ops = ðtool_ops; SET_NETDEV_DEV(net, &dev->device); /* Notify the netvsc driver of the new device */ diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index 63aa9d9e34c5..27536aa89199 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -348,7 +348,7 @@ static int ntb_netdev_probe(struct pci_dev *pdev) memcpy(ndev->dev_addr, ndev->perm_addr, ndev->addr_len); ndev->netdev_ops = &ntb_netdev_ops; - SET_ETHTOOL_OPS(ndev, &ntb_ethtool_ops); + ndev->ethtool_ops = &ntb_ethtool_ops; dev->qp = ntb_transport_create_queue(ndev, pdev, &ntb_netdev_handlers); if (!dev->qp) { diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index a8497183ff8b..dac7a0d9bb46 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -494,7 +494,7 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) ndev->mtu = RIO_MAX_MSG_SIZE - 14; ndev->features = NETIF_F_LLTX; SET_NETDEV_DEV(ndev, &mport->dev); - SET_ETHTOOL_OPS(ndev, &rionet_ethtool_ops); + ndev->ethtool_ops = &rionet_ethtool_ops; spin_lock_init(&rnet->lock); spin_lock_init(&rnet->tx_lock); diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 630caf48f63a..8cfc3bb0c6a6 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -793,7 +793,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id netdev->netdev_ops = &catc_netdev_ops; netdev->watchdog_timeo = TX_TIMEOUT; - SET_ETHTOOL_OPS(netdev, &ops); + netdev->ethtool_ops = &ops; catc->usbdev = usbdev; catc->netdev = netdev; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 660bd5ea9fc0..a3a05869309d 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2425,7 +2425,7 @@ static void hso_net_init(struct net_device *net) net->type = ARPHRD_NONE; net->mtu = DEFAULT_MTU - 14; net->tx_queue_len = 10; - SET_ETHTOOL_OPS(net, &ops); + net->ethtool_ops = &ops; /* and initialize the semaphore */ spin_lock_init(&hso_net->net_lock); diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index 421934c83f1c..f72570708edb 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -524,7 +524,7 @@ static int ipheth_probe(struct usb_interface *intf, usb_set_intfdata(intf, dev); SET_NETDEV_DEV(netdev, &intf->dev); - SET_ETHTOOL_OPS(netdev, &ops); + netdev->ethtool_ops = &ops; retval = register_netdev(netdev); if (retval) { diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index a359d3bb7c5b..dcb6d33141e0 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -1171,7 +1171,7 @@ err_fw: netdev->netdev_ops = &kaweth_netdev_ops; netdev->watchdog_timeo = KAWETH_TX_TIMEOUT; netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size); - SET_ETHTOOL_OPS(netdev, &ops); + netdev->ethtool_ops = &ops; /* kaweth is zeroed as part of alloc_netdev */ INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl); diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 03e8a15d7deb..f84080215915 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1159,7 +1159,7 @@ static int pegasus_probe(struct usb_interface *intf, net->watchdog_timeo = PEGASUS_TX_TIMEOUT; net->netdev_ops = &pegasus_netdev_ops; - SET_ETHTOOL_OPS(net, &ops); + net->ethtool_ops = &ops; pegasus->mii.dev = net; pegasus->mii.mdio_read = mdio_read; pegasus->mii.mdio_write = mdio_write; diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 3fbfb0869030..9f91c7aba4b0 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -3452,7 +3452,7 @@ static int rtl8152_probe(struct usb_interface *intf, NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6; - SET_ETHTOOL_OPS(netdev, &ops); + netdev->ethtool_ops = &ops; netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); tp->mii.dev = netdev; diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index da2c4583bd2d..6e87e5710048 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -878,7 +878,7 @@ static int rtl8150_probe(struct usb_interface *intf, dev->netdev = netdev; netdev->netdev_ops = &rtl8150_netdev_ops; netdev->watchdog_timeo = RTL8150_TX_TIMEOUT; - SET_ETHTOOL_OPS(netdev, &ops); + netdev->ethtool_ops = &ops; dev->intr_interval = 100; /* 100ms */ if (!alloc_all_urbs(dev)) { diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index b852bb368acc..7d9f84a91f37 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1646,7 +1646,7 @@ static int virtnet_probe(struct virtio_device *vdev) dev->netdev_ops = &virtnet_netdev; dev->features = NETIF_F_HIGHDMA; - SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops); + dev->ethtool_ops = &virtnet_ethtool_ops; SET_NETDEV_DEV(dev, &vdev->dev); /* Do we support "hardware" checksums? */ diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index 600ab56c0008..00e120296e92 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -635,5 +635,5 @@ static const struct ethtool_ops vmxnet3_ethtool_ops = { void vmxnet3_set_ethtool_ops(struct net_device *netdev) { - SET_ETHTOOL_OPS(netdev, &vmxnet3_ethtool_ops); + netdev->ethtool_ops = &vmxnet3_ethtool_ops; } diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 1dfee9a7fbf7..e68c8eb4ea8e 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2716,7 +2716,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, return -EEXIST; } - SET_ETHTOOL_OPS(dev, &vxlan_ethtool_ops); + dev->ethtool_ops = &vxlan_ethtool_ops; /* create an fdb entry for a valid default destination */ if (!vxlan_addr_any(&vxlan->default_dst.remote_ip)) { diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index 67db34e56d7e..52919ad42726 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -882,7 +882,7 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local, dev->mtu = local->mtu; - SET_ETHTOOL_OPS(dev, &prism2_ethtool_ops); + dev->ethtool_ops = &prism2_ethtool_ops; } diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index ef05c5c49d41..a7557331699f 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -386,7 +386,7 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6; dev->features = dev->hw_features | NETIF_F_RXCSUM; - SET_ETHTOOL_OPS(dev, &xenvif_ethtool_ops); + dev->ethtool_ops = &xenvif_ethtool_ops; dev->tx_queue_len = XENVIF_QUEUE_LENGTH; diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 158b5e639fc7..895355de8ac4 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1332,7 +1332,7 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) */ netdev->features |= netdev->hw_features; - SET_ETHTOOL_OPS(netdev, &xennet_ethtool_ops); + netdev->ethtool_ops = &xennet_ethtool_ops; SET_NETDEV_DEV(netdev, &dev->dev); netif_set_gso_max_size(netdev, XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index e232ceca38fe..5ef5b4f45758 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -969,10 +969,9 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->mtu = card->info.initial_mtu; card->dev->netdev_ops = &qeth_l2_netdev_ops; - if (card->info.type != QETH_CARD_TYPE_OSN) - SET_ETHTOOL_OPS(card->dev, &qeth_l2_ethtool_ops); - else - SET_ETHTOOL_OPS(card->dev, &qeth_l2_osn_ops); + card->dev->ethtool_ops = + (card->info.type != QETH_CARD_TYPE_OSN) ? + &qeth_l2_ethtool_ops : &qeth_l2_osn_ops; card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; card->info.broadcast_capable = 1; qeth_l2_request_initial_mac(card); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index bc2499a24884..c58f82af3658 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3301,7 +3301,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) card->dev->ml_priv = card; card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->mtu = card->info.initial_mtu; - SET_ETHTOOL_OPS(card->dev, &qeth_l3_ethtool_ops); + card->dev->ethtool_ops = &qeth_l3_ethtool_ops; card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; diff --git a/drivers/staging/et131x/et131x.c b/drivers/staging/et131x/et131x.c index d329cf314360..15e0f4da3ce0 100644 --- a/drivers/staging/et131x/et131x.c +++ b/drivers/staging/et131x/et131x.c @@ -4604,7 +4604,7 @@ static int et131x_pci_setup(struct pci_dev *pdev, netdev->netdev_ops = &et131x_netdev_ops; SET_NETDEV_DEV(netdev, &pdev->dev); - SET_ETHTOOL_OPS(netdev, &et131x_ethtool_ops); + netdev->ethtool_ops = &et131x_ethtool_ops; adapter = et131x_adapter_init(netdev, pdev); diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c index d6421b9b5981..a6158bef58e5 100644 --- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c @@ -2249,7 +2249,7 @@ struct net_device *init_ft1000_card(struct pcmcia_device *link, ft1000InitProc(dev); ft1000_card_present = 1; - SET_ETHTOOL_OPS(dev, &ops); + dev->ethtool_ops = &ops; printk(KERN_INFO "ft1000: %s: addr 0x%04lx irq %d, MAC addr %pM\n", dev->name, dev->base_addr, dev->irq, dev->dev_addr); return dev; diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c index c83e3375104b..9d957615e32a 100644 --- a/drivers/staging/netlogic/xlr_net.c +++ b/drivers/staging/netlogic/xlr_net.c @@ -1066,7 +1066,7 @@ static int xlr_net_probe(struct platform_device *pdev) xlr_set_rx_mode(ndev); priv->num_rx_desc += MAX_NUM_DESC_SPILL; - SET_ETHTOOL_OPS(ndev, &xlr_ethtool_ops); + ndev->ethtool_ops = &xlr_ethtool_ops; SET_NETDEV_DEV(ndev, &pdev->dev); /* Common registers, do one time initialization */ diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index ff7214aac9dd..da9dd6bc5660 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -469,7 +469,7 @@ int cvm_oct_common_init(struct net_device *dev) /* We do our own locking, Linux doesn't need to */ dev->features |= NETIF_F_LLTX; - SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops); + dev->ethtool_ops = &cvm_oct_ethtool_ops; cvm_oct_phy_setup_device(dev); cvm_oct_set_mac_filter(dev); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index b7d4f82872b7..ce8e28146162 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -793,7 +793,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, net->netdev_ops = ð_netdev_ops; - SET_ETHTOOL_OPS(net, &ops); + net->ethtool_ops = &ops; dev->gadget = g; SET_NETDEV_DEV(net, &g->dev); @@ -850,7 +850,7 @@ struct net_device *gether_setup_name_default(const char *netname) net->netdev_ops = ð_netdev_ops; - SET_ETHTOOL_OPS(net, &ops); + net->ethtool_ops = &ops; SET_NETDEV_DEVTYPE(net, &gadget_type); return net; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index adc4658e9873..2dea98cbbdba 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -56,9 +56,6 @@ struct device; struct phy_device; /* 802.11 specific */ struct wireless_dev; - /* source back-compat hooks */ -#define SET_ETHTOOL_OPS(netdev,ops) \ - ( (netdev)->ethtool_ops = (ops) ) void netdev_set_default_ethtool_ops(struct net_device *dev, const struct ethtool_ops *ops); diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 744a59b85e15..e7ee65dc20bf 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -884,7 +884,7 @@ static void batadv_softif_init_early(struct net_device *dev) /* generate random address */ eth_hw_addr_random(dev); - SET_ETHTOOL_OPS(dev, &batadv_ethtool_ops); + dev->ethtool_ops = &batadv_ethtool_ops; memset(priv, 0, sizeof(*priv)); } diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 3e2da2cb72db..9212015abc8f 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -348,7 +348,7 @@ void br_dev_setup(struct net_device *dev) dev->netdev_ops = &br_netdev_ops; dev->destructor = br_dev_free; - SET_ETHTOOL_OPS(dev, &br_ethtool_ops); + dev->ethtool_ops = &br_ethtool_ops; SET_NETDEV_DEVTYPE(dev, &br_type); dev->tx_queue_len = 0; dev->priv_flags = IFF_EBRIDGE; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 02c0e1716f64..64c5af0a10dd 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -346,7 +346,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, return slave_dev; slave_dev->features = master->vlan_features; - SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops); + slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; eth_hw_addr_inherit(slave_dev, master); slave_dev->tx_queue_len = 0; diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index 729c68763fe7..789af9280e77 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -130,7 +130,7 @@ static void do_setup(struct net_device *netdev) netdev->priv_flags &= ~IFF_TX_SKB_SHARING; netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE; netdev->destructor = internal_dev_destructor; - SET_ETHTOOL_OPS(netdev, &internal_dev_ethtool_ops); + netdev->ethtool_ops = &internal_dev_ethtool_ops; netdev->tx_queue_len = 0; netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST | -- cgit v1.2.3 From 50a0ffaf75e9d2d97200b523f2c600f40e9756b1 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 11 May 2014 10:47:15 +0200 Subject: net: cdc_ncm/cdc_mbim: rework probing of NCM/MBIM functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NCM class match in the cdc_mbim driver is confusing and cause unexpected behaviour. The USB core guarantees that a USB interface is in altsetting 0 when probing starts. This means that devices implementing a NCM 1.0 backwards compatible MBIM function (a "NCM/MBIM function") always hit the NCM entry in the cdc_mbim driver match table. Such functions will never match any of the MBIM entries. This causes unexpeced behaviour for cases where the NCM and MBIM entries are differet, which is currently the case for all except Ericsson devices. Improve the probing of NCM/MBIM functions by looking up the device again in the cdc_mbim match table after switching to the MBIM identity. The shared altsetting selection is updated to better accommodate the new probing logic, returning the preferred altsetting for the control interface instead of the data interface. The control interface altsetting update is moved to the cdc_mbim driver. It is never necessary to change the control interface altsetting for NCM. Cc: Greg Suarez Reported by: Yu-an Shih Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_mbim.c | 43 +++++++++++++++++++++++++++++++++++++++++-- drivers/net/usb/cdc_ncm.c | 27 +++++++++++++-------------- include/linux/usb/cdc_ncm.h | 2 +- 3 files changed, 55 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 80d27719ba38..bc23273d0455 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -107,15 +107,54 @@ static const struct net_device_ops cdc_mbim_netdev_ops = { .ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid, }; +/* Change the control interface altsetting and update the .driver_info + * pointer if the matching entry after changing class codes points to + * a different struct + */ +static int cdc_mbim_set_ctrlalt(struct usbnet *dev, struct usb_interface *intf, u8 alt) +{ + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + const struct usb_device_id *id; + struct driver_info *info; + int ret; + + ret = usb_set_interface(dev->udev, + intf->cur_altsetting->desc.bInterfaceNumber, + alt); + if (ret) + return ret; + + id = usb_match_id(intf, driver->id_table); + if (!id) + return -ENODEV; + + info = (struct driver_info *)id->driver_info; + if (info != dev->driver_info) { + dev_dbg(&intf->dev, "driver_info updated to '%s'\n", + info->description); + dev->driver_info = info; + } + return 0; +} + static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) { struct cdc_ncm_ctx *ctx; struct usb_driver *subdriver = ERR_PTR(-ENODEV); int ret = -ENODEV; - u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf); + u8 data_altsetting = 1; struct cdc_mbim_state *info = (void *)&dev->data; - /* Probably NCM, defer for cdc_ncm_bind */ + /* should we change control altsetting on a NCM/MBIM function? */ + if (cdc_ncm_select_altsetting(intf) == CDC_NCM_COMM_ALTSETTING_MBIM) { + data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM; + ret = cdc_mbim_set_ctrlalt(dev, intf, CDC_NCM_COMM_ALTSETTING_MBIM); + if (ret) + goto err; + ret = -ENODEV; + } + + /* we will hit this for NCM/MBIM functions if prefer_mbim is false */ if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) goto err; diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 9a2bd11943eb..d23bca57a23f 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -541,10 +541,10 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) } EXPORT_SYMBOL_GPL(cdc_ncm_unbind); -/* Select the MBIM altsetting iff it is preferred and available, - * returning the number of the corresponding data interface altsetting +/* Return the number of the MBIM control interface altsetting iff it + * is preferred and available, */ -u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf) +u8 cdc_ncm_select_altsetting(struct usb_interface *intf) { struct usb_host_interface *alt; @@ -563,15 +563,15 @@ u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf) * the rules given in section 6 (USB Device Model) of this * specification." */ - if (prefer_mbim && intf->num_altsetting == 2) { + if (intf->num_altsetting < 2) + return intf->cur_altsetting->desc.bAlternateSetting; + + if (prefer_mbim) { alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM); - if (alt && cdc_ncm_comm_intf_is_mbim(alt) && - !usb_set_interface(dev->udev, - intf->cur_altsetting->desc.bInterfaceNumber, - CDC_NCM_COMM_ALTSETTING_MBIM)) - return CDC_NCM_DATA_ALTSETTING_MBIM; + if (alt && cdc_ncm_comm_intf_is_mbim(alt)) + return CDC_NCM_COMM_ALTSETTING_MBIM; } - return CDC_NCM_DATA_ALTSETTING_NCM; + return CDC_NCM_COMM_ALTSETTING_NCM; } EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); @@ -580,12 +580,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) int ret; /* MBIM backwards compatible function? */ - cdc_ncm_select_altsetting(dev, intf); - if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) + if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM) return -ENODEV; - /* NCM data altsetting is always 1 */ - ret = cdc_ncm_bind_common(dev, intf, 1); + /* The NCM data altsetting is fixed */ + ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM); /* * We should get an event when network connection is "connected" or diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 44b38b92236a..55b6feead93b 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -121,7 +121,7 @@ struct cdc_ncm_ctx { u16 connected; }; -u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf); +u8 cdc_ncm_select_altsetting(struct usb_interface *intf); int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting); void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); -- cgit v1.2.3 From 5b7ed0892f2af4e60b9a8d2c71c77774512a6cb9 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Sun, 11 May 2014 20:22:09 -0700 Subject: tcp: move fastopen functions to tcp_fastopen.c Move common TFO functions that will be used by both v4 and v6 to tcp_fastopen.c. Create a helper tcp_fastopen_queue_check(). Signed-off-by: Yuchung Cheng Signed-off-by: Daniel Lee Signed-off-by: Jerry Chu Signed-off-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 10 ++- net/ipv4/tcp_fastopen.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 185 +--------------------------------------------- 3 files changed, 200 insertions(+), 185 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 3c9418456640..012236838583 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1329,8 +1329,14 @@ void tcp_free_fastopen_req(struct tcp_sock *tp); extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; int tcp_fastopen_reset_cipher(void *key, unsigned int len); -void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, - struct tcp_fastopen_cookie *foc); +int tcp_fastopen_create_child(struct sock *sk, + struct sk_buff *skb, + struct sk_buff *skb_synack, + struct request_sock *req); +bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct tcp_fastopen_cookie *foc, + struct tcp_fastopen_cookie *valid_foc); void tcp_fastopen_init_key_once(bool publish); #define TCP_FASTOPEN_KEY_LENGTH 16 diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index f195d9316e55..0606c91d9d0b 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -94,3 +94,193 @@ void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, } rcu_read_unlock(); } + +int tcp_fastopen_create_child(struct sock *sk, + struct sk_buff *skb, + struct sk_buff *skb_synack, + struct request_sock *req) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; + const struct inet_request_sock *ireq = inet_rsk(req); + struct sock *child; + int err; + + req->num_retrans = 0; + req->num_timeout = 0; + req->sk = NULL; + + child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); + if (child == NULL) { + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPFASTOPENPASSIVEFAIL); + kfree_skb(skb_synack); + return -1; + } + err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr, + ireq->ir_rmt_addr, ireq->opt); + err = net_xmit_eval(err); + if (!err) + tcp_rsk(req)->snt_synack = tcp_time_stamp; + /* XXX (TFO) - is it ok to ignore error and continue? */ + + spin_lock(&queue->fastopenq->lock); + queue->fastopenq->qlen++; + spin_unlock(&queue->fastopenq->lock); + + /* Initialize the child socket. Have to fix some values to take + * into account the child is a Fast Open socket and is created + * only out of the bits carried in the SYN packet. + */ + tp = tcp_sk(child); + + tp->fastopen_rsk = req; + /* Do a hold on the listner sk so that if the listener is being + * closed, the child that has been accepted can live on and still + * access listen_lock. + */ + sock_hold(sk); + tcp_rsk(req)->listener = sk; + + /* RFC1323: The window in SYN & SYN/ACK segments is never + * scaled. So correct it appropriately. + */ + tp->snd_wnd = ntohs(tcp_hdr(skb)->window); + + /* Activate the retrans timer so that SYNACK can be retransmitted. + * The request socket is not added to the SYN table of the parent + * because it's been added to the accept queue directly. + */ + inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS, + TCP_TIMEOUT_INIT, TCP_RTO_MAX); + + /* Add the child socket directly into the accept queue */ + inet_csk_reqsk_queue_add(sk, req, child); + + /* Now finish processing the fastopen child socket. */ + inet_csk(child)->icsk_af_ops->rebuild_header(child); + tcp_init_congestion_control(child); + tcp_mtup_init(child); + tcp_init_metrics(child); + tcp_init_buffer_space(child); + + /* Queue the data carried in the SYN packet. We need to first + * bump skb's refcnt because the caller will attempt to free it. + * + * XXX (TFO) - we honor a zero-payload TFO request for now. + * (Any reason not to?) + */ + if (TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq + 1) { + /* Don't queue the skb if there is no payload in SYN. + * XXX (TFO) - How about SYN+FIN? + */ + tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + } else { + 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->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + tp->syn_data_acked = 1; + } + sk->sk_data_ready(sk); + bh_unlock_sock(child); + sock_put(child); + WARN_ON(req->sk == NULL); + return 0; +} +EXPORT_SYMBOL(tcp_fastopen_create_child); + +static bool tcp_fastopen_queue_check(struct sock *sk) +{ + struct fastopen_queue *fastopenq; + + /* Make sure the listener has enabled fastopen, and we don't + * exceed the max # of pending TFO requests allowed before trying + * to validating the cookie in order to avoid burning CPU cycles + * unnecessarily. + * + * XXX (TFO) - The implication of checking the max_qlen before + * processing a cookie request is that clients can't differentiate + * between qlen overflow causing Fast Open to be disabled + * temporarily vs a server not supporting Fast Open at all. + */ + fastopenq = inet_csk(sk)->icsk_accept_queue.fastopenq; + if (fastopenq == NULL || fastopenq->max_qlen == 0) + return false; + + if (fastopenq->qlen >= fastopenq->max_qlen) { + struct request_sock *req1; + spin_lock(&fastopenq->lock); + req1 = fastopenq->rskq_rst_head; + if ((req1 == NULL) || time_after(req1->expires, jiffies)) { + spin_unlock(&fastopenq->lock); + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPFASTOPENLISTENOVERFLOW); + return false; + } + fastopenq->rskq_rst_head = req1->dl_next; + fastopenq->qlen--; + spin_unlock(&fastopenq->lock); + reqsk_free(req1); + } + return true; +} + +bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct tcp_fastopen_cookie *foc, + struct tcp_fastopen_cookie *valid_foc) +{ + bool skip_cookie = false; + + if (likely(!fastopen_cookie_present(foc))) { + /* See include/net/tcp.h for the meaning of these knobs */ + if ((sysctl_tcp_fastopen & TFO_SERVER_ALWAYS) || + ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD) && + (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1))) + skip_cookie = true; /* no cookie to validate */ + else + return false; + } + /* A FO option is present; bump the counter. */ + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE); + + if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) == 0 || + !tcp_fastopen_queue_check(sk)) + return false; + + if (skip_cookie) { + tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + return true; + } + + if (foc->len == TCP_FASTOPEN_COOKIE_SIZE) { + if ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_CHKED) == 0) { + tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, valid_foc); + if ((valid_foc->len != TCP_FASTOPEN_COOKIE_SIZE) || + memcmp(&foc->val[0], &valid_foc->val[0], + TCP_FASTOPEN_COOKIE_SIZE) != 0) + return false; + valid_foc->len = -1; + } + /* Acknowledge the data received from the peer. */ + tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + return true; + } else if (foc->len == 0) { /* Client requesting a cookie */ + tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, valid_foc); + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPFASTOPENCOOKIEREQD); + } else { + /* Client sent a cookie with wrong size. Treat it + * the same as invalid and return a valid one. + */ + tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, valid_foc); + } + return false; +} +EXPORT_SYMBOL(tcp_fastopen_check); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ad166dcc278f..032fcaee164a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1260,187 +1260,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { }; #endif -static bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct tcp_fastopen_cookie *foc, - struct tcp_fastopen_cookie *valid_foc) -{ - bool skip_cookie = false; - struct fastopen_queue *fastopenq; - - if (likely(!fastopen_cookie_present(foc))) { - /* See include/net/tcp.h for the meaning of these knobs */ - if ((sysctl_tcp_fastopen & TFO_SERVER_ALWAYS) || - ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD) && - (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1))) - skip_cookie = true; /* no cookie to validate */ - else - return false; - } - fastopenq = inet_csk(sk)->icsk_accept_queue.fastopenq; - /* A FO option is present; bump the counter. */ - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE); - - /* Make sure the listener has enabled fastopen, and we don't - * exceed the max # of pending TFO requests allowed before trying - * to validating the cookie in order to avoid burning CPU cycles - * unnecessarily. - * - * XXX (TFO) - The implication of checking the max_qlen before - * processing a cookie request is that clients can't differentiate - * between qlen overflow causing Fast Open to be disabled - * temporarily vs a server not supporting Fast Open at all. - */ - if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) == 0 || - fastopenq == NULL || fastopenq->max_qlen == 0) - return false; - - if (fastopenq->qlen >= fastopenq->max_qlen) { - struct request_sock *req1; - spin_lock(&fastopenq->lock); - req1 = fastopenq->rskq_rst_head; - if ((req1 == NULL) || time_after(req1->expires, jiffies)) { - spin_unlock(&fastopenq->lock); - NET_INC_STATS_BH(sock_net(sk), - LINUX_MIB_TCPFASTOPENLISTENOVERFLOW); - /* Avoid bumping LINUX_MIB_TCPFASTOPENPASSIVEFAIL*/ - foc->len = -1; - return false; - } - fastopenq->rskq_rst_head = req1->dl_next; - fastopenq->qlen--; - spin_unlock(&fastopenq->lock); - reqsk_free(req1); - } - if (skip_cookie) { - tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - return true; - } - - if (foc->len == TCP_FASTOPEN_COOKIE_SIZE) { - if ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_CHKED) == 0) { - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, valid_foc); - if ((valid_foc->len != TCP_FASTOPEN_COOKIE_SIZE) || - memcmp(&foc->val[0], &valid_foc->val[0], - TCP_FASTOPEN_COOKIE_SIZE) != 0) - return false; - valid_foc->len = -1; - } - /* Acknowledge the data received from the peer. */ - tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - return true; - } else if (foc->len == 0) { /* Client requesting a cookie */ - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, valid_foc); - NET_INC_STATS_BH(sock_net(sk), - LINUX_MIB_TCPFASTOPENCOOKIEREQD); - } else { - /* Client sent a cookie with wrong size. Treat it - * the same as invalid and return a valid one. - */ - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, valid_foc); - } - return false; -} - -static int tcp_v4_conn_req_fastopen(struct sock *sk, - struct sk_buff *skb, - struct sk_buff *skb_synack, - struct request_sock *req) -{ - struct tcp_sock *tp = tcp_sk(sk); - struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; - const struct inet_request_sock *ireq = inet_rsk(req); - struct sock *child; - int err; - - req->num_retrans = 0; - req->num_timeout = 0; - req->sk = NULL; - - child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); - if (child == NULL) { - NET_INC_STATS_BH(sock_net(sk), - LINUX_MIB_TCPFASTOPENPASSIVEFAIL); - kfree_skb(skb_synack); - return -1; - } - err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr, - ireq->ir_rmt_addr, ireq->opt); - err = net_xmit_eval(err); - if (!err) - tcp_rsk(req)->snt_synack = tcp_time_stamp; - /* XXX (TFO) - is it ok to ignore error and continue? */ - - spin_lock(&queue->fastopenq->lock); - queue->fastopenq->qlen++; - spin_unlock(&queue->fastopenq->lock); - - /* Initialize the child socket. Have to fix some values to take - * into account the child is a Fast Open socket and is created - * only out of the bits carried in the SYN packet. - */ - tp = tcp_sk(child); - - tp->fastopen_rsk = req; - /* Do a hold on the listner sk so that if the listener is being - * closed, the child that has been accepted can live on and still - * access listen_lock. - */ - sock_hold(sk); - tcp_rsk(req)->listener = sk; - - /* RFC1323: The window in SYN & SYN/ACK segments is never - * scaled. So correct it appropriately. - */ - tp->snd_wnd = ntohs(tcp_hdr(skb)->window); - - /* Activate the retrans timer so that SYNACK can be retransmitted. - * The request socket is not added to the SYN table of the parent - * because it's been added to the accept queue directly. - */ - inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS, - TCP_TIMEOUT_INIT, TCP_RTO_MAX); - - /* Add the child socket directly into the accept queue */ - inet_csk_reqsk_queue_add(sk, req, child); - - /* Now finish processing the fastopen child socket. */ - inet_csk(child)->icsk_af_ops->rebuild_header(child); - tcp_init_congestion_control(child); - tcp_mtup_init(child); - tcp_init_metrics(child); - tcp_init_buffer_space(child); - - /* Queue the data carried in the SYN packet. We need to first - * bump skb's refcnt because the caller will attempt to free it. - * - * XXX (TFO) - we honor a zero-payload TFO request for now. - * (Any reason not to?) - */ - if (TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq + 1) { - /* Don't queue the skb if there is no payload in SYN. - * XXX (TFO) - How about SYN+FIN? - */ - tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - } else { - 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->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - tp->syn_data_acked = 1; - } - sk->sk_data_ready(sk); - bh_unlock_sock(child); - sock_put(child); - WARN_ON(req->sk == NULL); - return 0; -} - int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) { struct tcp_options_received tmp_opt; @@ -1599,8 +1418,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (fastopen_cookie_present(&foc) && foc.len != 0) NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL); - } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req)) - goto drop_and_free; + } else if (tcp_fastopen_create_child(sk, skb, skb_synack, req)) + goto drop_and_release; return 0; -- cgit v1.2.3 From 89278c9dc922272df921042aafa18311f3398c6c Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Sun, 11 May 2014 20:22:10 -0700 Subject: tcp: simplify fast open cookie processing Consolidate various cookie checking and generation code to simplify the fast open processing. The main goal is to reduce code duplication in tcp_v4_conn_request() for IPv6 support. Removes two experimental sysctl flags TFO_SERVER_ALWAYS and TFO_SERVER_COOKIE_NOT_CHKD used primarily for developmental debugging purposes. Signed-off-by: Yuchung Cheng Signed-off-by: Daniel Lee Signed-off-by: Jerry Chu Signed-off-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 5 ---- include/net/tcp.h | 9 +------ net/ipv4/tcp_fastopen.c | 71 +++++++++++++++++++------------------------------ net/ipv4/tcp_ipv4.c | 10 +++---- net/ipv4/tcp_output.c | 2 +- 5 files changed, 33 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4e37c71ecd74..bc35e4709e8e 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -366,11 +366,6 @@ static inline bool tcp_passive_fastopen(const struct sock *sk) tcp_sk(sk)->fastopen_rsk != NULL); } -static inline bool fastopen_cookie_present(struct tcp_fastopen_cookie *foc) -{ - return foc->len != -1; -} - extern void tcp_sock_destruct(struct sock *sk); static inline int fastopen_init_queue(struct sock *sk, int backlog) diff --git a/include/net/tcp.h b/include/net/tcp.h index 012236838583..17d7c6a3d037 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -220,8 +220,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TFO_SERVER_ENABLE 2 #define TFO_CLIENT_NO_COOKIE 4 /* Data in SYN w/o cookie option */ -/* Process SYN data but skip cookie validation */ -#define TFO_SERVER_COOKIE_NOT_CHKED 0x100 /* Accept SYN data w/o any cookie option */ #define TFO_SERVER_COOKIE_NOT_REQD 0x200 @@ -230,10 +228,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); */ #define TFO_SERVER_WO_SOCKOPT1 0x400 #define TFO_SERVER_WO_SOCKOPT2 0x800 -/* Always create TFO child sockets on a TFO listener even when - * cookie/data not present. (For testing purpose!) - */ -#define TFO_SERVER_ALWAYS 0x1000 extern struct inet_timewait_death_row tcp_death_row; @@ -1335,8 +1329,7 @@ int tcp_fastopen_create_child(struct sock *sk, struct request_sock *req); bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, struct request_sock *req, - struct tcp_fastopen_cookie *foc, - struct tcp_fastopen_cookie *valid_foc); + struct tcp_fastopen_cookie *foc); void tcp_fastopen_init_key_once(bool publish); #define TCP_FASTOPEN_KEY_LENGTH 16 diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 0606c91d9d0b..5a98277b9a82 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -228,59 +228,44 @@ static bool tcp_fastopen_queue_check(struct sock *sk) return true; } +/* Returns true if we should perform Fast Open on the SYN. The cookie (foc) + * may be updated and return the client in the SYN-ACK later. E.g., Fast Open + * cookie request (foc->len == 0). + */ bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, struct request_sock *req, - struct tcp_fastopen_cookie *foc, - struct tcp_fastopen_cookie *valid_foc) + struct tcp_fastopen_cookie *foc) { - bool skip_cookie = false; - - if (likely(!fastopen_cookie_present(foc))) { - /* See include/net/tcp.h for the meaning of these knobs */ - if ((sysctl_tcp_fastopen & TFO_SERVER_ALWAYS) || - ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD) && - (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1))) - skip_cookie = true; /* no cookie to validate */ - else - return false; - } - /* A FO option is present; bump the counter. */ - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE); + struct tcp_fastopen_cookie valid_foc = { .len = -1 }; + bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1; - if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) == 0 || - !tcp_fastopen_queue_check(sk)) + if (!((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) && + (syn_data || foc->len >= 0) && + tcp_fastopen_queue_check(sk))) { + foc->len = -1; return false; - - if (skip_cookie) { - tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - return true; } - if (foc->len == TCP_FASTOPEN_COOKIE_SIZE) { - if ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_CHKED) == 0) { - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, valid_foc); - if ((valid_foc->len != TCP_FASTOPEN_COOKIE_SIZE) || - memcmp(&foc->val[0], &valid_foc->val[0], - TCP_FASTOPEN_COOKIE_SIZE) != 0) - return false; - valid_foc->len = -1; - } - /* Acknowledge the data received from the peer. */ + if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD)) + goto fastopen; + + tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, &valid_foc); + + if (foc->len == TCP_FASTOPEN_COOKIE_SIZE && + foc->len == valid_foc.len && + !memcmp(foc->val, valid_foc.val, foc->len)) { +fastopen: tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + foc->len = -1; + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE); return true; - } else if (foc->len == 0) { /* Client requesting a cookie */ - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, valid_foc); - NET_INC_STATS_BH(sock_net(sk), - LINUX_MIB_TCPFASTOPENCOOKIEREQD); - } else { - /* Client sent a cookie with wrong size. Treat it - * the same as invalid and return a valid one. - */ - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, valid_foc); } + + NET_INC_STATS_BH(sock_net(sk), foc->len ? + LINUX_MIB_TCPFASTOPENPASSIVEFAIL : + LINUX_MIB_TCPFASTOPENCOOKIEREQD); + *foc = valid_foc; return false; } EXPORT_SYMBOL(tcp_fastopen_check); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 032fcaee164a..5ea0949dadfd 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1273,7 +1273,6 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) bool want_cookie = false; struct flowi4 fl4; struct tcp_fastopen_cookie foc = { .len = -1 }; - struct tcp_fastopen_cookie valid_foc = { .len = -1 }; struct sk_buff *skb_synack; int do_fastopen; @@ -1381,7 +1380,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (dst == NULL) goto drop_and_free; } - do_fastopen = tcp_fastopen_check(sk, skb, req, &foc, &valid_foc); + do_fastopen = !want_cookie && + tcp_fastopen_check(sk, skb, req, &foc); /* We don't call tcp_v4_send_synack() directly because we need * to make sure a child socket can be created successfully before @@ -1394,8 +1394,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) * latter to remove its dependency on the current implementation * of tcp_v4_send_synack()->tcp_select_initial_window(). */ - skb_synack = tcp_make_synack(sk, dst, req, - fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL); + skb_synack = tcp_make_synack(sk, dst, req, &foc); if (skb_synack) { __tcp_v4_send_check(skb_synack, ireq->ir_loc_addr, ireq->ir_rmt_addr); @@ -1415,9 +1414,6 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tcp_rsk(req)->listener = NULL; /* Add the request_sock to the SYN table */ inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); - if (fastopen_cookie_present(&foc) && foc.len != 0) - NET_INC_STATS_BH(sock_net(sk), - LINUX_MIB_TCPFASTOPENPASSIVEFAIL); } else if (tcp_fastopen_create_child(sk, skb, skb_synack, req)) goto drop_and_release; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 694711a140d4..b20fc02920f9 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -627,7 +627,7 @@ static unsigned int tcp_synack_options(struct sock *sk, if (unlikely(!ireq->tstamp_ok)) remaining -= TCPOLEN_SACKPERM_ALIGNED; } - if (foc != NULL) { + if (foc != NULL && foc->len >= 0) { u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len; need = (need + 3) & ~3U; /* Align to 32 bits */ if (remaining >= need) { -- cgit v1.2.3 From 843f4a55e336e6d0c7bb92e7f9621535bc8d5fcd Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Sun, 11 May 2014 20:22:11 -0700 Subject: tcp: use tcp_v4_send_synack on first SYN-ACK To avoid large code duplication in IPv6, we need to first simplify the complicate SYN-ACK sending code in tcp_v4_conn_request(). To use tcp_v4(6)_send_synack() to send all SYN-ACKs, we need to initialize the mini socket's receive window before trying to create the child socket and/or building the SYN-ACK packet. So we move that initialization from tcp_make_synack() to tcp_v4_conn_request() as a new function tcp_openreq_init_req_rwin(). After this refactoring the SYN-ACK sending code is simpler and easier to implement Fast Open for IPv6. Signed-off-by: Yuchung Cheng Signed-off-by: Daniel Lee Signed-off-by: Jerry Chu Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 14 +++++----- net/ipv4/tcp_fastopen.c | 67 ++++++++++++++++++++++-------------------------- net/ipv4/tcp_ipv4.c | 57 ++++++++++++---------------------------- net/ipv4/tcp_minisocks.c | 31 ++++++++++++++++++++++ net/ipv4/tcp_output.c | 21 --------------- 5 files changed, 85 insertions(+), 105 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 17d7c6a3d037..f5d6ca4a9d28 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1114,6 +1114,9 @@ static inline void tcp_openreq_init(struct request_sock *req, ireq->ir_num = ntohs(tcp_hdr(skb)->dest); } +extern void tcp_openreq_init_rwin(struct request_sock *req, + struct sock *sk, struct dst_entry *dst); + void tcp_enter_memory_pressure(struct sock *sk); static inline int keepalive_intvl_when(const struct tcp_sock *tp) @@ -1323,13 +1326,10 @@ void tcp_free_fastopen_req(struct tcp_sock *tp); extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; int tcp_fastopen_reset_cipher(void *key, unsigned int len); -int tcp_fastopen_create_child(struct sock *sk, - struct sk_buff *skb, - struct sk_buff *skb_synack, - struct request_sock *req); -bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct tcp_fastopen_cookie *foc); +bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct tcp_fastopen_cookie *foc, + struct dst_entry *dst); void tcp_fastopen_init_key_once(bool publish); #define TCP_FASTOPEN_KEY_LENGTH 16 diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 5a98277b9a82..9b947a9aaf6e 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -95,34 +95,22 @@ void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, rcu_read_unlock(); } -int tcp_fastopen_create_child(struct sock *sk, - struct sk_buff *skb, - struct sk_buff *skb_synack, - struct request_sock *req) +static bool tcp_fastopen_create_child(struct sock *sk, + struct sk_buff *skb, + struct dst_entry *dst, + struct request_sock *req) { struct tcp_sock *tp = tcp_sk(sk); struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; - const struct inet_request_sock *ireq = inet_rsk(req); struct sock *child; - int err; req->num_retrans = 0; req->num_timeout = 0; req->sk = NULL; child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); - if (child == NULL) { - NET_INC_STATS_BH(sock_net(sk), - LINUX_MIB_TCPFASTOPENPASSIVEFAIL); - kfree_skb(skb_synack); - return -1; - } - err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr, - ireq->ir_rmt_addr, ireq->opt); - err = net_xmit_eval(err); - if (!err) - tcp_rsk(req)->snt_synack = tcp_time_stamp; - /* XXX (TFO) - is it ok to ignore error and continue? */ + if (child == NULL) + return false; spin_lock(&queue->fastopenq->lock); queue->fastopenq->qlen++; @@ -167,28 +155,24 @@ int 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. * - * XXX (TFO) - we honor a zero-payload TFO request for now. - * (Any reason not to?) + * 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) { - /* Don't queue the skb if there is no payload in SYN. - * XXX (TFO) - How about SYN+FIN? - */ - tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - } else { + 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->rcv_nxt = TCP_SKB_CB(skb)->end_seq; tp->syn_data_acked = 1; } + tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; sk->sk_data_ready(sk); bh_unlock_sock(child); sock_put(child); WARN_ON(req->sk == NULL); - return 0; + return true; } EXPORT_SYMBOL(tcp_fastopen_create_child); @@ -232,9 +216,10 @@ static bool tcp_fastopen_queue_check(struct sock *sk) * may be updated and return the client in the SYN-ACK later. E.g., Fast Open * cookie request (foc->len == 0). */ -bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct tcp_fastopen_cookie *foc) +bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct tcp_fastopen_cookie *foc, + struct dst_entry *dst) { struct tcp_fastopen_cookie valid_foc = { .len = -1 }; bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1; @@ -255,11 +240,21 @@ bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, if (foc->len == TCP_FASTOPEN_COOKIE_SIZE && foc->len == valid_foc.len && !memcmp(foc->val, valid_foc.val, foc->len)) { + /* Cookie is valid. Create a (full) child socket to accept + * the data in SYN before returning a SYN-ACK to ack the + * data. If we fail to create the socket, fall back and + * ack the ISN only but includes the same cookie. + * + * Note: Data-less SYN with valid cookie is allowed to send + * data in SYN_RECV state. + */ fastopen: - tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; - foc->len = -1; - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE); - return true; + if (tcp_fastopen_create_child(sk, skb, dst, req)) { + foc->len = -1; + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPFASTOPENPASSIVE); + return true; + } } NET_INC_STATS_BH(sock_net(sk), foc->len ? @@ -268,4 +263,4 @@ fastopen: *foc = valid_foc; return false; } -EXPORT_SYMBOL(tcp_fastopen_check); +EXPORT_SYMBOL(tcp_try_fastopen); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5ea0949dadfd..1665f0f84233 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -822,7 +822,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, */ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - u16 queue_mapping) + u16 queue_mapping, + struct tcp_fastopen_cookie *foc) { const struct inet_request_sock *ireq = inet_rsk(req); struct flowi4 fl4; @@ -833,7 +834,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) return -1; - skb = tcp_make_synack(sk, dst, req, NULL); + skb = tcp_make_synack(sk, dst, req, foc); if (skb) { __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr); @@ -852,7 +853,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) { - int res = tcp_v4_send_synack(sk, NULL, req, 0); + int res = tcp_v4_send_synack(sk, NULL, req, 0, NULL); if (!res) { TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); @@ -1270,11 +1271,10 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) __be32 saddr = ip_hdr(skb)->saddr; __be32 daddr = ip_hdr(skb)->daddr; __u32 isn = TCP_SKB_CB(skb)->when; - bool want_cookie = false; + bool want_cookie = false, fastopen; struct flowi4 fl4; struct tcp_fastopen_cookie foc = { .len = -1 }; - struct sk_buff *skb_synack; - int do_fastopen; + int err; /* Never answer to SYNs send to broadcast or multicast */ if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) @@ -1373,49 +1373,24 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) isn = tcp_v4_init_sequence(skb); } - tcp_rsk(req)->snt_isn = isn; - - if (dst == NULL) { - dst = inet_csk_route_req(sk, &fl4, req); - if (dst == NULL) - goto drop_and_free; - } - do_fastopen = !want_cookie && - tcp_fastopen_check(sk, skb, req, &foc); - - /* We don't call tcp_v4_send_synack() directly because we need - * to make sure a child socket can be created successfully before - * sending back synack! - * - * XXX (TFO) - Ideally one would simply call tcp_v4_send_synack() - * (or better yet, call tcp_send_synack() in the child context - * directly, but will have to fix bunch of other code first) - * after syn_recv_sock() except one will need to first fix the - * latter to remove its dependency on the current implementation - * of tcp_v4_send_synack()->tcp_select_initial_window(). - */ - skb_synack = tcp_make_synack(sk, dst, req, &foc); - - if (skb_synack) { - __tcp_v4_send_check(skb_synack, ireq->ir_loc_addr, ireq->ir_rmt_addr); - skb_set_queue_mapping(skb_synack, skb_get_queue_mapping(skb)); - } else + if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) goto drop_and_free; - if (likely(!do_fastopen)) { - int err; - err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr, - ireq->ir_rmt_addr, ireq->opt); - err = net_xmit_eval(err); + tcp_rsk(req)->snt_isn = isn; + tcp_rsk(req)->snt_synack = tcp_time_stamp; + tcp_openreq_init_rwin(req, sk, dst); + fastopen = !want_cookie && + tcp_try_fastopen(sk, skb, req, &foc, dst); + err = tcp_v4_send_synack(sk, dst, req, + skb_get_queue_mapping(skb), &foc); + if (!fastopen) { if (err || want_cookie) goto drop_and_free; tcp_rsk(req)->snt_synack = tcp_time_stamp; tcp_rsk(req)->listener = NULL; - /* Add the request_sock to the SYN table */ inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); - } else if (tcp_fastopen_create_child(sk, skb, skb_synack, req)) - goto drop_and_release; + } return 0; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 05c1b155251d..e68e0d4af6c9 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -362,6 +362,37 @@ void tcp_twsk_destructor(struct sock *sk) } EXPORT_SYMBOL_GPL(tcp_twsk_destructor); +void tcp_openreq_init_rwin(struct request_sock *req, + struct sock *sk, struct dst_entry *dst) +{ + struct inet_request_sock *ireq = inet_rsk(req); + struct tcp_sock *tp = tcp_sk(sk); + __u8 rcv_wscale; + int mss = dst_metric_advmss(dst); + + if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss) + mss = tp->rx_opt.user_mss; + + /* Set this up on the first call only */ + req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW); + + /* limit the window selection if the user enforce a smaller rx buffer */ + if (sk->sk_userlocks & SOCK_RCVBUF_LOCK && + (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0)) + req->window_clamp = tcp_full_space(sk); + + /* tcp_full_space because it is guaranteed to be the first packet */ + tcp_select_initial_window(tcp_full_space(sk), + mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), + &req->rcv_wnd, + &req->window_clamp, + ireq->wscale_ok, + &rcv_wscale, + dst_metric(dst, RTAX_INITRWND)); + ireq->rcv_wscale = rcv_wscale; +} +EXPORT_SYMBOL(tcp_openreq_init_rwin); + static inline void TCP_ECN_openreq_child(struct tcp_sock *tp, struct request_sock *req) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index b20fc02920f9..3d61c52bdf79 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2803,27 +2803,6 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss) mss = tp->rx_opt.user_mss; - if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */ - __u8 rcv_wscale; - /* Set this up on the first call only */ - req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW); - - /* limit the window selection if the user enforce a smaller rx buffer */ - if (sk->sk_userlocks & SOCK_RCVBUF_LOCK && - (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0)) - req->window_clamp = tcp_full_space(sk); - - /* tcp_full_space because it is guaranteed to be the first packet */ - tcp_select_initial_window(tcp_full_space(sk), - mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), - &req->rcv_wnd, - &req->window_clamp, - ireq->wscale_ok, - &rcv_wscale, - dst_metric(dst, RTAX_INITRWND)); - ireq->rcv_wscale = rcv_wscale; - } - memset(&opts, 0, sizeof(opts)); #ifdef CONFIG_SYN_COOKIES if (unlikely(req->cookie_ts)) -- cgit v1.2.3 From e110861f86094cd78cc85593b873970092deb43a Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 13 May 2014 10:17:33 -0700 Subject: net: add a sysctl to reflect the fwmark on replies Kernel-originated IP packets that have no user socket associated with them (e.g., ICMP errors and echo replies, TCP RSTs, etc.) are emitted with a mark of zero. Add a sysctl to make them have the same mark as the packet they are replying to. This allows an administrator that wishes to do so to use mark-based routing, firewalling, etc. for these replies by marking the original packets inbound. Tested using user-mode linux: - ICMP/ICMPv6 echo replies and errors. - TCP RST packets (IPv4 and IPv6). Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- include/net/ip.h | 3 +++ include/net/ipv6.h | 3 +++ include/net/netns/ipv4.h | 2 ++ include/net/netns/ipv6.h | 1 + net/ipv4/icmp.c | 11 +++++++++-- net/ipv4/ip_output.c | 3 ++- net/ipv4/sysctl_net_ipv4.c | 7 +++++++ net/ipv6/icmp.c | 6 ++++++ net/ipv6/sysctl_net_ipv6.c | 7 +++++++ net/ipv6/tcp_ipv6.c | 1 + 10 files changed, 41 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 55752985c144..14c50a1650ef 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -231,6 +231,9 @@ void ipfrag_init(void); void ip_static_sysctl_init(void); +#define IP4_REPLY_MARK(net, mark) \ + ((net)->ipv4.sysctl_fwmark_reflect ? (mark) : 0) + static inline bool ip_is_fragment(const struct iphdr *iph) { return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 5b40ad297b8c..ba810d0546bc 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -113,6 +113,9 @@ struct frag_hdr { #define IP6_MF 0x0001 #define IP6_OFFSET 0xFFF8 +#define IP6_REPLY_MARK(net, mark) \ + ((net)->ipv6.sysctl.fwmark_reflect ? (mark) : 0) + #include /* sysctls */ diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index b2704fd0ec80..a32fc4d705da 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -77,6 +77,8 @@ struct netns_ipv4 { int sysctl_ip_no_pmtu_disc; int sysctl_ip_fwd_use_pmtu; + int sysctl_fwmark_reflect; + struct ping_group_range ping_group_range; atomic_t dev_addr_genid; diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 21edaf1f7916..19d3446e59d2 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -30,6 +30,7 @@ struct netns_sysctl_ipv6 { int flowlabel_consistency; int icmpv6_time; int anycast_src_echo_reply; + int fwmark_reflect; }; struct netns_ipv6 { diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index fe52666dc43c..79c3d947a481 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -337,6 +337,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) struct sock *sk; struct inet_sock *inet; __be32 daddr, saddr; + u32 mark = IP4_REPLY_MARK(net, skb->mark); if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb)) return; @@ -349,6 +350,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) icmp_param->data.icmph.checksum = 0; inet->tos = ip_hdr(skb)->tos; + sk->sk_mark = mark; daddr = ipc.addr = ip_hdr(skb)->saddr; saddr = fib_compute_spec_dst(skb); ipc.opt = NULL; @@ -364,6 +366,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) memset(&fl4, 0, sizeof(fl4)); fl4.daddr = daddr; fl4.saddr = saddr; + fl4.flowi4_mark = mark; fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); fl4.flowi4_proto = IPPROTO_ICMP; security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); @@ -382,7 +385,7 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4, struct sk_buff *skb_in, const struct iphdr *iph, - __be32 saddr, u8 tos, + __be32 saddr, u8 tos, u32 mark, int type, int code, struct icmp_bxm *param) { @@ -394,6 +397,7 @@ static struct rtable *icmp_route_lookup(struct net *net, fl4->daddr = (param->replyopts.opt.opt.srr ? param->replyopts.opt.opt.faddr : iph->saddr); fl4->saddr = saddr; + fl4->flowi4_mark = mark; fl4->flowi4_tos = RT_TOS(tos); fl4->flowi4_proto = IPPROTO_ICMP; fl4->fl4_icmp_type = type; @@ -491,6 +495,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) struct flowi4 fl4; __be32 saddr; u8 tos; + u32 mark; struct net *net; struct sock *sk; @@ -592,6 +597,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) : iph->tos; + mark = IP4_REPLY_MARK(net, skb_in->mark); if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb_in)) goto out_unlock; @@ -608,13 +614,14 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) icmp_param->skb = skb_in; icmp_param->offset = skb_network_offset(skb_in); inet_sk(sk)->tos = tos; + sk->sk_mark = mark; ipc.addr = iph->saddr; ipc.opt = &icmp_param->replyopts.opt; ipc.tx_flags = 0; ipc.ttl = 0; ipc.tos = -1; - rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, + rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, mark, type, code, icmp_param); if (IS_ERR(rt)) goto out_unlock; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 6aa4380fde1a..6e231ab58d65 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1546,7 +1546,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, daddr = replyopts.opt.opt.faddr; } - flowi4_init_output(&fl4, arg->bound_dev_if, 0, + flowi4_init_output(&fl4, arg->bound_dev_if, + IP4_REPLY_MARK(net, skb->mark), RT_TOS(arg->tos), RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol, ip_reply_arg_flowi_flags(arg), diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 5cde8f263d40..f50d51850285 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -838,6 +838,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "fwmark_reflect", + .data = &init_net.ipv4.sysctl_fwmark_reflect, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 8d3952796d39..f6c84a6eb238 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -400,6 +400,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) int len; int hlimit; int err = 0; + u32 mark = IP6_REPLY_MARK(net, skb->mark); if ((u8 *)hdr < skb->head || (skb_network_header(skb) + sizeof(*hdr)) > skb_tail_pointer(skb)) @@ -466,6 +467,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) fl6.daddr = hdr->saddr; if (saddr) fl6.saddr = *saddr; + fl6.flowi6_mark = mark; fl6.flowi6_oif = iif; fl6.fl6_icmp_type = type; fl6.fl6_icmp_code = code; @@ -474,6 +476,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) sk = icmpv6_xmit_lock(net); if (sk == NULL) return; + sk->sk_mark = mark; np = inet6_sk(sk); if (!icmpv6_xrlim_allow(sk, type, &fl6)) @@ -551,6 +554,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) int err = 0; int hlimit; u8 tclass; + u32 mark = IP6_REPLY_MARK(net, skb->mark); saddr = &ipv6_hdr(skb)->daddr; @@ -569,11 +573,13 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl6.saddr = *saddr; fl6.flowi6_oif = skb->dev->ifindex; fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; + fl6.flowi6_mark = mark; security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); if (sk == NULL) return; + sk->sk_mark = mark; np = inet6_sk(sk); if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 7f405a168822..058f3eca2e53 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -38,6 +38,13 @@ static struct ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "fwmark_reflect", + .data = &init_net.ipv6.sysctl.fwmark_reflect, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3a267bf14f2f..c54976a44425 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -812,6 +812,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, fl6.flowi6_oif = inet6_iif(skb); else fl6.flowi6_oif = oif; + fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark); fl6.fl6_dport = t1->dest; fl6.fl6_sport = t1->source; security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); -- cgit v1.2.3 From 84f39b08d7868ce10eeaf640627cb89777f0ae93 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 13 May 2014 10:17:35 -0700 Subject: net: support marking accepting TCP sockets When using mark-based routing, sockets returned from accept() may need to be marked differently depending on the incoming connection request. This is the case, for example, if different socket marks identify different networks: a listening socket may want to accept connections from all networks, but each connection should be marked with the network that the request came in on, so that subsequent packets are sent on the correct network. This patch adds a sysctl to mark TCP sockets based on the fwmark of the incoming SYN packet. If enabled, and an unmarked socket receives a SYN, then the SYN packet's fwmark is written to the connection's inet_request_sock, and later written back to the accepted socket when the connection is established. If the socket already has a nonzero mark, then the behaviour is the same as it is today, i.e., the listening socket's fwmark is used. Black-box tested using user-mode linux: - IPv4/IPv6 SYN+ACK, FIN, etc. packets are routed based on the mark of the incoming SYN packet. - The socket returned by accept() is marked with the mark of the incoming SYN packet. - Tested with syncookies=1 and syncookies=2. Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- include/net/inet_sock.h | 10 ++++++++++ include/net/netns/ipv4.h | 1 + net/ipv4/inet_connection_sock.c | 6 ++++-- net/ipv4/syncookies.c | 3 ++- net/ipv4/sysctl_net_ipv4.c | 7 +++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv6/inet6_connection_sock.c | 2 +- net/ipv6/syncookies.c | 4 +++- net/ipv6/tcp_ipv6.c | 1 + 9 files changed, 30 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 1833c3f389ee..b1edf17bec01 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -90,6 +90,7 @@ struct inet_request_sock { kmemcheck_bitfield_end(flags); struct ip_options_rcu *opt; struct sk_buff *pktopts; + u32 ir_mark; }; static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) @@ -97,6 +98,15 @@ static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) return (struct inet_request_sock *)sk; } +static inline u32 inet_request_mark(struct sock *sk, struct sk_buff *skb) +{ + if (!sk->sk_mark && sock_net(sk)->ipv4.sysctl_tcp_fwmark_accept) { + return skb->mark; + } else { + return sk->sk_mark; + } +} + struct inet_cork { unsigned int flags; __be32 addr; diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index a32fc4d705da..2f0cfad66666 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -78,6 +78,7 @@ struct netns_ipv4 { int sysctl_ip_fwd_use_pmtu; int sysctl_fwmark_reflect; + int sysctl_tcp_fwmark_accept; struct ping_group_range ping_group_range; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index a56b8e6e866a..12e502cbfdc7 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -408,7 +408,7 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, struct net *net = sock_net(sk); int flags = inet_sk_flowi_flags(sk); - flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, + flowi4_init_output(fl4, sk->sk_bound_dev_if, ireq->ir_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, flags, @@ -445,7 +445,7 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, rcu_read_lock(); opt = rcu_dereference(newinet->inet_opt); - flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, + flowi4_init_output(fl4, sk->sk_bound_dev_if, inet_rsk(req)->ir_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, @@ -680,6 +680,8 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, inet_sk(newsk)->inet_sport = htons(inet_rsk(req)->ir_num); newsk->sk_write_space = sk_stream_write_space; + newsk->sk_mark = inet_rsk(req)->ir_mark; + newicsk->icsk_retransmits = 0; newicsk->icsk_backoff = 0; newicsk->icsk_probes_out = 0; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index f2ed13c2125f..c86624b36a62 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -303,6 +303,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, ireq->ir_rmt_port = th->source; ireq->ir_loc_addr = ip_hdr(skb)->daddr; ireq->ir_rmt_addr = ip_hdr(skb)->saddr; + ireq->ir_mark = inet_request_mark(sk, skb); ireq->ecn_ok = ecn_ok; ireq->snd_wscale = tcp_opt.snd_wscale; ireq->sack_ok = tcp_opt.sack_ok; @@ -339,7 +340,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, * hasn't changed since we received the original syn, but I see * no easy way to do this. */ - flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark, + flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr, diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index f50d51850285..a33b9fbc1d80 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -845,6 +845,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_fwmark_accept", + .data = &init_net.ipv4.sysctl_tcp_fwmark_accept, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a2780e5334c9..77cccda1ad0c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1318,6 +1318,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) ireq->ir_rmt_addr = saddr; ireq->no_srccheck = inet_sk(sk)->transparent; ireq->opt = tcp_v4_save_options(skb); + ireq->ir_mark = inet_request_mark(sk, skb); if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index d4ade34ab375..a245e5ddffbd 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -81,7 +81,7 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, final_p = fl6_update_dst(fl6, np->opt, &final); fl6->saddr = ireq->ir_v6_loc_addr; fl6->flowi6_oif = ireq->ir_iif; - fl6->flowi6_mark = sk->sk_mark; + fl6->flowi6_mark = ireq->ir_mark; fl6->fl6_dport = ireq->ir_rmt_port; fl6->fl6_sport = htons(ireq->ir_num); security_req_classify_flow(req, flowi6_to_flowi(fl6)); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index bb53a5e73c1a..a822b880689b 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -216,6 +216,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) ireq->ir_iif = inet6_iif(skb); + ireq->ir_mark = inet_request_mark(sk, skb); + req->expires = 0UL; req->num_retrans = 0; ireq->ecn_ok = ecn_ok; @@ -242,7 +244,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) final_p = fl6_update_dst(&fl6, np->opt, &final); fl6.saddr = ireq->ir_v6_loc_addr; fl6.flowi6_oif = sk->sk_bound_dev_if; - fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_mark = ireq->ir_mark; fl6.fl6_dport = ireq->ir_rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; security_req_classify_flow(req, flowi6_to_flowi(&fl6)); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c54976a44425..f07b2abba359 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1034,6 +1034,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) TCP_ECN_create_request(req, skb, sock_net(sk)); ireq->ir_iif = sk->sk_bound_dev_if; + ireq->ir_mark = inet_request_mark(sk, skb); /* So that link locals have meaning */ if (!sk->sk_bound_dev_if && -- cgit v1.2.3 From 48353863e2e7edb75d4e518c1a8d1d58c36d4c2c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 17 Feb 2014 08:44:12 -0300 Subject: [media] v4l2-subdev.h: add g_tvnorms video op While there was already a g_tvnorms_output video op, it's counterpart for video capture was missing. Add it. This is necessary for generic bridge drivers like soc-camera to set the video_device tvnorms field correctly. Otherwise ENUMSTD cannot work. Signed-off-by: Hans Verkuil Tested-by: Laurent Pinchart Acked-by: Laurent Pinchart Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 7653194e821a..9205df13f3a5 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -272,8 +272,11 @@ struct v4l2_mbus_frame_desc { g_std_output: get current standard for video OUTPUT devices. This is ignored by video input devices. - g_tvnorms_output: get v4l2_std_id with all standards supported by video - OUTPUT device. This is ignored by video input devices. + g_tvnorms: get v4l2_std_id with all standards supported by the video + CAPTURE device. This is ignored by video output devices. + + g_tvnorms_output: get v4l2_std_id with all standards supported by the video + OUTPUT device. This is ignored by video capture devices. s_crystal_freq: sets the frequency of the crystal used to generate the clocks in Hz. An extra flags field allows device specific configuration @@ -316,6 +319,7 @@ struct v4l2_subdev_video_ops { int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std); + int (*g_tvnorms)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*g_input_status)(struct v4l2_subdev *sd, u32 *status); int (*s_stream)(struct v4l2_subdev *sd, int enable); -- cgit v1.2.3 From cea092c9488cbb22c8b70336ab1413e0daf350f0 Mon Sep 17 00:00:00 2001 From: Brian W Hart Date: Wed, 14 May 2014 10:33:45 +0930 Subject: cpumask.h: silence warning with -Wsign-compare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silence the warning when building with -Wsign-compare when cpumask.h is included: include/linux/cpumask.h: In function ‘cpumask_parse’: include/linux/cpumask.h:603:26: warning: signed and unsigned type in conditional expression [-Wsign-compare] int len = nl ? nl - buf : strlen(buf); ^ V2: Rusty pointed out that unsigned should be used instead. Signed-off-by: Brian W Hart Signed-off-by: Rusty Russell --- include/linux/cpumask.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index d08e4d2a9b92..3557ea7b2049 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -600,7 +600,7 @@ static inline int cpulist_scnprintf(char *buf, int len, static inline int cpumask_parse(const char *buf, struct cpumask *dstp) { char *nl = strchr(buf, '\n'); - int len = nl ? nl - buf : strlen(buf); + unsigned int len = nl ? (unsigned int)(nl - buf) : strlen(buf); return bitmap_parse(buf, len, cpumask_bits(dstp), nr_cpumask_bits); } -- cgit v1.2.3 From 08e53fbdb85c0f6f45c0f7c1ea3defc1752a95ce Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Wed, 14 May 2014 10:33:46 +0930 Subject: virtio-rng: support multiple virtio-rng devices Current hwrng core supports to register multiple hwrng devices, and there is only one device really works in the same time. QEMU alsu supports to have multiple virtio-rng backends. This patch changes virtio-rng driver to support multiple virtio-rng devices. ]# cat /sys/class/misc/hw_random/rng_available virtio_rng.0 virtio_rng.1 ]# cat /sys/class/misc/hw_random/rng_current virtio_rng.0 ]# echo -n virtio_rng.1 > /sys/class/misc/hw_random/rng_current ]# dd if=/dev/hwrng of=/dev/null Signed-off-by: Amos Kong Signed-off-by: Rusty Russell --- drivers/char/hw_random/virtio-rng.c | 102 ++++++++++++++++++++++-------------- include/linux/hw_random.h | 2 +- 2 files changed, 64 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 2ce0e225e58c..12e242bbb0f5 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -25,88 +25,108 @@ #include #include -static struct virtqueue *vq; -static unsigned int data_avail; -static DECLARE_COMPLETION(have_data); -static bool busy; + +struct virtrng_info { + struct virtio_device *vdev; + struct hwrng hwrng; + struct virtqueue *vq; + unsigned int data_avail; + struct completion have_data; + bool busy; +}; static void random_recv_done(struct virtqueue *vq) { + struct virtrng_info *vi = vq->vdev->priv; + /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!virtqueue_get_buf(vq, &data_avail)) + if (!virtqueue_get_buf(vi->vq, &vi->data_avail)) return; - complete(&have_data); + complete(&vi->have_data); } /* The host will fill any buffer we give it with sweet, sweet randomness. */ -static void register_buffer(u8 *buf, size_t size) +static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size) { struct scatterlist sg; sg_init_one(&sg, buf, size); /* There should always be room for one buffer. */ - virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL); + virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL); - virtqueue_kick(vq); + virtqueue_kick(vi->vq); } static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) { int ret; + struct virtrng_info *vi = (struct virtrng_info *)rng->priv; - if (!busy) { - busy = true; - init_completion(&have_data); - register_buffer(buf, size); + if (!vi->busy) { + vi->busy = true; + init_completion(&vi->have_data); + register_buffer(vi, buf, size); } if (!wait) return 0; - ret = wait_for_completion_killable(&have_data); + ret = wait_for_completion_killable(&vi->have_data); if (ret < 0) return ret; - busy = false; + vi->busy = false; - return data_avail; + return vi->data_avail; } static void virtio_cleanup(struct hwrng *rng) { - if (busy) - wait_for_completion(&have_data); -} - + struct virtrng_info *vi = (struct virtrng_info *)rng->priv; -static struct hwrng virtio_hwrng = { - .name = "virtio", - .cleanup = virtio_cleanup, - .read = virtio_read, -}; + if (vi->busy) + wait_for_completion(&vi->have_data); +} static int probe_common(struct virtio_device *vdev) { - int err; + int err, i; + struct virtrng_info *vi = NULL; + + vi = kmalloc(sizeof(struct virtrng_info), GFP_KERNEL); + vi->hwrng.name = kmalloc(40, GFP_KERNEL); + init_completion(&vi->have_data); + + vi->hwrng.read = virtio_read; + vi->hwrng.cleanup = virtio_cleanup; + vi->hwrng.priv = (unsigned long)vi; + vdev->priv = vi; - if (vq) { - /* We only support one device for now */ - return -EBUSY; - } /* We expect a single virtqueue. */ - vq = virtio_find_single_vq(vdev, random_recv_done, "input"); - if (IS_ERR(vq)) { - err = PTR_ERR(vq); - vq = NULL; + vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input"); + if (IS_ERR(vi->vq)) { + err = PTR_ERR(vi->vq); + kfree(vi->hwrng.name); + vi->vq = NULL; + kfree(vi); + vi = NULL; return err; } - err = hwrng_register(&virtio_hwrng); + i = 0; + do { + sprintf(vi->hwrng.name, "virtio_rng.%d", i++); + err = hwrng_register(&vi->hwrng); + } while (err == -EEXIST); + if (err) { vdev->config->del_vqs(vdev); - vq = NULL; + kfree(vi->hwrng.name); + vi->vq = NULL; + kfree(vi); + vi = NULL; return err; } @@ -115,11 +135,15 @@ static int probe_common(struct virtio_device *vdev) static void remove_common(struct virtio_device *vdev) { + struct virtrng_info *vi = vdev->priv; vdev->config->reset(vdev); - busy = false; - hwrng_unregister(&virtio_hwrng); + vi->busy = false; + hwrng_unregister(&vi->hwrng); vdev->config->del_vqs(vdev); - vq = NULL; + kfree(vi->hwrng.name); + vi->vq = NULL; + kfree(vi); + vi = NULL; } static int virtrng_probe(struct virtio_device *vdev) diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h index b4b0eef5fddf..02d9c87be54c 100644 --- a/include/linux/hw_random.h +++ b/include/linux/hw_random.h @@ -31,7 +31,7 @@ * @priv: Private data, for use by the RNG driver. */ struct hwrng { - const char *name; + char *name; int (*init)(struct hwrng *rng); void (*cleanup)(struct hwrng *rng); int (*data_present)(struct hwrng *rng, int wait); -- cgit v1.2.3 From b6face404f38f783c4428d953efa4eeb68d7cc24 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 14 May 2014 03:10:06 +0200 Subject: ARM: shmobile: r7s72100: add essential clock nodes to dtsi Only essential clocks are added for now. Other clocks will be added when needed. Acked-by: Laurent Pinchart Acked-by: Magnus Damm Signed-off-by: Wolfram Sang Signed-off-by: Simon Horman --- arch/arm/boot/dts/r7s72100.dtsi | 86 +++++++++++++++++++++++++++++- include/dt-bindings/clock/r7s72100-clock.h | 28 ++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 include/dt-bindings/clock/r7s72100-clock.h (limited to 'include') diff --git a/arch/arm/boot/dts/r7s72100.dtsi b/arch/arm/boot/dts/r7s72100.dtsi index ee700717a34b..5a6e2481b567 100644 --- a/arch/arm/boot/dts/r7s72100.dtsi +++ b/arch/arm/boot/dts/r7s72100.dtsi @@ -1,13 +1,15 @@ /* * Device Tree Source for the r7s72100 SoC * - * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013-14 Renesas Solutions Corp. + * Copyright (C) 2014 Wolfram Sang, Sang Engineering * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */ +#include #include / { @@ -28,6 +30,88 @@ spi4 = &spi4; }; + clocks { + ranges; + #address-cells = <1>; + #size-cells = <1>; + + /* External clocks */ + extal_clk: extal_clk { + #clock-cells = <0>; + compatible = "fixed-clock"; + /* If clk present, value must be set by board */ + clock-frequency = <0>; + clock-output-names = "extal"; + }; + + usb_x1_clk: usb_x1_clk { + #clock-cells = <0>; + compatible = "fixed-clock"; + /* If clk present, value must be set by board */ + clock-frequency = <0>; + clock-output-names = "usb_x1"; + }; + + /* Special CPG clocks */ + cpg_clocks: cpg_clocks@fcfe0000 { + #clock-cells = <1>; + compatible = "renesas,r7s72100-cpg-clocks", + "renesas,rz-cpg-clocks"; + reg = <0xfcfe0000 0x18>; + clocks = <&extal_clk>, <&usb_x1_clk>; + clock-output-names = "pll", "i", "g"; + }; + + /* Fixed factor clocks */ + b_clk: b_clk { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + clocks = <&cpg_clocks R7S72100_CLK_PLL>; + clock-mult = <1>; + clock-div = <3>; + clock-output-names = "b"; + }; + p1_clk: p1_clk { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + clocks = <&cpg_clocks R7S72100_CLK_PLL>; + clock-mult = <1>; + clock-div = <6>; + clock-output-names = "p1"; + }; + p0_clk: p0_clk { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + clocks = <&cpg_clocks R7S72100_CLK_PLL>; + clock-mult = <1>; + clock-div = <12>; + clock-output-names = "p0"; + }; + + /* MSTP clocks */ + mstp3_clks: mstp3_clks@fcfe0420 { + #clock-cells = <1>; + compatible = "renesas,r7s72100-mstp-clocks", "renesas,cpg-mstp-clocks"; + reg = <0xfcfe0420 4>; + clocks = <&p0_clk>; + clock-indices = ; + clock-output-names = "mtu2"; + }; + + mstp4_clks: mstp4_clks@fcfe0424 { + #clock-cells = <1>; + compatible = "renesas,r7s72100-mstp-clocks", "renesas,cpg-mstp-clocks"; + reg = <0xfcfe0424 4>; + clocks = <&p1_clk>, <&p1_clk>, <&p1_clk>, <&p1_clk>, + <&p1_clk>, <&p1_clk>, <&p1_clk>, <&p1_clk>; + clock-indices = < + R7S72100_CLK_SCIF0 R7S72100_CLK_SCIF1 R7S72100_CLK_SCIF2 R7S72100_CLK_SCIF3 + R7S72100_CLK_SCIF4 R7S72100_CLK_SCIF5 R7S72100_CLK_SCIF6 R7S72100_CLK_SCIF7 + >; + clock-output-names = "scif0", "scif1", "scif2", "scif3", "scif4", "scif5", "scif6", "scif7"; + }; + }; + cpus { #address-cells = <1>; #size-cells = <0>; diff --git a/include/dt-bindings/clock/r7s72100-clock.h b/include/dt-bindings/clock/r7s72100-clock.h new file mode 100644 index 000000000000..c756f1cf0d52 --- /dev/null +++ b/include/dt-bindings/clock/r7s72100-clock.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 Renesas Solutions Corp. + * Copyright (C) 2014 Wolfram Sang, Sang Engineering + * + * 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; version 2 of the License. + */ + +#ifndef __DT_BINDINGS_CLOCK_R7S72100_H__ +#define __DT_BINDINGS_CLOCK_R7S72100_H__ + +#define R7S72100_CLK_PLL 0 + +/* MSTP3 */ +#define R7S72100_CLK_MTU2 3 + +/* MSTP4 */ +#define R7S72100_CLK_SCIF0 7 +#define R7S72100_CLK_SCIF1 6 +#define R7S72100_CLK_SCIF2 5 +#define R7S72100_CLK_SCIF3 4 +#define R7S72100_CLK_SCIF4 3 +#define R7S72100_CLK_SCIF5 2 +#define R7S72100_CLK_SCIF6 1 +#define R7S72100_CLK_SCIF7 0 + +#endif /* __DT_BINDINGS_CLOCK_R7S72100_H__ */ -- cgit v1.2.3 From d165566b8dc425e52a4f2c19c27c63d9807128b2 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 14 May 2014 03:10:11 +0200 Subject: ARM: shmobile: r7s72100: add i2c clocks to dtsi Acked-by: Laurent Pinchart Acked-by: Magnus Damm Signed-off-by: Wolfram Sang Signed-off-by: Simon Horman --- arch/arm/boot/dts/r7s72100.dtsi | 15 +++++++++++++++ include/dt-bindings/clock/r7s72100-clock.h | 6 ++++++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/arch/arm/boot/dts/r7s72100.dtsi b/arch/arm/boot/dts/r7s72100.dtsi index a4a67380b0a2..afce2e344c7a 100644 --- a/arch/arm/boot/dts/r7s72100.dtsi +++ b/arch/arm/boot/dts/r7s72100.dtsi @@ -110,6 +110,17 @@ >; clock-output-names = "scif0", "scif1", "scif2", "scif3", "scif4", "scif5", "scif6", "scif7"; }; + + mstp9_clks: mstp9_clks@fcfe0438 { + #clock-cells = <1>; + compatible = "renesas,r7s72100-mstp-clocks", "renesas,cpg-mstp-clocks"; + reg = <0xfcfe0438 4>; + clocks = <&p0_clk>, <&p0_clk>, <&p0_clk>, <&p0_clk>; + clock-indices = < + R7S72100_CLK_I2C0 R7S72100_CLK_I2C1 R7S72100_CLK_I2C2 R7S72100_CLK_I2C3 + >; + clock-output-names = "i2c0", "i2c1", "i2c2", "i2c3"; + }; }; cpus { @@ -145,6 +156,7 @@ <0 162 IRQ_TYPE_LEVEL_HIGH>, <0 163 IRQ_TYPE_LEVEL_HIGH>, <0 164 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp9_clks R7S72100_CLK_I2C0>; clock-frequency = <100000>; status = "disabled"; }; @@ -162,6 +174,7 @@ <0 170 IRQ_TYPE_LEVEL_HIGH>, <0 171 IRQ_TYPE_LEVEL_HIGH>, <0 172 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp9_clks R7S72100_CLK_I2C1>; clock-frequency = <100000>; status = "disabled"; }; @@ -179,6 +192,7 @@ <0 178 IRQ_TYPE_LEVEL_HIGH>, <0 179 IRQ_TYPE_LEVEL_HIGH>, <0 180 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp9_clks R7S72100_CLK_I2C2>; clock-frequency = <100000>; status = "disabled"; }; @@ -196,6 +210,7 @@ <0 186 IRQ_TYPE_LEVEL_HIGH>, <0 187 IRQ_TYPE_LEVEL_HIGH>, <0 188 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp9_clks R7S72100_CLK_I2C3>; clock-frequency = <100000>; status = "disabled"; }; diff --git a/include/dt-bindings/clock/r7s72100-clock.h b/include/dt-bindings/clock/r7s72100-clock.h index c756f1cf0d52..ff84e0bafd7b 100644 --- a/include/dt-bindings/clock/r7s72100-clock.h +++ b/include/dt-bindings/clock/r7s72100-clock.h @@ -25,4 +25,10 @@ #define R7S72100_CLK_SCIF6 1 #define R7S72100_CLK_SCIF7 0 +/* MSTP9 */ +#define R7S72100_CLK_I2C0 7 +#define R7S72100_CLK_I2C1 6 +#define R7S72100_CLK_I2C2 5 +#define R7S72100_CLK_I2C3 4 + #endif /* __DT_BINDINGS_CLOCK_R7S72100_H__ */ -- cgit v1.2.3 From 52eed4f5c267bd50d870f9c63d0a83d6f83d8bab Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 14 May 2014 03:10:13 +0200 Subject: ARM: shmobile: r7s72100: add spi clocks to dtsi Acked-by: Laurent Pinchart Acked-by: Magnus Damm Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/boot/dts/r7s72100.dtsi | 18 ++++++++++++++++++ include/dt-bindings/clock/r7s72100-clock.h | 7 +++++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/arch/arm/boot/dts/r7s72100.dtsi b/arch/arm/boot/dts/r7s72100.dtsi index afce2e344c7a..f50fbc8f3bd9 100644 --- a/arch/arm/boot/dts/r7s72100.dtsi +++ b/arch/arm/boot/dts/r7s72100.dtsi @@ -121,6 +121,19 @@ >; clock-output-names = "i2c0", "i2c1", "i2c2", "i2c3"; }; + + mstp10_clks: mstp10_clks@fcfe043c { + #clock-cells = <1>; + compatible = "renesas,r7s72100-mstp-clocks", "renesas,cpg-mstp-clocks"; + reg = <0xfcfe043c 4>; + clocks = <&p1_clk>, <&p1_clk>, <&p1_clk>, <&p1_clk>, + <&p1_clk>; + clock-indices = < + R7S72100_CLK_SPI0 R7S72100_CLK_SPI1 R7S72100_CLK_SPI2 R7S72100_CLK_SPI3 + R7S72100_CLK_SPI4 + >; + clock-output-names = "spi0", "spi1", "spi2", "spi3", "spi4"; + }; }; cpus { @@ -318,6 +331,7 @@ <0 239 IRQ_TYPE_LEVEL_HIGH>, <0 240 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "error", "rx", "tx"; + clocks = <&mstp10_clks R7S72100_CLK_SPI0>; num-cs = <1>; #address-cells = <1>; #size-cells = <0>; @@ -331,6 +345,7 @@ <0 242 IRQ_TYPE_LEVEL_HIGH>, <0 243 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "error", "rx", "tx"; + clocks = <&mstp10_clks R7S72100_CLK_SPI1>; num-cs = <1>; #address-cells = <1>; #size-cells = <0>; @@ -344,6 +359,7 @@ <0 245 IRQ_TYPE_LEVEL_HIGH>, <0 246 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "error", "rx", "tx"; + clocks = <&mstp10_clks R7S72100_CLK_SPI2>; num-cs = <1>; #address-cells = <1>; #size-cells = <0>; @@ -357,6 +373,7 @@ <0 248 IRQ_TYPE_LEVEL_HIGH>, <0 249 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "error", "rx", "tx"; + clocks = <&mstp10_clks R7S72100_CLK_SPI3>; num-cs = <1>; #address-cells = <1>; #size-cells = <0>; @@ -370,6 +387,7 @@ <0 251 IRQ_TYPE_LEVEL_HIGH>, <0 252 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "error", "rx", "tx"; + clocks = <&mstp10_clks R7S72100_CLK_SPI4>; num-cs = <1>; #address-cells = <1>; #size-cells = <0>; diff --git a/include/dt-bindings/clock/r7s72100-clock.h b/include/dt-bindings/clock/r7s72100-clock.h index ff84e0bafd7b..5128f4d94f44 100644 --- a/include/dt-bindings/clock/r7s72100-clock.h +++ b/include/dt-bindings/clock/r7s72100-clock.h @@ -31,4 +31,11 @@ #define R7S72100_CLK_I2C2 5 #define R7S72100_CLK_I2C3 4 +/* MSTP10 */ +#define R7S72100_CLK_SPI0 7 +#define R7S72100_CLK_SPI1 6 +#define R7S72100_CLK_SPI2 5 +#define R7S72100_CLK_SPI3 4 +#define R7S72100_CLK_SPI4 3 + #endif /* __DT_BINDINGS_CLOCK_R7S72100_H__ */ -- cgit v1.2.3 From 3d4405226d27b3a215e4d03cfa51f536244e5de7 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Sun, 11 May 2014 22:59:30 +0200 Subject: net: avoid dependency of net_get_random_once on nop patching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit net_get_random_once depends on the static keys infrastructure to patch up the branch to the slow path during boot. This was realized by abusing the static keys api and defining a new initializer to not enable the call site while still indicating that the branch point should get patched up. This was needed to have the fast path considered likely by gcc. The static key initialization during boot up normally walks through all the registered keys and either patches in ideal nops or enables the jump site but omitted that step on x86 if ideal nops where already placed at static_key branch points. Thus net_get_random_once branches not always became active. This patch switches net_get_random_once to the ordinary static_key api and thus places the kernel fast path in the - by gcc considered - unlikely path. Microbenchmarks on Intel and AMD x86-64 showed that the unlikely path actually beats the likely path in terms of cycle cost and that different nop patterns did not make much difference, thus this switch should not be noticeable. Fixes: a48e42920ff38b ("net: introduce new macro net_get_random_once") Reported-by: Tuomas Räsänen Cc: Linus Torvalds Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/linux/net.h | 15 ++++----------- net/core/utils.c | 8 ++++---- 2 files changed, 8 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/net.h b/include/linux/net.h index 94734a6259a4..17d83393afcc 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -248,24 +248,17 @@ do { \ bool __net_get_random_once(void *buf, int nbytes, bool *done, struct static_key *done_key); -#ifdef HAVE_JUMP_LABEL -#define ___NET_RANDOM_STATIC_KEY_INIT ((struct static_key) \ - { .enabled = ATOMIC_INIT(0), .entries = (void *)1 }) -#else /* !HAVE_JUMP_LABEL */ -#define ___NET_RANDOM_STATIC_KEY_INIT STATIC_KEY_INIT_FALSE -#endif /* HAVE_JUMP_LABEL */ - #define net_get_random_once(buf, nbytes) \ ({ \ bool ___ret = false; \ static bool ___done = false; \ - static struct static_key ___done_key = \ - ___NET_RANDOM_STATIC_KEY_INIT; \ - if (!static_key_true(&___done_key)) \ + static struct static_key ___once_key = \ + STATIC_KEY_INIT_TRUE; \ + if (static_key_true(&___once_key)) \ ___ret = __net_get_random_once(buf, \ nbytes, \ &___done, \ - &___done_key); \ + &___once_key); \ ___ret; \ }) diff --git a/net/core/utils.c b/net/core/utils.c index 2f737bf90b3f..eed34338736c 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -348,8 +348,8 @@ static void __net_random_once_deferred(struct work_struct *w) { struct __net_random_once_work *work = container_of(w, struct __net_random_once_work, work); - if (!static_key_enabled(work->key)) - static_key_slow_inc(work->key); + BUG_ON(!static_key_enabled(work->key)); + static_key_slow_dec(work->key); kfree(work); } @@ -367,7 +367,7 @@ static void __net_random_once_disable_jump(struct static_key *key) } bool __net_get_random_once(void *buf, int nbytes, bool *done, - struct static_key *done_key) + struct static_key *once_key) { static DEFINE_SPINLOCK(lock); unsigned long flags; @@ -382,7 +382,7 @@ bool __net_get_random_once(void *buf, int nbytes, bool *done, *done = true; spin_unlock_irqrestore(&lock, flags); - __net_random_once_disable_jump(done_key); + __net_random_once_disable_jump(once_key); return true; } -- cgit v1.2.3 From 24972557b12ce8fd5b6c6847d0e2ee1837ddc13b Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Thu, 1 May 2014 22:26:55 -0500 Subject: GFS2: remove transaction glock GFS2 has a transaction glock, which must be grabbed for every transaction, whose purpose is to deal with freezing the filesystem. Aside from this involving a large amount of locking, it is very easy to make the current fsfreeze code hang on unfreezing. This patch rewrites how gfs2 handles freezing the filesystem. The transaction glock is removed. In it's place is a freeze glock, which is cached (but not held) in a shared state by every node in the cluster when the filesystem is mounted. This lock only needs to be grabbed on freezing, and actions which need to be safe from freezing, like recovery. When a node wants to freeze the filesystem, it grabs this glock exclusively. When the freeze glock state changes on the nodes (either from shared to unlocked, or shared to exclusive), the filesystem does a special log flush. gfs2_log_flush() does all the work for flushing out the and shutting down the incore log, and then it tries to grab the freeze glock in a shared state again. Since the filesystem is stuck in gfs2_log_flush, no new transaction can start, and nothing can be written to disk. Unfreezing the filesytem simply involes dropping the freeze glock, allowing gfs2_log_flush() to grab and then release the shared lock, so it is cached for next time. However, in order for the unfreezing ioctl to occur, gfs2 needs to get a shared lock on the filesystem root directory inode to check permissions. If that glock has already been grabbed exclusively, fsfreeze will be unable to get the shared lock and unfreeze the filesystem. In order to allow the unfreeze, this patch makes gfs2 grab a shared lock on the filesystem root directory during the freeze, and hold it until it unfreezes the filesystem. The functions which need to grab a shared lock in order to allow the unfreeze ioctl to be issued now use the lock grabbed by the freeze code instead. The freeze and unfreeze code take care to make sure that this shared lock will not be dropped while another process is using it. Signed-off-by: Benjamin Marzinski Signed-off-by: Steven Whitehouse --- fs/gfs2/aops.c | 2 +- fs/gfs2/file.c | 2 +- fs/gfs2/glops.c | 51 ++++++++++++++-------- fs/gfs2/glops.h | 2 +- fs/gfs2/incore.h | 12 ++++-- fs/gfs2/inode.c | 40 ++++++++++++----- fs/gfs2/log.c | 93 +++++++++++++++++++++++++++------------- fs/gfs2/log.h | 11 ++++- fs/gfs2/ops_fstype.c | 22 +++++++--- fs/gfs2/quota.c | 2 +- fs/gfs2/recovery.c | 22 +++++----- fs/gfs2/rgrp.c | 2 +- fs/gfs2/super.c | 69 ++++++++++++++++++----------- fs/gfs2/sys.c | 4 +- fs/gfs2/trans.c | 44 +++---------------- include/uapi/linux/gfs2_ondisk.h | 2 +- 16 files changed, 227 insertions(+), 153 deletions(-) (limited to 'include') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index ce62dcac90b6..5a49b037da81 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -431,7 +431,7 @@ static int gfs2_jdata_writepages(struct address_space *mapping, ret = gfs2_write_cache_jdata(mapping, wbc); if (ret == 0 && wbc->sync_mode == WB_SYNC_ALL) { - gfs2_log_flush(sdp, ip->i_gl); + gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH); ret = gfs2_write_cache_jdata(mapping, wbc); } return ret; diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 80d67253623c..606525215acc 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -256,7 +256,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) } if ((flags ^ new_flags) & GFS2_DIF_JDATA) { if (flags & GFS2_DIF_JDATA) - gfs2_log_flush(sdp, ip->i_gl); + gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH); error = filemap_fdatawrite(inode->i_mapping); if (error) goto out; diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 54b66809e818..0b527939c46f 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -89,18 +89,23 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl) if (!tr.tr_revokes) return; - /* A shortened, inline version of gfs2_trans_begin() */ + /* A shortened, inline version of gfs2_trans_begin() + * tr->alloced is not set since the transaction structure is + * on the stack */ tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64)); tr.tr_ip = (unsigned long)__builtin_return_address(0); sb_start_intwrite(sdp->sd_vfs); - gfs2_log_reserve(sdp, tr.tr_reserved); + if (gfs2_log_reserve(sdp, tr.tr_reserved) < 0) { + sb_end_intwrite(sdp->sd_vfs); + return; + } WARN_ON_ONCE(current->journal_info); current->journal_info = &tr; __gfs2_ail_flush(gl, 0, tr.tr_revokes); gfs2_trans_end(sdp); - gfs2_log_flush(sdp, NULL); + gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); } void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) @@ -121,7 +126,7 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) return; __gfs2_ail_flush(gl, fsync, max_revokes); gfs2_trans_end(sdp); - gfs2_log_flush(sdp, NULL); + gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); } /** @@ -144,7 +149,7 @@ static void rgrp_go_sync(struct gfs2_glock *gl) return; GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE); - gfs2_log_flush(sdp, gl); + gfs2_log_flush(sdp, gl, NORMAL_FLUSH); filemap_fdatawrite_range(mapping, gl->gl_vm.start, gl->gl_vm.end); error = filemap_fdatawait_range(mapping, gl->gl_vm.start, gl->gl_vm.end); mapping_set_error(mapping, error); @@ -206,7 +211,7 @@ static void inode_go_sync(struct gfs2_glock *gl) GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE); - gfs2_log_flush(gl->gl_sbd, gl); + gfs2_log_flush(gl->gl_sbd, gl, NORMAL_FLUSH); filemap_fdatawrite(metamapping); if (ip) { struct address_space *mapping = ip->i_inode.i_mapping; @@ -253,7 +258,7 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags) } if (ip == GFS2_I(gl->gl_sbd->sd_rindex)) { - gfs2_log_flush(gl->gl_sbd, NULL); + gfs2_log_flush(gl->gl_sbd, NULL, NORMAL_FLUSH); gl->gl_sbd->sd_rindex_uptodate = 0; } if (ip && S_ISREG(ip->i_inode.i_mode)) @@ -455,31 +460,39 @@ static void inode_go_dump(struct seq_file *seq, const struct gfs2_glock *gl) } /** - * trans_go_sync - promote/demote the transaction glock + * freeze_go_sync - promote/demote the freeze glock * @gl: the glock * @state: the requested state * @flags: * */ -static void trans_go_sync(struct gfs2_glock *gl) +static void freeze_go_sync(struct gfs2_glock *gl) { struct gfs2_sbd *sdp = gl->gl_sbd; + DEFINE_WAIT(wait); - if (gl->gl_state != LM_ST_UNLOCKED && + if (gl->gl_state == LM_ST_SHARED && test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) { - gfs2_meta_syncfs(sdp); - gfs2_log_shutdown(sdp); + atomic_set(&sdp->sd_log_freeze, 1); + wake_up(&sdp->sd_logd_waitq); + do { + prepare_to_wait(&sdp->sd_log_frozen_wait, &wait, + TASK_UNINTERRUPTIBLE); + if (atomic_read(&sdp->sd_log_freeze)) + io_schedule(); + } while(atomic_read(&sdp->sd_log_freeze)); + finish_wait(&sdp->sd_log_frozen_wait, &wait); } } /** - * trans_go_xmote_bh - After promoting/demoting the transaction glock + * freeze_go_xmote_bh - After promoting/demoting the freeze glock * @gl: the glock * */ -static int trans_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh) +static int freeze_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh) { struct gfs2_sbd *sdp = gl->gl_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode); @@ -512,7 +525,7 @@ static int trans_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh) * Always returns 0 */ -static int trans_go_demote_ok(const struct gfs2_glock *gl) +static int freeze_go_demote_ok(const struct gfs2_glock *gl) { return 0; } @@ -563,10 +576,10 @@ const struct gfs2_glock_operations gfs2_rgrp_glops = { .go_flags = GLOF_LVB, }; -const struct gfs2_glock_operations gfs2_trans_glops = { - .go_sync = trans_go_sync, - .go_xmote_bh = trans_go_xmote_bh, - .go_demote_ok = trans_go_demote_ok, +const struct gfs2_glock_operations gfs2_freeze_glops = { + .go_sync = freeze_go_sync, + .go_xmote_bh = freeze_go_xmote_bh, + .go_demote_ok = freeze_go_demote_ok, .go_type = LM_TYPE_NONDISK, }; diff --git a/fs/gfs2/glops.h b/fs/gfs2/glops.h index bf95a2dc1662..7455d2629bcb 100644 --- a/fs/gfs2/glops.h +++ b/fs/gfs2/glops.h @@ -15,7 +15,7 @@ extern const struct gfs2_glock_operations gfs2_meta_glops; extern const struct gfs2_glock_operations gfs2_inode_glops; extern const struct gfs2_glock_operations gfs2_rgrp_glops; -extern const struct gfs2_glock_operations gfs2_trans_glops; +extern const struct gfs2_glock_operations gfs2_freeze_glops; extern const struct gfs2_glock_operations gfs2_iopen_glops; extern const struct gfs2_glock_operations gfs2_flock_glops; extern const struct gfs2_glock_operations gfs2_nondisk_glops; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index bdf70c18610c..2434a96f95df 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -465,9 +465,7 @@ struct gfs2_trans { unsigned int tr_reserved; unsigned int tr_touched:1; unsigned int tr_attached:1; - - struct gfs2_holder tr_t_gh; - + unsigned int tr_alloced:1; unsigned int tr_num_buf_new; unsigned int tr_num_databuf_new; @@ -682,7 +680,7 @@ struct gfs2_sbd { struct lm_lockstruct sd_lockstruct; struct gfs2_holder sd_live_gh; struct gfs2_glock *sd_rename_gl; - struct gfs2_glock *sd_trans_gl; + struct gfs2_glock *sd_freeze_gl; wait_queue_head_t sd_glock_wait; atomic_t sd_glock_disposal; struct completion sd_locking_init; @@ -794,6 +792,12 @@ struct gfs2_sbd { /* For quiescing the filesystem */ struct gfs2_holder sd_freeze_gh; + struct gfs2_holder sd_freeze_root_gh; + struct gfs2_holder sd_thaw_gh; + atomic_t sd_log_freeze; + atomic_t sd_frozen_root; + wait_queue_head_t sd_frozen_root_wait; + wait_queue_head_t sd_log_frozen_wait; char sd_fsname[GFS2_FSNAME_LEN]; char sd_table_name[GFS2_FSNAME_LEN]; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 28cc7bf6575a..e62e59477884 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1613,18 +1613,26 @@ int gfs2_permission(struct inode *inode, int mask) { struct gfs2_inode *ip; struct gfs2_holder i_gh; + struct gfs2_sbd *sdp = GFS2_SB(inode); int error; int unlock = 0; + int frozen_root = 0; ip = GFS2_I(inode); if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) { - if (mask & MAY_NOT_BLOCK) - return -ECHILD; - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); - if (error) - return error; - unlock = 1; + if (unlikely(gfs2_glock_is_held_excl(sdp->sd_freeze_gl) && + inode == sdp->sd_root_dir->d_inode && + atomic_inc_not_zero(&sdp->sd_frozen_root))) + frozen_root = 1; + else { + if (mask & MAY_NOT_BLOCK) + return -ECHILD; + error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); + if (error) + return error; + unlock = 1; + } } if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) @@ -1633,6 +1641,8 @@ int gfs2_permission(struct inode *inode, int mask) error = generic_permission(inode, mask); if (unlock) gfs2_glock_dq_uninit(&i_gh); + else if (frozen_root && atomic_dec_and_test(&sdp->sd_frozen_root)) + wake_up(&sdp->sd_frozen_root_wait); return error; } @@ -1805,19 +1815,29 @@ static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, struct inode *inode = dentry->d_inode; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder gh; + struct gfs2_sbd *sdp = GFS2_SB(inode); int error; int unlock = 0; + int frozen_root = 0; if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) { - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); - if (error) - return error; - unlock = 1; + if (unlikely(gfs2_glock_is_held_excl(sdp->sd_freeze_gl) && + inode == sdp->sd_root_dir->d_inode && + atomic_inc_not_zero(&sdp->sd_frozen_root))) + frozen_root = 1; + else { + error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); + if (error) + return error; + unlock = 1; + } } generic_fillattr(inode, stat); if (unlock) gfs2_glock_dq_uninit(&gh); + else if (frozen_root && atomic_dec_and_test(&sdp->sd_frozen_root)) + wake_up(&sdp->sd_frozen_root_wait); return 0; } diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 4a14d504ef83..3966fadbcebd 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -300,6 +300,23 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail) spin_unlock(&sdp->sd_ail_lock); } +/** + * gfs2_log_release - Release a given number of log blocks + * @sdp: The GFS2 superblock + * @blks: The number of blocks + * + */ + +void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks) +{ + + atomic_add(blks, &sdp->sd_log_blks_free); + trace_gfs2_log_blocks(sdp, blks); + gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <= + sdp->sd_jdesc->jd_blocks); + up_read(&sdp->sd_log_flush_lock); +} + /** * gfs2_log_reserve - Make a log reservation * @sdp: The GFS2 superblock @@ -358,7 +375,10 @@ retry: wake_up(&sdp->sd_log_waitq); down_read(&sdp->sd_log_flush_lock); - + if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) { + gfs2_log_release(sdp, blks); + return -EROFS; + } return 0; } @@ -671,7 +691,8 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags) * */ -void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) +void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, + enum gfs2_flush_type type) { struct gfs2_trans *tr; @@ -723,6 +744,42 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) } spin_unlock(&sdp->sd_ail_lock); gfs2_log_unlock(sdp); + + if (atomic_read(&sdp->sd_log_freeze)) + type = FREEZE_FLUSH; + if (type != NORMAL_FLUSH) { + if (!sdp->sd_log_idle) { + for (;;) { + gfs2_ail1_start(sdp); + gfs2_ail1_wait(sdp); + if (gfs2_ail1_empty(sdp)) + break; + } + atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */ + trace_gfs2_log_blocks(sdp, -1); + sdp->sd_log_flush_wrapped = 0; + log_write_header(sdp, 0); + sdp->sd_log_head = sdp->sd_log_flush_head; + } + if (type == SHUTDOWN_FLUSH || type == FREEZE_FLUSH) + gfs2_log_shutdown(sdp); + if (type == FREEZE_FLUSH) { + int error; + + atomic_set(&sdp->sd_log_freeze, 0); + wake_up(&sdp->sd_log_frozen_wait); + error = gfs2_glock_nq_init(sdp->sd_freeze_gl, + LM_ST_SHARED, 0, + &sdp->sd_thaw_gh); + if (error) { + printk(KERN_INFO "GFS2: couln't get freeze lock : %d\n", error); + gfs2_assert_withdraw(sdp, 0); + } + else + gfs2_glock_dq_uninit(&sdp->sd_thaw_gh); + } + } + trace_gfs2_log_flush(sdp, 0); up_write(&sdp->sd_log_flush_lock); @@ -761,7 +818,7 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) if (sdp->sd_log_tr) { gfs2_merge_trans(sdp->sd_log_tr, tr); } else if (tr->tr_num_buf_new || tr->tr_num_databuf_new) { - gfs2_assert_withdraw(sdp, tr->tr_t_gh.gh_gl); + gfs2_assert_withdraw(sdp, tr->tr_alloced); sdp->sd_log_tr = tr; tr->tr_attached = 1; } @@ -813,8 +870,6 @@ void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) void gfs2_log_shutdown(struct gfs2_sbd *sdp) { - down_write(&sdp->sd_log_flush_lock); - gfs2_assert_withdraw(sdp, !sdp->sd_log_blks_reserved); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list)); @@ -824,38 +879,16 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp) log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT); - gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_blks_free) == sdp->sd_jdesc->jd_blocks); gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail); gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list)); sdp->sd_log_head = sdp->sd_log_flush_head; sdp->sd_log_tail = sdp->sd_log_head; - - up_write(&sdp->sd_log_flush_lock); -} - - -/** - * gfs2_meta_syncfs - sync all the buffers in a filesystem - * @sdp: the filesystem - * - */ - -void gfs2_meta_syncfs(struct gfs2_sbd *sdp) -{ - gfs2_log_flush(sdp, NULL); - for (;;) { - gfs2_ail1_start(sdp); - gfs2_ail1_wait(sdp); - if (gfs2_ail1_empty(sdp)) - break; - } - gfs2_log_flush(sdp, NULL); } static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp) { - return (atomic_read(&sdp->sd_log_pinned) >= atomic_read(&sdp->sd_log_thresh1)); + return (atomic_read(&sdp->sd_log_pinned) >= atomic_read(&sdp->sd_log_thresh1) || atomic_read(&sdp->sd_log_freeze)); } static inline int gfs2_ail_flush_reqd(struct gfs2_sbd *sdp) @@ -882,14 +915,14 @@ int gfs2_logd(void *data) if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { gfs2_ail1_empty(sdp); - gfs2_log_flush(sdp, NULL); + gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); } if (gfs2_ail_flush_reqd(sdp)) { gfs2_ail1_start(sdp); gfs2_ail1_wait(sdp); gfs2_ail1_empty(sdp); - gfs2_log_flush(sdp, NULL); + gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); } if (!gfs2_ail_flush_reqd(sdp)) diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h index 37216634f0aa..9499a6049212 100644 --- a/fs/gfs2/log.h +++ b/fs/gfs2/log.h @@ -63,14 +63,21 @@ extern void gfs2_ordered_del_inode(struct gfs2_inode *ip); extern unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, unsigned int ssize); +extern void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks); extern int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks); -extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl); +enum gfs2_flush_type { + NORMAL_FLUSH = 0, + SYNC_FLUSH, + SHUTDOWN_FLUSH, + FREEZE_FLUSH +}; +extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, + enum gfs2_flush_type type); extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans); extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd); extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc); extern void gfs2_log_shutdown(struct gfs2_sbd *sdp); -extern void gfs2_meta_syncfs(struct gfs2_sbd *sdp); extern int gfs2_logd(void *data); extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd); extern void gfs2_write_revokes(struct gfs2_sbd *sdp); diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 22f954051bb8..be45c79f6745 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -129,6 +129,10 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) init_rwsem(&sdp->sd_log_flush_lock); atomic_set(&sdp->sd_log_in_flight, 0); init_waitqueue_head(&sdp->sd_log_flush_wait); + init_waitqueue_head(&sdp->sd_log_frozen_wait); + atomic_set(&sdp->sd_log_freeze, 0); + atomic_set(&sdp->sd_frozen_root, 0); + init_waitqueue_head(&sdp->sd_frozen_root_wait); return sdp; } @@ -419,8 +423,8 @@ static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh, goto fail_live; } - error = gfs2_glock_get(sdp, GFS2_TRANS_LOCK, &gfs2_trans_glops, - CREATE, &sdp->sd_trans_gl); + error = gfs2_glock_get(sdp, GFS2_FREEZE_LOCK, &gfs2_freeze_glops, + CREATE, &sdp->sd_freeze_gl); if (error) { fs_err(sdp, "can't create transaction glock: %d\n", error); goto fail_rename; @@ -429,7 +433,7 @@ static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh, return 0; fail_trans: - gfs2_glock_put(sdp->sd_trans_gl); + gfs2_glock_put(sdp->sd_freeze_gl); fail_rename: gfs2_glock_put(sdp->sd_rename_gl); fail_live: @@ -755,7 +759,15 @@ static int init_journal(struct gfs2_sbd *sdp, int undo) set_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags); gfs2_glock_dq_uninit(&ji_gh); jindex = 0; - + if (!sdp->sd_args.ar_spectator) { + error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, 0, + &sdp->sd_thaw_gh); + if (error) { + fs_err(sdp, "can't acquire freeze glock: %d\n", error); + goto fail_jinode_gh; + } + } + gfs2_glock_dq_uninit(&sdp->sd_thaw_gh); return 0; fail_jinode_gh: @@ -1380,7 +1392,7 @@ static void gfs2_kill_sb(struct super_block *sb) return; } - gfs2_meta_syncfs(sdp); + gfs2_log_flush(sdp, NULL, SYNC_FLUSH); dput(sdp->sd_root_dir); dput(sdp->sd_master_dir); sdp->sd_root_dir = NULL; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 619389649d03..64b29f7f6b4c 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -880,7 +880,7 @@ out: gfs2_glock_dq_uninit(&ghs[qx]); mutex_unlock(&ip->i_inode.i_mutex); kfree(ghs); - gfs2_log_flush(ip->i_gl->gl_sbd, ip->i_gl); + gfs2_log_flush(ip->i_gl->gl_sbd, ip->i_gl, NORMAL_FLUSH); return error; } diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 7ad4094d68c0..a4ed78b5f47e 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -454,7 +454,7 @@ void gfs2_recover_func(struct work_struct *work) struct gfs2_inode *ip = GFS2_I(jd->jd_inode); struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); struct gfs2_log_header_host head; - struct gfs2_holder j_gh, ji_gh, t_gh; + struct gfs2_holder j_gh, ji_gh, thaw_gh; unsigned long t; int ro = 0; unsigned int pass; @@ -508,11 +508,11 @@ void gfs2_recover_func(struct work_struct *work) t = jiffies; - /* Acquire a shared hold on the transaction lock */ + /* Acquire a shared hold on the freeze lock */ - error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, - LM_FLAG_NOEXP | LM_FLAG_PRIORITY | - GL_NOCACHE, &t_gh); + error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, + LM_FLAG_NOEXP | LM_FLAG_PRIORITY, + &thaw_gh); if (error) goto fail_gunlock_ji; @@ -538,7 +538,7 @@ void gfs2_recover_func(struct work_struct *work) fs_warn(sdp, "jid=%u: Can't replay: read-only block " "device\n", jd->jd_jid); error = -EROFS; - goto fail_gunlock_tr; + goto fail_gunlock_thaw; } fs_info(sdp, "jid=%u: Replaying journal...\n", jd->jd_jid); @@ -549,14 +549,14 @@ void gfs2_recover_func(struct work_struct *work) head.lh_blkno, pass); lops_after_scan(jd, error, pass); if (error) - goto fail_gunlock_tr; + goto fail_gunlock_thaw; } error = clean_journal(jd, &head); if (error) - goto fail_gunlock_tr; + goto fail_gunlock_thaw; - gfs2_glock_dq_uninit(&t_gh); + gfs2_glock_dq_uninit(&thaw_gh); t = DIV_ROUND_UP(jiffies - t, HZ); fs_info(sdp, "jid=%u: Journal replayed in %lus\n", jd->jd_jid, t); @@ -572,8 +572,8 @@ void gfs2_recover_func(struct work_struct *work) fs_info(sdp, "jid=%u: Done\n", jd->jd_jid); goto done; -fail_gunlock_tr: - gfs2_glock_dq_uninit(&t_gh); +fail_gunlock_thaw: + gfs2_glock_dq_uninit(&thaw_gh); fail_gunlock_ji: if (jlocked) { gfs2_glock_dq_uninit(&ji_gh); diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 281a7716e3f3..db629d1bd1bd 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -2001,7 +2001,7 @@ next_rgrp: } /* Flushing the log may release space */ if (loops == 2) - gfs2_log_flush(sdp, NULL); + gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); } return -ENOSPC; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index de8afad89e51..1319b5c4ec68 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -399,7 +399,7 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) { struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode); struct gfs2_glock *j_gl = ip->i_gl; - struct gfs2_holder t_gh; + struct gfs2_holder thaw_gh; struct gfs2_log_header_host head; int error; @@ -407,7 +407,8 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) if (error) return error; - error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &t_gh); + error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, 0, + &thaw_gh); if (error) goto fail_threads; @@ -433,13 +434,13 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); - gfs2_glock_dq_uninit(&t_gh); + gfs2_glock_dq_uninit(&thaw_gh); return 0; fail: - t_gh.gh_flags |= GL_NOCACHE; - gfs2_glock_dq_uninit(&t_gh); + thaw_gh.gh_flags |= GL_NOCACHE; + gfs2_glock_dq_uninit(&thaw_gh); fail_threads: kthread_stop(sdp->sd_quotad_process); kthread_stop(sdp->sd_logd_process); @@ -635,15 +636,21 @@ struct lfcc { */ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp, - struct gfs2_holder *t_gh) + struct gfs2_holder *freeze_gh) { struct gfs2_inode *ip; struct gfs2_jdesc *jd; struct lfcc *lfcc; LIST_HEAD(list); struct gfs2_log_header_host lh; + struct gfs2_inode *dip = GFS2_I(sdp->sd_root_dir->d_inode); int error; + error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, + &sdp->sd_freeze_root_gh); + if (error) + return error; + atomic_set(&sdp->sd_frozen_root, 1); list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) { lfcc = kmalloc(sizeof(struct lfcc), GFP_KERNEL); if (!lfcc) { @@ -659,8 +666,8 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp, list_add(&lfcc->list, &list); } - error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_DEFERRED, - GL_NOCACHE, t_gh); + error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_EXCLUSIVE, + GL_NOCACHE, freeze_gh); list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) { error = gfs2_jdesc_check(jd); @@ -676,7 +683,7 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp, } if (error) - gfs2_glock_dq_uninit(t_gh); + gfs2_glock_dq_uninit(freeze_gh); out: while (!list_empty(&list)) { @@ -685,6 +692,11 @@ out: gfs2_glock_dq_uninit(&lfcc->gh); kfree(lfcc); } + if (error) { + atomic_dec(&sdp->sd_frozen_root); + wait_event(sdp->sd_frozen_root_wait, atomic_read(&sdp->sd_frozen_root) == 0); + gfs2_glock_dq_uninit(&sdp->sd_freeze_root_gh); + } return error; } @@ -742,7 +754,7 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc) int ret = 0; if (wbc->sync_mode == WB_SYNC_ALL) - gfs2_log_flush(GFS2_SB(inode), ip->i_gl); + gfs2_log_flush(GFS2_SB(inode), ip->i_gl, NORMAL_FLUSH); if (bdi->dirty_exceeded) gfs2_ail1_flush(sdp, wbc); else @@ -822,9 +834,18 @@ out: static int gfs2_make_fs_ro(struct gfs2_sbd *sdp) { - struct gfs2_holder t_gh; + struct gfs2_holder thaw_gh; int error; + error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, GL_NOCACHE, + &thaw_gh); + if (error && !test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) + return error; + + down_write(&sdp->sd_log_flush_lock); + clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); + up_write(&sdp->sd_log_flush_lock); + kthread_stop(sdp->sd_quotad_process); kthread_stop(sdp->sd_logd_process); @@ -832,18 +853,11 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp) gfs2_quota_sync(sdp->sd_vfs, 0); gfs2_statfs_sync(sdp->sd_vfs, 0); - error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, GL_NOCACHE, - &t_gh); - if (error && !test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) - return error; - - gfs2_meta_syncfs(sdp); - gfs2_log_shutdown(sdp); - - clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); + gfs2_log_flush(sdp, NULL, SHUTDOWN_FLUSH); + gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_blks_free) == sdp->sd_jdesc->jd_blocks); - if (t_gh.gh_gl) - gfs2_glock_dq_uninit(&t_gh); + if (thaw_gh.gh_gl) + gfs2_glock_dq_uninit(&thaw_gh); gfs2_quota_cleanup(sdp); @@ -900,7 +914,7 @@ restart: iput(sdp->sd_quota_inode); gfs2_glock_put(sdp->sd_rename_gl); - gfs2_glock_put(sdp->sd_trans_gl); + gfs2_glock_put(sdp->sd_freeze_gl); if (!sdp->sd_args.ar_spectator) { gfs2_glock_dq_uninit(&sdp->sd_journal_gh); @@ -935,8 +949,8 @@ static int gfs2_sync_fs(struct super_block *sb, int wait) struct gfs2_sbd *sdp = sb->s_fs_info; gfs2_quota_sync(sb, -1); - if (wait && sdp) - gfs2_log_flush(sdp, NULL); + if (wait && sdp && !atomic_read(&sdp->sd_log_freeze)) + gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); return 0; } @@ -986,6 +1000,9 @@ static int gfs2_unfreeze(struct super_block *sb) struct gfs2_sbd *sdp = sb->s_fs_info; gfs2_glock_dq_uninit(&sdp->sd_freeze_gh); + atomic_dec(&sdp->sd_frozen_root); + wait_event(sdp->sd_frozen_root_wait, atomic_read(&sdp->sd_frozen_root) == 0); + gfs2_glock_dq_uninit(&sdp->sd_freeze_root_gh); return 0; } @@ -1525,7 +1542,7 @@ static void gfs2_evict_inode(struct inode *inode) goto out_unlock; out_truncate: - gfs2_log_flush(sdp, ip->i_gl); + gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH); if (test_bit(GLF_DIRTY, &ip->i_gl->gl_flags)) { struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl); filemap_fdatawrite(metamapping); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index de25d5577e5d..7bc17edcb51f 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -240,8 +240,8 @@ static ssize_t demote_rq_store(struct gfs2_sbd *sdp, const char *buf, size_t len if (gltype > LM_TYPE_JOURNAL) return -EINVAL; - if (gltype == LM_TYPE_NONDISK && glnum == GFS2_TRANS_LOCK) - glops = &gfs2_trans_glops; + if (gltype == LM_TYPE_NONDISK && glnum == GFS2_FREEZE_LOCK) + glops = &gfs2_freeze_glops; else glops = gfs2_glops_list[gltype]; if (glops == NULL) diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c index bead90d27bad..0546ab4e28e8 100644 --- a/fs/gfs2/trans.c +++ b/fs/gfs2/trans.c @@ -48,6 +48,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks, tr->tr_blocks = blocks; tr->tr_revokes = revokes; tr->tr_reserved = 1; + tr->tr_alloced = 1; if (blocks) tr->tr_reserved += 6 + blocks; if (revokes) @@ -57,48 +58,22 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks, INIT_LIST_HEAD(&tr->tr_buf); sb_start_intwrite(sdp->sd_vfs); - gfs2_holder_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &tr->tr_t_gh); - - error = gfs2_glock_nq(&tr->tr_t_gh); - if (error) - goto fail_holder_uninit; error = gfs2_log_reserve(sdp, tr->tr_reserved); if (error) - goto fail_gunlock; + goto fail; current->journal_info = tr; return 0; -fail_gunlock: - gfs2_glock_dq(&tr->tr_t_gh); - -fail_holder_uninit: +fail: sb_end_intwrite(sdp->sd_vfs); - gfs2_holder_uninit(&tr->tr_t_gh); kfree(tr); return error; } -/** - * gfs2_log_release - Release a given number of log blocks - * @sdp: The GFS2 superblock - * @blks: The number of blocks - * - */ - -static void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks) -{ - - atomic_add(blks, &sdp->sd_log_blks_free); - trace_gfs2_log_blocks(sdp, blks); - gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <= - sdp->sd_jdesc->jd_blocks); - up_read(&sdp->sd_log_flush_lock); -} - static void gfs2_print_trans(const struct gfs2_trans *tr) { pr_warn("Transaction created at: %pSR\n", (void *)tr->tr_ip); @@ -119,11 +94,8 @@ void gfs2_trans_end(struct gfs2_sbd *sdp) if (!tr->tr_touched) { gfs2_log_release(sdp, tr->tr_reserved); - if (tr->tr_t_gh.gh_gl) { - gfs2_glock_dq(&tr->tr_t_gh); - gfs2_holder_uninit(&tr->tr_t_gh); + if (tr->tr_alloced) kfree(tr); - } sb_end_intwrite(sdp->sd_vfs); return; } @@ -137,16 +109,12 @@ void gfs2_trans_end(struct gfs2_sbd *sdp) gfs2_print_trans(tr); gfs2_log_commit(sdp, tr); - if (tr->tr_t_gh.gh_gl) { - gfs2_glock_dq(&tr->tr_t_gh); - gfs2_holder_uninit(&tr->tr_t_gh); - if (!tr->tr_attached) + if (tr->tr_alloced && !tr->tr_attached) kfree(tr); - } up_read(&sdp->sd_log_flush_lock); if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS) - gfs2_log_flush(sdp, NULL); + gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); sb_end_intwrite(sdp->sd_vfs); } diff --git a/include/uapi/linux/gfs2_ondisk.h b/include/uapi/linux/gfs2_ondisk.h index db3fdd083882..1a763eaae0bb 100644 --- a/include/uapi/linux/gfs2_ondisk.h +++ b/include/uapi/linux/gfs2_ondisk.h @@ -20,7 +20,7 @@ #define GFS2_MOUNT_LOCK 0 #define GFS2_LIVE_LOCK 1 -#define GFS2_TRANS_LOCK 2 +#define GFS2_FREEZE_LOCK 2 #define GFS2_RENAME_LOCK 3 #define GFS2_CONTROL_LOCK 4 #define GFS2_MOUNTED_LOCK 5 -- cgit v1.2.3 From b02ef20a9fba08948e643d3eec0efadf1da01a44 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 12 May 2014 18:24:45 +0200 Subject: uprobes/x86: Fix the wrong ->si_addr when xol triggers a trap If the probed insn triggers a trap, ->si_addr = regs->ip is technically correct, but this is not what the signal handler wants; we need to pass the address of the probed insn, not the address of xol slot. Add the new arch-agnostic helper, uprobe_get_trap_addr(), and change fill_trap_info() and math_error() to use it. !CONFIG_UPROBES case in uprobes.h uses a macro to avoid include hell and ensure that it can be compiled even if an architecture doesn't define instruction_pointer(). Test-case: #include #include #include extern void probe_div(void); void sigh(int sig, siginfo_t *info, void *c) { int passed = (info->si_addr == probe_div); printf(passed ? "PASS\n" : "FAIL\n"); _exit(!passed); } int main(void) { struct sigaction sa = { .sa_sigaction = sigh, .sa_flags = SA_SIGINFO, }; sigaction(SIGFPE, &sa, NULL); asm ( "xor %ecx,%ecx\n" ".globl probe_div; probe_div:\n" "idiv %ecx\n" ); return 0; } it fails if probe_div() is probed. Note: show_unhandled_signals users should probably use this helper too, but we need to cleanup them first. Signed-off-by: Oleg Nesterov Reviewed-by: Masami Hiramatsu --- arch/x86/kernel/traps.c | 7 ++++--- include/linux/uprobes.h | 4 ++++ kernel/events/uprobes.c | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 73b3ea32245a..3fdb20548c4b 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -148,11 +149,11 @@ static siginfo_t *fill_trap_info(struct pt_regs *regs, int signr, int trapnr, case X86_TRAP_DE: sicode = FPE_INTDIV; - siaddr = regs->ip; + siaddr = uprobe_get_trap_addr(regs); break; case X86_TRAP_UD: sicode = ILL_ILLOPN; - siaddr = regs->ip; + siaddr = uprobe_get_trap_addr(regs); break; case X86_TRAP_AC: sicode = BUS_ADRALN; @@ -531,7 +532,7 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr) task->thread.error_code = error_code; info.si_signo = SIGFPE; info.si_errno = 0; - info.si_addr = (void __user *)regs->ip; + info.si_addr = (void __user *)uprobe_get_trap_addr(regs); if (trapnr == X86_TRAP_MF) { unsigned short cwd, swd; /* diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index edff2b97b864..88c3b7e8b384 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -102,6 +102,7 @@ extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, u extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern bool __weak is_trap_insn(uprobe_opcode_t *insn); extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); +extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs); extern int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); @@ -130,6 +131,9 @@ extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *r #else /* !CONFIG_UPROBES */ struct uprobes_state { }; + +#define uprobe_get_trap_addr(regs) instruction_pointer(regs) + static inline int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index a13251e8bfa4..3b02c72938a8 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1351,6 +1351,16 @@ unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs) return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE; } +unsigned long uprobe_get_trap_addr(struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + if (unlikely(utask && utask->active_uprobe)) + return utask->vaddr; + + return instruction_pointer(regs); +} + /* * Called with no locks held. * Called in context of a exiting or a exec-ing thread. -- cgit v1.2.3 From 9d800df12d31734a6853915e9d2deb5d6747985f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 14 May 2014 09:15:00 -0400 Subject: cgroup: rename cgroup->dummy_css to ->self and move it to the top cgroup->dummy_css is used as the placeholder css when performing css oriended operations on the cgroup. We're gonna shift more cgroup management to this css. Let's rename it to ->self and move it to the top. This is pure rename and field relocation. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 6 +++--- kernel/cgroup.c | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index aa7353deaaf3..164851e388e7 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -143,6 +143,9 @@ enum { }; struct cgroup { + /* self css with NULL ->ss, points back to this cgroup */ + struct cgroup_subsys_state self; + unsigned long flags; /* "unsigned long" so bitops work */ /* @@ -224,9 +227,6 @@ struct cgroup { struct list_head pidlists; struct mutex pidlist_mutex; - /* dummy css with NULL ->ss, points back to this cgroup */ - struct cgroup_subsys_state dummy_css; - /* For css percpu_ref killing and RCU-protected deletion */ struct rcu_head rcu_head; struct work_struct destroy_work; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index f36fd9c15b3a..b57a949ae4bc 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -220,7 +220,7 @@ static void cgroup_idr_remove(struct idr *idr, int id) /** * cgroup_css - obtain a cgroup's css for the specified subsystem * @cgrp: the cgroup of interest - * @ss: the subsystem of interest (%NULL returns the dummy_css) + * @ss: the subsystem of interest (%NULL returns @cgrp->self) * * Return @cgrp's css (cgroup_subsys_state) associated with @ss. This * function must be called either under cgroup_mutex or rcu_read_lock() and @@ -235,13 +235,13 @@ static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, return rcu_dereference_check(cgrp->subsys[ss->id], lockdep_is_held(&cgroup_mutex)); else - return &cgrp->dummy_css; + return &cgrp->self; } /** * cgroup_e_css - obtain a cgroup's effective css for the specified subsystem * @cgrp: the cgroup of interest - * @ss: the subsystem of interest (%NULL returns the dummy_css) + * @ss: the subsystem of interest (%NULL returns @cgrp->self) * * Similar to cgroup_css() but returns the effctive css, which is defined * as the matching css of the nearest ancestor including self which has @ss @@ -254,7 +254,7 @@ static struct cgroup_subsys_state *cgroup_e_css(struct cgroup *cgrp, lockdep_assert_held(&cgroup_mutex); if (!ss) - return &cgrp->dummy_css; + return &cgrp->self; if (!(cgrp->root->subsys_mask & (1 << ss->id))) return NULL; @@ -288,7 +288,7 @@ struct cgroup_subsys_state *of_css(struct kernfs_open_file *of) if (cft->ss) return rcu_dereference_raw(cgrp->subsys[cft->ss->id]); else - return &cgrp->dummy_css; + return &cgrp->self; } EXPORT_SYMBOL_GPL(of_css); @@ -1551,7 +1551,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) INIT_LIST_HEAD(&cgrp->release_list); INIT_LIST_HEAD(&cgrp->pidlists); mutex_init(&cgrp->pidlist_mutex); - cgrp->dummy_css.cgroup = cgrp; + cgrp->self.cgroup = cgrp; for_each_subsys(ss, ssid) INIT_LIST_HEAD(&cgrp->e_csets[ssid]); @@ -3454,7 +3454,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) * ->can_attach() fails. */ do { - css_task_iter_start(&from->dummy_css, &it); + css_task_iter_start(&from->self, &it); task = css_task_iter_next(&it); if (task) get_task_struct(task); @@ -3719,7 +3719,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, if (!array) return -ENOMEM; /* now, populate the array */ - css_task_iter_start(&cgrp->dummy_css, &it); + css_task_iter_start(&cgrp->self, &it); while ((tsk = css_task_iter_next(&it))) { if (unlikely(n == length)) break; @@ -3793,7 +3793,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) } rcu_read_unlock(); - css_task_iter_start(&cgrp->dummy_css, &it); + css_task_iter_start(&cgrp->self, &it); while ((tsk = css_task_iter_next(&it))) { switch (tsk->state) { case TASK_RUNNING: @@ -4274,7 +4274,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, init_cgroup_housekeeping(cgrp); cgrp->parent = parent; - cgrp->dummy_css.parent = &parent->dummy_css; + cgrp->self.parent = &parent->self; cgrp->root = root; if (notify_on_release(parent)) -- cgit v1.2.3 From 249f3468a282dcbad53484c821bebb447f14ee03 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 14 May 2014 09:15:01 -0400 Subject: cgroup: remove cgroup_destory_css_killed() cgroup_destroy_css_killed() is cgroup destruction stage which happens after all csses are offlined. After the recent updates, it no longer does anything other than putting the base reference. This patch removes the function and makes cgroup_destroy_locked() put the base ref at the end isntead. This also makes cgroup->nr_css unnecessary. Removed. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 3 --- kernel/cgroup.c | 62 +++++--------------------------------------------- 2 files changed, 6 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 164851e388e7..160fcc69149e 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -158,9 +158,6 @@ struct cgroup { */ int id; - /* the number of attached css's */ - int nr_css; - /* * If this cgroup contains any tasks, it contributes one to * populated_cnt. All children with non-zero popuplated_cnt of diff --git a/kernel/cgroup.c b/kernel/cgroup.c index e9aa2a51ca68..4a94b0be598d 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -178,7 +178,6 @@ static struct cftype cgroup_base_files[]; static void cgroup_put(struct cgroup *cgrp); static int rebind_subsystems(struct cgroup_root *dst_root, unsigned int ss_mask); -static void cgroup_destroy_css_killed(struct cgroup *cgrp); static int cgroup_destroy_locked(struct cgroup *cgrp); static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss); static void kill_css(struct cgroup_subsys_state *css); @@ -4169,7 +4168,6 @@ static int online_css(struct cgroup_subsys_state *css) ret = ss->css_online(css); if (!ret) { css->flags |= CSS_ONLINE; - css->cgroup->nr_css++; rcu_assign_pointer(css->cgroup->subsys[ss->id], css); } return ret; @@ -4189,7 +4187,6 @@ static void offline_css(struct cgroup_subsys_state *css) ss->css_offline(css); css->flags &= ~CSS_ONLINE; - css->cgroup->nr_css--; RCU_INIT_POINTER(css->cgroup->subsys[ss->id], NULL); wake_up_all(&css->cgroup->offline_waitq); @@ -4374,39 +4371,18 @@ out_destroy: /* * This is called when the refcnt of a css is confirmed to be killed. - * css_tryget_online() is now guaranteed to fail. + * css_tryget_online() is now guaranteed to fail. Tell the subsystem to + * initate destruction and put the css ref from kill_css(). */ static void css_killed_work_fn(struct work_struct *work) { struct cgroup_subsys_state *css = container_of(work, struct cgroup_subsys_state, destroy_work); - struct cgroup *cgrp = css->cgroup; mutex_lock(&cgroup_mutex); - - /* - * css_tryget_online() is guaranteed to fail now. Tell subsystems - * to initate destruction. - */ offline_css(css); - - /* - * If @cgrp is marked dead, it's waiting for refs of all css's to - * be disabled before proceeding to the second phase of cgroup - * destruction. If we are the last one, kick it off. - */ - if (!cgrp->nr_css && cgroup_is_dead(cgrp)) - cgroup_destroy_css_killed(cgrp); - mutex_unlock(&cgroup_mutex); - /* - * Put the css refs from kill_css(). Each css holds an extra - * reference to the cgroup's dentry and cgroup removal proceeds - * regardless of css refs. On the last put of each css, whenever - * that may be, the extra dentry ref is put so that dentry - * destruction happens only after all css's are released. - */ css_put(css); } @@ -4518,11 +4494,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) */ set_bit(CGRP_DEAD, &cgrp->flags); - /* - * Initiate massacre of all css's. cgroup_destroy_css_killed() - * will be invoked to perform the rest of destruction once the - * percpu refs of all css's are confirmed to be killed. - */ + /* initiate massacre of all css's */ for_each_css(css, ssid, cgrp) kill_css(css); @@ -4532,15 +4504,6 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) list_del_init(&cgrp->release_list); raw_spin_unlock(&release_list_lock); - /* - * If @cgrp has css's attached, the second stage of cgroup - * destruction is kicked off from css_killed_work_fn() after the - * refs of all attached css's are killed. If @cgrp doesn't have - * any css, we kick it off here. - */ - if (!cgrp->nr_css) - cgroup_destroy_css_killed(cgrp); - /* * Remove @cgrp directory along with the base files. @cgrp has an * extra ref on its kn. @@ -4550,25 +4513,12 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) set_bit(CGRP_RELEASABLE, &cgrp->parent->flags); check_for_release(cgrp->parent); + /* put the base reference */ + cgroup_put(cgrp); + return 0; }; -/** - * cgroup_destroy_css_killed - the second step of cgroup destruction - * @cgrp: the cgroup whose csses have just finished offlining - * - * This function is invoked from a work item for a cgroup which is being - * destroyed after all css's are offlined and performs the rest of - * destruction. This is the second step of destruction described in the - * comment above cgroup_destroy_locked(). - */ -static void cgroup_destroy_css_killed(struct cgroup *cgrp) -{ - lockdep_assert_held(&cgroup_mutex); - - cgroup_put(cgrp); -} - static int cgroup_rmdir(struct kernfs_node *kn) { struct cgroup *cgrp; -- cgit v1.2.3 From 9395a4500404e05173eda9a2d198b6fa500e90c5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 14 May 2014 09:15:02 -0400 Subject: cgroup: enable refcnting for root csses Currently, css_get(), css_tryget() and css_tryget_online() are noops for root csses as an optimization; however, we're planning to use css refcnts to track of cgroup lifetime too and root cgroups also need to be reference counted. Since css has been converted to percpu_refcnt, the overhead of refcnting is miniscule and this optimization isn't too meaningful anymore. Furthermore, controllers which optimize the root cgroup often never even invoke these functions in their hot paths. This patch enables refcnting for root csses too. This makes CSS_ROOT flag unused and removes it. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 10 ++-------- kernel/cgroup.c | 6 +++--- 2 files changed, 5 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 160fcc69149e..286e39e4e9bf 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -77,7 +77,6 @@ struct cgroup_subsys_state { /* bits in struct cgroup_subsys_state flags field */ enum { - CSS_ROOT = (1 << 0), /* this CSS is the root of the subsystem */ CSS_ONLINE = (1 << 1), /* between ->css_online() and ->css_offline() */ }; @@ -89,9 +88,7 @@ enum { */ static inline void css_get(struct cgroup_subsys_state *css) { - /* We don't need to reference count the root state */ - if (!(css->flags & CSS_ROOT)) - percpu_ref_get(&css->refcnt); + percpu_ref_get(&css->refcnt); } /** @@ -106,8 +103,6 @@ static inline void css_get(struct cgroup_subsys_state *css) */ static inline bool css_tryget_online(struct cgroup_subsys_state *css) { - if (css->flags & CSS_ROOT) - return true; return percpu_ref_tryget_live(&css->refcnt); } @@ -119,8 +114,7 @@ static inline bool css_tryget_online(struct cgroup_subsys_state *css) */ static inline void css_put(struct cgroup_subsys_state *css) { - if (!(css->flags & CSS_ROOT)) - percpu_ref_put(&css->refcnt); + percpu_ref_put(&css->refcnt); } /* bits in struct cgroup flags field */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index e694f4153edb..cb5864e36f99 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4158,8 +4158,6 @@ static void init_and_link_css(struct cgroup_subsys_state *css, if (cgrp->parent) { css->parent = cgroup_css(cgrp->parent, ss); css_get(css->parent); - } else { - css->flags |= CSS_ROOT; } BUG_ON(cgroup_css(cgrp, ss)); @@ -4582,9 +4580,10 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) BUG_ON(IS_ERR(css)); init_and_link_css(css, ss, &cgrp_dfl_root.cgrp); if (early) { - /* idr_alloc() can't be called safely during early init */ + /* allocation can't be done safely during early init */ css->id = 1; } else { + BUG_ON(percpu_ref_init(&css->refcnt, css_release)); css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, GFP_KERNEL); BUG_ON(css->id < 0); } @@ -4671,6 +4670,7 @@ int __init cgroup_init(void) struct cgroup_subsys_state *css = init_css_set.subsys[ss->id]; + BUG_ON(percpu_ref_init(&css->refcnt, css_release)); css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, GFP_KERNEL); BUG_ON(css->id < 0); -- cgit v1.2.3 From 9d755d33f0db8c9b49438f71b38a56e375b34360 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 14 May 2014 09:15:02 -0400 Subject: cgroup: use cgroup->self.refcnt for cgroup refcnting Currently cgroup implements refcnting separately using atomic_t cgroup->refcnt. The destruction paths of cgroup and css are rather complex and bear a lot of similiarities including the use of RCU and bouncing to a work item. This patch makes cgroup use the refcnt of self css for refcnting instead of using its own. This makes cgroup refcnting use css's percpu refcnt and share the destruction mechanism. * css_release_work_fn() and css_free_work_fn() are updated to handle both csses and cgroups. This is a bit messy but should do until we can make cgroup->self a full css, which currently can't be done thanks to multiple hierarchies. * cgroup_destroy_locked() now performs percpu_ref_kill(&cgrp->self.refcnt) instead of cgroup_put(cgrp). * Negative refcnt sanity check in cgroup_get() is no longer necessary as percpu_ref already handles it. * Similarly, as a cgroup which hasn't been killed will never be released regardless of its refcnt value and percpu_ref has sanity check on kill, cgroup_is_dead() sanity check in cgroup_put() is no longer necessary. * As whether a refcnt reached zero or not can only be decided after the reference count is killed, cgroup_root->cgrp's refcnting can no longer be used to decide whether to kill the root or not. Let's make cgroup_kill_sb() explicitly initiate destruction if the root doesn't have any children. This makes sense anyway as unmounted cgroup hierarchy without any children should be destroyed. While this is a bit messy, this will allow pushing more bookkeeping towards cgroup->self and thus handling cgroups and csses in more uniform way. In the very long term, it should be possible to introduce a base subsystem and convert the self css to a proper one making things whole lot simpler and unified. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 6 -- kernel/cgroup.c | 146 +++++++++++++++++++++++++++---------------------- 2 files changed, 80 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 286e39e4e9bf..76dadd77a120 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -160,8 +160,6 @@ struct cgroup { */ int populated_cnt; - atomic_t refcnt; - /* * We link our 'sibling' struct into our parent's 'children'. * Our children link their 'sibling' into our 'children'. @@ -218,10 +216,6 @@ struct cgroup { struct list_head pidlists; struct mutex pidlist_mutex; - /* For css percpu_ref killing and RCU-protected deletion */ - struct rcu_head rcu_head; - struct work_struct destroy_work; - /* used to wait for offlining of csses */ wait_queue_head_t offline_waitq; }; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index cb5864e36f99..c01e8e8dfad0 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -176,10 +176,12 @@ static int need_forkexit_callback __read_mostly; static struct cftype cgroup_base_files[]; static void cgroup_put(struct cgroup *cgrp); +static bool cgroup_has_live_children(struct cgroup *cgrp); static int rebind_subsystems(struct cgroup_root *dst_root, unsigned int ss_mask); static int cgroup_destroy_locked(struct cgroup *cgrp); static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss); +static void css_release(struct percpu_ref *ref); static void kill_css(struct cgroup_subsys_state *css); static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], bool is_add); @@ -1008,62 +1010,15 @@ static umode_t cgroup_file_mode(const struct cftype *cft) return mode; } -static void cgroup_free_fn(struct work_struct *work) -{ - struct cgroup *cgrp = container_of(work, struct cgroup, destroy_work); - - atomic_dec(&cgrp->root->nr_cgrps); - cgroup_pidlist_destroy_all(cgrp); - - if (cgrp->parent) { - /* - * We get a ref to the parent, and put the ref when this - * cgroup is being freed, so it's guaranteed that the - * parent won't be destroyed before its children. - */ - cgroup_put(cgrp->parent); - kernfs_put(cgrp->kn); - kfree(cgrp); - } else { - /* - * This is root cgroup's refcnt reaching zero, which - * indicates that the root should be released. - */ - cgroup_destroy_root(cgrp->root); - } -} - -static void cgroup_free_rcu(struct rcu_head *head) -{ - struct cgroup *cgrp = container_of(head, struct cgroup, rcu_head); - - INIT_WORK(&cgrp->destroy_work, cgroup_free_fn); - queue_work(cgroup_destroy_wq, &cgrp->destroy_work); -} - static void cgroup_get(struct cgroup *cgrp) { WARN_ON_ONCE(cgroup_is_dead(cgrp)); - WARN_ON_ONCE(atomic_read(&cgrp->refcnt) <= 0); - atomic_inc(&cgrp->refcnt); + css_get(&cgrp->self); } static void cgroup_put(struct cgroup *cgrp) { - if (!atomic_dec_and_test(&cgrp->refcnt)) - return; - if (WARN_ON_ONCE(cgrp->parent && !cgroup_is_dead(cgrp))) - return; - - /* delete this cgroup from parent->children */ - mutex_lock(&cgroup_mutex); - list_del_rcu(&cgrp->sibling); - mutex_unlock(&cgroup_mutex); - - cgroup_idr_remove(&cgrp->root->cgroup_idr, cgrp->id); - cgrp->id = -1; - - call_rcu(&cgrp->rcu_head, cgroup_free_rcu); + css_put(&cgrp->self); } /** @@ -1548,7 +1503,6 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) struct cgroup_subsys *ss; int ssid; - atomic_set(&cgrp->refcnt, 1); INIT_LIST_HEAD(&cgrp->sibling); INIT_LIST_HEAD(&cgrp->children); INIT_LIST_HEAD(&cgrp->cset_links); @@ -1597,6 +1551,10 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned int ss_mask) goto out; root_cgrp->id = ret; + ret = percpu_ref_init(&root_cgrp->self.refcnt, css_release); + if (ret) + goto out; + /* * We're accessing css_set_count without locking css_set_rwsem here, * but that's OK - it can only be increased by someone holding @@ -1605,11 +1563,11 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned int ss_mask) */ ret = allocate_cgrp_cset_links(css_set_count, &tmp_links); if (ret) - goto out; + goto cancel_ref; ret = cgroup_init_root_id(root); if (ret) - goto out; + goto cancel_ref; root->kf_root = kernfs_create_root(&cgroup_kf_syscall_ops, KERNFS_ROOT_CREATE_DEACTIVATED, @@ -1657,6 +1615,8 @@ destroy_root: root->kf_root = NULL; exit_root_id: cgroup_exit_root_id(root); +cancel_ref: + percpu_ref_cancel_init(&root_cgrp->self.refcnt); out: free_cgrp_cset_links(&tmp_links); return ret; @@ -1735,13 +1695,14 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, } /* - * A root's lifetime is governed by its root cgroup. Zero - * ref indicate that the root is being destroyed. Wait for - * destruction to complete so that the subsystems are free. - * We can use wait_queue for the wait but this path is - * super cold. Let's just sleep for a bit and retry. + * A root's lifetime is governed by its root cgroup. + * tryget_live failure indicate that the root is being + * destroyed. Wait for destruction to complete so that the + * subsystems are free. We can use wait_queue for the wait + * but this path is super cold. Let's just sleep for a bit + * and retry. */ - if (!atomic_inc_not_zero(&root->cgrp.refcnt)) { + if (!percpu_ref_tryget_live(&root->cgrp.self.refcnt)) { mutex_unlock(&cgroup_mutex); msleep(10); ret = restart_syscall(); @@ -1794,7 +1755,16 @@ static void cgroup_kill_sb(struct super_block *sb) struct kernfs_root *kf_root = kernfs_root_from_sb(sb); struct cgroup_root *root = cgroup_root_from_kf(kf_root); - cgroup_put(&root->cgrp); + /* + * If @root doesn't have any mounts or children, start killing it. + * This prevents new mounts by disabling percpu_ref_tryget_live(). + * cgroup_mount() may wait for @root's release. + */ + if (cgroup_has_live_children(&root->cgrp)) + cgroup_put(&root->cgrp); + else + percpu_ref_kill(&root->cgrp.self.refcnt); + kernfs_kill_sb(sb); } @@ -4110,11 +4080,37 @@ static void css_free_work_fn(struct work_struct *work) container_of(work, struct cgroup_subsys_state, destroy_work); struct cgroup *cgrp = css->cgroup; - if (css->parent) - css_put(css->parent); + if (css->ss) { + /* css free path */ + if (css->parent) + css_put(css->parent); - css->ss->css_free(css); - cgroup_put(cgrp); + css->ss->css_free(css); + cgroup_put(cgrp); + } else { + /* cgroup free path */ + atomic_dec(&cgrp->root->nr_cgrps); + cgroup_pidlist_destroy_all(cgrp); + + if (cgrp->parent) { + /* + * We get a ref to the parent, and put the ref when + * this cgroup is being freed, so it's guaranteed + * that the parent won't be destroyed before its + * children. + */ + cgroup_put(cgrp->parent); + kernfs_put(cgrp->kn); + kfree(cgrp); + } else { + /* + * This is root cgroup's refcnt reaching zero, + * which indicates that the root should be + * released. + */ + cgroup_destroy_root(cgrp->root); + } + } } static void css_free_rcu_fn(struct rcu_head *rcu_head) @@ -4131,8 +4127,20 @@ static void css_release_work_fn(struct work_struct *work) struct cgroup_subsys_state *css = container_of(work, struct cgroup_subsys_state, destroy_work); struct cgroup_subsys *ss = css->ss; + struct cgroup *cgrp = css->cgroup; - cgroup_idr_remove(&ss->css_idr, css->id); + if (ss) { + /* css release path */ + cgroup_idr_remove(&ss->css_idr, css->id); + } else { + /* cgroup release path */ + mutex_lock(&cgroup_mutex); + list_del_rcu(&cgrp->sibling); + mutex_unlock(&cgroup_mutex); + + cgroup_idr_remove(&cgrp->root->cgroup_idr, cgrp->id); + cgrp->id = -1; + } call_rcu(&css->rcu_head, css_free_rcu_fn); } @@ -4285,6 +4293,10 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, goto out_unlock; } + ret = percpu_ref_init(&cgrp->self.refcnt, css_release); + if (ret) + goto out_free_cgrp; + /* * Temporarily set the pointer to NULL, so idr_find() won't return * a half-baked cgroup. @@ -4292,7 +4304,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, cgrp->id = cgroup_idr_alloc(&root->cgroup_idr, NULL, 2, 0, GFP_NOWAIT); if (cgrp->id < 0) { ret = -ENOMEM; - goto out_free_cgrp; + goto out_cancel_ref; } init_cgroup_housekeeping(cgrp); @@ -4365,6 +4377,8 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, out_free_id: cgroup_idr_remove(&root->cgroup_idr, cgrp->id); +out_cancel_ref: + percpu_ref_cancel_init(&cgrp->self.refcnt); out_free_cgrp: kfree(cgrp); out_unlock: @@ -4521,7 +4535,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) check_for_release(cgrp->parent); /* put the base reference */ - cgroup_put(cgrp); + percpu_ref_kill(&cgrp->self.refcnt); return 0; }; -- cgit v1.2.3 From 19824d5eeecedfb46639961da1b7a21ba3179930 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 8 May 2014 14:06:22 +0200 Subject: usb: gadget: OS String support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a custom (non-USB IF) extension to the USB standard: http://msdn.microsoft.com/library/windows/hardware/gg463182 They grant permission to use the specification - there is "Microsoft OS Descriptor Specification License Agreement" under the link mentioned above, and its Section 2 "Grant of License", letter (b) reads: "Patent license. Microsoft hereby grants to You a nonexclusive, royalty-free, nontransferable, worldwide license under Microsoft’s patents embodied solely within the Specification and that are owned or licensable by Microsoft to make, use, import, offer to sell, sell and distribute directly or indirectly to Your Licensees Your Implementation. You may sublicense this patent license to Your Licensees under the same terms and conditions." The said extension is maintained by Microsoft for Microsoft. Yet it is fairly common for various devices to use it, and a popular proprietary operating system expects devices to provide "OS descriptors", so Linux-based USB gadgets whishing to be able to talk to a variety of operating systems should be able to provide the "OS descriptors". This patch adds optional support for gadgets whishing to expose the so called "OS String" under index 0xEE of language 0. The contents of the string is generated based on the qw_sign array and b_vendor_code. Interested gadgets need to set the cdev->use_os_string flag, fill cdev->qw_sign with appropriate values and fill cdev->b_vendor_code with a value of their choice. This patch does not however implement responding to any vendor-specific USB requests. Signed-off-by: Andrzej Pietrasiewicz Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 29 +++++++++++++++++++++++++++++ include/linux/usb/composite.h | 11 +++++++++++ 2 files changed, 40 insertions(+) (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 8060de6562cd..2f87b1697bf5 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -21,6 +21,22 @@ #include #include +/** + * struct usb_os_string - represents OS String to be reported by a gadget + * @bLength: total length of the entire descritor, always 0x12 + * @bDescriptorType: USB_DT_STRING + * @qwSignature: the OS String proper + * @bMS_VendorCode: code used by the host for subsequent requests + * @bPad: not used, must be zero + */ +struct usb_os_string { + __u8 bLength; + __u8 bDescriptorType; + __u8 qwSignature[OS_STRING_QW_SIGN_LEN]; + __u8 bMS_VendorCode; + __u8 bPad; +} __packed; + /* * The code in this file is utility code, used to build a gadget driver * from one or more "function" drivers, one or more "configuration" @@ -961,6 +977,19 @@ static int get_string(struct usb_composite_dev *cdev, return s->bLength; } + if (cdev->use_os_string && language == 0 && id == OS_STRING_IDX) { + struct usb_os_string *b = buf; + b->bLength = sizeof(*b); + b->bDescriptorType = USB_DT_STRING; + compiletime_assert( + sizeof(b->qwSignature) == sizeof(cdev->qw_sign), + "qwSignature size must be equal to qw_sign"); + memcpy(&b->qwSignature, cdev->qw_sign, sizeof(b->qwSignature)); + b->bMS_VendorCode = cdev->b_vendor_code; + b->bPad = 0; + return sizeof(*b); + } + list_for_each_entry(uc, &cdev->gstrings, list) { struct usb_gadget_strings **sp; diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index d3ca3b53837c..7d29ee9363e8 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -335,11 +335,17 @@ static inline struct usb_composite_driver *to_cdriver( return container_of(gdrv, struct usb_composite_driver, gadget_driver); } +#define OS_STRING_QW_SIGN_LEN 14 +#define OS_STRING_IDX 0xEE + /** * struct usb_composite_device - represents one composite usb gadget * @gadget: read-only, abstracts the gadget's usb peripheral controller * @req: used for control responses; buffer is pre-allocated * @config: the currently active configuration + * @qw_sign: qwSignature part of the OS string + * @b_vendor_code: bMS_VendorCode part of the OS string + * @use_os_string: false by default, interested gadgets set it * * One of these devices is allocated and initialized before the * associated device driver's bind() is called. @@ -372,6 +378,11 @@ struct usb_composite_dev { struct usb_configuration *config; + /* OS String is a custom (yet popular) extension to the USB standard. */ + u8 qw_sign[OS_STRING_QW_SIGN_LEN]; + u8 b_vendor_code; + unsigned int use_os_string:1; + /* private: */ /* internals */ unsigned int suspended:1; -- cgit v1.2.3 From 37a3a533429ef9b3cc9f15a656c19623f0e88df7 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 8 May 2014 14:06:23 +0200 Subject: usb: gadget: OS Feature Descriptors support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a custom (non-USB IF) extension to the USB standard: http://msdn.microsoft.com/library/windows/hardware/gg463182 They grant permission to use the specification - there is "Microsoft OS Descriptor Specification License Agreement" under the link mentioned above, and its Section 2 "Grant of License", letter (b) reads: "Patent license. Microsoft hereby grants to You a nonexclusive, royalty-free, nontransferable, worldwide license under Microsoft’s patents embodied solely within the Specification and that are owned or licensable by Microsoft to make, use, import, offer to sell, sell and distribute directly or indirectly to Your Licensees Your Implementation. You may sublicense this patent license to Your Licensees under the same terms and conditions." The said extension is maintained by Microsoft for Microsoft. Yet it is fairly common for various devices to use it, and a popular proprietary operating system expects devices to provide "OS descriptors", so Linux-based USB gadgets whishing to be able to talk to a variety of operating systems should be able to provide the "OS descriptors". This patch adds optional support for gadgets whishing to expose the so called "OS Feature Descriptors", that is "Extended Compatibility ID" and "Extended Properties". Hosts which do request "OS descriptors" from gadgets do so during the enumeration phase and before the configuration is set with SET_CONFIGURATION. What is more, those hosts never ask for configurations at indices other than 0. Therefore, gadgets whishing to provide "OS descriptors" must designate one configuration to be used with this kind of hosts - this is what os_desc_config is added for in struct usb_composite_dev. There is an additional advantage to it: if a gadget provides "OS descriptors" and designates one configuration to be used with such non-USB-compliant hosts it can invoke "usb_add_config" in any order because the designated configuration will be reported to be at index 0 anyway. This patch also adds handling vendor-specific requests addressed at device or interface and related to handling "OS descriptors". Signed-off-by: Andrzej Pietrasiewicz Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 288 ++++++++++++++++++++++++++++++++++++++++- drivers/usb/gadget/u_os_desc.h | 90 +++++++++++++ include/linux/usb/composite.h | 58 +++++++++ 3 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/gadget/u_os_desc.h (limited to 'include') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 2f87b1697bf5..042c66b71df8 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -21,6 +21,8 @@ #include #include +#include "u_os_desc.h" + /** * struct usb_os_string - represents OS String to be reported by a gadget * @bLength: total length of the entire descritor, always 0x12 @@ -438,6 +440,7 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) { struct usb_gadget *gadget = cdev->gadget; struct usb_configuration *c; + struct list_head *pos; u8 type = w_value >> 8; enum usb_device_speed speed = USB_SPEED_UNKNOWN; @@ -456,7 +459,20 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) /* This is a lookup by config *INDEX* */ w_value &= 0xff; - list_for_each_entry(c, &cdev->configs, list) { + + pos = &cdev->configs; + c = cdev->os_desc_config; + if (c) + goto check_config; + + while ((pos = pos->next) != &cdev->configs) { + c = list_entry(pos, typeof(*c), list); + + /* skip OS Descriptors config which is handled separately */ + if (c == cdev->os_desc_config) + continue; + +check_config: /* ignore configs that won't work at this speed */ switch (speed) { case USB_SPEED_SUPER: @@ -1236,6 +1252,158 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) req->status, req->actual, req->length); } +static int count_ext_compat(struct usb_configuration *c) +{ + int i, res; + + res = 0; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + ++res; + } + } + BUG_ON(res > 255); + return res; +} + +static void fill_ext_compat(struct usb_configuration *c, u8 *buf) +{ + int i, count; + + count = 16; + for (i = 0; i < c->next_interface_id; ++i) { + struct usb_function *f; + int j; + + f = c->interface[i]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (i != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) { + *buf++ = i; + *buf++ = 0x01; + memcpy(buf, d->ext_compat_id, 16); + buf += 22; + } else { + ++buf; + *buf = 0x01; + buf += 23; + } + count += 24; + if (count >= 4096) + return; + } + } +} + +static int count_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + int j, res; + + res = 0; + + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + struct usb_os_desc *d; + + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d && d->ext_compat_id) + return d->ext_prop_count; + } + return res; +} + +static int len_ext_prop(struct usb_configuration *c, int interface) +{ + struct usb_function *f; + struct usb_os_desc *d; + int j, res; + + res = 10; /* header length */ + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + return min(res + d->ext_prop_len, 4096); + } + return res; +} + +static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) +{ + struct usb_function *f; + struct usb_os_desc *d; + struct usb_os_desc_ext_prop *ext_prop; + int j, count, n, ret; + u8 *start = buf; + + f = c->interface[interface]; + for (j = 0; j < f->os_desc_n; ++j) { + if (interface != f->os_desc_table[j].if_id) + continue; + d = f->os_desc_table[j].os_desc; + if (d) + list_for_each_entry(ext_prop, &d->ext_prop, entry) { + /* 4kB minus header length */ + n = buf - start; + if (n >= 4086) + return 0; + + count = ext_prop->data_len + + ext_prop->name_len + 14; + if (count > 4086 - n) + return -EINVAL; + usb_ext_prop_put_size(buf, count); + usb_ext_prop_put_type(buf, ext_prop->type); + ret = usb_ext_prop_put_name(buf, ext_prop->name, + ext_prop->name_len); + if (ret < 0) + return ret; + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + usb_ext_prop_put_unicode(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_BINARY: + usb_ext_prop_put_binary(buf, ret, + ext_prop->data, + ext_prop->data_len); + break; + case USB_EXT_PROP_LE32: + /* not implemented */ + case USB_EXT_PROP_BE32: + /* not implemented */ + default: + return -EINVAL; + } + buf += count; + } + } + + return 0; +} + /* * The setup() callback implements all the ep0 functionality that's * not handled lower down, in hardware or the hardware driver(like @@ -1445,6 +1613,91 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) break; default: unknown: + /* + * OS descriptors handling + */ + if (cdev->use_os_string && cdev->os_desc_config && + (ctrl->bRequest & USB_TYPE_VENDOR) && + ctrl->bRequest == cdev->b_vendor_code) { + struct usb_request *req; + struct usb_configuration *os_desc_cfg; + u8 *buf; + int interface; + int count = 0; + + req = cdev->os_desc_req; + req->complete = composite_setup_complete; + buf = req->buf; + os_desc_cfg = cdev->os_desc_config; + memset(buf, 0, w_length); + buf[5] = 0x01; + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + if (w_index != 0x4 || (w_value >> 8)) + break; + buf[6] = w_index; + if (w_length == 0x10) { + /* Number of ext compat interfaces */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + value = w_length; + } else { + /* "extended compatibility ID"s */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + buf += 16; + fill_ext_compat(os_desc_cfg, buf); + value = w_length; + } + break; + case USB_RECIP_INTERFACE: + if (w_index != 0x5 || (w_value >> 8)) + break; + interface = w_value & 0xFF; + buf[6] = w_index; + if (w_length == 0x0A) { + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + + value = w_length; + } else { + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + buf += 10; + value = fill_ext_prop(os_desc_cfg, + interface, buf); + if (value < 0) + return value; + + value = w_length; + } + break; + } + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + return value; + } + VDBG(cdev, "non-core control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, @@ -1668,6 +1921,29 @@ fail: return ret; } +int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, + struct usb_ep *ep0) +{ + int ret = 0; + + cdev->os_desc_req = usb_ep_alloc_request(ep0, GFP_KERNEL); + if (!cdev->os_desc_req) { + ret = PTR_ERR(cdev->os_desc_req); + goto end; + } + + /* OS feature descriptor length <= 4kB */ + cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL); + if (!cdev->os_desc_req->buf) { + ret = PTR_ERR(cdev->os_desc_req->buf); + kfree(cdev->os_desc_req); + goto end; + } + cdev->os_desc_req->complete = composite_setup_complete; +end: + return ret; +} + void composite_dev_cleanup(struct usb_composite_dev *cdev) { struct usb_gadget_string_container *uc, *tmp; @@ -1676,6 +1952,10 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) list_del(&uc->list); kfree(uc); } + if (cdev->os_desc_req) { + kfree(cdev->os_desc_req->buf); + usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); + } if (cdev->req) { kfree(cdev->req->buf); usb_ep_free_request(cdev->gadget->ep0, cdev->req); @@ -1713,6 +1993,12 @@ static int composite_bind(struct usb_gadget *gadget, if (status < 0) goto fail; + if (cdev->use_os_string) { + status = composite_os_desc_req_prepare(cdev, gadget->ep0); + if (status) + goto fail; + } + update_unchanged_dev_desc(&cdev->desc, composite->dev); /* has userspace failed to provide a serial number? */ diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h new file mode 100644 index 000000000000..ea5cf8c2da28 --- /dev/null +++ b/drivers/usb/gadget/u_os_desc.h @@ -0,0 +1,90 @@ +/* + * u_os_desc.h + * + * Utility definitions for "OS Descriptors" support + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 __U_OS_DESC_H__ +#define __U_OS_DESC_H__ + +#include +#include + +#define USB_EXT_PROP_DW_SIZE 0 +#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4 +#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8 +#define USB_EXT_PROP_B_PROPERTY_NAME 10 +#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10 +#define USB_EXT_PROP_B_PROPERTY_DATA 14 + +#define USB_EXT_PROP_RESERVED 0 +#define USB_EXT_PROP_UNICODE 1 +#define USB_EXT_PROP_UNICODE_ENV 2 +#define USB_EXT_PROP_BINARY 3 +#define USB_EXT_PROP_LE32 4 +#define USB_EXT_PROP_BE32 5 +#define USB_EXT_PROP_UNICODE_LINK 6 +#define USB_EXT_PROP_UNICODE_MULTI 7 + +static inline void usb_ext_prop_put_size(u8 *buf, int dw_size) +{ + put_unaligned_le32(dw_size, &buf[USB_EXT_PROP_DW_SIZE]); +} + +static inline void usb_ext_prop_put_type(u8 *buf, int type) +{ + put_unaligned_le32(type, &buf[USB_EXT_PROP_DW_PROPERTY_DATA_TYPE]); +} + +static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl) +{ + int result; + + put_unaligned_le16(pnl, &buf[USB_EXT_PROP_W_PROPERTY_NAME_LENGTH]); + result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN, + (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_NAME], pnl - 2); + if (result < 0) + return result; + + put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl]); + + return pnl; +} + +static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data, + int data_len) +{ + put_unaligned_le32(data_len, + &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); + memcpy(&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], data, data_len); +} + +static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string, + int data_len) +{ + int result; + put_unaligned_le32(data_len, + &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]); + + result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN, + (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], + data_len - 2); + if (result < 0) + return result; + + put_unaligned_le16(0, + &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len]); + + return data_len; +} + +#endif /* __U_OS_DESC_H__ */ diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 7d29ee9363e8..549f5382b01a 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -56,6 +56,53 @@ #define USB_MS_TO_HS_INTERVAL(x) (ilog2((x * 1000 / 125)) + 1) struct usb_configuration; +/** + * struct usb_os_desc_ext_prop - describes one "Extended Property" + * @entry: used to keep a list of extended properties + * @type: Extended Property type + * @name_len: Extended Property unicode name length, including terminating '\0' + * @name: Extended Property name + * @data_len: Length of Extended Property blob (for unicode store double len) + * @data: Extended Property blob + */ +struct usb_os_desc_ext_prop { + struct list_head entry; + u8 type; + int name_len; + char *name; + int data_len; + char *data; +}; + +/** + * struct usb_os_desc - describes OS descriptors associated with one interface + * @ext_compat_id: 16 bytes of "Compatible ID" and "Subcompatible ID" + * @ext_prop: Extended Properties list + * @ext_prop_len: Total length of Extended Properties blobs + * @ext_prop_count: Number of Extended Properties + */ +struct usb_os_desc { + char *ext_compat_id; + struct list_head ext_prop; + int ext_prop_len; + int ext_prop_count; +}; + +/** + * struct usb_os_desc_table - describes OS descriptors associated with one + * interface of a usb_function + * @if_id: Interface id + * @os_desc: "Extended Compatibility ID" and "Extended Properties" of the + * interface + * + * Each interface can have at most one "Extended Compatibility ID" and a + * number of "Extended Properties". + */ +struct usb_os_desc_table { + int if_id; + struct usb_os_desc *os_desc; +}; + /** * struct usb_function - describes one function of a configuration * @name: For diagnostics, identifies the function. @@ -73,6 +120,10 @@ struct usb_configuration; * be available at super speed. * @config: assigned when @usb_add_function() is called; this is the * configuration with which this function is associated. + * @os_desc_table: Table of (interface id, os descriptors) pairs. The function + * can expose more than one interface. If an interface is a member of + * an IAD, only the first interface of IAD has its entry in the table. + * @os_desc_n: Number of entries in os_desc_table * @bind: Before the gadget can register, all of its functions bind() to the * available resources including string and interface identifiers used * in interface or class descriptors; endpoints; I/O buffers; and so on. @@ -129,6 +180,9 @@ struct usb_function { struct usb_configuration *config; + struct usb_os_desc_table *os_desc_table; + unsigned os_desc_n; + /* REVISIT: bind() functions can be marked __init, which * makes trouble for section mismatch analysis. See if * we can't restructure things to avoid mismatching. @@ -342,10 +396,12 @@ static inline struct usb_composite_driver *to_cdriver( * struct usb_composite_device - represents one composite usb gadget * @gadget: read-only, abstracts the gadget's usb peripheral controller * @req: used for control responses; buffer is pre-allocated + * @os_desc_req: used for OS descriptors responses; buffer is pre-allocated * @config: the currently active configuration * @qw_sign: qwSignature part of the OS string * @b_vendor_code: bMS_VendorCode part of the OS string * @use_os_string: false by default, interested gadgets set it + * @os_desc_config: the configuration to be used with OS descriptors * * One of these devices is allocated and initialized before the * associated device driver's bind() is called. @@ -375,12 +431,14 @@ static inline struct usb_composite_driver *to_cdriver( struct usb_composite_dev { struct usb_gadget *gadget; struct usb_request *req; + struct usb_request *os_desc_req; struct usb_configuration *config; /* OS String is a custom (yet popular) extension to the USB standard. */ u8 qw_sign[OS_STRING_QW_SIGN_LEN]; u8 b_vendor_code; + struct usb_configuration *os_desc_config; unsigned int use_os_string:1; /* private: */ -- cgit v1.2.3 From da4243145fb197622425d4c2feff5d6422f2391e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 8 May 2014 14:06:26 +0200 Subject: usb: gadget: configfs: OS Extended Compatibility descriptors support Add handling of OS Extended Compatibility descriptors from configfs interface. Hosts which expect the "OS Descriptors" ask only for configurations @ index 0, but linux-based USB devices can provide more than one configuration. This patch adds marking one of gadget's configurations the configuration to be reported at index 0, regardless of the actual sequence of usb_add_config invocations used for adding the configurations. The configuration is selected by creating a symbolic link pointing to it from the "os_desc" directory located at the top of a gadget's directory hierarchy. One kind of "OS Descriptors" are "Extended Compatibility Descriptors", which need to be specified per interface. This patch adds interface. directory in function's configfs directory to represent each interface defined by the function. Each interface's directory contains two attributes: "compatible_id" and "sub_compatible_id", which represent 8-byte strings to be reported to the host as the "Compatible ID" and "Sub Compatible ID". Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/ABI/testing/configfs-usb-gadget | 13 ++ drivers/usb/gadget/configfs.c | 190 ++++++++++++++++++++++++++ drivers/usb/gadget/configfs.h | 12 ++ include/linux/usb/composite.h | 6 + 4 files changed, 221 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/configfs-usb-gadget b/Documentation/ABI/testing/configfs-usb-gadget index 0e7b786f24ac..5c0b3e6eb981 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget +++ b/Documentation/ABI/testing/configfs-usb-gadget @@ -62,6 +62,19 @@ KernelVersion: 3.11 Description: This group contains functions available to this USB gadget. +What: /config/usb-gadget/gadget/functions/./interface. +Date: May 2014 +KernelVersion: 3.16 +Description: + This group contains "Feature Descriptors" specific for one + gadget's USB interface or one interface group described + by an IAD. + + The attributes: + + compatible_id - 8-byte string for "Compatible ID" + sub_compatible_id - 8-byte string for "Sub Compatible ID" + What: /config/usb-gadget/gadget/strings Date: Jun 2013 KernelVersion: 3.11 diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 8b9e038ac22b..fa6cb06cca09 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -6,6 +6,7 @@ #include #include #include "configfs.h" +#include "u_f.h" int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) @@ -872,10 +873,63 @@ static void os_desc_attr_release(struct config_item *item) kfree(os_desc); } +static int os_desc_link(struct config_item *os_desc_ci, + struct config_item *usb_cfg_ci) +{ + struct gadget_info *gi = container_of(to_config_group(os_desc_ci), + struct gadget_info, os_desc_group); + struct usb_composite_dev *cdev = &gi->cdev; + struct config_usb_cfg *c_target = + container_of(to_config_group(usb_cfg_ci), + struct config_usb_cfg, group); + struct usb_configuration *c; + int ret; + + mutex_lock(&gi->lock); + list_for_each_entry(c, &cdev->configs, list) { + if (c == &c_target->c) + break; + } + if (c != &c_target->c) { + ret = -EINVAL; + goto out; + } + + if (cdev->os_desc_config) { + ret = -EBUSY; + goto out; + } + + cdev->os_desc_config = &c_target->c; + ret = 0; + +out: + mutex_unlock(&gi->lock); + return ret; +} + +static int os_desc_unlink(struct config_item *os_desc_ci, + struct config_item *usb_cfg_ci) +{ + struct gadget_info *gi = container_of(to_config_group(os_desc_ci), + struct gadget_info, os_desc_group); + struct usb_composite_dev *cdev = &gi->cdev; + + mutex_lock(&gi->lock); + if (gi->udc_name) + unregister_gadget(gi); + cdev->os_desc_config = NULL; + WARN_ON(gi->udc_name); + mutex_unlock(&gi->lock); + return 0; +} + static struct configfs_item_operations os_desc_ops = { .release = os_desc_attr_release, .show_attribute = os_desc_attr_show, .store_attribute = os_desc_attr_store, + .allow_link = os_desc_link, + .drop_link = os_desc_unlink, }; static struct config_item_type os_desc_type = { @@ -884,6 +938,133 @@ static struct config_item_type os_desc_type = { .ct_owner = THIS_MODULE, }; +CONFIGFS_ATTR_STRUCT(usb_os_desc); +CONFIGFS_ATTR_OPS(usb_os_desc); + +static struct configfs_item_operations interf_item_ops = { + .show_attribute = usb_os_desc_attr_show, + .store_attribute = usb_os_desc_attr_store, +}; + +static ssize_t rndis_grp_compatible_id_show(struct usb_os_desc *desc, + char *page) +{ + memcpy(page, desc->ext_compat_id, 8); + return 8; +} + +static ssize_t rndis_grp_compatible_id_store(struct usb_os_desc *desc, + const char *page, size_t len) +{ + int l; + + l = min_t(int, 8, len); + if (page[l - 1] == '\n') + --l; + 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); + + return len; +} + +static struct usb_os_desc_attribute rndis_grp_attr_compatible_id = + __CONFIGFS_ATTR(compatible_id, S_IRUGO | S_IWUSR, + rndis_grp_compatible_id_show, + rndis_grp_compatible_id_store); + +static ssize_t rndis_grp_sub_compatible_id_show(struct usb_os_desc *desc, + char *page) +{ + memcpy(page, desc->ext_compat_id + 8, 8); + return 8; +} + +static ssize_t rndis_grp_sub_compatible_id_store(struct usb_os_desc *desc, + const char *page, size_t len) +{ + int l; + + l = min_t(int, 8, len); + if (page[l - 1] == '\n') + --l; + 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); + + return len; +} + +static struct usb_os_desc_attribute rndis_grp_attr_sub_compatible_id = + __CONFIGFS_ATTR(sub_compatible_id, S_IRUGO | S_IWUSR, + rndis_grp_sub_compatible_id_show, + rndis_grp_sub_compatible_id_store); + +static struct configfs_attribute *interf_grp_attrs[] = { + &rndis_grp_attr_compatible_id.attr, + &rndis_grp_attr_sub_compatible_id.attr, + NULL +}; + +int usb_os_desc_prepare_interf_dir(struct config_group *parent, + int n_interf, + struct usb_os_desc **desc, + struct module *owner) +{ + struct config_group **f_default_groups, *os_desc_group, + **interface_groups; + struct config_item_type *os_desc_type, *interface_type; + + vla_group(data_chunk); + vla_item(data_chunk, struct config_group *, f_default_groups, 2); + vla_item(data_chunk, struct config_group, os_desc_group, 1); + vla_item(data_chunk, struct config_group *, interface_groups, + n_interf + 1); + vla_item(data_chunk, struct config_item_type, os_desc_type, 1); + vla_item(data_chunk, struct config_item_type, interface_type, 1); + + char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); + if (!vlabuf) + return -ENOMEM; + + f_default_groups = vla_ptr(vlabuf, data_chunk, f_default_groups); + os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group); + os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type); + interface_groups = vla_ptr(vlabuf, data_chunk, interface_groups); + interface_type = vla_ptr(vlabuf, data_chunk, interface_type); + + parent->default_groups = f_default_groups; + os_desc_type->ct_owner = owner; + config_group_init_type_name(os_desc_group, "os_desc", os_desc_type); + f_default_groups[0] = os_desc_group; + + os_desc_group->default_groups = interface_groups; + interface_type->ct_item_ops = &interf_item_ops; + interface_type->ct_attrs = interf_grp_attrs; + interface_type->ct_owner = owner; + + while (n_interf--) { + struct usb_os_desc *d; + + d = desc[n_interf]; + config_group_init_type_name(&d->group, "", interface_type); + config_item_set_name(&d->group.cg_item, "interface.%d", + n_interf); + interface_groups[n_interf] = &d->group; + } + + return 0; +} +EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir); + static int configfs_do_nothing(struct usb_composite_dev *cdev) { WARN_ON(1); @@ -893,6 +1074,9 @@ static int configfs_do_nothing(struct usb_composite_dev *cdev) int composite_dev_prepare(struct usb_composite_driver *composite, struct usb_composite_dev *dev); +int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, + struct usb_ep *ep0); + static void purge_configs_funcs(struct gadget_info *gi) { struct usb_configuration *c; @@ -1028,6 +1212,12 @@ static int configfs_composite_bind(struct usb_gadget *gadget, } usb_ep_autoconfig_reset(cdev->gadget); } + if (cdev->use_os_string) { + ret = composite_os_desc_req_prepare(cdev, gadget->ep0); + if (ret) + goto err_purge_funcs; + } + usb_ep_autoconfig_reset(cdev->gadget); return 0; diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h index a7b564a913d1..a14ac792c698 100644 --- a/drivers/usb/gadget/configfs.h +++ b/drivers/usb/gadget/configfs.h @@ -1,6 +1,18 @@ #ifndef USB__GADGET__CONFIGFS__H #define USB__GADGET__CONFIGFS__H +#include + void unregister_gadget_item(struct config_item *item); +int usb_os_desc_prepare_interf_dir(struct config_group *parent, + int n_interf, + struct usb_os_desc **desc, + struct module *owner); + +static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item) +{ + return container_of(to_config_group(item), struct usb_os_desc, group); +} + #endif /* USB__GADGET__CONFIGFS__H */ diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 549f5382b01a..9c3903d76781 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -80,12 +80,16 @@ struct usb_os_desc_ext_prop { * @ext_prop: Extended Properties list * @ext_prop_len: Total length of Extended Properties blobs * @ext_prop_count: Number of Extended Properties + * @opts_mutex: Optional mutex protecting config data of a usb_function_instance + * @group: Represents OS descriptors associated with an interface in configfs */ struct usb_os_desc { char *ext_compat_id; struct list_head ext_prop; int ext_prop_len; int ext_prop_count; + struct mutex *opts_mutex; + struct config_group group; }; /** @@ -381,6 +385,8 @@ extern void usb_composite_unregister(struct usb_composite_driver *driver); extern void usb_composite_setup_continue(struct usb_composite_dev *cdev); extern int composite_dev_prepare(struct usb_composite_driver *composite, struct usb_composite_dev *cdev); +extern int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, + struct usb_ep *ep0); void composite_dev_cleanup(struct usb_composite_dev *cdev); static inline struct usb_composite_driver *to_cdriver( -- cgit v1.2.3 From 7419485f197c436d41535df78ddea1085042d271 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 8 May 2014 14:06:28 +0200 Subject: usb: gadget: configfs: OS Extended Properties descriptors support Add handling of OS Extended Properties descriptors from configfs interface. One kind of "OS Descriptors" are "Extended Properties" descriptors, which need to be specified per interface or per group of interfaces described by an IAD. This patch adds support for creating subdirectories in interface. directory located in the function's directory. Names of subdirectories created become names of properties. Each property contains two attributes: "type" and "data". The type can be a numeric value 1..7 while data is a blob interpreted depending on the type specified. The types are: 1 - unicode string 2 - unicode string with environment variables 3 - binary 4 - little-endian 32-bit 5 - big-endian 32-bit 6 - unicode string with a symbolic link 7 - multiple unicode strings Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/ABI/testing/configfs-usb-gadget | 21 +++ drivers/usb/gadget/configfs.c | 201 ++++++++++++++++++++++++++ include/linux/usb/composite.h | 4 + 3 files changed, 226 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/configfs-usb-gadget b/Documentation/ABI/testing/configfs-usb-gadget index 5c0b3e6eb981..95a36589a66b 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget +++ b/Documentation/ABI/testing/configfs-usb-gadget @@ -75,6 +75,27 @@ Description: compatible_id - 8-byte string for "Compatible ID" sub_compatible_id - 8-byte string for "Sub Compatible ID" +What: /config/usb-gadget/gadget/functions/./interface./ +Date: May 2014 +KernelVersion: 3.16 +Description: + This group contains "Extended Property Descriptors" specific for one + gadget's USB interface or one interface group described + by an IAD. + + The attributes: + + type - value 1..7 for interpreting the data + 1: unicode string + 2: unicode string with environment variable + 3: binary + 4: little-endian 32-bit + 5: big-endian 32-bit + 6: unicode string with a symbolic link + 7: multiple unicode strings + data - blob of data to be interpreted depending on + type + What: /config/usb-gadget/gadget/strings Date: Jun 2013 KernelVersion: 3.11 diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index fa6cb06cca09..2ddcd635ca2a 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -7,6 +7,7 @@ #include #include "configfs.h" #include "u_f.h" +#include "u_os_desc.h" int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) @@ -941,6 +942,204 @@ static struct config_item_type os_desc_type = { CONFIGFS_ATTR_STRUCT(usb_os_desc); CONFIGFS_ATTR_OPS(usb_os_desc); + +static inline struct usb_os_desc_ext_prop +*to_usb_os_desc_ext_prop(struct config_item *item) +{ + return container_of(item, struct usb_os_desc_ext_prop, item); +} + +CONFIGFS_ATTR_STRUCT(usb_os_desc_ext_prop); +CONFIGFS_ATTR_OPS(usb_os_desc_ext_prop); + +static ssize_t ext_prop_type_show(struct usb_os_desc_ext_prop *ext_prop, + char *page) +{ + return sprintf(page, "%d", ext_prop->type); +} + +static ssize_t ext_prop_type_store(struct usb_os_desc_ext_prop *ext_prop, + const char *page, size_t len) +{ + struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); + u8 type; + int ret; + + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + ret = kstrtou8(page, 0, &type); + if (ret) + goto end; + if (type < USB_EXT_PROP_UNICODE || type > USB_EXT_PROP_UNICODE_MULTI) { + ret = -EINVAL; + goto end; + } + + if ((ext_prop->type == USB_EXT_PROP_BINARY || + ext_prop->type == USB_EXT_PROP_LE32 || + ext_prop->type == USB_EXT_PROP_BE32) && + (type == USB_EXT_PROP_UNICODE || + type == USB_EXT_PROP_UNICODE_ENV || + type == USB_EXT_PROP_UNICODE_LINK)) + ext_prop->data_len <<= 1; + else if ((ext_prop->type == USB_EXT_PROP_UNICODE || + ext_prop->type == USB_EXT_PROP_UNICODE_ENV || + ext_prop->type == USB_EXT_PROP_UNICODE_LINK) && + (type == USB_EXT_PROP_BINARY || + type == USB_EXT_PROP_LE32 || + type == USB_EXT_PROP_BE32)) + ext_prop->data_len >>= 1; + ext_prop->type = type; + ret = len; + +end: + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + return ret; +} + +static ssize_t ext_prop_data_show(struct usb_os_desc_ext_prop *ext_prop, + char *page) +{ + int len = ext_prop->data_len; + + if (ext_prop->type == USB_EXT_PROP_UNICODE || + ext_prop->type == USB_EXT_PROP_UNICODE_ENV || + ext_prop->type == USB_EXT_PROP_UNICODE_LINK) + len >>= 1; + memcpy(page, ext_prop->data, len); + + return len; +} + +static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop, + const char *page, size_t len) +{ + struct usb_os_desc *desc = to_usb_os_desc(ext_prop->item.ci_parent); + char *new_data; + size_t ret_len = len; + + if (page[len - 1] == '\n' || page[len - 1] == '\0') + --len; + new_data = kzalloc(len, GFP_KERNEL); + if (!new_data) + return -ENOMEM; + + memcpy(new_data, page, len); + + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + kfree(ext_prop->data); + ext_prop->data = new_data; + desc->ext_prop_len -= ext_prop->data_len; + ext_prop->data_len = len; + desc->ext_prop_len += ext_prop->data_len; + if (ext_prop->type == USB_EXT_PROP_UNICODE || + ext_prop->type == USB_EXT_PROP_UNICODE_ENV || + ext_prop->type == USB_EXT_PROP_UNICODE_LINK) { + desc->ext_prop_len -= ext_prop->data_len; + ext_prop->data_len <<= 1; + ext_prop->data_len += 2; + desc->ext_prop_len += ext_prop->data_len; + } + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + return ret_len; +} + +static struct usb_os_desc_ext_prop_attribute ext_prop_type = + __CONFIGFS_ATTR(type, S_IRUGO | S_IWUSR, + ext_prop_type_show, ext_prop_type_store); + +static struct usb_os_desc_ext_prop_attribute ext_prop_data = + __CONFIGFS_ATTR(data, S_IRUGO | S_IWUSR, + ext_prop_data_show, ext_prop_data_store); + +static struct configfs_attribute *ext_prop_attrs[] = { + &ext_prop_type.attr, + &ext_prop_data.attr, + NULL, +}; + +static void usb_os_desc_ext_prop_release(struct config_item *item) +{ + struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); + + kfree(ext_prop); /* frees a whole chunk */ +} + +static struct configfs_item_operations ext_prop_ops = { + .release = usb_os_desc_ext_prop_release, + .show_attribute = usb_os_desc_ext_prop_attr_show, + .store_attribute = usb_os_desc_ext_prop_attr_store, +}; + +static struct config_item *ext_prop_make( + struct config_group *group, + const char *name) +{ + struct usb_os_desc_ext_prop *ext_prop; + struct config_item_type *ext_prop_type; + struct usb_os_desc *desc; + char *vlabuf; + + vla_group(data_chunk); + vla_item(data_chunk, struct usb_os_desc_ext_prop, ext_prop, 1); + vla_item(data_chunk, struct config_item_type, ext_prop_type, 1); + + vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); + if (!vlabuf) + return ERR_PTR(-ENOMEM); + + ext_prop = vla_ptr(vlabuf, data_chunk, ext_prop); + ext_prop_type = vla_ptr(vlabuf, data_chunk, ext_prop_type); + + desc = container_of(group, struct usb_os_desc, group); + ext_prop_type->ct_item_ops = &ext_prop_ops; + ext_prop_type->ct_attrs = ext_prop_attrs; + ext_prop_type->ct_owner = desc->owner; + + config_item_init_type_name(&ext_prop->item, name, ext_prop_type); + + ext_prop->name = kstrdup(name, GFP_KERNEL); + if (!ext_prop->name) { + kfree(vlabuf); + return ERR_PTR(-ENOMEM); + } + desc->ext_prop_len += 14; + ext_prop->name_len = 2 * strlen(ext_prop->name) + 2; + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + desc->ext_prop_len += ext_prop->name_len; + list_add_tail(&ext_prop->entry, &desc->ext_prop); + ++desc->ext_prop_count; + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + + return &ext_prop->item; +} + +static void ext_prop_drop(struct config_group *group, struct config_item *item) +{ + struct usb_os_desc_ext_prop *ext_prop = to_usb_os_desc_ext_prop(item); + struct usb_os_desc *desc = to_usb_os_desc(&group->cg_item); + + if (desc->opts_mutex) + mutex_lock(desc->opts_mutex); + list_del(&ext_prop->entry); + --desc->ext_prop_count; + kfree(ext_prop->name); + desc->ext_prop_len -= (ext_prop->name_len + ext_prop->data_len + 14); + if (desc->opts_mutex) + mutex_unlock(desc->opts_mutex); + config_item_put(item); +} + +static struct configfs_group_operations interf_grp_ops = { + .make_item = &ext_prop_make, + .drop_item = &ext_prop_drop, +}; + static struct configfs_item_operations interf_item_ops = { .show_attribute = usb_os_desc_attr_show, .store_attribute = usb_os_desc_attr_store, @@ -1048,6 +1247,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent, os_desc_group->default_groups = interface_groups; interface_type->ct_item_ops = &interf_item_ops; + interface_type->ct_group_ops = &interf_grp_ops; interface_type->ct_attrs = interf_grp_attrs; interface_type->ct_owner = owner; @@ -1055,6 +1255,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent, struct usb_os_desc *d; d = desc[n_interf]; + d->owner = owner; config_group_init_type_name(&d->group, "", interface_type); config_item_set_name(&d->group.cg_item, "interface.%d", n_interf); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 9c3903d76781..7373203140e7 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -64,6 +64,7 @@ struct usb_configuration; * @name: Extended Property name * @data_len: Length of Extended Property blob (for unicode store double len) * @data: Extended Property blob + * @item: Represents this Extended Property in configfs */ struct usb_os_desc_ext_prop { struct list_head entry; @@ -72,6 +73,7 @@ struct usb_os_desc_ext_prop { char *name; int data_len; char *data; + struct config_item item; }; /** @@ -82,6 +84,7 @@ struct usb_os_desc_ext_prop { * @ext_prop_count: Number of Extended Properties * @opts_mutex: Optional mutex protecting config data of a usb_function_instance * @group: Represents OS descriptors associated with an interface in configfs + * @owner: Module associated with this OS descriptor */ struct usb_os_desc { char *ext_compat_id; @@ -90,6 +93,7 @@ struct usb_os_desc { int ext_prop_count; struct mutex *opts_mutex; struct config_group group; + struct module *owner; }; /** -- cgit v1.2.3 From 7413af1fb70e7efa6dbc7f27663e7a5126b3aa33 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 6 May 2014 21:34:14 -0400 Subject: ftrace: Make get_ftrace_addr() and get_ftrace_addr_old() global Move and rename get_ftrace_addr() and get_ftrace_addr_old() to ftrace_get_addr_new() and ftrace_get_addr_curr() respectively. This moves these two helper functions in the generic code out from the arch specific code, and renames them to have a better generic name. This will allow other archs to use them as well as makes it a bit easier to work on getting separate trampolines for different functions. ftrace_get_addr_new() returns the trampoline address that the mcount call address will be converted to. ftrace_get_addr_curr() returns the trampoline address of what the mcount call address currently jumps to. Signed-off-by: Steven Rostedt --- arch/x86/kernel/ftrace.c | 36 +++++------------------------------- include/linux/ftrace.h | 2 ++ kernel/trace/ftrace.c | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 4b3c195d4133..5ef43ce8492f 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -349,38 +349,12 @@ static int add_brk_on_nop(struct dyn_ftrace *rec) return add_break(rec->ip, old); } -/* - * If the record has the FTRACE_FL_REGS set, that means that it - * wants to convert to a callback that saves all regs. If FTRACE_FL_REGS - * is not not set, then it wants to convert to the normal callback. - */ -static unsigned long get_ftrace_addr(struct dyn_ftrace *rec) -{ - if (rec->flags & FTRACE_FL_REGS) - return (unsigned long)FTRACE_REGS_ADDR; - else - return (unsigned long)FTRACE_ADDR; -} - -/* - * The FTRACE_FL_REGS_EN is set when the record already points to - * a function that saves all the regs. Basically the '_EN' version - * represents the current state of the function. - */ -static unsigned long get_ftrace_old_addr(struct dyn_ftrace *rec) -{ - if (rec->flags & FTRACE_FL_REGS_EN) - return (unsigned long)FTRACE_REGS_ADDR; - else - return (unsigned long)FTRACE_ADDR; -} - static int add_breakpoints(struct dyn_ftrace *rec, int enable) { unsigned long ftrace_addr; int ret; - ftrace_addr = get_ftrace_old_addr(rec); + ftrace_addr = ftrace_get_addr_curr(rec); ret = ftrace_test_record(rec, enable); @@ -438,14 +412,14 @@ static int remove_breakpoint(struct dyn_ftrace *rec) * If not, don't touch the breakpoint, we make just create * a disaster. */ - ftrace_addr = get_ftrace_addr(rec); + ftrace_addr = ftrace_get_addr_new(rec); nop = ftrace_call_replace(ip, ftrace_addr); if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) == 0) goto update; /* Check both ftrace_addr and ftrace_old_addr */ - ftrace_addr = get_ftrace_old_addr(rec); + ftrace_addr = ftrace_get_addr_curr(rec); nop = ftrace_call_replace(ip, ftrace_addr); if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) @@ -489,7 +463,7 @@ static int add_update(struct dyn_ftrace *rec, int enable) ret = ftrace_test_record(rec, enable); - ftrace_addr = get_ftrace_addr(rec); + ftrace_addr = ftrace_get_addr_new(rec); switch (ret) { case FTRACE_UPDATE_IGNORE: @@ -536,7 +510,7 @@ static int finish_update(struct dyn_ftrace *rec, int enable) ret = ftrace_update_record(rec, enable); - ftrace_addr = get_ftrace_addr(rec); + ftrace_addr = ftrace_get_addr_new(rec); switch (ret) { case FTRACE_UPDATE_IGNORE: diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index f0ff2c2453e7..2f8cbffecd3d 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -400,6 +400,8 @@ int ftrace_update_record(struct dyn_ftrace *rec, int enable); int ftrace_test_record(struct dyn_ftrace *rec, int enable); void ftrace_run_stop_machine(int command); unsigned long ftrace_location(unsigned long ip); +unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec); +unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec); extern ftrace_func_t ftrace_trace_function; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 98fa931b6864..e825fded435d 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1755,6 +1755,42 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable) return ftrace_check_record(rec, enable, 0); } +/** + * ftrace_get_addr_new - Get the call address to set to + * @rec: The ftrace record descriptor + * + * If the record has the FTRACE_FL_REGS set, that means that it + * wants to convert to a callback that saves all regs. If FTRACE_FL_REGS + * is not not set, then it wants to convert to the normal callback. + * + * Returns the address of the trampoline to set to + */ +unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec) +{ + if (rec->flags & FTRACE_FL_REGS) + return (unsigned long)FTRACE_REGS_ADDR; + else + return (unsigned long)FTRACE_ADDR; +} + +/** + * ftrace_get_addr_curr - Get the call address that is already there + * @rec: The ftrace record descriptor + * + * The FTRACE_FL_REGS_EN is set when the record already points to + * a function that saves all the regs. Basically the '_EN' version + * represents the current state of the function. + * + * Returns the address of the trampoline that is currently being called + */ +unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec) +{ + if (rec->flags & FTRACE_FL_REGS_EN) + return (unsigned long)FTRACE_REGS_ADDR; + else + return (unsigned long)FTRACE_ADDR; +} + static int __ftrace_replace_code(struct dyn_ftrace *rec, int enable) { -- cgit v1.2.3 From f1b2f2bd5821c6ab7feed2e133343dd54b212ed9 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 7 May 2014 16:09:49 -0400 Subject: ftrace: Remove FTRACE_UPDATE_MODIFY_CALL_REGS flag As the decision to what needs to be done (converting a call to the ftrace_caller to ftrace_caller_regs or to convert from ftrace_caller_regs to ftrace_caller) can easily be determined from the rec->flags of FTRACE_FL_REGS and FTRACE_FL_REGS_EN, there's no need to have the ftrace_check_record() return either a UPDATE_MODIFY_CALL_REGS or a UPDATE_MODIFY_CALL. Just he latter is enough. This added flag causes more complexity than is required. Remove it. Signed-off-by: Steven Rostedt --- arch/x86/kernel/ftrace.c | 3 --- include/linux/ftrace.h | 2 -- kernel/trace/ftrace.c | 13 ++++--------- 3 files changed, 4 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 5ef43ce8492f..89de3eaf8772 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -366,7 +366,6 @@ static int add_breakpoints(struct dyn_ftrace *rec, int enable) /* converting nop to call */ return add_brk_on_nop(rec); - case FTRACE_UPDATE_MODIFY_CALL_REGS: case FTRACE_UPDATE_MODIFY_CALL: case FTRACE_UPDATE_MAKE_NOP: /* converting a call to a nop */ @@ -469,7 +468,6 @@ static int add_update(struct dyn_ftrace *rec, int enable) case FTRACE_UPDATE_IGNORE: return 0; - case FTRACE_UPDATE_MODIFY_CALL_REGS: case FTRACE_UPDATE_MODIFY_CALL: case FTRACE_UPDATE_MAKE_CALL: /* converting nop to call */ @@ -516,7 +514,6 @@ static int finish_update(struct dyn_ftrace *rec, int enable) case FTRACE_UPDATE_IGNORE: return 0; - case FTRACE_UPDATE_MODIFY_CALL_REGS: case FTRACE_UPDATE_MODIFY_CALL: case FTRACE_UPDATE_MAKE_CALL: /* converting nop to call */ diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 2f8cbffecd3d..3e6dfb31f8e6 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -362,14 +362,12 @@ enum { * IGNORE - The function is already what we want it to be * MAKE_CALL - Start tracing the function * MODIFY_CALL - Stop saving regs for the function - * MODIFY_CALL_REGS - Start saving regs for the function * MAKE_NOP - Stop tracing the function */ enum { FTRACE_UPDATE_IGNORE, FTRACE_UPDATE_MAKE_CALL, FTRACE_UPDATE_MODIFY_CALL, - FTRACE_UPDATE_MODIFY_CALL_REGS, FTRACE_UPDATE_MAKE_NOP, }; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 52c2b53b7953..cc07b7fc4372 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1701,19 +1701,15 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) /* * If this record is being updated from a nop, then * return UPDATE_MAKE_CALL. - * Otherwise, if the EN flag is set, then return - * UPDATE_MODIFY_CALL_REGS to tell the caller to convert - * from the non-save regs, to a save regs function. * Otherwise, * return UPDATE_MODIFY_CALL to tell the caller to convert - * from the save regs, to a non-save regs function. + * from the save regs, to a non-save regs function or + * vice versa. */ if (flag & FTRACE_FL_ENABLED) return FTRACE_UPDATE_MAKE_CALL; - else if (rec->flags & FTRACE_FL_REGS_EN) - return FTRACE_UPDATE_MODIFY_CALL_REGS; - else - return FTRACE_UPDATE_MODIFY_CALL; + + return FTRACE_UPDATE_MODIFY_CALL; } if (update) { @@ -1815,7 +1811,6 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) case FTRACE_UPDATE_MAKE_NOP: return ftrace_make_nop(NULL, rec, ftrace_addr); - case FTRACE_UPDATE_MODIFY_CALL_REGS: case FTRACE_UPDATE_MODIFY_CALL: return ftrace_modify_call(rec, ftrace_old_addr, ftrace_addr); } -- cgit v1.2.3 From afea227fd4acf4f097a9e77bbc2f07d4856ebd01 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 12 Mar 2014 07:10:41 -0700 Subject: rcutorture: Export RCU grace-period kthread wait state to rcutorture This commit allows rcutorture to print additional state for the RCU grace-period kthreads in cases where RCU seems reluctant to start a new grace period. Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcutiny.h | 4 ++++ include/linux/rcutree.h | 1 + kernel/rcu/rcutorture.c | 1 + kernel/rcu/tree.c | 17 +++++++++++++++++ kernel/rcu/tree.h | 8 +++++++- 5 files changed, 30 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 425c659d54e5..d40a6a451330 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -119,6 +119,10 @@ static inline void rcu_sched_force_quiescent_state(void) { } +static inline void show_rcu_gp_kthreads(void) +{ +} + static inline void rcu_cpu_stall_reset(void) { } diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index a59ca05fd4e3..3e2f5d432743 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -84,6 +84,7 @@ extern unsigned long rcutorture_vernum; long rcu_batches_completed(void); long rcu_batches_completed_bh(void); long rcu_batches_completed_sched(void); +void show_rcu_gp_kthreads(void); void rcu_force_quiescent_state(void); void rcu_bh_force_quiescent_state(void); diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 9decce0f110c..37ae5e1d4a1d 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1034,6 +1034,7 @@ rcu_torture_printk(char *page) "??? Writer stall state %d g%lu c%lu f%#x\n", rcu_torture_writer_state, gpnum, completed, flags); + show_rcu_gp_kthreads(); rcutorture_trace_dump(); } rtcv_snap = rcu_torture_current_version; diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 3d15b5a82ae8..93e64381aa2a 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -279,6 +279,21 @@ void rcu_bh_force_quiescent_state(void) } EXPORT_SYMBOL_GPL(rcu_bh_force_quiescent_state); +/* + * Show the state of the grace-period kthreads. + */ +void show_rcu_gp_kthreads(void) +{ + struct rcu_state *rsp; + + for_each_rcu_flavor(rsp) { + pr_info("%s: wait state: %d ->state: %#lx\n", + rsp->name, rsp->gp_state, rsp->gp_kthread->state); + /* sched_show_task(rsp->gp_kthread); */ + } +} +EXPORT_SYMBOL_GPL(show_rcu_gp_kthreads); + /* * Record the number of times rcutorture tests have been initiated and * terminated. This information allows the debugfs tracing stats to be @@ -1626,6 +1641,7 @@ static int __noreturn rcu_gp_kthread(void *arg) trace_rcu_grace_period(rsp->name, ACCESS_ONCE(rsp->gpnum), TPS("reqwait")); + rsp->gp_state = RCU_GP_WAIT_GPS; wait_event_interruptible(rsp->gp_wq, ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_INIT); @@ -1653,6 +1669,7 @@ static int __noreturn rcu_gp_kthread(void *arg) trace_rcu_grace_period(rsp->name, ACCESS_ONCE(rsp->gpnum), TPS("fqswait")); + rsp->gp_state = RCU_GP_WAIT_FQS; ret = wait_event_interruptible_timeout(rsp->gp_wq, ((gf = ACCESS_ONCE(rsp->gp_flags)) & RCU_GP_FLAG_FQS) || diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 75dc3c39a02a..c2fd1e722879 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -406,7 +406,8 @@ struct rcu_state { unsigned long completed; /* # of last completed gp. */ struct task_struct *gp_kthread; /* Task for grace periods. */ wait_queue_head_t gp_wq; /* Where GP task waits. */ - int gp_flags; /* Commands for GP task. */ + short gp_flags; /* Commands for GP task. */ + short gp_state; /* GP kthread sleep state. */ /* End of fields guarded by root rcu_node's lock. */ @@ -469,6 +470,11 @@ struct rcu_state { #define RCU_GP_FLAG_INIT 0x1 /* Need grace-period initialization. */ #define RCU_GP_FLAG_FQS 0x2 /* Need grace-period quiescent-state forcing. */ +/* Values for rcu_state structure's gp_flags field. */ +#define RCU_GP_WAIT_INIT 0 /* Initial state. */ +#define RCU_GP_WAIT_GPS 1 /* Wait for grace-period start. */ +#define RCU_GP_WAIT_FQS 2 /* Wait for force-quiescent-state time. */ + extern struct list_head rcu_struct_flavors; /* Sequence through rcu_state structures for each RCU flavor. */ -- cgit v1.2.3 From 0e980234c97f98be6619b9281d83777f725b94ff Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 16 Apr 2014 10:07:09 -0700 Subject: percpu: Fix raw_cpu_inc_return() The definition for raw_cpu_add_return() uses the operation prefix "raw_add_return_", but the definitions in the various percpu.h files expect "raw_cpu_add_return_". This commit therefore appropriately adjusts the definition of raw_cpu_add_return(). Signed-off-by: Paul E. McKenney Acked-by: Christoph Lameter Reviewed-by: Josh Triplett --- include/linux/percpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index e7a0b95ed527..495c6543a8f2 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -639,7 +639,7 @@ do { \ # define raw_cpu_add_return_8(pcp, val) raw_cpu_generic_add_return(pcp, val) # endif # define raw_cpu_add_return(pcp, val) \ - __pcpu_size_call_return2(raw_add_return_, pcp, val) + __pcpu_size_call_return2(raw_cpu_add_return_, pcp, val) #endif #define raw_cpu_sub_return(pcp, val) raw_cpu_add_return(pcp, -(typeof(pcp))(val)) -- cgit v1.2.3 From ac1bea85781e9004da9b3e8a4b097c18492d857c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 16 Mar 2014 21:36:25 -0700 Subject: sched,rcu: Make cond_resched() report RCU quiescent states Given a CPU running a loop containing cond_resched(), with no other tasks runnable on that CPU, RCU will eventually report RCU CPU stall warnings due to lack of quiescent states. Fortunately, every call to cond_resched() is a perfectly good quiescent state. Unfortunately, invoking rcu_note_context_switch() is a bit heavyweight for cond_resched(), especially given the need to disable preemption, and, for RCU-preempt, interrupts as well. This commit therefore maintains a per-CPU counter that causes cond_resched(), cond_resched_lock(), and cond_resched_softirq() to call rcu_note_context_switch(), but only about once per 256 invocations. This ratio was chosen in keeping with the relative time constants of RCU grace periods. Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 36 ++++++++++++++++++++++++++++++++++++ kernel/rcu/update.c | 18 ++++++++++++++++++ kernel/sched/core.c | 7 ++++++- 3 files changed, 60 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 82973738125b..97cc8d6679b4 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -44,6 +44,7 @@ #include #include #include +#include #include extern int rcu_expedited; /* for sysctl */ @@ -286,6 +287,41 @@ static inline void rcu_user_hooks_switch(struct task_struct *prev, bool __rcu_is_watching(void); #endif /* #if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) || defined(CONFIG_SMP) */ +/* + * Hooks for cond_resched() and friends to avoid RCU CPU stall warnings. + */ + +#define RCU_COND_RESCHED_LIM 256 /* ms vs. 100s of ms. */ +DECLARE_PER_CPU(int, rcu_cond_resched_count); +void rcu_resched(void); + +/* + * Is it time to report RCU quiescent states? + * + * Note unsynchronized access to rcu_cond_resched_count. Yes, we might + * increment some random CPU's count, and possibly also load the result from + * yet another CPU's count. We might even clobber some other CPU's attempt + * to zero its counter. This is all OK because the goal is not precision, + * but rather reasonable amortization of rcu_note_context_switch() overhead + * and extremely high probability of avoiding RCU CPU stall warnings. + * Note that this function has to be preempted in just the wrong place, + * many thousands of times in a row, for anything bad to happen. + */ +static inline bool rcu_should_resched(void) +{ + return raw_cpu_inc_return(rcu_cond_resched_count) >= + RCU_COND_RESCHED_LIM; +} + +/* + * Report quiscent states to RCU if it is time to do so. + */ +static inline void rcu_cond_resched(void) +{ + if (unlikely(rcu_should_resched())) + rcu_resched(); +} + /* * Infrastructure to implement the synchronize_() primitives in * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index 4c0a9b0af469..ed7a0d72562c 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -338,3 +338,21 @@ static int __init check_cpu_stall_init(void) early_initcall(check_cpu_stall_init); #endif /* #ifdef CONFIG_RCU_STALL_COMMON */ + +/* + * Hooks for cond_resched() and friends to avoid RCU CPU stall warnings. + */ + +DEFINE_PER_CPU(int, rcu_cond_resched_count); + +/* + * Report a set of RCU quiescent states, for use by cond_resched() + * and friends. Out of line due to being called infrequently. + */ +void rcu_resched(void) +{ + preempt_disable(); + __this_cpu_write(rcu_cond_resched_count, 0); + rcu_note_context_switch(smp_processor_id()); + preempt_enable(); +} diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 268a45ea238c..9f530c9ed911 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4051,6 +4051,7 @@ static void __cond_resched(void) int __sched _cond_resched(void) { + rcu_cond_resched(); if (should_resched()) { __cond_resched(); return 1; @@ -4069,15 +4070,18 @@ EXPORT_SYMBOL(_cond_resched); */ int __cond_resched_lock(spinlock_t *lock) { + bool need_rcu_resched = rcu_should_resched(); int resched = should_resched(); int ret = 0; lockdep_assert_held(lock); - if (spin_needbreak(lock) || resched) { + if (spin_needbreak(lock) || resched || need_rcu_resched) { spin_unlock(lock); if (resched) __cond_resched(); + else if (unlikely(need_rcu_resched)) + rcu_resched(); else cpu_relax(); ret = 1; @@ -4091,6 +4095,7 @@ int __sched __cond_resched_softirq(void) { BUG_ON(!in_softirq()); + rcu_cond_resched(); /* BH disabled OK, just recording QSes. */ if (should_resched()) { local_bh_enable(); __cond_resched(); -- cgit v1.2.3 From 5228084eed8d54c426c7abde3be66daf8e1b0e57 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 7 Apr 2014 09:14:11 -0700 Subject: torture: Check for multiple concurrent torture tests The torture tests are designed to run in isolation, but do not enforce this isolation. This commit therefore checks for concurrent torture tests, and refuses to start new tests while old tests are running. Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/torture.h | 2 +- kernel/locking/locktorture.c | 3 ++- kernel/rcu/rcutorture.c | 3 ++- kernel/torture.c | 13 +++++++++++-- 4 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/torture.h b/include/linux/torture.h index b2e2b468e511..f998574247fd 100644 --- a/include/linux/torture.h +++ b/include/linux/torture.h @@ -81,7 +81,7 @@ void stutter_wait(const char *title); int torture_stutter_init(int s); /* Initialization and cleanup. */ -void torture_init_begin(char *ttype, bool v, int *runnable); +bool torture_init_begin(char *ttype, bool v, int *runnable); void torture_init_end(void); bool torture_cleanup(void); bool torture_must_stop(void); diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c index 1952466c7db5..dbafeac18e4d 100644 --- a/kernel/locking/locktorture.c +++ b/kernel/locking/locktorture.c @@ -355,7 +355,8 @@ static int __init lock_torture_init(void) &lock_busted_ops, &spin_lock_ops, &spin_lock_irq_ops, }; - torture_init_begin(torture_type, verbose, &locktorture_runnable); + if (!torture_init_begin(torture_type, verbose, &locktorture_runnable)) + return -EBUSY; /* Process args and tell the world that the torturer is on the job. */ for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 4b7b97ff1195..7fa34f86e5ba 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1536,7 +1536,8 @@ rcu_torture_init(void) &rcu_ops, &rcu_bh_ops, &rcu_busted_ops, &srcu_ops, &sched_ops, }; - torture_init_begin(torture_type, verbose, &rcutorture_runnable); + if (!torture_init_begin(torture_type, verbose, &rcutorture_runnable)) + return -EBUSY; /* Process args and tell the world that the torturer is on the job. */ for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { diff --git a/kernel/torture.c b/kernel/torture.c index ae1723a4c751..0ed0b49d2ce1 100644 --- a/kernel/torture.c +++ b/kernel/torture.c @@ -599,14 +599,20 @@ static void torture_stutter_cleanup(void) * The runnable parameter points to a flag that controls whether or not * the test is currently runnable. If there is no such flag, pass in NULL. */ -void __init torture_init_begin(char *ttype, bool v, int *runnable) +bool __init torture_init_begin(char *ttype, bool v, int *runnable) { mutex_lock(&fullstop_mutex); + if (torture_type != NULL) { + pr_alert("torture_init_begin: refusing %s init: %s running", + ttype, torture_type); + mutex_unlock(&fullstop_mutex); + return false; + } torture_type = ttype; verbose = v; torture_runnable = runnable; fullstop = FULLSTOP_DONTSTOP; - + return true; } EXPORT_SYMBOL_GPL(torture_init_begin); @@ -645,6 +651,9 @@ bool torture_cleanup(void) torture_shuffle_cleanup(); torture_stutter_cleanup(); torture_onoff_cleanup(); + mutex_lock(&fullstop_mutex); + torture_type = NULL; + mutex_unlock(&fullstop_mutex); return false; } EXPORT_SYMBOL_GPL(torture_cleanup); -- cgit v1.2.3 From 6348675c4e3612e001860354fea78258e041d9a1 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Wed, 16 Apr 2014 16:46:01 -0400 Subject: torture: Remove unused definition The torture_parm() macro is the same as torture_param(), and torture_parm() is not used. This commit therefore removes torture_parm(). Signed-off-by: Pranith Kumar Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/torture.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/linux/torture.h b/include/linux/torture.h index f998574247fd..5ca58fcbaf1b 100644 --- a/include/linux/torture.h +++ b/include/linux/torture.h @@ -49,12 +49,6 @@ #define VERBOSE_TOROUT_ERRSTRING(s) \ do { if (verbose) pr_alert("%s" TORTURE_FLAG "!!! %s\n", torture_type, s); } while (0) -/* Definitions for a non-string torture-test module parameter. */ -#define torture_parm(type, name, init, msg) \ - static type name = init; \ - module_param(name, type, 0444); \ - MODULE_PARM_DESC(name, msg); - /* Definitions for online/offline exerciser. */ int torture_onoff_init(long ooholdoff, long oointerval); char *torture_onoff_stats(char *page); -- cgit v1.2.3 From f9f36917903b57c571b1ddcfc6bc794ca4dd8232 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Wed, 14 May 2014 14:13:41 +0800 Subject: libahci_platform: add host_flags parameter in ahci_platform_init_host() Add a dynamic host_flags argument to make ahci_platform_init_host more flexible, then remove the AHCI_HFLAGS(...) argument from some driver's ata_port_info, and pass that in as the new argument. Cc: Hans de Geode Reviewed-by: Hans de Goede Signed-off-by: Kefeng Wang Signed-off-by: Tejun Heo --- drivers/ata/ahci_da850.c | 3 ++- drivers/ata/ahci_imx.c | 3 ++- drivers/ata/ahci_mvebu.c | 3 ++- drivers/ata/ahci_platform.c | 2 +- drivers/ata/ahci_st.c | 2 +- drivers/ata/ahci_sunxi.c | 9 ++++++--- drivers/ata/ahci_xgene.c | 7 +++++-- drivers/ata/libahci_platform.c | 5 ++++- include/linux/ahci_platform.h | 1 + 9 files changed, 24 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index 2c83613ce2db..2b77d53bccf8 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -85,7 +85,8 @@ static int ahci_da850_probe(struct platform_device *pdev) da850_sata_init(dev, pwrdn_reg, hpriv->mmio); - rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, 0, 0); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, + 0, 0, 0); if (rc) goto disable_resources; diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 497c7abe1c7d..e7e44a73e4fe 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -267,7 +267,8 @@ static int imx_ahci_probe(struct platform_device *pdev) reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; writel(reg_val, hpriv->mmio + HOST_TIMER1MS); - ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, 0, 0); + ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, + 0, 0, 0); if (ret) imx_sata_disable(hpriv); diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c index 1df8630c6b65..fd3dfd733b84 100644 --- a/drivers/ata/ahci_mvebu.c +++ b/drivers/ata/ahci_mvebu.c @@ -88,7 +88,8 @@ static int ahci_mvebu_probe(struct platform_device *pdev) ahci_mvebu_mbus_config(hpriv, dram); ahci_mvebu_regret_option(hpriv); - rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info, 0, 0); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info, + 0, 0, 0); if (rc) goto disable_resources; diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index ef67e79944f9..a476a1fd3f8f 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -55,7 +55,7 @@ static int ahci_probe(struct platform_device *pdev) goto disable_resources; } - rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, 0, 0); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, 0, 0, 0); if (rc) goto pdata_exit; diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 633222226c19..2595598df9ce 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -166,7 +166,7 @@ static int st_ahci_probe(struct platform_device *pdev) if (err) return err; - err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, 0, 0); + err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, 0, 0, 0); if (err) { ahci_platform_disable_resources(hpriv); return err; diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c index 42d3f64e74b3..02002f125bd4 100644 --- a/drivers/ata/ahci_sunxi.c +++ b/drivers/ata/ahci_sunxi.c @@ -157,8 +157,6 @@ static void ahci_sunxi_start_engine(struct ata_port *ap) } static const struct ata_port_info ahci_sunxi_port_info = { - AHCI_HFLAGS(AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | - AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, @@ -169,6 +167,7 @@ static int ahci_sunxi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; + unsigned long hflags; int rc; hpriv = ahci_platform_get_resources(pdev); @@ -185,7 +184,11 @@ static int ahci_sunxi_probe(struct platform_device *pdev) if (rc) goto disable_resources; - rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, 0, 0); + hflags = AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | + AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, + hflags, 0, 0); if (rc) goto disable_resources; diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index 77c89bf171f1..042a9bb45c86 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -303,7 +303,6 @@ static struct ata_port_operations xgene_ahci_ops = { }; static const struct ata_port_info xgene_ahci_port_info = { - AHCI_HFLAGS(AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, @@ -382,6 +381,7 @@ static int xgene_ahci_probe(struct platform_device *pdev) struct ahci_host_priv *hpriv; struct xgene_ahci_context *ctx; struct resource *res; + unsigned long hflags; int rc; hpriv = ahci_platform_get_resources(pdev); @@ -450,7 +450,10 @@ static int xgene_ahci_probe(struct platform_device *pdev) goto disable_resources; } - rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info, 0, 0); + hflags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; + + rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info, + hflags, 0, 0); if (rc) goto disable_resources; diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 7cb3a85719c0..3a5b4ed25a4f 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -283,6 +283,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources); * @pdev: platform device pointer for the host * @hpriv: ahci-host private data for the host * @pi_template: template for the ata_port_info to use + * @host_flags: ahci host flags used in ahci_host_priv * @force_port_map: param passed to ahci_save_initial_config * @mask_port_map: param passed to ahci_save_initial_config * @@ -296,6 +297,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources); int ahci_platform_init_host(struct platform_device *pdev, struct ahci_host_priv *hpriv, const struct ata_port_info *pi_template, + unsigned long host_flags, unsigned int force_port_map, unsigned int mask_port_map) { @@ -312,7 +314,8 @@ int ahci_platform_init_host(struct platform_device *pdev, } /* prepare host */ - hpriv->flags |= (unsigned long)pi.private_data; + pi.private_data = (void *)host_flags; + hpriv->flags |= host_flags; ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 1f16d502600c..6dfd51a04d77 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -44,6 +44,7 @@ struct ahci_host_priv *ahci_platform_get_resources( int ahci_platform_init_host(struct platform_device *pdev, struct ahci_host_priv *hpriv, const struct ata_port_info *pi_template, + unsigned long host_flags, unsigned int force_port_map, unsigned int mask_port_map); -- cgit v1.2.3 From 1a56f2aa4752293e5a9c0c3a2331620aa1fdb808 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 14 May 2014 13:43:37 +0900 Subject: workqueue: Remove deprecated flush[_delayed]_work_sync() flush[_delayed]_work_sync() were deprecated by 4382973 ("workqueue: deprecate flush[_delayed]_work_sync()") and have been deprecated for a long time. In addition, these are not used anymore. So, let's remove these functions. Signed-off-by: Jingoo Han Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 1b22c42e9c2d..aa92d0295e28 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -587,18 +587,6 @@ static inline bool keventd_up(void) return system_wq != NULL; } -/* used to be different but now identical to flush_work(), deprecated */ -static inline bool __deprecated flush_work_sync(struct work_struct *work) -{ - return flush_work(work); -} - -/* used to be different but now identical to flush_delayed_work(), deprecated */ -static inline bool __deprecated flush_delayed_work_sync(struct delayed_work *dwork) -{ - return flush_delayed_work(dwork); -} - #ifndef CONFIG_SMP static inline long work_on_cpu(int cpu, long (*fn)(void *), void *arg) { -- cgit v1.2.3 From cf416171e7e1d966111f53bdae82f51af05e7bf8 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 14 May 2014 13:58:06 +0900 Subject: workqueue: Remove deprecated system_nrt[_freezable]_wq system_nrt[_freezable]_wq were deprecated by 3b07e9c ("workqueue: deprecate system_nrt[_freezable]_wq") and have been deprecated for a long time. In addition, these are not used anymore. So, let's remove these functions. Signed-off-by: Jingoo Han Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index aa92d0295e28..d93d28b2ec73 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -364,20 +364,6 @@ extern struct workqueue_struct *system_freezable_wq; extern struct workqueue_struct *system_power_efficient_wq; extern struct workqueue_struct *system_freezable_power_efficient_wq; -static inline struct workqueue_struct * __deprecated __system_nrt_wq(void) -{ - return system_wq; -} - -static inline struct workqueue_struct * __deprecated __system_nrt_freezable_wq(void) -{ - return system_freezable_wq; -} - -/* equivlalent to system_wq and system_freezable_wq, deprecated */ -#define system_nrt_wq __system_nrt_wq() -#define system_nrt_freezable_wq __system_nrt_freezable_wq() - extern struct workqueue_struct * __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, struct lock_class_key *key, const char *lock_name, ...) __printf(1, 6); -- cgit v1.2.3 From 5a989cf6a05a93a360f38f8a6900ce4dd1e4b6c4 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 24 Mar 2014 02:15:53 +0530 Subject: clk/exynos5260: add macros and documentation for exynos5260 Add macros which are used as Clock IDs in DT and clock file. It also adds the documentation for the exynos5260 clocks. Signed-off-by: Rahul Sharma Signed-off-by: Tomasz Figa --- .../devicetree/bindings/clock/exynos5260-clock.txt | 190 +++++++++ include/dt-bindings/clock/exynos5260-clk.h | 469 +++++++++++++++++++++ 2 files changed, 659 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/exynos5260-clock.txt create mode 100644 include/dt-bindings/clock/exynos5260-clk.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5260-clock.txt b/Documentation/devicetree/bindings/clock/exynos5260-clock.txt new file mode 100644 index 000000000000..5496b2fac483 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/exynos5260-clock.txt @@ -0,0 +1,190 @@ +* Samsung Exynos5260 Clock Controller + +Exynos5260 has 13 clock controllers which are instantiated +independently from the device-tree. These clock controllers +generate and supply clocks to various hardware blocks within +the SoC. + +Each clock is assigned an identifier and client nodes can use +this identifier to specify the clock which they consume. All +available clocks are defined as preprocessor macros in +dt-bindings/clock/exynos5260-clk.h header and can be used in +device tree sources. + +External clocks: + +There are several clocks that are generated outside the SoC. It +is expected that they are defined using standard clock bindings +with following clock-output-names: + + - "fin_pll" - PLL input clock from XXTI + - "xrtcxti" - input clock from XRTCXTI + - "ioclk_pcm_extclk" - pcm external operation clock + - "ioclk_spdif_extclk" - spdif external operation clock + - "ioclk_i2s_cdclk" - i2s0 codec clock + +Phy clocks: + +There are several clocks which are generated by specific PHYs. +These clocks are fed into the clock controller and then routed to +the hardware blocks. These clocks are defined as fixed clocks in the +driver with following names: + + - "phyclk_dptx_phy_ch3_txd_clk" - dp phy clock for channel 3 + - "phyclk_dptx_phy_ch2_txd_clk" - dp phy clock for channel 2 + - "phyclk_dptx_phy_ch1_txd_clk" - dp phy clock for channel 1 + - "phyclk_dptx_phy_ch0_txd_clk" - dp phy clock for channel 0 + - "phyclk_hdmi_phy_tmds_clko" - hdmi phy tmds clock + - "phyclk_hdmi_phy_pixel_clko" - hdmi phy pixel clock + - "phyclk_hdmi_link_o_tmds_clkhi" - hdmi phy for hdmi link + - "phyclk_dptx_phy_o_ref_clk_24m" - dp phy reference clock + - "phyclk_dptx_phy_clk_div2" + - "phyclk_mipi_dphy_4l_m_rxclkesc0" + - "phyclk_usbhost20_phy_phyclock" - usb 2.0 phy clock + - "phyclk_usbhost20_phy_freeclk" + - "phyclk_usbhost20_phy_clk48mohci" + - "phyclk_usbdrd30_udrd30_pipe_pclk" + - "phyclk_usbdrd30_udrd30_phyclock" - usb 3.0 phy clock + +Required Properties for Clock Controller: + + - compatible: should be one of the following. + 1) "samsung,exynos5260-clock-top" + 2) "samsung,exynos5260-clock-peri" + 3) "samsung,exynos5260-clock-egl" + 4) "samsung,exynos5260-clock-kfc" + 5) "samsung,exynos5260-clock-g2d" + 6) "samsung,exynos5260-clock-mif" + 7) "samsung,exynos5260-clock-mfc" + 8) "samsung,exynos5260-clock-g3d" + 9) "samsung,exynos5260-clock-fsys" + 10) "samsung,exynos5260-clock-aud" + 11) "samsung,exynos5260-clock-isp" + 12) "samsung,exynos5260-clock-gscl" + 13) "samsung,exynos5260-clock-disp" + + - reg: physical base address of the controller and the length of + memory mapped region. + + - #clock-cells: should be 1. + + - clocks: list of clock identifiers which are fed as the input to + the given clock controller. Please refer the next section to find + the input clocks for a given controller. + + - clock-names: list of names of clocks which are fed as the input + to the given clock controller. + +Input clocks for top clock controller: + - fin_pll + - dout_mem_pll + - dout_bus_pll + - dout_media_pll + +Input clocks for peri clock controller: + - fin_pll + - ioclk_pcm_extclk + - ioclk_i2s_cdclk + - ioclk_spdif_extclk + - phyclk_hdmi_phy_ref_cko + - dout_aclk_peri_66 + - dout_sclk_peri_uart0 + - dout_sclk_peri_uart1 + - dout_sclk_peri_uart2 + - dout_sclk_peri_spi0_b + - dout_sclk_peri_spi1_b + - dout_sclk_peri_spi2_b + - dout_aclk_peri_aud + - dout_sclk_peri_spi0_b + +Input clocks for egl clock controller: + - fin_pll + - dout_bus_pll + +Input clocks for kfc clock controller: + - fin_pll + - dout_media_pll + +Input clocks for g2d clock controller: + - fin_pll + - dout_aclk_g2d_333 + +Input clocks for mif clock controller: + - fin_pll + +Input clocks for mfc clock controller: + - fin_pll + - dout_aclk_mfc_333 + +Input clocks for g3d clock controller: + - fin_pll + +Input clocks for fsys clock controller: + - fin_pll + - phyclk_usbhost20_phy_phyclock + - phyclk_usbhost20_phy_freeclk + - phyclk_usbhost20_phy_clk48mohci + - phyclk_usbdrd30_udrd30_pipe_pclk + - phyclk_usbdrd30_udrd30_phyclock + - dout_aclk_fsys_200 + +Input clocks for aud clock controller: + - fin_pll + - fout_aud_pll + - ioclk_i2s_cdclk + - ioclk_pcm_extclk + +Input clocks for isp clock controller: + - fin_pll + - dout_aclk_isp1_266 + - dout_aclk_isp1_400 + - mout_aclk_isp1_266 + +Input clocks for gscl clock controller: + - fin_pll + - dout_aclk_gscl_400 + - dout_aclk_gscl_333 + +Input clocks for disp clock controller: + - fin_pll + - phyclk_dptx_phy_ch3_txd_clk + - phyclk_dptx_phy_ch2_txd_clk + - phyclk_dptx_phy_ch1_txd_clk + - phyclk_dptx_phy_ch0_txd_clk + - phyclk_hdmi_phy_tmds_clko + - phyclk_hdmi_phy_ref_clko + - phyclk_hdmi_phy_pixel_clko + - phyclk_hdmi_link_o_tmds_clkhi + - phyclk_mipi_dphy_4l_m_txbyte_clkhs + - phyclk_dptx_phy_o_ref_clk_24m + - phyclk_dptx_phy_clk_div2 + - phyclk_mipi_dphy_4l_m_rxclkesc0 + - phyclk_hdmi_phy_ref_cko + - ioclk_spdif_extclk + - dout_aclk_peri_aud + - dout_aclk_disp_222 + - dout_sclk_disp_pixel + - dout_aclk_disp_333 + +Example 1: An example of a clock controller node is listed below. + + clock_mfc: clock-controller@11090000 { + compatible = "samsung,exynos5260-clock-mfc"; + clock = <&fin_pll>, <&clock_top TOP_DOUT_ACLK_MFC_333>; + clock-names = "fin_pll", "dout_aclk_mfc_333"; + reg = <0x11090000 0x10000>; + #clock-cells = <1>; + }; + +Example 2: UART controller node that consumes the clock generated by the + peri clock controller. Refer to the standard clock bindings for + information about 'clocks' and 'clock-names' property. + + serial@12C00000 { + compatible = "samsung,exynos4210-uart"; + reg = <0x12C00000 0x100>; + interrupts = <0 146 0>; + clocks = <&clock_peri PERI_PCLK_UART0>, <&clock_peri PERI_SCLK_UART0>; + clock-names = "uart", "clk_uart_baud0"; + }; + diff --git a/include/dt-bindings/clock/exynos5260-clk.h b/include/dt-bindings/clock/exynos5260-clk.h new file mode 100644 index 000000000000..a4bac9a1764f --- /dev/null +++ b/include/dt-bindings/clock/exynos5260-clk.h @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Rahul Sharma + * + * 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. + * + * Provides Constants for Exynos5260 clocks. +*/ + +#ifndef _DT_BINDINGS_CLK_EXYNOS5260_H +#define _DT_BINDINGS_CLK_EXYNOS5260_H + +/* Clock names: */ + +/* List Of Clocks For CMU_TOP */ + +#define TOP_FOUT_DISP_PLL 1 +#define TOP_FOUT_AUD_PLL 2 +#define TOP_MOUT_AUDTOP_PLL_USER 3 +#define TOP_MOUT_AUD_PLL 4 +#define TOP_MOUT_DISP_PLL 5 +#define TOP_MOUT_BUSTOP_PLL_USER 6 +#define TOP_MOUT_MEMTOP_PLL_USER 7 +#define TOP_MOUT_MEDIATOP_PLL_USER 8 +#define TOP_MOUT_DISP_DISP_333 9 +#define TOP_MOUT_ACLK_DISP_333 10 +#define TOP_MOUT_DISP_DISP_222 11 +#define TOP_MOUT_ACLK_DISP_222 12 +#define TOP_MOUT_DISP_MEDIA_PIXEL 13 +#define TOP_MOUT_FIMD1 14 +#define TOP_MOUT_SCLK_PERI_SPI0_CLK 15 +#define TOP_MOUT_SCLK_PERI_SPI1_CLK 16 +#define TOP_MOUT_SCLK_PERI_SPI2_CLK 17 +#define TOP_MOUT_SCLK_PERI_UART0_UCLK 18 +#define TOP_MOUT_SCLK_PERI_UART2_UCLK 19 +#define TOP_MOUT_SCLK_PERI_UART1_UCLK 20 +#define TOP_MOUT_BUS4_BUSTOP_100 21 +#define TOP_MOUT_BUS4_BUSTOP_400 22 +#define TOP_MOUT_BUS3_BUSTOP_100 23 +#define TOP_MOUT_BUS3_BUSTOP_400 24 +#define TOP_MOUT_BUS2_BUSTOP_400 25 +#define TOP_MOUT_BUS2_BUSTOP_100 26 +#define TOP_MOUT_BUS1_BUSTOP_100 27 +#define TOP_MOUT_BUS1_BUSTOP_400 28 +#define TOP_MOUT_SCLK_FSYS_USB 29 +#define TOP_MOUT_SCLK_FSYS_MMC0_SDCLKIN_A 30 +#define TOP_MOUT_SCLK_FSYS_MMC1_SDCLKIN_A 31 +#define TOP_MOUT_SCLK_FSYS_MMC2_SDCLKIN_A 32 +#define TOP_MOUT_SCLK_FSYS_MMC0_SDCLKIN_B 33 +#define TOP_MOUT_SCLK_FSYS_MMC1_SDCLKIN_B 34 +#define TOP_MOUT_SCLK_FSYS_MMC2_SDCLKIN_B 35 +#define TOP_MOUT_ACLK_ISP1_266 36 +#define TOP_MOUT_ISP1_MEDIA_266 37 +#define TOP_MOUT_ACLK_ISP1_400 38 +#define TOP_MOUT_ISP1_MEDIA_400 39 +#define TOP_MOUT_SCLK_ISP1_SPI0 40 +#define TOP_MOUT_SCLK_ISP1_SPI1 41 +#define TOP_MOUT_SCLK_ISP1_UART 42 +#define TOP_MOUT_SCLK_ISP1_SENSOR2 43 +#define TOP_MOUT_SCLK_ISP1_SENSOR1 44 +#define TOP_MOUT_SCLK_ISP1_SENSOR0 45 +#define TOP_MOUT_ACLK_MFC_333 46 +#define TOP_MOUT_MFC_BUSTOP_333 47 +#define TOP_MOUT_ACLK_G2D_333 48 +#define TOP_MOUT_G2D_BUSTOP_333 49 +#define TOP_MOUT_ACLK_GSCL_FIMC 50 +#define TOP_MOUT_GSCL_BUSTOP_FIMC 51 +#define TOP_MOUT_ACLK_GSCL_333 52 +#define TOP_MOUT_GSCL_BUSTOP_333 53 +#define TOP_MOUT_ACLK_GSCL_400 54 +#define TOP_MOUT_M2M_MEDIATOP_400 55 +#define TOP_DOUT_ACLK_MFC_333 56 +#define TOP_DOUT_ACLK_G2D_333 57 +#define TOP_DOUT_SCLK_ISP1_SENSOR2_A 58 +#define TOP_DOUT_SCLK_ISP1_SENSOR1_A 59 +#define TOP_DOUT_SCLK_ISP1_SENSOR0_A 60 +#define TOP_DOUT_ACLK_GSCL_FIMC 61 +#define TOP_DOUT_ACLK_GSCL_400 62 +#define TOP_DOUT_ACLK_GSCL_333 63 +#define TOP_DOUT_SCLK_ISP1_SPI0_B 64 +#define TOP_DOUT_SCLK_ISP1_SPI0_A 65 +#define TOP_DOUT_ACLK_ISP1_400 66 +#define TOP_DOUT_ACLK_ISP1_266 67 +#define TOP_DOUT_SCLK_ISP1_UART 68 +#define TOP_DOUT_SCLK_ISP1_SPI1_B 69 +#define TOP_DOUT_SCLK_ISP1_SPI1_A 70 +#define TOP_DOUT_SCLK_ISP1_SENSOR2_B 71 +#define TOP_DOUT_SCLK_ISP1_SENSOR1_B 72 +#define TOP_DOUT_SCLK_ISP1_SENSOR0_B 73 +#define TOP_DOUTTOP__SCLK_HPM_TARGETCLK 74 +#define TOP_DOUT_SCLK_DISP_PIXEL 75 +#define TOP_DOUT_ACLK_DISP_222 76 +#define TOP_DOUT_ACLK_DISP_333 77 +#define TOP_DOUT_ACLK_BUS4_100 78 +#define TOP_DOUT_ACLK_BUS4_400 79 +#define TOP_DOUT_ACLK_BUS3_100 80 +#define TOP_DOUT_ACLK_BUS3_400 81 +#define TOP_DOUT_ACLK_BUS2_100 82 +#define TOP_DOUT_ACLK_BUS2_400 83 +#define TOP_DOUT_ACLK_BUS1_100 84 +#define TOP_DOUT_ACLK_BUS1_400 85 +#define TOP_DOUT_SCLK_PERI_SPI1_B 86 +#define TOP_DOUT_SCLK_PERI_SPI1_A 87 +#define TOP_DOUT_SCLK_PERI_SPI0_B 88 +#define TOP_DOUT_SCLK_PERI_SPI0_A 89 +#define TOP_DOUT_SCLK_PERI_UART0 90 +#define TOP_DOUT_SCLK_PERI_UART2 91 +#define TOP_DOUT_SCLK_PERI_UART1 92 +#define TOP_DOUT_SCLK_PERI_SPI2_B 93 +#define TOP_DOUT_SCLK_PERI_SPI2_A 94 +#define TOP_DOUT_ACLK_PERI_AUD 95 +#define TOP_DOUT_ACLK_PERI_66 96 +#define TOP_DOUT_SCLK_FSYS_MMC0_SDCLKIN_B 97 +#define TOP_DOUT_SCLK_FSYS_MMC0_SDCLKIN_A 98 +#define TOP_DOUT_SCLK_FSYS_USBDRD30_SUSPEND_CLK 99 +#define TOP_DOUT_ACLK_FSYS_200 100 +#define TOP_DOUT_SCLK_FSYS_MMC2_SDCLKIN_B 101 +#define TOP_DOUT_SCLK_FSYS_MMC2_SDCLKIN_A 102 +#define TOP_DOUT_SCLK_FSYS_MMC1_SDCLKIN_B 103 +#define TOP_DOUT_SCLK_FSYS_MMC1_SDCLKIN_A 104 +#define TOP_SCLK_FIMD1 105 +#define TOP_SCLK_MMC2 106 +#define TOP_SCLK_MMC1 107 +#define TOP_SCLK_MMC0 108 +#define PHYCLK_DPTX_PHY_CH3_TXD_CLK 109 +#define PHYCLK_DPTX_PHY_CH2_TXD_CLK 110 +#define PHYCLK_DPTX_PHY_CH1_TXD_CLK 111 +#define PHYCLK_DPTX_PHY_CH0_TXD_CLK 112 +#define phyclk_hdmi_phy_tmds_clko 113 +#define PHYCLK_HDMI_PHY_PIXEL_CLKO 114 +#define PHYCLK_HDMI_LINK_O_TMDS_CLKHI 115 +#define PHYCLK_MIPI_DPHY_4L_M_TXBYTECLKHS 116 +#define PHYCLK_DPTX_PHY_O_REF_CLK_24M 117 +#define PHYCLK_DPTX_PHY_CLK_DIV2 118 +#define PHYCLK_MIPI_DPHY_4L_M_RXCLKESC0 119 +#define PHYCLK_USBHOST20_PHY_PHYCLOCK 120 +#define PHYCLK_USBHOST20_PHY_FREECLK 121 +#define PHYCLK_USBHOST20_PHY_CLK48MOHCI 122 +#define PHYCLK_USBDRD30_UDRD30_PIPE_PCLK 123 +#define PHYCLK_USBDRD30_UDRD30_PHYCLOCK 124 +#define TOP_NR_CLK 125 + + +/* List Of Clocks For CMU_EGL */ + +#define EGL_FOUT_EGL_PLL 1 +#define EGL_FOUT_EGL_DPLL 2 +#define EGL_MOUT_EGL_B 3 +#define EGL_MOUT_EGL_PLL 4 +#define EGL_DOUT_EGL_PLL 5 +#define EGL_DOUT_EGL_PCLK_DBG 6 +#define EGL_DOUT_EGL_ATCLK 7 +#define EGL_DOUT_PCLK_EGL 8 +#define EGL_DOUT_ACLK_EGL 9 +#define EGL_DOUT_EGL2 10 +#define EGL_DOUT_EGL1 11 +#define EGL_NR_CLK 12 + + +/* List Of Clocks For CMU_KFC */ + +#define KFC_FOUT_KFC_PLL 1 +#define KFC_MOUT_KFC_PLL 2 +#define KFC_MOUT_KFC 3 +#define KFC_DOUT_KFC_PLL 4 +#define KFC_DOUT_PCLK_KFC 5 +#define KFC_DOUT_ACLK_KFC 6 +#define KFC_DOUT_KFC_PCLK_DBG 7 +#define KFC_DOUT_KFC_ATCLK 8 +#define KFC_DOUT_KFC2 9 +#define KFC_DOUT_KFC1 10 +#define KFC_NR_CLK 11 + + +/* List Of Clocks For CMU_MIF */ + +#define MIF_FOUT_MEM_PLL 1 +#define MIF_FOUT_MEDIA_PLL 2 +#define MIF_FOUT_BUS_PLL 3 +#define MIF_MOUT_CLK2X_PHY 4 +#define MIF_MOUT_MIF_DREX2X 5 +#define MIF_MOUT_CLKM_PHY 6 +#define MIF_MOUT_MIF_DREX 7 +#define MIF_MOUT_MEDIA_PLL 8 +#define MIF_MOUT_BUS_PLL 9 +#define MIF_MOUT_MEM_PLL 10 +#define MIF_DOUT_ACLK_BUS_100 11 +#define MIF_DOUT_ACLK_BUS_200 12 +#define MIF_DOUT_ACLK_MIF_466 13 +#define MIF_DOUT_CLK2X_PHY 14 +#define MIF_DOUT_CLKM_PHY 15 +#define MIF_DOUT_BUS_PLL 16 +#define MIF_DOUT_MEM_PLL 17 +#define MIF_DOUT_MEDIA_PLL 18 +#define MIF_CLK_LPDDR3PHY_WRAP1 19 +#define MIF_CLK_LPDDR3PHY_WRAP0 20 +#define MIF_CLK_MONOCNT 21 +#define MIF_CLK_MIF_RTC 22 +#define MIF_CLK_DREX1 23 +#define MIF_CLK_DREX0 24 +#define MIF_CLK_INTMEM 25 +#define MIF_SCLK_LPDDR3PHY_WRAP_U1 26 +#define MIF_SCLK_LPDDR3PHY_WRAP_U0 27 +#define MIF_NR_CLK 28 + + +/* List Of Clocks For CMU_G3D */ + +#define G3D_FOUT_G3D_PLL 1 +#define G3D_MOUT_G3D_PLL 2 +#define G3D_DOUT_PCLK_G3D 3 +#define G3D_DOUT_ACLK_G3D 4 +#define G3D_CLK_G3D_HPM 5 +#define G3D_CLK_G3D 6 +#define G3D_NR_CLK 7 + + +/* List Of Clocks For CMU_AUD */ + +#define AUD_MOUT_SCLK_AUD_PCM 1 +#define AUD_MOUT_SCLK_AUD_I2S 2 +#define AUD_MOUT_AUD_PLL_USER 3 +#define AUD_DOUT_ACLK_AUD_131 4 +#define AUD_DOUT_SCLK_AUD_UART 5 +#define AUD_DOUT_SCLK_AUD_PCM 6 +#define AUD_DOUT_SCLK_AUD_I2S 7 +#define AUD_CLK_AUD_UART 8 +#define AUD_CLK_PCM 9 +#define AUD_CLK_I2S 10 +#define AUD_CLK_DMAC 11 +#define AUD_CLK_SRAMC 12 +#define AUD_SCLK_AUD_UART 13 +#define AUD_SCLK_PCM 14 +#define AUD_SCLK_I2S 15 +#define AUD_NR_CLK 16 + + +/* List Of Clocks For CMU_MFC */ + +#define MFC_MOUT_ACLK_MFC_333_USER 1 +#define MFC_DOUT_PCLK_MFC_83 2 +#define MFC_CLK_MFC 3 +#define MFC_CLK_SMMU2_MFCM1 4 +#define MFC_CLK_SMMU2_MFCM0 5 +#define MFC_NR_CLK 6 + + +/* List Of Clocks For CMU_GSCL */ + +#define GSCL_MOUT_ACLK_CSIS 1 +#define GSCL_MOUT_ACLK_GSCL_FIMC_USER 2 +#define GSCL_MOUT_ACLK_M2M_400_USER 3 +#define GSCL_MOUT_ACLK_GSCL_333_USER 4 +#define GSCL_DOUT_ACLK_CSIS_200 5 +#define GSCL_DOUT_PCLK_M2M_100 6 +#define GSCL_CLK_PIXEL_GSCL1 7 +#define GSCL_CLK_PIXEL_GSCL0 8 +#define GSCL_CLK_MSCL1 9 +#define GSCL_CLK_MSCL0 10 +#define GSCL_CLK_GSCL1 11 +#define GSCL_CLK_GSCL0 12 +#define GSCL_CLK_FIMC_LITE_D 13 +#define GSCL_CLK_FIMC_LITE_B 14 +#define GSCL_CLK_FIMC_LITE_A 15 +#define GSCL_CLK_CSIS1 16 +#define GSCL_CLK_CSIS0 17 +#define GSCL_CLK_SMMU3_LITE_D 18 +#define GSCL_CLK_SMMU3_LITE_B 19 +#define GSCL_CLK_SMMU3_LITE_A 20 +#define GSCL_CLK_SMMU3_GSCL0 21 +#define GSCL_CLK_SMMU3_GSCL1 22 +#define GSCL_CLK_SMMU3_MSCL0 23 +#define GSCL_CLK_SMMU3_MSCL1 24 +#define GSCL_SCLK_CSIS1_WRAP 25 +#define GSCL_SCLK_CSIS0_WRAP 26 +#define GSCL_NR_CLK 27 + + +/* List Of Clocks For CMU_FSYS */ + +#define FSYS_MOUT_PHYCLK_USBHOST20_PHYCLK_USER 1 +#define FSYS_MOUT_PHYCLK_USBHOST20_FREECLK_USER 2 +#define FSYS_MOUT_PHYCLK_USBHOST20_CLK48MOHCI_USER 3 +#define FSYS_MOUT_PHYCLK_USBDRD30_PIPE_PCLK_USER 4 +#define FSYS_MOUT_PHYCLK_USBDRD30_PHYCLOCK_USER 5 +#define FSYS_CLK_TSI 6 +#define FSYS_CLK_USBLINK 7 +#define FSYS_CLK_USBHOST20 8 +#define FSYS_CLK_USBDRD30 9 +#define FSYS_CLK_SROMC 10 +#define FSYS_CLK_PDMA 11 +#define FSYS_CLK_MMC2 12 +#define FSYS_CLK_MMC1 13 +#define FSYS_CLK_MMC0 14 +#define FSYS_CLK_RTIC 15 +#define FSYS_CLK_SMMU_RTIC 16 +#define FSYS_PHYCLK_USBDRD30 17 +#define FSYS_PHYCLK_USBHOST20 18 +#define FSYS_NR_CLK 19 + + +/* List Of Clocks For CMU_PERI */ + +#define PERI_MOUT_SCLK_SPDIF 1 +#define PERI_MOUT_SCLK_I2SCOD 2 +#define PERI_MOUT_SCLK_PCM 3 +#define PERI_DOUT_I2S 4 +#define PERI_DOUT_PCM 5 +#define PERI_CLK_WDT_KFC 6 +#define PERI_CLK_WDT_EGL 7 +#define PERI_CLK_HSIC3 8 +#define PERI_CLK_HSIC2 9 +#define PERI_CLK_HSIC1 10 +#define PERI_CLK_HSIC0 11 +#define PERI_CLK_PCM 12 +#define PERI_CLK_MCT 13 +#define PERI_CLK_I2S 14 +#define PERI_CLK_I2CHDMI 15 +#define PERI_CLK_I2C7 16 +#define PERI_CLK_I2C6 17 +#define PERI_CLK_I2C5 18 +#define PERI_CLK_I2C4 19 +#define PERI_CLK_I2C9 20 +#define PERI_CLK_I2C8 21 +#define PERI_CLK_I2C11 22 +#define PERI_CLK_I2C10 23 +#define PERI_CLK_HDMICEC 24 +#define PERI_CLK_EFUSE_WRITER 25 +#define PERI_CLK_ABB 26 +#define PERI_CLK_UART2 27 +#define PERI_CLK_UART1 28 +#define PERI_CLK_UART0 29 +#define PERI_CLK_ADC 30 +#define PERI_CLK_TMU4 31 +#define PERI_CLK_TMU3 32 +#define PERI_CLK_TMU2 33 +#define PERI_CLK_TMU1 34 +#define PERI_CLK_TMU0 35 +#define PERI_CLK_SPI2 36 +#define PERI_CLK_SPI1 37 +#define PERI_CLK_SPI0 38 +#define PERI_CLK_SPDIF 39 +#define PERI_CLK_PWM 40 +#define PERI_CLK_UART4 41 +#define PERI_CLK_CHIPID 42 +#define PERI_CLK_PROVKEY0 43 +#define PERI_CLK_PROVKEY1 44 +#define PERI_CLK_SECKEY 45 +#define PERI_CLK_TOP_RTC 46 +#define PERI_CLK_TZPC10 47 +#define PERI_CLK_TZPC9 48 +#define PERI_CLK_TZPC8 49 +#define PERI_CLK_TZPC7 50 +#define PERI_CLK_TZPC6 51 +#define PERI_CLK_TZPC5 52 +#define PERI_CLK_TZPC4 53 +#define PERI_CLK_TZPC3 54 +#define PERI_CLK_TZPC2 55 +#define PERI_CLK_TZPC1 56 +#define PERI_CLK_TZPC0 57 +#define PERI_SCLK_UART2 58 +#define PERI_SCLK_UART1 59 +#define PERI_SCLK_UART0 60 +#define PERI_SCLK_SPI2 61 +#define PERI_SCLK_SPI1 62 +#define PERI_SCLK_SPI0 63 +#define PERI_SCLK_SPDIF 64 +#define PERI_SCLK_I2S 65 +#define PERI_SCLK_PCM1 66 +#define PERI_NR_CLK 67 + + +/* List Of Clocks For CMU_DISP */ + +#define DISP_MOUT_SCLK_HDMI_SPDIF 1 +#define DISP_MOUT_SCLK_HDMI_PIXEL 2 +#define DISP_MOUT_PHYCLK_MIPI_DPHY_4LMRXCLK_ESC0_USER 3 +#define DISP_MOUT_PHYCLK_HDMI_PHY_TMDS_CLKO_USER 4 +#define DISP_MOUT_PHYCLK_HDMI_PHY_REF_CLKO_USER 5 +#define DISP_MOUT_HDMI_PHY_PIXEL 6 +#define DISP_MOUT_PHYCLK_HDMI_LINK_O_TMDS_CLKHI_USER 7 +#define DISP_MOUT_PHYCLK_MIPI_DPHY_4L_M_TXBYTE_CLKHS 8 +#define DISP_MOUT_PHYCLK_DPTX_PHY_O_REF_CLK_24M_USER 9 +#define DISP_MOUT_PHYCLK_DPTX_PHY_CLK_DIV2_USER 10 +#define DISP_MOUT_PHYCLK_DPTX_PHY_CH3_TXD_CLK_USER 11 +#define DISP_MOUT_PHYCLK_DPTX_PHY_CH2_TXD_CLK_USER 12 +#define DISP_MOUT_PHYCLK_DPTX_PHY_CH1_TXD_CLK_USER 13 +#define DISP_MOUT_PHYCLK_DPTX_PHY_CH0_TXD_CLK_USER 14 +#define DISP_MOUT_ACLK_DISP_222_USER 15 +#define DISP_MOUT_SCLK_DISP_PIXEL_USER 16 +#define DISP_MOUT_ACLK_DISP_333_USER 17 +#define DISP_DOUT_SCLK_HDMI_PHY_PIXEL_CLKI 18 +#define DISP_DOUT_SCLK_FIMD1_EXTCLKPLL 19 +#define DISP_DOUT_PCLK_DISP_111 20 +#define DISP_CLK_SMMU_TV 21 +#define DISP_CLK_SMMU_FIMD1M1 22 +#define DISP_CLK_SMMU_FIMD1M0 23 +#define DISP_CLK_PIXEL_MIXER 24 +#define DISP_CLK_PIXEL_DISP 25 +#define DISP_CLK_MIXER 26 +#define DISP_CLK_MIPIPHY 27 +#define DISP_CLK_HDMIPHY 28 +#define DISP_CLK_HDMI 29 +#define DISP_CLK_FIMD1 30 +#define DISP_CLK_DSIM1 31 +#define DISP_CLK_DPPHY 32 +#define DISP_CLK_DP 33 +#define DISP_SCLK_PIXEL 34 +#define DISP_MOUT_HDMI_PHY_PIXEL_USER 35 +#define DISP_NR_CLK 36 + + +/* List Of Clocks For CMU_G2D */ + +#define G2D_MOUT_ACLK_G2D_333_USER 1 +#define G2D_DOUT_PCLK_G2D_83 2 +#define G2D_CLK_SMMU3_JPEG 3 +#define G2D_CLK_MDMA 4 +#define G2D_CLK_JPEG 5 +#define G2D_CLK_G2D 6 +#define G2D_CLK_SSS 7 +#define G2D_CLK_SLIM_SSS 8 +#define G2D_CLK_SMMU_SLIM_SSS 9 +#define G2D_CLK_SMMU_SSS 10 +#define G2D_CLK_SMMU_MDMA 11 +#define G2D_CLK_SMMU3_G2D 12 +#define G2D_NR_CLK 13 + + +/* List Of Clocks For CMU_ISP */ + +#define ISP_MOUT_ISP_400_USER 1 +#define ISP_MOUT_ISP_266_USER 2 +#define ISP_DOUT_SCLK_MPWM 3 +#define ISP_DOUT_CA5_PCLKDBG 4 +#define ISP_DOUT_CA5_ATCLKIN 5 +#define ISP_DOUT_PCLK_ISP_133 6 +#define ISP_DOUT_PCLK_ISP_66 7 +#define ISP_CLK_GIC 8 +#define ISP_CLK_WDT 9 +#define ISP_CLK_UART 10 +#define ISP_CLK_SPI1 11 +#define ISP_CLK_SPI0 12 +#define ISP_CLK_SMMU_SCALERP 13 +#define ISP_CLK_SMMU_SCALERC 14 +#define ISP_CLK_SMMU_ISPCX 15 +#define ISP_CLK_SMMU_ISP 16 +#define ISP_CLK_SMMU_FD 17 +#define ISP_CLK_SMMU_DRC 18 +#define ISP_CLK_PWM 19 +#define ISP_CLK_MTCADC 20 +#define ISP_CLK_MPWM 21 +#define ISP_CLK_MCUCTL 22 +#define ISP_CLK_I2C1 23 +#define ISP_CLK_I2C0 24 +#define ISP_CLK_FIMC_SCALERP 25 +#define ISP_CLK_FIMC_SCALERC 26 +#define ISP_CLK_FIMC 27 +#define ISP_CLK_FIMC_FD 28 +#define ISP_CLK_FIMC_DRC 29 +#define ISP_CLK_CA5 30 +#define ISP_SCLK_SPI0_EXT 31 +#define ISP_SCLK_SPI1_EXT 32 +#define ISP_SCLK_UART_EXT 33 +#define ISP_NR_CLK 34 + +#endif -- cgit v1.2.3 From 5b73721b60360163169a8eccd3c4285f4a605d07 Mon Sep 17 00:00:00 2001 From: Naveen Krishna Chatradhi Date: Mon, 17 Feb 2014 15:14:31 +0530 Subject: clk: samsung: exynos5250/5420: Add gate clock for SSS module This patch adds gating clock for SSS(Security SubSystem) module on Exynos5250/5420. Signed-off-by: Naveen Krishna Chatradhi [t.figa: Fixed sort order and group name.] Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5250.c | 1 + drivers/clk/samsung/clk-exynos5420.c | 4 ++++ include/dt-bindings/clock/exynos5250.h | 1 + 3 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index e549e862524a..d1d53ca45e20 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -428,6 +428,7 @@ static struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { * CMU_ACP */ GATE(CLK_MDMA0, "mdma0", "div_aclk266", GATE_IP_ACP, 1, 0, 0), + GATE(CLK_SSS, "sss", "div_aclk266", GATE_IP_ACP, 2, 0, 0), GATE(CLK_G2D, "g2d", "div_aclk200", GATE_IP_ACP, 3, 0, 0), GATE(CLK_SMMU_MDMA0, "smmu_mdma0", "div_aclk266", GATE_IP_ACP, 5, 0, 0), diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index c3e0894d0279..a8704540a214 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -27,6 +27,7 @@ #define DIV_CPU1 0x504 #define GATE_BUS_CPU 0x700 #define GATE_SCLK_CPU 0x800 +#define GATE_IP_G2D 0x8800 #define CPLL_LOCK 0x10020 #define DPLL_LOCK 0x10030 #define EPLL_LOCK 0x10040 @@ -515,6 +516,9 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { }; static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { + /* G2D */ + GATE(CLK_SSS, "sss", "aclk266_g2d", GATE_IP_G2D, 2, 0, 0), + /* TODO: Re-verify the CG bits for all the gate clocks */ GATE_A(CLK_MCT, "pclk_st", "aclk66_psgen", GATE_BUS_PERIS1, 2, 0, 0, "mct"), diff --git a/include/dt-bindings/clock/exynos5250.h b/include/dt-bindings/clock/exynos5250.h index 922f2dca9bf0..f9b452bb3019 100644 --- a/include/dt-bindings/clock/exynos5250.h +++ b/include/dt-bindings/clock/exynos5250.h @@ -150,6 +150,7 @@ #define CLK_G2D 345 #define CLK_MDMA0 346 #define CLK_SMMU_MDMA0 347 +#define CLK_SSS 348 /* mux clocks */ #define CLK_MOUT_HDMI 1024 -- cgit v1.2.3 From 04bc7d96fbbbd7c379a8351db9f2466b47c74ec2 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 15 Apr 2014 18:30:20 +0200 Subject: clk: samsung: exynos4: Use single clock ID for CLK_MDMA gate clocks Exynos4210 and Exynos4x12 SoCs have the PL330 MDMA IP block clock defined exactly in same way in documentation. Using different names for these clocks is a bit misleading. Since there is no users of CLK_MDMA2 in existing dts files this patch drops CLK_MDMA2 and replaces it with CLK_MDMA in the driver. This ensures PL330 MDMA has correct clock assigned on Exynos4x12 SoCs. Suggested-by: Tomasz Figa Signed-off-by: Sylwester Nawrocki Acked-by: Kyungmin Park Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos4.c | 2 +- include/dt-bindings/clock/exynos4.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 57ed5a8fb052..7e2adcbee4cd 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -903,7 +903,7 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE(CLK_AUDSS, "audss", "sclk_epll", E4X12_GATE_IP_MAUDIO, 0, 0, 0), GATE(CLK_MDNIE0, "mdnie0", "aclk160", GATE_IP_LCD0, 2, 0, 0), GATE(CLK_ROTATOR, "rotator", "aclk200", E4X12_GATE_IP_IMAGE, 1, 0, 0), - GATE(CLK_MDMA2, "mdma2", "aclk200", E4X12_GATE_IP_IMAGE, 2, 0, 0), + GATE(CLK_MDMA, "mdma", "aclk200", E4X12_GATE_IP_IMAGE, 2, 0, 0), GATE(CLK_SMMU_MDMA, "smmu_mdma", "aclk200", E4X12_GATE_IP_IMAGE, 5, 0, 0), GATE(CLK_MIPI_HSI, "mipi_hsi", "aclk133", GATE_IP_FSYS, 10, 0, 0), diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 75aff336dfb0..3ff13bcbe56c 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -181,7 +181,6 @@ #define CLK_KEYIF 347 #define CLK_AUDSS 348 #define CLK_MIPI_HSI 349 /* Exynos4210 only */ -#define CLK_MDMA2 350 /* Exynos4210 only */ #define CLK_PIXELASYNCM0 351 #define CLK_PIXELASYNCM1 352 #define CLK_FIMC_LITE0 353 /* Exynos4x12 only */ -- cgit v1.2.3 From 20b82ae27e89739ed8740323913d58efe593ef91 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Mon, 28 Apr 2014 15:07:01 +0530 Subject: clk: samsung: exynos5250: Add clocks for G3D This patch adds the required clocks for ARM Mali IP in Exynos5250. Signed-off-by: Arun Kumar K [t.figa: Changed clock ID to avoid conflict with CLK_SSS] Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5250.c | 15 ++++++++++++++- include/dt-bindings/clock/exynos5250.h | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index d1d53ca45e20..88488596c00b 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -37,6 +37,7 @@ #define VPLL_CON0 0x10140 #define GPLL_CON0 0x10150 #define SRC_TOP0 0x10210 +#define SRC_TOP1 0x10214 #define SRC_TOP2 0x10218 #define SRC_TOP3 0x1021c #define SRC_GSCL 0x10220 @@ -71,6 +72,7 @@ #define GATE_IP_GSCL 0x10920 #define GATE_IP_DISP1 0x10928 #define GATE_IP_MFC 0x1092c +#define GATE_IP_G3D 0x10930 #define GATE_IP_GEN 0x10934 #define GATE_IP_FSYS 0x10944 #define GATE_IP_PERIC 0x10950 @@ -100,6 +102,7 @@ static unsigned long exynos5250_clk_regs[] __initdata = { DIV_CPU0, SRC_CORE1, SRC_TOP0, + SRC_TOP1, SRC_TOP2, SRC_TOP3, SRC_GSCL, @@ -133,6 +136,7 @@ static unsigned long exynos5250_clk_regs[] __initdata = { DIV_PERIC5, GATE_IP_GSCL, GATE_IP_MFC, + GATE_IP_G3D, GATE_IP_GEN, GATE_IP_FSYS, GATE_IP_PERIC, @@ -189,10 +193,12 @@ PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi27m" }; PNAME(mout_vpll_p) = { "mout_vpllsrc", "fout_vpll" }; PNAME(mout_cpll_p) = { "fin_pll", "fout_cpll" }; PNAME(mout_epll_p) = { "fin_pll", "fout_epll" }; +PNAME(mout_gpll_p) = { "fin_pll", "fout_gpll" }; PNAME(mout_mpll_user_p) = { "fin_pll", "mout_mpll" }; PNAME(mout_bpll_user_p) = { "fin_pll", "mout_bpll" }; PNAME(mout_aclk166_p) = { "mout_cpll", "mout_mpll_user" }; PNAME(mout_aclk200_p) = { "mout_mpll_user", "mout_bpll_user" }; +PNAME(mout_aclk400_p) = { "mout_aclk400_g3d_mid", "mout_gpll" }; PNAME(mout_aclk200_sub_p) = { "fin_pll", "div_aclk200" }; PNAME(mout_aclk266_sub_p) = { "fin_pll", "div_aclk266" }; PNAME(mout_aclk333_sub_p) = { "fin_pll", "div_aclk333" }; @@ -273,12 +279,16 @@ static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { MUX(0, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1), MUX(0, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1), MUX(0, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1), + MUX(0, "mout_aclk400_g3d_mid", mout_aclk200_p, SRC_TOP0, 20, 1), + + MUX(0, "mout_aclk400_g3d", mout_aclk400_p, SRC_TOP1, 28, 1), MUX(0, "mout_cpll", mout_cpll_p, SRC_TOP2, 8, 1), MUX(0, "mout_epll", mout_epll_p, SRC_TOP2, 12, 1), MUX(0, "mout_vpll", mout_vpll_p, SRC_TOP2, 16, 1), MUX(0, "mout_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1), MUX(0, "mout_bpll_user", mout_bpll_user_p, SRC_TOP2, 24, 1), + MUX(CLK_MOUT_GPLL, "mout_gpll", mout_gpll_p, SRC_TOP2, 28, 1), MUX(0, "mout_aclk200_disp1_sub", mout_aclk200_sub_p, SRC_TOP3, 4, 1), MUX(0, "mout_aclk266_gscl_sub", mout_aclk266_sub_p, SRC_TOP3, 8, 1), @@ -351,6 +361,8 @@ static struct samsung_div_clock exynos5250_div_clks[] __initdata = { DIV(0, "div_aclk200", "mout_aclk200", DIV_TOP0, 12, 3), DIV(0, "div_aclk266", "mout_mpll_user", DIV_TOP0, 16, 3), DIV(0, "div_aclk333", "mout_aclk333", DIV_TOP0, 20, 3), + DIV(0, "div_aclk400_g3d", "mout_aclk400_g3d", DIV_TOP0, + 24, 3), DIV(0, "div_aclk66_pre", "mout_mpll_user", DIV_TOP1, 24, 3), @@ -534,7 +546,8 @@ static struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { 0), GATE(CLK_SMMU_MFCL, "smmu_mfcl", "mout_aclk333_sub", GATE_IP_MFC, 2, 0, 0), - + GATE(CLK_G3D, "g3d", "div_aclk400_g3d", GATE_IP_G3D, 0, + CLK_SET_RATE_PARENT, 0), GATE(CLK_ROTATOR, "rotator", "div_aclk266", GATE_IP_GEN, 1, 0, 0), GATE(CLK_JPEG, "jpeg", "div_aclk166", GATE_IP_GEN, 2, 0, 0), GATE(CLK_MDMA1, "mdma1", "div_aclk266", GATE_IP_GEN, 4, 0, 0), diff --git a/include/dt-bindings/clock/exynos5250.h b/include/dt-bindings/clock/exynos5250.h index f9b452bb3019..415d447e6e29 100644 --- a/include/dt-bindings/clock/exynos5250.h +++ b/include/dt-bindings/clock/exynos5250.h @@ -151,11 +151,13 @@ #define CLK_MDMA0 346 #define CLK_SMMU_MDMA0 347 #define CLK_SSS 348 +#define CLK_G3D 349 /* mux clocks */ #define CLK_MOUT_HDMI 1024 +#define CLK_MOUT_GPLL 1025 /* must be greater than maximal clock id */ -#define CLK_NR_CLKS 1025 +#define CLK_NR_CLKS 1026 #endif /* _DT_BINDINGS_CLOCK_EXYNOS_5250_H */ -- cgit v1.2.3 From a5b219b40c463ff162368c7a1dc93387054c79f5 Mon Sep 17 00:00:00 2001 From: Tomasz Stanislawski Date: Fri, 4 Apr 2014 16:53:19 +0200 Subject: clk: samsung: exynos4: export sclk_hdmiphy clock Export sclk_hdmiphy clock to be usable from DT. Signed-off-by: Tomasz Stanislawski Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos4.c | 2 +- include/dt-bindings/clock/exynos4.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 7e2adcbee4cd..c4df294bb7fb 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -428,7 +428,7 @@ static struct samsung_fixed_rate_clock exynos4_fixed_rate_ext_clks[] __initdata /* fixed rate clocks generated inside the soc */ static struct samsung_fixed_rate_clock exynos4_fixed_rate_clks[] __initdata = { FRATE(0, "sclk_hdmi24m", NULL, CLK_IS_ROOT, 24000000), - FRATE(0, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), + FRATE(CLK_SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), FRATE(0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000), }; diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 3ff13bcbe56c..1106ca540a96 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -33,6 +33,7 @@ #define CLK_MOUT_MPLL_USER_C 18 /* Exynos4x12 only */ #define CLK_MOUT_CORE 19 #define CLK_MOUT_APLL 20 +#define CLK_SCLK_HDMIPHY 22 /* gate for special clocks (sclk) */ #define CLK_SCLK_FIMC0 128 -- cgit v1.2.3 From 2ce262f456550f046da1175b968d754b0862b309 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Mon, 28 Apr 2014 15:50:44 +0530 Subject: clk: samsung: exynos5420: Add clock IDs needed by GPU Adds IDs for the clocks needed by the ARM Mali GPU in exynos5420. Signed-off-by: Arun Kumar K Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 4 ++-- include/dt-bindings/clock/exynos5420.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 676a0bdabba0..15c884d0628f 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -363,7 +363,7 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_aclk66_psgen", aclk66_peric_p, SRC_TOP5, 4, 1), MUX(0, "mout_user_aclk333_g2d", user_aclk333_g2d_p, SRC_TOP5, 8, 1), MUX(0, "mout_user_aclk266_g2d", user_aclk266_g2d_p, SRC_TOP5, 12, 1), - MUX_A(0, "mout_user_aclk_g3d", user_aclk_g3d_p, + MUX_A(CLK_MOUT_G3D, "mout_user_aclk_g3d", user_aclk_g3d_p, SRC_TOP5, 16, 1, "aclkg3d"), MUX(0, "mout_user_aclk300_jpeg", user_aclk300_jpeg_p, SRC_TOP5, 20, 1), @@ -373,7 +373,7 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { SRC_TOP5, 28, 1), MUX(0, "sclk_mpll", mpll_p, SRC_TOP6, 0, 1), - MUX(0, "sclk_vpll", vpll_p, SRC_TOP6, 4, 1), + MUX(CLK_MOUT_VPLL, "sclk_vpll", vpll_p, SRC_TOP6, 4, 1), MUX(0, "sclk_spll", spll_p, SRC_TOP6, 8, 1), MUX(0, "sclk_ipll", ipll_p, SRC_TOP6, 12, 1), MUX(0, "sclk_rpll", rpll_p, SRC_TOP6, 16, 1), diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 5eefd8813f02..54db8b34b1dc 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -178,6 +178,8 @@ /* mux clocks */ #define CLK_MOUT_HDMI 640 +#define CLK_MOUT_G3D 641 +#define CLK_MOUT_VPLL 642 /* divider clocks */ #define CLK_DOUT_PIXEL 768 -- cgit v1.2.3 From 3a767b35c6c2f2e5f75e22a429b4d6d8c6736626 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 8 May 2014 16:57:51 +0530 Subject: clk: samsung: exynos5420: add clocks for ISP block This patch adds minimum set of clocks to gate ISP block for power saving. Signed-off-by: Rahul Sharma Signed-off-by: Shaik Ameer Basha Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 86 ++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5420.h | 7 +++ 2 files changed, 93 insertions(+) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 2ee7ef21909d..c7e66219434f 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -57,6 +57,7 @@ #define SRC_FSYS 0x10244 #define SRC_PERIC0 0x10250 #define SRC_PERIC1 0x10254 +#define SRC_ISP 0x10270 #define SRC_TOP10 0x10280 #define SRC_TOP11 0x10284 #define SRC_TOP12 0x10288 @@ -77,12 +78,15 @@ #define DIV_PERIC2 0x10560 #define DIV_PERIC3 0x10564 #define DIV_PERIC4 0x10568 +#define SCLK_DIV_ISP0 0x10580 +#define SCLK_DIV_ISP1 0x10584 #define GATE_BUS_TOP 0x10700 #define GATE_BUS_FSYS0 0x10740 #define GATE_BUS_PERIC 0x10750 #define GATE_BUS_PERIC1 0x10754 #define GATE_BUS_PERIS0 0x10760 #define GATE_BUS_PERIS1 0x10764 +#define GATE_TOP_SCLK_ISP 0x10870 #define GATE_IP_GSCL0 0x10910 #define GATE_IP_GSCL1 0x10920 #define GATE_IP_MFC 0x1092c @@ -145,6 +149,7 @@ static unsigned long exynos5420_clk_regs[] __initdata = { SRC_MASK_FSYS, SRC_MASK_PERIC0, SRC_MASK_PERIC1, + SRC_ISP, DIV_TOP0, DIV_TOP1, DIV_TOP2, @@ -158,12 +163,15 @@ static unsigned long exynos5420_clk_regs[] __initdata = { DIV_PERIC2, DIV_PERIC3, DIV_PERIC4, + SCLK_DIV_ISP0, + SCLK_DIV_ISP1, GATE_BUS_TOP, GATE_BUS_FSYS0, GATE_BUS_PERIC, GATE_BUS_PERIC1, GATE_BUS_PERIS0, GATE_BUS_PERIS1, + GATE_TOP_SCLK_ISP, GATE_IP_GSCL0, GATE_IP_GSCL1, GATE_IP_MFC, @@ -250,6 +258,15 @@ PNAME(mout_user_aclk200_fsys_p) = {"fin_pll", "mout_sw_aclk200_fsys"}; PNAME(mout_sw_aclk200_fsys2_p) = {"dout_aclk200_fsys2", "mout_sclk_spll"}; PNAME(mout_user_aclk200_fsys2_p) = {"fin_pll", "mout_sw_aclk200_fsys2"}; +PNAME(mout_sw_aclk400_isp_p) = {"dout_aclk400_isp", "mout_sclk_spll"}; +PNAME(mout_user_aclk400_isp_p) = {"fin_pll", "mout_sw_aclk400_isp"}; + +PNAME(mout_sw_aclk333_432_isp0_p) = {"dout_aclk333_432_isp0", + "mout_sclk_spll"}; +PNAME(mout_user_aclk333_432_isp0_p) = {"fin_pll", "mout_sw_aclk333_432_isp0"}; + +PNAME(mout_sw_aclk333_432_isp_p) = {"dout_aclk333_432_isp", "mout_sclk_spll"}; +PNAME(mout_user_aclk333_432_isp_p) = {"fin_pll", "mout_sw_aclk333_432_isp"}; PNAME(mout_sw_aclk200_p) = {"dout_aclk200", "mout_sclk_spll"}; PNAME(mout_aclk200_disp1_p) = {"fin_pll", "mout_sw_aclk200"}; @@ -265,6 +282,7 @@ PNAME(mout_user_aclk166_p) = {"fin_pll", "mout_sw_aclk166"}; PNAME(mout_sw_aclk266_p) = {"dout_aclk266", "mout_sclk_spll"}; PNAME(mout_user_aclk266_p) = {"fin_pll", "mout_sw_aclk266"}; +PNAME(mout_user_aclk266_isp_p) = {"fin_pll", "mout_sw_aclk266"}; PNAME(mout_sw_aclk333_432_gscl_p) = {"dout_aclk333_432_gscl", "mout_sclk_spll"}; PNAME(mout_user_aclk333_432_gscl_p) = {"fin_pll", "mout_sw_aclk333_432_gscl"}; @@ -332,6 +350,7 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "sclk_bpll", mout_bpll_p, SRC_CDREX, 0, 1), + MUX(0, "mout_aclk400_isp", mout_group1_p, SRC_TOP0, 0, 2), MUX_A(0, "mout_aclk400_mscl", mout_group1_p, SRC_TOP0, 4, 2, "aclk400_mscl"), MUX(0, "mout_aclk200", mout_group1_p, SRC_TOP0, 8, 2), @@ -339,7 +358,10 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_aclk200_fsys", mout_group1_p, SRC_TOP0, 28, 2), MUX(0, "mout_aclk333_432_gscl", mout_group4_p, SRC_TOP1, 0, 2), + MUX(0, "mout_aclk333_432_isp", mout_group4_p, + SRC_TOP1, 4, 2), MUX(0, "mout_aclk66", mout_group1_p, SRC_TOP1, 8, 2), + MUX(0, "mout_aclk333_432_isp0", mout_group4_p, SRC_TOP1, 12, 2), MUX(0, "mout_aclk266", mout_group1_p, SRC_TOP1, 20, 2), MUX(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2), MUX(0, "mout_aclk333", mout_group1_p, SRC_TOP1, 28, 2), @@ -351,6 +373,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_aclk300_disp1", mout_group1_p, SRC_TOP2, 24, 2), MUX(0, "mout_aclk300_gscl", mout_group1_p, SRC_TOP2, 28, 2), + MUX(0, "mout_user_aclk400_isp", mout_user_aclk400_isp_p, + SRC_TOP3, 0, 1), MUX(0, "mout_user_aclk400_mscl", mout_user_aclk400_mscl_p, SRC_TOP3, 4, 1), MUX(0, "mout_aclk200_disp1", mout_aclk200_disp1_p, SRC_TOP3, 8, 1), @@ -361,7 +385,13 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_user_aclk333_432_gscl", mout_user_aclk333_432_gscl_p, SRC_TOP4, 0, 1), + MUX(0, "mout_user_aclk333_432_isp", mout_user_aclk333_432_isp_p, + SRC_TOP4, 4, 1), MUX(0, "mout_aclk66_peric", mout_aclk66_peric_p, SRC_TOP4, 8, 1), + MUX(0, "mout_user_aclk333_432_isp0", mout_user_aclk333_432_isp0_p, + SRC_TOP4, 12, 1), + MUX(0, "mout_user_aclk266_isp", mout_user_aclk266_isp_p, + SRC_TOP4, 16, 1), MUX(0, "mout_user_aclk266", mout_user_aclk266_p, SRC_TOP4, 20, 1), MUX(0, "mout_user_aclk166", mout_user_aclk166_p, SRC_TOP4, 24, 1), MUX(0, "mout_user_aclk333", mout_user_aclk333_p, SRC_TOP4, 28, 1), @@ -389,6 +419,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_sclk_dpll", mout_dpll_p, SRC_TOP6, 24, 1), MUX(0, "mout_sclk_cpll", mout_cpll_p, SRC_TOP6, 28, 1), + MUX(0, "mout_sw_aclk400_isp", mout_sw_aclk400_isp_p, + SRC_TOP10, 0, 1), MUX(0, "mout_sw_aclk400_mscl", mout_sw_aclk400_mscl_p, SRC_TOP10, 4, 1), MUX(0, "mout_sw_aclk200", mout_sw_aclk200_p, SRC_TOP10, 8, 1), @@ -396,9 +428,14 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { SRC_TOP10, 12, 1), MUX(0, "mout_sw_aclk200_fsys", mout_sw_aclk200_fsys_p, SRC_TOP10, 28, 1), + MUX(0, "mout_sw_aclk333_432_gscl", mout_sw_aclk333_432_gscl_p, SRC_TOP11, 0, 1), + MUX(0, "mout_sw_aclk333_432_isp", mout_sw_aclk333_432_isp_p, + SRC_TOP11, 4, 1), MUX(0, "mout_sw_aclk66", mout_sw_aclk66_p, SRC_TOP11, 8, 1), + MUX(0, "mout_sw_aclk333_432_isp0", mout_sw_aclk333_432_isp0_p, + SRC_TOP11, 12, 1), MUX(0, "mout_sw_aclk266", mout_sw_aclk266_p, SRC_TOP11, 20, 1), MUX(0, "mout_sw_aclk166", mout_sw_aclk166_p, SRC_TOP11, 24, 1), MUX(0, "mout_sw_aclk333", mout_sw_aclk333_p, SRC_TOP11, 28, 1), @@ -446,6 +483,13 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_spi0", mout_group2_p, SRC_PERIC1, 20, 3), MUX(0, "mout_spi1", mout_group2_p, SRC_PERIC1, 24, 3), MUX(0, "mout_spi2", mout_group2_p, SRC_PERIC1, 28, 3), + + /* ISP Block */ + MUX(0, "mout_pwm_isp", mout_group2_p, SRC_ISP, 24, 3), + MUX(0, "mout_uart_isp", mout_group2_p, SRC_ISP, 20, 3), + MUX(0, "mout_spi0_isp", mout_group2_p, SRC_ISP, 12, 3), + MUX(0, "mout_spi1_isp", mout_group2_p, SRC_ISP, 16, 3), + MUX(0, "mout_isp_sensor", mout_group2_p, SRC_ISP, 28, 3), }; static struct samsung_div_clock exynos5420_div_clks[] __initdata = { @@ -455,6 +499,7 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "div_kfc", "mout_kfc", DIV_KFC0, 0, 3), DIV(0, "sclk_kpll", "mout_kpll", DIV_KFC0, 24, 3), + DIV(0, "dout_aclk400_isp", "mout_aclk400_isp", DIV_TOP0, 0, 3), DIV(0, "dout_aclk400_mscl", "mout_aclk400_mscl", DIV_TOP0, 4, 3), DIV(0, "dout_aclk200", "mout_aclk200", DIV_TOP0, 8, 3), DIV(0, "dout_aclk200_fsys2", "mout_aclk200_fsys2", DIV_TOP0, 12, 3), @@ -463,7 +508,11 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_aclk333_432_gscl", "mout_aclk333_432_gscl", DIV_TOP1, 0, 3), + DIV(0, "dout_aclk333_432_isp", "mout_aclk333_432_isp", + DIV_TOP1, 4, 3), DIV(0, "dout_aclk66", "mout_aclk66", DIV_TOP1, 8, 6), + DIV(0, "dout_aclk333_432_isp0", "mout_aclk333_432_isp0", + DIV_TOP1, 16, 3), DIV(0, "dout_aclk266", "mout_aclk266", DIV_TOP1, 20, 3), DIV(0, "dout_aclk166", "mout_aclk166", DIV_TOP1, 24, 3), DIV(0, "dout_aclk333", "mout_aclk333", DIV_TOP1, 28, 3), @@ -526,6 +575,19 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_pre_spi0", "dout_spi0", DIV_PERIC4, 8, 8), DIV(0, "dout_pre_spi1", "dout_spi1", DIV_PERIC4, 16, 8), DIV(0, "dout_pre_spi2", "dout_spi2", DIV_PERIC4, 24, 8), + + /* ISP Block */ + DIV(0, "dout_isp_sensor0", "mout_isp_sensor", SCLK_DIV_ISP0, 8, 8), + DIV(0, "dout_isp_sensor1", "mout_isp_sensor", SCLK_DIV_ISP0, 16, 8), + DIV(0, "dout_isp_sensor2", "mout_isp_sensor", SCLK_DIV_ISP0, 24, 8), + DIV(0, "dout_pwm_isp", "mout_pwm_isp", SCLK_DIV_ISP1, 28, 4), + DIV(0, "dout_uart_isp", "mout_uart_isp", SCLK_DIV_ISP1, 24, 4), + DIV(0, "dout_spi0_isp", "mout_spi0_isp", SCLK_DIV_ISP1, 16, 4), + DIV(0, "dout_spi1_isp", "mout_spi1_isp", SCLK_DIV_ISP1, 20, 4), + DIV_F(0, "dout_spi0_isp_pre", "dout_spi0_isp", SCLK_DIV_ISP1, 0, 8, + CLK_SET_RATE_PARENT, 0), + DIV_F(0, "dout_spi1_isp_pre", "dout_spi1_isp", SCLK_DIV_ISP1, 8, 8, + CLK_SET_RATE_PARENT, 0), }; static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { @@ -547,20 +609,28 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE_BUS_TOP, 1, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk300_jpeg", "mout_user_aclk300_jpeg", GATE_BUS_TOP, 4, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk333_432_isp0", "mout_user_aclk333_432_isp0", + GATE_BUS_TOP, 5, 0, 0), GATE(0, "aclk300_gscl", "mout_user_aclk300_gscl", GATE_BUS_TOP, 6, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk333_432_gscl", "mout_user_aclk333_432_gscl", GATE_BUS_TOP, 7, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk333_432_isp", "mout_user_aclk333_432_isp", + GATE_BUS_TOP, 8, 0, 0), GATE(0, "pclk66_gpio", "mout_sw_aclk66", GATE_BUS_TOP, 9, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk66_psgen", "mout_aclk66_psgen", GATE_BUS_TOP, 10, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk66_peric", "mout_aclk66_peric", GATE_BUS_TOP, 11, 0, 0), + GATE(0, "aclk266_isp", "mout_user_aclk266_isp", + GATE_BUS_TOP, 13, 0, 0), GATE(0, "aclk166", "mout_user_aclk166", GATE_BUS_TOP, 14, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk333", "mout_aclk333", GATE_BUS_TOP, 15, CLK_IGNORE_UNUSED, 0), + GATE(0, "aclk400_isp", "mout_user_aclk400_isp", + GATE_BUS_TOP, 16, 0, 0), /* sclk */ GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_uart0", @@ -735,6 +805,22 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_SMMU_FIMD1, "smmu_fimd1", "aclk300_disp1", GATE_IP_DISP1, 8, 0, 0), + /* ISP */ + GATE(CLK_SCLK_UART_ISP, "sclk_uart_isp", "dout_uart_isp", + GATE_TOP_SCLK_ISP, 0, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI0_ISP, "sclk_spi0_isp", "dout_spi0_isp_pre", + GATE_TOP_SCLK_ISP, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI1_ISP, "sclk_spi1_isp", "dout_spi1_isp_pre", + GATE_TOP_SCLK_ISP, 2, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_PWM_ISP, "sclk_pwm_isp", "dout_pwm_isp", + GATE_TOP_SCLK_ISP, 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_ISP_SENSOR0, "sclk_isp_sensor0", "dout_isp_sensor0", + GATE_TOP_SCLK_ISP, 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_ISP_SENSOR1, "sclk_isp_sensor1", "dout_isp_sensor1", + GATE_TOP_SCLK_ISP, 8, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_ISP_SENSOR2, "sclk_isp_sensor2", "dout_isp_sensor2", + GATE_TOP_SCLK_ISP, 12, CLK_SET_RATE_PARENT, 0), + GATE(CLK_MFC, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0), GATE(CLK_SMMU_MFCL, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0), GATE(CLK_SMMU_MFCR, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0), diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 54db8b34b1dc..bddf5496fef2 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -175,6 +175,13 @@ #define CLK_ACLK_G3D 500 #define CLK_G3D 501 #define CLK_SMMU_MIXER 502 +#define CLK_SCLK_UART_ISP 510 +#define CLK_SCLK_SPI0_ISP 511 +#define CLK_SCLK_SPI1_ISP 512 +#define CLK_SCLK_PWM_ISP 513 +#define CLK_SCLK_ISP_SENSOR0 514 +#define CLK_SCLK_ISP_SENSOR1 515 +#define CLK_SCLK_ISP_SENSOR2 516 /* mux clocks */ #define CLK_MOUT_HDMI 640 -- cgit v1.2.3 From 02932381ca1d9ab894c893b28fed288d6bae011b Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 8 May 2014 16:57:52 +0530 Subject: clk: samsung: exynos5420: update clocks for GSCL and MSCL blocks This patch adds the missing GSCL and MSCL block clocks and corrects some wrong parent-child relationships. Signed-off-by: Rahul Sharma Signed-off-by: Shaik Ameer Basha Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 71 +++++++++++++++++++++------------- include/dt-bindings/clock/exynos5420.h | 4 +- 2 files changed, 47 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index c7e66219434f..cb7a63913e18 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -80,6 +80,7 @@ #define DIV_PERIC4 0x10568 #define SCLK_DIV_ISP0 0x10580 #define SCLK_DIV_ISP1 0x10584 +#define DIV2_RATIO0 0x10590 #define GATE_BUS_TOP 0x10700 #define GATE_BUS_FSYS0 0x10740 #define GATE_BUS_PERIC 0x10750 @@ -165,6 +166,7 @@ static unsigned long exynos5420_clk_regs[] __initdata = { DIV_PERIC4, SCLK_DIV_ISP0, SCLK_DIV_ISP1, + DIV2_RATIO0, GATE_BUS_TOP, GATE_BUS_FSYS0, GATE_BUS_PERIC, @@ -576,6 +578,11 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_pre_spi1", "dout_spi1", DIV_PERIC4, 16, 8), DIV(0, "dout_pre_spi2", "dout_spi2", DIV_PERIC4, 24, 8), + /* GSCL Block */ + DIV(0, "dout_gscl_blk_300", "mout_user_aclk300_gscl", + DIV2_RATIO0, 4, 2), + DIV(0, "dout_gscl_blk_333", "aclk333_432_gscl", DIV2_RATIO0, 6, 2), + /* ISP Block */ DIV(0, "dout_isp_sensor0", "mout_isp_sensor", SCLK_DIV_ISP0, 8, 8), DIV(0, "dout_isp_sensor1", "mout_isp_sensor", SCLK_DIV_ISP0, 16, 8), @@ -631,6 +638,8 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE_BUS_TOP, 15, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk400_isp", "mout_user_aclk400_isp", GATE_BUS_TOP, 16, 0, 0), + GATE(0, "aclk400_mscl", "mout_user_aclk400_mscl", + GATE_BUS_TOP, 17, 0, 0), /* sclk */ GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_uart0", @@ -678,11 +687,6 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_SCLK_USBD301, "sclk_unipro", "dout_unipro", SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_GSCL_WA, "sclk_gscl_wa", "aclK333_432_gscl", - GATE_TOP_SCLK_GSCL, 6, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_GSCL_WB, "sclk_gscl_wb", "aclk333_432_gscl", - GATE_TOP_SCLK_GSCL, 7, CLK_SET_RATE_PARENT, 0), - /* Display */ GATE(CLK_SCLK_FIMD1, "sclk_fimd1", "dout_fimd1", GATE_TOP_SCLK_DISP1, 0, CLK_SET_RATE_PARENT, 0), @@ -776,27 +780,49 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_TMU, "tmu", "aclk66_psgen", GATE_BUS_PERIS1, 5, 0, 0), GATE(CLK_TMU_GPU, "tmu_gpu", "aclk66_psgen", GATE_BUS_PERIS1, 6, 0, 0), + /* GSCL Block */ + GATE(CLK_SCLK_GSCL_WA, "sclk_gscl_wa", "mout_user_aclk333_432_gscl", + GATE_TOP_SCLK_GSCL, 6, 0, 0), + GATE(CLK_SCLK_GSCL_WB, "sclk_gscl_wb", "mout_user_aclk333_432_gscl", + GATE_TOP_SCLK_GSCL, 7, 0, 0), + GATE(CLK_GSCL0, "gscl0", "aclk300_gscl", GATE_IP_GSCL0, 0, 0, 0), GATE(CLK_GSCL1, "gscl1", "aclk300_gscl", GATE_IP_GSCL0, 1, 0, 0), - GATE(CLK_CLK_3AA, "clk_3aa", "aclk300_gscl", GATE_IP_GSCL0, 4, 0, 0), - - GATE(CLK_SMMU_3AA, "smmu_3aa", "aclk333_432_gscl", GATE_IP_GSCL1, 2, 0, - 0), - GATE(CLK_SMMU_FIMCL0, "smmu_fimcl0", "aclk333_432_gscl", + GATE(CLK_FIMC_3AA, "fimc_3aa", "aclk333_432_gscl", + GATE_IP_GSCL0, 4, 0, 0), + GATE(CLK_FIMC_LITE0, "fimc_lite0", "aclk333_432_gscl", + GATE_IP_GSCL0, 5, 0, 0), + GATE(CLK_FIMC_LITE1, "fimc_lite1", "aclk333_432_gscl", + GATE_IP_GSCL0, 6, 0, 0), + + GATE(CLK_SMMU_3AA, "smmu_3aa", "dout_gscl_blk_333", + GATE_IP_GSCL1, 2, 0, 0), + GATE(CLK_SMMU_FIMCL0, "smmu_fimcl0", "dout_gscl_blk_333", GATE_IP_GSCL1, 3, 0, 0), - GATE(CLK_SMMU_FIMCL1, "smmu_fimcl1", "aclk333_432_gscl", + GATE(CLK_SMMU_FIMCL1, "smmu_fimcl1", "dout_gscl_blk_333", GATE_IP_GSCL1, 4, 0, 0), - GATE(CLK_SMMU_GSCL0, "smmu_gscl0", "aclk300_gscl", GATE_IP_GSCL1, 6, 0, - 0), - GATE(CLK_SMMU_GSCL1, "smmu_gscl1", "aclk300_gscl", GATE_IP_GSCL1, 7, 0, - 0), - GATE(CLK_GSCL_WA, "gscl_wa", "aclk300_gscl", GATE_IP_GSCL1, 12, 0, 0), - GATE(CLK_GSCL_WB, "gscl_wb", "aclk300_gscl", GATE_IP_GSCL1, 13, 0, 0), - GATE(CLK_SMMU_FIMCL3, "smmu_fimcl3,", "aclk333_432_gscl", + GATE(CLK_SMMU_GSCL0, "smmu_gscl0", "dout_gscl_blk_300", + GATE_IP_GSCL1, 6, 0, 0), + GATE(CLK_SMMU_GSCL1, "smmu_gscl1", "dout_gscl_blk_300", + GATE_IP_GSCL1, 7, 0, 0), + GATE(CLK_GSCL_WA, "gscl_wa", "sclk_gscl_wa", GATE_IP_GSCL1, 12, 0, 0), + GATE(CLK_GSCL_WB, "gscl_wb", "sclk_gscl_wb", GATE_IP_GSCL1, 13, 0, 0), + GATE(CLK_SMMU_FIMCL3, "smmu_fimcl3,", "dout_gscl_blk_333", GATE_IP_GSCL1, 16, 0, 0), GATE(CLK_FIMC_LITE3, "fimc_lite3", "aclk333_432_gscl", GATE_IP_GSCL1, 17, 0, 0), + /* MSCL Block */ + GATE(CLK_MSCL0, "mscl0", "aclk400_mscl", GATE_IP_MSCL, 0, 0, 0), + GATE(CLK_MSCL1, "mscl1", "aclk400_mscl", GATE_IP_MSCL, 1, 0, 0), + GATE(CLK_MSCL2, "mscl2", "aclk400_mscl", GATE_IP_MSCL, 2, 0, 0), + GATE(CLK_SMMU_MSCL0, "smmu_mscl0", "aclk400_mscl", + GATE_IP_MSCL, 8, 0, 0), + GATE(CLK_SMMU_MSCL1, "smmu_mscl1", "aclk400_mscl", + GATE_IP_MSCL, 9, 0, 0), + GATE(CLK_SMMU_MSCL2, "smmu_mscl2", "aclk400_mscl", + GATE_IP_MSCL, 10, 0, 0), + GATE(CLK_FIMD1, "fimd1", "aclk300_disp1", GATE_IP_DISP1, 0, 0, 0), GATE(CLK_DSIM1, "dsim1", "aclk200_disp1", GATE_IP_DISP1, 3, 0, 0), GATE(CLK_DP1, "dp1", "aclk200_disp1", GATE_IP_DISP1, 4, 0, 0), @@ -835,15 +861,6 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_SMMU_JPEG, "smmu_jpeg", "aclk300_jpeg", GATE_IP_GEN, 7, 0, 0), GATE(CLK_SMMU_MDMA1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0), - GATE(CLK_MSCL0, "mscl0", "aclk400_mscl", GATE_IP_MSCL, 0, 0, 0), - GATE(CLK_MSCL1, "mscl1", "aclk400_mscl", GATE_IP_MSCL, 1, 0, 0), - GATE(CLK_MSCL2, "mscl2", "aclk400_mscl", GATE_IP_MSCL, 2, 0, 0), - GATE(CLK_SMMU_MSCL0, "smmu_mscl0", "aclk400_mscl", GATE_IP_MSCL, 8, 0, - 0), - GATE(CLK_SMMU_MSCL1, "smmu_mscl1", "aclk400_mscl", GATE_IP_MSCL, 9, 0, - 0), - GATE(CLK_SMMU_MSCL2, "smmu_mscl2", "aclk400_mscl", GATE_IP_MSCL, 10, 0, - 0), GATE(CLK_SMMU_MIXER, "smmu_mixer", "aclk200_disp1", GATE_IP_DISP1, 9, 0, 0), }; diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index bddf5496fef2..6e22fddb0134 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -159,7 +159,7 @@ #define CLK_GSCL_WB 464 #define CLK_GSCL0 465 #define CLK_GSCL1 466 -#define CLK_CLK_3AA 467 +#define CLK_FIMC_3AA 467 #define CLK_ACLK266_G2D 470 #define CLK_SSS 471 #define CLK_SLIM_SSS 472 @@ -172,6 +172,8 @@ #define CLK_SMMU_FIMCL1 493 #define CLK_SMMU_FIMCL3 494 #define CLK_FIMC_LITE3 495 +#define CLK_FIMC_LITE0 496 +#define CLK_FIMC_LITE1 497 #define CLK_ACLK_G3D 500 #define CLK_G3D 501 #define CLK_SMMU_MIXER 502 -- cgit v1.2.3 From 3fac5941da6c3afacabf3cc01914583e5689622b Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 8 May 2014 16:57:54 +0530 Subject: clk: samsung: exynos5420: update clocks for G2D and G3D blocks This patch adds missing clocks of G2D block. It also removes the aclkg3d alias from G3D block clocks. Signed-off-by: Rahul Sharma Signed-off-by: Shaik Ameer Basha Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 18 +++++++++++------- include/dt-bindings/clock/exynos5420.h | 2 ++ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 48a5772dca89..e263b7be5059 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -399,12 +399,12 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_user_aclk333", mout_user_aclk333_p, SRC_TOP4, 28, 1), MUX(0, "mout_aclk66_psgen", mout_aclk66_peric_p, SRC_TOP5, 4, 1), - MUX(0, "mout_user_aclk333_g2d", mout_user_aclk333_g2d_p, SRC_TOP5, - 8, 1), - MUX(0, "mout_user_aclk266_g2d", mout_user_aclk266_g2d_p, SRC_TOP5, - 12, 1), - MUX_A(CLK_MOUT_G3D, "mout_user_aclk_g3d", mout_user_aclk_g3d_p, - SRC_TOP5, 16, 1, "aclkg3d"), + MUX(0, "mout_user_aclk333_g2d", mout_user_aclk333_g2d_p, + SRC_TOP5, 8, 1), + MUX(0, "mout_user_aclk266_g2d", mout_user_aclk266_g2d_p, + SRC_TOP5, 12, 1), + MUX(CLK_MOUT_G3D, "mout_user_aclk_g3d", mout_user_aclk_g3d_p, + SRC_TOP5, 16, 1), MUX(0, "mout_user_aclk300_jpeg", mout_user_aclk300_jpeg_p, SRC_TOP5, 20, 1), MUX(0, "mout_user_aclk300_disp1", mout_user_aclk300_disp1_p, @@ -602,7 +602,11 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { /* G2D */ + GATE(CLK_MDMA0, "mdma0", "aclk266_g2d", GATE_IP_G2D, 1, 0, 0), GATE(CLK_SSS, "sss", "aclk266_g2d", GATE_IP_G2D, 2, 0, 0), + GATE(CLK_G2D, "g2d", "aclk333_g2d", GATE_IP_G2D, 3, 0, 0), + GATE(CLK_SMMU_MDMA0, "smmu_mdma0", "aclk266_g2d", GATE_IP_G2D, 5, 0, 0), + GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk333_g2d", GATE_IP_G2D, 7, 0, 0), /* TODO: Re-verify the CG bits for all the gate clocks */ GATE_A(CLK_MCT, "pclk_st", "aclk66_psgen", GATE_BUS_PERIS1, 2, 0, 0, @@ -854,7 +858,7 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_SMMU_MFCL, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0), GATE(CLK_SMMU_MFCR, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0), - GATE(CLK_G3D, "g3d", "aclkg3d", GATE_IP_G3D, 9, 0, 0), + GATE(CLK_G3D, "g3d", "mout_user_aclk_g3d", GATE_IP_G3D, 9, 0, 0), GATE(CLK_ROTATOR, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0), GATE(CLK_JPEG, "jpeg", "aclk300_jpeg", GATE_IP_GEN, 2, 0, 0), diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 6e22fddb0134..bf85418a2945 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -177,6 +177,8 @@ #define CLK_ACLK_G3D 500 #define CLK_G3D 501 #define CLK_SMMU_MIXER 502 +#define CLK_SMMU_G2D 503 +#define CLK_SMMU_MDMA0 504 #define CLK_SCLK_UART_ISP 510 #define CLK_SCLK_SPI0_ISP 511 #define CLK_SCLK_SPI1_ISP 512 -- cgit v1.2.3 From 424b673a0557693fdc2ac6cff5289153d6fb8903 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 8 May 2014 16:57:55 +0530 Subject: clk: samsung: exynos5420: update clocks for DISP1 block This patch corrects some child-parent clock relationships, and updates the clocks according to the latest datasheet. Signed-off-by: Rahul Sharma Signed-off-by: Shaik Ameer Basha Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 56 +++++++++++++++++++++++----------- include/dt-bindings/clock/exynos5420.h | 3 +- 2 files changed, 41 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index e263b7be5059..32d16f5cff53 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -61,7 +61,8 @@ #define SRC_TOP10 0x10280 #define SRC_TOP11 0x10284 #define SRC_TOP12 0x10288 -#define SRC_MASK_DISP10 0x1032c +#define SRC_MASK_TOP2 0x10308 +#define SRC_MASK_DISP10 0x1032c #define SRC_MASK_FSYS 0x10340 #define SRC_MASK_PERIC0 0x10350 #define SRC_MASK_PERIC1 0x10354 @@ -100,6 +101,7 @@ #define GATE_TOP_SCLK_MAU 0x1083c #define GATE_TOP_SCLK_FSYS 0x10840 #define GATE_TOP_SCLK_PERIC 0x10850 +#define TOP_SPARE2 0x10b08 #define BPLL_LOCK 0x20010 #define BPLL_CON0 0x20110 #define SRC_CDREX 0x20200 @@ -146,6 +148,7 @@ static unsigned long exynos5420_clk_regs[] __initdata = { SRC_TOP10, SRC_TOP11, SRC_TOP12, + SRC_MASK_TOP2, SRC_MASK_DISP10, SRC_MASK_FSYS, SRC_MASK_PERIC0, @@ -186,6 +189,7 @@ static unsigned long exynos5420_clk_regs[] __initdata = { GATE_TOP_SCLK_MAU, GATE_TOP_SCLK_FSYS, GATE_TOP_SCLK_PERIC, + TOP_SPARE2, SRC_CDREX, SRC_KFC, DIV_KFC0, @@ -252,6 +256,7 @@ PNAME(mout_group3_p) = {"mout_sclk_rpll", "mout_sclk_spll"}; PNAME(mout_group4_p) = {"mout_sclk_ipll", "mout_sclk_dpll", "mout_sclk_mpll"}; PNAME(mout_group5_p) = {"mout_sclk_vpll", "mout_sclk_dpll"}; +PNAME(mout_fimd1_final_p) = {"mout_fimd1", "mout_fimd1_opt"}; PNAME(mout_sw_aclk66_p) = {"dout_aclk66", "mout_sclk_spll"}; PNAME(mout_aclk66_peric_p) = { "fin_pll", "mout_sw_aclk66" }; @@ -271,7 +276,7 @@ PNAME(mout_sw_aclk333_432_isp_p) = {"dout_aclk333_432_isp", "mout_sclk_spll"}; PNAME(mout_user_aclk333_432_isp_p) = {"fin_pll", "mout_sw_aclk333_432_isp"}; PNAME(mout_sw_aclk200_p) = {"dout_aclk200", "mout_sclk_spll"}; -PNAME(mout_aclk200_disp1_p) = {"fin_pll", "mout_sw_aclk200"}; +PNAME(mout_user_aclk200_disp1_p) = {"fin_pll", "mout_sw_aclk200"}; PNAME(mout_sw_aclk400_mscl_p) = {"dout_aclk400_mscl", "mout_sclk_spll"}; PNAME(mout_user_aclk400_mscl_p) = {"fin_pll", "mout_sw_aclk400_mscl"}; @@ -293,7 +298,9 @@ PNAME(mout_sw_aclk300_gscl_p) = {"dout_aclk300_gscl", "mout_sclk_spll"}; PNAME(mout_user_aclk300_gscl_p) = {"fin_pll", "mout_sw_aclk300_gscl"}; PNAME(mout_sw_aclk300_disp1_p) = {"dout_aclk300_disp1", "mout_sclk_spll"}; +PNAME(mout_sw_aclk400_disp1_p) = {"dout_aclk400_disp1", "mout_sclk_spll"}; PNAME(mout_user_aclk300_disp1_p) = {"fin_pll", "mout_sw_aclk300_disp1"}; +PNAME(mout_user_aclk400_disp1_p) = {"fin_pll", "mout_sw_aclk400_disp1"}; PNAME(mout_sw_aclk300_jpeg_p) = {"dout_aclk300_jpeg", "mout_sclk_spll"}; PNAME(mout_user_aclk300_jpeg_p) = {"fin_pll", "mout_sw_aclk300_jpeg"}; @@ -368,6 +375,7 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2), MUX(0, "mout_aclk333", mout_group1_p, SRC_TOP1, 28, 2), + MUX(0, "mout_aclk400_disp1", mout_group1_p, SRC_TOP2, 4, 2), MUX(0, "mout_aclk333_g2d", mout_group1_p, SRC_TOP2, 8, 2), MUX(0, "mout_aclk266_g2d", mout_group1_p, SRC_TOP2, 12, 2), MUX(0, "mout_aclk_g3d", mout_group5_p, SRC_TOP2, 16, 1), @@ -379,7 +387,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { SRC_TOP3, 0, 1), MUX(0, "mout_user_aclk400_mscl", mout_user_aclk400_mscl_p, SRC_TOP3, 4, 1), - MUX(0, "mout_aclk200_disp1", mout_aclk200_disp1_p, SRC_TOP3, 8, 1), + MUX(0, "mout_user_aclk200_disp1", mout_user_aclk200_disp1_p, + SRC_TOP3, 8, 1), MUX(0, "mout_user_aclk200_fsys2", mout_user_aclk200_fsys2_p, SRC_TOP3, 12, 1), MUX(0, "mout_user_aclk200_fsys", mout_user_aclk200_fsys_p, @@ -398,6 +407,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_user_aclk166", mout_user_aclk166_p, SRC_TOP4, 24, 1), MUX(0, "mout_user_aclk333", mout_user_aclk333_p, SRC_TOP4, 28, 1), + MUX(0, "mout_user_aclk400_disp1", mout_user_aclk400_disp1_p, + SRC_TOP5, 0, 1), MUX(0, "mout_aclk66_psgen", mout_aclk66_peric_p, SRC_TOP5, 4, 1), MUX(0, "mout_user_aclk333_g2d", mout_user_aclk333_g2d_p, SRC_TOP5, 8, 1), @@ -442,6 +453,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_sw_aclk166", mout_sw_aclk166_p, SRC_TOP11, 24, 1), MUX(0, "mout_sw_aclk333", mout_sw_aclk333_p, SRC_TOP11, 28, 1), + MUX(0, "mout_sw_aclk400_disp1", mout_sw_aclk400_disp1_p, + SRC_TOP12, 4, 1), MUX(0, "mout_sw_aclk333_g2d", mout_sw_aclk333_g2d_p, SRC_TOP12, 8, 1), MUX(0, "mout_sw_aclk266_g2d", mout_sw_aclk266_g2d_p, @@ -460,6 +473,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_dp1", mout_group2_p, SRC_DISP10, 20, 3), MUX(0, "mout_pixel", mout_group2_p, SRC_DISP10, 24, 3), MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_DISP10, 28, 1), + MUX(0, "mout_fimd1_opt", mout_group2_p, SRC_DISP10, 8, 3), + MUX(0, "mout_fimd1_final", mout_fimd1_final_p, TOP_SPARE2, 8, 1), /* MAU Block */ MUX(0, "mout_maudio0", mout_maudio0_p, SRC_MAU, 28, 3), @@ -523,15 +538,16 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_aclk266_g2d", "mout_aclk266_g2d", DIV_TOP2, 12, 3), DIV(0, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, 16, 3), DIV(0, "dout_aclk300_jpeg", "mout_aclk300_jpeg", DIV_TOP2, 20, 3), - DIV_A(0, "dout_aclk300_disp1", "mout_aclk300_disp1", - DIV_TOP2, 24, 3, "aclk300_disp1"), + DIV(0, "dout_aclk300_disp1", "mout_aclk300_disp1", DIV_TOP2, 24, 3), DIV(0, "dout_aclk300_gscl", "mout_aclk300_gscl", DIV_TOP2, 28, 3), /* DISP1 Block */ - DIV(0, "dout_fimd1", "mout_fimd1", DIV_DISP10, 0, 4), + DIV(0, "dout_fimd1", "mout_fimd1_final", DIV_DISP10, 0, 4), DIV(0, "dout_mipi1", "mout_mipi1", DIV_DISP10, 16, 8), DIV(0, "dout_dp1", "mout_dp1", DIV_DISP10, 24, 4), DIV(CLK_DOUT_PIXEL, "dout_hdmi_pixel", "mout_pixel", DIV_DISP10, 28, 4), + DIV(0, "dout_disp1_blk", "aclk200_disp1", DIV2_RATIO0, 16, 2), + DIV(0, "dout_aclk400_disp1", "mout_aclk400_disp1", DIV_TOP2, 4, 3), /* Audio Block */ DIV(0, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4), @@ -647,6 +663,11 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE_BUS_TOP, 16, 0, 0), GATE(0, "aclk400_mscl", "mout_user_aclk400_mscl", GATE_BUS_TOP, 17, 0, 0), + GATE(0, "aclk200_disp1", "mout_user_aclk200_disp1", + GATE_BUS_TOP, 18, 0, 0), + + GATE(0, "aclk300_disp1", "mout_user_aclk300_disp1", + SRC_MASK_TOP2, 24, 0, 0), /* sclk */ GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_uart0", @@ -696,15 +717,15 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { /* Display */ GATE(CLK_SCLK_FIMD1, "sclk_fimd1", "dout_fimd1", - GATE_TOP_SCLK_DISP1, 0, CLK_SET_RATE_PARENT, 0), + GATE_TOP_SCLK_DISP1, 0, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_MIPI1, "sclk_mipi1", "dout_mipi1", - GATE_TOP_SCLK_DISP1, 3, CLK_SET_RATE_PARENT, 0), + GATE_TOP_SCLK_DISP1, 3, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_HDMI, "sclk_hdmi", "mout_hdmi", - GATE_TOP_SCLK_DISP1, 9, CLK_SET_RATE_PARENT, 0), + GATE_TOP_SCLK_DISP1, 9, 0, 0), GATE(CLK_SCLK_PIXEL, "sclk_pixel", "dout_hdmi_pixel", - GATE_TOP_SCLK_DISP1, 10, CLK_SET_RATE_PARENT, 0), + GATE_TOP_SCLK_DISP1, 10, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_DP1, "sclk_dp1", "dout_dp1", - GATE_TOP_SCLK_DISP1, 20, CLK_SET_RATE_PARENT, 0), + GATE_TOP_SCLK_DISP1, 20, CLK_SET_RATE_PARENT, 0), /* Maudio Block */ GATE(CLK_SCLK_MAUDIO0, "sclk_maudio0", "dout_maudio0", @@ -833,10 +854,14 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_FIMD1, "fimd1", "aclk300_disp1", GATE_IP_DISP1, 0, 0, 0), GATE(CLK_DSIM1, "dsim1", "aclk200_disp1", GATE_IP_DISP1, 3, 0, 0), GATE(CLK_DP1, "dp1", "aclk200_disp1", GATE_IP_DISP1, 4, 0, 0), - GATE(CLK_MIXER, "mixer", "aclk166", GATE_IP_DISP1, 5, 0, 0), + GATE(CLK_MIXER, "mixer", "aclk200_disp1", GATE_IP_DISP1, 5, 0, 0), GATE(CLK_HDMI, "hdmi", "aclk200_disp1", GATE_IP_DISP1, 6, 0, 0), - GATE(CLK_SMMU_FIMD1, "smmu_fimd1", "aclk300_disp1", GATE_IP_DISP1, 8, 0, - 0), + GATE(CLK_SMMU_FIMD1M0, "smmu_fimd1m0", "dout_disp1_blk", + GATE_IP_DISP1, 7, 0, 0), + GATE(CLK_SMMU_FIMD1M1, "smmu_fimd1m1", "dout_disp1_blk", + GATE_IP_DISP1, 8, 0, 0), + GATE(CLK_SMMU_MIXER, "smmu_mixer", "aclk200_disp1", + GATE_IP_DISP1, 9, 0, 0), /* ISP */ GATE(CLK_SCLK_UART_ISP, "sclk_uart_isp", "dout_uart_isp", @@ -867,9 +892,6 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_SMMU_ROTATOR, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0), GATE(CLK_SMMU_JPEG, "smmu_jpeg", "aclk300_jpeg", GATE_IP_GEN, 7, 0, 0), GATE(CLK_SMMU_MDMA1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0), - - GATE(CLK_SMMU_MIXER, "smmu_mixer", "aclk200_disp1", GATE_IP_DISP1, 9, 0, - 0), }; static struct samsung_pll_clock exynos5420_plls[nr_plls] __initdata = { diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index bf85418a2945..2a28a86fc8e8 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -140,7 +140,8 @@ #define CLK_HDMI 413 #define CLK_ACLK300_DISP1 420 #define CLK_FIMD1 421 -#define CLK_SMMU_FIMD1 422 +#define CLK_SMMU_FIMD1M0 422 +#define CLK_SMMU_FIMD1M1 423 #define CLK_ACLK166 430 #define CLK_MIXER 431 #define CLK_ACLK266 440 -- cgit v1.2.3 From faec151b5006f832c8cefc76d01893496445a7ec Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 8 May 2014 16:57:56 +0530 Subject: clk: samsung: exynos5420: update clocks for PERIC block This patch includes, 1] renaming of the HSI2C clocks 2] renaming of spi clocks according to the datasheet 3] fixes for child-parent relationships 4] adding of more clocks related to PERIC block 5] use GATE_IP_* offsets instead of GATE_BUS_* Signed-off-by: Rahul Sharma Signed-off-by: Shaik Ameer Basha Signed-off-by: Tomasz Figa --- arch/arm/boot/dts/exynos5420.dtsi | 14 +++--- drivers/clk/samsung/clk-exynos5420.c | 92 ++++++++++++++++------------------ include/dt-bindings/clock/exynos5420.h | 14 +++--- 3 files changed, 58 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index c3a9a66c5767..67ba2c56fa8e 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -549,7 +549,7 @@ #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2c4_hs_bus>; - clocks = <&clock CLK_I2C4>; + clocks = <&clock CLK_USI0>; clock-names = "hsi2c"; status = "disabled"; }; @@ -562,7 +562,7 @@ #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2c5_hs_bus>; - clocks = <&clock CLK_I2C5>; + clocks = <&clock CLK_USI1>; clock-names = "hsi2c"; status = "disabled"; }; @@ -575,7 +575,7 @@ #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2c6_hs_bus>; - clocks = <&clock CLK_I2C6>; + clocks = <&clock CLK_USI2>; clock-names = "hsi2c"; status = "disabled"; }; @@ -588,7 +588,7 @@ #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2c7_hs_bus>; - clocks = <&clock CLK_I2C7>; + clocks = <&clock CLK_USI3>; clock-names = "hsi2c"; status = "disabled"; }; @@ -601,7 +601,7 @@ #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2c8_hs_bus>; - clocks = <&clock CLK_I2C8>; + clocks = <&clock CLK_USI4>; clock-names = "hsi2c"; status = "disabled"; }; @@ -614,7 +614,7 @@ #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2c9_hs_bus>; - clocks = <&clock CLK_I2C9>; + clocks = <&clock CLK_USI5>; clock-names = "hsi2c"; status = "disabled"; }; @@ -627,7 +627,7 @@ #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2c10_hs_bus>; - clocks = <&clock CLK_I2C10>; + clocks = <&clock CLK_USI6>; clock-names = "hsi2c"; status = "disabled"; }; diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 32d16f5cff53..f38c0efaaa73 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -95,6 +95,7 @@ #define GATE_IP_DISP1 0x10928 #define GATE_IP_G3D 0x10930 #define GATE_IP_GEN 0x10934 +#define GATE_IP_PERIC 0x10950 #define GATE_IP_MSCL 0x10970 #define GATE_TOP_SCLK_GSCL 0x10820 #define GATE_TOP_SCLK_DISP1 0x10828 @@ -183,6 +184,7 @@ static unsigned long exynos5420_clk_regs[] __initdata = { GATE_IP_DISP1, GATE_IP_G3D, GATE_IP_GEN, + GATE_IP_PERIC, GATE_IP_MSCL, GATE_TOP_SCLK_GSCL, GATE_TOP_SCLK_DISP1, @@ -258,7 +260,7 @@ PNAME(mout_group5_p) = {"mout_sclk_vpll", "mout_sclk_dpll"}; PNAME(mout_fimd1_final_p) = {"mout_fimd1", "mout_fimd1_opt"}; PNAME(mout_sw_aclk66_p) = {"dout_aclk66", "mout_sclk_spll"}; -PNAME(mout_aclk66_peric_p) = { "fin_pll", "mout_sw_aclk66" }; +PNAME(mout_user_aclk66_peric_p) = { "fin_pll", "mout_sw_aclk66" }; PNAME(mout_sw_aclk200_fsys_p) = {"dout_aclk200_fsys", "mout_sclk_spll"}; PNAME(mout_user_aclk200_fsys_p) = {"fin_pll", "mout_sw_aclk200_fsys"}; @@ -398,7 +400,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { SRC_TOP4, 0, 1), MUX(0, "mout_user_aclk333_432_isp", mout_user_aclk333_432_isp_p, SRC_TOP4, 4, 1), - MUX(0, "mout_aclk66_peric", mout_aclk66_peric_p, SRC_TOP4, 8, 1), + MUX(0, "mout_user_aclk66_peric", mout_user_aclk66_peric_p, + SRC_TOP4, 8, 1), MUX(0, "mout_user_aclk333_432_isp0", mout_user_aclk333_432_isp0_p, SRC_TOP4, 12, 1), MUX(0, "mout_user_aclk266_isp", mout_user_aclk266_isp_p, @@ -409,7 +412,8 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_user_aclk400_disp1", mout_user_aclk400_disp1_p, SRC_TOP5, 0, 1), - MUX(0, "mout_aclk66_psgen", mout_aclk66_peric_p, SRC_TOP5, 4, 1), + MUX(0, "mout_user_aclk66_psgen", mout_user_aclk66_peric_p, + SRC_TOP5, 4, 1), MUX(0, "mout_user_aclk333_g2d", mout_user_aclk333_g2d_p, SRC_TOP5, 8, 1), MUX(0, "mout_user_aclk266_g2d", mout_user_aclk266_g2d_p, @@ -590,9 +594,9 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_audio2", "mout_audio2", DIV_PERIC3, 28, 4), /* SPI Pre-Ratio */ - DIV(0, "dout_pre_spi0", "dout_spi0", DIV_PERIC4, 8, 8), - DIV(0, "dout_pre_spi1", "dout_spi1", DIV_PERIC4, 16, 8), - DIV(0, "dout_pre_spi2", "dout_spi2", DIV_PERIC4, 24, 8), + DIV(0, "dout_spi0_pre", "dout_spi0", DIV_PERIC4, 8, 8), + DIV(0, "dout_spi1_pre", "dout_spi1", DIV_PERIC4, 16, 8), + DIV(0, "dout_spi2_pre", "dout_spi2", DIV_PERIC4, 24, 8), /* GSCL Block */ DIV(0, "dout_gscl_blk_300", "mout_user_aclk300_gscl", @@ -649,10 +653,10 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE_BUS_TOP, 8, 0, 0), GATE(0, "pclk66_gpio", "mout_sw_aclk66", GATE_BUS_TOP, 9, CLK_IGNORE_UNUSED, 0), - GATE(0, "aclk66_psgen", "mout_aclk66_psgen", + GATE(0, "aclk66_psgen", "mout_user_aclk66_psgen", GATE_BUS_TOP, 10, CLK_IGNORE_UNUSED, 0), - GATE(0, "aclk66_peric", "mout_aclk66_peric", - GATE_BUS_TOP, 11, 0, 0), + GATE(CLK_ACLK66_PERIC, "aclk66_peric", "mout_user_aclk66_peric", + GATE_BUS_TOP, 11, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk266_isp", "mout_user_aclk266_isp", GATE_BUS_TOP, 13, 0, 0), GATE(0, "aclk166", "mout_user_aclk166", @@ -678,11 +682,11 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE_TOP_SCLK_PERIC, 2, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_UART3, "sclk_uart3", "dout_uart3", GATE_TOP_SCLK_PERIC, 3, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_SPI0, "sclk_spi0", "dout_pre_spi0", + GATE(CLK_SCLK_SPI0, "sclk_spi0", "dout_spi0_pre", GATE_TOP_SCLK_PERIC, 6, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_SPI1, "sclk_spi1", "dout_pre_spi1", + GATE(CLK_SCLK_SPI1, "sclk_spi1", "dout_spi1_pre", GATE_TOP_SCLK_PERIC, 7, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_SPI2, "sclk_spi2", "dout_pre_spi2", + GATE(CLK_SCLK_SPI2, "sclk_spi2", "dout_spi2_pre", GATE_TOP_SCLK_PERIC, 8, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_SPDIF, "sclk_spdif", "mout_spdif", GATE_TOP_SCLK_PERIC, 9, CLK_SET_RATE_PARENT, 0), @@ -747,43 +751,35 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_USBD300, "usbd300", "aclk200_fsys", GATE_BUS_FSYS0, 21, 0, 0), GATE(CLK_USBD301, "usbd301", "aclk200_fsys", GATE_BUS_FSYS0, 28, 0, 0), - /* UART */ - GATE(CLK_UART0, "uart0", "aclk66_peric", GATE_BUS_PERIC, 4, 0, 0), - GATE(CLK_UART1, "uart1", "aclk66_peric", GATE_BUS_PERIC, 5, 0, 0), - GATE_A(CLK_UART2, "uart2", "aclk66_peric", - GATE_BUS_PERIC, 6, CLK_IGNORE_UNUSED, 0, "uart2"), - GATE(CLK_UART3, "uart3", "aclk66_peric", GATE_BUS_PERIC, 7, 0, 0), - /* I2C */ - GATE(CLK_I2C0, "i2c0", "aclk66_peric", GATE_BUS_PERIC, 9, 0, 0), - GATE(CLK_I2C1, "i2c1", "aclk66_peric", GATE_BUS_PERIC, 10, 0, 0), - GATE(CLK_I2C2, "i2c2", "aclk66_peric", GATE_BUS_PERIC, 11, 0, 0), - GATE(CLK_I2C3, "i2c3", "aclk66_peric", GATE_BUS_PERIC, 12, 0, 0), - GATE(CLK_I2C4, "i2c4", "aclk66_peric", GATE_BUS_PERIC, 13, 0, 0), - GATE(CLK_I2C5, "i2c5", "aclk66_peric", GATE_BUS_PERIC, 14, 0, 0), - GATE(CLK_I2C6, "i2c6", "aclk66_peric", GATE_BUS_PERIC, 15, 0, 0), - GATE(CLK_I2C7, "i2c7", "aclk66_peric", GATE_BUS_PERIC, 16, 0, 0), - GATE(CLK_I2C_HDMI, "i2c_hdmi", "aclk66_peric", GATE_BUS_PERIC, 17, 0, - 0), - GATE(CLK_TSADC, "tsadc", "aclk66_peric", GATE_BUS_PERIC, 18, 0, 0), - /* SPI */ - GATE(CLK_SPI0, "spi0", "aclk66_peric", GATE_BUS_PERIC, 19, 0, 0), - GATE(CLK_SPI1, "spi1", "aclk66_peric", GATE_BUS_PERIC, 20, 0, 0), - GATE(CLK_SPI2, "spi2", "aclk66_peric", GATE_BUS_PERIC, 21, 0, 0), + /* PERIC Block */ + GATE(CLK_UART0, "uart0", "aclk66_peric", GATE_IP_PERIC, 0, 0, 0), + GATE(CLK_UART1, "uart1", "aclk66_peric", GATE_IP_PERIC, 1, 0, 0), + GATE(CLK_UART2, "uart2", "aclk66_peric", GATE_IP_PERIC, 2, 0, 0), + GATE(CLK_UART3, "uart3", "aclk66_peric", GATE_IP_PERIC, 3, 0, 0), + GATE(CLK_I2C0, "i2c0", "aclk66_peric", GATE_IP_PERIC, 6, 0, 0), + GATE(CLK_I2C1, "i2c1", "aclk66_peric", GATE_IP_PERIC, 7, 0, 0), + GATE(CLK_I2C2, "i2c2", "aclk66_peric", GATE_IP_PERIC, 8, 0, 0), + GATE(CLK_I2C3, "i2c3", "aclk66_peric", GATE_IP_PERIC, 9, 0, 0), + GATE(CLK_USI0, "usi0", "aclk66_peric", GATE_IP_PERIC, 10, 0, 0), + GATE(CLK_USI1, "usi1", "aclk66_peric", GATE_IP_PERIC, 11, 0, 0), + GATE(CLK_USI2, "usi2", "aclk66_peric", GATE_IP_PERIC, 12, 0, 0), + GATE(CLK_USI3, "usi3", "aclk66_peric", GATE_IP_PERIC, 13, 0, 0), + GATE(CLK_I2C_HDMI, "i2c_hdmi", "aclk66_peric", GATE_IP_PERIC, 14, 0, 0), + GATE(CLK_TSADC, "tsadc", "aclk66_peric", GATE_IP_PERIC, 15, 0, 0), + GATE(CLK_SPI0, "spi0", "aclk66_peric", GATE_IP_PERIC, 16, 0, 0), + GATE(CLK_SPI1, "spi1", "aclk66_peric", GATE_IP_PERIC, 17, 0, 0), + GATE(CLK_SPI2, "spi2", "aclk66_peric", GATE_IP_PERIC, 18, 0, 0), + GATE(CLK_I2S1, "i2s1", "aclk66_peric", GATE_IP_PERIC, 20, 0, 0), + GATE(CLK_I2S2, "i2s2", "aclk66_peric", GATE_IP_PERIC, 21, 0, 0), + GATE(CLK_PCM1, "pcm1", "aclk66_peric", GATE_IP_PERIC, 22, 0, 0), + GATE(CLK_PCM2, "pcm2", "aclk66_peric", GATE_IP_PERIC, 23, 0, 0), + GATE(CLK_PWM, "pwm", "aclk66_peric", GATE_IP_PERIC, 24, 0, 0), + GATE(CLK_SPDIF, "spdif", "aclk66_peric", GATE_IP_PERIC, 26, 0, 0), + GATE(CLK_USI4, "usi4", "aclk66_peric", GATE_IP_PERIC, 28, 0, 0), + GATE(CLK_USI5, "usi5", "aclk66_peric", GATE_IP_PERIC, 30, 0, 0), + GATE(CLK_USI6, "usi6", "aclk66_peric", GATE_IP_PERIC, 31, 0, 0), + GATE(CLK_KEYIF, "keyif", "aclk66_peric", GATE_BUS_PERIC, 22, 0, 0), - /* I2S */ - GATE(CLK_I2S1, "i2s1", "aclk66_peric", GATE_BUS_PERIC, 23, 0, 0), - GATE(CLK_I2S2, "i2s2", "aclk66_peric", GATE_BUS_PERIC, 24, 0, 0), - /* PCM */ - GATE(CLK_PCM1, "pcm1", "aclk66_peric", GATE_BUS_PERIC, 25, 0, 0), - GATE(CLK_PCM2, "pcm2", "aclk66_peric", GATE_BUS_PERIC, 26, 0, 0), - /* PWM */ - GATE(CLK_PWM, "pwm", "aclk66_peric", GATE_BUS_PERIC, 27, 0, 0), - /* SPDIF */ - GATE(CLK_SPDIF, "spdif", "aclk66_peric", GATE_BUS_PERIC, 29, 0, 0), - - GATE(CLK_I2C8, "i2c8", "aclk66_peric", GATE_BUS_PERIC1, 0, 0, 0), - GATE(CLK_I2C9, "i2c9", "aclk66_peric", GATE_BUS_PERIC1, 1, 0, 0), - GATE(CLK_I2C10, "i2c10", "aclk66_peric", GATE_BUS_PERIC1, 2, 0, 0), GATE(CLK_CHIPID, "chipid", "aclk66_psgen", GATE_BUS_PERIS0, 12, CLK_IGNORE_UNUSED, 0), diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 2a28a86fc8e8..e688b64564b2 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -69,10 +69,10 @@ #define CLK_I2C1 262 #define CLK_I2C2 263 #define CLK_I2C3 264 -#define CLK_I2C4 265 -#define CLK_I2C5 266 -#define CLK_I2C6 267 -#define CLK_I2C7 268 +#define CLK_USI0 265 +#define CLK_USI1 266 +#define CLK_USI2 267 +#define CLK_USI3 268 #define CLK_I2C_HDMI 269 #define CLK_TSADC 270 #define CLK_SPI0 271 @@ -85,9 +85,9 @@ #define CLK_PCM2 278 #define CLK_PWM 279 #define CLK_SPDIF 280 -#define CLK_I2C8 281 -#define CLK_I2C9 282 -#define CLK_I2C10 283 +#define CLK_USI4 281 +#define CLK_USI5 282 +#define CLK_USI6 283 #define CLK_ACLK66_PSGEN 300 #define CLK_CHIPID 301 #define CLK_SYSREG 302 -- cgit v1.2.3 From 0a22c3065333d3138475ff1d25851633e8dae722 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 8 May 2014 16:57:57 +0530 Subject: clk: samsung: exynos5420: update clocks for PERIS and GEN blocks This patch fixes some parent-child relationships according to the latest datasheet and adds more clocks related to PERIS and GEN blocks. Signed-off-by: Rahul Sharma Signed-off-by: Shaik Ameer Basha Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 76 ++++++++++++++++++++-------------- include/dt-bindings/clock/exynos5420.h | 3 ++ 2 files changed, 48 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index f38c0efaaa73..41af467719dc 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -83,6 +83,7 @@ #define SCLK_DIV_ISP1 0x10584 #define DIV2_RATIO0 0x10590 #define GATE_BUS_TOP 0x10700 +#define GATE_BUS_GEN 0x1073c #define GATE_BUS_FSYS0 0x10740 #define GATE_BUS_PERIC 0x10750 #define GATE_BUS_PERIC1 0x10754 @@ -96,6 +97,7 @@ #define GATE_IP_G3D 0x10930 #define GATE_IP_GEN 0x10934 #define GATE_IP_PERIC 0x10950 +#define GATE_IP_PERIS 0x10960 #define GATE_IP_MSCL 0x10970 #define GATE_TOP_SCLK_GSCL 0x10820 #define GATE_TOP_SCLK_DISP1 0x10828 @@ -172,6 +174,7 @@ static unsigned long exynos5420_clk_regs[] __initdata = { SCLK_DIV_ISP1, DIV2_RATIO0, GATE_BUS_TOP, + GATE_BUS_GEN, GATE_BUS_FSYS0, GATE_BUS_PERIC, GATE_BUS_PERIC1, @@ -185,6 +188,7 @@ static unsigned long exynos5420_clk_regs[] __initdata = { GATE_IP_G3D, GATE_IP_GEN, GATE_IP_PERIC, + GATE_IP_PERIS, GATE_IP_MSCL, GATE_TOP_SCLK_GSCL, GATE_TOP_SCLK_DISP1, @@ -606,6 +610,10 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { /* MSCL Block */ DIV(0, "dout_mscl_blk", "aclk400_mscl", DIV2_RATIO0, 28, 2), + /* PSGEN */ + DIV(0, "dout_gen_blk", "mout_user_aclk266", DIV2_RATIO0, 8, 1), + DIV(0, "dout_jpg_blk", "aclk166", DIV2_RATIO0, 20, 1), + /* ISP Block */ DIV(0, "dout_isp_sensor0", "mout_isp_sensor", SCLK_DIV_ISP0, 8, 8), DIV(0, "dout_isp_sensor1", "mout_isp_sensor", SCLK_DIV_ISP0, 16, 8), @@ -628,10 +636,6 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_SMMU_MDMA0, "smmu_mdma0", "aclk266_g2d", GATE_IP_G2D, 5, 0, 0), GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk333_g2d", GATE_IP_G2D, 7, 0, 0), - /* TODO: Re-verify the CG bits for all the gate clocks */ - GATE_A(CLK_MCT, "pclk_st", "aclk66_psgen", GATE_BUS_PERIS1, 2, 0, 0, - "mct"), - GATE(0, "aclk200_fsys", "mout_user_aclk200_fsys", GATE_BUS_FSYS0, 9, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk200_fsys2", "mout_user_aclk200_fsys2", @@ -781,28 +785,46 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_KEYIF, "keyif", "aclk66_peric", GATE_BUS_PERIC, 22, 0, 0), + /* PERIS Block */ GATE(CLK_CHIPID, "chipid", "aclk66_psgen", - GATE_BUS_PERIS0, 12, CLK_IGNORE_UNUSED, 0), + GATE_IP_PERIS, 0, CLK_IGNORE_UNUSED, 0), GATE(CLK_SYSREG, "sysreg", "aclk66_psgen", - GATE_BUS_PERIS0, 13, CLK_IGNORE_UNUSED, 0), - GATE(CLK_TZPC0, "tzpc0", "aclk66_psgen", GATE_BUS_PERIS0, 18, 0, 0), - GATE(CLK_TZPC1, "tzpc1", "aclk66_psgen", GATE_BUS_PERIS0, 19, 0, 0), - GATE(CLK_TZPC2, "tzpc2", "aclk66_psgen", GATE_BUS_PERIS0, 20, 0, 0), - GATE(CLK_TZPC3, "tzpc3", "aclk66_psgen", GATE_BUS_PERIS0, 21, 0, 0), - GATE(CLK_TZPC4, "tzpc4", "aclk66_psgen", GATE_BUS_PERIS0, 22, 0, 0), - GATE(CLK_TZPC5, "tzpc5", "aclk66_psgen", GATE_BUS_PERIS0, 23, 0, 0), - GATE(CLK_TZPC6, "tzpc6", "aclk66_psgen", GATE_BUS_PERIS0, 24, 0, 0), - GATE(CLK_TZPC7, "tzpc7", "aclk66_psgen", GATE_BUS_PERIS0, 25, 0, 0), - GATE(CLK_TZPC8, "tzpc8", "aclk66_psgen", GATE_BUS_PERIS0, 26, 0, 0), - GATE(CLK_TZPC9, "tzpc9", "aclk66_psgen", GATE_BUS_PERIS0, 27, 0, 0), - - GATE(CLK_HDMI_CEC, "hdmi_cec", "aclk66_psgen", GATE_BUS_PERIS1, 0, 0, - 0), + GATE_IP_PERIS, 1, CLK_IGNORE_UNUSED, 0), + GATE(CLK_TZPC0, "tzpc0", "aclk66_psgen", GATE_IP_PERIS, 6, 0, 0), + GATE(CLK_TZPC1, "tzpc1", "aclk66_psgen", GATE_IP_PERIS, 7, 0, 0), + GATE(CLK_TZPC2, "tzpc2", "aclk66_psgen", GATE_IP_PERIS, 8, 0, 0), + GATE(CLK_TZPC3, "tzpc3", "aclk66_psgen", GATE_IP_PERIS, 9, 0, 0), + GATE(CLK_TZPC4, "tzpc4", "aclk66_psgen", GATE_IP_PERIS, 10, 0, 0), + GATE(CLK_TZPC5, "tzpc5", "aclk66_psgen", GATE_IP_PERIS, 11, 0, 0), + GATE(CLK_TZPC6, "tzpc6", "aclk66_psgen", GATE_IP_PERIS, 12, 0, 0), + GATE(CLK_TZPC7, "tzpc7", "aclk66_psgen", GATE_IP_PERIS, 13, 0, 0), + GATE(CLK_TZPC8, "tzpc8", "aclk66_psgen", GATE_IP_PERIS, 14, 0, 0), + GATE(CLK_TZPC9, "tzpc9", "aclk66_psgen", GATE_IP_PERIS, 15, 0, 0), + GATE(CLK_HDMI_CEC, "hdmi_cec", "aclk66_psgen", GATE_IP_PERIS, 16, 0, 0), + GATE(CLK_MCT, "mct", "aclk66_psgen", GATE_IP_PERIS, 18, 0, 0), + GATE(CLK_WDT, "wdt", "aclk66_psgen", GATE_IP_PERIS, 19, 0, 0), + GATE(CLK_RTC, "rtc", "aclk66_psgen", GATE_IP_PERIS, 20, 0, 0), + GATE(CLK_TMU, "tmu", "aclk66_psgen", GATE_IP_PERIS, 21, 0, 0), + GATE(CLK_TMU_GPU, "tmu_gpu", "aclk66_psgen", GATE_IP_PERIS, 22, 0, 0), + GATE(CLK_SECKEY, "seckey", "aclk66_psgen", GATE_BUS_PERIS1, 1, 0, 0), - GATE(CLK_WDT, "wdt", "aclk66_psgen", GATE_BUS_PERIS1, 3, 0, 0), - GATE(CLK_RTC, "rtc", "aclk66_psgen", GATE_BUS_PERIS1, 4, 0, 0), - GATE(CLK_TMU, "tmu", "aclk66_psgen", GATE_BUS_PERIS1, 5, 0, 0), - GATE(CLK_TMU_GPU, "tmu_gpu", "aclk66_psgen", GATE_BUS_PERIS1, 6, 0, 0), + + /* GEN Block */ + GATE(CLK_ROTATOR, "rotator", "mout_user_aclk266", GATE_IP_GEN, 1, 0, 0), + GATE(CLK_JPEG, "jpeg", "aclk300_jpeg", GATE_IP_GEN, 2, 0, 0), + GATE(CLK_JPEG2, "jpeg2", "aclk300_jpeg", GATE_IP_GEN, 3, 0, 0), + GATE(CLK_MDMA1, "mdma1", "mout_user_aclk266", GATE_IP_GEN, 4, 0, 0), + GATE(CLK_TOP_RTC, "top_rtc", "aclk66_psgen", GATE_IP_GEN, 5, 0, 0), + GATE(CLK_SMMU_ROTATOR, "smmu_rotator", "dout_gen_blk", + GATE_IP_GEN, 6, 0, 0), + GATE(CLK_SMMU_JPEG, "smmu_jpeg", "dout_jpg_blk", GATE_IP_GEN, 7, 0, 0), + GATE(CLK_SMMU_MDMA1, "smmu_mdma1", "dout_gen_blk", + GATE_IP_GEN, 9, 0, 0), + + /* GATE_IP_GEN doesn't list gates for smmu_jpeg2 and mc */ + GATE(CLK_SMMU_JPEG2, "smmu_jpeg2", "dout_jpg_blk", + GATE_BUS_GEN, 28, 0, 0), + GATE(CLK_MC, "mc", "aclk66_psgen", GATE_BUS_GEN, 12, 0, 0), /* GSCL Block */ GATE(CLK_SCLK_GSCL_WA, "sclk_gscl_wa", "mout_user_aclk333_432_gscl", @@ -880,14 +902,6 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_SMMU_MFCR, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0), GATE(CLK_G3D, "g3d", "mout_user_aclk_g3d", GATE_IP_G3D, 9, 0, 0), - - GATE(CLK_ROTATOR, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0), - GATE(CLK_JPEG, "jpeg", "aclk300_jpeg", GATE_IP_GEN, 2, 0, 0), - GATE(CLK_JPEG2, "jpeg2", "aclk300_jpeg", GATE_IP_GEN, 3, 0, 0), - GATE(CLK_MDMA1, "mdma1", "aclk266", GATE_IP_GEN, 4, 0, 0), - GATE(CLK_SMMU_ROTATOR, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0), - GATE(CLK_SMMU_JPEG, "smmu_jpeg", "aclk300_jpeg", GATE_IP_GEN, 7, 0, 0), - GATE(CLK_SMMU_MDMA1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0), }; static struct samsung_pll_clock exynos5420_plls[nr_plls] __initdata = { diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index e688b64564b2..16262da05cf2 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -153,6 +153,7 @@ #define CLK_JPEG 451 #define CLK_JPEG2 452 #define CLK_SMMU_JPEG 453 +#define CLK_SMMU_JPEG2 454 #define CLK_ACLK300_GSCL 460 #define CLK_SMMU_GSCL0 461 #define CLK_SMMU_GSCL1 462 @@ -180,6 +181,8 @@ #define CLK_SMMU_MIXER 502 #define CLK_SMMU_G2D 503 #define CLK_SMMU_MDMA0 504 +#define CLK_MC 505 +#define CLK_TOP_RTC 506 #define CLK_SCLK_UART_ISP 510 #define CLK_SCLK_SPI0_ISP 511 #define CLK_SCLK_SPI1_ISP 512 -- cgit v1.2.3 From 31116a642b148587b92928d37212a547d0ce10b5 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 8 May 2014 16:58:02 +0530 Subject: clk: samsung: exynos5420: update clocks for MAU Block This patch adds the missing MAU block specific clocks. Signed-off-by: Rahul Sharma Signed-off-by: Shaik Ameer Basha Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 12 +++++++++++- include/dt-bindings/clock/exynos5420.h | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 2171366237af..9ff36140bcdf 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -62,7 +62,9 @@ #define SRC_TOP11 0x10284 #define SRC_TOP12 0x10288 #define SRC_MASK_TOP2 0x10308 +#define SRC_MASK_TOP7 0x1031c #define SRC_MASK_DISP10 0x1032c +#define SRC_MASK_MAU 0x10334 #define SRC_MASK_FSYS 0x10340 #define SRC_MASK_PERIC0 0x10350 #define SRC_MASK_PERIC1 0x10354 @@ -155,6 +157,7 @@ static unsigned long exynos5420_clk_regs[] __initdata = { SRC_TOP11, SRC_TOP12, SRC_MASK_TOP2, + SRC_MASK_TOP7, SRC_MASK_DISP10, SRC_MASK_FSYS, SRC_MASK_PERIC0, @@ -351,6 +354,8 @@ PNAME(mout_hdmi_p) = {"dout_hdmi_pixel", "sclk_hdmiphy"}; PNAME(mout_maudio0_p) = {"fin_pll", "maudio_clk", "mout_sclk_dpll", "mout_sclk_mpll", "mout_sclk_spll", "mout_sclk_ipll", "mout_sclk_epll", "mout_sclk_rpll"}; +PNAME(mout_mau_epll_clk_p) = {"mout_sclk_epll", "mout_sclk_dpll", + "mout_sclk_mpll", "mout_sclk_spll"}; /* fixed rate clocks generated outside the soc */ static struct samsung_fixed_rate_clock exynos5420_fixed_rate_ext_clks[] __initdata = { @@ -373,6 +378,8 @@ static struct samsung_fixed_factor_clock exynos5420_fixed_factor_clks[] __initda static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_mspll_kfc", mout_mspll_cpu_p, SRC_TOP7, 8, 2), MUX(0, "mout_mspll_cpu", mout_mspll_cpu_p, SRC_TOP7, 12, 2), + MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2), + MUX(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), MUX(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1), MUX(0, "mout_kpll", mout_kpll_p, SRC_KFC, 0, 1), @@ -518,7 +525,7 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_fimd1_final", mout_fimd1_final_p, TOP_SPARE2, 8, 1), /* MAU Block */ - MUX(0, "mout_maudio0", mout_maudio0_p, SRC_MAU, 28, 3), + MUX(CLK_MOUT_MAUDIO0, "mout_maudio0", mout_maudio0_p, SRC_MAU, 28, 3), /* FSYS Block */ MUX(0, "mout_usbd301", mout_group2_p, SRC_FSYS, 4, 3), @@ -718,6 +725,9 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(0, "aclk300_disp1", "mout_user_aclk300_disp1", SRC_MASK_TOP2, 24, 0, 0), + GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk", + SRC_MASK_TOP7, 20, 0, 0), + /* sclk */ GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_uart0", GATE_TOP_SCLK_PERIC, 0, CLK_SET_RATE_PARENT, 0), diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 16262da05cf2..128fb97d175c 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -58,6 +58,7 @@ #define CLK_SCLK_GSCL_WA 156 #define CLK_SCLK_GSCL_WB 157 #define CLK_SCLK_HDMIPHY 158 +#define CLK_MAU_EPLL 159 /* gate clocks */ #define CLK_ACLK66_PERIC 256 @@ -195,6 +196,7 @@ #define CLK_MOUT_HDMI 640 #define CLK_MOUT_G3D 641 #define CLK_MOUT_VPLL 642 +#define CLK_MOUT_MAUDIO0 643 /* divider clocks */ #define CLK_DOUT_PIXEL 768 -- cgit v1.2.3 From b31ca2a0176ee1d7f011f4cf0f6b33e1163e254b Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 8 May 2014 16:58:03 +0530 Subject: clk: samsung: exynos5420: add misc clocks This patch adds some missing miscellaneous clocks specific to exynos5420. Signed-off-by: Rahul Sharma Signed-off-by: Shaik Ameer Basha Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 14 +++++++++++--- include/dt-bindings/clock/exynos5420.h | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 9ff36140bcdf..4bc94f1c53d1 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -273,7 +273,8 @@ PNAME(mout_group5_p) = {"mout_sclk_vpll", "mout_sclk_dpll"}; PNAME(mout_fimd1_final_p) = {"mout_fimd1", "mout_fimd1_opt"}; PNAME(mout_sw_aclk66_p) = {"dout_aclk66", "mout_sclk_spll"}; -PNAME(mout_user_aclk66_peric_p) = { "fin_pll", "mout_sw_aclk66" }; +PNAME(mout_user_aclk66_peric_p) = { "fin_pll", "mout_sw_aclk66"}; +PNAME(mout_user_pclk66_gpio_p) = {"mout_sw_aclk66", "ff_sw_aclk66"}; PNAME(mout_sw_aclk200_fsys_p) = {"dout_aclk200_fsys", "mout_sclk_spll"}; PNAME(mout_sw_pclk200_fsys_p) = {"dout_pclk200_fsys", "mout_sclk_spll"}; @@ -372,10 +373,13 @@ static struct samsung_fixed_rate_clock exynos5420_fixed_rate_clks[] __initdata = }; static struct samsung_fixed_factor_clock exynos5420_fixed_factor_clks[] __initdata = { - FFACTOR(0, "sclk_hsic_12m", "fin_pll", 1, 2, 0), + FFACTOR(0, "ff_hsic_12m", "fin_pll", 1, 2, 0), + FFACTOR(0, "ff_sw_aclk66", "mout_sw_aclk66", 1, 2, 0), }; static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { + MUX(0, "mout_user_pclk66_gpio", mout_user_pclk66_gpio_p, + SRC_TOP7, 4, 1), MUX(0, "mout_mspll_kfc", mout_mspll_cpu_p, SRC_TOP7, 8, 2), MUX(0, "mout_mspll_cpu", mout_mspll_cpu_p, SRC_TOP7, 12, 2), MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2), @@ -703,7 +707,7 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE_BUS_TOP, 7, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk333_432_isp", "mout_user_aclk333_432_isp", GATE_BUS_TOP, 8, 0, 0), - GATE(0, "pclk66_gpio", "mout_sw_aclk66", + GATE(CLK_PCLK66_GPIO, "pclk66_gpio", "mout_user_pclk66_gpio", GATE_BUS_TOP, 9, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk66_psgen", "mout_user_aclk66_psgen", GATE_BUS_TOP, 10, CLK_IGNORE_UNUSED, 0), @@ -721,6 +725,10 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE_BUS_TOP, 17, 0, 0), GATE(0, "aclk200_disp1", "mout_user_aclk200_disp1", GATE_BUS_TOP, 18, 0, 0), + GATE(CLK_SCLK_MPHY_IXTAL24, "sclk_mphy_ixtal24", "mphy_refclk_ixtal24", + GATE_BUS_TOP, 28, 0, 0), + GATE(CLK_SCLK_HSIC_12M, "sclk_hsic_12m", "ff_hsic_12m", + GATE_BUS_TOP, 29, 0, 0), GATE(0, "aclk300_disp1", "mout_user_aclk300_disp1", SRC_MASK_TOP2, 24, 0, 0), diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 128fb97d175c..7dd1cc3b5c57 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -59,6 +59,8 @@ #define CLK_SCLK_GSCL_WB 157 #define CLK_SCLK_HDMIPHY 158 #define CLK_MAU_EPLL 159 +#define CLK_SCLK_HSIC_12M 160 +#define CLK_SCLK_MPHY_IXTAL24 161 /* gate clocks */ #define CLK_ACLK66_PERIC 256 -- cgit v1.2.3 From 2ce16c53422a17c0afea8203dd96ac1e7a142cf9 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 13 May 2014 22:05:06 +0900 Subject: clk: samsung: exynos3250: Add clocks using common clock framework This patch add new the clock drvier of Exynos3250 SoC based on Cortex-A7 using common clock framework. The CMU (Clock Management Unit) of Exynos3250 control PLLs(Phase Locked Loops) and generate system clocks for CPU, buses, and function clocks for individual IPs. The CMU of Exynos3250 includes following clock doamins: - CPU block for Cortex-A7 MPCore processor - LEFTBUS/RIGHTBUS block - TOP block for G3D/MFC/LCD0/ISP/CAM/FSYS/MFC/PERIL/PERIR Signed-off-by: Tomasz Figa Signed-off-by: Chanwoo Choi Signed-off-by: Hyunhee Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Inki Dae Signed-off-by: Seung-Woo Kim Signed-off-by: Jaehoon Chung Signed-off-by: Karol Wrona Signed-off-by: YoungJun Cho Signed-off-by: Kyungmin Park Cc: Mike Turquette Cc: Kukjin Kim Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala --- drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-exynos3250.c | 780 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos3250.h | 258 +++++++++++ 3 files changed, 1039 insertions(+) create mode 100644 drivers/clk/samsung/clk-exynos3250.c create mode 100644 include/dt-bindings/clock/exynos3250.h (limited to 'include') diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 916049472ba3..25646c61a02b 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o +obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5260) += clk-exynos5260.o diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c new file mode 100644 index 000000000000..7a17bd40d1dd --- /dev/null +++ b/drivers/clk/samsung/clk-exynos3250.c @@ -0,0 +1,780 @@ +/* + * 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 version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for Exynos3250 SoC. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +#define SRC_LEFTBUS 0x4200 +#define DIV_LEFTBUS 0x4500 +#define GATE_IP_LEFTBUS 0x4800 +#define SRC_RIGHTBUS 0x8200 +#define DIV_RIGHTBUS 0x8500 +#define GATE_IP_RIGHTBUS 0x8800 +#define GATE_IP_PERIR 0x8960 +#define MPLL_LOCK 0xc010 +#define MPLL_CON0 0xc110 +#define VPLL_LOCK 0xc020 +#define VPLL_CON0 0xc120 +#define UPLL_LOCK 0xc030 +#define UPLL_CON0 0xc130 +#define SRC_TOP0 0xc210 +#define SRC_TOP1 0xc214 +#define SRC_CAM 0xc220 +#define SRC_MFC 0xc228 +#define SRC_G3D 0xc22c +#define SRC_LCD 0xc234 +#define SRC_ISP 0xc238 +#define SRC_FSYS 0xc240 +#define SRC_PERIL0 0xc250 +#define SRC_PERIL1 0xc254 +#define SRC_MASK_TOP 0xc310 +#define SRC_MASK_CAM 0xc320 +#define SRC_MASK_LCD 0xc334 +#define SRC_MASK_ISP 0xc338 +#define SRC_MASK_FSYS 0xc340 +#define SRC_MASK_PERIL0 0xc350 +#define SRC_MASK_PERIL1 0xc354 +#define DIV_TOP 0xc510 +#define DIV_CAM 0xc520 +#define DIV_MFC 0xc528 +#define DIV_G3D 0xc52c +#define DIV_LCD 0xc534 +#define DIV_ISP 0xc538 +#define DIV_FSYS0 0xc540 +#define DIV_FSYS1 0xc544 +#define DIV_FSYS2 0xc548 +#define DIV_PERIL0 0xc550 +#define DIV_PERIL1 0xc554 +#define DIV_PERIL3 0xc55c +#define DIV_PERIL4 0xc560 +#define DIV_PERIL5 0xc564 +#define DIV_CAM1 0xc568 +#define CLKDIV2_RATIO 0xc580 +#define GATE_SCLK_CAM 0xc820 +#define GATE_SCLK_MFC 0xc828 +#define GATE_SCLK_G3D 0xc82c +#define GATE_SCLK_LCD 0xc834 +#define GATE_SCLK_ISP_TOP 0xc838 +#define GATE_SCLK_FSYS 0xc840 +#define GATE_SCLK_PERIL 0xc850 +#define GATE_IP_CAM 0xc920 +#define GATE_IP_MFC 0xc928 +#define GATE_IP_G3D 0xc92c +#define GATE_IP_LCD 0xc934 +#define GATE_IP_ISP 0xc938 +#define GATE_IP_FSYS 0xc940 +#define GATE_IP_PERIL 0xc950 +#define GATE_BLOCK 0xc970 +#define APLL_LOCK 0x14000 +#define APLL_CON0 0x14100 +#define SRC_CPU 0x14200 +#define DIV_CPU0 0x14500 +#define DIV_CPU1 0x14504 + +/* list of PLLs to be registered */ +enum exynos3250_plls { + apll, mpll, vpll, upll, + nr_plls +}; + +static void __iomem *reg_base; + +/* + * Support for CMU save/restore across system suspends + */ +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *exynos3250_clk_regs; + +static unsigned long exynos3250_cmu_clk_regs[] __initdata = { + SRC_LEFTBUS, + DIV_LEFTBUS, + GATE_IP_LEFTBUS, + SRC_RIGHTBUS, + DIV_RIGHTBUS, + GATE_IP_RIGHTBUS, + GATE_IP_PERIR, + MPLL_LOCK, + MPLL_CON0, + VPLL_LOCK, + VPLL_CON0, + UPLL_LOCK, + UPLL_CON0, + SRC_TOP0, + SRC_TOP1, + SRC_CAM, + SRC_MFC, + SRC_G3D, + SRC_LCD, + SRC_ISP, + SRC_FSYS, + SRC_PERIL0, + SRC_PERIL1, + SRC_MASK_TOP, + SRC_MASK_CAM, + SRC_MASK_LCD, + SRC_MASK_ISP, + SRC_MASK_FSYS, + SRC_MASK_PERIL0, + SRC_MASK_PERIL1, + DIV_TOP, + DIV_CAM, + DIV_MFC, + DIV_G3D, + DIV_LCD, + DIV_ISP, + DIV_FSYS0, + DIV_FSYS1, + DIV_FSYS2, + DIV_PERIL0, + DIV_PERIL1, + DIV_PERIL3, + DIV_PERIL4, + DIV_PERIL5, + DIV_CAM1, + CLKDIV2_RATIO, + GATE_SCLK_CAM, + GATE_SCLK_MFC, + GATE_SCLK_G3D, + GATE_SCLK_LCD, + GATE_SCLK_ISP_TOP, + GATE_SCLK_FSYS, + GATE_SCLK_PERIL, + GATE_IP_CAM, + GATE_IP_MFC, + GATE_IP_G3D, + GATE_IP_LCD, + GATE_IP_ISP, + GATE_IP_FSYS, + GATE_IP_PERIL, + GATE_BLOCK, + APLL_LOCK, + SRC_CPU, + DIV_CPU0, + DIV_CPU1, +}; + +static int exynos3250_clk_suspend(void) +{ + samsung_clk_save(reg_base, exynos3250_clk_regs, + ARRAY_SIZE(exynos3250_cmu_clk_regs)); + return 0; +} + +static void exynos3250_clk_resume(void) +{ + samsung_clk_restore(reg_base, exynos3250_clk_regs, + ARRAY_SIZE(exynos3250_cmu_clk_regs)); +} + +static struct syscore_ops exynos3250_clk_syscore_ops = { + .suspend = exynos3250_clk_suspend, + .resume = exynos3250_clk_resume, +}; + +static void exynos3250_clk_sleep_init(void) +{ + exynos3250_clk_regs = + samsung_clk_alloc_reg_dump(exynos3250_cmu_clk_regs, + ARRAY_SIZE(exynos3250_cmu_clk_regs)); + if (!exynos3250_clk_regs) { + pr_warn("%s: Failed to allocate sleep save data\n", __func__); + goto err; + } + + register_syscore_ops(&exynos3250_clk_syscore_ops); + return; +err: + kfree(exynos3250_clk_regs); +} +#else +static inline void exynos3250_clk_sleep_init(void) { } +#endif + +/* list of all parent clock list */ +PNAME(mout_vpllsrc_p) = { "fin_pll", }; + +PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; +PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", }; +PNAME(mout_vpll_p) = { "fin_pll", "fout_vpll", }; +PNAME(mout_upll_p) = { "fin_pll", "fout_upll", }; + +PNAME(mout_mpll_user_p) = { "fin_pll", "div_mpll_pre", }; +PNAME(mout_epll_user_p) = { "fin_pll", "mout_epll", }; +PNAME(mout_core_p) = { "mout_apll", "mout_mpll_user_c", }; +PNAME(mout_hpm_p) = { "mout_apll", "mout_mpll_user_c", }; + +PNAME(mout_ebi_p) = { "div_aclk_200", "div_aclk_160", }; +PNAME(mout_ebi_1_p) = { "mout_ebi", "mout_vpll", }; + +PNAME(mout_gdl_p) = { "mout_mpll_user_l", }; +PNAME(mout_gdr_p) = { "mout_mpll_user_r", }; + +PNAME(mout_aclk_400_mcuisp_sub_p) + = { "fin_pll", "div_aclk_400_mcuisp", }; +PNAME(mout_aclk_266_0_p) = { "div_mpll_pre", "mout_vpll", }; +PNAME(mout_aclk_266_1_p) = { "mout_epll_user", }; +PNAME(mout_aclk_266_p) = { "mout_aclk_266_0", "mout_aclk_266_1", }; +PNAME(mout_aclk_266_sub_p) = { "fin_pll", "div_aclk_266", }; + +PNAME(group_div_mpll_pre_p) = { "div_mpll_pre", }; +PNAME(group_epll_vpll_p) = { "mout_epll_user", "mout_vpll" }; +PNAME(group_sclk_p) = { "xxti", "xusbxti", + "none", "none", + "none", "none", "div_mpll_pre", + "mout_epll_user", "mout_vpll", }; +PNAME(group_sclk_audio_p) = { "audiocdclk", "none", + "none", "none", + "xxti", "xusbxti", + "div_mpll_pre", "mout_epll_user", + "mout_vpll", }; +PNAME(group_sclk_cam_blk_p) = { "xxti", "xusbxti", + "none", "none", "none", + "none", "div_mpll_pre", + "mout_epll_user", "mout_vpll", + "div_cam_blk_320", }; +PNAME(group_sclk_fimd0_p) = { "xxti", "xusbxti", + "m_bitclkhsdiv4_2l", "none", + "none", "none", "div_mpll_pre", + "mout_epll_user", "mout_vpll", + "none", "none", "none", + "div_lcd_blk_145", }; + +PNAME(mout_mfc_p) = { "mout_mfc_0", "mout_mfc_1" }; +PNAME(mout_g3d_p) = { "mout_g3d_0", "mout_g3d_1" }; + +static struct samsung_fixed_factor_clock fixed_factor_clks[] __initdata = { + FFACTOR(0, "sclk_mpll_1600", "mout_mpll", 1, 1, 0), + FFACTOR(0, "sclk_mpll_mif", "mout_mpll", 1, 2, 0), + FFACTOR(0, "sclk_bpll", "fout_bpll", 1, 2, 0), + FFACTOR(0, "div_cam_blk_320", "sclk_mpll_1600", 1, 5, 0), + FFACTOR(0, "div_lcd_blk_145", "sclk_mpll_1600", 1, 11, 0), + + /* HACK: fin_pll hardcoded to xusbxti until detection is implemented. */ + FFACTOR(CLK_FIN_PLL, "fin_pll", "xusbxti", 1, 1, 0), +}; + +static struct samsung_mux_clock mux_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + + /* SRC_LEFTBUS */ + MUX(CLK_MOUT_MPLL_USER_L, "mout_mpll_user_l", mout_mpll_user_p, + SRC_LEFTBUS, 4, 1), + MUX(CLK_MOUT_GDL, "mout_gdl", mout_gdl_p, SRC_LEFTBUS, 0, 1), + + /* SRC_RIGHTBUS */ + MUX(CLK_MOUT_MPLL_USER_R, "mout_mpll_user_r", mout_mpll_user_p, + SRC_RIGHTBUS, 4, 1), + MUX(CLK_MOUT_GDR, "mout_gdr", mout_gdr_p, SRC_RIGHTBUS, 0, 1), + + /* SRC_TOP0 */ + MUX(CLK_MOUT_EBI, "mout_ebi", mout_ebi_p, SRC_TOP0, 28, 1), + MUX(CLK_MOUT_ACLK_200, "mout_aclk_200", group_div_mpll_pre_p,SRC_TOP0, 24, 1), + MUX(CLK_MOUT_ACLK_160, "mout_aclk_160", group_div_mpll_pre_p, SRC_TOP0, 20, 1), + MUX(CLK_MOUT_ACLK_100, "mout_aclk_100", group_div_mpll_pre_p, SRC_TOP0, 16, 1), + MUX(CLK_MOUT_ACLK_266_1, "mout_aclk_266_1", mout_aclk_266_1_p, SRC_TOP0, 14, 1), + MUX(CLK_MOUT_ACLK_266_0, "mout_aclk_266_0", mout_aclk_266_0_p, SRC_TOP0, 13, 1), + MUX(CLK_MOUT_ACLK_266, "mout_aclk_266", mout_aclk_266_p, SRC_TOP0, 12, 1), + MUX(CLK_MOUT_VPLL, "mout_vpll", mout_vpll_p, SRC_TOP0, 8, 1), + MUX(CLK_MOUT_EPLL_USER, "mout_epll_user", mout_epll_user_p, SRC_TOP0, 4, 1), + MUX(CLK_MOUT_EBI_1, "mout_ebi_1", mout_ebi_1_p, SRC_TOP0, 0, 1), + + /* SRC_TOP1 */ + MUX(CLK_MOUT_UPLL, "mout_upll", mout_upll_p, SRC_TOP1, 28, 1), + MUX(CLK_MOUT_ACLK_400_MCUISP_SUB, "mout_aclk_400_mcuisp_sub", mout_aclk_400_mcuisp_sub_p, + SRC_TOP1, 24, 1), + MUX(CLK_MOUT_ACLK_266_SUB, "mout_aclk_266_sub", mout_aclk_266_sub_p, SRC_TOP1, 20, 1), + MUX(CLK_MOUT_MPLL, "mout_mpll", mout_mpll_p, SRC_TOP1, 12, 1), + MUX(CLK_MOUT_ACLK_400_MCUISP, "mout_aclk_400_mcuisp", group_div_mpll_pre_p, SRC_TOP1, 8, 1), + MUX(CLK_MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP1, 0, 1), + + /* SRC_CAM */ + MUX(CLK_MOUT_CAM1, "mout_cam1", group_sclk_p, SRC_CAM, 20, 4), + MUX(CLK_MOUT_CAM_BLK, "mout_cam_blk", group_sclk_cam_blk_p, SRC_CAM, 0, 4), + + /* SRC_MFC */ + MUX(CLK_MOUT_MFC, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), + MUX(CLK_MOUT_MFC_1, "mout_mfc_1", group_epll_vpll_p, SRC_MFC, 4, 1), + MUX(CLK_MOUT_MFC_0, "mout_mfc_0", group_div_mpll_pre_p, SRC_MFC, 0, 1), + + /* SRC_G3D */ + MUX(CLK_MOUT_G3D, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1), + MUX(CLK_MOUT_G3D_1, "mout_g3d_1", group_epll_vpll_p, SRC_G3D, 4, 1), + MUX(CLK_MOUT_G3D_0, "mout_g3d_0", group_div_mpll_pre_p, SRC_G3D, 0, 1), + + /* SRC_LCD */ + MUX(CLK_MOUT_MIPI0, "mout_mipi0", group_sclk_p, SRC_LCD, 12, 4), + MUX(CLK_MOUT_FIMD0, "mout_fimd0", group_sclk_fimd0_p, SRC_LCD, 0, 4), + + /* SRC_ISP */ + MUX(CLK_MOUT_UART_ISP, "mout_uart_isp", group_sclk_p, SRC_ISP, 12, 4), + MUX(CLK_MOUT_SPI1_ISP, "mout_spi1_isp", group_sclk_p, SRC_ISP, 8, 4), + MUX(CLK_MOUT_SPI0_ISP, "mout_spi0_isp", group_sclk_p, SRC_ISP, 4, 4), + + /* SRC_FSYS */ + MUX(CLK_MOUT_TSADC, "mout_tsadc", group_sclk_p, SRC_FSYS, 28, 4), + MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 3), + MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 3), + + /* SRC_PERIL0 */ + MUX(CLK_MOUT_UART1, "mout_uart1", group_sclk_p, SRC_PERIL0, 4, 4), + MUX(CLK_MOUT_UART0, "mout_uart0", group_sclk_p, SRC_PERIL0, 0, 4), + + /* SRC_PERIL1 */ + MUX(CLK_MOUT_SPI1, "mout_spi1", group_sclk_p, SRC_PERIL1, 20, 4), + MUX(CLK_MOUT_SPI0, "mout_spi0", group_sclk_p, SRC_PERIL1, 16, 4), + MUX(CLK_MOUT_AUDIO, "mout_audio", group_sclk_audio_p, SRC_PERIL1, 4, 4), + + /* SRC_CPU */ + MUX(CLK_MOUT_MPLL_USER_C, "mout_mpll_user_c", mout_mpll_user_p, + SRC_CPU, 24, 1), + MUX(CLK_MOUT_HPM, "mout_hpm", mout_hpm_p, SRC_CPU, 20, 1), + MUX(CLK_MOUT_CORE, "mout_core", mout_core_p, SRC_CPU, 16, 1), + MUX(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), +}; + +static struct samsung_div_clock div_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + + /* DIV_LEFTBUS */ + DIV(CLK_DIV_GPL, "div_gpl", "div_gdl", DIV_LEFTBUS, 4, 3), + DIV(CLK_DIV_GDL, "div_gdl", "mout_gdl", DIV_LEFTBUS, 0, 4), + + /* DIV_RIGHTBUS */ + DIV(CLK_DIV_GPR, "div_gpr", "div_gdr", DIV_RIGHTBUS, 4, 3), + DIV(CLK_DIV_GDR, "div_gdr", "mout_gdr", DIV_RIGHTBUS, 0, 4), + + /* DIV_TOP */ + DIV(CLK_DIV_MPLL_PRE, "div_mpll_pre", "sclk_mpll_mif", DIV_TOP, 28, 2), + DIV(CLK_DIV_ACLK_400_MCUISP, "div_aclk_400_mcuisp", + "mout_aclk_400_mcuisp", DIV_TOP, 24, 3), + DIV(CLK_DIV_EBI, "div_ebi", "mout_ebi_1", DIV_TOP, 16, 3), + DIV(CLK_DIV_ACLK_200, "div_aclk_200", "mout_aclk_200", DIV_TOP, 12, 3), + DIV(CLK_DIV_ACLK_160, "div_aclk_160", "mout_aclk_160", DIV_TOP, 8, 3), + DIV(CLK_DIV_ACLK_100, "div_aclk_100", "mout_aclk_100", DIV_TOP, 4, 4), + DIV(CLK_DIV_ACLK_266, "div_aclk_266", "mout_aclk_266", DIV_TOP, 0, 3), + + /* DIV_CAM */ + DIV(CLK_DIV_CAM1, "div_cam1", "mout_cam1", DIV_CAM, 20, 4), + DIV(CLK_DIV_CAM_BLK, "div_cam_blk", "mout_cam_blk", DIV_CAM, 0, 4), + + /* DIV_MFC */ + DIV(CLK_DIV_MFC, "div_mfc", "mout_mfc", DIV_MFC, 0, 4), + + /* DIV_G3D */ + DIV(CLK_DIV_G3D, "div_g3d", "mout_g3d", DIV_G3D, 0, 4), + + /* DIV_LCD */ + DIV_F(CLK_DIV_MIPI0_PRE, "div_mipi0_pre", "div_mipi0", DIV_LCD, 20, 4, + CLK_SET_RATE_PARENT, 0), + DIV(CLK_DIV_MIPI0, "div_mipi0", "mout_mipi0", DIV_LCD, 16, 4), + DIV(CLK_DIV_FIMD0, "div_fimd0", "mout_fimd0", DIV_LCD, 0, 4), + + /* DIV_ISP */ + DIV(CLK_DIV_UART_ISP, "div_uart_isp", "mout_uart_isp", DIV_ISP, 28, 4), + DIV_F(CLK_DIV_SPI1_ISP_PRE, "div_spi1_isp_pre", "div_spi1_isp", + DIV_ISP, 20, 8, CLK_SET_RATE_PARENT, 0), + DIV(CLK_DIV_SPI1_ISP, "div_spi1_isp", "mout_spi1_isp", DIV_ISP, 16, 4), + DIV_F(CLK_DIV_SPI0_ISP_PRE, "div_spi0_isp_pre", "div_spi0_isp", + DIV_ISP, 8, 8, CLK_SET_RATE_PARENT, 0), + DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 0, 4), + + /* DIV_FSYS0 */ + DIV_F(CLK_DIV_TSADC_PRE, "div_tsadc_pre", "div_tsadc", DIV_FSYS0, 8, 8, + CLK_SET_RATE_PARENT, 0), + DIV(CLK_DIV_TSADC, "div_tsadc", "mout_tsadc", DIV_FSYS0, 0, 4), + + /* DIV_FSYS1 */ + DIV_F(CLK_DIV_MMC1_PRE, "div_mmc1_pre", "div_mmc1", DIV_FSYS1, 24, 8, + CLK_SET_RATE_PARENT, 0), + DIV(CLK_DIV_MMC1, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), + DIV_F(CLK_DIV_MMC0_PRE, "div_mmc0_pre", "div_mmc0", DIV_FSYS1, 8, 8, + CLK_SET_RATE_PARENT, 0), + DIV(CLK_DIV_MMC0, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), + + /* DIV_PERIL0 */ + DIV(CLK_DIV_UART1, "div_uart1", "mout_uart1", DIV_PERIL0, 4, 4), + DIV(CLK_DIV_UART0, "div_uart0", "mout_uart0", DIV_PERIL0, 0, 4), + + /* DIV_PERIL1 */ + DIV_F(CLK_DIV_SPI1_PRE, "div_spi1_pre", "div_spi1", DIV_PERIL1, 24, 8, + CLK_SET_RATE_PARENT, 0), + DIV(CLK_DIV_SPI1, "div_spi1", "mout_spi1", DIV_PERIL1, 16, 4), + DIV_F(CLK_DIV_SPI0_PRE, "div_spi0_pre", "div_spi0", DIV_PERIL1, 8, 8, + CLK_SET_RATE_PARENT, 0), + DIV(CLK_DIV_SPI0, "div_spi0", "mout_spi0", DIV_PERIL1, 0, 4), + + /* DIV_PERIL4 */ + DIV(CLK_DIV_PCM, "div_pcm", "div_audio", DIV_PERIL4, 20, 8), + DIV(CLK_DIV_AUDIO, "div_audio", "mout_audio", DIV_PERIL4, 16, 4), + + /* DIV_PERIL5 */ + DIV(CLK_DIV_I2S, "div_i2s", "div_audio", DIV_PERIL5, 8, 6), + + /* DIV_CPU0 */ + DIV(CLK_DIV_CORE2, "div_core2", "div_core", DIV_CPU0, 28, 3), + DIV(CLK_DIV_APLL, "div_apll", "mout_apll", DIV_CPU0, 24, 3), + DIV(CLK_DIV_PCLK_DBG, "div_pclk_dbg", "div_core2", DIV_CPU0, 20, 3), + DIV(CLK_DIV_ATB, "div_atb", "div_core2", DIV_CPU0, 16, 3), + DIV(CLK_DIV_COREM, "div_corem", "div_core2", DIV_CPU0, 4, 3), + DIV(CLK_DIV_CORE, "div_core", "mout_core", DIV_CPU0, 0, 3), + + /* DIV_CPU1 */ + DIV(CLK_DIV_HPM, "div_hpm", "div_copy", DIV_CPU1, 4, 3), + DIV(CLK_DIV_COPY, "div_copy", "mout_hpm", DIV_CPU1, 0, 3), +}; + +static struct samsung_gate_clock gate_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + + /* GATE_IP_LEFTBUS */ + GATE(CLK_ASYNC_G3D, "async_g3d", "div_aclk_100", GATE_IP_LEFTBUS, 6, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_ASYNC_MFCL, "async_mfcl", "div_aclk_100", GATE_IP_LEFTBUS, 4, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PPMULEFT, "ppmuleft", "div_aclk_100", GATE_IP_LEFTBUS, 1, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_GPIO_LEFT, "gpio_left", "div_aclk_100", GATE_IP_LEFTBUS, 0, + CLK_IGNORE_UNUSED, 0), + + /* GATE_IP_RIGHTBUS */ + GATE(CLK_ASYNC_ISPMX, "async_ispmx", "div_aclk_100", + GATE_IP_RIGHTBUS, 9, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ASYNC_FSYSD, "async_fsysd", "div_aclk_100", + GATE_IP_RIGHTBUS, 5, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ASYNC_LCD0X, "async_lcd0x", "div_aclk_100", + GATE_IP_RIGHTBUS, 3, CLK_IGNORE_UNUSED, 0), + GATE(CLK_ASYNC_CAMX, "async_camx", "div_aclk_100", GATE_IP_RIGHTBUS, 2, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PPMURIGHT, "ppmuright", "div_aclk_100", GATE_IP_RIGHTBUS, 1, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_GPIO_RIGHT, "gpio_right", "div_aclk_100", GATE_IP_RIGHTBUS, 0, + CLK_IGNORE_UNUSED, 0), + + /* GATE_IP_PERIR */ + GATE(CLK_MONOCNT, "monocnt", "div_aclk_100", GATE_IP_PERIR, 22, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_TZPC6, "tzpc6", "div_aclk_100", GATE_IP_PERIR, 21, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PROVISIONKEY1, "provisionkey1", "div_aclk_100", + GATE_IP_PERIR, 20, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PROVISIONKEY0, "provisionkey0", "div_aclk_100", + GATE_IP_PERIR, 19, CLK_IGNORE_UNUSED, 0), + GATE(CLK_CMU_ISPPART, "cmu_isppart", "div_aclk_100", GATE_IP_PERIR, 18, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_TMU_APBIF, "tmu_apbif", "div_aclk_100", + GATE_IP_PERIR, 17, 0, 0), + GATE(CLK_KEYIF, "keyif", "div_aclk_100", GATE_IP_PERIR, 16, 0, 0), + GATE(CLK_RTC, "rtc", "div_aclk_100", GATE_IP_PERIR, 15, 0, 0), + GATE(CLK_WDT, "wdt", "div_aclk_100", GATE_IP_PERIR, 14, 0, 0), + GATE(CLK_MCT, "mct", "div_aclk_100", GATE_IP_PERIR, 13, 0, 0), + GATE(CLK_SECKEY, "seckey", "div_aclk_100", GATE_IP_PERIR, 12, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_TZPC5, "tzpc5", "div_aclk_100", GATE_IP_PERIR, 10, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_TZPC4, "tzpc4", "div_aclk_100", GATE_IP_PERIR, 9, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_TZPC3, "tzpc3", "div_aclk_100", GATE_IP_PERIR, 8, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_TZPC2, "tzpc2", "div_aclk_100", GATE_IP_PERIR, 7, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_TZPC1, "tzpc1", "div_aclk_100", GATE_IP_PERIR, 6, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_TZPC0, "tzpc0", "div_aclk_100", GATE_IP_PERIR, 5, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_CMU_COREPART, "cmu_corepart", "div_aclk_100", GATE_IP_PERIR, 4, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_CMU_TOPPART, "cmu_toppart", "div_aclk_100", GATE_IP_PERIR, 3, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PMU_APBIF, "pmu_apbif", "div_aclk_100", GATE_IP_PERIR, 2, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_SYSREG, "sysreg", "div_aclk_100", GATE_IP_PERIR, 1, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_CHIP_ID, "chip_id", "div_aclk_100", GATE_IP_PERIR, 0, + CLK_IGNORE_UNUSED, 0), + + /* GATE_SCLK_CAM */ + GATE(CLK_SCLK_JPEG, "sclk_jpeg", "div_cam_blk", + GATE_SCLK_CAM, 8, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_M2MSCALER, "sclk_m2mscaler", "div_cam_blk", + GATE_SCLK_CAM, 2, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_GSCALER1, "sclk_gscaler1", "div_cam_blk", + GATE_SCLK_CAM, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_GSCALER0, "sclk_gscaler0", "div_cam_blk", + GATE_SCLK_CAM, 0, CLK_SET_RATE_PARENT, 0), + + /* GATE_SCLK_MFC */ + GATE(CLK_SCLK_MFC, "sclk_mfc", "div_mfc", + GATE_SCLK_MFC, 0, CLK_SET_RATE_PARENT, 0), + + /* GATE_SCLK_G3D */ + GATE(CLK_SCLK_G3D, "sclk_g3d", "div_g3d", + GATE_SCLK_G3D, 0, CLK_SET_RATE_PARENT, 0), + + /* GATE_SCLK_LCD */ + GATE(CLK_SCLK_MIPIDPHY2L, "sclk_mipidphy2l", "div_mipi0", + GATE_SCLK_LCD, 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MIPI0, "sclk_mipi0", "div_mipi0_pre", + GATE_SCLK_LCD, 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_FIMD0, "sclk_fimd0", "div_fimd0", + GATE_SCLK_LCD, 0, CLK_SET_RATE_PARENT, 0), + + /* GATE_SCLK_ISP_TOP */ + GATE(CLK_SCLK_CAM1, "sclk_cam1", "div_cam1", + GATE_SCLK_ISP_TOP, 4, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART_ISP, "sclk_uart_isp", "div_uart_isp", + GATE_SCLK_ISP_TOP, 3, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI1_ISP, "sclk_spi1_isp", "div_spi1_isp", + GATE_SCLK_ISP_TOP, 2, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI0_ISP, "sclk_spi0_isp", "div_spi0_isp", + GATE_SCLK_ISP_TOP, 1, CLK_SET_RATE_PARENT, 0), + + /* GATE_SCLK_FSYS */ + GATE(CLK_SCLK_UPLL, "sclk_upll", "mout_upll", GATE_SCLK_FSYS, 10, 0, 0), + GATE(CLK_SCLK_TSADC, "sclk_tsadc", "div_tsadc_pre", + GATE_SCLK_FSYS, 9, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_EBI, "sclk_ebi", "div_ebi", + GATE_SCLK_FSYS, 6, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MMC1, "sclk_mmc1", "div_mmc1_pre", + GATE_SCLK_FSYS, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MMC0, "sclk_mmc0", "div_mmc0_pre", + GATE_SCLK_FSYS, 0, CLK_SET_RATE_PARENT, 0), + + /* GATE_SCLK_PERIL */ + GATE(CLK_SCLK_I2S, "sclk_i2s", "div_i2s", + GATE_SCLK_PERIL, 18, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_PCM, "sclk_pcm", "div_pcm", + GATE_SCLK_PERIL, 16, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI1, "sclk_spi1", "div_spi1_pre", + GATE_SCLK_PERIL, 7, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPI0, "sclk_spi0", "div_spi0_pre", + GATE_SCLK_PERIL, 6, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART1, "sclk_uart1", "div_uart1", + GATE_SCLK_PERIL, 1, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_UART0, "sclk_uart0", "div_uart0", + GATE_SCLK_PERIL, 0, CLK_SET_RATE_PARENT, 0), + + /* GATE_IP_CAM */ + GATE(CLK_QEJPEG, "qejpeg", "div_cam_blk_320", GATE_IP_CAM, 19, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PIXELASYNCM1, "pixelasyncm1", "div_cam_blk_320", + GATE_IP_CAM, 18, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PIXELASYNCM0, "pixelasyncm0", "div_cam_blk_320", + GATE_IP_CAM, 17, CLK_IGNORE_UNUSED, 0), + GATE(CLK_PPMUCAMIF, "ppmucamif", "div_cam_blk_320", + GATE_IP_CAM, 16, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QEM2MSCALER, "qem2mscaler", "div_cam_blk_320", + GATE_IP_CAM, 14, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QEGSCALER1, "qegscaler1", "div_cam_blk_320", + GATE_IP_CAM, 13, CLK_IGNORE_UNUSED, 0), + GATE(CLK_QEGSCALER0, "qegscaler0", "div_cam_blk_320", + GATE_IP_CAM, 12, CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMUJPEG, "smmujpeg", "div_cam_blk_320", + GATE_IP_CAM, 11, 0, 0), + GATE(CLK_SMMUM2M2SCALER, "smmum2m2scaler", "div_cam_blk_320", + GATE_IP_CAM, 9, 0, 0), + GATE(CLK_SMMUGSCALER1, "smmugscaler1", "div_cam_blk_320", + GATE_IP_CAM, 8, 0, 0), + GATE(CLK_SMMUGSCALER0, "smmugscaler0", "div_cam_blk_320", + GATE_IP_CAM, 7, 0, 0), + GATE(CLK_JPEG, "jpeg", "div_cam_blk_320", GATE_IP_CAM, 6, 0, 0), + GATE(CLK_M2MSCALER, "m2mscaler", "div_cam_blk_320", + GATE_IP_CAM, 2, 0, 0), + GATE(CLK_GSCALER1, "gscaler1", "div_cam_blk_320", GATE_IP_CAM, 1, 0, 0), + GATE(CLK_GSCALER0, "gscaler0", "div_cam_blk_320", GATE_IP_CAM, 0, 0, 0), + + /* GATE_IP_MFC */ + GATE(CLK_QEMFC, "qemfc", "div_aclk_200", GATE_IP_MFC, 5, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PPMUMFC_L, "ppmumfc_l", "div_aclk_200", GATE_IP_MFC, 3, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMUMFC_L, "smmumfc_l", "div_aclk_200", GATE_IP_MFC, 1, 0, 0), + GATE(CLK_MFC, "mfc", "div_aclk_200", GATE_IP_MFC, 0, 0, 0), + + /* GATE_IP_G3D */ + GATE(CLK_SMMUG3D, "smmug3d", "div_aclk_200", GATE_IP_G3D, 3, 0, 0), + GATE(CLK_QEG3D, "qeg3d", "div_aclk_200", GATE_IP_G3D, 2, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PPMUG3D, "ppmug3d", "div_aclk_200", GATE_IP_G3D, 1, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_G3D, "g3d", "div_aclk_200", GATE_IP_G3D, 0, 0, 0), + + /* GATE_IP_LCD */ + GATE(CLK_QE_CH1_LCD, "qe_ch1_lcd", "div_aclk_160", GATE_IP_LCD, 7, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_QE_CH0_LCD, "qe_ch0_lcd", "div_aclk_160", GATE_IP_LCD, 6, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_PPMULCD0, "ppmulcd0", "div_aclk_160", GATE_IP_LCD, 5, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_SMMUFIMD0, "smmufimd0", "div_aclk_160", GATE_IP_LCD, 4, 0, 0), + GATE(CLK_DSIM0, "dsim0", "div_aclk_160", GATE_IP_LCD, 3, 0, 0), + GATE(CLK_SMIES, "smies", "div_aclk_160", GATE_IP_LCD, 2, 0, 0), + GATE(CLK_FIMD0, "fimd0", "div_aclk_160", GATE_IP_LCD, 0, 0, 0), + + /* GATE_IP_ISP */ + GATE(CLK_CAM1, "cam1", "mout_aclk_266_sub", GATE_IP_ISP, 5, 0, 0), + GATE(CLK_UART_ISP_TOP, "uart_isp_top", "mout_aclk_266_sub", + GATE_IP_ISP, 3, 0, 0), + GATE(CLK_SPI1_ISP_TOP, "spi1_isp_top", "mout_aclk_266_sub", + GATE_IP_ISP, 2, 0, 0), + GATE(CLK_SPI0_ISP_TOP, "spi0_isp_top", "mout_aclk_266_sub", + GATE_IP_ISP, 1, 0, 0), + + /* GATE_IP_FSYS */ + GATE(CLK_TSADC, "tsadc", "div_aclk_200", GATE_IP_FSYS, 20, 0, 0), + GATE(CLK_PPMUFILE, "ppmufile", "div_aclk_200", GATE_IP_FSYS, 17, + CLK_IGNORE_UNUSED, 0), + GATE(CLK_USBOTG, "usbotg", "div_aclk_200", GATE_IP_FSYS, 13, 0, 0), + GATE(CLK_USBHOST, "usbhost", "div_aclk_200", GATE_IP_FSYS, 12, 0, 0), + GATE(CLK_SROMC, "sromc", "div_aclk_200", GATE_IP_FSYS, 11, 0, 0), + GATE(CLK_SDMMC1, "sdmmc1", "div_aclk_200", GATE_IP_FSYS, 6, 0, 0), + GATE(CLK_SDMMC0, "sdmmc0", "div_aclk_200", GATE_IP_FSYS, 5, 0, 0), + GATE(CLK_PDMA1, "pdma1", "div_aclk_200", GATE_IP_FSYS, 1, 0, 0), + GATE(CLK_PDMA0, "pdma0", "div_aclk_200", GATE_IP_FSYS, 0, 0, 0), + + /* GATE_IP_PERIL */ + GATE(CLK_PWM, "pwm", "div_aclk_100", GATE_IP_PERIL, 24, 0, 0), + GATE(CLK_PCM, "pcm", "div_aclk_100", GATE_IP_PERIL, 23, 0, 0), + GATE(CLK_I2S, "i2s", "div_aclk_100", GATE_IP_PERIL, 21, 0, 0), + GATE(CLK_SPI1, "spi1", "div_aclk_100", GATE_IP_PERIL, 17, 0, 0), + GATE(CLK_SPI0, "spi0", "div_aclk_100", GATE_IP_PERIL, 16, 0, 0), + GATE(CLK_I2C7, "i2c7", "div_aclk_100", GATE_IP_PERIL, 13, 0, 0), + GATE(CLK_I2C6, "i2c6", "div_aclk_100", GATE_IP_PERIL, 12, 0, 0), + GATE(CLK_I2C5, "i2c5", "div_aclk_100", GATE_IP_PERIL, 11, 0, 0), + GATE(CLK_I2C4, "i2c4", "div_aclk_100", GATE_IP_PERIL, 10, 0, 0), + GATE(CLK_I2C3, "i2c3", "div_aclk_100", GATE_IP_PERIL, 9, 0, 0), + GATE(CLK_I2C2, "i2c2", "div_aclk_100", GATE_IP_PERIL, 8, 0, 0), + GATE(CLK_I2C1, "i2c1", "div_aclk_100", GATE_IP_PERIL, 7, 0, 0), + GATE(CLK_I2C0, "i2c0", "div_aclk_100", GATE_IP_PERIL, 6, 0, 0), + GATE(CLK_UART1, "uart1", "div_aclk_100", GATE_IP_PERIL, 1, 0, 0), + GATE(CLK_UART0, "uart0", "div_aclk_100", GATE_IP_PERIL, 0, 0, 0), +}; + +/* APLL & MPLL & BPLL & UPLL */ +static struct samsung_pll_rate_table exynos3250_pll_rates[] = { + PLL_35XX_RATE(1200000000, 400, 4, 1), + PLL_35XX_RATE(1100000000, 275, 3, 1), + PLL_35XX_RATE(1066000000, 533, 6, 1), + PLL_35XX_RATE(1000000000, 250, 3, 1), + PLL_35XX_RATE( 960000000, 320, 4, 1), + PLL_35XX_RATE( 900000000, 300, 4, 1), + PLL_35XX_RATE( 850000000, 425, 6, 1), + PLL_35XX_RATE( 800000000, 200, 3, 1), + PLL_35XX_RATE( 700000000, 175, 3, 1), + PLL_35XX_RATE( 667000000, 667, 12, 1), + PLL_35XX_RATE( 600000000, 400, 4, 2), + PLL_35XX_RATE( 533000000, 533, 6, 2), + PLL_35XX_RATE( 520000000, 260, 3, 2), + PLL_35XX_RATE( 500000000, 250, 3, 2), + PLL_35XX_RATE( 400000000, 200, 3, 2), + PLL_35XX_RATE( 200000000, 200, 3, 3), + PLL_35XX_RATE( 100000000, 200, 3, 4), + { /* sentinel */ } +}; + +/* VPLL */ +static struct samsung_pll_rate_table exynos3250_vpll_rates[] = { + PLL_36XX_RATE(600000000, 100, 2, 1, 0), + PLL_36XX_RATE(533000000, 266, 3, 2, 32768), + PLL_36XX_RATE(519230987, 173, 2, 2, 5046), + PLL_36XX_RATE(500000000, 250, 3, 2, 0), + PLL_36XX_RATE(445500000, 148, 2, 2, 32768), + PLL_36XX_RATE(445055007, 148, 2, 2, 23047), + PLL_36XX_RATE(400000000, 200, 3, 2, 0), + PLL_36XX_RATE(371250000, 123, 2, 2, 49152), + PLL_36XX_RATE(370878997, 185, 3, 2, 28803), + PLL_36XX_RATE(340000000, 170, 3, 2, 0), + PLL_36XX_RATE(335000015, 111, 2, 2, 43691), + PLL_36XX_RATE(333000000, 111, 2, 2, 0), + PLL_36XX_RATE(330000000, 110, 2, 2, 0), + PLL_36XX_RATE(320000015, 106, 2, 2, 43691), + PLL_36XX_RATE(300000000, 100, 2, 2, 0), + PLL_36XX_RATE(275000000, 275, 3, 3, 0), + PLL_36XX_RATE(222750000, 148, 2, 3, 32768), + PLL_36XX_RATE(222528007, 148, 2, 3, 23069), + PLL_36XX_RATE(160000000, 160, 3, 3, 0), + PLL_36XX_RATE(148500000, 99, 2, 3, 0), + PLL_36XX_RATE(148352005, 98, 2, 3, 59070), + PLL_36XX_RATE(108000000, 144, 2, 4, 0), + PLL_36XX_RATE( 74250000, 99, 2, 4, 0), + PLL_36XX_RATE( 74176002, 98, 3, 4, 59070), + PLL_36XX_RATE( 54054000, 216, 3, 5, 14156), + PLL_36XX_RATE( 54000000, 144, 2, 5, 0), + { /* sentinel */ } +}; + +static struct samsung_pll_clock exynos3250_plls[nr_plls] __initdata = { + [apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON0, NULL), + [mpll] = PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", + MPLL_LOCK, MPLL_CON0, NULL), + [vpll] = PLL(pll_36xx, CLK_FOUT_VPLL, "fout_vpll", "fin_pll", + VPLL_LOCK, VPLL_CON0, NULL), + [upll] = PLL(pll_35xx, CLK_FOUT_UPLL, "fout_upll", "fin_pll", + UPLL_LOCK, UPLL_CON0, NULL), +}; + +static void __init exynos3250_cmu_init(struct device_node *np) +{ + struct samsung_clk_provider *ctx; + + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + + ctx = samsung_clk_init(np, reg_base, CLK_NR_CLKS); + if (!ctx) + panic("%s: unable to allocate context.\n", __func__); + + samsung_clk_register_fixed_factor(ctx, fixed_factor_clks, + ARRAY_SIZE(fixed_factor_clks)); + + exynos3250_plls[apll].rate_table = exynos3250_pll_rates; + exynos3250_plls[mpll].rate_table = exynos3250_pll_rates; + exynos3250_plls[vpll].rate_table = exynos3250_vpll_rates; + exynos3250_plls[upll].rate_table = exynos3250_pll_rates; + + samsung_clk_register_pll(ctx, exynos3250_plls, + ARRAY_SIZE(exynos3250_plls), reg_base); + + samsung_clk_register_mux(ctx, mux_clks, ARRAY_SIZE(mux_clks)); + samsung_clk_register_div(ctx, div_clks, ARRAY_SIZE(div_clks)); + samsung_clk_register_gate(ctx, gate_clks, ARRAY_SIZE(gate_clks)); + + exynos3250_clk_sleep_init(); +} +CLK_OF_DECLARE(exynos3250_cmu, "samsung,exynos3250-cmu", exynos3250_cmu_init); diff --git a/include/dt-bindings/clock/exynos3250.h b/include/dt-bindings/clock/exynos3250.h new file mode 100644 index 000000000000..b535e9da7de6 --- /dev/null +++ b/include/dt-bindings/clock/exynos3250.h @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2014 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. + * + * Device Tree binding constants for Samsung Exynos3250 clock controllers. + */ + +#ifndef _DT_BINDINGS_CLOCK_SAMSUNG_EXYNOS3250_CLOCK_H +#define _DT_BINDINGS_CLOCK_SAMSUNG_EXYNOS3250_CLOCK_H + +/* + * Let each exported clock get a unique index, which is used on DT-enabled + * platforms to lookup the clock from a clock specifier. These indices are + * therefore considered an ABI and so must not be changed. This implies + * that new clocks should be added either in free spaces between clock groups + * or at the end. + */ + + +/* + * Main CMU + */ + +#define CLK_OSCSEL 1 +#define CLK_FIN_PLL 2 +#define CLK_FOUT_APLL 3 +#define CLK_FOUT_VPLL 4 +#define CLK_FOUT_UPLL 5 +#define CLK_FOUT_MPLL 6 + +/* Muxes */ +#define CLK_MOUT_MPLL_USER_L 16 +#define CLK_MOUT_GDL 17 +#define CLK_MOUT_MPLL_USER_R 18 +#define CLK_MOUT_GDR 19 +#define CLK_MOUT_EBI 20 +#define CLK_MOUT_ACLK_200 21 +#define CLK_MOUT_ACLK_160 22 +#define CLK_MOUT_ACLK_100 23 +#define CLK_MOUT_ACLK_266_1 24 +#define CLK_MOUT_ACLK_266_0 25 +#define CLK_MOUT_ACLK_266 26 +#define CLK_MOUT_VPLL 27 +#define CLK_MOUT_EPLL_USER 28 +#define CLK_MOUT_EBI_1 29 +#define CLK_MOUT_UPLL 30 +#define CLK_MOUT_ACLK_400_MCUISP_SUB 31 +#define CLK_MOUT_MPLL 32 +#define CLK_MOUT_ACLK_400_MCUISP 33 +#define CLK_MOUT_VPLLSRC 34 +#define CLK_MOUT_CAM1 35 +#define CLK_MOUT_CAM_BLK 36 +#define CLK_MOUT_MFC 37 +#define CLK_MOUT_MFC_1 38 +#define CLK_MOUT_MFC_0 39 +#define CLK_MOUT_G3D 40 +#define CLK_MOUT_G3D_1 41 +#define CLK_MOUT_G3D_0 42 +#define CLK_MOUT_MIPI0 43 +#define CLK_MOUT_FIMD0 44 +#define CLK_MOUT_UART_ISP 45 +#define CLK_MOUT_SPI1_ISP 46 +#define CLK_MOUT_SPI0_ISP 47 +#define CLK_MOUT_TSADC 48 +#define CLK_MOUT_MMC1 49 +#define CLK_MOUT_MMC0 50 +#define CLK_MOUT_UART1 51 +#define CLK_MOUT_UART0 52 +#define CLK_MOUT_SPI1 53 +#define CLK_MOUT_SPI0 54 +#define CLK_MOUT_AUDIO 55 +#define CLK_MOUT_MPLL_USER_C 56 +#define CLK_MOUT_HPM 57 +#define CLK_MOUT_CORE 58 +#define CLK_MOUT_APLL 59 +#define CLK_MOUT_ACLK_266_SUB 60 + +/* Dividers */ +#define CLK_DIV_GPL 64 +#define CLK_DIV_GDL 65 +#define CLK_DIV_GPR 66 +#define CLK_DIV_GDR 67 +#define CLK_DIV_MPLL_PRE 68 +#define CLK_DIV_ACLK_400_MCUISP 69 +#define CLK_DIV_EBI 70 +#define CLK_DIV_ACLK_200 71 +#define CLK_DIV_ACLK_160 72 +#define CLK_DIV_ACLK_100 73 +#define CLK_DIV_ACLK_266 74 +#define CLK_DIV_CAM1 75 +#define CLK_DIV_CAM_BLK 76 +#define CLK_DIV_MFC 77 +#define CLK_DIV_G3D 78 +#define CLK_DIV_MIPI0_PRE 79 +#define CLK_DIV_MIPI0 80 +#define CLK_DIV_FIMD0 81 +#define CLK_DIV_UART_ISP 82 +#define CLK_DIV_SPI1_ISP_PRE 83 +#define CLK_DIV_SPI1_ISP 84 +#define CLK_DIV_SPI0_ISP_PRE 85 +#define CLK_DIV_SPI0_ISP 86 +#define CLK_DIV_TSADC_PRE 87 +#define CLK_DIV_TSADC 88 +#define CLK_DIV_MMC1_PRE 89 +#define CLK_DIV_MMC1 90 +#define CLK_DIV_MMC0_PRE 91 +#define CLK_DIV_MMC0 92 +#define CLK_DIV_UART1 93 +#define CLK_DIV_UART0 94 +#define CLK_DIV_SPI1_PRE 95 +#define CLK_DIV_SPI1 96 +#define CLK_DIV_SPI0_PRE 97 +#define CLK_DIV_SPI0 98 +#define CLK_DIV_PCM 99 +#define CLK_DIV_AUDIO 100 +#define CLK_DIV_I2S 101 +#define CLK_DIV_CORE2 102 +#define CLK_DIV_APLL 103 +#define CLK_DIV_PCLK_DBG 104 +#define CLK_DIV_ATB 105 +#define CLK_DIV_COREM 106 +#define CLK_DIV_CORE 107 +#define CLK_DIV_HPM 108 +#define CLK_DIV_COPY 109 + +/* Gates */ +#define CLK_ASYNC_G3D 128 +#define CLK_ASYNC_MFCL 129 +#define CLK_PPMULEFT 130 +#define CLK_GPIO_LEFT 131 +#define CLK_ASYNC_ISPMX 132 +#define CLK_ASYNC_FSYSD 133 +#define CLK_ASYNC_LCD0X 134 +#define CLK_ASYNC_CAMX 135 +#define CLK_PPMURIGHT 136 +#define CLK_GPIO_RIGHT 137 +#define CLK_MONOCNT 138 +#define CLK_TZPC6 139 +#define CLK_PROVISIONKEY1 140 +#define CLK_PROVISIONKEY0 141 +#define CLK_CMU_ISPPART 142 +#define CLK_TMU_APBIF 143 +#define CLK_KEYIF 144 +#define CLK_RTC 145 +#define CLK_WDT 146 +#define CLK_MCT 147 +#define CLK_SECKEY 148 +#define CLK_TZPC5 149 +#define CLK_TZPC4 150 +#define CLK_TZPC3 151 +#define CLK_TZPC2 152 +#define CLK_TZPC1 153 +#define CLK_TZPC0 154 +#define CLK_CMU_COREPART 155 +#define CLK_CMU_TOPPART 156 +#define CLK_PMU_APBIF 157 +#define CLK_SYSREG 158 +#define CLK_CHIP_ID 159 +#define CLK_QEJPEG 160 +#define CLK_PIXELASYNCM1 161 +#define CLK_PIXELASYNCM0 162 +#define CLK_PPMUCAMIF 163 +#define CLK_QEM2MSCALER 164 +#define CLK_QEGSCALER1 165 +#define CLK_QEGSCALER0 166 +#define CLK_SMMUJPEG 167 +#define CLK_SMMUM2M2SCALER 168 +#define CLK_SMMUGSCALER1 169 +#define CLK_SMMUGSCALER0 170 +#define CLK_JPEG 171 +#define CLK_M2MSCALER 172 +#define CLK_GSCALER1 173 +#define CLK_GSCALER0 174 +#define CLK_QEMFC 175 +#define CLK_PPMUMFC_L 176 +#define CLK_SMMUMFC_L 177 +#define CLK_MFC 178 +#define CLK_SMMUG3D 179 +#define CLK_QEG3D 180 +#define CLK_PPMUG3D 181 +#define CLK_G3D 182 +#define CLK_QE_CH1_LCD 183 +#define CLK_QE_CH0_LCD 184 +#define CLK_PPMULCD0 185 +#define CLK_SMMUFIMD0 186 +#define CLK_DSIM0 187 +#define CLK_FIMD0 188 +#define CLK_CAM1 189 +#define CLK_UART_ISP_TOP 190 +#define CLK_SPI1_ISP_TOP 191 +#define CLK_SPI0_ISP_TOP 192 +#define CLK_TSADC 193 +#define CLK_PPMUFILE 194 +#define CLK_USBOTG 195 +#define CLK_USBHOST 196 +#define CLK_SROMC 197 +#define CLK_SDMMC1 198 +#define CLK_SDMMC0 199 +#define CLK_PDMA1 200 +#define CLK_PDMA0 201 +#define CLK_PWM 202 +#define CLK_PCM 203 +#define CLK_I2S 204 +#define CLK_SPI1 205 +#define CLK_SPI0 206 +#define CLK_I2C7 207 +#define CLK_I2C6 208 +#define CLK_I2C5 209 +#define CLK_I2C4 210 +#define CLK_I2C3 211 +#define CLK_I2C2 212 +#define CLK_I2C1 213 +#define CLK_I2C0 214 +#define CLK_UART1 215 +#define CLK_UART0 216 +#define CLK_BLOCK_LCD 217 +#define CLK_BLOCK_G3D 218 +#define CLK_BLOCK_MFC 219 +#define CLK_BLOCK_CAM 220 +#define CLK_SMIES 221 + +/* Special clocks */ +#define CLK_SCLK_JPEG 224 +#define CLK_SCLK_M2MSCALER 225 +#define CLK_SCLK_GSCALER1 226 +#define CLK_SCLK_GSCALER0 227 +#define CLK_SCLK_MFC 228 +#define CLK_SCLK_G3D 229 +#define CLK_SCLK_MIPIDPHY2L 230 +#define CLK_SCLK_MIPI0 231 +#define CLK_SCLK_FIMD0 232 +#define CLK_SCLK_CAM1 233 +#define CLK_SCLK_UART_ISP 234 +#define CLK_SCLK_SPI1_ISP 235 +#define CLK_SCLK_SPI0_ISP 236 +#define CLK_SCLK_UPLL 237 +#define CLK_SCLK_TSADC 238 +#define CLK_SCLK_EBI 239 +#define CLK_SCLK_MMC1 240 +#define CLK_SCLK_MMC0 241 +#define CLK_SCLK_I2S 242 +#define CLK_SCLK_PCM 243 +#define CLK_SCLK_SPI1 244 +#define CLK_SCLK_SPI0 245 +#define CLK_SCLK_UART1 246 +#define CLK_SCLK_UART0 247 + +/* + * Total number of clocks of main CMU. + * NOTE: Must be equal to last clock ID increased by one. + */ +#define CLK_NR_CLKS 248 + +#endif /* _DT_BINDINGS_CLOCK_SAMSUNG_EXYNOS3250_CLOCK_H */ -- cgit v1.2.3 From 122ff243f5f104194750ecbc76d5946dd1eec934 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Mon, 12 May 2014 16:04:53 -0700 Subject: ipv4: make ip_local_reserved_ports per netns ip_local_port_range is already per netns, so should ip_local_reserved_ports be. And since it is none by default we don't actually need it when we don't enable CONFIG_SYSCTL. By the way, rename inet_is_reserved_local_port() to inet_is_local_reserved_port() Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/ip.h | 14 +++++++++++--- include/net/netns/ipv4.h | 4 ++++ kernel/sysctl.c | 4 ++-- net/ipv4/af_inet.c | 8 +------- net/ipv4/inet_connection_sock.c | 5 +---- net/ipv4/inet_hashtables.c | 2 +- net/ipv4/sysctl_net_ipv4.c | 31 ++++++++++++++----------------- net/ipv4/udp.c | 2 +- net/sctp/socket.c | 5 +++-- 9 files changed, 38 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 14c50a1650ef..512bcd5dabac 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -208,11 +208,19 @@ static inline u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_o void inet_get_local_port_range(struct net *net, int *low, int *high); -extern unsigned long *sysctl_local_reserved_ports; -static inline int inet_is_reserved_local_port(int port) +#if CONFIG_SYSCTL +static inline int inet_is_local_reserved_port(struct net *net, int port) { - return test_bit(port, sysctl_local_reserved_ports); + if (!net->ipv4.sysctl_local_reserved_ports) + return 0; + return test_bit(port, net->ipv4.sysctl_local_reserved_ports); } +#else +static inline int inet_is_local_reserved_port(struct net *net, int port) +{ + return 0; +} +#endif extern int sysctl_ip_nonlocal_bind; diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 2f0cfad66666..aec5e12f9f19 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -84,6 +84,10 @@ struct netns_ipv4 { atomic_t dev_addr_genid; +#ifdef CONFIG_SYSCTL + unsigned long *sysctl_local_reserved_ports; +#endif + #ifdef CONFIG_IP_MROUTE #ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES struct mr_table *mrt; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 74f5b580fe34..e36ae4b15726 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2501,11 +2501,11 @@ int proc_do_large_bitmap(struct ctl_table *table, int write, bool first = 1; size_t left = *lenp; unsigned long bitmap_len = table->maxlen; - unsigned long *bitmap = (unsigned long *) table->data; + unsigned long *bitmap = *(unsigned long **) table->data; unsigned long *tmp_bitmap = NULL; char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c; - if (!bitmap_len || !left || (*ppos && !write)) { + if (!bitmap || !bitmap_len || !left || (*ppos && !write)) { *lenp = 0; return 0; } diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 211c0cc6c3d3..279132bcadd9 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1705,13 +1705,9 @@ static int __init inet_init(void) BUILD_BUG_ON(sizeof(struct inet_skb_parm) > FIELD_SIZEOF(struct sk_buff, cb)); - sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL); - if (!sysctl_local_reserved_ports) - goto out; - rc = proto_register(&tcp_prot, 1); if (rc) - goto out_free_reserved_ports; + goto out; rc = proto_register(&udp_prot, 1); if (rc) @@ -1821,8 +1817,6 @@ out_unregister_udp_proto: proto_unregister(&udp_prot); out_unregister_tcp_proto: proto_unregister(&tcp_prot); -out_free_reserved_ports: - kfree(sysctl_local_reserved_ports); goto out; } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 12e502cbfdc7..14d02ea905b6 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -29,9 +29,6 @@ const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n"; EXPORT_SYMBOL(inet_csk_timer_bug_msg); #endif -unsigned long *sysctl_local_reserved_ports; -EXPORT_SYMBOL(sysctl_local_reserved_ports); - void inet_get_local_port_range(struct net *net, int *low, int *high) { unsigned int seq; @@ -113,7 +110,7 @@ again: smallest_size = -1; do { - if (inet_is_reserved_local_port(rover)) + if (inet_is_local_reserved_port(net, rover)) goto next_nolock; head = &hashinfo->bhash[inet_bhashfn(net, rover, hashinfo->bhash_size)]; diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 8b9cf279450d..83331f1b86ac 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -500,7 +500,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, local_bh_disable(); for (i = 1; i <= remaining; i++) { port = low + (i + offset) % remaining; - if (inet_is_reserved_local_port(port)) + if (inet_is_local_reserved_port(net, port)) continue; head = &hinfo->bhash[inet_bhashfn(net, port, hinfo->bhash_size)]; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index a33b9fbc1d80..79a007c52558 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -436,13 +436,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "ip_local_reserved_ports", - .data = NULL, /* initialized in sysctl_ipv4_init */ - .maxlen = 65536, - .mode = 0644, - .proc_handler = proc_do_large_bitmap, - }, { .procname = "igmp_max_memberships", .data = &sysctl_igmp_max_memberships, @@ -824,6 +817,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = ipv4_local_port_range, }, + { + .procname = "ip_local_reserved_ports", + .data = &init_net.ipv4.sysctl_local_reserved_ports, + .maxlen = 65536, + .mode = 0644, + .proc_handler = proc_do_large_bitmap, + }, { .procname = "ip_no_pmtu_disc", .data = &init_net.ipv4.sysctl_ip_no_pmtu_disc, @@ -876,8 +876,14 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) if (net->ipv4.ipv4_hdr == NULL) goto err_reg; + net->ipv4.sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL); + if (!net->ipv4.sysctl_local_reserved_ports) + goto err_ports; + return 0; +err_ports: + unregister_net_sysctl_table(net->ipv4.ipv4_hdr); err_reg: if (!net_eq(net, &init_net)) kfree(table); @@ -889,6 +895,7 @@ static __net_exit void ipv4_sysctl_exit_net(struct net *net) { struct ctl_table *table; + kfree(net->ipv4.sysctl_local_reserved_ports); table = net->ipv4.ipv4_hdr->ctl_table_arg; unregister_net_sysctl_table(net->ipv4.ipv4_hdr); kfree(table); @@ -902,16 +909,6 @@ static __net_initdata struct pernet_operations ipv4_sysctl_ops = { static __init int sysctl_ipv4_init(void) { struct ctl_table_header *hdr; - struct ctl_table *i; - - for (i = ipv4_table; i->procname; i++) { - if (strcmp(i->procname, "ip_local_reserved_ports") == 0) { - i->data = sysctl_local_reserved_ports; - break; - } - } - if (!i->procname) - return -EINVAL; hdr = register_net_sysctl(&init_net, "net/ipv4", ipv4_table); if (hdr == NULL) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 54ea0a3a48f1..6729ea97a59d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -246,7 +246,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, do { if (low <= snum && snum <= high && !test_bit(snum >> udptable->log, bitmap) && - !inet_is_reserved_local_port(snum)) + !inet_is_local_reserved_port(net, snum)) goto found; snum += rand; } while (snum != first); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index e37b2cbbf177..2af76eaba8f7 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5946,8 +5946,9 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) /* Search for an available port. */ int low, high, remaining, index; unsigned int rover; + struct net *net = sock_net(sk); - inet_get_local_port_range(sock_net(sk), &low, &high); + inet_get_local_port_range(net, &low, &high); remaining = (high - low) + 1; rover = prandom_u32() % remaining + low; @@ -5955,7 +5956,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) rover++; if ((rover < low) || (rover > high)) rover = low; - if (inet_is_reserved_local_port(rover)) + if (inet_is_local_reserved_port(net, rover)) continue; index = sctp_phashfn(sock_net(sk), rover); head = &sctp_port_hashtable[index]; -- cgit v1.2.3 From 2eacc23c422b4553030168f315cb49522fa1b1f6 Mon Sep 17 00:00:00 2001 From: Yuval Atias Date: Wed, 14 May 2014 12:15:10 +0300 Subject: net/mlx4_core: Enforce irq affinity changes immediatly During heavy traffic, napi is constatntly polling the complition queue and no interrupt is fired. Because of that, changes to irq affinity are ignored until traffic is stopped and resumed. By registering to the irq notifier mechanism, and forcing interrupt when affinity is changed, irq affinity changes will be immediatly enforced. Signed-off-by: Yuval Atias Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cq.c | 3 ++ drivers/net/ethernet/mellanox/mlx4/en_rx.c | 11 +++++- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 6 +++ drivers/net/ethernet/mellanox/mlx4/eq.c | 62 ++++++++++++++++++++++++++++++ include/linux/mlx4/device.h | 3 ++ 5 files changed, 83 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index 0487121e4a0f..8542030b89cf 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -293,6 +293,9 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, atomic_set(&cq->refcount, 1); init_completion(&cq->free); + cq->irq = priv->eq_table.eq[cq->vector].irq; + cq->irq_affinity_change = false; + return 0; err_radix: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index a1512450816d..e8c0d2b832b7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -895,10 +895,17 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) mlx4_en_cq_unlock_napi(cq); /* If we used up all the quota - we're probably not done yet... */ - if (done == budget) + if (done == budget) { INC_PERF_COUNTER(priv->pstats.napi_quota); - else { + if (unlikely(cq->mcq.irq_affinity_change)) { + cq->mcq.irq_affinity_change = false; + napi_complete(napi); + mlx4_en_arm_cq(priv, cq); + return 0; + } + } else { /* Done for now */ + cq->mcq.irq_affinity_change = false; napi_complete(napi); mlx4_en_arm_cq(priv, cq); } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 89585c6311c3..cb964056d710 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -474,9 +474,15 @@ int mlx4_en_poll_tx_cq(struct napi_struct *napi, int budget) /* If we used up all the quota - we're probably not done yet... */ if (done < budget) { /* Done for now */ + cq->mcq.irq_affinity_change = false; napi_complete(napi); mlx4_en_arm_cq(priv, cq); return done; + } else if (unlikely(cq->mcq.irq_affinity_change)) { + cq->mcq.irq_affinity_change = false; + napi_complete(napi); + mlx4_en_arm_cq(priv, cq); + return 0; } return budget; } diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 6c088bc1845b..d954ec1eac17 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -53,6 +53,11 @@ enum { MLX4_EQ_ENTRY_SIZE = 0x20 }; +struct mlx4_irq_notify { + void *arg; + struct irq_affinity_notify notify; +}; + #define MLX4_EQ_STATUS_OK ( 0 << 28) #define MLX4_EQ_STATUS_WRITE_FAIL (10 << 28) #define MLX4_EQ_OWNER_SW ( 0 << 24) @@ -1083,6 +1088,57 @@ static void mlx4_unmap_clr_int(struct mlx4_dev *dev) iounmap(priv->clr_base); } +static void mlx4_irq_notifier_notify(struct irq_affinity_notify *notify, + const cpumask_t *mask) +{ + struct mlx4_irq_notify *n = container_of(notify, + struct mlx4_irq_notify, + notify); + struct mlx4_priv *priv = (struct mlx4_priv *)n->arg; + struct radix_tree_iter iter; + void **slot; + + radix_tree_for_each_slot(slot, &priv->cq_table.tree, &iter, 0) { + struct mlx4_cq *cq = (struct mlx4_cq *)(*slot); + + if (cq->irq == notify->irq) + cq->irq_affinity_change = true; + } +} + +static void mlx4_release_irq_notifier(struct kref *ref) +{ + struct mlx4_irq_notify *n = container_of(ref, struct mlx4_irq_notify, + notify.kref); + kfree(n); +} + +static void mlx4_assign_irq_notifier(struct mlx4_priv *priv, + struct mlx4_dev *dev, int irq) +{ + struct mlx4_irq_notify *irq_notifier = NULL; + int err = 0; + + irq_notifier = kzalloc(sizeof(*irq_notifier), GFP_KERNEL); + if (!irq_notifier) { + mlx4_warn(dev, "Failed to allocate irq notifier. irq %d\n", + irq); + return; + } + + irq_notifier->notify.irq = irq; + irq_notifier->notify.notify = mlx4_irq_notifier_notify; + irq_notifier->notify.release = mlx4_release_irq_notifier; + irq_notifier->arg = priv; + err = irq_set_affinity_notifier(irq, &irq_notifier->notify); + if (err) { + kfree(irq_notifier); + irq_notifier = NULL; + mlx4_warn(dev, "Failed to set irq notifier. irq %d\n", irq); + } +} + + int mlx4_alloc_eq_table(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -1353,6 +1409,9 @@ int mlx4_assign_eq(struct mlx4_dev *dev, char *name, struct cpu_rmap *rmap, continue; /*we dont want to break here*/ } + mlx4_assign_irq_notifier(priv, dev, + priv->eq_table.eq[vec].irq); + eq_set_ci(&priv->eq_table.eq[vec], 1); } } @@ -1379,6 +1438,9 @@ void mlx4_release_eq(struct mlx4_dev *dev, int vec) Belonging to a legacy EQ*/ mutex_lock(&priv->msix_ctl.pool_lock); if (priv->msix_ctl.pool_bm & 1ULL << i) { + irq_set_affinity_notifier( + priv->eq_table.eq[vec].irq, + NULL); free_irq(priv->eq_table.eq[vec].irq, &priv->eq_table.eq[vec]); priv->msix_ctl.pool_bm &= ~(1ULL << i); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index ba87bd21295a..c0468e6f0442 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -577,6 +577,9 @@ struct mlx4_cq { u32 cons_index; + u16 irq; + bool irq_affinity_change; + __be32 *set_ci_db; __be32 *arm_db; int arm_sn; -- cgit v1.2.3 From c7228317441f4dee5e5916e30300dd8c61f75af7 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 13 May 2014 20:30:07 -0700 Subject: net: Use a more standard macro for INET_ADDR_COOKIE Missing a colon on definition use is a bit odd so change the macro for the 32 bit case to declare an __attribute__((unused)) and __deprecated variable. The __deprecated attribute will cause gcc to emit an error if the variable is actually used. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 8 +++++--- net/ipv4/inet_hashtables.c | 4 ++-- net/ipv4/udp.c | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 1bdb47715def..dd1950a7e273 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -292,12 +292,12 @@ static inline struct sock *inet_lookup_listener(struct net *net, #define INET_ADDR_COOKIE(__name, __saddr, __daddr) \ const __addrpair __name = (__force __addrpair) ( \ (((__force __u64)(__be32)(__saddr)) << 32) | \ - ((__force __u64)(__be32)(__daddr))); + ((__force __u64)(__be32)(__daddr))) #else /* __LITTLE_ENDIAN */ #define INET_ADDR_COOKIE(__name, __saddr, __daddr) \ const __addrpair __name = (__force __addrpair) ( \ (((__force __u64)(__be32)(__daddr)) << 32) | \ - ((__force __u64)(__be32)(__saddr))); + ((__force __u64)(__be32)(__saddr))) #endif /* __BIG_ENDIAN */ #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ (((__sk)->sk_portpair == (__ports)) && \ @@ -306,7 +306,9 @@ static inline struct sock *inet_lookup_listener(struct net *net, ((__sk)->sk_bound_dev_if == (__dif))) && \ net_eq(sock_net(__sk), (__net))) #else /* 32-bit arch */ -#define INET_ADDR_COOKIE(__name, __saddr, __daddr) +#define INET_ADDR_COOKIE(__name, __saddr, __daddr) \ + const int __name __deprecated __attribute__((unused)) + #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ (((__sk)->sk_portpair == (__ports)) && \ ((__sk)->sk_daddr == (__saddr)) && \ diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 83331f1b86ac..43116e8c8e13 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -274,7 +274,7 @@ struct sock *__inet_lookup_established(struct net *net, const __be32 daddr, const u16 hnum, const int dif) { - INET_ADDR_COOKIE(acookie, saddr, daddr) + INET_ADDR_COOKIE(acookie, saddr, daddr); const __portpair ports = INET_COMBINED_PORTS(sport, hnum); struct sock *sk; const struct hlist_nulls_node *node; @@ -327,7 +327,7 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row, __be32 daddr = inet->inet_rcv_saddr; __be32 saddr = inet->inet_daddr; int dif = sk->sk_bound_dev_if; - INET_ADDR_COOKIE(acookie, saddr, daddr) + INET_ADDR_COOKIE(acookie, saddr, daddr); const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport); struct net *net = sock_net(sk); unsigned int hash = inet_ehashfn(net, daddr, lport, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 6729ea97a59d..590532a7bd2d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1875,7 +1875,7 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net, unsigned int hash2 = udp4_portaddr_hash(net, loc_addr, hnum); unsigned int slot2 = hash2 & udp_table.mask; struct udp_hslot *hslot2 = &udp_table.hash2[slot2]; - INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr) + INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr); const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum); rcu_read_lock(); -- cgit v1.2.3 From 542ad4f8886b376dac9a4334bdb38f9c22a4d8da Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Apr 2014 15:08:29 -0700 Subject: Input: gpio_keys - convert struct descriptions to kernel-doc This patch converts descriptions of the structures defined in linux/gpio_keys.h to follow kernel-doc format. There is no functional change. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Signed-off-by: Dmitry Torokhov --- include/linux/gpio_keys.h | 48 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index a7e977ff4abf..8b622468952c 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -3,29 +3,53 @@ struct device; +/** + * struct gpio_keys_button - configuration parameters + * @code: input event code (KEY_*, SW_*) + * @gpio: %-1 if this key does not support gpio + * @active_low: %true indicates that button is considered + * depressed when gpio is low + * @desc: label that will be attached to button's gpio + * @type: input event type (%EV_KEY, %EV_SW, %EV_ABS) + * @wakeup: configure the button as a wake-up source + * @debounce_interval: debounce ticks interval in msecs + * @can_disable: %true indicates that userspace is allowed to + * disable button via sysfs + * @value: axis value for %EV_ABS + * @irq: Irq number in case of interrupt keys + */ struct gpio_keys_button { - /* Configuration parameters */ - unsigned int code; /* input event code (KEY_*, SW_*) */ - int gpio; /* -1 if this key does not support gpio */ + unsigned int code; + int gpio; int active_low; const char *desc; - unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */ - int wakeup; /* configure the button as a wake-up source */ - int debounce_interval; /* debounce ticks interval in msecs */ + unsigned int type; + int wakeup; + int debounce_interval; bool can_disable; - int value; /* axis value for EV_ABS */ - unsigned int irq; /* Irq number in case of interrupt keys */ + int value; + unsigned int irq; }; +/** + * struct gpio_keys_platform_data - platform data for gpio_keys driver + * @buttons: pointer to array of &gpio_keys_button structures + * describing buttons attached to the device + * @nbuttons: number of elements in @buttons array + * @poll_interval: polling interval in msecs - for polling driver only + * @rep: enable input subsystem auto repeat + * @enable: platform hook for enabling the device + * @disable: platform hook for disabling the device + * @name: input device name + */ struct gpio_keys_platform_data { struct gpio_keys_button *buttons; int nbuttons; - unsigned int poll_interval; /* polling interval in msecs - - for polling driver only */ - unsigned int rep:1; /* enable input subsystem auto repeat */ + unsigned int poll_interval; + unsigned int rep:1; int (*enable)(struct device *dev); void (*disable)(struct device *dev); - const char *name; /* input device name */ + const char *name; }; #endif -- cgit v1.2.3 From bf1de9761c21f56d5b0c6a0acd3b792d801c61e6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 28 Apr 2014 10:49:51 -0700 Subject: Input: implement managed polled input devices Managed resources are becoming more and more popular in drivers. Let's implement managed polled input devices, to complement managed regular input devices. Similarly to managed regular input devices only one new call devm_input_allocate_polled_device() is added and the rest of APIs is modified to work with both managed and non-managed devices. Reviewed-by: David Herrmann Tested-by: Alexander Shiyan Signed-off-by: Dmitry Torokhov --- drivers/input/input-polldev.c | 118 +++++++++++++++++++++++++++++++++++++++++- include/linux/input-polldev.h | 3 ++ 2 files changed, 119 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 4b191908d5de..3664f81655ca 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -176,6 +176,91 @@ struct input_polled_dev *input_allocate_polled_device(void) } EXPORT_SYMBOL(input_allocate_polled_device); +struct input_polled_devres { + struct input_polled_dev *polldev; +}; + +static int devm_input_polldev_match(struct device *dev, void *res, void *data) +{ + struct input_polled_devres *devres = res; + + return devres->polldev == data; +} + +static void devm_input_polldev_release(struct device *dev, void *res) +{ + struct input_polled_devres *devres = res; + struct input_polled_dev *polldev = devres->polldev; + + dev_dbg(dev, "%s: dropping reference/freeing %s\n", + __func__, dev_name(&polldev->input->dev)); + + input_put_device(polldev->input); + kfree(polldev); +} + +static void devm_input_polldev_unregister(struct device *dev, void *res) +{ + struct input_polled_devres *devres = res; + struct input_polled_dev *polldev = devres->polldev; + + dev_dbg(dev, "%s: unregistering device %s\n", + __func__, dev_name(&polldev->input->dev)); + input_unregister_device(polldev->input); + + /* + * Note that we are still holding extra reference to the input + * device so it will stick around until devm_input_polldev_release() + * is called. + */ +} + +/** + * devm_input_allocate_polled_device - allocate managed polled device + * @dev: device owning the polled device being created + * + * Returns prepared &struct input_polled_dev or %NULL. + * + * Managed polled input devices do not need to be explicitly unregistered + * or freed as it will be done automatically when owner device unbinds + * from * its driver (or binding fails). Once such managed polled device + * is allocated, it is ready to be set up and registered in the same + * fashion as regular polled input devices (using + * input_register_polled_device() function). + * + * If you want to manually unregister and free such managed polled devices, + * it can be still done by calling input_unregister_polled_device() and + * input_free_polled_device(), although it is rarely needed. + * + * NOTE: the owner device is set up as parent of input device and users + * should not override it. + */ +struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev) +{ + struct input_polled_dev *polldev; + struct input_polled_devres *devres; + + devres = devres_alloc(devm_input_polldev_release, sizeof(*devres), + GFP_KERNEL); + if (!devres) + return NULL; + + polldev = input_allocate_polled_device(); + if (!polldev) { + devres_free(devres); + return NULL; + } + + polldev->input->dev.parent = dev; + polldev->devres_managed = true; + + devres->polldev = polldev; + devres_add(dev, devres); + + return polldev; +} +EXPORT_SYMBOL(devm_input_allocate_polled_device); + /** * input_free_polled_device - free memory allocated for polled device * @dev: device to free @@ -186,7 +271,12 @@ EXPORT_SYMBOL(input_allocate_polled_device); void input_free_polled_device(struct input_polled_dev *dev) { if (dev) { - input_free_device(dev->input); + if (dev->devres_managed) + WARN_ON(devres_destroy(dev->input->dev.parent, + devm_input_polldev_release, + devm_input_polldev_match, + dev)); + input_put_device(dev->input); kfree(dev); } } @@ -204,9 +294,19 @@ EXPORT_SYMBOL(input_free_polled_device); */ int input_register_polled_device(struct input_polled_dev *dev) { + struct input_polled_devres *devres = NULL; struct input_dev *input = dev->input; int error; + if (dev->devres_managed) { + devres = devres_alloc(devm_input_polldev_unregister, + sizeof(*devres), GFP_KERNEL); + if (!devres) + return -ENOMEM; + + devres->polldev = dev; + } + input_set_drvdata(input, dev); INIT_DELAYED_WORK(&dev->work, input_polled_device_work); @@ -221,8 +321,10 @@ int input_register_polled_device(struct input_polled_dev *dev) input->dev.groups = input_polldev_attribute_groups; error = input_register_device(input); - if (error) + if (error) { + devres_free(devres); return error; + } /* * Take extra reference to the underlying input device so @@ -233,6 +335,12 @@ int input_register_polled_device(struct input_polled_dev *dev) */ input_get_device(input); + if (dev->devres_managed) { + dev_dbg(input->dev.parent, "%s: registering %s with devres.\n", + __func__, dev_name(&input->dev)); + devres_add(input->dev.parent, devres); + } + return 0; } EXPORT_SYMBOL(input_register_polled_device); @@ -247,6 +355,12 @@ EXPORT_SYMBOL(input_register_polled_device); */ void input_unregister_polled_device(struct input_polled_dev *dev) { + if (dev->devres_managed) + WARN_ON(devres_destroy(dev->input->dev.parent, + devm_input_polldev_unregister, + devm_input_polldev_match, + dev)); + input_unregister_device(dev->input); } EXPORT_SYMBOL(input_unregister_polled_device); diff --git a/include/linux/input-polldev.h b/include/linux/input-polldev.h index ce0b72464eb8..2465182670db 100644 --- a/include/linux/input-polldev.h +++ b/include/linux/input-polldev.h @@ -48,9 +48,12 @@ struct input_polled_dev { /* private: */ struct delayed_work work; + + bool devres_managed; }; struct input_polled_dev *input_allocate_polled_device(void); +struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev); void input_free_polled_device(struct input_polled_dev *dev); int input_register_polled_device(struct input_polled_dev *dev); void input_unregister_polled_device(struct input_polled_dev *dev); -- cgit v1.2.3 From a97181adf1502128e2945b4fef2591249c565467 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 12 May 2014 14:04:47 +0200 Subject: clk: sunxi: Fixup clk_sunxi_mmc_phase_control to take a clk rather then a hw_clk __clk_get_hw is supposed to be used by clk providers, not clk consumers. Signed-off-by: Hans de Goede Reviewed-by: Ulf Hansson Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 3 ++- include/linux/clk/sunxi.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 59f90401b900..4cc2b2a5aa75 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -510,11 +510,12 @@ CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", * clk_sunxi_mmc_phase_control() - configures MMC clock phase control */ -void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output) +void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output) { #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + struct clk_hw *hw = __clk_get_hw(clk); struct clk_composite *composite = to_clk_composite(hw); struct clk_hw *rate_hw = composite->rate_hw; struct clk_factors *factors = to_clk_factors(rate_hw); diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h index 1ef5c899e458..aed28c4451d9 100644 --- a/include/linux/clk/sunxi.h +++ b/include/linux/clk/sunxi.h @@ -17,6 +17,6 @@ #include -void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output); +void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output); #endif -- cgit v1.2.3 From 34d22ce22b0b249804816990a3b62b08b1a62546 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Fri, 9 May 2014 14:11:44 +0300 Subject: cfg80211: Add API to update CSA counters in mgmt frames Add NL80211_ATTR_CSA_C_OFFSETS_TX which holds an array of offsets to the CSA counters which should be updated when sending a management frames with NL80211_CMD_FRAME. This API should be used by the drivers that wish to keep the CSA counter updated in probe responses, but do not implement probe response offloading and so, do not use ieee80211_proberesp_get function. Signed-off-by: Andrei Otcheretianski Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ include/uapi/linux/nl80211.h | 8 ++++++++ net/wireless/nl80211.c | 24 ++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e3a48b0a2b3b..f46e1e15746d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1986,6 +1986,8 @@ struct cfg80211_update_ft_ies_params { * @len: buffer length * @no_cck: don't use cck rates for this frame * @dont_wait_for_ack: tells the low level not to wait for an ack + * @n_csa_offsets: length of csa_offsets array + * @csa_offsets: array of all the csa offsets in the frame */ struct cfg80211_mgmt_tx_params { struct ieee80211_channel *chan; @@ -1995,6 +1997,8 @@ struct cfg80211_mgmt_tx_params { size_t len; bool no_cck; bool dont_wait_for_ack; + int n_csa_offsets; + const u16 *csa_offsets; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b65095a85dee..ec90fc9d2358 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -503,6 +503,9 @@ * TX status event pertaining to the TX request. * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * management frames at CCK rate or not in 2GHz band. + * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA + * counters which will be updated to the current value. This attribute + * is used during CSA period. * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this * command may be used with the corresponding cookie to cancel the wait * time if it is known that it is no longer necessary. @@ -1576,6 +1579,9 @@ enum nl80211_commands { * advertise values that cannot always be met. In such cases, an attempt * to add a new station entry with @NL80211_CMD_NEW_STATION may fail. * + * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which + * should be updated when the frame is transmitted. + * * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. * As specified in the &enum nl80211_tdls_peer_capability. * @@ -1920,6 +1926,8 @@ enum nl80211_attrs { NL80211_ATTR_IFACE_SOCKET_OWNER, + NL80211_ATTR_CSA_C_OFFSETS_TX, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 74e7299e4add..4c0ca40ef90e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -386,6 +386,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG }, + [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY }, }; /* policy for the key attributes */ @@ -7786,6 +7787,27 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!chandef.chan && params.offchan) return -EINVAL; + params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); + params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); + + if (info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]) { + int len = nla_len(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]); + int i; + + if (len % sizeof(u16)) + return -EINVAL; + + params.n_csa_offsets = len / sizeof(u16); + params.csa_offsets = + nla_data(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]); + + /* check that all the offsets fit the frame */ + for (i = 0; i < params.n_csa_offsets; i++) { + if (params.csa_offsets[i] >= params.len) + return -EINVAL; + } + } + if (!params.dont_wait_for_ack) { msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) @@ -7799,8 +7821,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } } - params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); - params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); params.chan = chandef.chan; err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie); if (err) -- cgit v1.2.3 From 9a774c78e2114c7e8605e3a168ccd552cbe3d922 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Fri, 9 May 2014 14:11:46 +0300 Subject: cfg80211: Support multiple CSA counters Change the type of NL80211_ATTR_CSA_C_OFF_BEACON and NL80211_ATTR_CSA_C_OFF_PRESP to be NLA_BINARY which allows userspace to use beacons and probe responses with multiple CSA counters. This isn't breaking the API since userspace can continue to use nla_put_u16 for this attributes, which is equivalent to a single element u16 array. In addition advertise max number of supported CSA counters. This is needed when using CSA and eCSA IEs together. Signed-off-by: Andrei Otcheretianski Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 18 ++++++++++-- include/uapi/linux/nl80211.h | 11 +++++--- net/mac80211/cfg.c | 10 +++++-- net/wireless/core.c | 2 ++ net/wireless/nl80211.c | 65 ++++++++++++++++++++++++++++++++++---------- net/wireless/trace.h | 22 +++++++++------ 6 files changed, 96 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f46e1e15746d..447cb58f0d77 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -694,8 +694,10 @@ struct cfg80211_ap_settings { * * @chandef: defines the channel to use after the switch * @beacon_csa: beacon data while performing the switch - * @counter_offset_beacon: offset for the counter within the beacon (tail) - * @counter_offset_presp: offset for the counter within the probe response + * @counter_offsets_beacon: offsets of the counters within the beacon (tail) + * @counter_offsets_presp: offsets of the counters within the probe response + * @n_counter_offsets_beacon: number of csa counters the beacon (tail) + * @n_counter_offsets_presp: number of csa counters in the probe response * @beacon_after: beacon data to be used on the new channel * @radar_required: whether radar detection is required on the new channel * @block_tx: whether transmissions should be blocked while changing @@ -704,7 +706,10 @@ struct cfg80211_ap_settings { struct cfg80211_csa_settings { struct cfg80211_chan_def chandef; struct cfg80211_beacon_data beacon_csa; - u16 counter_offset_beacon, counter_offset_presp; + const u16 *counter_offsets_beacon; + const u16 *counter_offsets_presp; + unsigned int n_counter_offsets_beacon; + unsigned int n_counter_offsets_presp; struct cfg80211_beacon_data beacon_after; bool radar_required; bool block_tx; @@ -3048,6 +3053,13 @@ struct wiphy { u16 max_ap_assoc_sta; + /* + * Number of supported csa_counters in beacons and probe responses. + * This value should be set if the driver wishes to limit the number of + * csa counters. Default (0) means infinite. + */ + u8 max_num_csa_counters; + char priv[0] __aligned(NETDEV_ALIGN); }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index ec90fc9d2358..0cfa827123ff 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1528,10 +1528,10 @@ enum nl80211_commands { * operation). * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information * for the time while performing a channel switch. - * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter - * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL). - * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter - * field in the probe response (%NL80211_ATTR_PROBE_RESP). + * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel + * switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL). + * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel + * switch counters in the probe response (%NL80211_ATTR_PROBE_RESP). * * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. * As specified in the &enum nl80211_rxmgmt_flags. @@ -1581,6 +1581,8 @@ enum nl80211_commands { * * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which * should be updated when the frame is transmitted. + * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum + * supported number of csa counters. * * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. * As specified in the &enum nl80211_tdls_peer_capability. @@ -1927,6 +1929,7 @@ enum nl80211_attrs { NL80211_ATTR_IFACE_SOCKET_OWNER, NL80211_ATTR_CSA_C_OFFSETS_TX, + NL80211_ATTR_MAX_CSA_COUNTERS, /* add attributes here, update the policy in nl80211.c */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8e4754ebc2d8..7a6f8aba5c46 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3192,8 +3192,14 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, break; sdata->csa_counter_offset_beacon = - params->counter_offset_beacon; - sdata->csa_counter_offset_presp = params->counter_offset_presp; + params->counter_offsets_beacon[0]; + + if (params->n_counter_offsets_presp) + sdata->csa_counter_offset_presp = + params->counter_offsets_presp[0]; + else + sdata->csa_counter_offset_presp = 0; + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); if (err < 0) { kfree(sdata->u.ap.next_beacon); diff --git a/net/wireless/core.c b/net/wireless/core.c index 39788711ce6e..d03d8bdb29ca 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -402,6 +402,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.rts_threshold = (u32) -1; rdev->wiphy.coverage_class = 0; + rdev->wiphy.max_num_csa_counters = 1; + return &rdev->wiphy; } EXPORT_SYMBOL(wiphy_new); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4c0ca40ef90e..ca19b1520389 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -371,8 +371,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 }, [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG }, [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, - [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 }, - [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 }, + [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_BINARY }, + [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_BINARY }, [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY }, [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, @@ -1670,6 +1670,13 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, } nla_nest_end(msg, nested); } + state->split_start++; + break; + case 12: + if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH && + nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS, + rdev->wiphy.max_num_csa_counters)) + goto nla_put_failure; /* done */ state->split_start = 0; @@ -5864,6 +5871,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) u8 radar_detect_width = 0; int err; bool need_new_beacon = false; + int len, i; if (!rdev->ops->channel_switch || !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) @@ -5922,26 +5930,55 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]) return -EINVAL; - params.counter_offset_beacon = - nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); - if (params.counter_offset_beacon >= params.beacon_csa.tail_len) + len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); + if (!len || (len % sizeof(u16))) return -EINVAL; - /* sanity check - counters should be the same */ - if (params.beacon_csa.tail[params.counter_offset_beacon] != - params.count) + params.n_counter_offsets_beacon = len / sizeof(u16); + if (rdev->wiphy.max_num_csa_counters && + (params.n_counter_offsets_beacon > + rdev->wiphy.max_num_csa_counters)) return -EINVAL; + params.counter_offsets_beacon = + nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); + + /* sanity checks - counters should fit and be the same */ + for (i = 0; i < params.n_counter_offsets_beacon; i++) { + u16 offset = params.counter_offsets_beacon[i]; + + if (offset >= params.beacon_csa.tail_len) + return -EINVAL; + + if (params.beacon_csa.tail[offset] != params.count) + return -EINVAL; + } + if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) { - params.counter_offset_presp = - nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); - if (params.counter_offset_presp >= - params.beacon_csa.probe_resp_len) + len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); + if (!len || (len % sizeof(u16))) return -EINVAL; - if (params.beacon_csa.probe_resp[params.counter_offset_presp] != - params.count) + params.n_counter_offsets_presp = len / sizeof(u16); + if (rdev->wiphy.max_num_csa_counters && + (params.n_counter_offsets_beacon > + rdev->wiphy.max_num_csa_counters)) return -EINVAL; + + params.counter_offsets_presp = + nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); + + /* sanity checks - counters should fit and be the same */ + for (i = 0; i < params.n_counter_offsets_presp; i++) { + u16 offset = params.counter_offsets_presp[i]; + + if (offset >= params.beacon_csa.probe_resp_len) + return -EINVAL; + + if (params.beacon_csa.probe_resp[offset] != + params.count) + return -EINVAL; + } } skip_beacons: diff --git a/net/wireless/trace.h b/net/wireless/trace.h index cdfbb00e1b37..560ed77084e9 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1876,29 +1876,33 @@ TRACE_EVENT(rdev_channel_switch, WIPHY_ENTRY NETDEV_ENTRY CHAN_DEF_ENTRY - __field(u16, counter_offset_beacon) - __field(u16, counter_offset_presp) __field(bool, radar_required) __field(bool, block_tx) __field(u8, count) + __dynamic_array(u16, bcn_ofs, params->n_counter_offsets_beacon) + __dynamic_array(u16, pres_ofs, params->n_counter_offsets_presp) ), TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; CHAN_DEF_ASSIGN(¶ms->chandef); - __entry->counter_offset_beacon = params->counter_offset_beacon; - __entry->counter_offset_presp = params->counter_offset_presp; __entry->radar_required = params->radar_required; __entry->block_tx = params->block_tx; __entry->count = params->count; + memcpy(__get_dynamic_array(bcn_ofs), + params->counter_offsets_beacon, + params->n_counter_offsets_beacon * sizeof(u16)); + + /* probe response offsets are optional */ + if (params->n_counter_offsets_presp) + memcpy(__get_dynamic_array(pres_ofs), + params->counter_offsets_presp, + params->n_counter_offsets_presp * sizeof(u16)); ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT - ", block_tx: %d, count: %u, radar_required: %d" - ", counter offsets (beacon/presp): %u/%u", + ", block_tx: %d, count: %u, radar_required: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG, - __entry->block_tx, __entry->count, __entry->radar_required, - __entry->counter_offset_beacon, - __entry->counter_offset_presp) + __entry->block_tx, __entry->count, __entry->radar_required) ); TRACE_EVENT(rdev_set_qos_map, -- cgit v1.2.3 From 6ec8c332a0f93959e615158cc212b3abfd52abe7 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Fri, 9 May 2014 14:11:49 +0300 Subject: mac80211: Provide ieee80211_beacon_get_template API Add a new API ieee80211_beacon_get_template, which doesn't affect DTIM counter and should be used if the device generates beacon frames, and new beacon template is needed. In addition set the offsets to TIM IE for MESH interface. Signed-off-by: Andrei Otcheretianski Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 43 ++++++++++++++++++++++----- net/mac80211/tx.c | 80 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 94 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3541c48a97cd..e6521261a585 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3411,6 +3411,39 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, */ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); +/** + * struct ieee80211_mutable_offsets - mutable beacon offsets + * @tim_offset: position of TIM element + * @tim_length: size of TIM element + */ +struct ieee80211_mutable_offsets { + u16 tim_offset; + u16 tim_length; +}; + +/** + * ieee80211_beacon_get_template - beacon template generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @offs: &struct ieee80211_mutable_offsets pointer to struct that will + * receive the offsets that may be updated by the driver. + * + * If the driver implements beaconing modes, it must use this function to + * obtain the beacon template. + * + * This function should be used if the beacon frames are generated by the + * device, and then the driver must use the returned beacon as the template + * The driver is responsible to update the DTIM count. + * + * The driver is responsible for freeing the returned skb. + * + * Return: The beacon template. %NULL on error. + */ +struct sk_buff * +ieee80211_beacon_get_template(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_mutable_offsets *offs); + /** * ieee80211_beacon_get_tim - beacon generation function * @hw: pointer obtained from ieee80211_alloc_hw(). @@ -3422,16 +3455,12 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); * Set to 0 if invalid (in non-AP modes). * * If the driver implements beaconing modes, it must use this function to - * obtain the beacon frame/template. + * obtain the beacon frame. * * If the beacon frames are generated by the host system (i.e., not in * hardware/firmware), the driver uses this function to get each beacon - * frame from mac80211 -- it is responsible for calling this function - * before the beacon is needed (e.g. based on hardware interrupt). - * - * If the beacon frames are generated by the device, then the driver - * must use the returned beacon as the template and change the TIM IE - * according to the current DTIM parameters/TIM bitmap. + * frame from mac80211 -- it is responsible for calling this function exactly + * once before the beacon is needed (e.g. based on hardware interrupt). * * The driver is responsible for freeing the returned skb. * diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0d1a42d4b6de..509456e5722d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2328,7 +2328,8 @@ void ieee80211_tx_pending(unsigned long data) /* functions for drivers to get certain frames */ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, - struct ps_data *ps, struct sk_buff *skb) + struct ps_data *ps, struct sk_buff *skb, + bool is_template) { u8 *pos, *tim; int aid0 = 0; @@ -2341,11 +2342,12 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, * checking byte-for-byte */ have_bits = !bitmap_empty((unsigned long *)ps->tim, IEEE80211_MAX_AID+1); - - if (ps->dtim_count == 0) - ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; - else - ps->dtim_count--; + if (!is_template) { + if (ps->dtim_count == 0) + ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; + else + ps->dtim_count--; + } tim = pos = (u8 *) skb_put(skb, 6); *pos++ = WLAN_EID_TIM; @@ -2391,7 +2393,8 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, } static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, - struct ps_data *ps, struct sk_buff *skb) + struct ps_data *ps, struct sk_buff *skb, + bool is_template) { struct ieee80211_local *local = sdata->local; @@ -2403,10 +2406,10 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, * of the tim bitmap in mac80211 and the driver. */ if (local->tim_in_locked_section) { - __ieee80211_beacon_add_tim(sdata, ps, skb); + __ieee80211_beacon_add_tim(sdata, ps, skb, is_template); } else { spin_lock_bh(&local->tim_lock); - __ieee80211_beacon_add_tim(sdata, ps, skb); + __ieee80211_beacon_add_tim(sdata, ps, skb, is_template); spin_unlock_bh(&local->tim_lock); } @@ -2536,9 +2539,11 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_csa_is_complete); -struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u16 *tim_offset, u16 *tim_length) +static struct sk_buff * +__ieee80211_beacon_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_mutable_offsets *offs, + bool is_template) { struct ieee80211_local *local = hw_to_local(hw); struct sk_buff *skb = NULL; @@ -2556,10 +2561,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!ieee80211_sdata_running(sdata) || !chanctx_conf) goto out; - if (tim_offset) - *tim_offset = 0; - if (tim_length) - *tim_length = 0; + if (offs) + memset(offs, 0, sizeof(*offs)); if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_if_ap *ap = &sdata->u.ap; @@ -2584,12 +2587,13 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, memcpy(skb_put(skb, beacon->head_len), beacon->head, beacon->head_len); - ieee80211_beacon_add_tim(sdata, &ap->ps, skb); + ieee80211_beacon_add_tim(sdata, &ap->ps, skb, + is_template); - if (tim_offset) - *tim_offset = beacon->head_len; - if (tim_length) - *tim_length = skb->len - beacon->head_len; + if (offs) { + offs->tim_offset = beacon->head_len; + offs->tim_length = skb->len - beacon->head_len; + } if (beacon->tail) memcpy(skb_put(skb, beacon->tail_len), @@ -2641,7 +2645,13 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, goto out; skb_reserve(skb, local->tx_headroom); memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len); - ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb); + ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template); + + if (offs) { + offs->tim_offset = bcn->head_len; + offs->tim_length = skb->len - bcn->head_len; + } + memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len); } else { WARN_ON(1); @@ -2678,6 +2688,32 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, out: rcu_read_unlock(); return skb; + +} + +struct sk_buff * +ieee80211_beacon_get_template(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_mutable_offsets *offs) +{ + return __ieee80211_beacon_get(hw, vif, offs, true); +} +EXPORT_SYMBOL(ieee80211_beacon_get_template); + +struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 *tim_offset, u16 *tim_length) +{ + struct ieee80211_mutable_offsets offs = {}; + struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false); + + if (tim_offset) + *tim_offset = offs.tim_offset; + + if (tim_length) + *tim_length = offs.tim_length; + + return bcn; } EXPORT_SYMBOL(ieee80211_beacon_get_tim); -- cgit v1.2.3 From 1af586c9116cdf6863823a830593c48cd9bcecde Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Fri, 9 May 2014 14:11:50 +0300 Subject: mac80211: Handle the CSA counters correctly Make the beacon CSA counters part of ieee80211_mutable_offsets and don't decrement CSA counters when generating a beacon template. This permits the driver to offload the CSA counters handling. Since mac80211 updates the probe responses with the correct counter, the driver should sync the counter's value with mac80211 using ieee80211_csa_update_counter function. Signed-off-by: Andrei Otcheretianski Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 23 +++++++++++++- net/mac80211/cfg.c | 4 +-- net/mac80211/ieee80211_i.h | 2 -- net/mac80211/tx.c | 76 ++++++++++++++++++++++++++++++++++------------ 4 files changed, 80 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e6521261a585..982d2cd80166 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3411,14 +3411,20 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, */ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); +#define IEEE80211_MAX_CSA_COUNTERS_NUM 2 + /** * struct ieee80211_mutable_offsets - mutable beacon offsets * @tim_offset: position of TIM element * @tim_length: size of TIM element + * @csa_offs: array of IEEE80211_MAX_CSA_COUNTERS_NUM offsets to CSA counters. + * This array can contain zero values which should be ignored. */ struct ieee80211_mutable_offsets { u16 tim_offset; u16 tim_length; + + u16 csa_counter_offs[IEEE80211_MAX_CSA_COUNTERS_NUM]; }; /** @@ -3433,7 +3439,8 @@ struct ieee80211_mutable_offsets { * * This function should be used if the beacon frames are generated by the * device, and then the driver must use the returned beacon as the template - * The driver is responsible to update the DTIM count. + * The driver or the device are responsible to update the DTIM and, when + * applicable, the CSA count. * * The driver is responsible for freeing the returned skb. * @@ -3485,6 +3492,20 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, return ieee80211_beacon_get_tim(hw, vif, NULL, NULL); } +/** + * ieee80211_csa_update_counter - request mac80211 to decrement the csa counter + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * The csa counter should be updated after each beacon transmission. + * This function is called implicitly when + * ieee80211_beacon_get/ieee80211_beacon_get_tim are called, however if the + * beacon frames are generated by the device, the driver should call this + * function after each beacon transmission to sync mac80211's csa counters. + * + * Return: new csa counter value + */ +u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif); + /** * ieee80211_csa_finish - notify mac80211 about channel switch * @vif: &struct ieee80211_vif pointer from the add_interface callback. diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d44dca56b8ff..bfd2534e5a4d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3502,10 +3502,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, sdata->vif.type == NL80211_IFTYPE_ADHOC) && params->n_csa_offsets) { int i; + u8 c = sdata->csa_current_counter; for (i = 0; i < params->n_csa_offsets; i++) - data[params->csa_offsets[i]] = - sdata->csa_current_counter; + data[params->csa_offsets[i]] = c; } IEEE80211_SKB_CB(skb)->flags = flags; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 05ed592d8bbe..57e0b2682cef 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -70,8 +70,6 @@ struct ieee80211_local; #define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */) -#define IEEE80211_MAX_CSA_COUNTERS_NUM 2 - struct ieee80211_fragment_entry { unsigned long first_frag_time; unsigned int seq; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 509456e5722d..5214686d9fd1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2416,13 +2416,14 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, return 0; } -static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, - struct beacon_data *beacon) +static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata, + struct beacon_data *beacon) { struct probe_resp *resp; u8 *beacon_data; size_t beacon_data_len; int i; + u8 count = sdata->csa_current_counter; switch (sdata->vif.type) { case NL80211_IFTYPE_AP: @@ -2450,16 +2451,7 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, if (WARN_ON(counter_offset_beacon >= beacon_data_len)) return; - /* Warn if the driver did not check for/react to csa - * completeness. A beacon with CSA counter set to 0 - * should never occur, because a counter of 1 means - * switch just before the next beacon. - */ - if (WARN_ON(beacon_data[counter_offset_beacon] == 1)) - return; - - beacon_data[counter_offset_beacon] = - sdata->csa_current_counter - 1; + beacon_data[counter_offset_beacon] = count; } if (sdata->vif.type == NL80211_IFTYPE_AP && @@ -2474,14 +2466,24 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); return; } - resp->data[counter_offset_presp] = - sdata->csa_current_counter - 1; + resp->data[counter_offset_presp] = count; rcu_read_unlock(); } } +} + +u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); sdata->csa_current_counter--; + + /* the counter should never reach 0 */ + WARN_ON(!sdata->csa_current_counter); + + return sdata->csa_current_counter; } +EXPORT_SYMBOL(ieee80211_csa_update_counter); bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) { @@ -2552,6 +2554,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, enum ieee80211_band band; struct ieee80211_tx_rate_control txrc; struct ieee80211_chanctx_conf *chanctx_conf; + int csa_off_base = 0; rcu_read_lock(); @@ -2569,8 +2572,12 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, struct beacon_data *beacon = rcu_dereference(ap->beacon); if (beacon) { - if (sdata->vif.csa_active) - ieee80211_update_csa(sdata, beacon); + if (sdata->vif.csa_active) { + if (!is_template) + ieee80211_csa_update_counter(vif); + + ieee80211_set_csa(sdata, beacon); + } /* * headroom, head length, @@ -2593,6 +2600,9 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, if (offs) { offs->tim_offset = beacon->head_len; offs->tim_length = skb->len - beacon->head_len; + + /* for AP the csa offsets are from tail */ + csa_off_base = skb->len; } if (beacon->tail) @@ -2608,9 +2618,12 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, if (!presp) goto out; - if (sdata->vif.csa_active) - ieee80211_update_csa(sdata, presp); + if (sdata->vif.csa_active) { + if (!is_template) + ieee80211_csa_update_counter(vif); + ieee80211_set_csa(sdata, presp); + } skb = dev_alloc_skb(local->tx_headroom + presp->head_len + local->hw.extra_beacon_tailroom); @@ -2630,8 +2643,17 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, if (!bcn) goto out; - if (sdata->vif.csa_active) - ieee80211_update_csa(sdata, bcn); + if (sdata->vif.csa_active) { + if (!is_template) + /* TODO: For mesh csa_counter is in TU, so + * decrementing it by one isn't correct, but + * for now we leave it consistent with overall + * mac80211's behavior. + */ + ieee80211_csa_update_counter(vif); + + ieee80211_set_csa(sdata, bcn); + } if (ifmsh->sync_ops) ifmsh->sync_ops->adjust_tbtt(sdata, bcn); @@ -2658,6 +2680,20 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, goto out; } + /* CSA offsets */ + if (offs) { + int i; + + for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) { + u16 csa_off = sdata->csa_counter_offset_beacon[i]; + + if (!csa_off) + continue; + + offs->csa_counter_offs[i] = csa_off_base + csa_off; + } + } + band = chanctx_conf->def.chan->band; info = IEEE80211_SKB_CB(skb); -- cgit v1.2.3 From 4449bf927b61bdb4389393c6fea6837214d1ace7 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 6 May 2014 13:10:24 -0400 Subject: tracing: Add __bitmask() macro to trace events to cpumasks and other bitmasks Being able to show a cpumask of events can be useful as some events may affect only some CPUs. There is no standard way to record the cpumask and converting it to a string is rather expensive during the trace as traces happen in hotpaths. It would be better to record the raw event mask and be able to parse it at print time. The following macros were added for use with the TRACE_EVENT() macro: __bitmask() __assign_bitmask() __get_bitmask() To test this, I added this to the sched_migrate_task event, which looked like this: TRACE_EVENT(sched_migrate_task, TP_PROTO(struct task_struct *p, int dest_cpu, const struct cpumask *cpus), TP_ARGS(p, dest_cpu, cpus), TP_STRUCT__entry( __array( char, comm, TASK_COMM_LEN ) __field( pid_t, pid ) __field( int, prio ) __field( int, orig_cpu ) __field( int, dest_cpu ) __bitmask( cpumask, num_possible_cpus() ) ), TP_fast_assign( memcpy(__entry->comm, p->comm, TASK_COMM_LEN); __entry->pid = p->pid; __entry->prio = p->prio; __entry->orig_cpu = task_cpu(p); __entry->dest_cpu = dest_cpu; __assign_bitmask(cpumask, cpumask_bits(cpus), num_possible_cpus()); ), TP_printk("comm=%s pid=%d prio=%d orig_cpu=%d dest_cpu=%d cpumask=%s", __entry->comm, __entry->pid, __entry->prio, __entry->orig_cpu, __entry->dest_cpu, __get_bitmask(cpumask)) ); With the output of: ksmtuned-3613 [003] d..2 485.220508: sched_migrate_task: comm=ksmtuned pid=3615 prio=120 orig_cpu=3 dest_cpu=2 cpumask=00000000,0000000f migration/1-13 [001] d..5 485.221202: sched_migrate_task: comm=ksmtuned pid=3614 prio=120 orig_cpu=1 dest_cpu=0 cpumask=00000000,0000000f awk-3615 [002] d.H5 485.221747: sched_migrate_task: comm=rcu_preempt pid=7 prio=120 orig_cpu=0 dest_cpu=1 cpumask=00000000,000000ff migration/2-18 [002] d..5 485.222062: sched_migrate_task: comm=ksmtuned pid=3615 prio=120 orig_cpu=2 dest_cpu=3 cpumask=00000000,0000000f Link: http://lkml.kernel.org/r/1399377998-14870-6-git-send-email-javi.merino@arm.com Link: http://lkml.kernel.org/r/20140506132238.22e136d1@gandalf.local.home Suggested-by: Javi Merino Tested-by: Javi Merino Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 3 +++ include/linux/trace_seq.h | 10 ++++++++ include/trace/ftrace.h | 57 +++++++++++++++++++++++++++++++++++++++++++- kernel/trace/trace_output.c | 41 +++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index d16da3e53bc7..cff3106ffe2c 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -38,6 +38,9 @@ const char *ftrace_print_symbols_seq_u64(struct trace_seq *p, *symbol_array); #endif +const char *ftrace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr, + unsigned int bitmask_size); + const char *ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int len); diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index a32d86ec8bf2..136116924d8d 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -46,6 +46,9 @@ extern int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, extern void *trace_seq_reserve(struct trace_seq *s, size_t len); extern int trace_seq_path(struct trace_seq *s, const struct path *path); +extern int trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, + int nmaskbits); + #else /* CONFIG_TRACING */ static inline int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) { @@ -57,6 +60,13 @@ trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) return 0; } +static inline int +trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, + int nmaskbits) +{ + return 0; +} + static inline int trace_print_seq(struct seq_file *m, struct trace_seq *s) { return 0; diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 0a1a4f7caf09..9b7a989dcbcc 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -53,6 +53,9 @@ #undef __string #define __string(item, src) __dynamic_array(char, item, -1) +#undef __bitmask +#define __bitmask(item, nr_bits) __dynamic_array(char, item, -1) + #undef TP_STRUCT__entry #define TP_STRUCT__entry(args...) args @@ -128,6 +131,9 @@ #undef __string #define __string(item, src) __dynamic_array(char, item, -1) +#undef __bitmask +#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1) + #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ struct ftrace_data_offsets_##call { \ @@ -200,6 +206,15 @@ #undef __get_str #define __get_str(field) (char *)__get_dynamic_array(field) +#undef __get_bitmask +#define __get_bitmask(field) \ + ({ \ + void *__bitmask = __get_dynamic_array(field); \ + unsigned int __bitmask_size; \ + __bitmask_size = (__entry->__data_loc_##field >> 16) & 0xffff; \ + ftrace_print_bitmask_seq(p, __bitmask, __bitmask_size); \ + }) + #undef __print_flags #define __print_flags(flag, delim, flag_array...) \ ({ \ @@ -322,6 +337,9 @@ static struct trace_event_functions ftrace_event_type_funcs_##call = { \ #undef __string #define __string(item, src) __dynamic_array(char, item, -1) +#undef __bitmask +#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1) + #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \ static int notrace __init \ @@ -372,6 +390,29 @@ ftrace_define_fields_##call(struct ftrace_event_call *event_call) \ #define __string(item, src) __dynamic_array(char, item, \ strlen((src) ? (const char *)(src) : "(null)") + 1) +/* + * __bitmask_size_in_bytes_raw is the number of bytes needed to hold + * num_possible_cpus(). + */ +#define __bitmask_size_in_bytes_raw(nr_bits) \ + (((nr_bits) + 7) / 8) + +#define __bitmask_size_in_longs(nr_bits) \ + ((__bitmask_size_in_bytes_raw(nr_bits) + \ + ((BITS_PER_LONG / 8) - 1)) / (BITS_PER_LONG / 8)) + +/* + * __bitmask_size_in_bytes is the number of bytes needed to hold + * num_possible_cpus() padded out to the nearest long. This is what + * is saved in the buffer, just to be consistent. + */ +#define __bitmask_size_in_bytes(nr_bits) \ + (__bitmask_size_in_longs(nr_bits) * (BITS_PER_LONG / 8)) + +#undef __bitmask +#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, \ + __bitmask_size_in_longs(nr_bits)) + #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ static inline notrace int ftrace_get_offsets_##call( \ @@ -513,12 +554,22 @@ static inline notrace int ftrace_get_offsets_##call( \ __entry->__data_loc_##item = __data_offsets.item; #undef __string -#define __string(item, src) __dynamic_array(char, item, -1) \ +#define __string(item, src) __dynamic_array(char, item, -1) #undef __assign_str #define __assign_str(dst, src) \ strcpy(__get_str(dst), (src) ? (const char *)(src) : "(null)"); +#undef __bitmask +#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1) + +#undef __get_bitmask +#define __get_bitmask(field) (char *)__get_dynamic_array(field) + +#undef __assign_bitmask +#define __assign_bitmask(dst, src, nr_bits) \ + memcpy(__get_bitmask(dst), (src), __bitmask_size_in_bytes(nr_bits)) + #undef TP_fast_assign #define TP_fast_assign(args...) args @@ -586,6 +637,7 @@ static inline void ftrace_test_probe_##call(void) \ #undef __print_hex #undef __get_dynamic_array #undef __get_str +#undef __get_bitmask #undef TP_printk #define TP_printk(fmt, args...) "\"" fmt "\", " __stringify(args) @@ -651,6 +703,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call #undef __get_str #define __get_str(field) (char *)__get_dynamic_array(field) +#undef __get_bitmask +#define __get_bitmask(field) (char *)__get_dynamic_array(field) + #undef __perf_addr #define __perf_addr(a) (__addr = (a)) diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index a436de18aa99..f3dad80c20b2 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -125,6 +125,34 @@ trace_seq_printf(struct trace_seq *s, const char *fmt, ...) } EXPORT_SYMBOL_GPL(trace_seq_printf); +/** + * trace_seq_bitmask - put a list of longs as a bitmask print output + * @s: trace sequence descriptor + * @maskp: points to an array of unsigned longs that represent a bitmask + * @nmaskbits: The number of bits that are valid in @maskp + * + * It returns 0 if the trace oversizes the buffer's free + * space, 1 otherwise. + * + * Writes a ASCII representation of a bitmask string into @s. + */ +int +trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, + int nmaskbits) +{ + int len = (PAGE_SIZE - 1) - s->len; + int ret; + + if (s->full || !len) + return 0; + + ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits); + s->len += ret; + + return 1; +} +EXPORT_SYMBOL_GPL(trace_seq_bitmask); + /** * trace_seq_vprintf - sequence printing of trace information * @s: trace sequence descriptor @@ -398,6 +426,19 @@ ftrace_print_symbols_seq_u64(struct trace_seq *p, unsigned long long val, EXPORT_SYMBOL(ftrace_print_symbols_seq_u64); #endif +const char * +ftrace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr, + unsigned int bitmask_size) +{ + const char *ret = p->buffer + p->len; + + trace_seq_bitmask(p, bitmask_ptr, bitmask_size * 8); + trace_seq_putc(p, 0); + + return ret; +} +EXPORT_SYMBOL_GPL(ftrace_print_bitmask_seq); + const char * ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len) { -- cgit v1.2.3 From c6e126de43e7d4abfd6cf796b40589db3a046167 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 15 May 2014 16:55:24 +0100 Subject: of: Keep track of populated platform devices In "Device Tree powered" systems, platform devices are usually massively populated with of_platform_populate() call, executed at some level of initcalls, either by generic architecture or by platform-specific code. There are situations though where certain devices must be created (and bound with drivers) before all the others. This presents a challenge, as devices created explicitly would be created again by of_platform_populate(). This patch tries to solve that issue in a generic way, adding a "populated" flag for a DT node description. Subsequent of_platform_populate() will skip such nodes (and its children) in a similar way to the non-available ones. This patch also adds of_platform_depopulate() as an operation complementary to the _populate() one. It removes a platform or an amba device populated from the Device Tree, together with its all children (leaving, however, devices without associated of_node untouched) clearing the "populated" flag on the way. Signed-off-by: Pawel Moll Reviewed-by: Rob Herring Acked-by: Grant Likely --- drivers/of/platform.c | 74 ++++++++++++++++++++++++++++++++++++++++++--- include/linux/of.h | 7 +++++ include/linux/of_platform.h | 5 +++ 3 files changed, 81 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index bd47fbc53dc9..e8376d646d98 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -206,12 +206,13 @@ static struct platform_device *of_platform_device_create_pdata( { struct platform_device *dev; - if (!of_device_is_available(np)) + if (!of_device_is_available(np) || + of_node_test_and_set_flag(np, OF_POPULATED)) return NULL; dev = of_device_alloc(np, bus_id, parent); if (!dev) - return NULL; + goto err_clear_flag; #if defined(CONFIG_MICROBLAZE) dev->archdata.dma_mask = 0xffffffffUL; @@ -229,10 +230,14 @@ static struct platform_device *of_platform_device_create_pdata( if (of_device_add(dev) != 0) { platform_device_put(dev); - return NULL; + goto err_clear_flag; } return dev; + +err_clear_flag: + of_node_clear_flag(np, OF_POPULATED); + return NULL; } /** @@ -264,14 +269,15 @@ static struct amba_device *of_amba_device_create(struct device_node *node, pr_debug("Creating amba device %s\n", node->full_name); - if (!of_device_is_available(node)) + if (!of_device_is_available(node) || + of_node_test_and_set_flag(node, OF_POPULATED)) return NULL; dev = amba_device_alloc(NULL, 0, 0); if (!dev) { pr_err("%s(): amba_device_alloc() failed for %s\n", __func__, node->full_name); - return NULL; + goto err_clear_flag; } /* setup generic device info */ @@ -311,6 +317,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node, err_free: amba_device_put(dev); +err_clear_flag: + of_node_clear_flag(node, OF_POPULATED); return NULL; } #else /* CONFIG_ARM_AMBA */ @@ -487,4 +495,60 @@ int of_platform_populate(struct device_node *root, return rc; } EXPORT_SYMBOL_GPL(of_platform_populate); + +static int of_platform_device_destroy(struct device *dev, void *data) +{ + bool *children_left = data; + + /* Do not touch devices not populated from the device tree */ + if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { + *children_left = true; + return 0; + } + + /* Recurse, but don't touch this device if it has any children left */ + if (of_platform_depopulate(dev) != 0) { + *children_left = true; + return 0; + } + + if (dev->bus == &platform_bus_type) + platform_device_unregister(to_platform_device(dev)); +#ifdef CONFIG_ARM_AMBA + else if (dev->bus == &amba_bustype) + amba_device_unregister(to_amba_device(dev)); +#endif + else { + *children_left = true; + return 0; + } + + of_node_clear_flag(dev->of_node, OF_POPULATED); + + return 0; +} + +/** + * of_platform_depopulate() - Remove devices populated from device tree + * @parent: device which childred will be removed + * + * Complementary to of_platform_populate(), this function removes children + * of the given device (and, recurrently, their children) that have been + * created from their respective device tree nodes (and only those, + * leaving others - eg. manually created - unharmed). + * + * Returns 0 when all children devices have been removed or + * -EBUSY when some children remained. + */ +int of_platform_depopulate(struct device *parent) +{ + bool children_left = false; + + device_for_each_child(parent, &children_left, + of_platform_device_destroy); + + return children_left ? -EBUSY : 0; +} +EXPORT_SYMBOL_GPL(of_platform_depopulate); + #endif /* CONFIG_OF_ADDRESS */ diff --git a/include/linux/of.h b/include/linux/of.h index 3bad8d106e0e..4c50d0b78b89 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -130,6 +130,12 @@ static inline int of_node_check_flag(struct device_node *n, unsigned long flag) return test_bit(flag, &n->_flags); } +static inline int of_node_test_and_set_flag(struct device_node *n, + unsigned long flag) +{ + return test_and_set_bit(flag, &n->_flags); +} + static inline void of_node_set_flag(struct device_node *n, unsigned long flag) { set_bit(flag, &n->_flags); @@ -197,6 +203,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) /* flag descriptions */ #define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ #define OF_DETACHED 2 /* node has been detached from the device tree */ +#define OF_POPULATED 3 /* device already created for the node */ #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 05cb4a928252..b1010eeaac0d 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -72,6 +72,7 @@ extern int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent); +extern int of_platform_depopulate(struct device *parent); #else static inline int of_platform_populate(struct device_node *root, const struct of_device_id *matches, @@ -80,6 +81,10 @@ static inline int of_platform_populate(struct device_node *root, { return -ENODEV; } +static inline int of_platform_depopulate(struct device *parent) +{ + return -ENODEV; +} #endif #endif /* _LINUX_OF_PLATFORM_H */ -- cgit v1.2.3 From 3b9334ac835bb431e2186645230c9f1eb94b5d49 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 30 Apr 2014 16:46:29 +0100 Subject: mfd: vexpress: Convert custom func API to regmap Components of the Versatile Express platform (configuration microcontrollers on motherboard and daughterboards in particular) talk to each other over a custom configuration bus. They provide miscellaneous functions (from clock generator control to energy sensors) which are represented as platform devices (and Device Tree nodes). The transactions on the bus can be generated by different "bridges" in the system, some of which are universal for the whole platform (for the price of high transfer latencies), others restricted to a subsystem (but much faster). Until now drivers for such functions were using custom "func" API, which is being replaced in this patch by regmap calls. This required: * a rework (and move to drivers/bus directory, as suggested by Samuel and Arnd) of the config bus core, which is much simpler now and uses device model infrastructure (class) to keep track of the bridges; non-DT case (soon to be retired anyway) is simply covered by a special device registration function * the new config-bus driver also takes over device population, so there is no need for special matching table for of_platform_populate nor "simple-bus" hack in the arm64 model dtsi file (relevant bindings documentation has been updated); this allows all the vexpress devices fit into normal device model, making it possible to remove plenty of early inits and other hacks in the near future * adaptation of the syscfg bridge implementation in the sysreg driver, again making it much simpler; there is a special case of the "energy" function spanning two registers, where they should be both defined in the tree now, but backward compatibility is maintained in the code * modification of the relevant drivers: * hwmon - just a straight-forward API change * power/reset driver - API change * regulator - API change plus error handling simplification * osc clock driver - this one required larger rework in order to turn in into a standard platform driver Signed-off-by: Pawel Moll Acked-by: Mark Brown Acked-by: Lee Jones Acked-by: Guenter Roeck Acked-by: Mike Turquette --- .../devicetree/bindings/arm/vexpress-sysreg.txt | 43 ++- Documentation/devicetree/bindings/arm/vexpress.txt | 15 +- arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 5 +- arch/arm/mach-vexpress/ct-ca9x4.c | 10 +- arch/arm/mach-vexpress/v2m.c | 18 +- arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi | 2 +- drivers/bus/Kconfig | 9 + drivers/bus/Makefile | 2 + drivers/bus/vexpress-config.c | 202 +++++++++++ drivers/clk/versatile/clk-vexpress-osc.c | 96 +++-- drivers/hwmon/vexpress.c | 17 +- drivers/mfd/Makefile | 2 +- drivers/mfd/vexpress-config.c | 287 --------------- drivers/mfd/vexpress-sysreg.c | 395 +++++++++++---------- drivers/power/reset/vexpress-poweroff.c | 16 +- drivers/regulator/vexpress.c | 50 +-- include/linux/vexpress.h | 79 +---- 17 files changed, 568 insertions(+), 680 deletions(-) create mode 100644 drivers/bus/vexpress-config.c delete mode 100644 drivers/mfd/vexpress-config.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt index 5580e9c4bd85..57b423f78995 100644 --- a/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt +++ b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt @@ -27,24 +27,45 @@ Example: This block also can also act a bridge to the platform's configuration bus via "system control" interface, addressing devices with site number, position in the board stack, config controller, function and device -numbers - see motherboard's TRM for more details. - -The node describing a config device must refer to the sysreg node via -"arm,vexpress,config-bridge" phandle (can be also defined in the node's -parent) and relies on the board topology properties - see main vexpress -node documentation for more details. It must also define the following -property: -- arm,vexpress-sysreg,func : must contain two cells: - - first cell defines function number (eg. 1 for clock generator, - 2 for voltage regulators etc.) - - device number (eg. osc 0, osc 1 etc.) +numbers - see motherboard's TRM for more details. All configuration +controller accessible via this interface must reference the sysreg +node via "arm,vexpress,config-bridge" phandle and define appropriate +topology properties - see main vexpress node documentation for more +details. Each child of such node describes one function and must +define the following properties: +- compatible value : must be one of (corresponding to the TRM): + "arm,vexpress-amp" + "arm,vexpress-dvimode" + "arm,vexpress-energy" + "arm,vexpress-muxfpga" + "arm,vexpress-osc" + "arm,vexpress-power" + "arm,vexpress-reboot" + "arm,vexpress-reset" + "arm,vexpress-scc" + "arm,vexpress-shutdown" + "arm,vexpress-temp" + "arm,vexpress-volt" +- arm,vexpress-sysreg,func : must contain a set of two cells long groups: + - first cell of each group defines the function number + (eg. 1 for clock generator, 2 for voltage regulators etc.) + - second cell of each group defines device number (eg. osc 0, + osc 1 etc.) + - some functions (eg. energy meter, with its 64 bit long counter) + are using more than one function/device number pair Example: mcc { + compatible = "arm,vexpress,config-bus"; arm,vexpress,config-bridge = <&v2m_sysreg>; osc@0 { compatible = "arm,vexpress-osc"; arm,vexpress-sysreg,func = <1 0>; }; + + energy@0 { + compatible = "arm,vexpress-energy"; + arm,vexpress-sysreg,func = <13 0>, <13 1>; + }; }; diff --git a/Documentation/devicetree/bindings/arm/vexpress.txt b/Documentation/devicetree/bindings/arm/vexpress.txt index ae49161e478a..39844cd0bcce 100644 --- a/Documentation/devicetree/bindings/arm/vexpress.txt +++ b/Documentation/devicetree/bindings/arm/vexpress.txt @@ -80,12 +80,17 @@ but also control clock generators, voltage regulators, gather environmental data like temperature, power consumption etc. Even the video output switch (FPGA) is controlled that way. -Nodes describing devices controlled by this infrastructure should -point at the bridge device node: +The controllers are not mapped into normal memory address space +and must be accessed through bridges - other devices capable +of generating transactions on the configuration bus. + +The nodes describing configuration controllers must define +the following properties: +- compatible value: + compatible = "arm,vexpress,config-bus"; - bridge phandle: arm,vexpress,config-bridge = ; -This property can be also defined in a parent node (eg. for a DCC) -and is effective for all children. +and children describing available functions. Platform topology @@ -197,7 +202,7 @@ Example of a VE tile description (simplified) }; dcc { - compatible = "simple-bus"; + compatible = "arm,vexpress,config-bus"; arm,vexpress,config-bridge = <&v2m_sysreg>; osc@0 { diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index 15f98cbcb75a..a25c262326dc 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -312,6 +312,7 @@ arm,vexpress-sysreg,func = <12 0>; label = "A15 Pcore"; }; + power@1 { /* Total power for the three A7 cores */ compatible = "arm,vexpress-power"; @@ -322,14 +323,14 @@ energy@0 { /* Total energy for the two A15 cores */ compatible = "arm,vexpress-energy"; - arm,vexpress-sysreg,func = <13 0>; + arm,vexpress-sysreg,func = <13 0>, <13 1>; label = "A15 Jcore"; }; energy@2 { /* Total energy for the three A7 cores */ compatible = "arm,vexpress-energy"; - arm,vexpress-sysreg,func = <13 2>; + arm,vexpress-sysreg,func = <13 2>, <13 3>; label = "A7 Jcore"; }; }; diff --git a/arch/arm/mach-vexpress/ct-ca9x4.c b/arch/arm/mach-vexpress/ct-ca9x4.c index 6f34497a4245..35e394aa00e5 100644 --- a/arch/arm/mach-vexpress/ct-ca9x4.c +++ b/arch/arm/mach-vexpress/ct-ca9x4.c @@ -128,6 +128,10 @@ static struct platform_device pmu_device = { .resource = pmu_resources, }; +static struct clk_lookup osc1_lookup = { + .dev_id = "ct:clcd", +}; + static struct platform_device osc1_device = { .name = "vexpress-osc", .id = 1, @@ -135,6 +139,7 @@ static struct platform_device osc1_device = { .resource = (struct resource []) { VEXPRESS_RES_FUNC(0xf, 1), }, + .dev.platform_data = &osc1_lookup, }; static void __init ct_ca9x4_init(void) @@ -155,10 +160,7 @@ static void __init ct_ca9x4_init(void) amba_device_register(ct_ca9x4_amba_devs[i], &iomem_resource); platform_device_register(&pmu_device); - platform_device_register(&osc1_device); - - WARN_ON(clk_register_clkdev(vexpress_osc_setup(&osc1_device.dev), - NULL, "ct:clcd")); + vexpress_sysreg_config_device_register(&osc1_device); } #ifdef CONFIG_SMP diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index 4f8b8cb17ff5..ac95220a5019 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -340,11 +340,6 @@ static void __init v2m_init(void) regulator_register_fixed(0, v2m_eth_supplies, ARRAY_SIZE(v2m_eth_supplies)); - platform_device_register(&v2m_muxfpga_device); - platform_device_register(&v2m_shutdown_device); - platform_device_register(&v2m_reboot_device); - platform_device_register(&v2m_dvimode_device); - platform_device_register(&v2m_sysreg_device); platform_device_register(&v2m_pcie_i2c_device); platform_device_register(&v2m_ddc_i2c_device); @@ -356,6 +351,11 @@ static void __init v2m_init(void) for (i = 0; i < ARRAY_SIZE(v2m_amba_devs); i++) amba_device_register(v2m_amba_devs[i], &iomem_resource); + vexpress_sysreg_config_device_register(&v2m_muxfpga_device); + vexpress_sysreg_config_device_register(&v2m_shutdown_device); + vexpress_sysreg_config_device_register(&v2m_reboot_device); + vexpress_sysreg_config_device_register(&v2m_dvimode_device); + ct_desc->init_tile(); } @@ -423,17 +423,11 @@ void __init v2m_dt_init_early(void) versatile_sched_clock_init(vexpress_get_24mhz_clock_base(), 24000000); } -static const struct of_device_id v2m_dt_bus_match[] __initconst = { - { .compatible = "simple-bus", }, - { .compatible = "arm,amba-bus", }, - { .compatible = "arm,vexpress,config-bus", }, - {} -}; static void __init v2m_dt_init(void) { l2x0_of_init(0x00400000, 0xfe0fffff); - of_platform_populate(NULL, v2m_dt_bus_match, NULL, NULL); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } static const char * const v2m_dt_match[] __initconst = { diff --git a/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi b/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi index 2f2ecd217363..ac2cb2418025 100644 --- a/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi +++ b/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi @@ -200,7 +200,7 @@ }; mcc { - compatible = "arm,vexpress,config-bus", "simple-bus"; + compatible = "arm,vexpress,config-bus"; arm,vexpress,config-bridge = <&v2m_sysreg>; v2m_oscclk1: osc@1 { diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 552373c4e362..f24e79dd51bf 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -41,4 +41,13 @@ config ARM_CCI help Driver supporting the CCI cache coherent interconnect for ARM platforms. + +config VEXPRESS_CONFIG + bool "Versatile Express configuration bus" + default y if ARCH_VEXPRESS + depends on ARM || ARM64 + select REGMAP + help + Platform configuration infrastructure for the ARM Ltd. + Versatile Express. endmenu diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 8947bdd0de8b..f095aa771de9 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o # CCI cache coherent interconnect for ARM platforms obj-$(CONFIG_ARM_CCI) += arm-cci.o + +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c new file mode 100644 index 000000000000..27a07dfcd626 --- /dev/null +++ b/drivers/bus/vexpress-config.c @@ -0,0 +1,202 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014 ARM Limited + */ + +#include +#include +#include +#include +#include + + +struct vexpress_config_bridge { + struct vexpress_config_bridge_ops *ops; + void *context; +}; + + +static DEFINE_MUTEX(vexpress_config_mutex); +static struct class *vexpress_config_class; +static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER; + + +void vexpress_config_set_master(u32 site) +{ + vexpress_config_site_master = site; +} + +u32 vexpress_config_get_master(void) +{ + return vexpress_config_site_master; +} + +void vexpress_config_lock(void *arg) +{ + mutex_lock(&vexpress_config_mutex); +} + +void vexpress_config_unlock(void *arg) +{ + mutex_unlock(&vexpress_config_mutex); +} + + +static void vexpress_config_find_prop(struct device_node *node, + const char *name, u32 *val) +{ + /* Default value */ + *val = 0; + + of_node_get(node); + while (node) { + if (of_property_read_u32(node, name, val) == 0) { + of_node_put(node); + return; + } + node = of_get_next_parent(node); + } +} + +int vexpress_config_get_topo(struct device_node *node, u32 *site, + u32 *position, u32 *dcc) +{ + vexpress_config_find_prop(node, "arm,vexpress,site", site); + if (*site == VEXPRESS_SITE_MASTER) + *site = vexpress_config_site_master; + if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER)) + return -EINVAL; + vexpress_config_find_prop(node, "arm,vexpress,position", position); + vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc); + + return 0; +} + + +static void vexpress_config_devres_release(struct device *dev, void *res) +{ + struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent); + struct regmap *regmap = res; + + bridge->ops->regmap_exit(regmap, bridge->context); +} + +struct regmap *devm_regmap_init_vexpress_config(struct device *dev) +{ + struct vexpress_config_bridge *bridge; + struct regmap *regmap; + struct regmap **res; + + if (WARN_ON(dev->parent->class != vexpress_config_class)) + return ERR_PTR(-ENODEV); + + bridge = dev_get_drvdata(dev->parent); + if (WARN_ON(!bridge)) + return ERR_PTR(-EINVAL); + + res = devres_alloc(vexpress_config_devres_release, sizeof(*res), + GFP_KERNEL); + if (!res) + return ERR_PTR(-ENOMEM); + + regmap = bridge->ops->regmap_init(dev, bridge->context); + if (IS_ERR(regmap)) { + devres_free(res); + return regmap; + } + + *res = regmap; + devres_add(dev, res); + + return regmap; +} + + +struct device *vexpress_config_bridge_register(struct device *parent, + struct vexpress_config_bridge_ops *ops, void *context) +{ + struct device *dev; + struct vexpress_config_bridge *bridge; + + if (!vexpress_config_class) { + vexpress_config_class = class_create(THIS_MODULE, + "vexpress-config"); + if (IS_ERR(vexpress_config_class)) + return (void *)vexpress_config_class; + } + + dev = device_create(vexpress_config_class, parent, 0, + NULL, "%s.bridge", dev_name(parent)); + + if (IS_ERR(dev)) + return dev; + + bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + put_device(dev); + device_unregister(dev); + return ERR_PTR(-ENOMEM); + } + bridge->ops = ops; + bridge->context = context; + + dev_set_drvdata(dev, bridge); + + dev_dbg(parent, "Registered bridge '%s', parent node %p\n", + dev_name(dev), parent->of_node); + + return dev; +} + + +static int vexpress_config_node_match(struct device *dev, const void *data) +{ + const struct device_node *node = data; + + dev_dbg(dev, "Parent node %p, looking for %p\n", + dev->parent->of_node, node); + + return dev->parent->of_node == node; +} + +static int vexpress_config_populate(struct device_node *node) +{ + struct device_node *bridge; + struct device *parent; + + bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0); + if (!bridge) + return -EINVAL; + + parent = class_find_device(vexpress_config_class, NULL, bridge, + vexpress_config_node_match); + if (WARN_ON(!parent)) + return -ENODEV; + + return of_platform_populate(node, NULL, NULL, parent); +} + +static int __init vexpress_config_init(void) +{ + int err = 0; + struct device_node *node; + + /* Need the config devices early, before the "normal" devices... */ + for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") { + err = vexpress_config_populate(node); + if (err) + break; + } + + return err; +} +postcore_initcall(vexpress_config_init); + diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c index 422391242b39..529a59c0fbfa 100644 --- a/drivers/clk/versatile/clk-vexpress-osc.c +++ b/drivers/clk/versatile/clk-vexpress-osc.c @@ -11,8 +11,6 @@ * Copyright (C) 2012 ARM Limited */ -#define pr_fmt(fmt) "vexpress-osc: " fmt - #include #include #include @@ -22,7 +20,7 @@ #include struct vexpress_osc { - struct vexpress_config_func *func; + struct regmap *reg; struct clk_hw hw; unsigned long rate_min; unsigned long rate_max; @@ -36,7 +34,7 @@ static unsigned long vexpress_osc_recalc_rate(struct clk_hw *hw, struct vexpress_osc *osc = to_vexpress_osc(hw); u32 rate; - vexpress_config_read(osc->func, 0, &rate); + regmap_read(osc->reg, 0, &rate); return rate; } @@ -60,7 +58,7 @@ static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate, { struct vexpress_osc *osc = to_vexpress_osc(hw); - return vexpress_config_write(osc->func, 0, rate); + return regmap_write(osc->reg, 0, rate); } static struct clk_ops vexpress_osc_ops = { @@ -70,58 +68,31 @@ static struct clk_ops vexpress_osc_ops = { }; -struct clk * __init vexpress_osc_setup(struct device *dev) -{ - struct clk_init_data init; - struct vexpress_osc *osc = kzalloc(sizeof(*osc), GFP_KERNEL); - - if (!osc) - return NULL; - - osc->func = vexpress_config_func_get_by_dev(dev); - if (!osc->func) { - kfree(osc); - return NULL; - } - - init.name = dev_name(dev); - init.ops = &vexpress_osc_ops; - init.flags = CLK_IS_ROOT; - init.num_parents = 0; - osc->hw.init = &init; - - return clk_register(NULL, &osc->hw); -} - -void __init vexpress_osc_of_setup(struct device_node *node) +static int vexpress_osc_probe(struct platform_device *pdev) { + struct clk_lookup *cl = pdev->dev.platform_data; /* Non-DT lookup */ struct clk_init_data init; struct vexpress_osc *osc; struct clk *clk; u32 range[2]; - vexpress_sysreg_of_early_init(); - - osc = kzalloc(sizeof(*osc), GFP_KERNEL); + osc = devm_kzalloc(&pdev->dev, sizeof(*osc), GFP_KERNEL); if (!osc) - return; + return -ENOMEM; - osc->func = vexpress_config_func_get_by_node(node); - if (!osc->func) { - pr_err("Failed to obtain config func for node '%s'!\n", - node->full_name); - goto error; - } + osc->reg = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(osc->reg)) + return PTR_ERR(osc->reg); - if (of_property_read_u32_array(node, "freq-range", range, + if (of_property_read_u32_array(pdev->dev.of_node, "freq-range", range, ARRAY_SIZE(range)) == 0) { osc->rate_min = range[0]; osc->rate_max = range[1]; } - of_property_read_string(node, "clock-output-names", &init.name); - if (!init.name) - init.name = node->full_name; + if (of_property_read_string(pdev->dev.of_node, "clock-output-names", + &init.name) != 0) + init.name = dev_name(&pdev->dev); init.ops = &vexpress_osc_ops; init.flags = CLK_IS_ROOT; @@ -130,20 +101,37 @@ void __init vexpress_osc_of_setup(struct device_node *node) osc->hw.init = &init; clk = clk_register(NULL, &osc->hw); - if (IS_ERR(clk)) { - pr_err("Failed to register clock '%s'!\n", init.name); - goto error; + if (IS_ERR(clk)) + return PTR_ERR(clk); + + of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk); + + /* Only happens for non-DT cases */ + if (cl) { + cl->clk = clk; + clkdev_add(cl); } - of_clk_add_provider(node, of_clk_src_simple_get, clk); + dev_dbg(&pdev->dev, "Registered clock '%s'\n", init.name); + + return 0; +} - pr_debug("Registered clock '%s'\n", init.name); +static struct of_device_id vexpress_osc_of_match[] = { + { .compatible = "arm,vexpress-osc", }, + {} +}; - return; +static struct platform_driver vexpress_osc_driver = { + .driver = { + .name = "vexpress-osc", + .of_match_table = vexpress_osc_of_match, + }, + .probe = vexpress_osc_probe, +}; -error: - if (osc->func) - vexpress_config_func_put(osc->func); - kfree(osc); +static int __init vexpress_osc_init(void) +{ + return platform_driver_register(&vexpress_osc_driver); } -CLK_OF_DECLARE(vexpress_soc, "arm,vexpress-osc", vexpress_osc_of_setup); +core_initcall(vexpress_osc_init); diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c index 8242b75d96c8..611f34c7333d 100644 --- a/drivers/hwmon/vexpress.c +++ b/drivers/hwmon/vexpress.c @@ -26,7 +26,7 @@ struct vexpress_hwmon_data { struct device *hwmon_dev; - struct vexpress_config_func *func; + struct regmap *reg; const char *name; }; @@ -53,7 +53,7 @@ static ssize_t vexpress_hwmon_u32_show(struct device *dev, int err; u32 value; - err = vexpress_config_read(data->func, 0, &value); + err = regmap_read(data->reg, 0, &value); if (err) return err; @@ -68,11 +68,11 @@ static ssize_t vexpress_hwmon_u64_show(struct device *dev, int err; u32 value_hi, value_lo; - err = vexpress_config_read(data->func, 0, &value_lo); + err = regmap_read(data->reg, 0, &value_lo); if (err) return err; - err = vexpress_config_read(data->func, 1, &value_hi); + err = regmap_read(data->reg, 1, &value_hi); if (err) return err; @@ -234,9 +234,9 @@ static int vexpress_hwmon_probe(struct platform_device *pdev) type = match->data; data->name = type->name; - data->func = vexpress_config_func_get_by_dev(&pdev->dev); - if (!data->func) - return -ENODEV; + data->reg = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(data->reg)) + return PTR_ERR(data->reg); err = sysfs_create_groups(&pdev->dev.kobj, type->attr_groups); if (err) @@ -252,7 +252,6 @@ static int vexpress_hwmon_probe(struct platform_device *pdev) error: sysfs_remove_group(&pdev->dev.kobj, match->data); - vexpress_config_func_put(data->func); return err; } @@ -266,8 +265,6 @@ static int vexpress_hwmon_remove(struct platform_device *pdev) match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); sysfs_remove_group(&pdev->dev.kobj, match->data); - vexpress_config_func_put(data->func); - return 0; } diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2851275e2656..9ba838eb5131 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -161,7 +161,7 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o -obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-sysreg.o obj-$(CONFIG_MFD_RETU) += retu-mfd.o obj-$(CONFIG_MFD_AS3711) += as3711.o obj-$(CONFIG_MFD_AS3722) += as3722.o diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c deleted file mode 100644 index d0db89d13e01..000000000000 --- a/drivers/mfd/vexpress-config.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Copyright (C) 2012 ARM Limited - */ - -#define pr_fmt(fmt) "vexpress-config: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define VEXPRESS_CONFIG_MAX_BRIDGES 2 - -static struct vexpress_config_bridge { - struct device_node *node; - struct vexpress_config_bridge_info *info; - struct list_head transactions; - spinlock_t transactions_lock; -} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES]; - -static DECLARE_BITMAP(vexpress_config_bridges_map, - ARRAY_SIZE(vexpress_config_bridges)); -static DEFINE_MUTEX(vexpress_config_bridges_mutex); - -struct vexpress_config_bridge *vexpress_config_bridge_register( - struct device_node *node, - struct vexpress_config_bridge_info *info) -{ - struct vexpress_config_bridge *bridge; - int i; - - pr_debug("Registering bridge '%s'\n", info->name); - - mutex_lock(&vexpress_config_bridges_mutex); - i = find_first_zero_bit(vexpress_config_bridges_map, - ARRAY_SIZE(vexpress_config_bridges)); - if (i >= ARRAY_SIZE(vexpress_config_bridges)) { - pr_err("Can't register more bridges!\n"); - mutex_unlock(&vexpress_config_bridges_mutex); - return NULL; - } - __set_bit(i, vexpress_config_bridges_map); - bridge = &vexpress_config_bridges[i]; - - bridge->node = node; - bridge->info = info; - INIT_LIST_HEAD(&bridge->transactions); - spin_lock_init(&bridge->transactions_lock); - - mutex_unlock(&vexpress_config_bridges_mutex); - - return bridge; -} -EXPORT_SYMBOL(vexpress_config_bridge_register); - -void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) -{ - struct vexpress_config_bridge __bridge = *bridge; - int i; - - mutex_lock(&vexpress_config_bridges_mutex); - for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) - if (&vexpress_config_bridges[i] == bridge) - __clear_bit(i, vexpress_config_bridges_map); - mutex_unlock(&vexpress_config_bridges_mutex); - - WARN_ON(!list_empty(&__bridge.transactions)); - while (!list_empty(&__bridge.transactions)) - cpu_relax(); -} -EXPORT_SYMBOL(vexpress_config_bridge_unregister); - - -struct vexpress_config_func { - struct vexpress_config_bridge *bridge; - void *func; -}; - -struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, - struct device_node *node) -{ - struct device_node *bridge_node; - struct vexpress_config_func *func; - int i; - - if (WARN_ON(dev && node && dev->of_node != node)) - return NULL; - if (dev && !node) - node = dev->of_node; - - func = kzalloc(sizeof(*func), GFP_KERNEL); - if (!func) - return NULL; - - bridge_node = of_node_get(node); - while (bridge_node) { - const __be32 *prop = of_get_property(bridge_node, - "arm,vexpress,config-bridge", NULL); - - if (prop) { - bridge_node = of_find_node_by_phandle( - be32_to_cpup(prop)); - break; - } - - bridge_node = of_get_next_parent(bridge_node); - } - - mutex_lock(&vexpress_config_bridges_mutex); - for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) { - struct vexpress_config_bridge *bridge = - &vexpress_config_bridges[i]; - - if (test_bit(i, vexpress_config_bridges_map) && - bridge->node == bridge_node) { - func->bridge = bridge; - func->func = bridge->info->func_get(dev, node); - break; - } - } - mutex_unlock(&vexpress_config_bridges_mutex); - - if (!func->func) { - of_node_put(node); - kfree(func); - return NULL; - } - - return func; -} -EXPORT_SYMBOL(__vexpress_config_func_get); - -void vexpress_config_func_put(struct vexpress_config_func *func) -{ - func->bridge->info->func_put(func->func); - of_node_put(func->bridge->node); - kfree(func); -} -EXPORT_SYMBOL(vexpress_config_func_put); - -struct vexpress_config_trans { - struct vexpress_config_func *func; - int offset; - bool write; - u32 *data; - int status; - struct completion completion; - struct list_head list; -}; - -static void vexpress_config_dump_trans(const char *what, - struct vexpress_config_trans *trans) -{ - pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n", - what, trans->write ? "write" : "read", trans, - trans->func->func, trans->offset, - trans->data ? *trans->data : 0, trans->status); -} - -static int vexpress_config_schedule(struct vexpress_config_trans *trans) -{ - int status; - struct vexpress_config_bridge *bridge = trans->func->bridge; - unsigned long flags; - - init_completion(&trans->completion); - trans->status = -EFAULT; - - spin_lock_irqsave(&bridge->transactions_lock, flags); - - if (list_empty(&bridge->transactions)) { - vexpress_config_dump_trans("Executing", trans); - status = bridge->info->func_exec(trans->func->func, - trans->offset, trans->write, trans->data); - } else { - vexpress_config_dump_trans("Queuing", trans); - status = VEXPRESS_CONFIG_STATUS_WAIT; - } - - switch (status) { - case VEXPRESS_CONFIG_STATUS_DONE: - vexpress_config_dump_trans("Finished", trans); - trans->status = status; - break; - case VEXPRESS_CONFIG_STATUS_WAIT: - list_add_tail(&trans->list, &bridge->transactions); - break; - } - - spin_unlock_irqrestore(&bridge->transactions_lock, flags); - - return status; -} - -void vexpress_config_complete(struct vexpress_config_bridge *bridge, - int status) -{ - struct vexpress_config_trans *trans; - unsigned long flags; - const char *message = "Completed"; - - spin_lock_irqsave(&bridge->transactions_lock, flags); - - trans = list_first_entry(&bridge->transactions, - struct vexpress_config_trans, list); - trans->status = status; - - do { - vexpress_config_dump_trans(message, trans); - list_del(&trans->list); - complete(&trans->completion); - - if (list_empty(&bridge->transactions)) - break; - - trans = list_first_entry(&bridge->transactions, - struct vexpress_config_trans, list); - vexpress_config_dump_trans("Executing pending", trans); - trans->status = bridge->info->func_exec(trans->func->func, - trans->offset, trans->write, trans->data); - message = "Finished pending"; - } while (trans->status == VEXPRESS_CONFIG_STATUS_DONE); - - spin_unlock_irqrestore(&bridge->transactions_lock, flags); -} -EXPORT_SYMBOL(vexpress_config_complete); - -int vexpress_config_wait(struct vexpress_config_trans *trans) -{ - wait_for_completion(&trans->completion); - - return trans->status; -} -EXPORT_SYMBOL(vexpress_config_wait); - -int vexpress_config_read(struct vexpress_config_func *func, int offset, - u32 *data) -{ - struct vexpress_config_trans trans = { - .func = func, - .offset = offset, - .write = false, - .data = data, - .status = 0, - }; - int status = vexpress_config_schedule(&trans); - - if (status == VEXPRESS_CONFIG_STATUS_WAIT) - status = vexpress_config_wait(&trans); - - return status; -} -EXPORT_SYMBOL(vexpress_config_read); - -int vexpress_config_write(struct vexpress_config_func *func, int offset, - u32 data) -{ - struct vexpress_config_trans trans = { - .func = func, - .offset = offset, - .write = true, - .data = &data, - .status = 0, - }; - int status = vexpress_config_schedule(&trans); - - if (status == VEXPRESS_CONFIG_STATUS_WAIT) - status = vexpress_config_wait(&trans); - - return status; -} -EXPORT_SYMBOL(vexpress_config_write); diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 35281e804e7e..b4138a7168db 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -72,9 +74,18 @@ static void __iomem *vexpress_sysreg_base; static struct device *vexpress_sysreg_dev; -static int vexpress_master_site; +static LIST_HEAD(vexpress_sysreg_config_funcs); +static struct device *vexpress_sysreg_config_bridge; +static int vexpress_sysreg_get_master(void) +{ + if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) + return VEXPRESS_SITE_DB2; + + return VEXPRESS_SITE_DB1; +} + void vexpress_flags_set(u32 data) { writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); @@ -84,7 +95,7 @@ void vexpress_flags_set(u32 data) u32 vexpress_get_procid(int site) { if (site == VEXPRESS_SITE_MASTER) - site = vexpress_master_site; + site = vexpress_sysreg_get_master(); return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? SYS_PROCID0 : SYS_PROCID1)); @@ -114,130 +125,33 @@ void __iomem *vexpress_get_24mhz_clock_base(void) } -static void vexpress_sysreg_find_prop(struct device_node *node, - const char *name, u32 *val) -{ - of_node_get(node); - while (node) { - if (of_property_read_u32(node, name, val) == 0) { - of_node_put(node); - return; - } - node = of_get_next_parent(node); - } -} - -unsigned __vexpress_get_site(struct device *dev, struct device_node *node) -{ - u32 site = 0; - - WARN_ON(dev && node && dev->of_node != node); - if (dev && !node) - node = dev->of_node; - - if (node) { - vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); - } else if (dev && dev->bus == &platform_bus_type) { - struct platform_device *pdev = to_platform_device(dev); - - if (pdev->num_resources == 1 && - pdev->resource[0].flags == IORESOURCE_BUS) - site = pdev->resource[0].start; - } else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) { - site = VEXPRESS_SITE_MASTER; - } - - if (site == VEXPRESS_SITE_MASTER) - site = vexpress_master_site; - - return site; -} - - struct vexpress_sysreg_config_func { - u32 template; - u32 device; + struct list_head list; + struct regmap *regmap; + int num_templates; + u32 template[0]; /* Keep this last */ }; -static struct vexpress_config_bridge *vexpress_sysreg_config_bridge; -static struct timer_list vexpress_sysreg_config_timer; -static u32 *vexpress_sysreg_config_data; -static int vexpress_sysreg_config_tries; - -static void *vexpress_sysreg_config_func_get(struct device *dev, - struct device_node *node) +static int vexpress_sysreg_config_exec(struct vexpress_sysreg_config_func *func, + int index, bool write, u32 *data) { - struct vexpress_sysreg_config_func *config_func; - u32 site = 0; - u32 position = 0; - u32 dcc = 0; - u32 func_device[2]; - int err = -EFAULT; - - if (node) { - of_node_get(node); - vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); - vexpress_sysreg_find_prop(node, "arm,vexpress,position", - &position); - vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc); - err = of_property_read_u32_array(node, - "arm,vexpress-sysreg,func", func_device, - ARRAY_SIZE(func_device)); - of_node_put(node); - } else if (dev && dev->bus == &platform_bus_type) { - struct platform_device *pdev = to_platform_device(dev); - - if (pdev->num_resources == 1 && - pdev->resource[0].flags == IORESOURCE_BUS) { - site = pdev->resource[0].start; - func_device[0] = pdev->resource[0].end; - func_device[1] = pdev->id; - err = 0; - } - } - if (err) - return NULL; - - config_func = kzalloc(sizeof(*config_func), GFP_KERNEL); - if (!config_func) - return NULL; - - config_func->template = SYS_CFGCTRL_DCC(dcc); - config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]); - config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ? - vexpress_master_site : site); - config_func->template |= SYS_CFGCTRL_POSITION(position); - config_func->device |= func_device[1]; - - dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func, - config_func->template, config_func->device); - - return config_func; -} - -static void vexpress_sysreg_config_func_put(void *func) -{ - kfree(func); -} - -static int vexpress_sysreg_config_func_exec(void *func, int offset, - bool write, u32 *data) -{ - int status; - struct vexpress_sysreg_config_func *config_func = func; - u32 command; + u32 command, status; + int tries; + long timeout; if (WARN_ON(!vexpress_sysreg_base)) return -ENOENT; + if (WARN_ON(index > func->num_templates)) + return -EINVAL; + command = readl(vexpress_sysreg_base + SYS_CFGCTRL); if (WARN_ON(command & SYS_CFGCTRL_START)) return -EBUSY; - command = SYS_CFGCTRL_START; + command = func->template[index]; + command |= SYS_CFGCTRL_START; command |= write ? SYS_CFGCTRL_WRITE : 0; - command |= config_func->template; - command |= SYS_CFGCTRL_DEVICE(config_func->device + offset); /* Use a canary for reads */ if (!write) @@ -250,90 +164,190 @@ static int vexpress_sysreg_config_func_exec(void *func, int offset, writel(command, vexpress_sysreg_base + SYS_CFGCTRL); mb(); - if (vexpress_sysreg_dev) { - /* Schedule completion check */ - if (!write) - vexpress_sysreg_config_data = data; - vexpress_sysreg_config_tries = 100; - mod_timer(&vexpress_sysreg_config_timer, - jiffies + usecs_to_jiffies(100)); - status = VEXPRESS_CONFIG_STATUS_WAIT; - } else { - /* Early execution, no timer available, have to spin */ - u32 cfgstat; + /* The operation can take ages... Go to sleep, 100us initially */ + tries = 100; + timeout = 100; + do { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(usecs_to_jiffies(timeout)); + if (signal_pending(current)) + return -EINTR; + + status = readl(vexpress_sysreg_base + SYS_CFGSTAT); + if (status & SYS_CFGSTAT_ERR) + return -EFAULT; + + if (timeout > 20) + timeout -= 20; + } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); + if (WARN_ON_ONCE(!tries)) + return -ETIMEDOUT; + + if (!write) { + *data = readl(vexpress_sysreg_base + SYS_CFGDATA); + dev_dbg(vexpress_sysreg_dev, "func %p, read data %x\n", + func, *data); + } - do { - cpu_relax(); - cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); - } while (!cfgstat); + return 0; +} - if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE)) - *data = readl(vexpress_sysreg_base + SYS_CFGDATA); - status = VEXPRESS_CONFIG_STATUS_DONE; +static int vexpress_sysreg_config_read(void *context, unsigned int index, + unsigned int *val) +{ + struct vexpress_sysreg_config_func *func = context; - if (cfgstat & SYS_CFGSTAT_ERR) - status = -EINVAL; - } + return vexpress_sysreg_config_exec(func, index, false, val); +} - return status; +static int vexpress_sysreg_config_write(void *context, unsigned int index, + unsigned int val) +{ + struct vexpress_sysreg_config_func *func = context; + + return vexpress_sysreg_config_exec(func, index, true, &val); } -struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = { - .name = "vexpress-sysreg", - .func_get = vexpress_sysreg_config_func_get, - .func_put = vexpress_sysreg_config_func_put, - .func_exec = vexpress_sysreg_config_func_exec, +struct regmap_config vexpress_sysreg_regmap_config = { + .lock = vexpress_config_lock, + .unlock = vexpress_config_unlock, + .reg_bits = 32, + .val_bits = 32, + .reg_read = vexpress_sysreg_config_read, + .reg_write = vexpress_sysreg_config_write, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, }; -static void vexpress_sysreg_config_complete(unsigned long data) +static struct regmap *vexpress_sysreg_config_regmap_init(struct device *dev, + void *context) { - int status = VEXPRESS_CONFIG_STATUS_DONE; - u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); - - if (cfgstat & SYS_CFGSTAT_ERR) - status = -EINVAL; - if (!vexpress_sysreg_config_tries--) - status = -ETIMEDOUT; - - if (status < 0) { - dev_err(vexpress_sysreg_dev, "error %d\n", status); - } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) { - mod_timer(&vexpress_sysreg_config_timer, - jiffies + usecs_to_jiffies(50)); - return; + struct platform_device *pdev = to_platform_device(dev); + struct vexpress_sysreg_config_func *func; + struct property *prop; + const __be32 *val = NULL; + __be32 energy_quirk[4]; + int num; + u32 site, position, dcc; + int err; + int i; + + if (dev->of_node) { + err = vexpress_config_get_topo(dev->of_node, &site, &position, + &dcc); + if (err) + return ERR_PTR(err); + + prop = of_find_property(dev->of_node, + "arm,vexpress-sysreg,func", NULL); + if (!prop) + return ERR_PTR(-EINVAL); + + num = prop->length / sizeof(u32) / 2; + val = prop->value; + } else { + if (pdev->num_resources != 1 || + pdev->resource[0].flags != IORESOURCE_BUS) + return ERR_PTR(-EFAULT); + + site = pdev->resource[0].start; + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_sysreg_get_master(); + position = 0; + dcc = 0; + num = 1; } - if (vexpress_sysreg_config_data) { - *vexpress_sysreg_config_data = readl(vexpress_sysreg_base + - SYS_CFGDATA); - dev_dbg(vexpress_sysreg_dev, "read data %x\n", - *vexpress_sysreg_config_data); - vexpress_sysreg_config_data = NULL; + /* + * "arm,vexpress-energy" function used to be described + * by its first device only, now it requires both + */ + if (num == 1 && of_device_is_compatible(dev->of_node, + "arm,vexpress-energy")) { + num = 2; + energy_quirk[0] = *val; + energy_quirk[2] = *val++; + energy_quirk[1] = *val; + energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); + val = energy_quirk; } - vexpress_config_complete(vexpress_sysreg_config_bridge, status); -} + func = kzalloc(sizeof(*func) + sizeof(*func->template) * num, + GFP_KERNEL); + if (!func) + return NULL; + func->num_templates = num; -void vexpress_sysreg_setup(struct device_node *node) -{ - if (WARN_ON(!vexpress_sysreg_base)) - return; + for (i = 0; i < num; i++) { + u32 function, device; - if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) - vexpress_master_site = VEXPRESS_SITE_DB2; + if (dev->of_node) { + function = be32_to_cpup(val++); + device = be32_to_cpup(val++); + } else { + function = pdev->resource[0].end; + device = pdev->id; + } + + dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", + func, site, position, dcc, + function, device); + + func->template[i] = SYS_CFGCTRL_DCC(dcc); + func->template[i] |= SYS_CFGCTRL_SITE(site); + func->template[i] |= SYS_CFGCTRL_POSITION(position); + func->template[i] |= SYS_CFGCTRL_FUNC(function); + func->template[i] |= SYS_CFGCTRL_DEVICE(device); + } + + vexpress_sysreg_regmap_config.max_register = num - 1; + + func->regmap = regmap_init(dev, NULL, func, + &vexpress_sysreg_regmap_config); + + if (IS_ERR(func->regmap)) + kfree(func); else - vexpress_master_site = VEXPRESS_SITE_DB1; + list_add(&func->list, &vexpress_sysreg_config_funcs); - vexpress_sysreg_config_bridge = vexpress_config_bridge_register( - node, &vexpress_sysreg_config_bridge_info); - WARN_ON(!vexpress_sysreg_config_bridge); + return func->regmap; +} + +static void vexpress_sysreg_config_regmap_exit(struct regmap *regmap, + void *context) +{ + struct vexpress_sysreg_config_func *func, *tmp; + + regmap_exit(regmap); + + list_for_each_entry_safe(func, tmp, &vexpress_sysreg_config_funcs, + list) { + if (func->regmap == regmap) { + list_del(&vexpress_sysreg_config_funcs); + kfree(func); + break; + } + } +} + +static struct vexpress_config_bridge_ops vexpress_sysreg_config_bridge_ops = { + .regmap_init = vexpress_sysreg_config_regmap_init, + .regmap_exit = vexpress_sysreg_config_regmap_exit, +}; + +int vexpress_sysreg_config_device_register(struct platform_device *pdev) +{ + pdev->dev.parent = vexpress_sysreg_config_bridge; + + return platform_device_register(pdev); } + void __init vexpress_sysreg_early_init(void __iomem *base) { vexpress_sysreg_base = base; - vexpress_sysreg_setup(NULL); + vexpress_config_set_master(vexpress_sysreg_get_master()); } void __init vexpress_sysreg_of_early_init(void) @@ -344,10 +358,14 @@ void __init vexpress_sysreg_of_early_init(void) return; node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); - if (node) { - vexpress_sysreg_base = of_iomap(node, 0); - vexpress_sysreg_setup(node); - } + if (WARN_ON(!node)) + return; + + vexpress_sysreg_base = of_iomap(node, 0); + if (WARN_ON(!vexpress_sysreg_base)) + return; + + vexpress_config_set_master(vexpress_sysreg_get_master()); } @@ -470,28 +488,22 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) return -EBUSY; } - if (!vexpress_sysreg_base) { + if (!vexpress_sysreg_base) vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - vexpress_sysreg_setup(pdev->dev.of_node); - } if (!vexpress_sysreg_base) { dev_err(&pdev->dev, "Failed to obtain base address!\n"); return -EFAULT; } - setup_timer(&vexpress_sysreg_config_timer, - vexpress_sysreg_config_complete, 0); - + vexpress_config_set_master(vexpress_sysreg_get_master()); vexpress_sysreg_dev = &pdev->dev; #ifdef CONFIG_GPIOLIB vexpress_sysreg_gpio_chip.dev = &pdev->dev; err = gpiochip_add(&vexpress_sysreg_gpio_chip); if (err) { - vexpress_config_bridge_unregister( - vexpress_sysreg_config_bridge); dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", err); return err; @@ -502,6 +514,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) sizeof(vexpress_sysreg_leds_pdata)); #endif + vexpress_sysreg_config_bridge = vexpress_config_bridge_register( + &pdev->dev, &vexpress_sysreg_config_bridge_ops, NULL); + WARN_ON(!vexpress_sysreg_config_bridge); + device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); return 0; @@ -522,7 +538,12 @@ static struct platform_driver vexpress_sysreg_driver = { static int __init vexpress_sysreg_init(void) { - vexpress_sysreg_of_early_init(); + struct device_node *node; + + /* Need the sysreg early, before any other device... */ + for_each_matching_node(node, vexpress_sysreg_match) + of_platform_device_create(node, NULL, NULL); + return platform_driver_register(&vexpress_sysreg_driver); } core_initcall(vexpress_sysreg_init); diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c index b95cf71ed695..4dc102e2b230 100644 --- a/drivers/power/reset/vexpress-poweroff.c +++ b/drivers/power/reset/vexpress-poweroff.c @@ -23,10 +23,10 @@ static void vexpress_reset_do(struct device *dev, const char *what) { int err = -ENOENT; - struct vexpress_config_func *func = dev_get_drvdata(dev); + struct regmap *reg = dev_get_drvdata(dev); - if (func) { - err = vexpress_config_write(func, 0, 0); + if (reg) { + err = regmap_write(reg, 0, 0); if (!err) mdelay(1000); } @@ -91,17 +91,17 @@ static int vexpress_reset_probe(struct platform_device *pdev) enum vexpress_reset_func func; const struct of_device_id *match = of_match_device(vexpress_reset_of_match, &pdev->dev); - struct vexpress_config_func *config_func; + struct regmap *regmap; if (match) func = (enum vexpress_reset_func)match->data; else func = pdev->id_entry->driver_data; - config_func = vexpress_config_func_get_by_dev(&pdev->dev); - if (!config_func) - return -EINVAL; - dev_set_drvdata(&pdev->dev, config_func); + regmap = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + dev_set_drvdata(&pdev->dev, regmap); switch (func) { case FUNC_SHUTDOWN: diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c index f3ae28a7e663..2863428813e4 100644 --- a/drivers/regulator/vexpress.c +++ b/drivers/regulator/vexpress.c @@ -26,14 +26,14 @@ struct vexpress_regulator { struct regulator_desc desc; struct regulator_dev *regdev; - struct vexpress_config_func *func; + struct regmap *regmap; }; static int vexpress_regulator_get_voltage(struct regulator_dev *regdev) { struct vexpress_regulator *reg = rdev_get_drvdata(regdev); u32 uV; - int err = vexpress_config_read(reg->func, 0, &uV); + int err = regmap_read(reg->regmap, 0, &uV); return err ? err : uV; } @@ -43,7 +43,7 @@ static int vexpress_regulator_set_voltage(struct regulator_dev *regdev, { struct vexpress_regulator *reg = rdev_get_drvdata(regdev); - return vexpress_config_write(reg->func, 0, min_uV); + return regmap_write(reg->regmap, 0, min_uV); } static struct regulator_ops vexpress_regulator_ops_ro = { @@ -57,22 +57,17 @@ static struct regulator_ops vexpress_regulator_ops = { static int vexpress_regulator_probe(struct platform_device *pdev) { - int err; struct vexpress_regulator *reg; struct regulator_init_data *init_data; struct regulator_config config = { }; reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL); - if (!reg) { - err = -ENOMEM; - goto error_kzalloc; - } + if (!reg) + return -ENOMEM; - reg->func = vexpress_config_func_get_by_dev(&pdev->dev); - if (!reg->func) { - err = -ENXIO; - goto error_get_func; - } + reg->regmap = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(reg->regmap)) + return PTR_ERR(reg->regmap); reg->desc.name = dev_name(&pdev->dev); reg->desc.type = REGULATOR_VOLTAGE; @@ -80,10 +75,8 @@ static int vexpress_regulator_probe(struct platform_device *pdev) reg->desc.continuous_voltage_range = true; init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); - if (!init_data) { - err = -EINVAL; - goto error_get_regulator_init_data; - } + if (!init_data) + return -EINVAL; init_data->constraints.apply_uV = 0; if (init_data->constraints.min_uV && init_data->constraints.max_uV) @@ -97,29 +90,11 @@ static int vexpress_regulator_probe(struct platform_device *pdev) config.of_node = pdev->dev.of_node; reg->regdev = devm_regulator_register(&pdev->dev, ®->desc, &config); - if (IS_ERR(reg->regdev)) { - err = PTR_ERR(reg->regdev); - goto error_regulator_register; - } + if (IS_ERR(reg->regdev)) + return PTR_ERR(reg->regdev); platform_set_drvdata(pdev, reg); - return 0; - -error_regulator_register: -error_get_regulator_init_data: - vexpress_config_func_put(reg->func); -error_get_func: -error_kzalloc: - return err; -} - -static int vexpress_regulator_remove(struct platform_device *pdev) -{ - struct vexpress_regulator *reg = platform_get_drvdata(pdev); - - vexpress_config_func_put(reg->func); - return 0; } @@ -130,7 +105,6 @@ static struct of_device_id vexpress_regulator_of_match[] = { static struct platform_driver vexpress_regulator_driver = { .probe = vexpress_regulator_probe, - .remove = vexpress_regulator_remove, .driver = { .name = DRVNAME, .owner = THIS_MODULE, diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index 617c01b8f74a..6b206ba6aa0e 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -15,16 +15,15 @@ #define _LINUX_VEXPRESS_H #include +#include #include +#include #define VEXPRESS_SITE_MB 0 #define VEXPRESS_SITE_DB1 1 #define VEXPRESS_SITE_DB2 2 #define VEXPRESS_SITE_MASTER 0xf -#define VEXPRESS_CONFIG_STATUS_DONE 0 -#define VEXPRESS_CONFIG_STATUS_WAIT 1 - #define VEXPRESS_GPIO_MMC_CARDIN 0 #define VEXPRESS_GPIO_MMC_WPROT 1 #define VEXPRESS_GPIO_FLASH_WPn 2 @@ -44,63 +43,30 @@ .flags = IORESOURCE_BUS, \ } -/* Config bridge API */ +/* Config infrastructure */ -/** - * struct vexpress_config_bridge_info - description of the platform - * configuration infrastructure bridge. - * - * @name: Bridge name - * - * @func_get: Obtains pointer to a configuration function for a given - * device or a Device Tree node, to be used with @func_put - * and @func_exec. The node pointer should take precedence - * over device pointer when both are passed. - * - * @func_put: Tells the bridge that the function will not be used any - * more, so all allocated resources can be released. - * - * @func_exec: Executes a configuration function read or write operation. - * The offset selects a 32 bit word of the value accessed. - * Must return VEXPRESS_CONFIG_STATUS_DONE when operation - * is finished immediately, VEXPRESS_CONFIG_STATUS_WAIT when - * will be completed in some time or negative value in case - * of error. - */ -struct vexpress_config_bridge_info { - const char *name; - void *(*func_get)(struct device *dev, struct device_node *node); - void (*func_put)(void *func); - int (*func_exec)(void *func, int offset, bool write, u32 *data); -}; +void vexpress_config_set_master(u32 site); +u32 vexpress_config_get_master(void); -struct vexpress_config_bridge; +void vexpress_config_lock(void *arg); +void vexpress_config_unlock(void *arg); -struct vexpress_config_bridge *vexpress_config_bridge_register( - struct device_node *node, - struct vexpress_config_bridge_info *info); -void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge); +int vexpress_config_get_topo(struct device_node *node, u32 *site, + u32 *position, u32 *dcc); -void vexpress_config_complete(struct vexpress_config_bridge *bridge, - int status); +/* Config bridge API */ -/* Config function API */ +struct vexpress_config_bridge_ops { + struct regmap * (*regmap_init)(struct device *dev, void *context); + void (*regmap_exit)(struct regmap *regmap, void *context); +}; -struct vexpress_config_func; +struct device *vexpress_config_bridge_register(struct device *parent, + struct vexpress_config_bridge_ops *ops, void *context); -struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, - struct device_node *node); -#define vexpress_config_func_get_by_dev(dev) \ - __vexpress_config_func_get(dev, NULL) -#define vexpress_config_func_get_by_node(node) \ - __vexpress_config_func_get(NULL, node) -void vexpress_config_func_put(struct vexpress_config_func *func); +/* Config regmap API */ -/* Both may sleep! */ -int vexpress_config_read(struct vexpress_config_func *func, int offset, - u32 *data); -int vexpress_config_write(struct vexpress_config_func *func, int offset, - u32 data); +struct regmap *devm_regmap_init_vexpress_config(struct device *dev); /* Platform control */ @@ -109,19 +75,12 @@ u32 vexpress_get_hbi(int site); void *vexpress_get_24mhz_clock_base(void); void vexpress_flags_set(u32 data); -#define vexpress_get_site_by_node(node) __vexpress_get_site(NULL, node) -#define vexpress_get_site_by_dev(dev) __vexpress_get_site(dev, NULL) -unsigned __vexpress_get_site(struct device *dev, struct device_node *node); - void vexpress_sysreg_early_init(void __iomem *base); void vexpress_sysreg_of_early_init(void); +int vexpress_sysreg_config_device_register(struct platform_device *pdev); /* Clocks */ -struct clk *vexpress_osc_setup(struct device *dev); -void vexpress_osc_of_setup(struct device_node *node); - void vexpress_clk_init(void __iomem *sp810_base); -void vexpress_clk_of_init(void); #endif -- cgit v1.2.3 From 29f9b6cf7bff6a118130163c848811e14f8022da Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 12 Feb 2014 10:47:10 +0000 Subject: mfd: syscon: Add platform data with a regmap config name Define syscon platform data structure that can be used to define a regmap config name. This is particularly useful in the regmap debugfs when there is more than one syscon device registered, to distinguish the register blocks. Signed-off-by: Pawel Moll Acked-by: Lee Jones --- drivers/mfd/syscon.c | 4 ++++ include/linux/platform_data/syscon.h | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 include/linux/platform_data/syscon.h (limited to 'include') diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index dbea55de4397..e2a04bb8bc1e 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -119,6 +120,7 @@ static struct regmap_config syscon_regmap_config = { static int syscon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct syscon_platform_data *pdata = dev_get_platdata(dev); struct syscon *syscon; struct resource *res; void __iomem *base; @@ -136,6 +138,8 @@ static int syscon_probe(struct platform_device *pdev) return -ENOMEM; syscon_regmap_config.max_register = res->end - res->start - 3; + if (pdata) + syscon_regmap_config.name = pdata->label; syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_regmap_config); if (IS_ERR(syscon->regmap)) { diff --git a/include/linux/platform_data/syscon.h b/include/linux/platform_data/syscon.h new file mode 100644 index 000000000000..2354c6fa3726 --- /dev/null +++ b/include/linux/platform_data/syscon.h @@ -0,0 +1,8 @@ +#ifndef PLATFORM_DATA_SYSCON_H +#define PLATFORM_DATA_SYSCON_H + +struct syscon_platform_data { + const char *label; +}; + +#endif -- cgit v1.2.3 From 974cc7b93441a0e78f030495436d1be7eb7c208d Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 23 Apr 2014 10:49:31 +0100 Subject: mfd: vexpress: Define the device as MFD cells This patch - finally, after over 6 months! :-( - addresses Samuel's request to split the vexpress-sysreg driver into smaller portions and define the device in a form of MFD cells: * LEDs code has been completely removed and replaced with "gpio-leds" nodes in the tree (referencing dedicated GPIO subnodes in sysreg - bindings documentation updated); this also better fits the reality as some variants of the motherboard don't have all the LEDs populated * syscfg bridge code has been extracted into a separate driver (placed in drivers/misc for no better place) * all the ID & MISC registers are defined as sysconf making them available for other drivers should they need to use them (and also to the user via /sys/kernel/debug/regmap which can be helpful in platform debugging) Signed-off-by: Pawel Moll Acked-by: Lee Jones --- .../devicetree/bindings/arm/vexpress-sysreg.txt | 36 +- arch/arm/boot/dts/vexpress-v2m-rs1.dtsi | 76 ++- arch/arm/boot/dts/vexpress-v2m.dtsi | 76 ++- arch/arm/mach-vexpress/ct-ca9x4.c | 2 +- arch/arm/mach-vexpress/v2m.c | 15 +- drivers/mfd/Kconfig | 15 +- drivers/mfd/Makefile | 2 +- drivers/mfd/vexpress-sysreg.c | 533 ++++++--------------- drivers/misc/Kconfig | 9 + drivers/misc/Makefile | 1 + drivers/misc/vexpress-syscfg.c | 324 +++++++++++++ include/linux/vexpress.h | 16 +- 12 files changed, 667 insertions(+), 438 deletions(-) create mode 100644 drivers/misc/vexpress-syscfg.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt index 57b423f78995..00318d083c9e 100644 --- a/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt +++ b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt @@ -8,6 +8,8 @@ interrupt generation, MMC and NOR Flash control etc. Required node properties: - compatible value : = "arm,vexpress,sysreg"; - reg : physical base address and the size of the registers window + +Deprecated properties, replaced by GPIO subnodes (see below): - gpio-controller : specifies that the node is a GPIO controller - #gpio-cells : size of the GPIO specifier, should be 2: - first cell is the pseudo-GPIO line number: @@ -16,12 +18,42 @@ Required node properties: 2 - NOR FLASH WPn - second cell can take standard GPIO flags (currently ignored). +Control registers providing pseudo-GPIO lines must be represented +by subnodes, each of them requiring the following properties: +- compatible value : one of + "arm,vexpress-sysreg,sys_led" + "arm,vexpress-sysreg,sys_mci" + "arm,vexpress-sysreg,sys_flash" +- gpio-controller : makes the node a GPIO controller +- #gpio-cells : size of the GPIO specifier, must be 2: + - first cell is the function number: + - for sys_led : 0..7 = LED 0..7 + - for sys_mci : 0 = MMC CARDIN, 1 = MMC WPROT + - for sys_flash : 0 = NOR FLASH WPn + - second cell can take standard GPIO flags (currently ignored). + Example: v2m_sysreg: sysreg@10000000 { compatible = "arm,vexpress-sysreg"; reg = <0x10000000 0x1000>; - gpio-controller; - #gpio-cells = <2>; + + v2m_led_gpios: sys_led@08 { + compatible = "arm,vexpress-sysreg,sys_led"; + gpio-controller; + #gpio-cells = <2>; + }; + + v2m_mmc_gpios: sys_mci@48 { + compatible = "arm,vexpress-sysreg,sys_mci"; + gpio-controller; + #gpio-cells = <2>; + }; + + v2m_flash_gpios: sys_flash@4c { + compatible = "arm,vexpress-sysreg,sys_flash"; + gpio-controller; + #gpio-cells = <2>; + }; }; This block also can also act a bridge to the platform's configuration diff --git a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi index ac870fb3fa0d..756c986995a3 100644 --- a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi +++ b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi @@ -74,8 +74,24 @@ v2m_sysreg: sysreg@010000 { compatible = "arm,vexpress-sysreg"; reg = <0x010000 0x1000>; - gpio-controller; - #gpio-cells = <2>; + + v2m_led_gpios: sys_led@08 { + compatible = "arm,vexpress-sysreg,sys_led"; + gpio-controller; + #gpio-cells = <2>; + }; + + v2m_mmc_gpios: sys_mci@48 { + compatible = "arm,vexpress-sysreg,sys_mci"; + gpio-controller; + #gpio-cells = <2>; + }; + + v2m_flash_gpios: sys_flash@4c { + compatible = "arm,vexpress-sysreg,sys_flash"; + gpio-controller; + #gpio-cells = <2>; + }; }; v2m_sysctl: sysctl@020000 { @@ -113,8 +129,8 @@ compatible = "arm,pl180", "arm,primecell"; reg = <0x050000 0x1000>; interrupts = <9 10>; - cd-gpios = <&v2m_sysreg 0 0>; - wp-gpios = <&v2m_sysreg 1 0>; + cd-gpios = <&v2m_mmc_gpios 0 0>; + wp-gpios = <&v2m_mmc_gpios 1 0>; max-frequency = <12000000>; vmmc-supply = <&v2m_fixed_3v3>; clocks = <&v2m_clk24mhz>, <&smbclk>; @@ -265,6 +281,58 @@ clock-output-names = "v2m:refclk32khz"; }; + leds { + compatible = "gpio-leds"; + + user@1 { + label = "v2m:green:user1"; + gpios = <&v2m_led_gpios 0 0>; + linux,default-trigger = "heartbeat"; + }; + + user@2 { + label = "v2m:green:user2"; + gpios = <&v2m_led_gpios 1 0>; + linux,default-trigger = "mmc0"; + }; + + user@3 { + label = "v2m:green:user3"; + gpios = <&v2m_led_gpios 2 0>; + linux,default-trigger = "cpu0"; + }; + + user@4 { + label = "v2m:green:user4"; + gpios = <&v2m_led_gpios 3 0>; + linux,default-trigger = "cpu1"; + }; + + user@5 { + label = "v2m:green:user5"; + gpios = <&v2m_led_gpios 4 0>; + linux,default-trigger = "cpu2"; + }; + + user@6 { + label = "v2m:green:user6"; + gpios = <&v2m_led_gpios 5 0>; + linux,default-trigger = "cpu3"; + }; + + user@7 { + label = "v2m:green:user7"; + gpios = <&v2m_led_gpios 6 0>; + linux,default-trigger = "cpu4"; + }; + + user@8 { + label = "v2m:green:user8"; + gpios = <&v2m_led_gpios 7 0>; + linux,default-trigger = "cpu5"; + }; + }; + mcc { compatible = "arm,vexpress,config-bus"; arm,vexpress,config-bridge = <&v2m_sysreg>; diff --git a/arch/arm/boot/dts/vexpress-v2m.dtsi b/arch/arm/boot/dts/vexpress-v2m.dtsi index f1420368355b..ba856d604fb7 100644 --- a/arch/arm/boot/dts/vexpress-v2m.dtsi +++ b/arch/arm/boot/dts/vexpress-v2m.dtsi @@ -73,8 +73,24 @@ v2m_sysreg: sysreg@00000 { compatible = "arm,vexpress-sysreg"; reg = <0x00000 0x1000>; - gpio-controller; - #gpio-cells = <2>; + + v2m_led_gpios: sys_led@08 { + compatible = "arm,vexpress-sysreg,sys_led"; + gpio-controller; + #gpio-cells = <2>; + }; + + v2m_mmc_gpios: sys_mci@48 { + compatible = "arm,vexpress-sysreg,sys_mci"; + gpio-controller; + #gpio-cells = <2>; + }; + + v2m_flash_gpios: sys_flash@4c { + compatible = "arm,vexpress-sysreg,sys_flash"; + gpio-controller; + #gpio-cells = <2>; + }; }; v2m_sysctl: sysctl@01000 { @@ -112,8 +128,8 @@ compatible = "arm,pl180", "arm,primecell"; reg = <0x05000 0x1000>; interrupts = <9 10>; - cd-gpios = <&v2m_sysreg 0 0>; - wp-gpios = <&v2m_sysreg 1 0>; + cd-gpios = <&v2m_mmc_gpios 0 0>; + wp-gpios = <&v2m_mmc_gpios 1 0>; max-frequency = <12000000>; vmmc-supply = <&v2m_fixed_3v3>; clocks = <&v2m_clk24mhz>, <&smbclk>; @@ -264,6 +280,58 @@ clock-output-names = "v2m:refclk32khz"; }; + leds { + compatible = "gpio-leds"; + + user@1 { + label = "v2m:green:user1"; + gpios = <&v2m_led_gpios 0 0>; + linux,default-trigger = "heartbeat"; + }; + + user@2 { + label = "v2m:green:user2"; + gpios = <&v2m_led_gpios 1 0>; + linux,default-trigger = "mmc0"; + }; + + user@3 { + label = "v2m:green:user3"; + gpios = <&v2m_led_gpios 2 0>; + linux,default-trigger = "cpu0"; + }; + + user@4 { + label = "v2m:green:user4"; + gpios = <&v2m_led_gpios 3 0>; + linux,default-trigger = "cpu1"; + }; + + user@5 { + label = "v2m:green:user5"; + gpios = <&v2m_led_gpios 4 0>; + linux,default-trigger = "cpu2"; + }; + + user@6 { + label = "v2m:green:user6"; + gpios = <&v2m_led_gpios 5 0>; + linux,default-trigger = "cpu3"; + }; + + user@7 { + label = "v2m:green:user7"; + gpios = <&v2m_led_gpios 6 0>; + linux,default-trigger = "cpu4"; + }; + + user@8 { + label = "v2m:green:user8"; + gpios = <&v2m_led_gpios 7 0>; + linux,default-trigger = "cpu5"; + }; + }; + mcc { compatible = "arm,vexpress,config-bus"; arm,vexpress,config-bridge = <&v2m_sysreg>; diff --git a/arch/arm/mach-vexpress/ct-ca9x4.c b/arch/arm/mach-vexpress/ct-ca9x4.c index 35e394aa00e5..494d70bfddad 100644 --- a/arch/arm/mach-vexpress/ct-ca9x4.c +++ b/arch/arm/mach-vexpress/ct-ca9x4.c @@ -160,7 +160,7 @@ static void __init ct_ca9x4_init(void) amba_device_register(ct_ca9x4_amba_devs[i], &iomem_resource); platform_device_register(&pmu_device); - vexpress_sysreg_config_device_register(&osc1_device); + vexpress_syscfg_device_register(&osc1_device); } #ifdef CONFIG_SMP diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index ac95220a5019..90f04c9b11d2 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -201,8 +201,9 @@ static struct platform_device v2m_cf_device = { static struct mmci_platform_data v2m_mmci_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, - .gpio_wp = VEXPRESS_GPIO_MMC_WPROT, - .gpio_cd = VEXPRESS_GPIO_MMC_CARDIN, + .status = vexpress_get_mci_cardin, + .gpio_cd = -1, + .gpio_wp = -1, }; static struct resource v2m_sysreg_resources[] = { @@ -351,10 +352,10 @@ static void __init v2m_init(void) for (i = 0; i < ARRAY_SIZE(v2m_amba_devs); i++) amba_device_register(v2m_amba_devs[i], &iomem_resource); - vexpress_sysreg_config_device_register(&v2m_muxfpga_device); - vexpress_sysreg_config_device_register(&v2m_shutdown_device); - vexpress_sysreg_config_device_register(&v2m_reboot_device); - vexpress_sysreg_config_device_register(&v2m_dvimode_device); + vexpress_syscfg_device_register(&v2m_muxfpga_device); + vexpress_syscfg_device_register(&v2m_shutdown_device); + vexpress_syscfg_device_register(&v2m_reboot_device); + vexpress_syscfg_device_register(&v2m_dvimode_device); ct_desc->init_tile(); } @@ -409,8 +410,6 @@ void __init v2m_dt_init_early(void) { u32 dt_hbi; - vexpress_sysreg_of_early_init(); - /* Confirm board type against DT property, if available */ if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { u32 hbi = vexpress_get_hbi(VEXPRESS_SITE_MASTER); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 33834120d057..490fd48a9541 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1227,12 +1227,17 @@ config MCP_UCB1200_TS endmenu -config VEXPRESS_CONFIG - bool "ARM Versatile Express platform infrastructure" - depends on ARM || ARM64 +config MFD_VEXPRESS_SYSREG + bool "Versatile Express System Registers" + depends on VEXPRESS_CONFIG + default y + select CLKSRC_MMIO + select GPIO_GENERIC_PLATFORM + select MFD_CORE + select MFD_SYSCON help - Platform configuration infrastructure for the ARM Ltd. - Versatile Express. + System Registers are the platform configuration block + on the ARM Ltd. Versatile Express board. endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9ba838eb5131..cec3487b539e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -161,7 +161,7 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o -obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-sysreg.o +obj-$(CONFIG_MFD_VEXPRESS_SYSREG) += vexpress-sysreg.o obj-$(CONFIG_MFD_RETU) += retu-mfd.o obj-$(CONFIG_MFD_AS3711) += as3711.o obj-$(CONFIG_MFD_AS3722) += as3722.o diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index b4138a7168db..952df843b6be 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -11,25 +11,22 @@ * Copyright (C) 2012 ARM Limited */ +#include #include -#include #include -#include +#include #include #include +#include #include -#include -#include #include #include -#include #include #define SYS_ID 0x000 #define SYS_SW 0x004 #define SYS_LED 0x008 #define SYS_100HZ 0x024 -#define SYS_FLAGS 0x030 #define SYS_FLAGSSET 0x030 #define SYS_FLAGSCLR 0x034 #define SYS_NVFLAGS 0x038 @@ -51,36 +48,32 @@ #define SYS_ID_HBI_SHIFT 16 #define SYS_PROCIDx_HBI_SHIFT 0 -#define SYS_LED_LED(n) (1 << (n)) - #define SYS_MCI_CARDIN (1 << 0) #define SYS_MCI_WPROT (1 << 1) -#define SYS_FLASH_WPn (1 << 0) - #define SYS_MISC_MASTERSITE (1 << 14) -#define SYS_CFGCTRL_START (1 << 31) -#define SYS_CFGCTRL_WRITE (1 << 30) -#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) -#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) -#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) -#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) -#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) -#define SYS_CFGSTAT_ERR (1 << 1) -#define SYS_CFGSTAT_COMPLETE (1 << 0) +static void __iomem *__vexpress_sysreg_base; +static void __iomem *vexpress_sysreg_base(void) +{ + if (!__vexpress_sysreg_base) { + struct device_node *node = of_find_compatible_node(NULL, NULL, + "arm,vexpress-sysreg"); -static void __iomem *vexpress_sysreg_base; -static struct device *vexpress_sysreg_dev; -static LIST_HEAD(vexpress_sysreg_config_funcs); -static struct device *vexpress_sysreg_config_bridge; + __vexpress_sysreg_base = of_iomap(node, 0); + } + + WARN_ON(!__vexpress_sysreg_base); + + return __vexpress_sysreg_base; +} static int vexpress_sysreg_get_master(void) { - if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) + if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE) return VEXPRESS_SITE_DB2; return VEXPRESS_SITE_DB1; @@ -88,8 +81,13 @@ static int vexpress_sysreg_get_master(void) void vexpress_flags_set(u32 data) { - writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); - writel(data, vexpress_sysreg_base + SYS_FLAGSSET); + writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR); + writel(data, vexpress_sysreg_base() + SYS_FLAGSSET); +} + +unsigned int vexpress_get_mci_cardin(struct device *dev) +{ + return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN; } u32 vexpress_get_procid(int site) @@ -97,7 +95,7 @@ u32 vexpress_get_procid(int site) if (site == VEXPRESS_SITE_MASTER) site = vexpress_sysreg_get_master(); - return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? + return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ? SYS_PROCID0 : SYS_PROCID1)); } @@ -107,7 +105,7 @@ u32 vexpress_get_hbi(int site) switch (site) { case VEXPRESS_SITE_MB: - id = readl(vexpress_sysreg_base + SYS_ID); + id = readl(vexpress_sysreg_base() + SYS_ID); return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; case VEXPRESS_SITE_MASTER: case VEXPRESS_SITE_DB1: @@ -121,406 +119,143 @@ u32 vexpress_get_hbi(int site) void __iomem *vexpress_get_24mhz_clock_base(void) { - return vexpress_sysreg_base + SYS_24MHZ; -} - - -struct vexpress_sysreg_config_func { - struct list_head list; - struct regmap *regmap; - int num_templates; - u32 template[0]; /* Keep this last */ -}; - -static int vexpress_sysreg_config_exec(struct vexpress_sysreg_config_func *func, - int index, bool write, u32 *data) -{ - u32 command, status; - int tries; - long timeout; - - if (WARN_ON(!vexpress_sysreg_base)) - return -ENOENT; - - if (WARN_ON(index > func->num_templates)) - return -EINVAL; - - command = readl(vexpress_sysreg_base + SYS_CFGCTRL); - if (WARN_ON(command & SYS_CFGCTRL_START)) - return -EBUSY; - - command = func->template[index]; - command |= SYS_CFGCTRL_START; - command |= write ? SYS_CFGCTRL_WRITE : 0; - - /* Use a canary for reads */ - if (!write) - *data = 0xdeadbeef; - - dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n", - command, *data); - writel(*data, vexpress_sysreg_base + SYS_CFGDATA); - writel(0, vexpress_sysreg_base + SYS_CFGSTAT); - writel(command, vexpress_sysreg_base + SYS_CFGCTRL); - mb(); - - /* The operation can take ages... Go to sleep, 100us initially */ - tries = 100; - timeout = 100; - do { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(usecs_to_jiffies(timeout)); - if (signal_pending(current)) - return -EINTR; - - status = readl(vexpress_sysreg_base + SYS_CFGSTAT); - if (status & SYS_CFGSTAT_ERR) - return -EFAULT; - - if (timeout > 20) - timeout -= 20; - } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); - if (WARN_ON_ONCE(!tries)) - return -ETIMEDOUT; - - if (!write) { - *data = readl(vexpress_sysreg_base + SYS_CFGDATA); - dev_dbg(vexpress_sysreg_dev, "func %p, read data %x\n", - func, *data); - } - - return 0; -} - -static int vexpress_sysreg_config_read(void *context, unsigned int index, - unsigned int *val) -{ - struct vexpress_sysreg_config_func *func = context; - - return vexpress_sysreg_config_exec(func, index, false, val); -} - -static int vexpress_sysreg_config_write(void *context, unsigned int index, - unsigned int val) -{ - struct vexpress_sysreg_config_func *func = context; - - return vexpress_sysreg_config_exec(func, index, true, &val); -} - -struct regmap_config vexpress_sysreg_regmap_config = { - .lock = vexpress_config_lock, - .unlock = vexpress_config_unlock, - .reg_bits = 32, - .val_bits = 32, - .reg_read = vexpress_sysreg_config_read, - .reg_write = vexpress_sysreg_config_write, - .reg_format_endian = REGMAP_ENDIAN_LITTLE, - .val_format_endian = REGMAP_ENDIAN_LITTLE, -}; - -static struct regmap *vexpress_sysreg_config_regmap_init(struct device *dev, - void *context) -{ - struct platform_device *pdev = to_platform_device(dev); - struct vexpress_sysreg_config_func *func; - struct property *prop; - const __be32 *val = NULL; - __be32 energy_quirk[4]; - int num; - u32 site, position, dcc; - int err; - int i; - - if (dev->of_node) { - err = vexpress_config_get_topo(dev->of_node, &site, &position, - &dcc); - if (err) - return ERR_PTR(err); - - prop = of_find_property(dev->of_node, - "arm,vexpress-sysreg,func", NULL); - if (!prop) - return ERR_PTR(-EINVAL); - - num = prop->length / sizeof(u32) / 2; - val = prop->value; - } else { - if (pdev->num_resources != 1 || - pdev->resource[0].flags != IORESOURCE_BUS) - return ERR_PTR(-EFAULT); - - site = pdev->resource[0].start; - if (site == VEXPRESS_SITE_MASTER) - site = vexpress_sysreg_get_master(); - position = 0; - dcc = 0; - num = 1; - } - - /* - * "arm,vexpress-energy" function used to be described - * by its first device only, now it requires both - */ - if (num == 1 && of_device_is_compatible(dev->of_node, - "arm,vexpress-energy")) { - num = 2; - energy_quirk[0] = *val; - energy_quirk[2] = *val++; - energy_quirk[1] = *val; - energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); - val = energy_quirk; - } - - func = kzalloc(sizeof(*func) + sizeof(*func->template) * num, - GFP_KERNEL); - if (!func) - return NULL; - - func->num_templates = num; - - for (i = 0; i < num; i++) { - u32 function, device; - - if (dev->of_node) { - function = be32_to_cpup(val++); - device = be32_to_cpup(val++); - } else { - function = pdev->resource[0].end; - device = pdev->id; - } - - dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", - func, site, position, dcc, - function, device); - - func->template[i] = SYS_CFGCTRL_DCC(dcc); - func->template[i] |= SYS_CFGCTRL_SITE(site); - func->template[i] |= SYS_CFGCTRL_POSITION(position); - func->template[i] |= SYS_CFGCTRL_FUNC(function); - func->template[i] |= SYS_CFGCTRL_DEVICE(device); - } - - vexpress_sysreg_regmap_config.max_register = num - 1; - - func->regmap = regmap_init(dev, NULL, func, - &vexpress_sysreg_regmap_config); - - if (IS_ERR(func->regmap)) - kfree(func); - else - list_add(&func->list, &vexpress_sysreg_config_funcs); - - return func->regmap; -} - -static void vexpress_sysreg_config_regmap_exit(struct regmap *regmap, - void *context) -{ - struct vexpress_sysreg_config_func *func, *tmp; - - regmap_exit(regmap); - - list_for_each_entry_safe(func, tmp, &vexpress_sysreg_config_funcs, - list) { - if (func->regmap == regmap) { - list_del(&vexpress_sysreg_config_funcs); - kfree(func); - break; - } - } -} - -static struct vexpress_config_bridge_ops vexpress_sysreg_config_bridge_ops = { - .regmap_init = vexpress_sysreg_config_regmap_init, - .regmap_exit = vexpress_sysreg_config_regmap_exit, -}; - -int vexpress_sysreg_config_device_register(struct platform_device *pdev) -{ - pdev->dev.parent = vexpress_sysreg_config_bridge; - - return platform_device_register(pdev); + return vexpress_sysreg_base() + SYS_24MHZ; } void __init vexpress_sysreg_early_init(void __iomem *base) { - vexpress_sysreg_base = base; - vexpress_config_set_master(vexpress_sysreg_get_master()); -} - -void __init vexpress_sysreg_of_early_init(void) -{ - struct device_node *node; - - if (vexpress_sysreg_base) - return; - - node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); - if (WARN_ON(!node)) - return; - - vexpress_sysreg_base = of_iomap(node, 0); - if (WARN_ON(!vexpress_sysreg_base)) - return; + __vexpress_sysreg_base = base; vexpress_config_set_master(vexpress_sysreg_get_master()); } -#ifdef CONFIG_GPIOLIB - -#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \ - [VEXPRESS_GPIO_##_name] = { \ - .reg = _reg, \ - .value = _reg##_##_value, \ - } +/* The sysreg block is just a random collection of various functions... */ -static struct vexpress_sysreg_gpio { - unsigned long reg; - u32 value; -} vexpress_sysreg_gpios[] = { - VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN), - VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT), - VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn), - VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)), - VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)), - VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)), - VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)), - VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)), - VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)), - VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)), - VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)), +static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = { + .label = "sys_id", }; -static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - return 0; -} - -static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, - unsigned offset) -{ - struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; - u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); - - return !!(reg_value & gpio->value); -} - -static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; - u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); - - if (value) - reg_value |= gpio->value; - else - reg_value &= ~gpio->value; - - writel(reg_value, vexpress_sysreg_base + gpio->reg); -} - -static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - vexpress_sysreg_gpio_set(chip, offset, value); - - return 0; -} - -static struct gpio_chip vexpress_sysreg_gpio_chip = { - .label = "vexpress-sysreg", - .direction_input = vexpress_sysreg_gpio_direction_input, - .direction_output = vexpress_sysreg_gpio_direction_output, - .get = vexpress_sysreg_gpio_get, - .set = vexpress_sysreg_gpio_set, - .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios), - .base = 0, +static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = { + .label = "sys_led", + .base = -1, + .ngpio = 8, }; - -#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \ - { \ - .name = "v2m:green:"_name, \ - .default_trigger = _default_trigger, \ - .gpio = VEXPRESS_GPIO_##_gpio, \ - } - -struct gpio_led vexpress_sysreg_leds[] = { - VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0), - VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1), - VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2), - VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3), - VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4), - VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5), - VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6), - VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7), +static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = { + .label = "sys_mci", + .base = -1, + .ngpio = 2, }; -struct gpio_led_platform_data vexpress_sysreg_leds_pdata = { - .num_leds = ARRAY_SIZE(vexpress_sysreg_leds), - .leds = vexpress_sysreg_leds, +static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = { + .label = "sys_flash", + .base = -1, + .ngpio = 1, }; -#endif - +static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = { + .label = "sys_misc", +}; -static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID)); -} +static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = { + .label = "sys_procid", +}; -DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL); +static struct mfd_cell vexpress_sysreg_cells[] = { + { + .name = "syscon", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM(SYS_ID, 0x4), + }, + .platform_data = &vexpress_sysreg_sys_id_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_id_pdata), + }, { + .name = "basic-mmio-gpio", + .of_compatible = "arm,vexpress-sysreg,sys_led", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"), + }, + .platform_data = &vexpress_sysreg_sys_led_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata), + }, { + .name = "basic-mmio-gpio", + .of_compatible = "arm,vexpress-sysreg,sys_mci", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"), + }, + .platform_data = &vexpress_sysreg_sys_mci_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata), + }, { + .name = "basic-mmio-gpio", + .of_compatible = "arm,vexpress-sysreg,sys_flash", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"), + }, + .platform_data = &vexpress_sysreg_sys_flash_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata), + }, { + .name = "syscon", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM(SYS_MISC, 0x4), + }, + .platform_data = &vexpress_sysreg_sys_misc_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata), + }, { + .name = "syscon", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM(SYS_PROCID0, 0x8), + }, + .platform_data = &vexpress_sysreg_sys_procid_pdata, + .pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata), + }, { + .name = "vexpress-syscfg", + .num_resources = 1, + .resources = (struct resource []) { + DEFINE_RES_MEM(SYS_CFGDATA, 0xc), + }, + } +}; static int vexpress_sysreg_probe(struct platform_device *pdev) { - int err; - struct resource *res = platform_get_resource(pdev, - IORESOURCE_MEM, 0); - - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "Failed to request memory region!\n"); - return -EBUSY; - } + struct resource *mem; + void __iomem *base; + struct bgpio_chip *mmc_gpio_chip; - if (!vexpress_sysreg_base) - vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -EINVAL; - if (!vexpress_sysreg_base) { - dev_err(&pdev->dev, "Failed to obtain base address!\n"); - return -EFAULT; - } + base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!base) + return -ENOMEM; vexpress_config_set_master(vexpress_sysreg_get_master()); - vexpress_sysreg_dev = &pdev->dev; - -#ifdef CONFIG_GPIOLIB - vexpress_sysreg_gpio_chip.dev = &pdev->dev; - err = gpiochip_add(&vexpress_sysreg_gpio_chip); - if (err) { - dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", - err); - return err; - } - platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", - PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, - sizeof(vexpress_sysreg_leds_pdata)); -#endif - - vexpress_sysreg_config_bridge = vexpress_config_bridge_register( - &pdev->dev, &vexpress_sysreg_config_bridge_ops, NULL); - WARN_ON(!vexpress_sysreg_config_bridge); - - device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); - - return 0; + /* + * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with + * older trees using sysreg node for MMC control lines. + */ + mmc_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*mmc_gpio_chip), + GFP_KERNEL); + if (!mmc_gpio_chip) + return -ENOMEM; + bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI, + NULL, NULL, NULL, NULL, 0); + mmc_gpio_chip->gc.ngpio = 2; + gpiochip_add(&mmc_gpio_chip->gc); + + return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, + vexpress_sysreg_cells, + ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL); } static const struct of_device_id vexpress_sysreg_match[] = { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8baff0effc7d..d9663ef90ce8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -515,6 +515,15 @@ config SRAM the genalloc API. It is supposed to be used for small on-chip SRAM areas found on many SoCs. +config VEXPRESS_SYSCFG + bool "Versatile Express System Configuration driver" + depends on VEXPRESS_CONFIG + default y + help + ARM Ltd. Versatile Express uses specialised platform configuration + bus. System Configuration interface is one of the possible means + of generating transactions on this bus. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7eb4b69580c0..d59ce1261b38 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,3 +55,4 @@ obj-$(CONFIG_SRAM) += sram.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ +obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c new file mode 100644 index 000000000000..73068e50e56d --- /dev/null +++ b/drivers/misc/vexpress-syscfg.c @@ -0,0 +1,324 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2014 ARM Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define SYS_CFGDATA 0x0 + +#define SYS_CFGCTRL 0x4 +#define SYS_CFGCTRL_START (1 << 31) +#define SYS_CFGCTRL_WRITE (1 << 30) +#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) +#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) +#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) +#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) +#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) + +#define SYS_CFGSTAT 0x8 +#define SYS_CFGSTAT_ERR (1 << 1) +#define SYS_CFGSTAT_COMPLETE (1 << 0) + + +struct vexpress_syscfg { + struct device *dev; + void __iomem *base; + struct list_head funcs; +}; + +struct vexpress_syscfg_func { + struct list_head list; + struct vexpress_syscfg *syscfg; + struct regmap *regmap; + int num_templates; + u32 template[0]; /* Keep it last! */ +}; + + +static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func, + int index, bool write, u32 *data) +{ + struct vexpress_syscfg *syscfg = func->syscfg; + u32 command, status; + int tries; + long timeout; + + if (WARN_ON(index > func->num_templates)) + return -EINVAL; + + command = readl(syscfg->base + SYS_CFGCTRL); + if (WARN_ON(command & SYS_CFGCTRL_START)) + return -EBUSY; + + command = func->template[index]; + command |= SYS_CFGCTRL_START; + command |= write ? SYS_CFGCTRL_WRITE : 0; + + /* Use a canary for reads */ + if (!write) + *data = 0xdeadbeef; + + dev_dbg(syscfg->dev, "func %p, command %x, data %x\n", + func, command, *data); + writel(*data, syscfg->base + SYS_CFGDATA); + writel(0, syscfg->base + SYS_CFGSTAT); + writel(command, syscfg->base + SYS_CFGCTRL); + mb(); + + /* The operation can take ages... Go to sleep, 100us initially */ + tries = 100; + timeout = 100; + do { + if (!irqs_disabled()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(usecs_to_jiffies(timeout)); + if (signal_pending(current)) + return -EINTR; + } else { + udelay(timeout); + } + + status = readl(syscfg->base + SYS_CFGSTAT); + if (status & SYS_CFGSTAT_ERR) + return -EFAULT; + + if (timeout > 20) + timeout -= 20; + } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); + if (WARN_ON_ONCE(!tries)) + return -ETIMEDOUT; + + if (!write) { + *data = readl(syscfg->base + SYS_CFGDATA); + dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data); + } + + return 0; +} + +static int vexpress_syscfg_read(void *context, unsigned int index, + unsigned int *val) +{ + struct vexpress_syscfg_func *func = context; + + return vexpress_syscfg_exec(func, index, false, val); +} + +static int vexpress_syscfg_write(void *context, unsigned int index, + unsigned int val) +{ + struct vexpress_syscfg_func *func = context; + + return vexpress_syscfg_exec(func, index, true, &val); +} + +struct regmap_config vexpress_syscfg_regmap_config = { + .lock = vexpress_config_lock, + .unlock = vexpress_config_unlock, + .reg_bits = 32, + .val_bits = 32, + .reg_read = vexpress_syscfg_read, + .reg_write = vexpress_syscfg_write, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + + +static struct regmap *vexpress_syscfg_regmap_init(struct device *dev, + void *context) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vexpress_syscfg *syscfg = context; + struct vexpress_syscfg_func *func; + struct property *prop; + const __be32 *val = NULL; + __be32 energy_quirk[4]; + int num; + u32 site, position, dcc; + int i; + + if (dev->of_node) { + int err = vexpress_config_get_topo(dev->of_node, &site, + &position, &dcc); + + if (err) + return ERR_PTR(err); + + prop = of_find_property(dev->of_node, + "arm,vexpress-sysreg,func", NULL); + if (!prop) + return ERR_PTR(-EINVAL); + + num = prop->length / sizeof(u32) / 2; + val = prop->value; + } else { + if (pdev->num_resources != 1 || + pdev->resource[0].flags != IORESOURCE_BUS) + return ERR_PTR(-EFAULT); + + site = pdev->resource[0].start; + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_config_get_master(); + position = 0; + dcc = 0; + num = 1; + } + + /* + * "arm,vexpress-energy" function used to be described + * by its first device only, now it requires both + */ + if (num == 1 && of_device_is_compatible(dev->of_node, + "arm,vexpress-energy")) { + num = 2; + energy_quirk[0] = *val; + energy_quirk[2] = *val++; + energy_quirk[1] = *val; + energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1); + val = energy_quirk; + } + + func = kzalloc(sizeof(*func) + sizeof(*func->template) * num, + GFP_KERNEL); + if (!func) + return NULL; + + func->syscfg = syscfg; + func->num_templates = num; + + for (i = 0; i < num; i++) { + u32 function, device; + + if (dev->of_node) { + function = be32_to_cpup(val++); + device = be32_to_cpup(val++); + } else { + function = pdev->resource[0].end; + device = pdev->id; + } + + dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n", + func, site, position, dcc, + function, device); + + func->template[i] = SYS_CFGCTRL_DCC(dcc); + func->template[i] |= SYS_CFGCTRL_SITE(site); + func->template[i] |= SYS_CFGCTRL_POSITION(position); + func->template[i] |= SYS_CFGCTRL_FUNC(function); + func->template[i] |= SYS_CFGCTRL_DEVICE(device); + } + + vexpress_syscfg_regmap_config.max_register = num - 1; + + func->regmap = regmap_init(dev, NULL, func, + &vexpress_syscfg_regmap_config); + + if (IS_ERR(func->regmap)) + kfree(func); + else + list_add(&func->list, &syscfg->funcs); + + return func->regmap; +} + +static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context) +{ + struct vexpress_syscfg *syscfg = context; + struct vexpress_syscfg_func *func, *tmp; + + regmap_exit(regmap); + + list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) { + if (func->regmap == regmap) { + list_del(&syscfg->funcs); + kfree(func); + break; + } + } +} + +static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = { + .regmap_init = vexpress_syscfg_regmap_init, + .regmap_exit = vexpress_syscfg_regmap_exit, +}; + + +/* Non-DT hack, to be gone... */ +static struct device *vexpress_syscfg_bridge; + +int vexpress_syscfg_device_register(struct platform_device *pdev) +{ + pdev->dev.parent = vexpress_syscfg_bridge; + + return platform_device_register(pdev); +} + + +int vexpress_syscfg_probe(struct platform_device *pdev) +{ + struct vexpress_syscfg *syscfg; + struct resource *res; + struct device *bridge; + + syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL); + if (!syscfg) + return -ENOMEM; + syscfg->dev = &pdev->dev; + INIT_LIST_HEAD(&syscfg->funcs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) + return -EBUSY; + + syscfg->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!syscfg->base) + return -EFAULT; + + /* Must use dev.parent (MFD), as that's where DT phandle points at... */ + bridge = vexpress_config_bridge_register(pdev->dev.parent, + &vexpress_syscfg_bridge_ops, syscfg); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + + /* Non-DT case */ + if (!pdev->dev.of_node) + vexpress_syscfg_bridge = bridge; + + return 0; +} + +static const struct platform_device_id vexpress_syscfg_id_table[] = { + { "vexpress-syscfg", }, + {}, +}; + +static struct platform_driver vexpress_syscfg_driver = { + .driver.name = "vexpress-syscfg", + .id_table = vexpress_syscfg_id_table, + .probe = vexpress_syscfg_probe, +}; + +static int __init vexpress_syscfg_init(void) +{ + return platform_driver_register(&vexpress_syscfg_driver); +} +core_initcall(vexpress_syscfg_init); diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index 6b206ba6aa0e..46636e3f43fd 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -24,18 +24,6 @@ #define VEXPRESS_SITE_DB2 2 #define VEXPRESS_SITE_MASTER 0xf -#define VEXPRESS_GPIO_MMC_CARDIN 0 -#define VEXPRESS_GPIO_MMC_WPROT 1 -#define VEXPRESS_GPIO_FLASH_WPn 2 -#define VEXPRESS_GPIO_LED0 3 -#define VEXPRESS_GPIO_LED1 4 -#define VEXPRESS_GPIO_LED2 5 -#define VEXPRESS_GPIO_LED3 6 -#define VEXPRESS_GPIO_LED4 7 -#define VEXPRESS_GPIO_LED5 8 -#define VEXPRESS_GPIO_LED6 9 -#define VEXPRESS_GPIO_LED7 10 - #define VEXPRESS_RES_FUNC(_site, _func) \ { \ .start = (_site), \ @@ -70,14 +58,14 @@ struct regmap *devm_regmap_init_vexpress_config(struct device *dev); /* Platform control */ +unsigned int vexpress_get_mci_cardin(struct device *dev); u32 vexpress_get_procid(int site); u32 vexpress_get_hbi(int site); void *vexpress_get_24mhz_clock_base(void); void vexpress_flags_set(u32 data); void vexpress_sysreg_early_init(void __iomem *base); -void vexpress_sysreg_of_early_init(void); -int vexpress_sysreg_config_device_register(struct platform_device *pdev); +int vexpress_syscfg_device_register(struct platform_device *pdev); /* Clocks */ -- cgit v1.2.3 From 6b2c31c71d6fa8896c5f3f2354d790a5bd3f0a1e Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 6 Feb 2014 14:33:44 +0000 Subject: ARM: vexpress: move HBI check to sysreg driver The last reason for static memory mapping is the HBI (board identification number) check early in the machine code. Moving the check to the sysreg driver makes it possible to completely remove the early mapping and init functions. Signed-off-by: Pawel Moll Acked-by: Lee Jones --- arch/arm/mach-vexpress/v2m.c | 49 ------------------------------------------- drivers/mfd/vexpress-sysreg.c | 30 ++++++++++---------------- include/linux/vexpress.h | 1 - 3 files changed, 11 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index d8b419bcf3c3..38f4f6f37770 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -370,53 +370,6 @@ MACHINE_START(VEXPRESS, "ARM-Versatile Express") .init_machine = v2m_init, MACHINE_END -static struct map_desc v2m_rs1_io_desc __initdata = { - .virtual = V2M_PERIPH, - .pfn = __phys_to_pfn(0x1c000000), - .length = SZ_2M, - .type = MT_DEVICE, -}; - -static int __init v2m_dt_scan_memory_map(unsigned long node, const char *uname, - int depth, void *data) -{ - const char **map = data; - - if (strcmp(uname, "motherboard") != 0) - return 0; - - *map = of_get_flat_dt_prop(node, "arm,v2m-memory-map", NULL); - - return 1; -} - -void __init v2m_dt_map_io(void) -{ - const char *map = NULL; - - of_scan_flat_dt(v2m_dt_scan_memory_map, &map); - - if (map && strcmp(map, "rs1") == 0) - iotable_init(&v2m_rs1_io_desc, 1); - else - iotable_init(v2m_io_desc, ARRAY_SIZE(v2m_io_desc)); -} - -void __init v2m_dt_init_early(void) -{ - u32 dt_hbi; - - /* Confirm board type against DT property, if available */ - if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { - u32 hbi = vexpress_get_hbi(VEXPRESS_SITE_MASTER); - - if (WARN_ON(dt_hbi != hbi)) - pr_warning("vexpress: DT HBI (%x) is not matching " - "hardware (%x)!\n", dt_hbi, hbi); - } -} - - static void __init v2m_dt_init(void) { l2x0_of_init(0x00400000, 0xfe0fffff); @@ -432,7 +385,5 @@ DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express") .dt_compat = v2m_dt_match, .smp = smp_ops(vexpress_smp_dt_ops), .smp_init = smp_init_ops(vexpress_smp_init_ops), - .map_io = v2m_dt_map_io, - .init_early = v2m_dt_init_early, .init_machine = v2m_dt_init, MACHINE_END diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 952df843b6be..9e21e4fc9599 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -45,7 +45,6 @@ #define SYS_CFGSTAT 0x0a8 #define SYS_HBI_MASK 0xfff -#define SYS_ID_HBI_SHIFT 16 #define SYS_PROCIDx_HBI_SHIFT 0 #define SYS_MCI_CARDIN (1 << 0) @@ -99,24 +98,6 @@ u32 vexpress_get_procid(int site) SYS_PROCID0 : SYS_PROCID1)); } -u32 vexpress_get_hbi(int site) -{ - u32 id; - - switch (site) { - case VEXPRESS_SITE_MB: - id = readl(vexpress_sysreg_base() + SYS_ID); - return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; - case VEXPRESS_SITE_MASTER: - case VEXPRESS_SITE_DB1: - case VEXPRESS_SITE_DB2: - id = vexpress_get_procid(site); - return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; - } - - return ~0; -} - void __iomem *vexpress_get_24mhz_clock_base(void) { return vexpress_sysreg_base() + SYS_24MHZ; @@ -229,6 +210,7 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) struct resource *mem; void __iomem *base; struct bgpio_chip *mmc_gpio_chip; + u32 dt_hbi; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) @@ -240,6 +222,16 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) vexpress_config_set_master(vexpress_sysreg_get_master()); + /* Confirm board type against DT property, if available */ + if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) { + u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER); + u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; + + if (WARN_ON(dt_hbi != hbi)) + dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n", + dt_hbi, hbi); + } + /* * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with * older trees using sysreg node for MMC control lines. diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index 46636e3f43fd..a4c9547aae64 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -60,7 +60,6 @@ struct regmap *devm_regmap_init_vexpress_config(struct device *dev); unsigned int vexpress_get_mci_cardin(struct device *dev); u32 vexpress_get_procid(int site); -u32 vexpress_get_hbi(int site); void *vexpress_get_24mhz_clock_base(void); void vexpress_flags_set(u32 data); -- cgit v1.2.3 From fcd77db07dd2b8d35e0db0d1209f2ce1bb05531e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 15 May 2014 13:43:14 -0400 Subject: net: Fix CONFIG_SYSCTL ifdef test. > include/net/ip.h:211:5: warning: "CONFIG_SYSCTL" is not defined [-Wundef] > #if CONFIG_SYSCTL > ^ Reported-by: Stephen Rothwell Signed-off-by: David S. Miller --- include/net/ip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 512bcd5dabac..2e4947895d75 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -208,7 +208,7 @@ static inline u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_o void inet_get_local_port_range(struct net *net, int *low, int *high); -#if CONFIG_SYSCTL +#ifdef CONFIG_SYSCTL static inline int inet_is_local_reserved_port(struct net *net, int port) { if (!net->ipv4.sysctl_local_reserved_ports) -- cgit v1.2.3 From 200b916f3575bdf11609cb447661b8d5957b0bbf Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 12 May 2014 15:11:20 -0700 Subject: rtnetlink: wait for unregistering devices in rtnl_link_unregister() From: Cong Wang commit 50624c934db18ab90 (net: Delay default_device_exit_batch until no devices are unregistering) introduced rtnl_lock_unregistering() for default_device_exit_batch(). Same race could happen we when rmmod a driver which calls rtnl_link_unregister() as we call dev->destructor without rtnl lock. For long term, I think we should clean up the mess of netdev_run_todo() and net namespce exit code. Cc: Eric W. Biederman Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 5 +++++ net/core/dev.c | 2 +- net/core/net_namespace.c | 2 +- net/core/rtnetlink.c | 33 ++++++++++++++++++++++++++++++++- 4 files changed, 39 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 8e3e66ac0a52..953937ea5233 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -4,6 +4,7 @@ #include #include +#include #include extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo); @@ -22,6 +23,10 @@ extern void rtnl_lock(void); extern void rtnl_unlock(void); extern int rtnl_trylock(void); extern int rtnl_is_locked(void); + +extern wait_queue_head_t netdev_unregistering_wq; +extern struct mutex net_mutex; + #ifdef CONFIG_PROVE_LOCKING extern int lockdep_rtnl_is_held(void); #else diff --git a/net/core/dev.c b/net/core/dev.c index c619b8641337..6da649bde4f7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5541,7 +5541,7 @@ static int dev_new_index(struct net *net) /* Delayed registration/unregisteration */ static LIST_HEAD(net_todo_list); -static DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq); +DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq); static void net_set_todo(struct net_device *dev) { diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 81d3a9a08453..7c8ffd974961 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -24,7 +24,7 @@ static LIST_HEAD(pernet_list); static struct list_head *first_device = &pernet_list; -static DEFINE_MUTEX(net_mutex); +DEFINE_MUTEX(net_mutex); LIST_HEAD(net_namespace_list); EXPORT_SYMBOL_GPL(net_namespace_list); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9837bebf93ce..2d8d8fcfa060 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -353,15 +353,46 @@ void __rtnl_link_unregister(struct rtnl_link_ops *ops) } EXPORT_SYMBOL_GPL(__rtnl_link_unregister); +/* Return with the rtnl_lock held when there are no network + * devices unregistering in any network namespace. + */ +static void rtnl_lock_unregistering_all(void) +{ + struct net *net; + bool unregistering; + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&netdev_unregistering_wq, &wait, + TASK_UNINTERRUPTIBLE); + unregistering = false; + rtnl_lock(); + for_each_net(net) { + if (net->dev_unreg_count > 0) { + unregistering = true; + break; + } + } + if (!unregistering) + break; + __rtnl_unlock(); + schedule(); + } + finish_wait(&netdev_unregistering_wq, &wait); +} + /** * rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink. * @ops: struct rtnl_link_ops * to unregister */ void rtnl_link_unregister(struct rtnl_link_ops *ops) { - rtnl_lock(); + /* Close the race with cleanup_net() */ + mutex_lock(&net_mutex); + rtnl_lock_unregistering_all(); __rtnl_link_unregister(ops); rtnl_unlock(); + mutex_unlock(&net_mutex); } EXPORT_SYMBOL_GPL(rtnl_link_unregister); -- cgit v1.2.3 From c3a6114f31600b94ee10ebf62e4d493b401ade87 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Wed, 14 May 2014 17:43:06 +0200 Subject: ieee802154: add definitions for link-layer security and header functions When dealing with 802.15.4, one often has to know the maximum payload size for a given packet. This depends on many factors, one of which is whether or not a security header is present in the frame. These definitions and functions provide an easy way for any upper layer to calculate the maximum payload size for a packet. The first obvious user for this is 6lowpan, which duplicates this calculation and gets it partially wrong because it ignores security headers. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154.h | 9 +++++++ include/net/ieee802154_netdev.h | 29 +++++++++++++++++++++++ net/ieee802154/header_ops.c | 52 +++++++++++++++++++++++++++++++++++------ 3 files changed, 83 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/ieee802154.h b/include/net/ieee802154.h index c7ae0ac528dc..0aa7122e8f15 100644 --- a/include/net/ieee802154.h +++ b/include/net/ieee802154.h @@ -79,6 +79,15 @@ #define IEEE802154_SCF_KEY_SHORT_INDEX 2 #define IEEE802154_SCF_KEY_HW_INDEX 3 +#define IEEE802154_SCF_SECLEVEL_NONE 0 +#define IEEE802154_SCF_SECLEVEL_MIC32 1 +#define IEEE802154_SCF_SECLEVEL_MIC64 2 +#define IEEE802154_SCF_SECLEVEL_MIC128 3 +#define IEEE802154_SCF_SECLEVEL_ENC 4 +#define IEEE802154_SCF_SECLEVEL_ENC_MIC32 5 +#define IEEE802154_SCF_SECLEVEL_ENC_MIC64 6 +#define IEEE802154_SCF_SECLEVEL_ENC_MIC128 7 + /* MAC footer size */ #define IEEE802154_MFR_SIZE 2 /* 2 octets */ diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 5a719ca892f4..6e4d3e1071b5 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -27,6 +27,7 @@ #ifndef IEEE802154_NETDEVICE_H #define IEEE802154_NETDEVICE_H +#include #include #include #include @@ -114,6 +115,34 @@ int ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr); int ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr); +/* parses the full 802.15.4 header a given skb and stores them into hdr, + * performing pan id decompression and length checks to be suitable for use in + * header_ops.parse + */ +int ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr); + +int ieee802154_max_payload(const struct ieee802154_hdr *hdr); + +static inline int +ieee802154_sechdr_authtag_len(const struct ieee802154_sechdr *sec) +{ + switch (sec->level) { + case IEEE802154_SCF_SECLEVEL_MIC32: + case IEEE802154_SCF_SECLEVEL_ENC_MIC32: + return 4; + case IEEE802154_SCF_SECLEVEL_MIC64: + case IEEE802154_SCF_SECLEVEL_ENC_MIC64: + return 8; + case IEEE802154_SCF_SECLEVEL_MIC128: + case IEEE802154_SCF_SECLEVEL_ENC_MIC128: + return 16; + case IEEE802154_SCF_SECLEVEL_NONE: + case IEEE802154_SCF_SECLEVEL_ENC: + default: + return 0; + } +} + static inline int ieee802154_hdr_length(struct sk_buff *skb) { struct ieee802154_hdr hdr; diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index bed42a48408c..c09294e39ca6 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -195,15 +195,16 @@ ieee802154_hdr_get_sechdr(const u8 *buf, struct ieee802154_sechdr *hdr) return pos; } +static int ieee802154_sechdr_lengths[4] = { + [IEEE802154_SCF_KEY_IMPLICIT] = 5, + [IEEE802154_SCF_KEY_INDEX] = 6, + [IEEE802154_SCF_KEY_SHORT_INDEX] = 10, + [IEEE802154_SCF_KEY_HW_INDEX] = 14, +}; + static int ieee802154_hdr_sechdr_len(u8 sc) { - switch (IEEE802154_SCF_KEY_ID_MODE(sc)) { - case IEEE802154_SCF_KEY_IMPLICIT: return 5; - case IEEE802154_SCF_KEY_INDEX: return 6; - case IEEE802154_SCF_KEY_SHORT_INDEX: return 10; - case IEEE802154_SCF_KEY_HW_INDEX: return 14; - default: return -EINVAL; - } + return ieee802154_sechdr_lengths[IEEE802154_SCF_KEY_ID_MODE(sc)]; } static int ieee802154_hdr_minlen(const struct ieee802154_hdr *hdr) @@ -285,3 +286,40 @@ ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr) return pos; } EXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs); + +int +ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr) +{ + const u8 *buf = skb_mac_header(skb); + int pos; + + pos = ieee802154_hdr_peek_addrs(skb, hdr); + if (pos < 0) + return -EINVAL; + + if (hdr->fc.security_enabled) { + u8 key_id_mode = IEEE802154_SCF_KEY_ID_MODE(*(buf + pos)); + int want = pos + ieee802154_sechdr_lengths[key_id_mode]; + + if (buf + want > skb_tail_pointer(skb)) + return -EINVAL; + + pos += ieee802154_hdr_get_sechdr(buf + pos, &hdr->sec); + } + + return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_peek); + +int ieee802154_max_payload(const struct ieee802154_hdr *hdr) +{ + int hlen = ieee802154_hdr_minlen(hdr); + + if (hdr->fc.security_enabled) { + hlen += ieee802154_sechdr_lengths[hdr->sec.key_id_mode] - 1; + hlen += ieee802154_sechdr_authtag_len(&hdr->sec); + } + + return IEEE802154_MTU - hlen - IEEE802154_MFR_SIZE; +} +EXPORT_SYMBOL_GPL(ieee802154_max_payload); -- cgit v1.2.3 From 32edc40ae65cf84e1ab69f6f8316ce81559e115d Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Wed, 14 May 2014 17:43:08 +0200 Subject: ieee802154: change _cb handling slightly The current mac_cb handling of ieee802154 is rather awkward and limited. Decompose the single flags field into multiple fields with the meanings of each subfield of the flags field to make future extensions (for example, link-layer security) easier. Also don't set the frame sequence number in upper layers, since that's a thing the MAC is supposed to set on frame transmit - we set it on header creation, but assuming that upper layers do not blindly duplicate our headers, this is fine. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 25 +++++++------------------ net/ieee802154/6lowpan_rtnl.c | 9 ++++----- net/ieee802154/dgram.c | 10 ++++------ net/mac802154/rx.c | 2 -- net/mac802154/wpan.c | 26 +++++++++++++------------- 5 files changed, 28 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 6e4d3e1071b5..bc9a7475e57e 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -222,8 +222,9 @@ static inline void ieee802154_addr_to_sa(struct ieee802154_addr_sa *sa, */ struct ieee802154_mac_cb { u8 lqi; - u8 flags; - u8 seq; + u8 type; + bool ackreq; + bool secen; struct ieee802154_addr source; struct ieee802154_addr dest; }; @@ -233,24 +234,12 @@ static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb) return (struct ieee802154_mac_cb *)skb->cb; } -#define MAC_CB_FLAG_TYPEMASK ((1 << 3) - 1) - -#define MAC_CB_FLAG_ACKREQ (1 << 3) -#define MAC_CB_FLAG_SECEN (1 << 4) - -static inline bool mac_cb_is_ackreq(struct sk_buff *skb) -{ - return mac_cb(skb)->flags & MAC_CB_FLAG_ACKREQ; -} - -static inline bool mac_cb_is_secen(struct sk_buff *skb) +static inline struct ieee802154_mac_cb *mac_cb_init(struct sk_buff *skb) { - return mac_cb(skb)->flags & MAC_CB_FLAG_SECEN; -} + BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb)); -static inline int mac_cb_type(struct sk_buff *skb) -{ - return mac_cb(skb)->flags & MAC_CB_FLAG_TYPEMASK; + memset(skb->cb, 0, sizeof(struct ieee802154_mac_cb)); + return mac_cb(skb); } #define IEEE802154_MAC_SCAN_ED 0 diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 0f5a69ed746d..d0191c53cbd8 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -92,6 +92,7 @@ static int lowpan_header_create(struct sk_buff *skb, const u8 *saddr = _saddr; const u8 *daddr = _daddr; struct ieee802154_addr sa, da; + struct ieee802154_mac_cb *cb = mac_cb_init(skb); /* TODO: * if this package isn't ipv6 one, where should it be routed? @@ -115,8 +116,7 @@ static int lowpan_header_create(struct sk_buff *skb, * from MAC subif of the 'dev' and 'real_dev' network devices, but * this isn't implemented in mainline yet, so currently we assign 0xff */ - mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; - mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); + cb->type = IEEE802154_FC_TYPE_DATA; /* prepare wpan address data */ sa.mode = IEEE802154_ADDR_LONG; @@ -135,11 +135,10 @@ static int lowpan_header_create(struct sk_buff *skb, } else { da.mode = IEEE802154_ADDR_LONG; da.extended_addr = ieee802154_devaddr_from_raw(daddr); - - /* request acknowledgment */ - mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; } + cb->ackreq = !lowpan_is_addr_broadcast(daddr); + return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, type, (void *)&da, (void *)&sa, 0); } diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 786437bc0c08..d95e2e1bdf54 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -209,6 +209,7 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, struct net_device *dev; unsigned int mtu; struct sk_buff *skb; + struct ieee802154_mac_cb *cb; struct dgram_sock *ro = dgram_sk(sk); int hlen, tlen; int err; @@ -249,18 +250,15 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, skb_reset_network_header(skb); - mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; - if (ro->want_ack) - mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; + cb = mac_cb_init(skb); + cb->type = IEEE802154_FC_TYPE_DATA; + cb->ackreq = ro->want_ack; - mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr, ro->bound ? &ro->src_addr : NULL, size); if (err < 0) goto out_skb; - skb_reset_mac_header(skb); - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); if (err < 0) goto out_skb; diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 03855b0677cc..0597b96dc9ba 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -59,8 +59,6 @@ mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi) skb->protocol = htons(ETH_P_IEEE802154); skb_reset_mac_header(skb); - BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb)); - if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { u16 crc; diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index cb34064d05f6..bb49e88c460f 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -192,15 +192,17 @@ static int mac802154_header_create(struct sk_buff *skb, { struct ieee802154_hdr hdr; struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_mac_cb *cb = mac_cb(skb); int hlen; if (!daddr) return -EINVAL; memset(&hdr.fc, 0, sizeof(hdr.fc)); - hdr.fc.type = mac_cb_type(skb); - hdr.fc.security_enabled = mac_cb_is_secen(skb); - hdr.fc.ack_request = mac_cb_is_ackreq(skb); + hdr.fc.type = cb->type; + hdr.fc.security_enabled = cb->secen; + hdr.fc.ack_request = cb->ackreq; + hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev); if (!saddr) { spin_lock_bh(&priv->mib_lock); @@ -391,12 +393,12 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) sdata->dev->stats.rx_packets++; sdata->dev->stats.rx_bytes += skb->len; - switch (mac_cb_type(skb)) { + switch (mac_cb(skb)->type) { case IEEE802154_FC_TYPE_DATA: return mac802154_process_data(sdata->dev, skb); default: pr_warn("ieee802154: bad frame received (type = %d)\n", - mac_cb_type(skb)); + mac_cb(skb)->type); kfree_skb(skb); return NET_RX_DROP; } @@ -423,6 +425,7 @@ static int mac802154_parse_frame_start(struct sk_buff *skb) { int hlen; struct ieee802154_hdr hdr; + struct ieee802154_mac_cb *cb = mac_cb_init(skb); hlen = ieee802154_hdr_pull(skb, &hdr); if (hlen < 0) @@ -433,18 +436,15 @@ static int mac802154_parse_frame_start(struct sk_buff *skb) pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr.fc), hdr.seq); - mac_cb(skb)->flags = hdr.fc.type; - - if (hdr.fc.ack_request) - mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; - if (hdr.fc.security_enabled) - mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN; + cb->type = hdr.fc.type; + cb->ackreq = hdr.fc.ack_request; + cb->secen = hdr.fc.security_enabled; mac802154_print_addr("destination", &hdr.dest); mac802154_print_addr("source", &hdr.source); - mac_cb(skb)->source = hdr.source; - mac_cb(skb)->dest = hdr.dest; + cb->source = hdr.source; + cb->dest = hdr.dest; if (hdr.fc.security_enabled) { u64 key; -- cgit v1.2.3 From 622582786c9e041d0bd52bde201787adeab249f8 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 13 May 2014 19:50:46 -0700 Subject: net: filter: x86: internal BPF JIT Maps all internal BPF instructions into x86_64 instructions. This patch replaces original BPF x64 JIT with internal BPF x64 JIT. sysctl net.core.bpf_jit_enable is reused as on/off switch. Performance: 1. old BPF JIT and internal BPF JIT generate equivalent x86_64 code. No performance difference is observed for filters that were JIT-able before Example assembler code for BPF filter "tcpdump port 22" original BPF -> old JIT: original BPF -> internal BPF -> new JIT: 0: push %rbp 0: push %rbp 1: mov %rsp,%rbp 1: mov %rsp,%rbp 4: sub $0x60,%rsp 4: sub $0x228,%rsp 8: mov %rbx,-0x8(%rbp) b: mov %rbx,-0x228(%rbp) // prologue 12: mov %r13,-0x220(%rbp) 19: mov %r14,-0x218(%rbp) 20: mov %r15,-0x210(%rbp) 27: xor %eax,%eax // clear A c: xor %ebx,%ebx 29: xor %r13,%r13 // clear X e: mov 0x68(%rdi),%r9d 2c: mov 0x68(%rdi),%r9d 12: sub 0x6c(%rdi),%r9d 30: sub 0x6c(%rdi),%r9d 16: mov 0xd8(%rdi),%r8 34: mov 0xd8(%rdi),%r10 3b: mov %rdi,%rbx 1d: mov $0xc,%esi 3e: mov $0xc,%esi 22: callq 0xffffffffe1021e15 43: callq 0xffffffffe102bd75 27: cmp $0x86dd,%eax 48: cmp $0x86dd,%rax 2c: jne 0x0000000000000069 4f: jne 0x000000000000009a 2e: mov $0x14,%esi 51: mov $0x14,%esi 33: callq 0xffffffffe1021e31 56: callq 0xffffffffe102bd91 38: cmp $0x84,%eax 5b: cmp $0x84,%rax 3d: je 0x0000000000000049 62: je 0x0000000000000074 3f: cmp $0x6,%eax 64: cmp $0x6,%rax 42: je 0x0000000000000049 68: je 0x0000000000000074 44: cmp $0x11,%eax 6a: cmp $0x11,%rax 47: jne 0x00000000000000c6 6e: jne 0x0000000000000117 49: mov $0x36,%esi 74: mov $0x36,%esi 4e: callq 0xffffffffe1021e15 79: callq 0xffffffffe102bd75 53: cmp $0x16,%eax 7e: cmp $0x16,%rax 56: je 0x00000000000000bf 82: je 0x0000000000000110 58: mov $0x38,%esi 88: mov $0x38,%esi 5d: callq 0xffffffffe1021e15 8d: callq 0xffffffffe102bd75 62: cmp $0x16,%eax 92: cmp $0x16,%rax 65: je 0x00000000000000bf 96: je 0x0000000000000110 67: jmp 0x00000000000000c6 98: jmp 0x0000000000000117 69: cmp $0x800,%eax 9a: cmp $0x800,%rax 6e: jne 0x00000000000000c6 a1: jne 0x0000000000000117 70: mov $0x17,%esi a3: mov $0x17,%esi 75: callq 0xffffffffe1021e31 a8: callq 0xffffffffe102bd91 7a: cmp $0x84,%eax ad: cmp $0x84,%rax 7f: je 0x000000000000008b b4: je 0x00000000000000c2 81: cmp $0x6,%eax b6: cmp $0x6,%rax 84: je 0x000000000000008b ba: je 0x00000000000000c2 86: cmp $0x11,%eax bc: cmp $0x11,%rax 89: jne 0x00000000000000c6 c0: jne 0x0000000000000117 8b: mov $0x14,%esi c2: mov $0x14,%esi 90: callq 0xffffffffe1021e15 c7: callq 0xffffffffe102bd75 95: test $0x1fff,%ax cc: test $0x1fff,%rax 99: jne 0x00000000000000c6 d3: jne 0x0000000000000117 d5: mov %rax,%r14 9b: mov $0xe,%esi d8: mov $0xe,%esi a0: callq 0xffffffffe1021e44 dd: callq 0xffffffffe102bd91 // MSH e2: and $0xf,%eax e5: shl $0x2,%eax e8: mov %rax,%r13 eb: mov %r14,%rax ee: mov %r13,%rsi a5: lea 0xe(%rbx),%esi f1: add $0xe,%esi a8: callq 0xffffffffe1021e0d f4: callq 0xffffffffe102bd6d ad: cmp $0x16,%eax f9: cmp $0x16,%rax b0: je 0x00000000000000bf fd: je 0x0000000000000110 ff: mov %r13,%rsi b2: lea 0x10(%rbx),%esi 102: add $0x10,%esi b5: callq 0xffffffffe1021e0d 105: callq 0xffffffffe102bd6d ba: cmp $0x16,%eax 10a: cmp $0x16,%rax bd: jne 0x00000000000000c6 10e: jne 0x0000000000000117 bf: mov $0xffff,%eax 110: mov $0xffff,%eax c4: jmp 0x00000000000000c8 115: jmp 0x000000000000011c c6: xor %eax,%eax 117: mov $0x0,%eax c8: mov -0x8(%rbp),%rbx 11c: mov -0x228(%rbp),%rbx // epilogue cc: leaveq 123: mov -0x220(%rbp),%r13 cd: retq 12a: mov -0x218(%rbp),%r14 131: mov -0x210(%rbp),%r15 138: leaveq 139: retq On fully cached SKBs both JITed functions take 12 nsec to execute. BPF interpreter executes the program in 30 nsec. The difference in generated assembler is due to the following: Old BPF imlements LDX_MSH instruction via sk_load_byte_msh() helper function inside bpf_jit.S. New JIT removes the helper and does it explicitly, so ldx_msh cost is the same for both JITs, but generated code looks longer. New JIT has 4 registers to save, so prologue/epilogue are larger, but the cost is within noise on x64. Old JIT checks whether first insn clears A and if not emits 'xor %eax,%eax'. New JIT clears %rax unconditionally. 2. old BPF JIT doesn't support ANC_NLATTR, ANC_PAY_OFFSET, ANC_RANDOM extensions. New JIT supports all BPF extensions. Performance of such filters improves 2-4 times depending on a filter. The longer the filter the higher performance gain. Synthetic benchmarks with many ancillary loads see 20x speedup which seems to be the maximum gain from JIT Notes: . net.core.bpf_jit_enable=2 + tools/net/bpf_jit_disasm is still functional and can be used to see generated assembler . there are two jit_compile() functions and code flow for classic filters is: sk_attach_filter() - load classic BPF bpf_jit_compile() - try to JIT from classic BPF sk_convert_filter() - convert classic to internal bpf_int_jit_compile() - JIT from internal BPF seccomp and tracing filters will just call bpf_int_jit_compile() Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- arch/x86/net/bpf_jit.S | 77 +-- arch/x86/net/bpf_jit_comp.c | 1314 +++++++++++++++++++++++-------------------- include/linux/filter.h | 3 + net/core/filter.c | 9 +- 4 files changed, 748 insertions(+), 655 deletions(-) (limited to 'include') diff --git a/arch/x86/net/bpf_jit.S b/arch/x86/net/bpf_jit.S index 01495755701b..6440221ced0d 100644 --- a/arch/x86/net/bpf_jit.S +++ b/arch/x86/net/bpf_jit.S @@ -12,13 +12,16 @@ /* * Calling convention : - * rdi : skb pointer + * rbx : skb pointer (callee saved) * esi : offset of byte(s) to fetch in skb (can be scratched) - * r8 : copy of skb->data + * r10 : copy of skb->data * r9d : hlen = skb->len - skb->data_len */ -#define SKBDATA %r8 +#define SKBDATA %r10 #define SKF_MAX_NEG_OFF $(-0x200000) /* SKF_LL_OFF from filter.h */ +#define MAX_BPF_STACK (512 /* from filter.h */ + \ + 32 /* space for rbx,r13,r14,r15 */ + \ + 8 /* space for skb_copy_bits */) sk_load_word: .globl sk_load_word @@ -68,53 +71,31 @@ sk_load_byte_positive_offset: movzbl (SKBDATA,%rsi),%eax ret -/** - * sk_load_byte_msh - BPF_S_LDX_B_MSH helper - * - * Implements BPF_S_LDX_B_MSH : ldxb 4*([offset]&0xf) - * Must preserve A accumulator (%eax) - * Inputs : %esi is the offset value - */ -sk_load_byte_msh: - .globl sk_load_byte_msh - test %esi,%esi - js bpf_slow_path_byte_msh_neg - -sk_load_byte_msh_positive_offset: - .globl sk_load_byte_msh_positive_offset - cmp %esi,%r9d /* if (offset >= hlen) goto bpf_slow_path_byte_msh */ - jle bpf_slow_path_byte_msh - movzbl (SKBDATA,%rsi),%ebx - and $15,%bl - shl $2,%bl - ret - /* rsi contains offset and can be scratched */ #define bpf_slow_path_common(LEN) \ - push %rdi; /* save skb */ \ + mov %rbx, %rdi; /* arg1 == skb */ \ push %r9; \ push SKBDATA; \ /* rsi already has offset */ \ mov $LEN,%ecx; /* len */ \ - lea -12(%rbp),%rdx; \ + lea - MAX_BPF_STACK + 32(%rbp),%rdx; \ call skb_copy_bits; \ test %eax,%eax; \ pop SKBDATA; \ - pop %r9; \ - pop %rdi + pop %r9; bpf_slow_path_word: bpf_slow_path_common(4) js bpf_error - mov -12(%rbp),%eax + mov - MAX_BPF_STACK + 32(%rbp),%eax bswap %eax ret bpf_slow_path_half: bpf_slow_path_common(2) js bpf_error - mov -12(%rbp),%ax + mov - MAX_BPF_STACK + 32(%rbp),%ax rol $8,%ax movzwl %ax,%eax ret @@ -122,21 +103,11 @@ bpf_slow_path_half: bpf_slow_path_byte: bpf_slow_path_common(1) js bpf_error - movzbl -12(%rbp),%eax - ret - -bpf_slow_path_byte_msh: - xchg %eax,%ebx /* dont lose A , X is about to be scratched */ - bpf_slow_path_common(1) - js bpf_error - movzbl -12(%rbp),%eax - and $15,%al - shl $2,%al - xchg %eax,%ebx + movzbl - MAX_BPF_STACK + 32(%rbp),%eax ret #define sk_negative_common(SIZE) \ - push %rdi; /* save skb */ \ + mov %rbx, %rdi; /* arg1 == skb */ \ push %r9; \ push SKBDATA; \ /* rsi already has offset */ \ @@ -145,10 +116,8 @@ bpf_slow_path_byte_msh: test %rax,%rax; \ pop SKBDATA; \ pop %r9; \ - pop %rdi; \ jz bpf_error - bpf_slow_path_word_neg: cmp SKF_MAX_NEG_OFF, %esi /* test range */ jl bpf_error /* offset lower -> error */ @@ -179,22 +148,12 @@ sk_load_byte_negative_offset: movzbl (%rax), %eax ret -bpf_slow_path_byte_msh_neg: - cmp SKF_MAX_NEG_OFF, %esi - jl bpf_error -sk_load_byte_msh_negative_offset: - .globl sk_load_byte_msh_negative_offset - xchg %eax,%ebx /* dont lose A , X is about to be scratched */ - sk_negative_common(1) - movzbl (%rax),%eax - and $15,%al - shl $2,%al - xchg %eax,%ebx - ret - bpf_error: # force a return 0 from jit handler - xor %eax,%eax - mov -8(%rbp),%rbx + xor %eax,%eax + mov - MAX_BPF_STACK(%rbp),%rbx + mov - MAX_BPF_STACK + 8(%rbp),%r13 + mov - MAX_BPF_STACK + 16(%rbp),%r14 + mov - MAX_BPF_STACK + 24(%rbp),%r15 leaveq ret diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index c5fa7c9cb665..92aef8fdac2f 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1,6 +1,7 @@ /* bpf_jit_comp.c : BPF JIT compiler * * Copyright (C) 2011-2013 Eric Dumazet (eric.dumazet@gmail.com) + * Internal BPF Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,28 +15,16 @@ #include #include -/* - * Conventions : - * EAX : BPF A accumulator - * EBX : BPF X accumulator - * RDI : pointer to skb (first argument given to JIT function) - * RBP : frame pointer (even if CONFIG_FRAME_POINTER=n) - * ECX,EDX,ESI : scratch registers - * r9d : skb->len - skb->data_len (headlen) - * r8 : skb->data - * -8(RBP) : saved RBX value - * -16(RBP)..-80(RBP) : BPF_MEMWORDS values - */ int bpf_jit_enable __read_mostly; /* * assembly code in arch/x86/net/bpf_jit.S */ -extern u8 sk_load_word[], sk_load_half[], sk_load_byte[], sk_load_byte_msh[]; +extern u8 sk_load_word[], sk_load_half[], sk_load_byte[]; extern u8 sk_load_word_positive_offset[], sk_load_half_positive_offset[]; -extern u8 sk_load_byte_positive_offset[], sk_load_byte_msh_positive_offset[]; +extern u8 sk_load_byte_positive_offset[]; extern u8 sk_load_word_negative_offset[], sk_load_half_negative_offset[]; -extern u8 sk_load_byte_negative_offset[], sk_load_byte_msh_negative_offset[]; +extern u8 sk_load_byte_negative_offset[]; static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) { @@ -56,30 +45,44 @@ static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) #define EMIT2(b1, b2) EMIT((b1) + ((b2) << 8), 2) #define EMIT3(b1, b2, b3) EMIT((b1) + ((b2) << 8) + ((b3) << 16), 3) #define EMIT4(b1, b2, b3, b4) EMIT((b1) + ((b2) << 8) + ((b3) << 16) + ((b4) << 24), 4) -#define EMIT1_off32(b1, off) do { EMIT1(b1); EMIT(off, 4);} while (0) - -#define CLEAR_A() EMIT2(0x31, 0xc0) /* xor %eax,%eax */ -#define CLEAR_X() EMIT2(0x31, 0xdb) /* xor %ebx,%ebx */ +#define EMIT1_off32(b1, off) \ + do {EMIT1(b1); EMIT(off, 4); } while (0) +#define EMIT2_off32(b1, b2, off) \ + do {EMIT2(b1, b2); EMIT(off, 4); } while (0) +#define EMIT3_off32(b1, b2, b3, off) \ + do {EMIT3(b1, b2, b3); EMIT(off, 4); } while (0) +#define EMIT4_off32(b1, b2, b3, b4, off) \ + do {EMIT4(b1, b2, b3, b4); EMIT(off, 4); } while (0) static inline bool is_imm8(int value) { return value <= 127 && value >= -128; } -static inline bool is_near(int offset) +static inline bool is_simm32(s64 value) { - return offset <= 127 && offset >= -128; + return value == (s64) (s32) value; } -#define EMIT_JMP(offset) \ -do { \ - if (offset) { \ - if (is_near(offset)) \ - EMIT2(0xeb, offset); /* jmp .+off8 */ \ - else \ - EMIT1_off32(0xe9, offset); /* jmp .+off32 */ \ - } \ -} while (0) +/* mov A, X */ +#define EMIT_mov(A, X) \ + do {if (A != X) \ + EMIT3(add_2mod(0x48, A, X), 0x89, add_2reg(0xC0, A, X)); \ + } while (0) + +static int bpf_size_to_x86_bytes(int bpf_size) +{ + if (bpf_size == BPF_W) + return 4; + else if (bpf_size == BPF_H) + return 2; + else if (bpf_size == BPF_B) + return 1; + else if (bpf_size == BPF_DW) + return 4; /* imm32 */ + else + return 0; +} /* list of x86 cond jumps opcodes (. + s8) * Add 0x10 (and an extra 0x0f) to generate far jumps (. + s32) @@ -90,27 +93,8 @@ do { \ #define X86_JNE 0x75 #define X86_JBE 0x76 #define X86_JA 0x77 - -#define EMIT_COND_JMP(op, offset) \ -do { \ - if (is_near(offset)) \ - EMIT2(op, offset); /* jxx .+off8 */ \ - else { \ - EMIT2(0x0f, op + 0x10); \ - EMIT(offset, 4); /* jxx .+off32 */ \ - } \ -} while (0) - -#define COND_SEL(CODE, TOP, FOP) \ - case CODE: \ - t_op = TOP; \ - f_op = FOP; \ - goto cond_branch - - -#define SEEN_DATAREF 1 /* might call external helpers */ -#define SEEN_XREG 2 /* ebx is used */ -#define SEEN_MEM 4 /* use mem[] for temporary storage */ +#define X86_JGE 0x7D +#define X86_JG 0x7F static inline void bpf_flush_icache(void *start, void *end) { @@ -125,26 +109,6 @@ static inline void bpf_flush_icache(void *start, void *end) #define CHOOSE_LOAD_FUNC(K, func) \ ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) -/* Helper to find the offset of pkt_type in sk_buff - * We want to make sure its still a 3bit field starting at a byte boundary. - */ -#define PKT_TYPE_MAX 7 -static int pkt_type_offset(void) -{ - struct sk_buff skb_probe = { - .pkt_type = ~0, - }; - char *ct = (char *)&skb_probe; - unsigned int off; - - for (off = 0; off < sizeof(struct sk_buff); off++) { - if (ct[off] == PKT_TYPE_MAX) - return off; - } - pr_err_once("Please fix pkt_type_offset(), as pkt_type couldn't be found\n"); - return -1; -} - struct bpf_binary_header { unsigned int pages; /* Note : for security reasons, bpf code will follow a randomly @@ -178,546 +142,715 @@ static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen, return header; } +/* pick a register outside of BPF range for JIT internal work */ +#define AUX_REG (MAX_BPF_REG + 1) + +/* the following table maps BPF registers to x64 registers. + * x64 register r12 is unused, since if used as base address register + * in load/store instructions, it always needs an extra byte of encoding + */ +static const int reg2hex[] = { + [BPF_REG_0] = 0, /* rax */ + [BPF_REG_1] = 7, /* rdi */ + [BPF_REG_2] = 6, /* rsi */ + [BPF_REG_3] = 2, /* rdx */ + [BPF_REG_4] = 1, /* rcx */ + [BPF_REG_5] = 0, /* r8 */ + [BPF_REG_6] = 3, /* rbx callee saved */ + [BPF_REG_7] = 5, /* r13 callee saved */ + [BPF_REG_8] = 6, /* r14 callee saved */ + [BPF_REG_9] = 7, /* r15 callee saved */ + [BPF_REG_FP] = 5, /* rbp readonly */ + [AUX_REG] = 3, /* r11 temp register */ +}; + +/* is_ereg() == true if BPF register 'reg' maps to x64 r8..r15 + * which need extra byte of encoding. + * rax,rcx,...,rbp have simpler encoding + */ +static inline bool is_ereg(u32 reg) +{ + if (reg == BPF_REG_5 || reg == AUX_REG || + (reg >= BPF_REG_7 && reg <= BPF_REG_9)) + return true; + else + return false; +} + +/* add modifiers if 'reg' maps to x64 registers r8..r15 */ +static inline u8 add_1mod(u8 byte, u32 reg) +{ + if (is_ereg(reg)) + byte |= 1; + return byte; +} + +static inline u8 add_2mod(u8 byte, u32 r1, u32 r2) +{ + if (is_ereg(r1)) + byte |= 1; + if (is_ereg(r2)) + byte |= 4; + return byte; +} + +/* encode dest register 'a_reg' into x64 opcode 'byte' */ +static inline u8 add_1reg(u8 byte, u32 a_reg) +{ + return byte + reg2hex[a_reg]; +} + +/* encode dest 'a_reg' and src 'x_reg' registers into x64 opcode 'byte' */ +static inline u8 add_2reg(u8 byte, u32 a_reg, u32 x_reg) +{ + return byte + reg2hex[a_reg] + (reg2hex[x_reg] << 3); +} + struct jit_context { unsigned int cleanup_addr; /* epilogue code offset */ - int pc_ret0; /* bpf index of first RET #0 instruction (if any) */ - u8 seen; + bool seen_ld_abs; }; static int do_jit(struct sk_filter *bpf_prog, int *addrs, u8 *image, int oldproglen, struct jit_context *ctx) { - const struct sock_filter *filter = bpf_prog->insns; - int flen = bpf_prog->len; + struct sock_filter_int *insn = bpf_prog->insnsi; + int insn_cnt = bpf_prog->len; u8 temp[64]; - u8 *prog; - int ilen, i, proglen; - int t_offset, f_offset; - u8 t_op, f_op, seen = 0; - u8 *func; - unsigned int cleanup_addr = ctx->cleanup_addr; - u8 seen_or_pass0 = ctx->seen; - - /* no prologue/epilogue for trivial filters (RET something) */ - proglen = 0; - prog = temp; + int i; + int proglen = 0; + u8 *prog = temp; + int stacksize = MAX_BPF_STACK + + 32 /* space for rbx, r13, r14, r15 */ + + 8 /* space for skb_copy_bits() buffer */; - if (seen_or_pass0) { - EMIT4(0x55, 0x48, 0x89, 0xe5); /* push %rbp; mov %rsp,%rbp */ - EMIT4(0x48, 0x83, 0xec, 96); /* subq $96,%rsp */ - /* note : must save %rbx in case bpf_error is hit */ - if (seen_or_pass0 & (SEEN_XREG | SEEN_DATAREF)) - EMIT4(0x48, 0x89, 0x5d, 0xf8); /* mov %rbx, -8(%rbp) */ - if (seen_or_pass0 & SEEN_XREG) - CLEAR_X(); /* make sure we dont leek kernel memory */ - - /* - * If this filter needs to access skb data, - * loads r9 and r8 with : - * r9 = skb->len - skb->data_len - * r8 = skb->data - */ - if (seen_or_pass0 & SEEN_DATAREF) { - if (offsetof(struct sk_buff, len) <= 127) - /* mov off8(%rdi),%r9d */ - EMIT4(0x44, 0x8b, 0x4f, offsetof(struct sk_buff, len)); - else { - /* mov off32(%rdi),%r9d */ - EMIT3(0x44, 0x8b, 0x8f); - EMIT(offsetof(struct sk_buff, len), 4); - } - if (is_imm8(offsetof(struct sk_buff, data_len))) - /* sub off8(%rdi),%r9d */ - EMIT4(0x44, 0x2b, 0x4f, offsetof(struct sk_buff, data_len)); - else { - EMIT3(0x44, 0x2b, 0x8f); - EMIT(offsetof(struct sk_buff, data_len), 4); - } + EMIT1(0x55); /* push rbp */ + EMIT3(0x48, 0x89, 0xE5); /* mov rbp,rsp */ - if (is_imm8(offsetof(struct sk_buff, data))) - /* mov off8(%rdi),%r8 */ - EMIT4(0x4c, 0x8b, 0x47, offsetof(struct sk_buff, data)); - else { - /* mov off32(%rdi),%r8 */ - EMIT3(0x4c, 0x8b, 0x87); - EMIT(offsetof(struct sk_buff, data), 4); - } + /* sub rsp, stacksize */ + EMIT3_off32(0x48, 0x81, 0xEC, stacksize); + + /* all classic BPF filters use R6(rbx) save it */ + + /* mov qword ptr [rbp-X],rbx */ + EMIT3_off32(0x48, 0x89, 0x9D, -stacksize); + + /* sk_convert_filter() maps classic BPF register X to R7 and uses R8 + * as temporary, so all tcpdump filters need to spill/fill R7(r13) and + * R8(r14). R9(r15) spill could be made conditional, but there is only + * one 'bpf_error' return path out of helper functions inside bpf_jit.S + * The overhead of extra spill is negligible for any filter other + * than synthetic ones. Therefore not worth adding complexity. + */ + + /* mov qword ptr [rbp-X],r13 */ + EMIT3_off32(0x4C, 0x89, 0xAD, -stacksize + 8); + /* mov qword ptr [rbp-X],r14 */ + EMIT3_off32(0x4C, 0x89, 0xB5, -stacksize + 16); + /* mov qword ptr [rbp-X],r15 */ + EMIT3_off32(0x4C, 0x89, 0xBD, -stacksize + 24); + + /* clear A and X registers */ + EMIT2(0x31, 0xc0); /* xor eax, eax */ + EMIT3(0x4D, 0x31, 0xED); /* xor r13, r13 */ + + if (ctx->seen_ld_abs) { + /* r9d : skb->len - skb->data_len (headlen) + * r10 : skb->data + */ + if (is_imm8(offsetof(struct sk_buff, len))) + /* mov %r9d, off8(%rdi) */ + EMIT4(0x44, 0x8b, 0x4f, + offsetof(struct sk_buff, len)); + else + /* mov %r9d, off32(%rdi) */ + EMIT3_off32(0x44, 0x8b, 0x8f, + offsetof(struct sk_buff, len)); + + if (is_imm8(offsetof(struct sk_buff, data_len))) + /* sub %r9d, off8(%rdi) */ + EMIT4(0x44, 0x2b, 0x4f, + offsetof(struct sk_buff, data_len)); + else + EMIT3_off32(0x44, 0x2b, 0x8f, + offsetof(struct sk_buff, data_len)); + + if (is_imm8(offsetof(struct sk_buff, data))) + /* mov %r10, off8(%rdi) */ + EMIT4(0x4c, 0x8b, 0x57, + offsetof(struct sk_buff, data)); + else + /* mov %r10, off32(%rdi) */ + EMIT3_off32(0x4c, 0x8b, 0x97, + offsetof(struct sk_buff, data)); + } + + for (i = 0; i < insn_cnt; i++, insn++) { + const s32 K = insn->imm; + u32 a_reg = insn->a_reg; + u32 x_reg = insn->x_reg; + u8 b1 = 0, b2 = 0, b3 = 0; + s64 jmp_offset; + u8 jmp_cond; + int ilen; + u8 *func; + + switch (insn->code) { + /* ALU */ + case BPF_ALU | BPF_ADD | BPF_X: + case BPF_ALU | BPF_SUB | BPF_X: + case BPF_ALU | BPF_AND | BPF_X: + case BPF_ALU | BPF_OR | BPF_X: + case BPF_ALU | BPF_XOR | BPF_X: + case BPF_ALU64 | BPF_ADD | BPF_X: + case BPF_ALU64 | BPF_SUB | BPF_X: + case BPF_ALU64 | BPF_AND | BPF_X: + case BPF_ALU64 | BPF_OR | BPF_X: + case BPF_ALU64 | BPF_XOR | BPF_X: + switch (BPF_OP(insn->code)) { + case BPF_ADD: b2 = 0x01; break; + case BPF_SUB: b2 = 0x29; break; + case BPF_AND: b2 = 0x21; break; + case BPF_OR: b2 = 0x09; break; + case BPF_XOR: b2 = 0x31; break; } - } + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_2mod(0x48, a_reg, x_reg)); + else if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT1(add_2mod(0x40, a_reg, x_reg)); + EMIT2(b2, add_2reg(0xC0, a_reg, x_reg)); + break; - switch (filter[0].code) { - case BPF_S_RET_K: - case BPF_S_LD_W_LEN: - case BPF_S_ANC_PROTOCOL: - case BPF_S_ANC_IFINDEX: - case BPF_S_ANC_MARK: - case BPF_S_ANC_RXHASH: - case BPF_S_ANC_CPU: - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: - case BPF_S_ANC_QUEUE: - case BPF_S_ANC_PKTTYPE: - case BPF_S_LD_W_ABS: - case BPF_S_LD_H_ABS: - case BPF_S_LD_B_ABS: - /* first instruction sets A register (or is RET 'constant') */ + /* mov A, X */ + case BPF_ALU64 | BPF_MOV | BPF_X: + EMIT_mov(a_reg, x_reg); break; - default: - /* make sure we dont leak kernel information to user */ - CLEAR_A(); /* A = 0 */ - } - for (i = 0; i < flen; i++) { - unsigned int K = filter[i].k; + /* mov32 A, X */ + case BPF_ALU | BPF_MOV | BPF_X: + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT1(add_2mod(0x40, a_reg, x_reg)); + EMIT2(0x89, add_2reg(0xC0, a_reg, x_reg)); + break; - switch (filter[i].code) { - case BPF_S_ALU_ADD_X: /* A += X; */ - seen |= SEEN_XREG; - EMIT2(0x01, 0xd8); /* add %ebx,%eax */ - break; - case BPF_S_ALU_ADD_K: /* A += K; */ - if (!K) - break; - if (is_imm8(K)) - EMIT3(0x83, 0xc0, K); /* add imm8,%eax */ - else - EMIT1_off32(0x05, K); /* add imm32,%eax */ - break; - case BPF_S_ALU_SUB_X: /* A -= X; */ - seen |= SEEN_XREG; - EMIT2(0x29, 0xd8); /* sub %ebx,%eax */ - break; - case BPF_S_ALU_SUB_K: /* A -= K */ - if (!K) - break; - if (is_imm8(K)) - EMIT3(0x83, 0xe8, K); /* sub imm8,%eax */ - else - EMIT1_off32(0x2d, K); /* sub imm32,%eax */ - break; - case BPF_S_ALU_MUL_X: /* A *= X; */ - seen |= SEEN_XREG; - EMIT3(0x0f, 0xaf, 0xc3); /* imul %ebx,%eax */ - break; - case BPF_S_ALU_MUL_K: /* A *= K */ - if (is_imm8(K)) - EMIT3(0x6b, 0xc0, K); /* imul imm8,%eax,%eax */ - else { - EMIT2(0x69, 0xc0); /* imul imm32,%eax */ - EMIT(K, 4); - } - break; - case BPF_S_ALU_DIV_X: /* A /= X; */ - seen |= SEEN_XREG; - EMIT2(0x85, 0xdb); /* test %ebx,%ebx */ - if (ctx->pc_ret0 > 0) { - /* addrs[pc_ret0 - 1] is start address of target - * (addrs[i] - 4) is the address following this jmp - * ("xor %edx,%edx; div %ebx" being 4 bytes long) - */ - EMIT_COND_JMP(X86_JE, addrs[ctx->pc_ret0 - 1] - - (addrs[i] - 4)); - } else { - EMIT_COND_JMP(X86_JNE, 2 + 5); - CLEAR_A(); - EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 4)); /* jmp .+off32 */ - } - EMIT4(0x31, 0xd2, 0xf7, 0xf3); /* xor %edx,%edx; div %ebx */ - break; - case BPF_S_ALU_MOD_X: /* A %= X; */ - seen |= SEEN_XREG; - EMIT2(0x85, 0xdb); /* test %ebx,%ebx */ - if (ctx->pc_ret0 > 0) { - /* addrs[pc_ret0 - 1] is start address of target - * (addrs[i] - 6) is the address following this jmp - * ("xor %edx,%edx; div %ebx;mov %edx,%eax" being 6 bytes long) - */ - EMIT_COND_JMP(X86_JE, addrs[ctx->pc_ret0 - 1] - - (addrs[i] - 6)); - } else { - EMIT_COND_JMP(X86_JNE, 2 + 5); - CLEAR_A(); - EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 6)); /* jmp .+off32 */ - } - EMIT2(0x31, 0xd2); /* xor %edx,%edx */ - EMIT2(0xf7, 0xf3); /* div %ebx */ - EMIT2(0x89, 0xd0); /* mov %edx,%eax */ - break; - case BPF_S_ALU_MOD_K: /* A %= K; */ - if (K == 1) { - CLEAR_A(); - break; - } - EMIT2(0x31, 0xd2); /* xor %edx,%edx */ - EMIT1(0xb9);EMIT(K, 4); /* mov imm32,%ecx */ - EMIT2(0xf7, 0xf1); /* div %ecx */ - EMIT2(0x89, 0xd0); /* mov %edx,%eax */ - break; - case BPF_S_ALU_DIV_K: /* A /= K */ - if (K == 1) - break; - EMIT2(0x31, 0xd2); /* xor %edx,%edx */ - EMIT1(0xb9);EMIT(K, 4); /* mov imm32,%ecx */ - EMIT2(0xf7, 0xf1); /* div %ecx */ - break; - case BPF_S_ALU_AND_X: - seen |= SEEN_XREG; - EMIT2(0x21, 0xd8); /* and %ebx,%eax */ - break; - case BPF_S_ALU_AND_K: - if (K >= 0xFFFFFF00) { - EMIT2(0x24, K & 0xFF); /* and imm8,%al */ - } else if (K >= 0xFFFF0000) { - EMIT2(0x66, 0x25); /* and imm16,%ax */ - EMIT(K, 2); - } else { - EMIT1_off32(0x25, K); /* and imm32,%eax */ - } - break; - case BPF_S_ALU_OR_X: - seen |= SEEN_XREG; - EMIT2(0x09, 0xd8); /* or %ebx,%eax */ - break; - case BPF_S_ALU_OR_K: - if (is_imm8(K)) - EMIT3(0x83, 0xc8, K); /* or imm8,%eax */ - else - EMIT1_off32(0x0d, K); /* or imm32,%eax */ - break; - case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */ - case BPF_S_ALU_XOR_X: - seen |= SEEN_XREG; - EMIT2(0x31, 0xd8); /* xor %ebx,%eax */ - break; - case BPF_S_ALU_XOR_K: /* A ^= K; */ - if (K == 0) - break; - if (is_imm8(K)) - EMIT3(0x83, 0xf0, K); /* xor imm8,%eax */ - else - EMIT1_off32(0x35, K); /* xor imm32,%eax */ - break; - case BPF_S_ALU_LSH_X: /* A <<= X; */ - seen |= SEEN_XREG; - EMIT4(0x89, 0xd9, 0xd3, 0xe0); /* mov %ebx,%ecx; shl %cl,%eax */ - break; - case BPF_S_ALU_LSH_K: - if (K == 0) - break; - else if (K == 1) - EMIT2(0xd1, 0xe0); /* shl %eax */ - else - EMIT3(0xc1, 0xe0, K); - break; - case BPF_S_ALU_RSH_X: /* A >>= X; */ - seen |= SEEN_XREG; - EMIT4(0x89, 0xd9, 0xd3, 0xe8); /* mov %ebx,%ecx; shr %cl,%eax */ - break; - case BPF_S_ALU_RSH_K: /* A >>= K; */ - if (K == 0) - break; - else if (K == 1) - EMIT2(0xd1, 0xe8); /* shr %eax */ - else - EMIT3(0xc1, 0xe8, K); - break; - case BPF_S_ALU_NEG: - EMIT2(0xf7, 0xd8); /* neg %eax */ - break; - case BPF_S_RET_K: - if (!K) { - if (ctx->pc_ret0 == -1) - ctx->pc_ret0 = i; - CLEAR_A(); - } else { - EMIT1_off32(0xb8, K); /* mov $imm32,%eax */ - } - /* fallinto */ - case BPF_S_RET_A: - if (seen_or_pass0) { - if (i != flen - 1) { - EMIT_JMP(cleanup_addr - addrs[i]); - break; - } - if (seen_or_pass0 & SEEN_XREG) - EMIT4(0x48, 0x8b, 0x5d, 0xf8); /* mov -8(%rbp),%rbx */ - EMIT1(0xc9); /* leaveq */ - } - EMIT1(0xc3); /* ret */ - break; - case BPF_S_MISC_TAX: /* X = A */ - seen |= SEEN_XREG; - EMIT2(0x89, 0xc3); /* mov %eax,%ebx */ - break; - case BPF_S_MISC_TXA: /* A = X */ - seen |= SEEN_XREG; - EMIT2(0x89, 0xd8); /* mov %ebx,%eax */ - break; - case BPF_S_LD_IMM: /* A = K */ - if (!K) - CLEAR_A(); - else - EMIT1_off32(0xb8, K); /* mov $imm32,%eax */ + /* neg A */ + case BPF_ALU | BPF_NEG: + case BPF_ALU64 | BPF_NEG: + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_1mod(0x48, a_reg)); + else if (is_ereg(a_reg)) + EMIT1(add_1mod(0x40, a_reg)); + EMIT2(0xF7, add_1reg(0xD8, a_reg)); + break; + + case BPF_ALU | BPF_ADD | BPF_K: + case BPF_ALU | BPF_SUB | BPF_K: + case BPF_ALU | BPF_AND | BPF_K: + case BPF_ALU | BPF_OR | BPF_K: + case BPF_ALU | BPF_XOR | BPF_K: + case BPF_ALU64 | BPF_ADD | BPF_K: + case BPF_ALU64 | BPF_SUB | BPF_K: + case BPF_ALU64 | BPF_AND | BPF_K: + case BPF_ALU64 | BPF_OR | BPF_K: + case BPF_ALU64 | BPF_XOR | BPF_K: + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_1mod(0x48, a_reg)); + else if (is_ereg(a_reg)) + EMIT1(add_1mod(0x40, a_reg)); + + switch (BPF_OP(insn->code)) { + case BPF_ADD: b3 = 0xC0; break; + case BPF_SUB: b3 = 0xE8; break; + case BPF_AND: b3 = 0xE0; break; + case BPF_OR: b3 = 0xC8; break; + case BPF_XOR: b3 = 0xF0; break; + } + + if (is_imm8(K)) + EMIT3(0x83, add_1reg(b3, a_reg), K); + else + EMIT2_off32(0x81, add_1reg(b3, a_reg), K); + break; + + case BPF_ALU64 | BPF_MOV | BPF_K: + /* optimization: if imm32 is positive, + * use 'mov eax, imm32' (which zero-extends imm32) + * to save 2 bytes + */ + if (K < 0) { + /* 'mov rax, imm32' sign extends imm32 */ + b1 = add_1mod(0x48, a_reg); + b2 = 0xC7; + b3 = 0xC0; + EMIT3_off32(b1, b2, add_1reg(b3, a_reg), K); break; - case BPF_S_LDX_IMM: /* X = K */ - seen |= SEEN_XREG; - if (!K) - CLEAR_X(); + } + + case BPF_ALU | BPF_MOV | BPF_K: + /* mov %eax, imm32 */ + if (is_ereg(a_reg)) + EMIT1(add_1mod(0x40, a_reg)); + EMIT1_off32(add_1reg(0xB8, a_reg), K); + break; + + /* A %= X, A /= X, A %= K, A /= K */ + case BPF_ALU | BPF_MOD | BPF_X: + case BPF_ALU | BPF_DIV | BPF_X: + case BPF_ALU | BPF_MOD | BPF_K: + case BPF_ALU | BPF_DIV | BPF_K: + case BPF_ALU64 | BPF_MOD | BPF_X: + case BPF_ALU64 | BPF_DIV | BPF_X: + case BPF_ALU64 | BPF_MOD | BPF_K: + case BPF_ALU64 | BPF_DIV | BPF_K: + EMIT1(0x50); /* push rax */ + EMIT1(0x52); /* push rdx */ + + if (BPF_SRC(insn->code) == BPF_X) + /* mov r11, X */ + EMIT_mov(AUX_REG, x_reg); + else + /* mov r11, K */ + EMIT3_off32(0x49, 0xC7, 0xC3, K); + + /* mov rax, A */ + EMIT_mov(BPF_REG_0, a_reg); + + /* xor edx, edx + * equivalent to 'xor rdx, rdx', but one byte less + */ + EMIT2(0x31, 0xd2); + + if (BPF_SRC(insn->code) == BPF_X) { + /* if (X == 0) return 0 */ + + /* cmp r11, 0 */ + EMIT4(0x49, 0x83, 0xFB, 0x00); + + /* jne .+9 (skip over pop, pop, xor and jmp) */ + EMIT2(X86_JNE, 1 + 1 + 2 + 5); + EMIT1(0x5A); /* pop rdx */ + EMIT1(0x58); /* pop rax */ + EMIT2(0x31, 0xc0); /* xor eax, eax */ + + /* jmp cleanup_addr + * addrs[i] - 11, because there are 11 bytes + * after this insn: div, mov, pop, pop, mov + */ + jmp_offset = ctx->cleanup_addr - (addrs[i] - 11); + EMIT1_off32(0xE9, jmp_offset); + } + + if (BPF_CLASS(insn->code) == BPF_ALU64) + /* div r11 */ + EMIT3(0x49, 0xF7, 0xF3); + else + /* div r11d */ + EMIT3(0x41, 0xF7, 0xF3); + + if (BPF_OP(insn->code) == BPF_MOD) + /* mov r11, rdx */ + EMIT3(0x49, 0x89, 0xD3); + else + /* mov r11, rax */ + EMIT3(0x49, 0x89, 0xC3); + + EMIT1(0x5A); /* pop rdx */ + EMIT1(0x58); /* pop rax */ + + /* mov A, r11 */ + EMIT_mov(a_reg, AUX_REG); + break; + + case BPF_ALU | BPF_MUL | BPF_K: + case BPF_ALU | BPF_MUL | BPF_X: + case BPF_ALU64 | BPF_MUL | BPF_K: + case BPF_ALU64 | BPF_MUL | BPF_X: + EMIT1(0x50); /* push rax */ + EMIT1(0x52); /* push rdx */ + + /* mov r11, A */ + EMIT_mov(AUX_REG, a_reg); + + if (BPF_SRC(insn->code) == BPF_X) + /* mov rax, X */ + EMIT_mov(BPF_REG_0, x_reg); + else + /* mov rax, K */ + EMIT3_off32(0x48, 0xC7, 0xC0, K); + + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_1mod(0x48, AUX_REG)); + else if (is_ereg(AUX_REG)) + EMIT1(add_1mod(0x40, AUX_REG)); + /* mul(q) r11 */ + EMIT2(0xF7, add_1reg(0xE0, AUX_REG)); + + /* mov r11, rax */ + EMIT_mov(AUX_REG, BPF_REG_0); + + EMIT1(0x5A); /* pop rdx */ + EMIT1(0x58); /* pop rax */ + + /* mov A, r11 */ + EMIT_mov(a_reg, AUX_REG); + break; + + /* shifts */ + case BPF_ALU | BPF_LSH | BPF_K: + case BPF_ALU | BPF_RSH | BPF_K: + case BPF_ALU | BPF_ARSH | BPF_K: + case BPF_ALU64 | BPF_LSH | BPF_K: + case BPF_ALU64 | BPF_RSH | BPF_K: + case BPF_ALU64 | BPF_ARSH | BPF_K: + if (BPF_CLASS(insn->code) == BPF_ALU64) + EMIT1(add_1mod(0x48, a_reg)); + else if (is_ereg(a_reg)) + EMIT1(add_1mod(0x40, a_reg)); + + switch (BPF_OP(insn->code)) { + case BPF_LSH: b3 = 0xE0; break; + case BPF_RSH: b3 = 0xE8; break; + case BPF_ARSH: b3 = 0xF8; break; + } + EMIT3(0xC1, add_1reg(b3, a_reg), K); + break; + + case BPF_ALU | BPF_END | BPF_FROM_BE: + switch (K) { + case 16: + /* emit 'ror %ax, 8' to swap lower 2 bytes */ + EMIT1(0x66); + if (is_ereg(a_reg)) + EMIT1(0x41); + EMIT3(0xC1, add_1reg(0xC8, a_reg), 8); + break; + case 32: + /* emit 'bswap eax' to swap lower 4 bytes */ + if (is_ereg(a_reg)) + EMIT2(0x41, 0x0F); else - EMIT1_off32(0xbb, K); /* mov $imm32,%ebx */ - break; - case BPF_S_LD_MEM: /* A = mem[K] : mov off8(%rbp),%eax */ - seen |= SEEN_MEM; - EMIT3(0x8b, 0x45, 0xf0 - K*4); + EMIT1(0x0F); + EMIT1(add_1reg(0xC8, a_reg)); break; - case BPF_S_LDX_MEM: /* X = mem[K] : mov off8(%rbp),%ebx */ - seen |= SEEN_XREG | SEEN_MEM; - EMIT3(0x8b, 0x5d, 0xf0 - K*4); - break; - case BPF_S_ST: /* mem[K] = A : mov %eax,off8(%rbp) */ - seen |= SEEN_MEM; - EMIT3(0x89, 0x45, 0xf0 - K*4); - break; - case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */ - seen |= SEEN_XREG | SEEN_MEM; - EMIT3(0x89, 0x5d, 0xf0 - K*4); - break; - case BPF_S_LD_W_LEN: /* A = skb->len; */ - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); - if (is_imm8(offsetof(struct sk_buff, len))) - /* mov off8(%rdi),%eax */ - EMIT3(0x8b, 0x47, offsetof(struct sk_buff, len)); - else { - EMIT2(0x8b, 0x87); - EMIT(offsetof(struct sk_buff, len), 4); - } - break; - case BPF_S_LDX_W_LEN: /* X = skb->len; */ - seen |= SEEN_XREG; - if (is_imm8(offsetof(struct sk_buff, len))) - /* mov off8(%rdi),%ebx */ - EMIT3(0x8b, 0x5f, offsetof(struct sk_buff, len)); - else { - EMIT2(0x8b, 0x9f); - EMIT(offsetof(struct sk_buff, len), 4); - } - break; - case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */ - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); - if (is_imm8(offsetof(struct sk_buff, protocol))) { - /* movzwl off8(%rdi),%eax */ - EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, protocol)); - } else { - EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */ - EMIT(offsetof(struct sk_buff, protocol), 4); - } - EMIT2(0x86, 0xc4); /* ntohs() : xchg %al,%ah */ - break; - case BPF_S_ANC_IFINDEX: - if (is_imm8(offsetof(struct sk_buff, dev))) { - /* movq off8(%rdi),%rax */ - EMIT4(0x48, 0x8b, 0x47, offsetof(struct sk_buff, dev)); - } else { - EMIT3(0x48, 0x8b, 0x87); /* movq off32(%rdi),%rax */ - EMIT(offsetof(struct sk_buff, dev), 4); - } - EMIT3(0x48, 0x85, 0xc0); /* test %rax,%rax */ - EMIT_COND_JMP(X86_JE, cleanup_addr - (addrs[i] - 6)); - BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4); - EMIT2(0x8b, 0x80); /* mov off32(%rax),%eax */ - EMIT(offsetof(struct net_device, ifindex), 4); - break; - case BPF_S_ANC_MARK: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); - if (is_imm8(offsetof(struct sk_buff, mark))) { - /* mov off8(%rdi),%eax */ - EMIT3(0x8b, 0x47, offsetof(struct sk_buff, mark)); - } else { - EMIT2(0x8b, 0x87); - EMIT(offsetof(struct sk_buff, mark), 4); - } - break; - case BPF_S_ANC_RXHASH: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); - if (is_imm8(offsetof(struct sk_buff, hash))) { - /* mov off8(%rdi),%eax */ - EMIT3(0x8b, 0x47, offsetof(struct sk_buff, hash)); - } else { - EMIT2(0x8b, 0x87); - EMIT(offsetof(struct sk_buff, hash), 4); - } - break; - case BPF_S_ANC_QUEUE: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); - if (is_imm8(offsetof(struct sk_buff, queue_mapping))) { - /* movzwl off8(%rdi),%eax */ - EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, queue_mapping)); - } else { - EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */ - EMIT(offsetof(struct sk_buff, queue_mapping), 4); - } - break; - case BPF_S_ANC_CPU: -#ifdef CONFIG_SMP - EMIT4(0x65, 0x8b, 0x04, 0x25); /* mov %gs:off32,%eax */ - EMIT((u32)(unsigned long)&cpu_number, 4); /* A = smp_processor_id(); */ -#else - CLEAR_A(); -#endif - break; - case BPF_S_ANC_VLAN_TAG: - case BPF_S_ANC_VLAN_TAG_PRESENT: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); - if (is_imm8(offsetof(struct sk_buff, vlan_tci))) { - /* movzwl off8(%rdi),%eax */ - EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, vlan_tci)); - } else { - EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */ - EMIT(offsetof(struct sk_buff, vlan_tci), 4); - } - BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000); - if (filter[i].code == BPF_S_ANC_VLAN_TAG) { - EMIT3(0x80, 0xe4, 0xef); /* and $0xef,%ah */ - } else { - EMIT3(0xc1, 0xe8, 0x0c); /* shr $0xc,%eax */ - EMIT3(0x83, 0xe0, 0x01); /* and $0x1,%eax */ - } - break; - case BPF_S_ANC_PKTTYPE: - { - int off = pkt_type_offset(); - - if (off < 0) - return -EINVAL; - if (is_imm8(off)) { - /* movzbl off8(%rdi),%eax */ - EMIT4(0x0f, 0xb6, 0x47, off); - } else { - /* movbl off32(%rdi),%eax */ - EMIT3(0x0f, 0xb6, 0x87); - EMIT(off, 4); - } - EMIT3(0x83, 0xe0, PKT_TYPE_MAX); /* and $0x7,%eax */ + case 64: + /* emit 'bswap rax' to swap 8 bytes */ + EMIT3(add_1mod(0x48, a_reg), 0x0F, + add_1reg(0xC8, a_reg)); break; } - case BPF_S_LD_W_ABS: - func = CHOOSE_LOAD_FUNC(K, sk_load_word); -common_load: seen |= SEEN_DATAREF; - t_offset = func - (image + addrs[i]); - EMIT1_off32(0xbe, K); /* mov imm32,%esi */ - EMIT1_off32(0xe8, t_offset); /* call */ - break; - case BPF_S_LD_H_ABS: - func = CHOOSE_LOAD_FUNC(K, sk_load_half); - goto common_load; - case BPF_S_LD_B_ABS: - func = CHOOSE_LOAD_FUNC(K, sk_load_byte); - goto common_load; - case BPF_S_LDX_B_MSH: - func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh); - seen |= SEEN_DATAREF | SEEN_XREG; - t_offset = func - (image + addrs[i]); - EMIT1_off32(0xbe, K); /* mov imm32,%esi */ - EMIT1_off32(0xe8, t_offset); /* call sk_load_byte_msh */ - break; - case BPF_S_LD_W_IND: - func = sk_load_word; -common_load_ind: seen |= SEEN_DATAREF | SEEN_XREG; - t_offset = func - (image + addrs[i]); - if (K) { - if (is_imm8(K)) { - EMIT3(0x8d, 0x73, K); /* lea imm8(%rbx), %esi */ - } else { - EMIT2(0x8d, 0xb3); /* lea imm32(%rbx),%esi */ - EMIT(K, 4); - } - } else { - EMIT2(0x89,0xde); /* mov %ebx,%esi */ - } - EMIT1_off32(0xe8, t_offset); /* call sk_load_xxx_ind */ - break; - case BPF_S_LD_H_IND: - func = sk_load_half; - goto common_load_ind; - case BPF_S_LD_B_IND: - func = sk_load_byte; - goto common_load_ind; - case BPF_S_JMP_JA: - t_offset = addrs[i + K] - addrs[i]; - EMIT_JMP(t_offset); - break; - COND_SEL(BPF_S_JMP_JGT_K, X86_JA, X86_JBE); - COND_SEL(BPF_S_JMP_JGE_K, X86_JAE, X86_JB); - COND_SEL(BPF_S_JMP_JEQ_K, X86_JE, X86_JNE); - COND_SEL(BPF_S_JMP_JSET_K,X86_JNE, X86_JE); - COND_SEL(BPF_S_JMP_JGT_X, X86_JA, X86_JBE); - COND_SEL(BPF_S_JMP_JGE_X, X86_JAE, X86_JB); - COND_SEL(BPF_S_JMP_JEQ_X, X86_JE, X86_JNE); - COND_SEL(BPF_S_JMP_JSET_X,X86_JNE, X86_JE); - -cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; - t_offset = addrs[i + filter[i].jt] - addrs[i]; - - /* same targets, can avoid doing the test :) */ - if (filter[i].jt == filter[i].jf) { - EMIT_JMP(t_offset); - break; - } + break; + + case BPF_ALU | BPF_END | BPF_FROM_LE: + break; + + /* ST: *(u8*)(a_reg + off) = imm */ + case BPF_ST | BPF_MEM | BPF_B: + if (is_ereg(a_reg)) + EMIT2(0x41, 0xC6); + else + EMIT1(0xC6); + goto st; + case BPF_ST | BPF_MEM | BPF_H: + if (is_ereg(a_reg)) + EMIT3(0x66, 0x41, 0xC7); + else + EMIT2(0x66, 0xC7); + goto st; + case BPF_ST | BPF_MEM | BPF_W: + if (is_ereg(a_reg)) + EMIT2(0x41, 0xC7); + else + EMIT1(0xC7); + goto st; + case BPF_ST | BPF_MEM | BPF_DW: + EMIT2(add_1mod(0x48, a_reg), 0xC7); + +st: if (is_imm8(insn->off)) + EMIT2(add_1reg(0x40, a_reg), insn->off); + else + EMIT1_off32(add_1reg(0x80, a_reg), insn->off); + + EMIT(K, bpf_size_to_x86_bytes(BPF_SIZE(insn->code))); + break; + + /* STX: *(u8*)(a_reg + off) = x_reg */ + case BPF_STX | BPF_MEM | BPF_B: + /* emit 'mov byte ptr [rax + off], al' */ + if (is_ereg(a_reg) || is_ereg(x_reg) || + /* have to add extra byte for x86 SIL, DIL regs */ + x_reg == BPF_REG_1 || x_reg == BPF_REG_2) + EMIT2(add_2mod(0x40, a_reg, x_reg), 0x88); + else + EMIT1(0x88); + goto stx; + case BPF_STX | BPF_MEM | BPF_H: + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT3(0x66, add_2mod(0x40, a_reg, x_reg), 0x89); + else + EMIT2(0x66, 0x89); + goto stx; + case BPF_STX | BPF_MEM | BPF_W: + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT2(add_2mod(0x40, a_reg, x_reg), 0x89); + else + EMIT1(0x89); + goto stx; + case BPF_STX | BPF_MEM | BPF_DW: + EMIT2(add_2mod(0x48, a_reg, x_reg), 0x89); +stx: if (is_imm8(insn->off)) + EMIT2(add_2reg(0x40, a_reg, x_reg), insn->off); + else + EMIT1_off32(add_2reg(0x80, a_reg, x_reg), + insn->off); + break; + + /* LDX: a_reg = *(u8*)(x_reg + off) */ + case BPF_LDX | BPF_MEM | BPF_B: + /* emit 'movzx rax, byte ptr [rax + off]' */ + EMIT3(add_2mod(0x48, x_reg, a_reg), 0x0F, 0xB6); + goto ldx; + case BPF_LDX | BPF_MEM | BPF_H: + /* emit 'movzx rax, word ptr [rax + off]' */ + EMIT3(add_2mod(0x48, x_reg, a_reg), 0x0F, 0xB7); + goto ldx; + case BPF_LDX | BPF_MEM | BPF_W: + /* emit 'mov eax, dword ptr [rax+0x14]' */ + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT2(add_2mod(0x40, x_reg, a_reg), 0x8B); + else + EMIT1(0x8B); + goto ldx; + case BPF_LDX | BPF_MEM | BPF_DW: + /* emit 'mov rax, qword ptr [rax+0x14]' */ + EMIT2(add_2mod(0x48, x_reg, a_reg), 0x8B); +ldx: /* if insn->off == 0 we can save one extra byte, but + * special case of x86 r13 which always needs an offset + * is not worth the hassle + */ + if (is_imm8(insn->off)) + EMIT2(add_2reg(0x40, x_reg, a_reg), insn->off); + else + EMIT1_off32(add_2reg(0x80, x_reg, a_reg), + insn->off); + break; + + /* STX XADD: lock *(u32*)(a_reg + off) += x_reg */ + case BPF_STX | BPF_XADD | BPF_W: + /* emit 'lock add dword ptr [rax + off], eax' */ + if (is_ereg(a_reg) || is_ereg(x_reg)) + EMIT3(0xF0, add_2mod(0x40, a_reg, x_reg), 0x01); + else + EMIT2(0xF0, 0x01); + goto xadd; + case BPF_STX | BPF_XADD | BPF_DW: + EMIT3(0xF0, add_2mod(0x48, a_reg, x_reg), 0x01); +xadd: if (is_imm8(insn->off)) + EMIT2(add_2reg(0x40, a_reg, x_reg), insn->off); + else + EMIT1_off32(add_2reg(0x80, a_reg, x_reg), + insn->off); + break; + + /* call */ + case BPF_JMP | BPF_CALL: + func = (u8 *) __bpf_call_base + K; + jmp_offset = func - (image + addrs[i]); + if (ctx->seen_ld_abs) { + EMIT2(0x41, 0x52); /* push %r10 */ + EMIT2(0x41, 0x51); /* push %r9 */ + /* need to adjust jmp offset, since + * pop %r9, pop %r10 take 4 bytes after call insn + */ + jmp_offset += 4; + } + if (!K || !is_simm32(jmp_offset)) { + pr_err("unsupported bpf func %d addr %p image %p\n", + K, func, image); + return -EINVAL; + } + EMIT1_off32(0xE8, jmp_offset); + if (ctx->seen_ld_abs) { + EMIT2(0x41, 0x59); /* pop %r9 */ + EMIT2(0x41, 0x5A); /* pop %r10 */ + } + break; + + /* cond jump */ + case BPF_JMP | BPF_JEQ | BPF_X: + case BPF_JMP | BPF_JNE | BPF_X: + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JSGT | BPF_X: + case BPF_JMP | BPF_JSGE | BPF_X: + /* cmp a_reg, x_reg */ + EMIT3(add_2mod(0x48, a_reg, x_reg), 0x39, + add_2reg(0xC0, a_reg, x_reg)); + goto emit_cond_jmp; + + case BPF_JMP | BPF_JSET | BPF_X: + /* test a_reg, x_reg */ + EMIT3(add_2mod(0x48, a_reg, x_reg), 0x85, + add_2reg(0xC0, a_reg, x_reg)); + goto emit_cond_jmp; + + case BPF_JMP | BPF_JSET | BPF_K: + /* test a_reg, imm32 */ + EMIT1(add_1mod(0x48, a_reg)); + EMIT2_off32(0xF7, add_1reg(0xC0, a_reg), K); + goto emit_cond_jmp; + + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JNE | BPF_K: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JSGT | BPF_K: + case BPF_JMP | BPF_JSGE | BPF_K: + /* cmp a_reg, imm8/32 */ + EMIT1(add_1mod(0x48, a_reg)); + + if (is_imm8(K)) + EMIT3(0x83, add_1reg(0xF8, a_reg), K); + else + EMIT2_off32(0x81, add_1reg(0xF8, a_reg), K); + +emit_cond_jmp: /* convert BPF opcode to x86 */ + switch (BPF_OP(insn->code)) { + case BPF_JEQ: + jmp_cond = X86_JE; + break; + case BPF_JSET: + case BPF_JNE: + jmp_cond = X86_JNE; + break; + case BPF_JGT: + /* GT is unsigned '>', JA in x86 */ + jmp_cond = X86_JA; + break; + case BPF_JGE: + /* GE is unsigned '>=', JAE in x86 */ + jmp_cond = X86_JAE; + break; + case BPF_JSGT: + /* signed '>', GT in x86 */ + jmp_cond = X86_JG; + break; + case BPF_JSGE: + /* signed '>=', GE in x86 */ + jmp_cond = X86_JGE; + break; + default: /* to silence gcc warning */ + return -EFAULT; + } + jmp_offset = addrs[i + insn->off] - addrs[i]; + if (is_imm8(jmp_offset)) { + EMIT2(jmp_cond, jmp_offset); + } else if (is_simm32(jmp_offset)) { + EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset); + } else { + pr_err("cond_jmp gen bug %llx\n", jmp_offset); + return -EFAULT; + } + + break; - switch (filter[i].code) { - case BPF_S_JMP_JGT_X: - case BPF_S_JMP_JGE_X: - case BPF_S_JMP_JEQ_X: - seen |= SEEN_XREG; - EMIT2(0x39, 0xd8); /* cmp %ebx,%eax */ - break; - case BPF_S_JMP_JSET_X: - seen |= SEEN_XREG; - EMIT2(0x85, 0xd8); /* test %ebx,%eax */ - break; - case BPF_S_JMP_JEQ_K: - if (K == 0) { - EMIT2(0x85, 0xc0); /* test %eax,%eax */ - break; - } - case BPF_S_JMP_JGT_K: - case BPF_S_JMP_JGE_K: - if (K <= 127) - EMIT3(0x83, 0xf8, K); /* cmp imm8,%eax */ + case BPF_JMP | BPF_JA: + jmp_offset = addrs[i + insn->off] - addrs[i]; + if (!jmp_offset) + /* optimize out nop jumps */ + break; +emit_jmp: + if (is_imm8(jmp_offset)) { + EMIT2(0xEB, jmp_offset); + } else if (is_simm32(jmp_offset)) { + EMIT1_off32(0xE9, jmp_offset); + } else { + pr_err("jmp gen bug %llx\n", jmp_offset); + return -EFAULT; + } + break; + + case BPF_LD | BPF_IND | BPF_W: + func = sk_load_word; + goto common_load; + case BPF_LD | BPF_ABS | BPF_W: + func = CHOOSE_LOAD_FUNC(K, sk_load_word); +common_load: ctx->seen_ld_abs = true; + jmp_offset = func - (image + addrs[i]); + if (!func || !is_simm32(jmp_offset)) { + pr_err("unsupported bpf func %d addr %p image %p\n", + K, func, image); + return -EINVAL; + } + if (BPF_MODE(insn->code) == BPF_ABS) { + /* mov %esi, imm32 */ + EMIT1_off32(0xBE, K); + } else { + /* mov %rsi, x_reg */ + EMIT_mov(BPF_REG_2, x_reg); + if (K) { + if (is_imm8(K)) + /* add %esi, imm8 */ + EMIT3(0x83, 0xC6, K); else - EMIT1_off32(0x3d, K); /* cmp imm32,%eax */ - break; - case BPF_S_JMP_JSET_K: - if (K <= 0xFF) - EMIT2(0xa8, K); /* test imm8,%al */ - else if (!(K & 0xFFFF00FF)) - EMIT3(0xf6, 0xc4, K >> 8); /* test imm8,%ah */ - else if (K <= 0xFFFF) { - EMIT2(0x66, 0xa9); /* test imm16,%ax */ - EMIT(K, 2); - } else { - EMIT1_off32(0xa9, K); /* test imm32,%eax */ - } - break; + /* add %esi, imm32 */ + EMIT2_off32(0x81, 0xC6, K); } - if (filter[i].jt != 0) { - if (filter[i].jf && f_offset) - t_offset += is_near(f_offset) ? 2 : 5; - EMIT_COND_JMP(t_op, t_offset); - if (filter[i].jf) - EMIT_JMP(f_offset); - break; - } - EMIT_COND_JMP(f_op, f_offset); - break; + } + /* skb pointer is in R6 (%rbx), it will be copied into + * %rdi if skb_copy_bits() call is necessary. + * sk_load_* helpers also use %r10 and %r9d. + * See bpf_jit.S + */ + EMIT1_off32(0xE8, jmp_offset); /* call */ + break; + + case BPF_LD | BPF_IND | BPF_H: + func = sk_load_half; + goto common_load; + case BPF_LD | BPF_ABS | BPF_H: + func = CHOOSE_LOAD_FUNC(K, sk_load_half); + goto common_load; + case BPF_LD | BPF_IND | BPF_B: + func = sk_load_byte; + goto common_load; + case BPF_LD | BPF_ABS | BPF_B: + func = CHOOSE_LOAD_FUNC(K, sk_load_byte); + goto common_load; + + case BPF_JMP | BPF_EXIT: + if (i != insn_cnt - 1) { + jmp_offset = ctx->cleanup_addr - addrs[i]; + goto emit_jmp; + } + /* update cleanup_addr */ + ctx->cleanup_addr = proglen; + /* mov rbx, qword ptr [rbp-X] */ + EMIT3_off32(0x48, 0x8B, 0x9D, -stacksize); + /* mov r13, qword ptr [rbp-X] */ + EMIT3_off32(0x4C, 0x8B, 0xAD, -stacksize + 8); + /* mov r14, qword ptr [rbp-X] */ + EMIT3_off32(0x4C, 0x8B, 0xB5, -stacksize + 16); + /* mov r15, qword ptr [rbp-X] */ + EMIT3_off32(0x4C, 0x8B, 0xBD, -stacksize + 24); + + EMIT1(0xC9); /* leave */ + EMIT1(0xC3); /* ret */ + break; + default: - /* hmm, too complex filter, give up with jit compiler */ + /* By design x64 JIT should support all BPF instructions + * This error will be seen if new instruction was added + * to interpreter, but not to JIT + * or if there is junk in sk_filter + */ + pr_err("bpf_jit: unknown opcode %02x\n", insn->code); return -EINVAL; } + ilen = prog - temp; if (image) { if (unlikely(proglen + ilen > oldproglen)) { - pr_err("bpb_jit_compile fatal error\n"); + pr_err("bpf_jit_compile fatal error\n"); return -EFAULT; } memcpy(image + proglen, temp, ilen); @@ -726,21 +859,14 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; addrs[i] = proglen; prog = temp; } - /* last bpf instruction is always a RET : - * use it to give the cleanup instruction(s) addr - */ - ctx->cleanup_addr = proglen - 1; /* ret */ - if (seen_or_pass0) - ctx->cleanup_addr -= 1; /* leaveq */ - if (seen_or_pass0 & SEEN_XREG) - ctx->cleanup_addr -= 4; /* mov -8(%rbp),%rbx */ - - ctx->seen = seen; - return proglen; } void bpf_jit_compile(struct sk_filter *prog) +{ +} + +void bpf_int_jit_compile(struct sk_filter *prog) { struct bpf_binary_header *header = NULL; int proglen, oldproglen = 0; @@ -768,8 +894,6 @@ void bpf_jit_compile(struct sk_filter *prog) addrs[i] = proglen; } ctx.cleanup_addr = proglen; - ctx.seen = SEEN_XREG | SEEN_DATAREF | SEEN_MEM; - ctx.pc_ret0 = -1; for (pass = 0; pass < 10; pass++) { proglen = do_jit(prog, addrs, image, oldproglen, &ctx); diff --git a/include/linux/filter.h b/include/linux/filter.h index 4457b383961c..9d5ae0a2c954 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -207,6 +207,9 @@ void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to); void sk_filter_charge(struct sock *sk, struct sk_filter *fp); void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp); +u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); +void bpf_int_jit_compile(struct sk_filter *fp); + #ifdef CONFIG_BPF_JIT #include #include diff --git a/net/core/filter.c b/net/core/filter.c index c442a0d7d0f7..32c5b44c537e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1524,6 +1524,10 @@ out_err: return ERR_PTR(err); } +void __weak bpf_int_jit_compile(struct sk_filter *prog) +{ +} + static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, struct sock *sk) { @@ -1544,9 +1548,12 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, /* JIT compiler couldn't process this filter, so do the * internal BPF translation for the optimized interpreter. */ - if (!fp->jited) + if (!fp->jited) { fp = __sk_migrate_filter(fp, sk); + /* Probe if internal BPF can be jit-ed */ + bpf_int_jit_compile(fp); + } return fp; } -- cgit v1.2.3 From a0bf37edb4d34c21bdaa19a1624378924b917491 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 6 Oct 2013 20:23:49 +0200 Subject: HSI: method to unregister clients from an hsi port This exports a method to unregister all clients from an hsi port. Signed-off-by: Sebastian Reichel Reviewed-by: Pavel Machek Tested-By: Ivaylo Dimitrov --- drivers/hsi/hsi.c | 10 ++++++++++ include/linux/hsi/hsi.h | 1 + 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index 749f7b5c8179..e96a9874b1a4 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -129,6 +129,16 @@ static void hsi_port_release(struct device *dev) kfree(to_hsi_port(dev)); } +/** + * hsi_unregister_port - Unregister an HSI port + * @port: The HSI port to unregister + */ +void hsi_port_unregister_clients(struct hsi_port *port) +{ + device_for_each_child(&port->device, NULL, hsi_remove_client); +} +EXPORT_SYMBOL_GPL(hsi_port_unregister_clients); + /** * hsi_unregister_controller - Unregister an HSI controller * @hsi: The HSI controller to register diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index 39bfd5b89077..5a9f1210ed22 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -282,6 +282,7 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags); void hsi_put_controller(struct hsi_controller *hsi); int hsi_register_controller(struct hsi_controller *hsi); void hsi_unregister_controller(struct hsi_controller *hsi); +void hsi_port_unregister_clients(struct hsi_port *port); static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi, void *data) -- cgit v1.2.3 From a088cf161cc87b39e83c7c53b9f239773422d212 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 28 Mar 2014 22:48:23 +0100 Subject: HSI: Add channel resource support to HSI clients Make HSI channel ids platform data, which can be provided by platform data. Signed-off-by: Sebastian Reichel Tested-By: Ivaylo Dimitrov --- drivers/hsi/clients/hsi_char.c | 12 +++++------ drivers/hsi/hsi.c | 46 +++++++++++++++++++++++++++++++++++++++++- include/linux/hsi/hsi.h | 24 ++++++++++++++++++---- 3 files changed, 71 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c index 30733209fde2..57f70c28fa38 100644 --- a/drivers/hsi/clients/hsi_char.c +++ b/drivers/hsi/clients/hsi_char.c @@ -367,7 +367,7 @@ static int hsc_rx_set(struct hsi_client *cl, struct hsc_rx_config *rxc) return -EINVAL; tmp = cl->rx_cfg; cl->rx_cfg.mode = rxc->mode; - cl->rx_cfg.channels = rxc->channels; + cl->rx_cfg.num_hw_channels = rxc->channels; cl->rx_cfg.flow = rxc->flow; ret = hsi_setup(cl); if (ret < 0) { @@ -383,7 +383,7 @@ static int hsc_rx_set(struct hsi_client *cl, struct hsc_rx_config *rxc) static inline void hsc_rx_get(struct hsi_client *cl, struct hsc_rx_config *rxc) { rxc->mode = cl->rx_cfg.mode; - rxc->channels = cl->rx_cfg.channels; + rxc->channels = cl->rx_cfg.num_hw_channels; rxc->flow = cl->rx_cfg.flow; } @@ -402,7 +402,7 @@ static int hsc_tx_set(struct hsi_client *cl, struct hsc_tx_config *txc) return -EINVAL; tmp = cl->tx_cfg; cl->tx_cfg.mode = txc->mode; - cl->tx_cfg.channels = txc->channels; + cl->tx_cfg.num_hw_channels = txc->channels; cl->tx_cfg.speed = txc->speed; cl->tx_cfg.arb_mode = txc->arb_mode; ret = hsi_setup(cl); @@ -417,7 +417,7 @@ static int hsc_tx_set(struct hsi_client *cl, struct hsc_tx_config *txc) static inline void hsc_tx_get(struct hsi_client *cl, struct hsc_tx_config *txc) { txc->mode = cl->tx_cfg.mode; - txc->channels = cl->tx_cfg.channels; + txc->channels = cl->tx_cfg.num_hw_channels; txc->speed = cl->tx_cfg.speed; txc->arb_mode = cl->tx_cfg.arb_mode; } @@ -435,7 +435,7 @@ static ssize_t hsc_read(struct file *file, char __user *buf, size_t len, return -EINVAL; if (len > max_data_size) len = max_data_size; - if (channel->ch >= channel->cl->rx_cfg.channels) + if (channel->ch >= channel->cl->rx_cfg.num_hw_channels) return -ECHRNG; if (test_and_set_bit(HSC_CH_READ, &channel->flags)) return -EBUSY; @@ -492,7 +492,7 @@ static ssize_t hsc_write(struct file *file, const char __user *buf, size_t len, return -EINVAL; if (len > max_data_size) len = max_data_size; - if (channel->ch >= channel->cl->tx_cfg.channels) + if (channel->ch >= channel->cl->tx_cfg.num_hw_channels) return -ECHRNG; if (test_and_set_bit(HSC_CH_WRITE, &channel->flags)) return -EBUSY; diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index e96a9874b1a4..de2ad8f20d55 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -62,18 +62,36 @@ static struct bus_type hsi_bus_type = { static void hsi_client_release(struct device *dev) { - kfree(to_hsi_client(dev)); + struct hsi_client *cl = to_hsi_client(dev); + + kfree(cl->tx_cfg.channels); + kfree(cl->rx_cfg.channels); + kfree(cl); } static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) { struct hsi_client *cl; + size_t size; cl = kzalloc(sizeof(*cl), GFP_KERNEL); if (!cl) return; + cl->tx_cfg = info->tx_cfg; + if (cl->tx_cfg.channels) { + size = cl->tx_cfg.num_channels * sizeof(*cl->tx_cfg.channels); + cl->tx_cfg.channels = kzalloc(size , GFP_KERNEL); + memcpy(cl->tx_cfg.channels, info->tx_cfg.channels, size); + } + cl->rx_cfg = info->rx_cfg; + if (cl->rx_cfg.channels) { + size = cl->rx_cfg.num_channels * sizeof(*cl->rx_cfg.channels); + cl->rx_cfg.channels = kzalloc(size , GFP_KERNEL); + memcpy(cl->rx_cfg.channels, info->rx_cfg.channels, size); + } + cl->device.bus = &hsi_bus_type; cl->device.parent = &port->device; cl->device.release = hsi_client_release; @@ -502,6 +520,32 @@ int hsi_event(struct hsi_port *port, unsigned long event) } EXPORT_SYMBOL_GPL(hsi_event); +/** + * hsi_get_channel_id_by_name - acquire channel id by channel name + * @cl: HSI client, which uses the channel + * @name: name the channel is known under + * + * Clients can call this function to get the hsi channel ids similar to + * requesting IRQs or GPIOs by name. This function assumes the same + * channel configuration is used for RX and TX. + * + * Returns -errno on error or channel id on success. + */ +int hsi_get_channel_id_by_name(struct hsi_client *cl, char *name) +{ + int i; + + if (!cl->rx_cfg.channels) + return -ENOENT; + + for (i = 0; i < cl->rx_cfg.num_channels; i++) + if (!strcmp(cl->rx_cfg.channels[i].name, name)) + return cl->rx_cfg.channels[i].id; + + return -ENXIO; +} +EXPORT_SYMBOL_GPL(hsi_get_channel_id_by_name); + static int __init hsi_init(void) { return bus_register(&hsi_bus_type); diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index 5a9f1210ed22..e3cff94bef04 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -67,18 +67,32 @@ enum { HSI_EVENT_STOP_RX, }; +/** + * struct hsi_channel - channel resource used by the hsi clients + * @id: Channel number + * @name: Channel name + */ +struct hsi_channel { + unsigned int id; + const char *name; +}; + /** * struct hsi_config - Configuration for RX/TX HSI modules * @mode: Bit transmission mode (STREAM or FRAME) - * @channels: Number of channels to use [1..16] + * @channels: Channel resources used by the client + * @num_channels: Number of channel resources + * @num_hw_channels: Number of channels the transceiver is configured for [1..16] * @speed: Max bit transmission speed (Kbit/s) * @flow: RX flow type (SYNCHRONIZED or PIPELINE) * @arb_mode: Arbitration mode for TX frame (Round robin, priority) */ struct hsi_config { - unsigned int mode; - unsigned int channels; - unsigned int speed; + unsigned int mode; + struct hsi_channel *channels; + unsigned int num_channels; + unsigned int num_hw_channels; + unsigned int speed; union { unsigned int flow; /* RX only */ unsigned int arb_mode; /* TX only */ @@ -306,6 +320,8 @@ static inline struct hsi_port *hsi_find_port_num(struct hsi_controller *hsi, */ int hsi_async(struct hsi_client *cl, struct hsi_msg *msg); +int hsi_get_channel_id_by_name(struct hsi_client *cl, char *name); + /** * hsi_id - Get HSI controller ID associated to a client * @cl: Pointer to a HSI client -- cgit v1.2.3 From 8491451024bcfabdcebd772ce9ec2fc5757acd42 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 28 Mar 2014 22:54:25 +0100 Subject: HSI: export method to (un)register clients Expose method for registering and unregistering HSI clients, so that client drivers can register other client drivers. This is useful for HSI drivers, which want to use the functionality of other HSI drivers. For example the N900 modem driver can load HSI drivers for mcsaab protocol and speech protocol. Signed-off-by: Sebastian Reichel Reviewed-by: Pavel Machek Tested-By: Ivaylo Dimitrov --- drivers/hsi/hsi.c | 11 ++++++++--- include/linux/hsi/hsi.h | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index de2ad8f20d55..834a2d6b444e 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -69,14 +69,15 @@ static void hsi_client_release(struct device *dev) kfree(cl); } -static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) +struct hsi_client *hsi_new_client(struct hsi_port *port, + struct hsi_board_info *info) { struct hsi_client *cl; size_t size; cl = kzalloc(sizeof(*cl), GFP_KERNEL); if (!cl) - return; + return NULL; cl->tx_cfg = info->tx_cfg; if (cl->tx_cfg.channels) { @@ -103,7 +104,10 @@ static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) pr_err("hsi: failed to register client: %s\n", info->name); put_device(&cl->device); } + + return cl; } +EXPORT_SYMBOL_GPL(hsi_new_client); static void hsi_scan_board_info(struct hsi_controller *hsi) { @@ -119,12 +123,13 @@ static void hsi_scan_board_info(struct hsi_controller *hsi) } } -static int hsi_remove_client(struct device *dev, void *data __maybe_unused) +int hsi_remove_client(struct device *dev, void *data __maybe_unused) { device_unregister(dev); return 0; } +EXPORT_SYMBOL_GPL(hsi_remove_client); static int hsi_remove_port(struct device *dev, void *data __maybe_unused) { diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index e3cff94bef04..e20a3999a696 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -296,6 +296,9 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags); void hsi_put_controller(struct hsi_controller *hsi); int hsi_register_controller(struct hsi_controller *hsi); void hsi_unregister_controller(struct hsi_controller *hsi); +struct hsi_client *hsi_new_client(struct hsi_port *port, + struct hsi_board_info *info); +int hsi_remove_client(struct device *dev, void *data); void hsi_port_unregister_clients(struct hsi_port *port); static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi, -- cgit v1.2.3 From a2aa24734d9dbbd3b9062c2459936c336278fa6a Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 28 Mar 2014 22:59:43 +0100 Subject: HSI: Add common DT binding for HSI client devices Implement and document generic DT bindings for HSI clients. Signed-off-by: Sebastian Reichel Reviewed-by: Pavel Machek Tested-By: Ivaylo Dimitrov --- .../devicetree/bindings/hsi/client-devices.txt | 44 +++++ drivers/hsi/hsi.c | 208 ++++++++++++++++++++- include/linux/hsi/hsi.h | 11 ++ 3 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/hsi/client-devices.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/hsi/client-devices.txt b/Documentation/devicetree/bindings/hsi/client-devices.txt new file mode 100644 index 000000000000..104c9a3e57a4 --- /dev/null +++ b/Documentation/devicetree/bindings/hsi/client-devices.txt @@ -0,0 +1,44 @@ +Each HSI port is supposed to have one child node, which +symbols the remote device connected to the HSI port. The +following properties are standardized for HSI clients: + +Required HSI configuration properties: + +- hsi-channel-ids: A list of channel ids + +- hsi-rx-mode: Receiver Bit transmission mode ("stream" or "frame") +- hsi-tx-mode: Transmitter Bit transmission mode ("stream" or "frame") +- hsi-mode: May be used instead hsi-rx-mode and hsi-tx-mode if + the transmission mode is the same for receiver and + transmitter +- hsi-speed-kbps: Max bit transmission speed in kbit/s +- hsi-flow: RX flow type ("synchronized" or "pipeline") +- hsi-arb-mode: Arbitration mode for TX frame ("round-robin", "priority") + +Optional HSI configuration properties: + +- hsi-channel-names: A list with one name per channel specified in the + hsi-channel-ids property + + +Device Tree node example for an HSI client: + +hsi-controller { + hsi-port { + modem: hsi-client { + compatible = "nokia,n900-modem"; + + hsi-channel-ids = <0>, <1>, <2>, <3>; + hsi-channel-names = "mcsaab-control", + "speech-control", + "speech-data", + "mcsaab-data"; + hsi-speed-kbps = <55000>; + hsi-mode = "frame"; + hsi-flow = "synchronized"; + hsi-arb-mode = "round-robin"; + + /* more client specific properties */ + }; + }; +}; diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index 834a2d6b444e..fe9371271ce2 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "hsi_core.h" static ssize_t modalias_show(struct device *dev, @@ -50,7 +52,13 @@ static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) static int hsi_bus_match(struct device *dev, struct device_driver *driver) { - return strcmp(dev_name(dev), driver->name) == 0; + if (of_driver_match_device(dev, driver)) + return true; + + if (strcmp(dev_name(dev), driver->name) == 0) + return true; + + return false; } static struct bus_type hsi_bus_type = { @@ -123,6 +131,202 @@ static void hsi_scan_board_info(struct hsi_controller *hsi) } } +#ifdef CONFIG_OF +static struct hsi_board_info hsi_char_dev_info = { + .name = "hsi_char", +}; + +static int hsi_of_property_parse_mode(struct device_node *client, char *name, + unsigned int *result) +{ + const char *mode; + int err; + + err = of_property_read_string(client, name, &mode); + if (err < 0) + return err; + + if (strcmp(mode, "stream") == 0) + *result = HSI_MODE_STREAM; + else if (strcmp(mode, "frame") == 0) + *result = HSI_MODE_FRAME; + else + return -EINVAL; + + return 0; +} + +static int hsi_of_property_parse_flow(struct device_node *client, char *name, + unsigned int *result) +{ + const char *flow; + int err; + + err = of_property_read_string(client, name, &flow); + if (err < 0) + return err; + + if (strcmp(flow, "synchronized") == 0) + *result = HSI_FLOW_SYNC; + else if (strcmp(flow, "pipeline") == 0) + *result = HSI_FLOW_PIPE; + else + return -EINVAL; + + return 0; +} + +static int hsi_of_property_parse_arb_mode(struct device_node *client, + char *name, unsigned int *result) +{ + const char *arb_mode; + int err; + + err = of_property_read_string(client, name, &arb_mode); + if (err < 0) + return err; + + if (strcmp(arb_mode, "round-robin") == 0) + *result = HSI_ARB_RR; + else if (strcmp(arb_mode, "priority") == 0) + *result = HSI_ARB_PRIO; + else + return -EINVAL; + + return 0; +} + +static void hsi_add_client_from_dt(struct hsi_port *port, + struct device_node *client) +{ + struct hsi_client *cl; + struct hsi_channel channel; + struct property *prop; + char name[32]; + int length, cells, err, i, max_chan, mode; + + cl = kzalloc(sizeof(*cl), GFP_KERNEL); + if (!cl) + return; + + err = of_modalias_node(client, name, sizeof(name)); + if (err) + goto err; + + dev_set_name(&cl->device, "%s", name); + + err = hsi_of_property_parse_mode(client, "hsi-mode", &mode); + if (err) { + err = hsi_of_property_parse_mode(client, "hsi-rx-mode", + &cl->rx_cfg.mode); + if (err) + goto err; + + err = hsi_of_property_parse_mode(client, "hsi-tx-mode", + &cl->tx_cfg.mode); + if (err) + goto err; + } else { + cl->rx_cfg.mode = mode; + cl->tx_cfg.mode = mode; + } + + err = of_property_read_u32(client, "hsi-speed-kbps", + &cl->tx_cfg.speed); + if (err) + goto err; + cl->rx_cfg.speed = cl->tx_cfg.speed; + + err = hsi_of_property_parse_flow(client, "hsi-flow", + &cl->rx_cfg.flow); + if (err) + goto err; + + err = hsi_of_property_parse_arb_mode(client, "hsi-arb-mode", + &cl->rx_cfg.arb_mode); + if (err) + goto err; + + prop = of_find_property(client, "hsi-channel-ids", &length); + if (!prop) { + err = -EINVAL; + goto err; + } + + cells = length / sizeof(u32); + + cl->rx_cfg.num_channels = cells; + cl->tx_cfg.num_channels = cells; + + cl->rx_cfg.channels = kzalloc(cells * sizeof(channel), GFP_KERNEL); + if (!cl->rx_cfg.channels) { + err = -ENOMEM; + goto err; + } + + cl->tx_cfg.channels = kzalloc(cells * sizeof(channel), GFP_KERNEL); + if (!cl->tx_cfg.channels) { + err = -ENOMEM; + goto err2; + } + + max_chan = 0; + for (i = 0; i < cells; i++) { + err = of_property_read_u32_index(client, "hsi-channel-ids", i, + &channel.id); + if (err) + goto err3; + + err = of_property_read_string_index(client, "hsi-channel-names", + i, &channel.name); + if (err) + channel.name = NULL; + + if (channel.id > max_chan) + max_chan = channel.id; + + cl->rx_cfg.channels[i] = channel; + cl->tx_cfg.channels[i] = channel; + } + + cl->rx_cfg.num_hw_channels = max_chan + 1; + cl->tx_cfg.num_hw_channels = max_chan + 1; + + cl->device.bus = &hsi_bus_type; + cl->device.parent = &port->device; + cl->device.release = hsi_client_release; + cl->device.of_node = client; + + if (device_register(&cl->device) < 0) { + pr_err("hsi: failed to register client: %s\n", name); + put_device(&cl->device); + goto err3; + } + + return; + +err3: + kfree(cl->tx_cfg.channels); +err2: + kfree(cl->rx_cfg.channels); +err: + kfree(cl); + pr_err("hsi client: missing or incorrect of property: err=%d\n", err); +} + +void hsi_add_clients_from_dt(struct hsi_port *port, struct device_node *clients) +{ + struct device_node *child; + + /* register hsi-char device */ + hsi_new_client(port, &hsi_char_dev_info); + + for_each_available_child_of_node(clients, child) + hsi_add_client_from_dt(port, child); +} +EXPORT_SYMBOL_GPL(hsi_add_clients_from_dt); +#endif + int hsi_remove_client(struct device *dev, void *data __maybe_unused) { device_unregister(dev); @@ -505,7 +709,7 @@ int hsi_unregister_port_event(struct hsi_client *cl) EXPORT_SYMBOL_GPL(hsi_unregister_port_event); /** - * hsi_event -Notifies clients about port events + * hsi_event - Notifies clients about port events * @port: Port where the event occurred * @event: The event type * diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index e20a3999a696..3ec06300d535 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -301,6 +301,17 @@ struct hsi_client *hsi_new_client(struct hsi_port *port, int hsi_remove_client(struct device *dev, void *data); void hsi_port_unregister_clients(struct hsi_port *port); +#ifdef CONFIG_OF +void hsi_add_clients_from_dt(struct hsi_port *port, + struct device_node *clients); +#else +static inline void hsi_add_clients_from_dt(struct hsi_port *port, + struct device_node *clients) +{ + return; +} +#endif + static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi, void *data) { -- cgit v1.2.3 From dc7bf5d7186849aa36b9f0e42e250a813a7b0bdb Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 15 Nov 2013 10:50:32 +0000 Subject: HSI: Introduce driver for SSI Protocol This adds a driver for the SSI McSAAB protocol as used in the Nokia N900. Signed-off-by: Carlos Chinea Signed-off-by: Sebastian Reichel Tested-By: Ivaylo Dimitrov --- drivers/hsi/clients/Kconfig | 8 + drivers/hsi/clients/Makefile | 3 +- drivers/hsi/clients/ssi_protocol.c | 1191 ++++++++++++++++++++++++++++++++++++ include/linux/hsi/ssi_protocol.h | 42 ++ 4 files changed, 1243 insertions(+), 1 deletion(-) create mode 100644 drivers/hsi/clients/ssi_protocol.c create mode 100644 include/linux/hsi/ssi_protocol.h (limited to 'include') diff --git a/drivers/hsi/clients/Kconfig b/drivers/hsi/clients/Kconfig index 3bacd275f479..1457cfb5b453 100644 --- a/drivers/hsi/clients/Kconfig +++ b/drivers/hsi/clients/Kconfig @@ -4,6 +4,14 @@ comment "HSI clients" +config SSI_PROTOCOL + tristate "SSI protocol" + depends on HSI && PHONET && (OMAP_SSI=y || OMAP_SSI=m) + help + If you say Y here, you will enable the SSI protocol aka McSAAB. + + If unsure, say N. + config HSI_CHAR tristate "HSI/SSI character driver" depends on HSI diff --git a/drivers/hsi/clients/Makefile b/drivers/hsi/clients/Makefile index 327c0e27c8b0..ccbf768ea42b 100644 --- a/drivers/hsi/clients/Makefile +++ b/drivers/hsi/clients/Makefile @@ -2,4 +2,5 @@ # Makefile for HSI clients # -obj-$(CONFIG_HSI_CHAR) += hsi_char.o +obj-$(CONFIG_SSI_PROTOCOL) += ssi_protocol.o +obj-$(CONFIG_HSI_CHAR) += hsi_char.o diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c new file mode 100644 index 000000000000..ce4be3738d46 --- /dev/null +++ b/drivers/hsi/clients/ssi_protocol.c @@ -0,0 +1,1191 @@ +/* + * ssi_protocol.c + * + * Implementation of the SSI McSAAB improved protocol. + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * Copyright (C) 2013 Sebastian Reichel + * + * Contact: Carlos Chinea + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void ssi_waketest(struct hsi_client *cl, unsigned int enable); + +#define SSIP_TXQUEUE_LEN 100 +#define SSIP_MAX_MTU 65535 +#define SSIP_DEFAULT_MTU 4000 +#define PN_MEDIA_SOS 21 +#define SSIP_MIN_PN_HDR 6 /* FIXME: Revisit */ +#define SSIP_WDTOUT 2000 /* FIXME: has to be 500 msecs */ +#define SSIP_KATOUT 15 /* 15 msecs */ +#define SSIP_MAX_CMDS 5 /* Number of pre-allocated commands buffers */ +#define SSIP_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1) +#define SSIP_CMT_LOADER_SYNC 0x11223344 +/* + * SSI protocol command definitions + */ +#define SSIP_COMMAND(data) ((data) >> 28) +#define SSIP_PAYLOAD(data) ((data) & 0xfffffff) +/* Commands */ +#define SSIP_SW_BREAK 0 +#define SSIP_BOOTINFO_REQ 1 +#define SSIP_BOOTINFO_RESP 2 +#define SSIP_WAKETEST_RESULT 3 +#define SSIP_START_TRANS 4 +#define SSIP_READY 5 +/* Payloads */ +#define SSIP_DATA_VERSION(data) ((data) & 0xff) +#define SSIP_LOCAL_VERID 1 +#define SSIP_WAKETEST_OK 0 +#define SSIP_WAKETEST_FAILED 1 +#define SSIP_PDU_LENGTH(data) (((data) >> 8) & 0xffff) +#define SSIP_MSG_ID(data) ((data) & 0xff) +/* Generic Command */ +#define SSIP_CMD(cmd, payload) (((cmd) << 28) | ((payload) & 0xfffffff)) +/* Commands for the control channel */ +#define SSIP_BOOTINFO_REQ_CMD(ver) \ + SSIP_CMD(SSIP_BOOTINFO_REQ, SSIP_DATA_VERSION(ver)) +#define SSIP_BOOTINFO_RESP_CMD(ver) \ + SSIP_CMD(SSIP_BOOTINFO_RESP, SSIP_DATA_VERSION(ver)) +#define SSIP_START_TRANS_CMD(pdulen, id) \ + SSIP_CMD(SSIP_START_TRANS, (((pdulen) << 8) | SSIP_MSG_ID(id))) +#define SSIP_READY_CMD SSIP_CMD(SSIP_READY, 0) +#define SSIP_SWBREAK_CMD SSIP_CMD(SSIP_SW_BREAK, 0) + +/* Main state machine states */ +enum { + INIT, + HANDSHAKE, + ACTIVE, +}; + +/* Send state machine states */ +enum { + SEND_IDLE, + WAIT4READY, + SEND_READY, + SENDING, + SENDING_SWBREAK, +}; + +/* Receive state machine states */ +enum { + RECV_IDLE, + RECV_READY, + RECEIVING, +}; + +/** + * struct ssi_protocol - SSI protocol (McSAAB) data + * @main_state: Main state machine + * @send_state: TX state machine + * @recv_state: RX state machine + * @waketest: Flag to follow wake line test + * @rxid: RX data id + * @txid: TX data id + * @txqueue_len: TX queue length + * @tx_wd: TX watchdog + * @rx_wd: RX watchdog + * @keep_alive: Workaround for SSI HW bug + * @lock: To serialize access to this struct + * @netdev: Phonet network device + * @txqueue: TX data queue + * @cmdqueue: Queue of free commands + * @cl: HSI client own reference + * @link: Link for ssip_list + * @tx_usecount: Refcount to keep track the slaves that use the wake line + * @channel_id_cmd: HSI channel id for command stream + * @channel_id_data: HSI channel id for data stream + */ +struct ssi_protocol { + unsigned int main_state; + unsigned int send_state; + unsigned int recv_state; + unsigned int waketest:1; + u8 rxid; + u8 txid; + unsigned int txqueue_len; + struct timer_list tx_wd; + struct timer_list rx_wd; + struct timer_list keep_alive; /* wake-up workaround */ + spinlock_t lock; + struct net_device *netdev; + struct list_head txqueue; + struct list_head cmdqueue; + struct hsi_client *cl; + struct list_head link; + atomic_t tx_usecnt; + int channel_id_cmd; + int channel_id_data; +}; + +/* List of ssi protocol instances */ +static LIST_HEAD(ssip_list); + +static void ssip_rxcmd_complete(struct hsi_msg *msg); + +static inline void ssip_set_cmd(struct hsi_msg *msg, u32 cmd) +{ + u32 *data; + + data = sg_virt(msg->sgt.sgl); + *data = cmd; +} + +static inline u32 ssip_get_cmd(struct hsi_msg *msg) +{ + u32 *data; + + data = sg_virt(msg->sgt.sgl); + + return *data; +} + +static void ssip_skb_to_msg(struct sk_buff *skb, struct hsi_msg *msg) +{ + skb_frag_t *frag; + struct scatterlist *sg; + int i; + + BUG_ON(msg->sgt.nents != (unsigned int)(skb_shinfo(skb)->nr_frags + 1)); + + sg = msg->sgt.sgl; + sg_set_buf(sg, skb->data, skb_headlen(skb)); + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + sg = sg_next(sg); + BUG_ON(!sg); + frag = &skb_shinfo(skb)->frags[i]; + sg_set_page(sg, frag->page.p, frag->size, frag->page_offset); + } +} + +static void ssip_free_data(struct hsi_msg *msg) +{ + struct sk_buff *skb; + + skb = msg->context; + pr_debug("free data: msg %p context %p skb %p\n", msg, msg->context, + skb); + msg->destructor = NULL; + dev_kfree_skb(skb); + hsi_free_msg(msg); +} + +static struct hsi_msg *ssip_alloc_data(struct ssi_protocol *ssi, + struct sk_buff *skb, gfp_t flags) +{ + struct hsi_msg *msg; + + msg = hsi_alloc_msg(skb_shinfo(skb)->nr_frags + 1, flags); + if (!msg) + return NULL; + ssip_skb_to_msg(skb, msg); + msg->destructor = ssip_free_data; + msg->channel = ssi->channel_id_data; + msg->context = skb; + + return msg; +} + +static inline void ssip_release_cmd(struct hsi_msg *msg) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(msg->cl); + + dev_dbg(&msg->cl->device, "Release cmd 0x%08x\n", ssip_get_cmd(msg)); + spin_lock_bh(&ssi->lock); + list_add_tail(&msg->link, &ssi->cmdqueue); + spin_unlock_bh(&ssi->lock); +} + +static struct hsi_msg *ssip_claim_cmd(struct ssi_protocol *ssi) +{ + struct hsi_msg *msg; + + BUG_ON(list_empty(&ssi->cmdqueue)); + + spin_lock_bh(&ssi->lock); + msg = list_first_entry(&ssi->cmdqueue, struct hsi_msg, link); + list_del(&msg->link); + spin_unlock_bh(&ssi->lock); + msg->destructor = ssip_release_cmd; + + return msg; +} + +static void ssip_free_cmds(struct ssi_protocol *ssi) +{ + struct hsi_msg *msg, *tmp; + + list_for_each_entry_safe(msg, tmp, &ssi->cmdqueue, link) { + list_del(&msg->link); + msg->destructor = NULL; + kfree(sg_virt(msg->sgt.sgl)); + hsi_free_msg(msg); + } +} + +static int ssip_alloc_cmds(struct ssi_protocol *ssi) +{ + struct hsi_msg *msg; + u32 *buf; + unsigned int i; + + for (i = 0; i < SSIP_MAX_CMDS; i++) { + msg = hsi_alloc_msg(1, GFP_KERNEL); + if (!msg) + goto out; + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + hsi_free_msg(msg); + goto out; + } + sg_init_one(msg->sgt.sgl, buf, sizeof(*buf)); + msg->channel = ssi->channel_id_cmd; + list_add_tail(&msg->link, &ssi->cmdqueue); + } + + return 0; +out: + ssip_free_cmds(ssi); + + return -ENOMEM; +} + +static void ssip_set_rxstate(struct ssi_protocol *ssi, unsigned int state) +{ + ssi->recv_state = state; + switch (state) { + case RECV_IDLE: + del_timer(&ssi->rx_wd); + if (ssi->send_state == SEND_IDLE) + del_timer(&ssi->keep_alive); + break; + case RECV_READY: + /* CMT speech workaround */ + if (atomic_read(&ssi->tx_usecnt)) + break; + /* Otherwise fall through */ + case RECEIVING: + mod_timer(&ssi->keep_alive, jiffies + + msecs_to_jiffies(SSIP_KATOUT)); + mod_timer(&ssi->rx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); + break; + default: + break; + } +} + +static void ssip_set_txstate(struct ssi_protocol *ssi, unsigned int state) +{ + ssi->send_state = state; + switch (state) { + case SEND_IDLE: + case SEND_READY: + del_timer(&ssi->tx_wd); + if (ssi->recv_state == RECV_IDLE) + del_timer(&ssi->keep_alive); + break; + case WAIT4READY: + case SENDING: + case SENDING_SWBREAK: + mod_timer(&ssi->keep_alive, + jiffies + msecs_to_jiffies(SSIP_KATOUT)); + mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); + break; + default: + break; + } +} + +struct hsi_client *ssip_slave_get_master(struct hsi_client *slave) +{ + struct hsi_client *master = ERR_PTR(-ENODEV); + struct ssi_protocol *ssi; + + list_for_each_entry(ssi, &ssip_list, link) + if (slave->device.parent == ssi->cl->device.parent) { + master = ssi->cl; + break; + } + + return master; +} +EXPORT_SYMBOL_GPL(ssip_slave_get_master); + +int ssip_slave_start_tx(struct hsi_client *master) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(master); + + dev_dbg(&master->device, "start TX %d\n", atomic_read(&ssi->tx_usecnt)); + spin_lock_bh(&ssi->lock); + if (ssi->send_state == SEND_IDLE) { + ssip_set_txstate(ssi, WAIT4READY); + hsi_start_tx(master); + } + spin_unlock_bh(&ssi->lock); + atomic_inc(&ssi->tx_usecnt); + + return 0; +} +EXPORT_SYMBOL_GPL(ssip_slave_start_tx); + +int ssip_slave_stop_tx(struct hsi_client *master) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(master); + + WARN_ON_ONCE(atomic_read(&ssi->tx_usecnt) == 0); + + if (atomic_dec_and_test(&ssi->tx_usecnt)) { + spin_lock_bh(&ssi->lock); + if ((ssi->send_state == SEND_READY) || + (ssi->send_state == WAIT4READY)) { + ssip_set_txstate(ssi, SEND_IDLE); + hsi_stop_tx(master); + } + spin_unlock_bh(&ssi->lock); + } + dev_dbg(&master->device, "stop TX %d\n", atomic_read(&ssi->tx_usecnt)); + + return 0; +} +EXPORT_SYMBOL_GPL(ssip_slave_stop_tx); + +int ssip_slave_running(struct hsi_client *master) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(master); + return netif_running(ssi->netdev); +} +EXPORT_SYMBOL_GPL(ssip_slave_running); + +static void ssip_reset(struct hsi_client *cl) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct list_head *head, *tmp; + struct hsi_msg *msg; + + if (netif_running(ssi->netdev)) + netif_carrier_off(ssi->netdev); + hsi_flush(cl); + spin_lock_bh(&ssi->lock); + if (ssi->send_state != SEND_IDLE) + hsi_stop_tx(cl); + if (ssi->waketest) + ssi_waketest(cl, 0); + del_timer(&ssi->rx_wd); + del_timer(&ssi->tx_wd); + del_timer(&ssi->keep_alive); + ssi->main_state = 0; + ssi->send_state = 0; + ssi->recv_state = 0; + ssi->waketest = 0; + ssi->rxid = 0; + ssi->txid = 0; + list_for_each_safe(head, tmp, &ssi->txqueue) { + msg = list_entry(head, struct hsi_msg, link); + dev_dbg(&cl->device, "Pending TX data\n"); + list_del(head); + ssip_free_data(msg); + } + ssi->txqueue_len = 0; + spin_unlock_bh(&ssi->lock); +} + +static void ssip_dump_state(struct hsi_client *cl) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *msg; + + spin_lock_bh(&ssi->lock); + dev_err(&cl->device, "Main state: %d\n", ssi->main_state); + dev_err(&cl->device, "Recv state: %d\n", ssi->recv_state); + dev_err(&cl->device, "Send state: %d\n", ssi->send_state); + dev_err(&cl->device, "CMT %s\n", (ssi->main_state == ACTIVE) ? + "Online" : "Offline"); + dev_err(&cl->device, "Wake test %d\n", ssi->waketest); + dev_err(&cl->device, "Data RX id: %d\n", ssi->rxid); + dev_err(&cl->device, "Data TX id: %d\n", ssi->txid); + + list_for_each_entry(msg, &ssi->txqueue, link) + dev_err(&cl->device, "pending TX data (%p)\n", msg); + spin_unlock_bh(&ssi->lock); +} + +static void ssip_error(struct hsi_client *cl) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *msg; + + ssip_dump_state(cl); + ssip_reset(cl); + msg = ssip_claim_cmd(ssi); + msg->complete = ssip_rxcmd_complete; + hsi_async_read(cl, msg); +} + +static void ssip_keep_alive(unsigned long data) +{ + struct hsi_client *cl = (struct hsi_client *)data; + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + + dev_dbg(&cl->device, "Keep alive kick in: m(%d) r(%d) s(%d)\n", + ssi->main_state, ssi->recv_state, ssi->send_state); + + spin_lock(&ssi->lock); + if (ssi->recv_state == RECV_IDLE) + switch (ssi->send_state) { + case SEND_READY: + if (atomic_read(&ssi->tx_usecnt) == 0) + break; + /* + * Fall through. Workaround for cmt-speech + * in that case we relay on audio timers. + */ + case SEND_IDLE: + spin_unlock(&ssi->lock); + return; + } + mod_timer(&ssi->keep_alive, jiffies + msecs_to_jiffies(SSIP_KATOUT)); + spin_unlock(&ssi->lock); +} + +static void ssip_wd(unsigned long data) +{ + struct hsi_client *cl = (struct hsi_client *)data; + + dev_err(&cl->device, "Watchdog trigerred\n"); + ssip_error(cl); +} + +static void ssip_send_bootinfo_req_cmd(struct hsi_client *cl) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *msg; + + dev_dbg(&cl->device, "Issuing BOOT INFO REQ command\n"); + msg = ssip_claim_cmd(ssi); + ssip_set_cmd(msg, SSIP_BOOTINFO_REQ_CMD(SSIP_LOCAL_VERID)); + msg->complete = ssip_release_cmd; + hsi_async_write(cl, msg); + dev_dbg(&cl->device, "Issuing RX command\n"); + msg = ssip_claim_cmd(ssi); + msg->complete = ssip_rxcmd_complete; + hsi_async_read(cl, msg); +} + +static void ssip_start_rx(struct hsi_client *cl) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *msg; + + dev_dbg(&cl->device, "RX start M(%d) R(%d)\n", ssi->main_state, + ssi->recv_state); + spin_lock(&ssi->lock); + /* + * We can have two UP events in a row due to a short low + * high transition. Therefore we need to ignore the sencond UP event. + */ + if ((ssi->main_state != ACTIVE) || (ssi->recv_state == RECV_READY)) { + if (ssi->main_state == INIT) { + ssi->main_state = HANDSHAKE; + spin_unlock(&ssi->lock); + ssip_send_bootinfo_req_cmd(cl); + } else { + spin_unlock(&ssi->lock); + } + return; + } + ssip_set_rxstate(ssi, RECV_READY); + spin_unlock(&ssi->lock); + + msg = ssip_claim_cmd(ssi); + ssip_set_cmd(msg, SSIP_READY_CMD); + msg->complete = ssip_release_cmd; + dev_dbg(&cl->device, "Send READY\n"); + hsi_async_write(cl, msg); +} + +static void ssip_stop_rx(struct hsi_client *cl) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + + dev_dbg(&cl->device, "RX stop M(%d)\n", ssi->main_state); + spin_lock(&ssi->lock); + if (likely(ssi->main_state == ACTIVE)) + ssip_set_rxstate(ssi, RECV_IDLE); + spin_unlock(&ssi->lock); +} + +static void ssip_free_strans(struct hsi_msg *msg) +{ + ssip_free_data(msg->context); + ssip_release_cmd(msg); +} + +static void ssip_strans_complete(struct hsi_msg *msg) +{ + struct hsi_client *cl = msg->cl; + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *data; + + data = msg->context; + ssip_release_cmd(msg); + spin_lock(&ssi->lock); + ssip_set_txstate(ssi, SENDING); + spin_unlock(&ssi->lock); + hsi_async_write(cl, data); +} + +static int ssip_xmit(struct hsi_client *cl) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *msg, *dmsg; + struct sk_buff *skb; + + spin_lock_bh(&ssi->lock); + if (list_empty(&ssi->txqueue)) { + spin_unlock_bh(&ssi->lock); + return 0; + } + dmsg = list_first_entry(&ssi->txqueue, struct hsi_msg, link); + list_del(&dmsg->link); + ssi->txqueue_len--; + spin_unlock_bh(&ssi->lock); + + msg = ssip_claim_cmd(ssi); + skb = dmsg->context; + msg->context = dmsg; + msg->complete = ssip_strans_complete; + msg->destructor = ssip_free_strans; + + spin_lock_bh(&ssi->lock); + ssip_set_cmd(msg, SSIP_START_TRANS_CMD(SSIP_BYTES_TO_FRAMES(skb->len), + ssi->txid)); + ssi->txid++; + ssip_set_txstate(ssi, SENDING); + spin_unlock_bh(&ssi->lock); + + dev_dbg(&cl->device, "Send STRANS (%d frames)\n", + SSIP_BYTES_TO_FRAMES(skb->len)); + + return hsi_async_write(cl, msg); +} + +/* In soft IRQ context */ +static void ssip_pn_rx(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + + if (unlikely(!netif_running(dev))) { + dev_dbg(&dev->dev, "Drop RX packet\n"); + dev->stats.rx_dropped++; + dev_kfree_skb(skb); + return; + } + if (unlikely(!pskb_may_pull(skb, SSIP_MIN_PN_HDR))) { + dev_dbg(&dev->dev, "Error drop RX packet\n"); + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + dev_kfree_skb(skb); + return; + } + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + /* length field is exchanged in network byte order */ + ((u16 *)skb->data)[2] = ntohs(((u16 *)skb->data)[2]); + dev_dbg(&dev->dev, "RX length fixed (%04x -> %u)\n", + ((u16 *)skb->data)[2], ntohs(((u16 *)skb->data)[2])); + + skb->protocol = htons(ETH_P_PHONET); + skb_reset_mac_header(skb); + __skb_pull(skb, 1); + netif_rx(skb); +} + +static void ssip_rx_data_complete(struct hsi_msg *msg) +{ + struct hsi_client *cl = msg->cl; + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct sk_buff *skb; + + if (msg->status == HSI_STATUS_ERROR) { + dev_err(&cl->device, "RX data error\n"); + ssip_free_data(msg); + ssip_error(cl); + return; + } + del_timer(&ssi->rx_wd); /* FIXME: Revisit */ + skb = msg->context; + ssip_pn_rx(skb); + hsi_free_msg(msg); +} + +static void ssip_rx_bootinforeq(struct hsi_client *cl, u32 cmd) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *msg; + + /* Workaroud: Ignore CMT Loader message leftover */ + if (cmd == SSIP_CMT_LOADER_SYNC) + return; + + switch (ssi->main_state) { + case ACTIVE: + dev_err(&cl->device, "Boot info req on active state\n"); + ssip_error(cl); + /* Fall through */ + case INIT: + spin_lock(&ssi->lock); + ssi->main_state = HANDSHAKE; + if (!ssi->waketest) { + ssi->waketest = 1; + ssi_waketest(cl, 1); /* FIXME: To be removed */ + } + /* Start boot handshake watchdog */ + mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); + spin_unlock(&ssi->lock); + dev_dbg(&cl->device, "Send BOOTINFO_RESP\n"); + if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID) + dev_warn(&cl->device, "boot info req verid mismatch\n"); + msg = ssip_claim_cmd(ssi); + ssip_set_cmd(msg, SSIP_BOOTINFO_RESP_CMD(SSIP_LOCAL_VERID)); + msg->complete = ssip_release_cmd; + hsi_async_write(cl, msg); + break; + case HANDSHAKE: + /* Ignore */ + break; + default: + dev_dbg(&cl->device, "Wrong state M(%d)\n", ssi->main_state); + break; + } +} + +static void ssip_rx_bootinforesp(struct hsi_client *cl, u32 cmd) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + + if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID) + dev_warn(&cl->device, "boot info resp verid mismatch\n"); + + spin_lock(&ssi->lock); + if (ssi->main_state != ACTIVE) + /* Use tx_wd as a boot watchdog in non ACTIVE state */ + mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); + else + dev_dbg(&cl->device, "boot info resp ignored M(%d)\n", + ssi->main_state); + spin_unlock(&ssi->lock); +} + +static void ssip_rx_waketest(struct hsi_client *cl, u32 cmd) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + unsigned int wkres = SSIP_PAYLOAD(cmd); + + spin_lock(&ssi->lock); + if (ssi->main_state != HANDSHAKE) { + dev_dbg(&cl->device, "wake lines test ignored M(%d)\n", + ssi->main_state); + spin_unlock(&ssi->lock); + return; + } + if (ssi->waketest) { + ssi->waketest = 0; + ssi_waketest(cl, 0); /* FIXME: To be removed */ + } + ssi->main_state = ACTIVE; + del_timer(&ssi->tx_wd); /* Stop boot handshake timer */ + spin_unlock(&ssi->lock); + + dev_notice(&cl->device, "WAKELINES TEST %s\n", + wkres & SSIP_WAKETEST_FAILED ? "FAILED" : "OK"); + if (wkres & SSIP_WAKETEST_FAILED) { + ssip_error(cl); + return; + } + dev_dbg(&cl->device, "CMT is ONLINE\n"); + netif_wake_queue(ssi->netdev); + netif_carrier_on(ssi->netdev); +} + +static void ssip_rx_ready(struct hsi_client *cl) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + + spin_lock(&ssi->lock); + if (unlikely(ssi->main_state != ACTIVE)) { + dev_dbg(&cl->device, "READY on wrong state: S(%d) M(%d)\n", + ssi->send_state, ssi->main_state); + spin_unlock(&ssi->lock); + return; + } + if (ssi->send_state != WAIT4READY) { + dev_dbg(&cl->device, "Ignore spurious READY command\n"); + spin_unlock(&ssi->lock); + return; + } + ssip_set_txstate(ssi, SEND_READY); + spin_unlock(&ssi->lock); + ssip_xmit(cl); +} + +static void ssip_rx_strans(struct hsi_client *cl, u32 cmd) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct sk_buff *skb; + struct hsi_msg *msg; + int len = SSIP_PDU_LENGTH(cmd); + + dev_dbg(&cl->device, "RX strans: %d frames\n", len); + spin_lock(&ssi->lock); + if (unlikely(ssi->main_state != ACTIVE)) { + dev_err(&cl->device, "START TRANS wrong state: S(%d) M(%d)\n", + ssi->send_state, ssi->main_state); + spin_unlock(&ssi->lock); + return; + } + ssip_set_rxstate(ssi, RECEIVING); + if (unlikely(SSIP_MSG_ID(cmd) != ssi->rxid)) { + dev_err(&cl->device, "START TRANS id %d expeceted %d\n", + SSIP_MSG_ID(cmd), ssi->rxid); + spin_unlock(&ssi->lock); + goto out1; + } + ssi->rxid++; + spin_unlock(&ssi->lock); + skb = netdev_alloc_skb(ssi->netdev, len * 4); + if (unlikely(!skb)) { + dev_err(&cl->device, "No memory for rx skb\n"); + goto out1; + } + skb->dev = ssi->netdev; + skb_put(skb, len * 4); + msg = ssip_alloc_data(ssi, skb, GFP_ATOMIC); + if (unlikely(!msg)) { + dev_err(&cl->device, "No memory for RX data msg\n"); + goto out2; + } + msg->complete = ssip_rx_data_complete; + hsi_async_read(cl, msg); + + return; +out2: + dev_kfree_skb(skb); +out1: + ssip_error(cl); +} + +static void ssip_rxcmd_complete(struct hsi_msg *msg) +{ + struct hsi_client *cl = msg->cl; + u32 cmd = ssip_get_cmd(msg); + unsigned int cmdid = SSIP_COMMAND(cmd); + + if (msg->status == HSI_STATUS_ERROR) { + dev_err(&cl->device, "RX error detected\n"); + ssip_release_cmd(msg); + ssip_error(cl); + return; + } + hsi_async_read(cl, msg); + dev_dbg(&cl->device, "RX cmd: 0x%08x\n", cmd); + switch (cmdid) { + case SSIP_SW_BREAK: + /* Ignored */ + break; + case SSIP_BOOTINFO_REQ: + ssip_rx_bootinforeq(cl, cmd); + break; + case SSIP_BOOTINFO_RESP: + ssip_rx_bootinforesp(cl, cmd); + break; + case SSIP_WAKETEST_RESULT: + ssip_rx_waketest(cl, cmd); + break; + case SSIP_START_TRANS: + ssip_rx_strans(cl, cmd); + break; + case SSIP_READY: + ssip_rx_ready(cl); + break; + default: + dev_warn(&cl->device, "command 0x%08x not supported\n", cmd); + break; + } +} + +static void ssip_swbreak_complete(struct hsi_msg *msg) +{ + struct hsi_client *cl = msg->cl; + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + + ssip_release_cmd(msg); + spin_lock(&ssi->lock); + if (list_empty(&ssi->txqueue)) { + if (atomic_read(&ssi->tx_usecnt)) { + ssip_set_txstate(ssi, SEND_READY); + } else { + ssip_set_txstate(ssi, SEND_IDLE); + hsi_stop_tx(cl); + } + spin_unlock(&ssi->lock); + } else { + spin_unlock(&ssi->lock); + ssip_xmit(cl); + } + netif_wake_queue(ssi->netdev); +} + +static void ssip_tx_data_complete(struct hsi_msg *msg) +{ + struct hsi_client *cl = msg->cl; + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *cmsg; + + if (msg->status == HSI_STATUS_ERROR) { + dev_err(&cl->device, "TX data error\n"); + ssip_error(cl); + goto out; + } + spin_lock(&ssi->lock); + if (list_empty(&ssi->txqueue)) { + ssip_set_txstate(ssi, SENDING_SWBREAK); + spin_unlock(&ssi->lock); + cmsg = ssip_claim_cmd(ssi); + ssip_set_cmd(cmsg, SSIP_SWBREAK_CMD); + cmsg->complete = ssip_swbreak_complete; + dev_dbg(&cl->device, "Send SWBREAK\n"); + hsi_async_write(cl, cmsg); + } else { + spin_unlock(&ssi->lock); + ssip_xmit(cl); + } +out: + ssip_free_data(msg); +} + +void ssip_port_event(struct hsi_client *cl, unsigned long event) +{ + switch (event) { + case HSI_EVENT_START_RX: + ssip_start_rx(cl); + break; + case HSI_EVENT_STOP_RX: + ssip_stop_rx(cl); + break; + default: + return; + } +} + +static int ssip_pn_open(struct net_device *dev) +{ + struct hsi_client *cl = to_hsi_client(dev->dev.parent); + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + int err; + + err = hsi_claim_port(cl, 1); + if (err < 0) { + dev_err(&cl->device, "SSI port already claimed\n"); + return err; + } + err = hsi_register_port_event(cl, ssip_port_event); + if (err < 0) { + dev_err(&cl->device, "Register HSI port event failed (%d)\n", + err); + return err; + } + dev_dbg(&cl->device, "Configuring SSI port\n"); + hsi_setup(cl); + spin_lock_bh(&ssi->lock); + if (!ssi->waketest) { + ssi->waketest = 1; + ssi_waketest(cl, 1); /* FIXME: To be removed */ + } + ssi->main_state = INIT; + spin_unlock_bh(&ssi->lock); + + return 0; +} + +static int ssip_pn_stop(struct net_device *dev) +{ + struct hsi_client *cl = to_hsi_client(dev->dev.parent); + + ssip_reset(cl); + hsi_unregister_port_event(cl); + hsi_release_port(cl); + + return 0; +} + +static int ssip_pn_set_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu > SSIP_MAX_MTU || new_mtu < PHONET_MIN_MTU) + return -EINVAL; + dev->mtu = new_mtu; + + return 0; +} + +static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hsi_client *cl = to_hsi_client(dev->dev.parent); + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + struct hsi_msg *msg; + + if ((skb->protocol != htons(ETH_P_PHONET)) || + (skb->len < SSIP_MIN_PN_HDR)) + goto drop; + /* Pad to 32-bits - FIXME: Revisit*/ + if ((skb->len & 3) && skb_pad(skb, 4 - (skb->len & 3))) + goto drop; + + /* + * Modem sends Phonet messages over SSI with its own endianess... + * Assume that modem has the same endianess as we do. + */ + if (skb_cow_head(skb, 0)) + goto drop; + + /* length field is exchanged in network byte order */ + ((u16 *)skb->data)[2] = htons(((u16 *)skb->data)[2]); + + msg = ssip_alloc_data(ssi, skb, GFP_ATOMIC); + if (!msg) { + dev_dbg(&cl->device, "Dropping tx data: No memory\n"); + goto drop; + } + msg->complete = ssip_tx_data_complete; + + spin_lock_bh(&ssi->lock); + if (unlikely(ssi->main_state != ACTIVE)) { + spin_unlock_bh(&ssi->lock); + dev_dbg(&cl->device, "Dropping tx data: CMT is OFFLINE\n"); + goto drop2; + } + list_add_tail(&msg->link, &ssi->txqueue); + ssi->txqueue_len++; + if (dev->tx_queue_len < ssi->txqueue_len) { + dev_info(&cl->device, "TX queue full %d\n", ssi->txqueue_len); + netif_stop_queue(dev); + } + if (ssi->send_state == SEND_IDLE) { + ssip_set_txstate(ssi, WAIT4READY); + spin_unlock_bh(&ssi->lock); + dev_dbg(&cl->device, "Start TX qlen %d\n", ssi->txqueue_len); + hsi_start_tx(cl); + } else if (ssi->send_state == SEND_READY) { + /* Needed for cmt-speech workaround */ + dev_dbg(&cl->device, "Start TX on SEND READY qlen %d\n", + ssi->txqueue_len); + spin_unlock_bh(&ssi->lock); + ssip_xmit(cl); + } else { + spin_unlock_bh(&ssi->lock); + } + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + return 0; +drop2: + hsi_free_msg(msg); +drop: + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + + return 0; +} + +/* CMT reset event handler */ +void ssip_reset_event(struct hsi_client *master) +{ + struct ssi_protocol *ssi = hsi_client_drvdata(master); + dev_err(&ssi->cl->device, "CMT reset detected!\n"); + ssip_error(ssi->cl); +} +EXPORT_SYMBOL_GPL(ssip_reset_event); + +static const struct net_device_ops ssip_pn_ops = { + .ndo_open = ssip_pn_open, + .ndo_stop = ssip_pn_stop, + .ndo_start_xmit = ssip_pn_xmit, + .ndo_change_mtu = ssip_pn_set_mtu, +}; + +static void ssip_pn_setup(struct net_device *dev) +{ + dev->features = 0; + dev->netdev_ops = &ssip_pn_ops; + dev->type = ARPHRD_PHONET; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = SSIP_DEFAULT_MTU; + dev->hard_header_len = 1; + dev->dev_addr[0] = PN_MEDIA_SOS; + dev->addr_len = 1; + dev->tx_queue_len = SSIP_TXQUEUE_LEN; + + dev->destructor = free_netdev; + dev->header_ops = &phonet_header_ops; +} + +static int ssi_protocol_probe(struct device *dev) +{ + static const char ifname[] = "phonet%d"; + struct hsi_client *cl = to_hsi_client(dev); + struct ssi_protocol *ssi; + int err; + + ssi = kzalloc(sizeof(*ssi), GFP_KERNEL); + if (!ssi) { + dev_err(dev, "No memory for ssi protocol\n"); + return -ENOMEM; + } + + spin_lock_init(&ssi->lock); + init_timer_deferrable(&ssi->rx_wd); + init_timer_deferrable(&ssi->tx_wd); + init_timer(&ssi->keep_alive); + ssi->rx_wd.data = (unsigned long)cl; + ssi->rx_wd.function = ssip_wd; + ssi->tx_wd.data = (unsigned long)cl; + ssi->tx_wd.function = ssip_wd; + ssi->keep_alive.data = (unsigned long)cl; + ssi->keep_alive.function = ssip_keep_alive; + INIT_LIST_HEAD(&ssi->txqueue); + INIT_LIST_HEAD(&ssi->cmdqueue); + atomic_set(&ssi->tx_usecnt, 0); + hsi_client_set_drvdata(cl, ssi); + ssi->cl = cl; + + ssi->channel_id_cmd = hsi_get_channel_id_by_name(cl, "mcsaab-control"); + if (ssi->channel_id_cmd < 0) { + err = ssi->channel_id_cmd; + dev_err(dev, "Could not get cmd channel (%d)\n", err); + goto out; + } + + ssi->channel_id_data = hsi_get_channel_id_by_name(cl, "mcsaab-data"); + if (ssi->channel_id_data < 0) { + err = ssi->channel_id_data; + dev_err(dev, "Could not get data channel (%d)\n", err); + goto out; + } + + err = ssip_alloc_cmds(ssi); + if (err < 0) { + dev_err(dev, "No memory for commands\n"); + goto out; + } + + ssi->netdev = alloc_netdev(0, ifname, ssip_pn_setup); + if (!ssi->netdev) { + dev_err(dev, "No memory for netdev\n"); + err = -ENOMEM; + goto out1; + } + + SET_NETDEV_DEV(ssi->netdev, dev); + netif_carrier_off(ssi->netdev); + err = register_netdev(ssi->netdev); + if (err < 0) { + dev_err(dev, "Register netdev failed (%d)\n", err); + goto out2; + } + + list_add(&ssi->link, &ssip_list); + + dev_dbg(dev, "channel configuration: cmd=%d, data=%d\n", + ssi->channel_id_cmd, ssi->channel_id_data); + + return 0; +out2: + free_netdev(ssi->netdev); +out1: + ssip_free_cmds(ssi); +out: + kfree(ssi); + + return err; +} + +static int ssi_protocol_remove(struct device *dev) +{ + struct hsi_client *cl = to_hsi_client(dev); + struct ssi_protocol *ssi = hsi_client_drvdata(cl); + + list_del(&ssi->link); + unregister_netdev(ssi->netdev); + ssip_free_cmds(ssi); + hsi_client_set_drvdata(cl, NULL); + kfree(ssi); + + return 0; +} + +static struct hsi_client_driver ssip_driver = { + .driver = { + .name = "ssi-protocol", + .owner = THIS_MODULE, + .probe = ssi_protocol_probe, + .remove = ssi_protocol_remove, + }, +}; + +static int __init ssip_init(void) +{ + pr_info("SSI protocol aka McSAAB added\n"); + + return hsi_register_client_driver(&ssip_driver); +} +module_init(ssip_init); + +static void __exit ssip_exit(void) +{ + hsi_unregister_client_driver(&ssip_driver); + pr_info("SSI protocol driver removed\n"); +} +module_exit(ssip_exit); + +MODULE_ALIAS("hsi:ssi-protocol"); +MODULE_AUTHOR("Carlos Chinea "); +MODULE_AUTHOR("Remi Denis-Courmont "); +MODULE_DESCRIPTION("SSI protocol improved aka McSAAB"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/hsi/ssi_protocol.h b/include/linux/hsi/ssi_protocol.h new file mode 100644 index 000000000000..1433651be0dc --- /dev/null +++ b/include/linux/hsi/ssi_protocol.h @@ -0,0 +1,42 @@ +/* + * ssip_slave.h + * + * SSIP slave support header file + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * + * Contact: Carlos Chinea + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __LINUX_SSIP_SLAVE_H__ +#define __LINUX_SSIP_SLAVE_H__ + +#include + +static inline void ssip_slave_put_master(struct hsi_client *master) +{ +} + +struct hsi_client *ssip_slave_get_master(struct hsi_client *slave); +int ssip_slave_start_tx(struct hsi_client *master); +int ssip_slave_stop_tx(struct hsi_client *master); +void ssip_reset_event(struct hsi_client *master); + +int ssip_slave_running(struct hsi_client *master); + +#endif /* __LINUX_SSIP_SLAVE_H__ */ + -- cgit v1.2.3 From ad222799bec32a2db99c12b4dfa5dc19a1f6eaac Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 2 May 2014 13:22:19 +1000 Subject: drm: fix memory leak around mode_group (v2) This mode group id_list was never being freed. v2: take David's suggestion to free in minor_free. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 6 ++++++ drivers/gpu/drm/drm_stub.c | 1 + include/drm/drm_crtc.h | 1 + 3 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d8b7099abece..a3fe32439a5b 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1378,6 +1378,12 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr return 0; } +void drm_mode_group_destroy(struct drm_mode_group *group) +{ + kfree(group->id_list); + group->id_list = NULL; +} + /* * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is * the drm core's responsibility to set up mode control groups. diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 1447b0ee3676..3727ac8bc310 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -294,6 +294,7 @@ static void drm_minor_free(struct drm_device *dev, unsigned int type) slot = drm_minor_get_slot(dev, type); if (*slot) { + drm_mode_group_destroy(&(*slot)->mode_group); kfree(*slot); *slot = NULL; } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e55fccbe7c42..c6b9e8ab0a26 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -915,6 +915,7 @@ extern const char *drm_get_tv_subconnector_name(int val); extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); +extern void drm_mode_group_destroy(struct drm_mode_group *group); extern bool drm_probe_ddc(struct i2c_adapter *adapter); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); -- cgit v1.2.3 From be7a010d6fa33dca9327ad8e91844278dfd1e712 Mon Sep 17 00:00:00 2001 From: Duan Jiong Date: Thu, 15 May 2014 15:56:14 +0800 Subject: ipv6: update Destination Cache entries when gateway turn into host RFC 4861 states in 7.2.5: The IsRouter flag in the cache entry MUST be set based on the Router flag in the received advertisement. In those cases where the IsRouter flag changes from TRUE to FALSE as a result of this update, the node MUST remove that router from the Default Router List and update the Destination Cache entries for all destinations using that neighbor as a router as specified in Section 7.3.3. This is needed to detect when a node that is used as a router stops forwarding packets due to being configured as a host. Currently, when dealing with NA Message which IsRouter flag changes from TRUE to FALSE, the kernel only removes router from the Default Router List, and don't update the Destination Cache entries. Now in order to update those Destination Cache entries, i introduce function rt6_clean_tohost(). Signed-off-by: Duan Jiong Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ip6_route.h | 1 + net/ipv6/ndisc.c | 7 ++----- net/ipv6/route.c | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 6c4f5eac98e7..216cecce65e9 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -127,6 +127,7 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg); void rt6_ifdown(struct net *net, struct net_device *dev); void rt6_mtu_change(struct net_device *dev, unsigned int mtu); void rt6_remove_prefsrc(struct inet6_ifaddr *ifp); +void rt6_clean_tohost(struct net *net, struct in6_addr *gateway); /* diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 09a22f4f36c9..ca8d4ea48a5d 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -851,7 +851,7 @@ out: static void ndisc_recv_na(struct sk_buff *skb) { struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb); - const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; + struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; u8 *lladdr = NULL; u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + @@ -944,10 +944,7 @@ static void ndisc_recv_na(struct sk_buff *skb) /* * Change: router to host */ - struct rt6_info *rt; - rt = rt6_get_dflt_router(saddr, dev); - if (rt) - ip6_del_rt(rt); + rt6_clean_tohost(dev_net(dev), saddr); } out: diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 004fffb6c221..58c449ad7f6e 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2234,6 +2234,27 @@ void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) fib6_clean_all(net, fib6_remove_prefsrc, &adni); } +#define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) +#define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) + +/* Remove routers and update dst entries when gateway turn into host. */ +static int fib6_clean_tohost(struct rt6_info *rt, void *arg) +{ + struct in6_addr *gateway = (struct in6_addr *)arg; + + if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || + ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && + ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { + return -1; + } + return 0; +} + +void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) +{ + fib6_clean_all(net, fib6_clean_tohost, gateway); +} + struct arg_dev_net { struct net_device *dev; struct net *net; -- cgit v1.2.3 From 31ad169148df2252a774c73c504aff43bfa4b656 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Wed, 14 May 2014 13:43:02 +0200 Subject: Bluetooth: Add conn info lifetime parameters to debugfs This patch adds conn_info_min_age and conn_info_max_age parameters to debugfs which determine lifetime of connection information. Actual lifetime will be random value between min and max age. Default values for min and max age are 1000ms and 3000ms respectively. Signed-off-by: Andrzej Kaczmarek Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 6 ++++ net/bluetooth/hci_core.c | 63 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 211bad6a3366..4623f45c8892 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -145,6 +145,10 @@ struct oob_data { /* Default LE RPA expiry time, 15 minutes */ #define HCI_DEFAULT_RPA_TIMEOUT (15 * 60) +/* Default min/max age of connection information (1s/3s) */ +#define DEFAULT_CONN_INFO_MIN_AGE 1000 +#define DEFAULT_CONN_INFO_MAX_AGE 3000 + struct amp_assoc { __u16 len; __u16 offset; @@ -200,6 +204,8 @@ struct hci_dev { __u16 le_conn_min_interval; __u16 le_conn_max_interval; __u16 discov_interleaved_timeout; + __u16 conn_info_min_age; + __u16 conn_info_max_age; __u8 ssp_debug_mode; __u16 devid_source; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d31f144860d1..313bd1c164d6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -579,6 +579,62 @@ static int sniff_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get, sniff_max_interval_set, "%llu\n"); +static int conn_info_min_age_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val > hdev->conn_info_max_age) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->conn_info_min_age = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_info_min_age_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->conn_info_min_age; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get, + conn_info_min_age_set, "%llu\n"); + +static int conn_info_max_age_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val < hdev->conn_info_min_age) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->conn_info_max_age = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_info_max_age_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->conn_info_max_age; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get, + conn_info_max_age_set, "%llu\n"); + static int identity_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -1754,6 +1810,11 @@ static int __hci_init(struct hci_dev *hdev) &blacklist_fops); debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); + debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev, + &conn_info_min_age_fops); + debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev, + &conn_info_max_age_fops); + if (lmp_bredr_capable(hdev)) { debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev, &inquiry_cache_fops); @@ -3789,6 +3850,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT; hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT; + hdev->conn_info_min_age = DEFAULT_CONN_INFO_MIN_AGE; + hdev->conn_info_max_age = DEFAULT_CONN_INFO_MAX_AGE; mutex_init(&hdev->lock); mutex_init(&hdev->req_lock); -- cgit v1.2.3 From dd9838087b8c2b45c7976e46290749732d7af9d5 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Wed, 14 May 2014 13:43:03 +0200 Subject: Bluetooth: Add support to get connection information This patch adds support for Get Connection Information mgmt command which can be used to query for information about connection, i.e. RSSI and local TX power level. In general values cached in hci_conn are returned as long as they are considered valid, i.e. do not exceed age limit set in hdev. This limit is calculated as random value between min/max values to avoid client trying to guess when to poll for updated information. Signed-off-by: Andrzej Kaczmarek Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 + include/net/bluetooth/mgmt.h | 12 +++ net/bluetooth/mgmt.c | 196 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4623f45c8892..cbbab6327621 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -384,6 +384,8 @@ struct hci_conn { __s8 tx_power; unsigned long flags; + unsigned long conn_info_timestamp; + __u8 remote_cap; __u8 remote_auth; __u8 remote_id; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index d4b571c2f9fd..226ae03cafe7 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -409,6 +409,18 @@ struct mgmt_cp_load_irks { } __packed; #define MGMT_LOAD_IRKS_SIZE 2 +#define MGMT_OP_GET_CONN_INFO 0x0031 +struct mgmt_cp_get_conn_info { + struct mgmt_addr_info addr; +} __packed; +#define MGMT_GET_CONN_INFO_SIZE MGMT_ADDR_INFO_SIZE +struct mgmt_rp_get_conn_info { + struct mgmt_addr_info addr; + __s8 rssi; + __s8 tx_power; + __s8 max_tx_power; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f2a9422d6139..0e5a316fafbf 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -83,6 +83,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_DEBUG_KEYS, MGMT_OP_SET_PRIVACY, MGMT_OP_LOAD_IRKS, + MGMT_OP_GET_CONN_INFO, }; static const u16 mgmt_events[] = { @@ -4557,6 +4558,200 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, return err; } +struct cmd_conn_lookup { + struct hci_conn *conn; + bool valid_tx_power; + u8 mgmt_status; +}; + +static void get_conn_info_complete(struct pending_cmd *cmd, void *data) +{ + struct cmd_conn_lookup *match = data; + struct mgmt_cp_get_conn_info *cp; + struct mgmt_rp_get_conn_info rp; + struct hci_conn *conn = cmd->user_data; + + if (conn != match->conn) + return; + + cp = (struct mgmt_cp_get_conn_info *) cmd->param; + + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + + if (!match->mgmt_status) { + rp.rssi = conn->rssi; + + if (match->valid_tx_power) + rp.tx_power = conn->tx_power; + else + rp.tx_power = HCI_TX_POWER_INVALID; + + rp.max_tx_power = HCI_TX_POWER_INVALID; + } + + cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, + match->mgmt_status, &rp, sizeof(rp)); + + hci_conn_drop(conn); + + mgmt_pending_remove(cmd); +} + +static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_read_rssi *cp; + struct hci_conn *conn; + struct cmd_conn_lookup match; + u16 handle; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + /* TX power data is valid in case request completed successfully, + * otherwise we assume it's not valid. + */ + match.valid_tx_power = !status; + + /* Commands sent in request are either Read RSSI or Read Transmit Power + * Level so we check which one was last sent to retrieve connection + * handle. Both commands have handle as first parameter so it's safe to + * cast data on the same command struct. + * + * First command sent is always Read RSSI and we fail only if it fails. + * In other case we simply override error to indicate success as we + * already remembered if TX power value is actually valid. + */ + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI); + if (!cp) { + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER); + status = 0; + } + + if (!cp) { + BT_ERR("invalid sent_cmd in response"); + goto unlock; + } + + handle = __le16_to_cpu(cp->handle); + conn = hci_conn_hash_lookup_handle(hdev, handle); + if (!conn) { + BT_ERR("unknown handle (%d) in response", handle); + goto unlock; + } + + match.conn = conn; + match.mgmt_status = mgmt_status(status); + + /* Cache refresh is complete, now reply for mgmt request for given + * connection only. + */ + mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev, + get_conn_info_complete, &match); + +unlock: + hci_dev_unlock(hdev); +} + +static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_get_conn_info *cp = data; + struct mgmt_rp_get_conn_info rp; + struct hci_conn *conn; + unsigned long conn_info_age; + int err = 0; + + BT_DBG("%s", hdev->name); + + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); + goto unlock; + } + + if (cp->addr.type == BDADDR_BREDR) + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &cp->addr.bdaddr); + else + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); + + if (!conn || conn->state != BT_CONNECTED) { + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp)); + goto unlock; + } + + /* To avoid client trying to guess when to poll again for information we + * calculate conn info age as random value between min/max set in hdev. + */ + conn_info_age = hdev->conn_info_min_age + + prandom_u32_max(hdev->conn_info_max_age - + hdev->conn_info_min_age); + + /* Query controller to refresh cached values if they are too old or were + * never read. + */ + if (time_after(jiffies, conn->conn_info_timestamp + conn_info_age) || + !conn->conn_info_timestamp) { + struct hci_request req; + struct hci_cp_read_tx_power req_txp_cp; + struct hci_cp_read_rssi req_rssi_cp; + struct pending_cmd *cmd; + + hci_req_init(&req, hdev); + req_rssi_cp.handle = cpu_to_le16(conn->handle); + hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp), + &req_rssi_cp); + + req_txp_cp.handle = cpu_to_le16(conn->handle); + req_txp_cp.type = 0x00; + hci_req_add(&req, HCI_OP_READ_TX_POWER, + sizeof(req_txp_cp), &req_txp_cp); + + err = hci_req_run(&req, conn_info_refresh_complete); + if (err < 0) + goto unlock; + + cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev, + data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_conn_hold(conn); + cmd->user_data = conn; + + conn->conn_info_timestamp = jiffies; + } else { + /* Cache is valid, just reply with values cached in hci_conn */ + rp.rssi = conn->rssi; + rp.tx_power = conn->tx_power; + rp.max_tx_power = HCI_TX_POWER_INVALID; + + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, + MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); + } + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -4612,6 +4807,7 @@ static const struct mgmt_handler { { set_debug_keys, false, MGMT_SETTING_SIZE }, { set_privacy, false, MGMT_SET_PRIVACY_SIZE }, { load_irks, true, MGMT_LOAD_IRKS_SIZE }, + { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, }; -- cgit v1.2.3 From d0455ed996df84fd2670a655fe13ab72f8264765 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Wed, 14 May 2014 13:43:05 +0200 Subject: Bluetooth: Store max TX power level for connection This patch adds support to store local maximum TX power level for connection when reply for HCI_Read_Transmit_Power_Level is received. Signed-off-by: Andrzej Kaczmarek Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 1 + net/bluetooth/hci_event.c | 12 +++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index cbbab6327621..b386bf17e6c2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -382,6 +382,7 @@ struct hci_conn { __u16 le_conn_max_interval; __s8 rssi; __s8 tx_power; + __s8 max_tx_power; unsigned long flags; unsigned long conn_info_timestamp; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 74b368bfe102..a987e7def025 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -408,6 +408,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->remote_auth = 0xff; conn->key_type = 0xff; conn->tx_power = HCI_TX_POWER_INVALID; + conn->max_tx_power = HCI_TX_POWER_INVALID; set_bit(HCI_CONN_POWER_SAVE, &conn->flags); conn->disc_timeout = HCI_DISCONN_TIMEOUT; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index fa614e3430a7..492d8d5071c7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1282,9 +1282,19 @@ static void hci_cc_read_tx_power(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn && sent->type == 0x00) + if (!conn) + goto unlock; + + switch (sent->type) { + case 0x00: conn->tx_power = rp->tx_power; + break; + case 0x01: + conn->max_tx_power = rp->tx_power; + break; + } +unlock: hci_dev_unlock(hdev); } -- cgit v1.2.3 From d55135689019c3a0b26bccd400d90c6d2fd13439 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Sun, 11 May 2014 22:47:09 +0800 Subject: ARM: imx: add clock driver for imx6sx Add clock driver for i.MX6 SoloX SoC. Signed-off-by: Anson Huang Signed-off-by: Shawn Guo --- .../devicetree/bindings/clock/imx6sx-clock.txt | 13 + arch/arm/mach-imx/clk-imx6sx.c | 524 +++++++++++++++++++++ include/dt-bindings/clock/imx6sx-clock.h | 256 ++++++++++ 3 files changed, 793 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/imx6sx-clock.txt create mode 100644 arch/arm/mach-imx/clk-imx6sx.c create mode 100644 include/dt-bindings/clock/imx6sx-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/imx6sx-clock.txt b/Documentation/devicetree/bindings/clock/imx6sx-clock.txt new file mode 100644 index 000000000000..22362b9b7ba3 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx6sx-clock.txt @@ -0,0 +1,13 @@ +* Clock bindings for Freescale i.MX6 SoloX + +Required properties: +- compatible: Should be "fsl,imx6sx-ccm" +- reg: Address and length of the register set +- #clock-cells: Should be <1> +- clocks: list of clock specifiers, must contain an entry for each required + entry in clock-names +- clock-names: should include entries "ckil", "osc", "ipp_di0" and "ipp_di1" + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx6sx-clock.h +for the full list of i.MX6 SoloX clock IDs. diff --git a/arch/arm/mach-imx/clk-imx6sx.c b/arch/arm/mach-imx/clk-imx6sx.c new file mode 100644 index 000000000000..72f8902235d1 --- /dev/null +++ b/arch/arm/mach-imx/clk-imx6sx.c @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" +#include "common.h" + +#define CCDR 0x4 +#define BM_CCM_CCDR_MMDC_CH0_MASK (0x2 << 16) + +static const char *step_sels[] = { "osc", "pll2_pfd2_396m", }; +static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; +static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; +static const char *periph2_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll4_audio_div", }; +static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", }; +static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "osc", }; +static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; +static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; +static const char *ocram_sels[] = { "periph", "pll2_pfd2_396m", "periph", "pll3_pfd1_540m", }; +static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", }; +static const char *gpu_axi_sels[] = { "pll2_pfd2_396m", "pll3_pfd0_720m", "pll3_pfd1_540m", "pll2_bus", }; +static const char *gpu_core_sels[] = { "pll3_pfd1_540m", "pll3_pfd0_720m", "pll2_bus", "pll2_pfd2_396m", }; +static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", }; +static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", }; +static const char *ldb_di0_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_pfd3_594m", "pll2_pfd1_594m", "pll3_pfd3_454m", }; +static const char *ldb_di1_sels[] = { "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_bus", "pll3_pfd3_454m", "pll3_pfd2_508m", }; +static const char *pcie_axi_sels[] = { "axi", "ahb", }; +static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll5_video_div", "pll4_audio_div", }; +static const char *qspi1_sels[] = { "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_bus", "pll3_pfd3_454m", "pll3_pfd2_508m", }; +static const char *perclk_sels[] = { "ipg", "osc", }; +static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *vid_sels[] = { "pll3_pfd1_540m", "pll3_usb_otg", "pll3_pfd3_454m", "pll4_audio_div", "pll5_video_div", }; +static const char *can_sels[] = { "pll3_60m", "osc", "pll3_80m", "dummy", }; +static const char *uart_sels[] = { "pll3_80m", "osc", }; +static const char *qspi2_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", "pll3_pfd3_454m", "dummy", "dummy", "dummy", }; +static const char *enet_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", }; +static const char *enet_sels[] = { "enet_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *m4_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "osc", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd3_454m", }; +static const char *m4_sels[] = { "m4_pre_sel", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *eim_slow_sels[] = { "ocram", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *ecspi_sels[] = { "pll3_60m", "osc", }; +static const char *lcdif1_pre_sels[] = { "pll2_bus", "pll3_pfd3_454m", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd1_594m", "pll3_pfd1_540m", }; +static const char *lcdif1_sels[] = { "lcdif1_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *lcdif2_pre_sels[] = { "pll2_bus", "pll3_pfd3_454m", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd3_594m", "pll3_pfd1_540m", }; +static const char *lcdif2_sels[] = { "lcdif2_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *display_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll3_usb_otg", "pll3_pfd1_540m", }; +static const char *csi_sels[] = { "osc", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; +static const char *cko1_sels[] = { + "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div", + "dummy", "ocram", "dummy", "pxp_axi", "epdc_axi", "lcdif_pix", + "epdc_pix", "ahb", "ipg", "perclk", "ckil", "pll4_audio_div", +}; +static const char *cko2_sels[] = { + "dummy", "mmdc_p0_fast", "usdhc4", "usdhc1", "dummy", "wrck", + "ecspi_root", "dummy", "usdhc3", "pcie", "arm", "csi_core", + "lcdif_axi", "dummy", "osc", "dummy", "gpu2d_ovg_core", + "usdhc2", "ssi1", "ssi2", "ssi3", "gpu2d_core", "dummy", + "dummy", "dummy", "dummy", "esai_extal", "eim_slow", "uart_serial", + "spdif", "asrc", "dummy", +}; +static const char *cko_sels[] = { "cko1", "cko2", }; +static const char *lvds_sels[] = { + "arm", "pll1_sys", "dummy", "dummy", "dummy", "dummy", "dummy", "pll5_video_div", + "dummy", "dummy", "pcie_ref_125m", "dummy", "usbphy1", "usbphy2", +}; + +static struct clk *clks[IMX6SX_CLK_CLK_END]; +static struct clk_onecell_data clk_data; + +static int const clks_init_on[] __initconst = { + IMX6SX_CLK_AIPS_TZ1, IMX6SX_CLK_AIPS_TZ2, IMX6SX_CLK_AIPS_TZ3, + IMX6SX_CLK_IPMUX1, IMX6SX_CLK_IPMUX2, IMX6SX_CLK_IPMUX3, + IMX6SX_CLK_WAKEUP, IMX6SX_CLK_MMDC_P0_FAST, IMX6SX_CLK_MMDC_P0_IPG, + IMX6SX_CLK_ROM, IMX6SX_CLK_ARM, IMX6SX_CLK_IPG, IMX6SX_CLK_OCRAM, + IMX6SX_CLK_PER2_MAIN, IMX6SX_CLK_PERCLK, IMX6SX_CLK_M4, + IMX6SX_CLK_QSPI1, IMX6SX_CLK_QSPI2, IMX6SX_CLK_UART_IPG, + IMX6SX_CLK_UART_SERIAL, IMX6SX_CLK_I2C3, IMX6SX_CLK_ECSPI5, + IMX6SX_CLK_CAN1_IPG, IMX6SX_CLK_CAN1_SERIAL, IMX6SX_CLK_CAN2_IPG, + IMX6SX_CLK_CAN2_SERIAL, IMX6SX_CLK_CANFD, IMX6SX_CLK_EPIT1, + IMX6SX_CLK_EPIT2, +}; + +static struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, + { } +}; + +static struct clk_div_table post_div_table[] = { + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static struct clk_div_table video_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 1, }, + { .val = 3, .div = 4, }, + { } +}; + +static u32 share_count_asrc; +static u32 share_count_audio; +static u32 share_count_esai; + +static void __init imx6sx_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *base; + int i; + + clks[IMX6SX_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + + clks[IMX6SX_CLK_CKIL] = of_clk_get_by_name(ccm_node, "ckil"); + clks[IMX6SX_CLK_OSC] = of_clk_get_by_name(ccm_node, "osc"); + + /* ipp_di clock is external input */ + clks[IMX6SX_CLK_IPP_DI0] = of_clk_get_by_name(ccm_node, "ipp_di0"); + clks[IMX6SX_CLK_IPP_DI1] = of_clk_get_by_name(ccm_node, "ipp_di1"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-anatop"); + base = of_iomap(np, 0); + WARN_ON(!base); + + /* type name parent_name base div_mask */ + clks[IMX6SX_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f); + clks[IMX6SX_CLK_PLL2_BUS] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1); + clks[IMX6SX_CLK_PLL3_USB_OTG] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3); + clks[IMX6SX_CLK_PLL4_AUDIO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f); + clks[IMX6SX_CLK_PLL5_VIDEO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f); + clks[IMX6SX_CLK_PLL6_ENET] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3); + clks[IMX6SX_CLK_PLL7_USB_HOST] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host", "osc", base + 0x20, 0x3); + + /* + * Bit 20 is the reserved and read-only bit, we do this only for: + * - Do nothing for usbphy clk_enable/disable + * - Keep refcount when do usbphy clk_enable/disable, in that case, + * the clk framework may need to enable/disable usbphy's parent + */ + clks[IMX6SX_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + clks[IMX6SX_CLK_USBPHY2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + + /* + * usbphy*_gate needs to be on after system boots up, and software + * never needs to control it anymore. + */ + clks[IMX6SX_CLK_USBPHY1_GATE] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6); + clks[IMX6SX_CLK_USBPHY2_GATE] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); + + /* FIXME 100Mhz is used for pcie ref for all imx6 pcie, excepted imx6q */ + clks[IMX6SX_CLK_PCIE_REF] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 5); + clks[IMX6SX_CLK_PCIE_REF_125M] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); + + clks[IMX6SX_CLK_LVDS1_OUT] = imx_clk_gate("lvds1_out", "lvds1_sel", base + 0x160, 10); + + clks[IMX6SX_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, + base + 0xe0, 0, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + clks[IMX6SX_CLK_ENET2_REF] = clk_register_divider_table(NULL, "enet2_ref", "pll6_enet", 0, + base + 0xe0, 2, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + clks[IMX6SX_CLK_ENET2_REF_125M] = imx_clk_gate("enet2_ref_125m", "enet2_ref", base + 0xe0, 20); + + clks[IMX6SX_CLK_ENET_PTP_REF] = imx_clk_fixed_factor("enet_ptp_ref", "pll6_enet", 1, 20); + clks[IMX6SX_CLK_ENET_PTP] = imx_clk_gate("enet_ptp_25m", "enet_ptp_ref", base + 0xe0, 21); + + /* name parent_name reg idx */ + clks[IMX6SX_CLK_PLL2_PFD0] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); + clks[IMX6SX_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); + clks[IMX6SX_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2); + clks[IMX6SX_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3_594m", "pll2_bus", base + 0x100, 3); + clks[IMX6SX_CLK_PLL3_PFD0] = imx_clk_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0); + clks[IMX6SX_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1); + clks[IMX6SX_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2); + clks[IMX6SX_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3); + + /* name parent_name mult div */ + clks[IMX6SX_CLK_PLL2_198M] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2); + clks[IMX6SX_CLK_PLL3_120M] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); + clks[IMX6SX_CLK_PLL3_80M] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + clks[IMX6SX_CLK_PLL3_60M] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + clks[IMX6SX_CLK_TWD] = imx_clk_fixed_factor("twd", "arm", 1, 2); + clks[IMX6SX_CLK_GPT_3M] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8); + + clks[IMX6SX_CLK_PLL4_POST_DIV] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", + CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX6SX_CLK_PLL4_AUDIO_DIV] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", + CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); + clks[IMX6SX_CLK_PLL5_POST_DIV] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", + CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX6SX_CLK_PLL5_VIDEO_DIV] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", + CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + + /* name reg shift width parent_names num_parents */ + clks[IMX6SX_CLK_LVDS1_SEL] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); + + np = ccm_node; + base = of_iomap(np, 0); + WARN_ON(!base); + + imx6q_pm_set_ccm_base(base); + + /* name reg shift width parent_names num_parents */ + clks[IMX6SX_CLK_STEP] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + clks[IMX6SX_CLK_PLL1_SW] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); + clks[IMX6SX_CLK_OCRAM_SEL] = imx_clk_mux("ocram_sel", base + 0x14, 6, 2, ocram_sels, ARRAY_SIZE(ocram_sels)); + clks[IMX6SX_CLK_PERIPH_PRE] = imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); + clks[IMX6SX_CLK_PERIPH2_PRE] = imx_clk_mux("periph2_pre", base + 0x18, 21, 2, periph2_pre_sels, ARRAY_SIZE(periph2_pre_sels)); + clks[IMX6SX_CLK_PERIPH_CLK2_SEL] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + clks[IMX6SX_CLK_PERIPH2_CLK2_SEL] = imx_clk_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + clks[IMX6SX_CLK_PCIE_AXI_SEL] = imx_clk_mux("pcie_axi_sel", base + 0x18, 10, 1, pcie_axi_sels, ARRAY_SIZE(pcie_axi_sels)); + clks[IMX6SX_CLK_GPU_AXI_SEL] = imx_clk_mux("gpu_axi_sel", base + 0x18, 8, 2, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); + clks[IMX6SX_CLK_GPU_CORE_SEL] = imx_clk_mux("gpu_core_sel", base + 0x18, 4, 2, gpu_core_sels, ARRAY_SIZE(gpu_core_sels)); + clks[IMX6SX_CLK_EIM_SLOW_SEL] = imx_clk_mux("eim_slow_sel", base + 0x1c, 29, 2, eim_slow_sels, ARRAY_SIZE(eim_slow_sels)); + clks[IMX6SX_CLK_USDHC1_SEL] = imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clks[IMX6SX_CLK_USDHC2_SEL] = imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clks[IMX6SX_CLK_USDHC3_SEL] = imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clks[IMX6SX_CLK_USDHC4_SEL] = imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clks[IMX6SX_CLK_SSI3_SEL] = imx_clk_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + clks[IMX6SX_CLK_SSI2_SEL] = imx_clk_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + clks[IMX6SX_CLK_SSI1_SEL] = imx_clk_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + clks[IMX6SX_CLK_QSPI1_SEL] = imx_clk_mux_flags("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); + clks[IMX6SX_CLK_VID_SEL] = imx_clk_mux("vid_sel", base + 0x20, 21, 3, vid_sels, ARRAY_SIZE(vid_sels)); + clks[IMX6SX_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SX_CLK_CAN_SEL] = imx_clk_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels)); + clks[IMX6SX_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + clks[IMX6SX_CLK_QSPI2_SEL] = imx_clk_mux_flags("qspi2_sel", base + 0x2c, 15, 3, qspi2_sels, ARRAY_SIZE(qspi2_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_SPDIF_SEL] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SX_CLK_AUDIO_SEL] = imx_clk_mux("audio_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SX_CLK_ENET_PRE_SEL] = imx_clk_mux("enet_pre_sel", base + 0x34, 15, 3, enet_pre_sels, ARRAY_SIZE(enet_pre_sels)); + clks[IMX6SX_CLK_ENET_SEL] = imx_clk_mux("enet_sel", base + 0x34, 9, 3, enet_sels, ARRAY_SIZE(enet_sels)); + clks[IMX6SX_CLK_M4_PRE_SEL] = imx_clk_mux("m4_pre_sel", base + 0x34, 6, 3, m4_pre_sels, ARRAY_SIZE(m4_pre_sels)); + clks[IMX6SX_CLK_M4_SEL] = imx_clk_mux("m4_sel", base + 0x34, 0, 3, m4_sels, ARRAY_SIZE(m4_sels)); + clks[IMX6SX_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + clks[IMX6SX_CLK_LCDIF2_PRE_SEL] = imx_clk_mux("lcdif2_pre_sel", base + 0x38, 6, 3, lcdif2_pre_sels, ARRAY_SIZE(lcdif2_pre_sels)); + clks[IMX6SX_CLK_LCDIF2_SEL] = imx_clk_mux("lcdif2_sel", base + 0x38, 0, 3, lcdif2_sels, ARRAY_SIZE(lcdif2_sels)); + clks[IMX6SX_CLK_DISPLAY_SEL] = imx_clk_mux("display_sel", base + 0x3c, 14, 2, display_sels, ARRAY_SIZE(display_sels)); + clks[IMX6SX_CLK_CSI_SEL] = imx_clk_mux("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels)); + clks[IMX6SX_CLK_CKO1_SEL] = imx_clk_mux("cko1_sel", base + 0x60, 0, 4, cko1_sels, ARRAY_SIZE(cko1_sels)); + clks[IMX6SX_CLK_CKO2_SEL] = imx_clk_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels)); + clks[IMX6SX_CLK_CKO] = imx_clk_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels)); + + clks[IMX6SX_CLK_LDB_DI1_DIV_SEL] = imx_clk_mux_flags("ldb_di1_div_sel", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_LDB_DI0_DIV_SEL] = imx_clk_mux_flags("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di1_sels, ARRAY_SIZE(ldb_di1_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_LCDIF1_PRE_SEL] = imx_clk_mux_flags("lcdif1_pre_sel", base + 0x38, 15, 3, lcdif1_pre_sels, ARRAY_SIZE(lcdif1_pre_sels), CLK_SET_RATE_PARENT); + clks[IMX6SX_CLK_LCDIF1_SEL] = imx_clk_mux_flags("lcdif1_sel", base + 0x38, 9, 3, lcdif1_sels, ARRAY_SIZE(lcdif1_sels), CLK_SET_RATE_PARENT); + + /* name parent_name reg shift width */ + clks[IMX6SX_CLK_PERIPH_CLK2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); + clks[IMX6SX_CLK_PERIPH2_CLK2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); + clks[IMX6SX_CLK_IPG] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); + clks[IMX6SX_CLK_GPU_CORE_PODF] = imx_clk_divider("gpu_core_podf", "gpu_core_sel", base + 0x18, 29, 3); + clks[IMX6SX_CLK_GPU_AXI_PODF] = imx_clk_divider("gpu_axi_podf", "gpu_axi_sel", base + 0x18, 26, 3); + clks[IMX6SX_CLK_LCDIF1_PODF] = imx_clk_divider("lcdif1_podf", "lcdif1_pred", base + 0x18, 23, 3); + clks[IMX6SX_CLK_QSPI1_PODF] = imx_clk_divider("qspi1_podf", "qspi1_sel", base + 0x1c, 26, 3); + clks[IMX6SX_CLK_EIM_SLOW_PODF] = imx_clk_divider("eim_slow_podf", "eim_slow_sel", base + 0x1c, 23, 3); + clks[IMX6SX_CLK_LCDIF2_PODF] = imx_clk_divider("lcdif2_podf", "lcdif2_pred", base + 0x1c, 20, 3); + clks[IMX6SX_CLK_PERCLK] = imx_clk_divider("perclk", "perclk_sel", base + 0x1c, 0, 6); + clks[IMX6SX_CLK_VID_PODF] = imx_clk_divider("vid_podf", "vid_sel", base + 0x20, 24, 2); + clks[IMX6SX_CLK_CAN_PODF] = imx_clk_divider("can_podf", "can_sel", base + 0x20, 2, 6); + clks[IMX6SX_CLK_USDHC4_PODF] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); + clks[IMX6SX_CLK_USDHC3_PODF] = imx_clk_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); + clks[IMX6SX_CLK_USDHC2_PODF] = imx_clk_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + clks[IMX6SX_CLK_USDHC1_PODF] = imx_clk_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + clks[IMX6SX_CLK_UART_PODF] = imx_clk_divider("uart_podf", "uart_sel", base + 0x24, 0, 6); + clks[IMX6SX_CLK_ESAI_PRED] = imx_clk_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); + clks[IMX6SX_CLK_ESAI_PODF] = imx_clk_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); + clks[IMX6SX_CLK_SSI3_PRED] = imx_clk_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); + clks[IMX6SX_CLK_SSI3_PODF] = imx_clk_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); + clks[IMX6SX_CLK_SSI1_PRED] = imx_clk_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); + clks[IMX6SX_CLK_SSI1_PODF] = imx_clk_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); + clks[IMX6SX_CLK_QSPI2_PRED] = imx_clk_divider("qspi2_pred", "qspi2_sel", base + 0x2c, 18, 3); + clks[IMX6SX_CLK_QSPI2_PODF] = imx_clk_divider("qspi2_podf", "qspi2_pred", base + 0x2c, 21, 6); + clks[IMX6SX_CLK_SSI2_PRED] = imx_clk_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); + clks[IMX6SX_CLK_SSI2_PODF] = imx_clk_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); + clks[IMX6SX_CLK_SPDIF_PRED] = imx_clk_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3); + clks[IMX6SX_CLK_SPDIF_PODF] = imx_clk_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3); + clks[IMX6SX_CLK_AUDIO_PRED] = imx_clk_divider("audio_pred", "audio_sel", base + 0x30, 12, 3); + clks[IMX6SX_CLK_AUDIO_PODF] = imx_clk_divider("audio_podf", "audio_pred", base + 0x30, 9, 3); + clks[IMX6SX_CLK_ENET_PODF] = imx_clk_divider("enet_podf", "enet_pre_sel", base + 0x34, 12, 3); + clks[IMX6SX_CLK_M4_PODF] = imx_clk_divider("m4_podf", "m4_sel", base + 0x34, 3, 3); + clks[IMX6SX_CLK_ECSPI_PODF] = imx_clk_divider("ecspi_podf", "ecspi_sel", base + 0x38, 19, 6); + clks[IMX6SX_CLK_LCDIF1_PRED] = imx_clk_divider("lcdif1_pred", "lcdif1_pre_sel", base + 0x38, 12, 3); + clks[IMX6SX_CLK_LCDIF2_PRED] = imx_clk_divider("lcdif2_pred", "lcdif2_pre_sel", base + 0x38, 3, 3); + clks[IMX6SX_CLK_DISPLAY_PODF] = imx_clk_divider("display_podf", "display_sel", base + 0x3c, 16, 3); + clks[IMX6SX_CLK_CSI_PODF] = imx_clk_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); + clks[IMX6SX_CLK_CKO1_PODF] = imx_clk_divider("cko1_podf", "cko1_sel", base + 0x60, 4, 3); + clks[IMX6SX_CLK_CKO2_PODF] = imx_clk_divider("cko2_podf", "cko2_sel", base + 0x60, 21, 3); + + clks[IMX6SX_CLK_LDB_DI0_DIV_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); + clks[IMX6SX_CLK_LDB_DI0_DIV_7] = imx_clk_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7); + clks[IMX6SX_CLK_LDB_DI1_DIV_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); + clks[IMX6SX_CLK_LDB_DI1_DIV_7] = imx_clk_fixed_factor("ldb_di1_div_7", "ldb_di1_sel", 1, 7); + + /* name reg shift width busy: reg, shift parent_names num_parents */ + clks[IMX6SX_CLK_PERIPH] = imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + clks[IMX6SX_CLK_PERIPH2] = imx_clk_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + /* name parent_name reg shift width busy: reg, shift */ + clks[IMX6SX_CLK_OCRAM_PODF] = imx_clk_busy_divider("ocram_podf", "ocram_sel", base + 0x14, 16, 3, base + 0x48, 0); + clks[IMX6SX_CLK_AHB] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + clks[IMX6SX_CLK_MMDC_PODF] = imx_clk_busy_divider("mmdc_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + clks[IMX6SX_CLK_ARM] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + + /* name parent_name reg shift */ + /* CCGR0 */ + clks[IMX6SX_CLK_AIPS_TZ1] = imx_clk_gate2("aips_tz1", "ahb", base + 0x68, 0); + clks[IMX6SX_CLK_AIPS_TZ2] = imx_clk_gate2("aips_tz2", "ahb", base + 0x68, 2); + clks[IMX6SX_CLK_APBH_DMA] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4); + clks[IMX6SX_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc); + clks[IMX6SX_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc); + clks[IMX6SX_CLK_CAAM_MEM] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8); + clks[IMX6SX_CLK_CAAM_ACLK] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10); + clks[IMX6SX_CLK_CAAM_IPG] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12); + clks[IMX6SX_CLK_CAN1_IPG] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14); + clks[IMX6SX_CLK_CAN1_SERIAL] = imx_clk_gate2("can1_serial", "can_podf", base + 0x68, 16); + clks[IMX6SX_CLK_CAN2_IPG] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); + clks[IMX6SX_CLK_CAN2_SERIAL] = imx_clk_gate2("can2_serial", "can_podf", base + 0x68, 20); + clks[IMX6SX_CLK_DCIC1] = imx_clk_gate2("dcic1", "display_podf", base + 0x68, 24); + clks[IMX6SX_CLK_DCIC2] = imx_clk_gate2("dcic2", "display_podf", base + 0x68, 26); + clks[IMX6SX_CLK_AIPS_TZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30); + + /* CCGR1 */ + clks[IMX6SX_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); + clks[IMX6SX_CLK_ECSPI2] = imx_clk_gate2("ecspi2", "ecspi_podf", base + 0x6c, 2); + clks[IMX6SX_CLK_ECSPI3] = imx_clk_gate2("ecspi3", "ecspi_podf", base + 0x6c, 4); + clks[IMX6SX_CLK_ECSPI4] = imx_clk_gate2("ecspi4", "ecspi_podf", base + 0x6c, 6); + clks[IMX6SX_CLK_ECSPI5] = imx_clk_gate2("ecspi5", "ecspi_podf", base + 0x6c, 8); + clks[IMX6SX_CLK_EPIT1] = imx_clk_gate2("epit1", "perclk", base + 0x6c, 12); + clks[IMX6SX_CLK_EPIT2] = imx_clk_gate2("epit2", "perclk", base + 0x6c, 14); + clks[IMX6SX_CLK_ESAI_EXTAL] = imx_clk_gate2_shared("esai_extal", "esai_podf", base + 0x6c, 16, &share_count_esai); + clks[IMX6SX_CLK_ESAI_IPG] = imx_clk_gate2_shared("esai_ipg", "ahb", base + 0x6c, 16, &share_count_esai); + clks[IMX6SX_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x6c, 16, &share_count_esai); + clks[IMX6SX_CLK_WAKEUP] = imx_clk_gate2("wakeup", "ipg", base + 0x6c, 18); + clks[IMX6SX_CLK_GPT_BUS] = imx_clk_gate2("gpt_bus", "perclk", base + 0x6c, 20); + clks[IMX6SX_CLK_GPT_SERIAL] = imx_clk_gate2("gpt_serial", "perclk", base + 0x6c, 22); + clks[IMX6SX_CLK_GPU] = imx_clk_gate2("gpu", "gpu_core_podf", base + 0x6c, 26); + clks[IMX6SX_CLK_CANFD] = imx_clk_gate2("canfd", "can_podf", base + 0x6c, 30); + + /* CCGR2 */ + clks[IMX6SX_CLK_CSI] = imx_clk_gate2("csi", "csi_podf", base + 0x70, 2); + clks[IMX6SX_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6); + clks[IMX6SX_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8); + clks[IMX6SX_CLK_I2C3] = imx_clk_gate2("i2c3", "perclk", base + 0x70, 10); + clks[IMX6SX_CLK_OCOTP] = imx_clk_gate2("ocotp", "ipg", base + 0x70, 12); + clks[IMX6SX_CLK_IOMUXC] = imx_clk_gate2("iomuxc", "lcdif1_podf", base + 0x70, 14); + clks[IMX6SX_CLK_IPMUX1] = imx_clk_gate2("ipmux1", "ahb", base + 0x70, 16); + clks[IMX6SX_CLK_IPMUX2] = imx_clk_gate2("ipmux2", "ahb", base + 0x70, 18); + clks[IMX6SX_CLK_IPMUX3] = imx_clk_gate2("ipmux3", "ahb", base + 0x70, 20); + clks[IMX6SX_CLK_TZASC1] = imx_clk_gate2("tzasc1", "mmdc_podf", base + 0x70, 22); + clks[IMX6SX_CLK_LCDIF_APB] = imx_clk_gate2("lcdif_apb", "display_podf", base + 0x70, 28); + clks[IMX6SX_CLK_PXP_AXI] = imx_clk_gate2("pxp_axi", "display_podf", base + 0x70, 30); + + /* CCGR3 */ + clks[IMX6SX_CLK_M4] = imx_clk_gate2("m4", "m4_podf", base + 0x74, 2); + clks[IMX6SX_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x74, 4); + clks[IMX6SX_CLK_ENET_AHB] = imx_clk_gate2("enet_ahb", "enet_sel", base + 0x74, 4); + clks[IMX6SX_CLK_DISPLAY_AXI] = imx_clk_gate2("display_axi", "display_podf", base + 0x74, 6); + clks[IMX6SX_CLK_LCDIF2_PIX] = imx_clk_gate2("lcdif2_pix", "lcdif2_sel", base + 0x74, 8); + clks[IMX6SX_CLK_LCDIF1_PIX] = imx_clk_gate2("lcdif1_pix", "lcdif1_sel", base + 0x74, 10); + clks[IMX6SX_CLK_LDB_DI0] = imx_clk_gate2("ldb_di0", "ldb_di0_div_sel", base + 0x74, 12); + clks[IMX6SX_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_podf", base + 0x74, 14); + clks[IMX6SX_CLK_MLB] = imx_clk_gate2("mlb", "ahb", base + 0x74, 18); + clks[IMX6SX_CLK_MMDC_P0_FAST] = imx_clk_gate2("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20); + clks[IMX6SX_CLK_MMDC_P0_IPG] = imx_clk_gate2("mmdc_p0_ipg", "ipg", base + 0x74, 24); + clks[IMX6SX_CLK_OCRAM] = imx_clk_gate2("ocram", "ocram_podf", base + 0x74, 28); + + /* CCGR4 */ + clks[IMX6SX_CLK_PCIE_AXI] = imx_clk_gate2("pcie_axi", "display_podf", base + 0x78, 0); + clks[IMX6SX_CLK_QSPI2] = imx_clk_gate2("qspi2", "qspi2_podf", base + 0x78, 10); + clks[IMX6SX_CLK_PER1_BCH] = imx_clk_gate2("per1_bch", "usdhc3", base + 0x78, 12); + clks[IMX6SX_CLK_PER2_MAIN] = imx_clk_gate2("per2_main", "ahb", base + 0x78, 14); + clks[IMX6SX_CLK_PWM1] = imx_clk_gate2("pwm1", "perclk", base + 0x78, 16); + clks[IMX6SX_CLK_PWM2] = imx_clk_gate2("pwm2", "perclk", base + 0x78, 18); + clks[IMX6SX_CLK_PWM3] = imx_clk_gate2("pwm3", "perclk", base + 0x78, 20); + clks[IMX6SX_CLK_PWM4] = imx_clk_gate2("pwm4", "perclk", base + 0x78, 22); + clks[IMX6SX_CLK_GPMI_BCH_APB] = imx_clk_gate2("gpmi_bch_apb", "usdhc3", base + 0x78, 24); + clks[IMX6SX_CLK_GPMI_BCH] = imx_clk_gate2("gpmi_bch", "usdhc4", base + 0x78, 26); + clks[IMX6SX_CLK_GPMI_IO] = imx_clk_gate2("gpmi_io", "qspi2_podf", base + 0x78, 28); + clks[IMX6SX_CLK_GPMI_APB] = imx_clk_gate2("gpmi_apb", "usdhc3", base + 0x78, 30); + + /* CCGR5 */ + clks[IMX6SX_CLK_ROM] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0); + clks[IMX6SX_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); + clks[IMX6SX_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); + clks[IMX6SX_CLK_AUDIO] = imx_clk_gate2_shared("audio", "audio_podf", base + 0x7c, 14, &share_count_audio); + clks[IMX6SX_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_audio); + clks[IMX6SX_CLK_SSI1_IPG] = imx_clk_gate2("ssi1_ipg", "ipg", base + 0x7c, 18); + clks[IMX6SX_CLK_SSI2_IPG] = imx_clk_gate2("ssi2_ipg", "ipg", base + 0x7c, 20); + clks[IMX6SX_CLK_SSI3_IPG] = imx_clk_gate2("ssi3_ipg", "ipg", base + 0x7c, 22); + clks[IMX6SX_CLK_SSI1] = imx_clk_gate2("ssi1", "ssi1_podf", base + 0x7c, 18); + clks[IMX6SX_CLK_SSI2] = imx_clk_gate2("ssi2", "ssi2_podf", base + 0x7c, 20); + clks[IMX6SX_CLK_SSI3] = imx_clk_gate2("ssi3", "ssi3_podf", base + 0x7c, 22); + clks[IMX6SX_CLK_UART_IPG] = imx_clk_gate2("uart_ipg", "ipg", base + 0x7c, 24); + clks[IMX6SX_CLK_UART_SERIAL] = imx_clk_gate2("uart_serial", "uart_podf", base + 0x7c, 26); + clks[IMX6SX_CLK_SAI1_IPG] = imx_clk_gate2("sai1_ipg", "ipg", base + 0x7c, 28); + clks[IMX6SX_CLK_SAI2_IPG] = imx_clk_gate2("sai2_ipg", "ipg", base + 0x7c, 30); + clks[IMX6SX_CLK_SAI1] = imx_clk_gate2("sai1", "ssi1_podf", base + 0x7c, 28); + clks[IMX6SX_CLK_SAI2] = imx_clk_gate2("sai2", "ssi2_podf", base + 0x7c, 30); + + /* CCGR6 */ + clks[IMX6SX_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); + clks[IMX6SX_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + clks[IMX6SX_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + clks[IMX6SX_CLK_USDHC3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); + clks[IMX6SX_CLK_USDHC4] = imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); + clks[IMX6SX_CLK_EIM_SLOW] = imx_clk_gate2("eim_slow", "eim_slow_podf", base + 0x80, 10); + clks[IMX6SX_CLK_PWM8] = imx_clk_gate2("pwm8", "perclk", base + 0x80, 16); + clks[IMX6SX_CLK_VADC] = imx_clk_gate2("vadc", "vid_podf", base + 0x80, 20); + clks[IMX6SX_CLK_GIS] = imx_clk_gate2("gis", "display_podf", base + 0x80, 22); + clks[IMX6SX_CLK_I2C4] = imx_clk_gate2("i2c4", "perclk", base + 0x80, 24); + clks[IMX6SX_CLK_PWM5] = imx_clk_gate2("pwm5", "perclk", base + 0x80, 26); + clks[IMX6SX_CLK_PWM6] = imx_clk_gate2("pwm6", "perclk", base + 0x80, 28); + clks[IMX6SX_CLK_PWM7] = imx_clk_gate2("pwm7", "perclk", base + 0x80, 30); + + clks[IMX6SX_CLK_CKO1] = imx_clk_gate("cko1", "cko1_podf", base + 0x60, 7); + clks[IMX6SX_CLK_CKO2] = imx_clk_gate("cko2", "cko2_podf", base + 0x60, 24); + + /* mask handshake of mmdc */ + writel_relaxed(BM_CCM_CCDR_MMDC_CH0_MASK, base + CCDR); + + for (i = 0; i < ARRAY_SIZE(clks); i++) + if (IS_ERR(clks[i])) + pr_err("i.MX6sx clk %d: register failed with %ld\n", i, PTR_ERR(clks[i])); + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + clk_register_clkdev(clks[IMX6SX_CLK_GPT_BUS], "ipg", "imx-gpt.0"); + clk_register_clkdev(clks[IMX6SX_CLK_GPT_SERIAL], "per", "imx-gpt.0"); + + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) + clk_prepare_enable(clks[clks_init_on[i]]); + + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(clks[IMX6SX_CLK_USBPHY1_GATE]); + clk_prepare_enable(clks[IMX6SX_CLK_USBPHY2_GATE]); + } + + /* Set the default 132MHz for EIM module */ + clk_set_parent(clks[IMX6SX_CLK_EIM_SLOW_SEL], clks[IMX6SX_CLK_PLL2_PFD2]); + clk_set_rate(clks[IMX6SX_CLK_EIM_SLOW], 132000000); + + /* set parent clock for LCDIF1 pixel clock */ + clk_set_parent(clks[IMX6SX_CLK_LCDIF1_PRE_SEL], clks[IMX6SX_CLK_PLL5_VIDEO_DIV]); + clk_set_parent(clks[IMX6SX_CLK_LCDIF1_SEL], clks[IMX6SX_CLK_LCDIF1_PODF]); + + /* Set the parent clks of PCIe lvds1 and pcie_axi to be pcie ref, axi */ + if (clk_set_parent(clks[IMX6SX_CLK_LVDS1_SEL], clks[IMX6SX_CLK_PCIE_REF_125M])) + pr_err("Failed to set pcie bus parent clk.\n"); + if (clk_set_parent(clks[IMX6SX_CLK_PCIE_AXI_SEL], clks[IMX6SX_CLK_AXI])) + pr_err("Failed to set pcie parent clk.\n"); + + /* + * Init enet system AHB clock, set to 200Mhz + * pll2_pfd2_396m-> ENET_PODF-> ENET_AHB + */ + clk_set_parent(clks[IMX6SX_CLK_ENET_PRE_SEL], clks[IMX6SX_CLK_PLL2_PFD2]); + clk_set_parent(clks[IMX6SX_CLK_ENET_SEL], clks[IMX6SX_CLK_ENET_PODF]); + clk_set_rate(clks[IMX6SX_CLK_ENET_PODF], 200000000); + clk_set_rate(clks[IMX6SX_CLK_ENET_REF], 125000000); + clk_set_rate(clks[IMX6SX_CLK_ENET2_REF], 125000000); + + /* Audio clocks */ + clk_set_rate(clks[IMX6SX_CLK_PLL4_AUDIO_DIV], 393216000); + + clk_set_parent(clks[IMX6SX_CLK_SPDIF_SEL], clks[IMX6SX_CLK_PLL4_AUDIO_DIV]); + clk_set_rate(clks[IMX6SX_CLK_SPDIF_PODF], 98304000); + + clk_set_parent(clks[IMX6SX_CLK_AUDIO_SEL], clks[IMX6SX_CLK_PLL3_USB_OTG]); + clk_set_rate(clks[IMX6SX_CLK_AUDIO_PODF], 24000000); + + clk_set_parent(clks[IMX6SX_CLK_SSI1_SEL], clks[IMX6SX_CLK_PLL4_AUDIO_DIV]); + clk_set_parent(clks[IMX6SX_CLK_SSI2_SEL], clks[IMX6SX_CLK_PLL4_AUDIO_DIV]); + clk_set_parent(clks[IMX6SX_CLK_SSI3_SEL], clks[IMX6SX_CLK_PLL4_AUDIO_DIV]); + clk_set_rate(clks[IMX6SX_CLK_SSI1_PODF], 24576000); + clk_set_rate(clks[IMX6SX_CLK_SSI2_PODF], 24576000); + clk_set_rate(clks[IMX6SX_CLK_SSI3_PODF], 24576000); + + clk_set_parent(clks[IMX6SX_CLK_ESAI_SEL], clks[IMX6SX_CLK_PLL4_AUDIO_DIV]); + clk_set_rate(clks[IMX6SX_CLK_ESAI_PODF], 24576000); + + /* Set parent clock for vadc */ + clk_set_parent(clks[IMX6SX_CLK_VID_SEL], clks[IMX6SX_CLK_PLL3_USB_OTG]); + + /* default parent of can_sel clock is invalid, manually set it here */ + clk_set_parent(clks[IMX6SX_CLK_CAN_SEL], clks[IMX6SX_CLK_PLL3_60M]); + + /* Update gpu clock from default 528M to 720M */ + clk_set_parent(clks[IMX6SX_CLK_GPU_CORE_SEL], clks[IMX6SX_CLK_PLL3_PFD0]); + clk_set_parent(clks[IMX6SX_CLK_GPU_AXI_SEL], clks[IMX6SX_CLK_PLL3_PFD0]); + + /* Set initial power mode */ + imx6q_set_lpm(WAIT_CLOCKED); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-gpt"); + mxc_timer_init_dt(np); +} +CLK_OF_DECLARE(imx6sx, "fsl,imx6sx-ccm", imx6sx_clocks_init); diff --git a/include/dt-bindings/clock/imx6sx-clock.h b/include/dt-bindings/clock/imx6sx-clock.h new file mode 100644 index 000000000000..421d8bb76f2f --- /dev/null +++ b/include/dt-bindings/clock/imx6sx-clock.h @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * 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 __DT_BINDINGS_CLOCK_IMX6SX_H +#define __DT_BINDINGS_CLOCK_IMX6SX_H + +#define IMX6SX_CLK_DUMMY 0 +#define IMX6SX_CLK_CKIL 1 +#define IMX6SX_CLK_CKIH 2 +#define IMX6SX_CLK_OSC 3 +#define IMX6SX_CLK_PLL1_SYS 4 +#define IMX6SX_CLK_PLL2_BUS 5 +#define IMX6SX_CLK_PLL3_USB_OTG 6 +#define IMX6SX_CLK_PLL4_AUDIO 7 +#define IMX6SX_CLK_PLL5_VIDEO 8 +#define IMX6SX_CLK_PLL6_ENET 9 +#define IMX6SX_CLK_PLL7_USB_HOST 10 +#define IMX6SX_CLK_USBPHY1 11 +#define IMX6SX_CLK_USBPHY2 12 +#define IMX6SX_CLK_USBPHY1_GATE 13 +#define IMX6SX_CLK_USBPHY2_GATE 14 +#define IMX6SX_CLK_PCIE_REF 15 +#define IMX6SX_CLK_PCIE_REF_125M 16 +#define IMX6SX_CLK_ENET_REF 17 +#define IMX6SX_CLK_PLL2_PFD0 18 +#define IMX6SX_CLK_PLL2_PFD1 19 +#define IMX6SX_CLK_PLL2_PFD2 20 +#define IMX6SX_CLK_PLL2_PFD3 21 +#define IMX6SX_CLK_PLL3_PFD0 22 +#define IMX6SX_CLK_PLL3_PFD1 23 +#define IMX6SX_CLK_PLL3_PFD2 24 +#define IMX6SX_CLK_PLL3_PFD3 25 +#define IMX6SX_CLK_PLL2_198M 26 +#define IMX6SX_CLK_PLL3_120M 27 +#define IMX6SX_CLK_PLL3_80M 28 +#define IMX6SX_CLK_PLL3_60M 29 +#define IMX6SX_CLK_TWD 30 +#define IMX6SX_CLK_PLL4_POST_DIV 31 +#define IMX6SX_CLK_PLL4_AUDIO_DIV 32 +#define IMX6SX_CLK_PLL5_POST_DIV 33 +#define IMX6SX_CLK_PLL5_VIDEO_DIV 34 +#define IMX6SX_CLK_STEP 35 +#define IMX6SX_CLK_PLL1_SW 36 +#define IMX6SX_CLK_OCRAM_SEL 37 +#define IMX6SX_CLK_PERIPH_PRE 38 +#define IMX6SX_CLK_PERIPH2_PRE 39 +#define IMX6SX_CLK_PERIPH_CLK2_SEL 40 +#define IMX6SX_CLK_PERIPH2_CLK2_SEL 41 +#define IMX6SX_CLK_PCIE_AXI_SEL 42 +#define IMX6SX_CLK_GPU_AXI_SEL 43 +#define IMX6SX_CLK_GPU_CORE_SEL 44 +#define IMX6SX_CLK_EIM_SLOW_SEL 45 +#define IMX6SX_CLK_USDHC1_SEL 46 +#define IMX6SX_CLK_USDHC2_SEL 47 +#define IMX6SX_CLK_USDHC3_SEL 48 +#define IMX6SX_CLK_USDHC4_SEL 49 +#define IMX6SX_CLK_SSI1_SEL 50 +#define IMX6SX_CLK_SSI2_SEL 51 +#define IMX6SX_CLK_SSI3_SEL 52 +#define IMX6SX_CLK_QSPI1_SEL 53 +#define IMX6SX_CLK_PERCLK_SEL 54 +#define IMX6SX_CLK_VID_SEL 55 +#define IMX6SX_CLK_ESAI_SEL 56 +#define IMX6SX_CLK_LDB_DI0_DIV_SEL 57 +#define IMX6SX_CLK_LDB_DI1_DIV_SEL 58 +#define IMX6SX_CLK_CAN_SEL 59 +#define IMX6SX_CLK_UART_SEL 60 +#define IMX6SX_CLK_QSPI2_SEL 61 +#define IMX6SX_CLK_LDB_DI1_SEL 62 +#define IMX6SX_CLK_LDB_DI0_SEL 63 +#define IMX6SX_CLK_SPDIF_SEL 64 +#define IMX6SX_CLK_AUDIO_SEL 65 +#define IMX6SX_CLK_ENET_PRE_SEL 66 +#define IMX6SX_CLK_ENET_SEL 67 +#define IMX6SX_CLK_M4_PRE_SEL 68 +#define IMX6SX_CLK_M4_SEL 69 +#define IMX6SX_CLK_ECSPI_SEL 70 +#define IMX6SX_CLK_LCDIF1_PRE_SEL 71 +#define IMX6SX_CLK_LCDIF2_PRE_SEL 72 +#define IMX6SX_CLK_LCDIF1_SEL 73 +#define IMX6SX_CLK_LCDIF2_SEL 74 +#define IMX6SX_CLK_DISPLAY_SEL 75 +#define IMX6SX_CLK_CSI_SEL 76 +#define IMX6SX_CLK_CKO1_SEL 77 +#define IMX6SX_CLK_CKO2_SEL 78 +#define IMX6SX_CLK_CKO 79 +#define IMX6SX_CLK_PERIPH_CLK2 80 +#define IMX6SX_CLK_PERIPH2_CLK2 81 +#define IMX6SX_CLK_IPG 82 +#define IMX6SX_CLK_GPU_CORE_PODF 83 +#define IMX6SX_CLK_GPU_AXI_PODF 84 +#define IMX6SX_CLK_LCDIF1_PODF 85 +#define IMX6SX_CLK_QSPI1_PODF 86 +#define IMX6SX_CLK_EIM_SLOW_PODF 87 +#define IMX6SX_CLK_LCDIF2_PODF 88 +#define IMX6SX_CLK_PERCLK 89 +#define IMX6SX_CLK_VID_PODF 90 +#define IMX6SX_CLK_CAN_PODF 91 +#define IMX6SX_CLK_USDHC1_PODF 92 +#define IMX6SX_CLK_USDHC2_PODF 93 +#define IMX6SX_CLK_USDHC3_PODF 94 +#define IMX6SX_CLK_USDHC4_PODF 95 +#define IMX6SX_CLK_UART_PODF 96 +#define IMX6SX_CLK_ESAI_PRED 97 +#define IMX6SX_CLK_ESAI_PODF 98 +#define IMX6SX_CLK_SSI3_PRED 99 +#define IMX6SX_CLK_SSI3_PODF 100 +#define IMX6SX_CLK_SSI1_PRED 101 +#define IMX6SX_CLK_SSI1_PODF 102 +#define IMX6SX_CLK_QSPI2_PRED 103 +#define IMX6SX_CLK_QSPI2_PODF 104 +#define IMX6SX_CLK_SSI2_PRED 105 +#define IMX6SX_CLK_SSI2_PODF 106 +#define IMX6SX_CLK_SPDIF_PRED 107 +#define IMX6SX_CLK_SPDIF_PODF 108 +#define IMX6SX_CLK_AUDIO_PRED 109 +#define IMX6SX_CLK_AUDIO_PODF 110 +#define IMX6SX_CLK_ENET_PODF 111 +#define IMX6SX_CLK_M4_PODF 112 +#define IMX6SX_CLK_ECSPI_PODF 113 +#define IMX6SX_CLK_LCDIF1_PRED 114 +#define IMX6SX_CLK_LCDIF2_PRED 115 +#define IMX6SX_CLK_DISPLAY_PODF 116 +#define IMX6SX_CLK_CSI_PODF 117 +#define IMX6SX_CLK_LDB_DI0_DIV_3_5 118 +#define IMX6SX_CLK_LDB_DI0_DIV_7 119 +#define IMX6SX_CLK_LDB_DI1_DIV_3_5 120 +#define IMX6SX_CLK_LDB_DI1_DIV_7 121 +#define IMX6SX_CLK_CKO1_PODF 122 +#define IMX6SX_CLK_CKO2_PODF 123 +#define IMX6SX_CLK_PERIPH 124 +#define IMX6SX_CLK_PERIPH2 125 +#define IMX6SX_CLK_OCRAM 126 +#define IMX6SX_CLK_AHB 127 +#define IMX6SX_CLK_MMDC_PODF 128 +#define IMX6SX_CLK_ARM 129 +#define IMX6SX_CLK_AIPS_TZ1 130 +#define IMX6SX_CLK_AIPS_TZ2 131 +#define IMX6SX_CLK_APBH_DMA 132 +#define IMX6SX_CLK_ASRC_GATE 133 +#define IMX6SX_CLK_CAAM_MEM 134 +#define IMX6SX_CLK_CAAM_ACLK 135 +#define IMX6SX_CLK_CAAM_IPG 136 +#define IMX6SX_CLK_CAN1_IPG 137 +#define IMX6SX_CLK_CAN1_SERIAL 138 +#define IMX6SX_CLK_CAN2_IPG 139 +#define IMX6SX_CLK_CAN2_SERIAL 140 +#define IMX6SX_CLK_CPU_DEBUG 141 +#define IMX6SX_CLK_DCIC1 142 +#define IMX6SX_CLK_DCIC2 143 +#define IMX6SX_CLK_AIPS_TZ3 144 +#define IMX6SX_CLK_ECSPI1 145 +#define IMX6SX_CLK_ECSPI2 146 +#define IMX6SX_CLK_ECSPI3 147 +#define IMX6SX_CLK_ECSPI4 148 +#define IMX6SX_CLK_ECSPI5 149 +#define IMX6SX_CLK_EPIT1 150 +#define IMX6SX_CLK_EPIT2 151 +#define IMX6SX_CLK_ESAI_EXTAL 152 +#define IMX6SX_CLK_WAKEUP 153 +#define IMX6SX_CLK_GPT_BUS 154 +#define IMX6SX_CLK_GPT_SERIAL 155 +#define IMX6SX_CLK_GPU 156 +#define IMX6SX_CLK_OCRAM_S 157 +#define IMX6SX_CLK_CANFD 158 +#define IMX6SX_CLK_CSI 159 +#define IMX6SX_CLK_I2C1 160 +#define IMX6SX_CLK_I2C2 161 +#define IMX6SX_CLK_I2C3 162 +#define IMX6SX_CLK_OCOTP 163 +#define IMX6SX_CLK_IOMUXC 164 +#define IMX6SX_CLK_IPMUX1 165 +#define IMX6SX_CLK_IPMUX2 166 +#define IMX6SX_CLK_IPMUX3 167 +#define IMX6SX_CLK_TZASC1 168 +#define IMX6SX_CLK_LCDIF_APB 169 +#define IMX6SX_CLK_PXP_AXI 170 +#define IMX6SX_CLK_M4 171 +#define IMX6SX_CLK_ENET 172 +#define IMX6SX_CLK_DISPLAY_AXI 173 +#define IMX6SX_CLK_LCDIF2_PIX 174 +#define IMX6SX_CLK_LCDIF1_PIX 175 +#define IMX6SX_CLK_LDB_DI0 176 +#define IMX6SX_CLK_QSPI1 177 +#define IMX6SX_CLK_MLB 178 +#define IMX6SX_CLK_MMDC_P0_FAST 179 +#define IMX6SX_CLK_MMDC_P0_IPG 180 +#define IMX6SX_CLK_AXI 181 +#define IMX6SX_CLK_PCIE_AXI 182 +#define IMX6SX_CLK_QSPI2 183 +#define IMX6SX_CLK_PER1_BCH 184 +#define IMX6SX_CLK_PER2_MAIN 185 +#define IMX6SX_CLK_PWM1 186 +#define IMX6SX_CLK_PWM2 187 +#define IMX6SX_CLK_PWM3 188 +#define IMX6SX_CLK_PWM4 189 +#define IMX6SX_CLK_GPMI_BCH_APB 190 +#define IMX6SX_CLK_GPMI_BCH 191 +#define IMX6SX_CLK_GPMI_IO 192 +#define IMX6SX_CLK_GPMI_APB 193 +#define IMX6SX_CLK_ROM 194 +#define IMX6SX_CLK_SDMA 195 +#define IMX6SX_CLK_SPBA 196 +#define IMX6SX_CLK_SPDIF 197 +#define IMX6SX_CLK_SSI1_IPG 198 +#define IMX6SX_CLK_SSI2_IPG 199 +#define IMX6SX_CLK_SSI3_IPG 200 +#define IMX6SX_CLK_SSI1 201 +#define IMX6SX_CLK_SSI2 202 +#define IMX6SX_CLK_SSI3 203 +#define IMX6SX_CLK_UART_IPG 204 +#define IMX6SX_CLK_UART_SERIAL 205 +#define IMX6SX_CLK_SAI1 206 +#define IMX6SX_CLK_SAI2 207 +#define IMX6SX_CLK_USBOH3 208 +#define IMX6SX_CLK_USDHC1 209 +#define IMX6SX_CLK_USDHC2 210 +#define IMX6SX_CLK_USDHC3 211 +#define IMX6SX_CLK_USDHC4 212 +#define IMX6SX_CLK_EIM_SLOW 213 +#define IMX6SX_CLK_PWM8 214 +#define IMX6SX_CLK_VADC 215 +#define IMX6SX_CLK_GIS 216 +#define IMX6SX_CLK_I2C4 217 +#define IMX6SX_CLK_PWM5 218 +#define IMX6SX_CLK_PWM6 219 +#define IMX6SX_CLK_PWM7 220 +#define IMX6SX_CLK_CKO1 221 +#define IMX6SX_CLK_CKO2 222 +#define IMX6SX_CLK_IPP_DI0 223 +#define IMX6SX_CLK_IPP_DI1 224 +#define IMX6SX_CLK_ENET_AHB 225 +#define IMX6SX_CLK_OCRAM_PODF 226 +#define IMX6SX_CLK_GPT_3M 227 +#define IMX6SX_CLK_ENET_PTP 228 +#define IMX6SX_CLK_ENET_PTP_REF 229 +#define IMX6SX_CLK_ENET2_REF 230 +#define IMX6SX_CLK_ENET2_REF_125M 231 +#define IMX6SX_CLK_AUDIO 232 +#define IMX6SX_CLK_LVDS1_SEL 233 +#define IMX6SX_CLK_LVDS1_OUT 234 +#define IMX6SX_CLK_ASRC_IPG 235 +#define IMX6SX_CLK_ASRC_MEM 236 +#define IMX6SX_CLK_SAI1_IPG 237 +#define IMX6SX_CLK_SAI2_IPG 238 +#define IMX6SX_CLK_ESAI_IPG 239 +#define IMX6SX_CLK_ESAI_MEM 240 +#define IMX6SX_CLK_CLK_END 241 + +#endif /* __DT_BINDINGS_CLOCK_IMX6SX_H */ -- cgit v1.2.3 From 1f0b63866fc1be700260547be8edf8e6f0af37f2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 15 May 2014 23:29:57 +0200 Subject: ACPI / PM: Hold ACPI scan lock over the "freeze" sleep state The "freeze" sleep state suffers from the same issue that was addressed by commit ad07277e82de (ACPI / PM: Hold acpi_scan_lock over system PM transitions) for ACPI sleep states, that is, things break if ->remove() is called for devices whose system resume callbacks haven't been executed yet. It also can be addressed in the same way, by holding the ACPI scan lock over the "freeze" sleep state and PM transitions to and from that state, but ->begin() and ->end() platform operations for the "freeze" sleep state are needed for this purpose. This change has been tested on Acer Aspire S5 with Thunderbolt. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 18 ++++++++++++++++++ include/linux/suspend.h | 7 +++++++ kernel/power/suspend.c | 15 +++++++++++++++ 3 files changed, 40 insertions(+) (limited to 'include') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2281ca31c1bc..c11e3795431b 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -612,6 +612,22 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = { .recover = acpi_pm_finish, }; +static int acpi_freeze_begin(void) +{ + acpi_scan_lock_acquire(); + return 0; +} + +static void acpi_freeze_end(void) +{ + acpi_scan_lock_release(); +} + +static const struct platform_freeze_ops acpi_freeze_ops = { + .begin = acpi_freeze_begin, + .end = acpi_freeze_end, +}; + static void acpi_sleep_suspend_setup(void) { int i; @@ -622,7 +638,9 @@ static void acpi_sleep_suspend_setup(void) suspend_set_ops(old_suspend_ordering ? &acpi_suspend_ops_old : &acpi_suspend_ops); + freeze_set_ops(&acpi_freeze_ops); } + #else /* !CONFIG_SUSPEND */ static inline void acpi_sleep_suspend_setup(void) {} #endif /* !CONFIG_SUSPEND */ diff --git a/include/linux/suspend.h b/include/linux/suspend.h index f73cabf59012..91d66fd8dce1 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -187,6 +187,11 @@ struct platform_suspend_ops { void (*recover)(void); }; +struct platform_freeze_ops { + int (*begin)(void); + void (*end)(void); +}; + #ifdef CONFIG_SUSPEND /** * suspend_set_ops - set platform dependent suspend operations @@ -194,6 +199,7 @@ struct platform_suspend_ops { */ extern void suspend_set_ops(const struct platform_suspend_ops *ops); extern int suspend_valid_only_mem(suspend_state_t state); +extern void freeze_set_ops(const struct platform_freeze_ops *ops); extern void freeze_wake(void); /** @@ -220,6 +226,7 @@ extern int pm_suspend(suspend_state_t state); static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } +static inline void freeze_set_ops(const struct platform_freeze_ops *ops) {} static inline void freeze_wake(void) {} #endif /* !CONFIG_SUSPEND */ diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 8233cd4047d7..73a905f83972 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -38,6 +38,7 @@ const char *const pm_states[PM_SUSPEND_MAX] = { }; static const struct platform_suspend_ops *suspend_ops; +static const struct platform_freeze_ops *freeze_ops; static bool need_suspend_ops(suspend_state_t state) { @@ -47,6 +48,13 @@ static bool need_suspend_ops(suspend_state_t state) static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); static bool suspend_freeze_wake; +void freeze_set_ops(const struct platform_freeze_ops *ops) +{ + lock_system_sleep(); + freeze_ops = ops; + unlock_system_sleep(); +} + static void freeze_begin(void) { suspend_freeze_wake = false; @@ -269,6 +277,10 @@ int suspend_devices_and_enter(suspend_state_t state) error = suspend_ops->begin(state); if (error) goto Close; + } else if (state == PM_SUSPEND_FREEZE && freeze_ops->begin) { + error = freeze_ops->begin(); + if (error) + goto Close; } suspend_console(); suspend_test_start(); @@ -294,6 +306,9 @@ int suspend_devices_and_enter(suspend_state_t state) Close: if (need_suspend_ops(state) && suspend_ops->end) suspend_ops->end(); + else if (state == PM_SUSPEND_FREEZE && freeze_ops->end) + freeze_ops->end(); + trace_machine_suspend(PWR_EVENT_EXIT); return error; -- cgit v1.2.3 From 7b6ef1262549f6afc5c881aaef80beb8fd15f908 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 7 May 2014 15:44:05 +0000 Subject: genirq: Provide generic hwirq allocation facility Not really the solution to the problem, but at least it confines the mess in the core code and allows to get rid of the create/destroy_irq variants from hell, i.e. 3 implementations with different semantics plus the x86 specific variants __create_irqs and create_irq_nr which have been invented in another circle of hell. x86 : x86 should be converted to irq domains and I'm deliberately making it impossible to do the multi-vector MSI support by adding more crap to the current mess. It's not that hard to do and I'm really tired of the trainwrecks which have been invented by baindaid engineering so far. Any attempt to do multi-vector MSI or ioapic hotplug without converting to irq domains is NAKed hereby. tile: Might use irq domains as well, but it has a very limited interrupt space, so handling it via this functionality might be the right thing to do even in the long run. ia64: That's an hopeless case, as I doubt that anyone has the stomach to rewrite the homebrewn dynamic allocation facilities. I stared at it for a couple of hours and gave up. The create/destroy_irq mess could be made private to itanic right away if there wouldn't be the iommu/dmar driver being shared with x86. So to do that I'm going to add a separate ia64 specific implementation later in order not to deep-six itanic right away. Signed-off-by: Thomas Gleixner Reviewed-by: Grant Likely Cc: Tony Luck Cc: Peter Zijlstra Cc: Chris Metcalf Cc: Fenghua Yu Cc: x86@kernel.org Link: http://lkml.kernel.org/r/20140507154334.208629358@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 15 +++++++++++++++ kernel/irq/Kconfig | 5 +++++ kernel/irq/irqdesc.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 5c57efb863d0..c75dd161d37f 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -637,6 +637,21 @@ static inline int irq_reserve_irq(unsigned int irq) return irq_reserve_irqs(irq, 1); } +#ifdef CONFIG_GENERIC_IRQ_LEGACY_ALLOC_HWIRQ +unsigned int irq_alloc_hwirqs(int cnt, int node); +static inline unsigned int irq_alloc_hwirq(int node) +{ + return irq_alloc_hwirqs(1, node); +} +void irq_free_hwirqs(unsigned int from, int cnt); +static inline void irq_free_hwirq(unsigned int irq) +{ + return irq_free_hwirqs(irq, 1); +} +int arch_setup_hwirq(unsigned int irq, int node); +void arch_teardown_hwirq(unsigned int irq); +#endif + #ifndef irq_reg_writel # define irq_reg_writel(val, addr) writel(val, addr) #endif diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 07cbdfea9ae2..a83f10e406c1 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -17,6 +17,11 @@ config GENERIC_IRQ_SHOW config GENERIC_IRQ_SHOW_LEVEL bool +# Facility to allocate a hardware interrupt. This is legacy support +# and should not be used in new code. Use irq domains instead. +config GENERIC_IRQ_LEGACY_ALLOC_HWIRQ + bool + # Support for delayed migration from interrupt context config GENERIC_PENDING_IRQ bool diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index bb07f2928f4b..f388ade5e792 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -396,6 +396,57 @@ err: } EXPORT_SYMBOL_GPL(__irq_alloc_descs); +#ifdef CONFIG_GENERIC_IRQ_LEGACY_ALLOC_HWIRQ +/** + * irq_alloc_hwirqs - Allocate an irq descriptor and initialize the hardware + * @cnt: number of interrupts to allocate + * @node: node on which to allocate + * + * Returns an interrupt number > 0 or 0, if the allocation fails. + */ +unsigned int irq_alloc_hwirqs(int cnt, int node) +{ + int i, irq = __irq_alloc_descs(-1, 0, cnt, node, NULL); + + if (irq < 0) + return 0; + + for (i = irq; cnt > 0; i++, cnt--) { + if (arch_setup_hwirq(i, node)) + goto err; + irq_clear_status_flags(i, _IRQ_NOREQUEST); + } + return irq; + +err: + for (i--; i >= irq; i--) { + irq_set_status_flags(i, _IRQ_NOREQUEST | _IRQ_NOPROBE); + arch_teardown_hwirq(i); + } + irq_free_descs(irq, cnt); + return 0; +} +EXPORT_SYMBOL_GPL(irq_alloc_hwirqs); + +/** + * irq_free_hwirqs - Free irq descriptor and cleanup the hardware + * @from: Free from irq number + * @cnt: number of interrupts to free + * + */ +void irq_free_hwirqs(unsigned int from, int cnt) +{ + int i; + + for (i = from; cnt > 0; i++, cnt--) { + irq_set_status_flags(i, _IRQ_NOREQUEST | _IRQ_NOPROBE); + arch_teardown_hwirq(i); + } + irq_free_descs(from, cnt); +} +EXPORT_SYMBOL_GPL(irq_free_hwirqs); +#endif + /** * irq_reserve_irqs - mark irqs allocated * @from: mark from irq number -- cgit v1.2.3 From 54859f59fc18e5c104a4095420b3fcef8bc3ae63 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 7 May 2014 15:44:12 +0000 Subject: x86: Remove create/destroy_irq() No more users. Remove the cruft Signed-off-by: Thomas Gleixner Reviewed-by: Grant Likely Cc: Tony Luck Cc: Peter Zijlstra Cc: x86@kernel.org Link: http://lkml.kernel.org/r/20140507154336.760446122@linutronix.de Signed-off-by: Thomas Gleixner --- arch/x86/kernel/apic/io_apic.c | 106 +---------------------------------------- include/linux/irq.h | 4 -- 2 files changed, 1 insertion(+), 109 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index be3b5741badb..efda2f648f59 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -281,18 +281,6 @@ static struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node) return cfg; } -static int alloc_irqs_from(unsigned int from, unsigned int count, int node) -{ - return irq_alloc_descs_from(from, count, node); -} - -static void free_irq_at(unsigned int at, struct irq_cfg *cfg) -{ - free_irq_cfg(at, cfg); - irq_free_desc(at); -} - - struct io_apic { unsigned int index; unsigned int unused[3]; @@ -2916,100 +2904,8 @@ static int __init ioapic_init_ops(void) device_initcall(ioapic_init_ops); /* - * Dynamic irq allocate and deallocation + * Dynamic irq allocate and deallocation. Should be replaced by irq domains! */ -unsigned int __create_irqs(unsigned int from, unsigned int count, int node) -{ - struct irq_cfg **cfg; - unsigned long flags; - int irq, i; - - if (from < nr_irqs_gsi) - from = nr_irqs_gsi; - - cfg = kzalloc_node(count * sizeof(cfg[0]), GFP_KERNEL, node); - if (!cfg) - return 0; - - irq = alloc_irqs_from(from, count, node); - if (irq < 0) - goto out_cfgs; - - for (i = 0; i < count; i++) { - cfg[i] = alloc_irq_cfg(irq + i, node); - if (!cfg[i]) - goto out_irqs; - } - - raw_spin_lock_irqsave(&vector_lock, flags); - for (i = 0; i < count; i++) - if (__assign_irq_vector(irq + i, cfg[i], apic->target_cpus())) - goto out_vecs; - raw_spin_unlock_irqrestore(&vector_lock, flags); - - for (i = 0; i < count; i++) { - irq_set_chip_data(irq + i, cfg[i]); - irq_clear_status_flags(irq + i, IRQ_NOREQUEST); - } - - kfree(cfg); - return irq; - -out_vecs: - for (i--; i >= 0; i--) - __clear_irq_vector(irq + i, cfg[i]); - raw_spin_unlock_irqrestore(&vector_lock, flags); -out_irqs: - for (i = 0; i < count; i++) - free_irq_at(irq + i, cfg[i]); -out_cfgs: - kfree(cfg); - return 0; -} - -unsigned int create_irq_nr(unsigned int from, int node) -{ - return __create_irqs(from, 1, node); -} - -int create_irq(void) -{ - int node = cpu_to_node(0); - unsigned int irq_want; - int irq; - - irq_want = nr_irqs_gsi; - irq = create_irq_nr(irq_want, node); - - if (irq == 0) - irq = -1; - - return irq; -} - -void destroy_irq(unsigned int irq) -{ - struct irq_cfg *cfg = irq_get_chip_data(irq); - unsigned long flags; - - irq_set_status_flags(irq, IRQ_NOREQUEST|IRQ_NOPROBE); - - free_remapped_irq(irq); - - raw_spin_lock_irqsave(&vector_lock, flags); - __clear_irq_vector(irq, cfg); - raw_spin_unlock_irqrestore(&vector_lock, flags); - free_irq_at(irq, cfg); -} - -void destroy_irqs(unsigned int irq, unsigned int count) -{ - unsigned int i; - - for (i = 0; i < count; i++) - destroy_irq(irq + i); -} - int arch_setup_hwirq(unsigned int irq, int node) { struct irq_cfg *cfg; diff --git a/include/linux/irq.h b/include/linux/irq.h index c75dd161d37f..7549ed59d3d4 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -526,12 +526,8 @@ static inline void irq_set_percpu_devid_flags(unsigned int irq) } /* Handle dynamic irq creation and destruction */ -extern unsigned int create_irq_nr(unsigned int irq_want, int node); -extern unsigned int __create_irqs(unsigned int from, unsigned int count, - int node); extern int create_irq(void); extern void destroy_irq(unsigned int irq); -extern void destroy_irqs(unsigned int irq, unsigned int count); /* * Dynamic irq helper functions. Obsolete. Use irq_alloc_desc* and -- cgit v1.2.3 From e8784e4f9a578344023ae4e08a509b7c5eab5eb0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 7 May 2014 15:44:17 +0000 Subject: genirq: Make create/destroy_irq() ia64 private No more users outside of itanic. Confine it. Signed-off-by: Thomas Gleixner Reviewed-by: Grant Likely Tested-by: Tony Luck Cc: Peter Zijlstra Cc: Fenghua Yu Link: http://lkml.kernel.org/r/20140507154338.700598389@linutronix.de Signed-off-by: Thomas Gleixner --- arch/ia64/include/asm/irq.h | 3 +++ include/linux/irq.h | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/arch/ia64/include/asm/irq.h b/arch/ia64/include/asm/irq.h index 91b920fd7d53..820667cbea7e 100644 --- a/arch/ia64/include/asm/irq.h +++ b/arch/ia64/include/asm/irq.h @@ -31,4 +31,7 @@ bool is_affinity_mask_valid(const struct cpumask *cpumask); #define is_affinity_mask_valid is_affinity_mask_valid +int create_irq(void); +void destroy_irq(unsigned int irq); + #endif /* _ASM_IA64_IRQ_H */ diff --git a/include/linux/irq.h b/include/linux/irq.h index 7549ed59d3d4..ac9634286f42 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -525,10 +525,6 @@ static inline void irq_set_percpu_devid_flags(unsigned int irq) IRQ_NOPROBE | IRQ_PER_CPU_DEVID); } -/* Handle dynamic irq creation and destruction */ -extern int create_irq(void); -extern void destroy_irq(unsigned int irq); - /* * Dynamic irq helper functions. Obsolete. Use irq_alloc_desc* and * irq_free_desc instead. -- cgit v1.2.3 From 1d008353ba088fdec0b2a944e140ff9154a5fb20 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 7 May 2014 15:44:21 +0000 Subject: genirq: Remove irq_reserve_irq[s] No more users. And it's not going to come back. If you need hotplugable irq chips, use irq domains. Signed-off-by: Thomas Gleixner Reviewed-and-acked-by: Grant Likely Tested-by: Tony Luck Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20140507154340.302183048@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 7 ------- kernel/irq/irqdesc.c | 25 ------------------------- 2 files changed, 32 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index ac9634286f42..2110f46fcafa 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -617,18 +617,11 @@ int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, irq_alloc_descs(-1, from, cnt, node) void irq_free_descs(unsigned int irq, unsigned int cnt); -int irq_reserve_irqs(unsigned int from, unsigned int cnt); - static inline void irq_free_desc(unsigned int irq) { irq_free_descs(irq, 1); } -static inline int irq_reserve_irq(unsigned int irq) -{ - return irq_reserve_irqs(irq, 1); -} - #ifdef CONFIG_GENERIC_IRQ_LEGACY_ALLOC_HWIRQ unsigned int irq_alloc_hwirqs(int cnt, int node); static inline unsigned int irq_alloc_hwirq(int node) diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 24029348729b..d514ed6080e1 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -454,31 +454,6 @@ void irq_free_hwirqs(unsigned int from, int cnt) EXPORT_SYMBOL_GPL(irq_free_hwirqs); #endif -/** - * irq_reserve_irqs - mark irqs allocated - * @from: mark from irq number - * @cnt: number of irqs to mark - * - * Returns 0 on success or an appropriate error code - */ -int irq_reserve_irqs(unsigned int from, unsigned int cnt) -{ - unsigned int start; - int ret = 0; - - if (!cnt || (from + cnt) > nr_irqs) - return -EINVAL; - - mutex_lock(&sparse_irq_lock); - start = bitmap_find_next_zero_area(allocated_irqs, nr_irqs, from, cnt, 0); - if (start == from) - bitmap_set(allocated_irqs, start, cnt); - else - ret = -EEXIST; - mutex_unlock(&sparse_irq_lock); - return ret; -} - /** * irq_get_next_irq - get next allocated irq number * @offset: where to start the search -- cgit v1.2.3 From c940e01c94e73a2a5318f1b82038e0746aaec753 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 7 May 2014 15:44:22 +0000 Subject: genirq: Replace dynamic_irq_init/cleanup Create a new interface and confine it with a config switch which makes clear that this is just legacy support and not to be used for new code. Signed-off-by: Thomas Gleixner Reviewed-by: Grant Likely Tested-by: Tony Luck Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20140507154340.574437049@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 4 ++++ kernel/irq/Kconfig | 4 ++++ kernel/irq/irqdesc.c | 7 +++++++ 3 files changed, 15 insertions(+) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 2110f46fcafa..8ff71d14365a 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -637,6 +637,10 @@ int arch_setup_hwirq(unsigned int irq, int node); void arch_teardown_hwirq(unsigned int irq); #endif +#ifdef CONFIG_GENERIC_IRQ_LEGACY +void irq_init_desc(unsigned int irq); +#endif + #ifndef irq_reg_writel # define irq_reg_writel(val, addr) writel(val, addr) #endif diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index a83f10e406c1..d269cecdfbf0 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -5,6 +5,10 @@ menu "IRQ subsystem" config MAY_HAVE_SPARSE_IRQ bool +# Legacy support, required for itanic +config GENERIC_IRQ_LEGACY + bool + # Enable the generic irq autoprobe mechanism config GENERIC_IRQ_PROBE bool diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index d514ed6080e1..7f267799a717 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -306,6 +306,13 @@ void irq_mark_irq(unsigned int irq) mutex_unlock(&sparse_irq_lock); } +#ifdef CONFIG_GENERIC_IRQ_LEGACY +void irq_init_desc(unsigned int irq) +{ + dynamic_irq_cleanup(irq); +} +#endif + #endif /* !CONFIG_SPARSE_IRQ */ /** -- cgit v1.2.3 From d8179bc0db8d0c9654d5de43de2874bf6d0a58fa Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 7 May 2014 15:44:23 +0000 Subject: genirq: Remove dynamic_irq mess No more users. Get rid of the cruft. Signed-off-by: Thomas Gleixner Reviewed-by: Grant Likely Tested-by: Tony Luck Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20140507154341.012847637@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/irq.h | 10 ---------- kernel/irq/irqdesc.c | 23 +++++++---------------- 2 files changed, 7 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 8ff71d14365a..0d998d8b01d8 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -525,16 +525,6 @@ static inline void irq_set_percpu_devid_flags(unsigned int irq) IRQ_NOPROBE | IRQ_PER_CPU_DEVID); } -/* - * Dynamic irq helper functions. Obsolete. Use irq_alloc_desc* and - * irq_free_desc instead. - */ -extern void dynamic_irq_cleanup(unsigned int irq); -static inline void dynamic_irq_init(unsigned int irq) -{ - dynamic_irq_cleanup(irq); -} - /* Set/get chip/data for an IRQ: */ extern int irq_set_chip(unsigned int irq, struct irq_chip *chip); extern int irq_set_handler_data(unsigned int irq, void *data); diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 7f267799a717..7339e42a85ab 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -278,7 +278,12 @@ EXPORT_SYMBOL(irq_to_desc); static void free_desc(unsigned int irq) { - dynamic_irq_cleanup(irq); + struct irq_desc *desc = irq_to_desc(irq); + unsigned long flags; + + raw_spin_lock_irqsave(&desc->lock, flags); + desc_set_defaults(irq, desc, desc_node(desc), NULL); + raw_spin_unlock_irqrestore(&desc->lock, flags); } static inline int alloc_descs(unsigned int start, unsigned int cnt, int node, @@ -309,7 +314,7 @@ void irq_mark_irq(unsigned int irq) #ifdef CONFIG_GENERIC_IRQ_LEGACY void irq_init_desc(unsigned int irq) { - dynamic_irq_cleanup(irq); + free_desc(irq); } #endif @@ -522,20 +527,6 @@ int irq_set_percpu_devid(unsigned int irq) return 0; } -/** - * dynamic_irq_cleanup - cleanup a dynamically allocated irq - * @irq: irq number to initialize - */ -void dynamic_irq_cleanup(unsigned int irq) -{ - struct irq_desc *desc = irq_to_desc(irq); - unsigned long flags; - - raw_spin_lock_irqsave(&desc->lock, flags); - desc_set_defaults(irq, desc, desc_node(desc), NULL); - raw_spin_unlock_irqrestore(&desc->lock, flags); -} - void kstat_incr_irq_this_cpu(unsigned int irq) { kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); -- cgit v1.2.3 From cdf86cd233207ed992a647f0b9d42c60735756e7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 8 May 2014 15:42:25 +0200 Subject: gpio: include linux/bug.h in interface header Today's linux-next kernel started showing build errors for the use of WARN_ON in linux/gpio/consumer.h: In file included from drivers/video/backlight/pwm_bl.c:13:0: include/linux/gpio/consumer.h: In function 'gpiod_put': include/linux/gpio/consumer.h:81:2: error: implicit declaration of function 'WARN_ON' [-Werror=implicit-function-declaration] It's not clear why this never happened before, but this patch fixes it by including the header that contains the defintion of this macro. Signed-off-by: Arnd Bergmann Acked-by: Alexandre Courbot Signed-off-by: Linus Walleij --- include/linux/gpio/consumer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 6a37ef0dc59c..05e53ccb708b 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -1,6 +1,7 @@ #ifndef __LINUX_GPIO_CONSUMER_H #define __LINUX_GPIO_CONSUMER_H +#include #include #include -- cgit v1.2.3 From 3b514d24e200fcdcde0a57c354a51d3677a86743 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:47 -0400 Subject: cgroup: skip refcnting on normal root csses and cgrp_dfl_root self css 9395a4500404 ("cgroup: enable refcnting for root csses") enabled reference counting for root csses (cgroup_subsys_states) so that cgroup's self csses can be used to manage the lifetime of the containing cgroups. Unfortunately, this change was incorrect. During early init, cgrp_dfl_root self css refcnt is used. percpu_ref can't initialized during early init and its initialization is deferred till cgroup_init() time. This means that cpu was using percpu_ref which wasn't properly initialized. Due to the way percpu variables are laid out on x86, this didn't blow up immediately on x86 but ended up incrementing and decrementing the percpu variable at offset zero, whatever it may be; however, on other archs, this caused fault and early boot failure. As cgroup self csses for root cgroups of non-dfl hierarchies need working refcounting, we can't revert 9395a4500404. This patch adds CSS_NO_REF which explicitly inhibits reference counting on the css and sets it on all normal (non-self) csses and cgroup_dfl_root self css. v2: cgrp_dfl_root.self is the offending one. Set the flag on it. Signed-off-by: Tejun Heo Reported-by: Stephen Warren Tested-by: Stephen Warren Fixes: 9395a4500404 ("cgroup: enable refcnting for root csses") --- include/linux/cgroup.h | 11 ++++++++--- kernel/cgroup.c | 11 +++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 76dadd77a120..1737db0c63fe 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -77,6 +77,7 @@ struct cgroup_subsys_state { /* bits in struct cgroup_subsys_state flags field */ enum { + CSS_NO_REF = (1 << 0), /* no reference counting for this css */ CSS_ONLINE = (1 << 1), /* between ->css_online() and ->css_offline() */ }; @@ -88,7 +89,8 @@ enum { */ static inline void css_get(struct cgroup_subsys_state *css) { - percpu_ref_get(&css->refcnt); + if (!(css->flags & CSS_NO_REF)) + percpu_ref_get(&css->refcnt); } /** @@ -103,7 +105,9 @@ static inline void css_get(struct cgroup_subsys_state *css) */ static inline bool css_tryget_online(struct cgroup_subsys_state *css) { - return percpu_ref_tryget_live(&css->refcnt); + if (!(css->flags & CSS_NO_REF)) + return percpu_ref_tryget_live(&css->refcnt); + return true; } /** @@ -114,7 +118,8 @@ static inline bool css_tryget_online(struct cgroup_subsys_state *css) */ static inline void css_put(struct cgroup_subsys_state *css) { - percpu_ref_put(&css->refcnt); + if (!(css->flags & CSS_NO_REF)) + percpu_ref_put(&css->refcnt); } /* bits in struct cgroup flags field */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index c01e8e8dfad0..0343d7ee6d62 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4593,11 +4593,17 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) /* We don't handle early failures gracefully */ BUG_ON(IS_ERR(css)); init_and_link_css(css, ss, &cgrp_dfl_root.cgrp); + + /* + * Root csses are never destroyed and we can't initialize + * percpu_ref during early init. Disable refcnting. + */ + css->flags |= CSS_NO_REF; + if (early) { /* allocation can't be done safely during early init */ css->id = 1; } else { - BUG_ON(percpu_ref_init(&css->refcnt, css_release)); css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, GFP_KERNEL); BUG_ON(css->id < 0); } @@ -4636,6 +4642,8 @@ int __init cgroup_init_early(void) int i; init_cgroup_root(&cgrp_dfl_root, &opts); + cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF; + RCU_INIT_POINTER(init_task.cgroups, &init_css_set); for_each_subsys(ss, i) { @@ -4684,7 +4692,6 @@ int __init cgroup_init(void) struct cgroup_subsys_state *css = init_css_set.subsys[ss->id]; - BUG_ON(percpu_ref_init(&css->refcnt, css_release)); css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, GFP_KERNEL); BUG_ON(css->id < 0); -- cgit v1.2.3 From 5c9d535b893f30266ea29fe377cb9b002fcd76aa Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:48 -0400 Subject: cgroup: remove css_parent() cgroup in general is moving towards using cgroup_subsys_state as the fundamental structural component and css_parent() was introduced to convert from using cgroup->parent to css->parent. It was quite some time ago and we're moving forward with making css more prominent. This patch drops the trivial wrapper css_parent() and let the users dereference css->parent. While at it, explicitly mark fields of css which are public and immutable. v2: New usage from device_cgroup.c converted. Signed-off-by: Tejun Heo Acked-by: Michal Hocko Acked-by: Neil Horman Acked-by: "David S. Miller" Acked-by: Li Zefan Cc: Vivek Goyal Cc: Jens Axboe Cc: Peter Zijlstra Cc: Johannes Weiner --- block/blk-cgroup.h | 2 +- include/linux/cgroup.h | 29 +++++++++++------------------ kernel/cgroup.c | 8 ++++---- kernel/cgroup_freezer.c | 2 +- kernel/cpuset.c | 2 +- kernel/sched/core.c | 2 +- kernel/sched/cpuacct.c | 2 +- mm/hugetlb_cgroup.c | 2 +- mm/memcontrol.c | 14 +++++++------- net/core/netclassid_cgroup.c | 2 +- net/core/netprio_cgroup.c | 2 +- security/device_cgroup.c | 8 ++++---- 12 files changed, 34 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 371fe8e92ab5..d692b29c083a 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -204,7 +204,7 @@ static inline struct blkcg *bio_blkcg(struct bio *bio) */ static inline struct blkcg *blkcg_parent(struct blkcg *blkcg) { - return css_to_blkcg(css_parent(&blkcg->css)); + return css_to_blkcg(blkcg->css.parent); } /** diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 1737db0c63fe..2549493d518d 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -48,22 +48,28 @@ enum cgroup_subsys_id { }; #undef SUBSYS -/* Per-subsystem/per-cgroup state maintained by the system. */ +/* + * Per-subsystem/per-cgroup state maintained by the system. This is the + * fundamental structural building block that controllers deal with. + * + * Fields marked with "PI:" are public and immutable and may be accessed + * directly without synchronization. + */ struct cgroup_subsys_state { - /* the cgroup that this css is attached to */ + /* PI: the cgroup that this css is attached to */ struct cgroup *cgroup; - /* the cgroup subsystem that this css is attached to */ + /* PI: the cgroup subsystem that this css is attached to */ struct cgroup_subsys *ss; /* reference count - access via css_[try]get() and css_put() */ struct percpu_ref refcnt; - /* the parent css */ + /* PI: the parent css */ struct cgroup_subsys_state *parent; /* - * Subsys-unique ID. 0 is unused and root is always 1. The + * PI: Subsys-unique ID. 0 is unused and root is always 1. The * matching css can be looked up using css_from_id(). */ int id; @@ -669,19 +675,6 @@ struct cgroup_subsys { #include #undef SUBSYS -/** - * css_parent - find the parent css - * @css: the target cgroup_subsys_state - * - * Return the parent css of @css. This function is guaranteed to return - * non-NULL parent as long as @css isn't the root. - */ -static inline -struct cgroup_subsys_state *css_parent(struct cgroup_subsys_state *css) -{ - return css->parent; -} - /** * task_css_set_check - obtain a task's css_set with extra access conditions * @task: the task to obtain css_set for diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 0343d7ee6d62..929bbbc539e9 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3176,10 +3176,10 @@ css_next_descendant_pre(struct cgroup_subsys_state *pos, /* no child, visit my or the closest ancestor's next sibling */ while (pos != root) { - next = css_next_child(pos, css_parent(pos)); + next = css_next_child(pos, pos->parent); if (next) return next; - pos = css_parent(pos); + pos = pos->parent; } return NULL; @@ -3261,12 +3261,12 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, return NULL; /* if there's an unvisited sibling, visit its leftmost descendant */ - next = css_next_child(pos, css_parent(pos)); + next = css_next_child(pos, pos->parent); if (next) return css_leftmost_descendant(next); /* no sibling left, visit parent */ - return css_parent(pos); + return pos->parent; } static bool cgroup_has_live_children(struct cgroup *cgrp) diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 6b4e60e33a9a..a79e40f9d700 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -59,7 +59,7 @@ static inline struct freezer *task_freezer(struct task_struct *task) static struct freezer *parent_freezer(struct freezer *freezer) { - return css_freezer(css_parent(&freezer->css)); + return css_freezer(freezer->css.parent); } bool cgroup_freezing(struct task_struct *task) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 2f4b08b8db24..5b2a31082f4f 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -124,7 +124,7 @@ static inline struct cpuset *task_cs(struct task_struct *task) static inline struct cpuset *parent_cs(struct cpuset *cs) { - return css_cs(css_parent(&cs->css)); + return css_cs(cs->css.parent); } #ifdef CONFIG_NUMA diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 268a45ea238c..ac61ad1a5f9f 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7586,7 +7586,7 @@ cpu_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) static int cpu_cgroup_css_online(struct cgroup_subsys_state *css) { struct task_group *tg = css_tg(css); - struct task_group *parent = css_tg(css_parent(css)); + struct task_group *parent = css_tg(css->parent); if (parent) sched_online_group(tg, parent); diff --git a/kernel/sched/cpuacct.c b/kernel/sched/cpuacct.c index c143ee380e3a..9cf350c94ec4 100644 --- a/kernel/sched/cpuacct.c +++ b/kernel/sched/cpuacct.c @@ -46,7 +46,7 @@ static inline struct cpuacct *task_ca(struct task_struct *tsk) static inline struct cpuacct *parent_ca(struct cpuacct *ca) { - return css_ca(css_parent(&ca->css)); + return css_ca(ca->css.parent); } static DEFINE_PER_CPU(u64, root_cpuacct_cpuusage); diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index a380681ab3cf..493f758445e7 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -52,7 +52,7 @@ static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg) static inline struct hugetlb_cgroup * parent_hugetlb_cgroup(struct hugetlb_cgroup *h_cg) { - return hugetlb_cgroup_from_css(css_parent(&h_cg->css)); + return hugetlb_cgroup_from_css(h_cg->css.parent); } static inline bool hugetlb_cgroup_have_usage(struct hugetlb_cgroup *h_cg) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index b638a79209ee..a5e0417b4f9a 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1540,7 +1540,7 @@ static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg) int mem_cgroup_swappiness(struct mem_cgroup *memcg) { /* root ? */ - if (!css_parent(&memcg->css)) + if (!memcg->css.parent) return vm_swappiness; return memcg->swappiness; @@ -4909,7 +4909,7 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css, { int retval = 0; struct mem_cgroup *memcg = mem_cgroup_from_css(css); - struct mem_cgroup *parent_memcg = mem_cgroup_from_css(css_parent(&memcg->css)); + struct mem_cgroup *parent_memcg = mem_cgroup_from_css(memcg->css.parent); mutex_lock(&memcg_create_mutex); @@ -5207,8 +5207,8 @@ static void memcg_get_hierarchical_limit(struct mem_cgroup *memcg, if (!memcg->use_hierarchy) goto out; - while (css_parent(&memcg->css)) { - memcg = mem_cgroup_from_css(css_parent(&memcg->css)); + while (memcg->css.parent) { + memcg = mem_cgroup_from_css(memcg->css.parent); if (!memcg->use_hierarchy) break; tmp = res_counter_read_u64(&memcg->res, RES_LIMIT); @@ -5443,7 +5443,7 @@ static int mem_cgroup_swappiness_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val) { struct mem_cgroup *memcg = mem_cgroup_from_css(css); - struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(&memcg->css)); + struct mem_cgroup *parent = mem_cgroup_from_css(memcg->css.parent); if (val > 100 || !parent) return -EINVAL; @@ -5790,7 +5790,7 @@ static int mem_cgroup_oom_control_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val) { struct mem_cgroup *memcg = mem_cgroup_from_css(css); - struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(&memcg->css)); + struct mem_cgroup *parent = mem_cgroup_from_css(memcg->css.parent); /* cannot set to root cgroup and only 0 and 1 are allowed */ if (!parent || !((val == 0) || (val == 1))) @@ -6407,7 +6407,7 @@ static int mem_cgroup_css_online(struct cgroup_subsys_state *css) { struct mem_cgroup *memcg = mem_cgroup_from_css(css); - struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(css)); + struct mem_cgroup *parent = mem_cgroup_from_css(css->parent); if (css->id > MEM_CGROUP_ID_MAX) return -ENOSPC; diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c index 22931e1b99b4..30d903b19c62 100644 --- a/net/core/netclassid_cgroup.c +++ b/net/core/netclassid_cgroup.c @@ -42,7 +42,7 @@ cgrp_css_alloc(struct cgroup_subsys_state *parent_css) static int cgrp_css_online(struct cgroup_subsys_state *css) { struct cgroup_cls_state *cs = css_cls_state(css); - struct cgroup_cls_state *parent = css_cls_state(css_parent(css)); + struct cgroup_cls_state *parent = css_cls_state(css->parent); if (parent) cs->classid = parent->classid; diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index b990cefd906b..2f385b9bccc0 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -140,7 +140,7 @@ cgrp_css_alloc(struct cgroup_subsys_state *parent_css) static int cgrp_css_online(struct cgroup_subsys_state *css) { - struct cgroup_subsys_state *parent_css = css_parent(css); + struct cgroup_subsys_state *parent_css = css->parent; struct net_device *dev; int ret = 0; diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 7dbac4061b1c..ce14a31b1337 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -182,7 +182,7 @@ static inline bool is_devcg_online(const struct dev_cgroup *devcg) static int devcgroup_online(struct cgroup_subsys_state *css) { struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); - struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(css)); + struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css->parent); int ret = 0; mutex_lock(&devcgroup_mutex); @@ -455,7 +455,7 @@ static bool verify_new_ex(struct dev_cgroup *dev_cgroup, static int parent_has_perm(struct dev_cgroup *childcg, struct dev_exception_item *ex) { - struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css)); + struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent); if (!parent) return 1; @@ -476,7 +476,7 @@ static int parent_has_perm(struct dev_cgroup *childcg, static bool parent_allows_removal(struct dev_cgroup *childcg, struct dev_exception_item *ex) { - struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css)); + struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent); if (!parent) return true; @@ -614,7 +614,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, char temp[12]; /* 11 + 1 characters needed for a u32 */ int count, rc = 0; struct dev_exception_item ex; - struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css)); + struct dev_cgroup *parent = css_to_devcgroup(devcgroup->css.parent); if (!capable(CAP_SYS_ADMIN)) return -EPERM; -- cgit v1.2.3 From d51f39b05ce0008118c45945e681b20484990571 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:48 -0400 Subject: cgroup: remove cgroup->parent cgroup->parent is redundant as cgroup->self.parent can also be used to determine the parent cgroup and we're moving towards using cgroup_subsys_states as the fundamental structural blocks. This patch introduces cgroup_parent() which follows cgroup->self.parent and removes cgroup->parent. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 1 - kernel/cgroup.c | 52 +++++++++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 2549493d518d..fd538f4c2bb6 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -178,7 +178,6 @@ struct cgroup { struct list_head sibling; /* my parent's children */ struct list_head children; /* my children */ - struct cgroup *parent; /* my parent */ struct kernfs_node *kn; /* cgroup kernfs entry */ struct kernfs_node *populated_kn; /* kn for "cgroup.subtree_populated" */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 929bbbc539e9..8c67a739aea4 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -218,6 +218,15 @@ static void cgroup_idr_remove(struct idr *idr, int id) spin_unlock_bh(&cgroup_idr_lock); } +static struct cgroup *cgroup_parent(struct cgroup *cgrp) +{ + struct cgroup_subsys_state *parent_css = cgrp->self.parent; + + if (parent_css) + return container_of(parent_css, struct cgroup, self); + return NULL; +} + /** * cgroup_css - obtain a cgroup's css for the specified subsystem * @cgrp: the cgroup of interest @@ -260,9 +269,9 @@ static struct cgroup_subsys_state *cgroup_e_css(struct cgroup *cgrp, if (!(cgrp->root->subsys_mask & (1 << ss->id))) return NULL; - while (cgrp->parent && - !(cgrp->parent->child_subsys_mask & (1 << ss->id))) - cgrp = cgrp->parent; + while (cgroup_parent(cgrp) && + !(cgroup_parent(cgrp)->child_subsys_mask & (1 << ss->id))) + cgrp = cgroup_parent(cgrp); return cgroup_css(cgrp, ss); } @@ -307,7 +316,7 @@ bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor) while (cgrp) { if (cgrp == ancestor) return true; - cgrp = cgrp->parent; + cgrp = cgroup_parent(cgrp); } return false; } @@ -454,7 +463,7 @@ static void cgroup_update_populated(struct cgroup *cgrp, bool populated) if (cgrp->populated_kn) kernfs_notify(cgrp->populated_kn); - cgrp = cgrp->parent; + cgrp = cgroup_parent(cgrp); } while (cgrp); } @@ -2018,7 +2027,7 @@ static int cgroup_migrate_prepare_dst(struct cgroup *dst_cgrp, * Except for the root, child_subsys_mask must be zero for a cgroup * with tasks so that child cgroups don't compete against tasks. */ - if (dst_cgrp && cgroup_on_dfl(dst_cgrp) && dst_cgrp->parent && + if (dst_cgrp && cgroup_on_dfl(dst_cgrp) && cgroup_parent(dst_cgrp) && dst_cgrp->child_subsys_mask) return -EBUSY; @@ -2427,7 +2436,7 @@ static int cgroup_controllers_show(struct seq_file *seq, void *v) { struct cgroup *cgrp = seq_css(seq)->cgroup; - cgroup_print_ss_mask(seq, cgrp->parent->child_subsys_mask); + cgroup_print_ss_mask(seq, cgroup_parent(cgrp)->child_subsys_mask); return 0; } @@ -2610,8 +2619,8 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, /* unavailable or not enabled on the parent? */ if (!(cgrp_dfl_root.subsys_mask & (1 << ssid)) || - (cgrp->parent && - !(cgrp->parent->child_subsys_mask & (1 << ssid)))) { + (cgroup_parent(cgrp) && + !(cgroup_parent(cgrp)->child_subsys_mask & (1 << ssid)))) { ret = -ENOENT; goto out_unlock; } @@ -2640,7 +2649,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, * Except for the root, child_subsys_mask must be zero for a cgroup * with tasks so that child cgroups don't compete against tasks. */ - if (enable && cgrp->parent && !list_empty(&cgrp->cset_links)) { + if (enable && cgroup_parent(cgrp) && !list_empty(&cgrp->cset_links)) { ret = -EBUSY; goto out_unlock; } @@ -2898,9 +2907,9 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], continue; if ((cft->flags & CFTYPE_INSANE) && cgroup_sane_behavior(cgrp)) continue; - if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent) + if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgroup_parent(cgrp)) continue; - if ((cft->flags & CFTYPE_ONLY_ON_ROOT) && cgrp->parent) + if ((cft->flags & CFTYPE_ONLY_ON_ROOT) && cgroup_parent(cgrp)) continue; if (is_add) { @@ -4092,14 +4101,14 @@ static void css_free_work_fn(struct work_struct *work) atomic_dec(&cgrp->root->nr_cgrps); cgroup_pidlist_destroy_all(cgrp); - if (cgrp->parent) { + if (cgroup_parent(cgrp)) { /* * We get a ref to the parent, and put the ref when * this cgroup is being freed, so it's guaranteed * that the parent won't be destroyed before its * children. */ - cgroup_put(cgrp->parent); + cgroup_put(cgroup_parent(cgrp)); kernfs_put(cgrp->kn); kfree(cgrp); } else { @@ -4163,8 +4172,8 @@ static void init_and_link_css(struct cgroup_subsys_state *css, css->ss = ss; css->flags = 0; - if (cgrp->parent) { - css->parent = cgroup_css(cgrp->parent, ss); + if (cgroup_parent(cgrp)) { + css->parent = cgroup_css(cgroup_parent(cgrp), ss); css_get(css->parent); } @@ -4218,7 +4227,7 @@ static void offline_css(struct cgroup_subsys_state *css) */ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss) { - struct cgroup *parent = cgrp->parent; + struct cgroup *parent = cgroup_parent(cgrp); struct cgroup_subsys_state *css; int err; @@ -4251,7 +4260,7 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss) goto err_clear_dir; if (ss->broken_hierarchy && !ss->warned_broken_hierarchy && - parent->parent) { + cgroup_parent(parent)) { pr_warn("%s (%d) created nested cgroup for controller \"%s\" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.\n", current->comm, current->pid, ss->name); if (!strcmp(ss->name, "memory")) @@ -4309,7 +4318,6 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, init_cgroup_housekeeping(cgrp); - cgrp->parent = parent; cgrp->self.parent = &parent->self; cgrp->root = root; @@ -4336,7 +4344,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, cgrp->serial_nr = cgroup_serial_nr_next++; /* allocation complete, commit to creation */ - list_add_tail_rcu(&cgrp->sibling, &cgrp->parent->children); + list_add_tail_rcu(&cgrp->sibling, &cgroup_parent(cgrp)->children); atomic_inc(&root->nr_cgrps); cgroup_get(parent); @@ -4531,8 +4539,8 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) */ kernfs_remove(cgrp->kn); - set_bit(CGRP_RELEASABLE, &cgrp->parent->flags); - check_for_release(cgrp->parent); + set_bit(CGRP_RELEASABLE, &cgroup_parent(cgrp)->flags); + check_for_release(cgroup_parent(cgrp)); /* put the base reference */ percpu_ref_kill(&cgrp->self.refcnt); -- cgit v1.2.3 From d5c419b68e368fdd9f1857bf8d4bb4480edb9b80 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:48 -0400 Subject: cgroup: move cgroup->sibling and ->children into cgroup_subsys_state We're moving towards using cgroup_subsys_states as the fundamental structural blocks. Let's move cgroup->sibling and ->children into cgroup_subsys_state. This is pure move without functional change and only cgroup->self's fields are actually used. Other csses will make use of the fields later. While at it, update init_and_link_css() so that it zeroes the whole css before initializing it and remove explicit zeroing of ->flags. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 11 ++++------- kernel/cgroup.c | 38 ++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index fd538f4c2bb6..cf8ba26b7c6e 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -68,6 +68,10 @@ struct cgroup_subsys_state { /* PI: the parent css */ struct cgroup_subsys_state *parent; + /* siblings list anchored at the parent's ->children */ + struct list_head sibling; + struct list_head children; + /* * PI: Subsys-unique ID. 0 is unused and root is always 1. The * matching css can be looked up using css_from_id(). @@ -171,13 +175,6 @@ struct cgroup { */ int populated_cnt; - /* - * We link our 'sibling' struct into our parent's 'children'. - * Our children link their 'sibling' into our 'children'. - */ - struct list_head sibling; /* my parent's children */ - struct list_head children; /* my children */ - struct kernfs_node *kn; /* cgroup kernfs entry */ struct kernfs_node *populated_kn; /* kn for "cgroup.subtree_populated" */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 8c67a739aea4..5385839e727b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -378,7 +378,7 @@ static int notify_on_release(const struct cgroup *cgrp) /* iterate over child cgrps, lock should be held throughout iteration */ #define cgroup_for_each_live_child(child, cgrp) \ - list_for_each_entry((child), &(cgrp)->children, sibling) \ + list_for_each_entry((child), &(cgrp)->self.children, self.sibling) \ if (({ lockdep_assert_held(&cgroup_mutex); \ cgroup_is_dead(child); })) \ ; \ @@ -870,7 +870,7 @@ static void cgroup_destroy_root(struct cgroup_root *root) mutex_lock(&cgroup_mutex); BUG_ON(atomic_read(&root->nr_cgrps)); - BUG_ON(!list_empty(&cgrp->children)); + BUG_ON(!list_empty(&cgrp->self.children)); /* Rebind all subsystems back to the default hierarchy */ rebind_subsystems(&cgrp_dfl_root, root->subsys_mask); @@ -1432,7 +1432,7 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data) } /* remounting is not allowed for populated hierarchies */ - if (!list_empty(&root->cgrp.children)) { + if (!list_empty(&root->cgrp.self.children)) { ret = -EBUSY; goto out_unlock; } @@ -1512,8 +1512,8 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) struct cgroup_subsys *ss; int ssid; - INIT_LIST_HEAD(&cgrp->sibling); - INIT_LIST_HEAD(&cgrp->children); + INIT_LIST_HEAD(&cgrp->self.sibling); + INIT_LIST_HEAD(&cgrp->self.children); INIT_LIST_HEAD(&cgrp->cset_links); INIT_LIST_HEAD(&cgrp->release_list); INIT_LIST_HEAD(&cgrp->pidlists); @@ -1612,7 +1612,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned int ss_mask) link_css_set(&tmp_links, cset, root_cgrp); up_write(&css_set_rwsem); - BUG_ON(!list_empty(&root_cgrp->children)); + BUG_ON(!list_empty(&root_cgrp->self.children)); BUG_ON(atomic_read(&root->nr_cgrps) != 1); kernfs_activate(root_cgrp->kn); @@ -3128,11 +3128,11 @@ css_next_child(struct cgroup_subsys_state *pos_css, * cgroup is removed or iteration and removal race. */ if (!pos) { - next = list_entry_rcu(cgrp->children.next, struct cgroup, sibling); + next = list_entry_rcu(cgrp->self.children.next, struct cgroup, self.sibling); } else if (likely(!cgroup_is_dead(pos))) { - next = list_entry_rcu(pos->sibling.next, struct cgroup, sibling); + next = list_entry_rcu(pos->self.sibling.next, struct cgroup, self.sibling); } else { - list_for_each_entry_rcu(next, &cgrp->children, sibling) + list_for_each_entry_rcu(next, &cgrp->self.children, self.sibling) if (next->serial_nr > pos->serial_nr) break; } @@ -3142,12 +3142,12 @@ css_next_child(struct cgroup_subsys_state *pos_css, * the next sibling; however, it might have @ss disabled. If so, * fast-forward to the next enabled one. */ - while (&next->sibling != &cgrp->children) { + while (&next->self.sibling != &cgrp->self.children) { struct cgroup_subsys_state *next_css = cgroup_css(next, parent_css->ss); if (next_css) return next_css; - next = list_entry_rcu(next->sibling.next, struct cgroup, sibling); + next = list_entry_rcu(next->self.sibling.next, struct cgroup, self.sibling); } return NULL; } @@ -3283,7 +3283,7 @@ static bool cgroup_has_live_children(struct cgroup *cgrp) struct cgroup *child; rcu_read_lock(); - list_for_each_entry_rcu(child, &cgrp->children, sibling) { + list_for_each_entry_rcu(child, &cgrp->self.children, self.sibling) { if (!cgroup_is_dead(child)) { rcu_read_unlock(); return true; @@ -4144,7 +4144,7 @@ static void css_release_work_fn(struct work_struct *work) } else { /* cgroup release path */ mutex_lock(&cgroup_mutex); - list_del_rcu(&cgrp->sibling); + list_del_rcu(&cgrp->self.sibling); mutex_unlock(&cgroup_mutex); cgroup_idr_remove(&cgrp->root->cgroup_idr, cgrp->id); @@ -4168,9 +4168,11 @@ static void init_and_link_css(struct cgroup_subsys_state *css, { cgroup_get(cgrp); + memset(css, 0, sizeof(*css)); css->cgroup = cgrp; css->ss = ss; - css->flags = 0; + INIT_LIST_HEAD(&css->sibling); + INIT_LIST_HEAD(&css->children); if (cgroup_parent(cgrp)) { css->parent = cgroup_css(cgroup_parent(cgrp), ss); @@ -4344,7 +4346,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, cgrp->serial_nr = cgroup_serial_nr_next++; /* allocation complete, commit to creation */ - list_add_tail_rcu(&cgrp->sibling, &cgroup_parent(cgrp)->children); + list_add_tail_rcu(&cgrp->self.sibling, &cgroup_parent(cgrp)->self.children); atomic_inc(&root->nr_cgrps); cgroup_get(parent); @@ -4507,9 +4509,9 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) return -EBUSY; /* - * Make sure there's no live children. We can't test ->children - * emptiness as dead children linger on it while being destroyed; - * otherwise, "rmdir parent/child parent" may fail with -EBUSY. + * Make sure there's no live children. We can't test emptiness of + * ->self.children as dead children linger on it while being + * drained; otherwise, "rmdir parent/child parent" may fail. */ if (cgroup_has_live_children(cgrp)) return -EBUSY; -- cgit v1.2.3 From 0cb51d71c1fa9234afe4213089844be76ec1765a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:49 -0400 Subject: cgroup: move cgroup->serial_nr into cgroup_subsys_state We're moving towards using cgroup_subsys_states as the fundamental structural blocks. All csses including the cgroup->self and actual ones now form trees through css->children and ->sibling which follow the same rules as what cgroup->children and ->sibling followed. This patch moves cgroup->serial_nr which is used to implement css iteration into css. Note that all csses, regardless of their types, allocate their serial numbers from the same monotonically increasing counter. This doesn't affect the ordering needed by css iteration or cause any other material behavior changes. This will be used to update css iteration. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 16 ++++++++-------- kernel/cgroup.c | 20 +++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index cf8ba26b7c6e..ebe7ce49f4b7 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -80,6 +80,14 @@ struct cgroup_subsys_state { unsigned int flags; + /* + * Monotonically increasing unique serial number which defines a + * uniform order among all csses. It's guaranteed that all + * ->children lists are in the ascending order of ->serial_nr and + * used to allow interrupting and resuming iterations. + */ + u64 serial_nr; + /* percpu_ref killing and RCU release */ struct rcu_head rcu_head; struct work_struct destroy_work; @@ -178,14 +186,6 @@ struct cgroup { struct kernfs_node *kn; /* cgroup kernfs entry */ struct kernfs_node *populated_kn; /* kn for "cgroup.subtree_populated" */ - /* - * Monotonically increasing unique serial number which defines a - * uniform order among all cgroups. It's guaranteed that all - * ->children lists are in the ascending order of ->serial_nr. - * It's used to allow interrupting and resuming iterations. - */ - u64 serial_nr; - /* the bitmask of subsystems enabled on the child cgroups */ unsigned int child_subsys_mask; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index dcb06e181ce4..d5af128ec1ec 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -157,14 +157,13 @@ static int cgroup_root_count; static DEFINE_IDR(cgroup_hierarchy_idr); /* - * Assign a monotonically increasing serial number to cgroups. It - * guarantees cgroups with bigger numbers are newer than those with smaller - * numbers. Also, as cgroups are always appended to the parent's - * ->children list, it guarantees that sibling cgroups are always sorted in - * the ascending serial number order on the list. Protected by - * cgroup_mutex. + * Assign a monotonically increasing serial number to csses. It guarantees + * cgroups with bigger numbers are newer than those with smaller numbers. + * Also, as csses are always appended to the parent's ->children list, it + * guarantees that sibling csses are always sorted in the ascending serial + * number order on the list. Protected by cgroup_mutex. */ -static u64 cgroup_serial_nr_next = 1; +static u64 css_serial_nr_next = 1; /* This flag indicates whether tasks in the fork and exit paths should * check for fork/exit handlers to call. This avoids us having to do @@ -3133,7 +3132,7 @@ css_next_child(struct cgroup_subsys_state *pos_css, next = list_entry_rcu(pos->self.sibling.next, struct cgroup, self.sibling); } else { list_for_each_entry_rcu(next, &cgrp->self.children, self.sibling) - if (next->serial_nr > pos->serial_nr) + if (next->self.serial_nr > pos->self.serial_nr) break; } @@ -4168,6 +4167,8 @@ static void css_release(struct percpu_ref *ref) static void init_and_link_css(struct cgroup_subsys_state *css, struct cgroup_subsys *ss, struct cgroup *cgrp) { + lockdep_assert_held(&cgroup_mutex); + cgroup_get(cgrp); memset(css, 0, sizeof(*css)); @@ -4175,6 +4176,7 @@ static void init_and_link_css(struct cgroup_subsys_state *css, css->ss = ss; INIT_LIST_HEAD(&css->sibling); INIT_LIST_HEAD(&css->children); + css->serial_nr = css_serial_nr_next++; if (cgroup_parent(cgrp)) { css->parent = cgroup_css(cgroup_parent(cgrp), ss); @@ -4348,7 +4350,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, */ kernfs_get(kn); - cgrp->serial_nr = cgroup_serial_nr_next++; + cgrp->self.serial_nr = css_serial_nr_next++; /* allocation complete, commit to creation */ list_add_tail_rcu(&cgrp->self.sibling, &cgroup_parent(cgrp)->self.children); -- cgit v1.2.3 From de3f034182ecbf0efbcef7ab8b253c6c3049a592 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:49 -0400 Subject: cgroup: introduce CSS_RELEASED and reduce css iteration fallback window css iterations allow the caller to drop RCU read lock. As long as the caller keeps the current position accessible, it can simply re-grab RCU read lock later and continue iteration. This is achieved by using CGRP_DEAD to detect whether the current positions next pointer is safe to dereference and if not re-iterate from the beginning to the next position using ->serial_nr. CGRP_DEAD is used as the marker to invalidate the next pointer and the only requirement is that the marker is set before the next sibling starts its RCU grace period. Because CGRP_DEAD is set at the end of cgroup_destroy_locked() but the cgroup is unlinked when the reference count reaches zero, we currently have a rather large window where this fallback re-iteration logic can be triggered. This patch introduces CSS_RELEASED which is set when a css is unlinked from its sibling list. This still keeps the re-iteration logic working while drastically reducing the window of its activation. While at it, rewrite the comment in css_next_child() to reflect the new flag and better explain the synchronization. This will also enable iterating csses directly instead of through cgroups. v2: CSS_RELEASED now assigned to 1 << 2 as 1 << 0 is used by CSS_NO_REF. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 1 + kernel/cgroup.c | 41 ++++++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index ebe7ce49f4b7..5375582ea5f6 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -97,6 +97,7 @@ struct cgroup_subsys_state { enum { CSS_NO_REF = (1 << 0), /* no reference counting for this css */ CSS_ONLINE = (1 << 1), /* between ->css_online() and ->css_offline() */ + CSS_RELEASED = (1 << 2), /* refcnt reached zero, released */ }; /** diff --git a/kernel/cgroup.c b/kernel/cgroup.c index d5af128ec1ec..5544e685f2da 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3108,27 +3108,28 @@ css_next_child(struct cgroup_subsys_state *pos_css, cgroup_assert_mutex_or_rcu_locked(); /* - * @pos could already have been removed. Once a cgroup is removed, - * its ->sibling.next is no longer updated when its next sibling - * changes. As CGRP_DEAD assertion is serialized and happens - * before the cgroup is taken off the ->sibling list, if we see it - * unasserted, it's guaranteed that the next sibling hasn't - * finished its grace period even if it's already removed, and thus - * safe to dereference from this RCU critical section. If - * ->sibling.next is inaccessible, cgroup_is_dead() is guaranteed - * to be visible as %true here. + * @pos could already have been unlinked from the sibling list. + * Once a cgroup is removed, its ->sibling.next is no longer + * updated when its next sibling changes. CSS_RELEASED is set when + * @pos is taken off list, at which time its next pointer is valid, + * and, as releases are serialized, the one pointed to by the next + * pointer is guaranteed to not have started release yet. This + * implies that if we observe !CSS_RELEASED on @pos in this RCU + * critical section, the one pointed to by its next pointer is + * guaranteed to not have finished its RCU grace period even if we + * have dropped rcu_read_lock() inbetween iterations. * - * If @pos is dead, its next pointer can't be dereferenced; - * however, as each cgroup is given a monotonically increasing - * unique serial number and always appended to the sibling list, - * the next one can be found by walking the parent's children until - * we see a cgroup with higher serial number than @pos's. While - * this path can be slower, it's taken only when either the current - * cgroup is removed or iteration and removal race. + * If @pos has CSS_RELEASED set, its next pointer can't be + * dereferenced; however, as each css is given a monotonically + * increasing unique serial number and always appended to the + * sibling list, the next one can be found by walking the parent's + * children until the first css with higher serial number than + * @pos's. While this path can be slower, it happens iff iteration + * races against release and the race window is very small. */ if (!pos) { next = list_entry_rcu(cgrp->self.children.next, struct cgroup, self.sibling); - } else if (likely(!cgroup_is_dead(pos))) { + } else if (likely(!(pos->self.flags & CSS_RELEASED))) { next = list_entry_rcu(pos->self.sibling.next, struct cgroup, self.sibling); } else { list_for_each_entry_rcu(next, &cgrp->self.children, self.sibling) @@ -4139,6 +4140,7 @@ static void css_release_work_fn(struct work_struct *work) mutex_lock(&cgroup_mutex); + css->flags |= CSS_RELEASED; list_del_rcu(&css->sibling); if (ss) { @@ -4525,10 +4527,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) /* * Mark @cgrp dead. This prevents further task migration and child - * creation by disabling cgroup_lock_live_group(). Note that - * CGRP_DEAD assertion is depended upon by css_next_child() to - * resume iteration after dropping RCU read lock. See - * css_next_child() for details. + * creation by disabling cgroup_lock_live_group(). */ set_bit(CGRP_DEAD, &cgrp->flags); -- cgit v1.2.3 From c2931b70a32c705b9bd5762f5044f9eac8a52bb3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:51 -0400 Subject: cgroup: iterate cgroup_subsys_states directly Currently, css_next_child() is implemented as finding the next child cgroup which has the css enabled, which used to be the only way to do it as only cgroups participated in sibling lists and thus could be iteratd. This works as long as what's required during iteration is not missing online csses; however, it turns out that there are use cases where offlined but not yet released csses need to be iterated. This is difficult to implement through cgroup iteration the unified hierarchy as there may be multiple dying csses for the same subsystem associated with single cgroup. After the recent changes, the cgroup self and regular csses behave identically in how they're linked and unlinked from the sibling lists including assertion of CSS_RELEASED and css_next_child() can simply switch to iterating csses directly. This both simplifies the logic and ensures that all visible non-released csses are included in the iteration whether there are multiple dying csses for a subsystem or not. As all other iterators depend on css_next_child() for sibling iteration, this changes behaviors of all css iterators. Add and update explanations on the css states which are included in traversal to all iterators. As css iteration could always contain offlined csses, this shouldn't break any of the current users and new usages which need iteration of all on and offline csses can make use of the new semantics. Signed-off-by: Tejun Heo Acked-by: Li Zefan Cc: Johannes Weiner --- include/linux/cgroup.h | 44 ++++++++++++++++++++--------------- kernel/cgroup.c | 62 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 63 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5375582ea5f6..f2ff578fc03a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -764,14 +764,14 @@ struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss); * @pos: the css * to use as the loop cursor * @parent: css whose children to walk * - * Walk @parent's children. Must be called under rcu_read_lock(). A child - * css which hasn't finished ->css_online() or already has finished - * ->css_offline() may show up during traversal and it's each subsystem's - * responsibility to verify that each @pos is alive. + * Walk @parent's children. Must be called under rcu_read_lock(). * - * If a subsystem synchronizes against the parent in its ->css_online() and - * before starting iterating, a css which finished ->css_online() is - * guaranteed to be visible in the future iterations. + * If a subsystem synchronizes ->css_online() and the start of iteration, a + * css which finished ->css_online() is guaranteed to be visible in the + * future iterations and will stay visible until the last reference is put. + * A css which hasn't finished ->css_online() or already finished + * ->css_offline() may show up during traversal. It's each subsystem's + * responsibility to synchronize against on/offlining. * * It is allowed to temporarily drop RCU read lock during iteration. The * caller is responsible for ensuring that @pos remains accessible until @@ -794,17 +794,16 @@ css_rightmost_descendant(struct cgroup_subsys_state *pos); * @root: css whose descendants to walk * * Walk @root's descendants. @root is included in the iteration and the - * first node to be visited. Must be called under rcu_read_lock(). A - * descendant css which hasn't finished ->css_online() or already has - * finished ->css_offline() may show up during traversal and it's each - * subsystem's responsibility to verify that each @pos is alive. + * first node to be visited. Must be called under rcu_read_lock(). * - * If a subsystem synchronizes against the parent in its ->css_online() and - * before starting iterating, and synchronizes against @pos on each - * iteration, any descendant css which finished ->css_online() is - * guaranteed to be visible in the future iterations. + * If a subsystem synchronizes ->css_online() and the start of iteration, a + * css which finished ->css_online() is guaranteed to be visible in the + * future iterations and will stay visible until the last reference is put. + * A css which hasn't finished ->css_online() or already finished + * ->css_offline() may show up during traversal. It's each subsystem's + * responsibility to synchronize against on/offlining. * - * In other words, the following guarantees that a descendant can't escape + * For example, the following guarantees that a descendant can't escape * state updates of its ancestors. * * my_online(@css) @@ -860,8 +859,17 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, * * Similar to css_for_each_descendant_pre() but performs post-order * traversal instead. @root is included in the iteration and the last - * node to be visited. Note that the walk visibility guarantee described - * in pre-order walk doesn't apply the same to post-order walks. + * node to be visited. + * + * If a subsystem synchronizes ->css_online() and the start of iteration, a + * css which finished ->css_online() is guaranteed to be visible in the + * future iterations and will stay visible until the last reference is put. + * A css which hasn't finished ->css_online() or already finished + * ->css_offline() may show up during traversal. It's each subsystem's + * responsibility to synchronize against on/offlining. + * + * Note that the walk visibility guarantee example described in pre-order + * walk doesn't apply the same to post-order walks. */ #define css_for_each_descendant_post(pos, css) \ for ((pos) = css_next_descendant_post(NULL, (css)); (pos); \ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 5544e685f2da..097a1fc1e1e8 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3089,21 +3089,25 @@ static int cgroup_task_count(const struct cgroup *cgrp) /** * css_next_child - find the next child of a given css - * @pos_css: the current position (%NULL to initiate traversal) - * @parent_css: css whose children to walk + * @pos: the current position (%NULL to initiate traversal) + * @parent: css whose children to walk * - * This function returns the next child of @parent_css and should be called + * This function returns the next child of @parent and should be called * under either cgroup_mutex or RCU read lock. The only requirement is - * that @parent_css and @pos_css are accessible. The next sibling is - * guaranteed to be returned regardless of their states. + * that @parent and @pos are accessible. The next sibling is guaranteed to + * be returned regardless of their states. + * + * If a subsystem synchronizes ->css_online() and the start of iteration, a + * css which finished ->css_online() is guaranteed to be visible in the + * future iterations and will stay visible until the last reference is put. + * A css which hasn't finished ->css_online() or already finished + * ->css_offline() may show up during traversal. It's each subsystem's + * responsibility to synchronize against on/offlining. */ -struct cgroup_subsys_state * -css_next_child(struct cgroup_subsys_state *pos_css, - struct cgroup_subsys_state *parent_css) +struct cgroup_subsys_state *css_next_child(struct cgroup_subsys_state *pos, + struct cgroup_subsys_state *parent) { - struct cgroup *pos = pos_css ? pos_css->cgroup : NULL; - struct cgroup *cgrp = parent_css->cgroup; - struct cgroup *next; + struct cgroup_subsys_state *next; cgroup_assert_mutex_or_rcu_locked(); @@ -3128,27 +3132,21 @@ css_next_child(struct cgroup_subsys_state *pos_css, * races against release and the race window is very small. */ if (!pos) { - next = list_entry_rcu(cgrp->self.children.next, struct cgroup, self.sibling); - } else if (likely(!(pos->self.flags & CSS_RELEASED))) { - next = list_entry_rcu(pos->self.sibling.next, struct cgroup, self.sibling); + next = list_entry_rcu(parent->children.next, struct cgroup_subsys_state, sibling); + } else if (likely(!(pos->flags & CSS_RELEASED))) { + next = list_entry_rcu(pos->sibling.next, struct cgroup_subsys_state, sibling); } else { - list_for_each_entry_rcu(next, &cgrp->self.children, self.sibling) - if (next->self.serial_nr > pos->self.serial_nr) + list_for_each_entry_rcu(next, &parent->children, sibling) + if (next->serial_nr > pos->serial_nr) break; } /* * @next, if not pointing to the head, can be dereferenced and is - * the next sibling; however, it might have @ss disabled. If so, - * fast-forward to the next enabled one. + * the next sibling. */ - while (&next->self.sibling != &cgrp->self.children) { - struct cgroup_subsys_state *next_css = cgroup_css(next, parent_css->ss); - - if (next_css) - return next_css; - next = list_entry_rcu(next->self.sibling.next, struct cgroup, self.sibling); - } + if (&next->sibling != &parent->children) + return next; return NULL; } @@ -3165,6 +3163,13 @@ css_next_child(struct cgroup_subsys_state *pos_css, * doesn't require the whole traversal to be contained in a single critical * section. This function will return the correct next descendant as long * as both @pos and @root are accessible and @pos is a descendant of @root. + * + * If a subsystem synchronizes ->css_online() and the start of iteration, a + * css which finished ->css_online() is guaranteed to be visible in the + * future iterations and will stay visible until the last reference is put. + * A css which hasn't finished ->css_online() or already finished + * ->css_offline() may show up during traversal. It's each subsystem's + * responsibility to synchronize against on/offlining. */ struct cgroup_subsys_state * css_next_descendant_pre(struct cgroup_subsys_state *pos, @@ -3252,6 +3257,13 @@ css_leftmost_descendant(struct cgroup_subsys_state *pos) * section. This function will return the correct next descendant as long * as both @pos and @cgroup are accessible and @pos is a descendant of * @cgroup. + * + * If a subsystem synchronizes ->css_online() and the start of iteration, a + * css which finished ->css_online() is guaranteed to be visible in the + * future iterations and will stay visible until the last reference is put. + * A css which hasn't finished ->css_online() or already finished + * ->css_offline() may show up during traversal. It's each subsystem's + * responsibility to synchronize against on/offlining. */ struct cgroup_subsys_state * css_next_descendant_post(struct cgroup_subsys_state *pos, -- cgit v1.2.3 From 184faf32328c65c9d86b19577b8d8b90bdd2cd2e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:51 -0400 Subject: cgroup: use CSS_ONLINE instead of CGRP_DEAD Use CSS_ONLINE on the self css to indicate whether a cgroup has been killed instead of CGRP_DEAD. This will allow re-using css online test for cgroup liveliness test. This doesn't introduce any functional change. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 2 -- kernel/cgroup.c | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f2ff578fc03a..51a339c99eb6 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -143,8 +143,6 @@ static inline void css_put(struct cgroup_subsys_state *css) /* bits in struct cgroup flags field */ enum { - /* Control Group is dead */ - CGRP_DEAD, /* * Control Group has previously had a child cgroup or a task, * but no longer (only if CGRP_NOTIFY_ON_RELEASE is set) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 097a1fc1e1e8..004004fd0ded 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -278,7 +278,7 @@ static struct cgroup_subsys_state *cgroup_e_css(struct cgroup *cgrp, /* convenient tests for these bits */ static inline bool cgroup_is_dead(const struct cgroup *cgrp) { - return test_bit(CGRP_DEAD, &cgrp->flags); + return !(cgrp->self.flags & CSS_ONLINE); } struct cgroup_subsys_state *of_css(struct kernfs_open_file *of) @@ -1518,6 +1518,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) INIT_LIST_HEAD(&cgrp->pidlists); mutex_init(&cgrp->pidlist_mutex); cgrp->self.cgroup = cgrp; + cgrp->self.flags |= CSS_ONLINE; for_each_subsys(ss, ssid) INIT_LIST_HEAD(&cgrp->e_csets[ssid]); @@ -4541,13 +4542,13 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * Mark @cgrp dead. This prevents further task migration and child * creation by disabling cgroup_lock_live_group(). */ - set_bit(CGRP_DEAD, &cgrp->flags); + cgrp->self.flags &= ~CSS_ONLINE; /* initiate massacre of all css's */ for_each_css(css, ssid, cgrp) kill_css(css); - /* CGRP_DEAD is set, remove from ->release_list for the last time */ + /* CSS_ONLINE is clear, remove from ->release_list for the last time */ raw_spin_lock(&release_list_lock); if (!list_empty(&cgrp->release_list)) list_del_init(&cgrp->release_list); -- cgit v1.2.3 From f3d4650015301d1c880df4523f7e7ef320a38aab Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:52 -0400 Subject: cgroup: convert cgroup_has_live_children() into css_has_online_children() Now that cgroup liveliness and css onliness are the same state, convert cgroup_has_live_children() into css_has_online_children() so that it can be used for actual csses too. The function now uses css_for_each_child() for iteration and is published. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 2 ++ kernel/cgroup.c | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 51a339c99eb6..b76999954beb 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -873,6 +873,8 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, for ((pos) = css_next_descendant_post(NULL, (css)); (pos); \ (pos) = css_next_descendant_post((pos), (css))) +bool css_has_online_children(struct cgroup_subsys_state *css); + /* A css_task_iter should be treated as an opaque object */ struct css_task_iter { struct cgroup_subsys *ss; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 004004fd0ded..082bb842b11a 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -175,7 +175,6 @@ static int need_forkexit_callback __read_mostly; static struct cftype cgroup_base_files[]; static void cgroup_put(struct cgroup *cgrp); -static bool cgroup_has_live_children(struct cgroup *cgrp); static int rebind_subsystems(struct cgroup_root *dst_root, unsigned int ss_mask); static int cgroup_destroy_locked(struct cgroup *cgrp); @@ -1769,7 +1768,7 @@ static void cgroup_kill_sb(struct super_block *sb) * This prevents new mounts by disabling percpu_ref_tryget_live(). * cgroup_mount() may wait for @root's release. */ - if (cgroup_has_live_children(&root->cgrp)) + if (css_has_online_children(&root->cgrp.self)) cgroup_put(&root->cgrp); else percpu_ref_kill(&root->cgrp.self.refcnt); @@ -3291,19 +3290,28 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, return pos->parent; } -static bool cgroup_has_live_children(struct cgroup *cgrp) +/** + * css_has_online_children - does a css have online children + * @css: the target css + * + * Returns %true if @css has any online children; otherwise, %false. This + * function can be called from any context but the caller is responsible + * for synchronizing against on/offlining as necessary. + */ +bool css_has_online_children(struct cgroup_subsys_state *css) { - struct cgroup *child; + struct cgroup_subsys_state *child; + bool ret = false; rcu_read_lock(); - list_for_each_entry_rcu(child, &cgrp->self.children, self.sibling) { - if (!cgroup_is_dead(child)) { - rcu_read_unlock(); - return true; + css_for_each_child(child, css) { + if (css->flags & CSS_ONLINE) { + ret = true; + break; } } rcu_read_unlock(); - return false; + return ret; } /** @@ -4535,7 +4543,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * ->self.children as dead children linger on it while being * drained; otherwise, "rmdir parent/child parent" may fail. */ - if (cgroup_has_live_children(cgrp)) + if (css_has_online_children(&cgrp->self)) return -EBUSY; /* @@ -5014,8 +5022,8 @@ void cgroup_exit(struct task_struct *tsk) static void check_for_release(struct cgroup *cgrp) { - if (cgroup_is_releasable(cgrp) && - list_empty(&cgrp->cset_links) && !cgroup_has_live_children(cgrp)) { + if (cgroup_is_releasable(cgrp) && list_empty(&cgrp->cset_links) && + !css_has_online_children(&cgrp->self)) { /* * Control Group is currently removeable. If it's not * already queued for a userspace notification, queue -- cgit v1.2.3 From 6f4524d355a86769b65d5420a6ef47fb0bba9b72 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 16 May 2014 13:22:52 -0400 Subject: cgroup: implement css_tryget() Implement css_tryget() which tries to grab a cgroup_subsys_state's reference as long as it already hasn't reached zero. Combined with the recent css iterator changes to include offline && !released csses during traversal, this can be used to access csses regardless of its online state. v2: Take the new flag CSS_NO_REF into account. Signed-off-by: Tejun Heo Acked-by: Li Zefan Cc: Johannes Weiner --- include/linux/cgroup.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index b76999954beb..4afe544d3547 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -112,6 +112,24 @@ static inline void css_get(struct cgroup_subsys_state *css) percpu_ref_get(&css->refcnt); } +/** + * css_tryget - try to obtain a reference on the specified css + * @css: target css + * + * Obtain a reference on @css unless it already has reached zero and is + * being released. This function doesn't care whether @css is on or + * offline. The caller naturally needs to ensure that @css is accessible + * but doesn't have to be holding a reference on it - IOW, RCU protected + * access is good enough for this function. Returns %true if a reference + * count was successfully obtained; %false otherwise. + */ +static inline bool css_tryget(struct cgroup_subsys_state *css) +{ + if (!(css->flags & CSS_NO_REF)) + return percpu_ref_tryget(&css->refcnt); + return true; +} + /** * css_tryget_online - try to obtain a reference on the specified css if online * @css: target css -- cgit v1.2.3 From 5cc9ed4b9a7ac579362ccebac67f7a4cdb36de06 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 16 May 2014 14:22:37 +0100 Subject: drm/i915: Introduce mapping of user pages into video memory (userptr) ioctl By exporting the ability to map user address and inserting PTEs representing their backing pages into the GTT, we can exploit UMA in order to utilize normal application data as a texture source or even as a render target (depending upon the capabilities of the chipset). This has a number of uses, with zero-copy downloads to the GPU and efficient readback making the intermixed streaming of CPU and GPU operations fairly efficient. This ability has many widespread implications from faster rendering of client-side software rasterisers (chromium), mitigation of stalls due to read back (firefox) and to faster pipelining of texture data (such as pixel buffer objects in GL or data blobs in CL). v2: Compile with CONFIG_MMU_NOTIFIER v3: We can sleep while performing invalidate-range, which we can utilise to drop our page references prior to the kernel manipulating the vma (for either discard or cloning) and so protect normal users. v4: Only run the invalidate notifier if the range intercepts the bo. v5: Prevent userspace from attempting to GTT mmap non-page aligned buffers v6: Recheck after reacquire mutex for lost mmu. v7: Fix implicit padding of ioctl struct by rounding to next 64bit boundary. v8: Fix rebasing error after forwarding porting the back port. v9: Limit the userptr to page aligned entries. We now expect userspace to handle all the offset-in-page adjustments itself. v10: Prevent vma from being copied across fork to avoid issues with cow. v11: Drop vma behaviour changes -- locking is nigh on impossible. Use a worker to load user pages to avoid lock inversions. v12: Use get_task_mm()/mmput() for correct refcounting of mm. v13: Use a worker to release the mmu_notifier to avoid lock inversion v14: Decouple mmu_notifier from struct_mutex using a custom mmu_notifer with its own locking and tree of objects for each mm/mmu_notifier. v15: Prevent overlapping userptr objects, and invalidate all objects within the mmu_notifier range v16: Fix a typo for iterating over multiple objects in the range and rearrange error path to destroy the mmu_notifier locklessly. Also close a race between invalidate_range and the get_pages_worker. v17: Close a race between get_pages_worker/invalidate_range and fresh allocations of the same userptr range - and notice that struct_mutex was presumed to be held when during creation it wasn't. v18: Sigh. Fix the refactor of st_set_pages() to allocate enough memory for the struct sg_table and to clear it before reporting an error. v19: Always error out on read-only userptr requests as we don't have the hardware infrastructure to support them at the moment. v20: Refuse to implement read-only support until we have the required infrastructure - but reserve the bit in flags for future use. v21: use_mm() is not required for get_user_pages(). It is only meant to be used to fix up the kernel thread's current->mm for use with copy_user(). v22: Use sg_alloc_table_from_pages for that chunky feeling v23: Export a function for sanity checking dma-buf rather than encode userptr details elsewhere, and clean up comments based on suggestions by Bradley. Signed-off-by: Chris Wilson Cc: Tvrtko Ursulin Cc: "Gong, Zhipeng" Cc: Akash Goel Cc: "Volkin, Bradley D" Reviewed-by: Tvrtko Ursulin Reviewed-by: Brad Volkin [danvet: Frob ioctl allocation to pick the next one - will cause a bit of fuss with create2 apparently, but such are the rules.] [danvet2: oops, forgot to git add after manual patch application] [danvet3: Appease sparse.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/Kconfig | 1 + drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 25 +- drivers/gpu/drm/i915/i915_gem.c | 4 + drivers/gpu/drm/i915/i915_gem_dmabuf.c | 8 + drivers/gpu/drm/i915/i915_gem_userptr.c | 711 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_gpu_error.c | 2 + include/uapi/drm/i915_drm.h | 16 + 9 files changed, 768 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/i915/i915_gem_userptr.c (limited to 'include') diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index e4e3c01b8cbc..437e1824d0bf 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -5,6 +5,7 @@ config DRM_I915 depends on (AGP || AGP=n) select INTEL_GTT select AGP_INTEL if AGP + select INTERVAL_TREE # we need shmfs for the swappable backing store, and in particular # the shmem_readpage() which depends upon tmpfs select SHMEM diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 24469168d550..7b2f3bee3518 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -27,6 +27,7 @@ i915-y += i915_cmd_parser.o \ i915_gem.o \ i915_gem_stolen.o \ i915_gem_tiling.o \ + i915_gem_userptr.o \ i915_gpu_error.o \ i915_irq.o \ i915_trace_points.o \ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 46f1decab76f..dea455bd889a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1983,6 +1983,7 @@ const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GET_RESET_STATS, i915_get_reset_stats_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9e9e8166a1f3..a270e0773e2d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,7 @@ enum hpd_pin { if ((intel_connector)->base.encoder == (__encoder)) struct drm_i915_private; +struct i915_mmu_object; enum intel_dpll_id { DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */ @@ -403,6 +405,7 @@ struct drm_i915_error_state { u32 tiling:2; u32 dirty:1; u32 purgeable:1; + u32 userptr:1; s32 ring:4; u32 cache_level:3; } **active_bo, **pinned_bo; @@ -1447,6 +1450,9 @@ struct drm_i915_private { struct i915_gtt gtt; /* VM representing the global address space */ struct i915_gem_mm mm; +#if defined(CONFIG_MMU_NOTIFIER) + DECLARE_HASHTABLE(mmu_notifiers, 7); +#endif /* Kernel Modesetting */ @@ -1580,6 +1586,8 @@ struct drm_i915_gem_object_ops { */ int (*get_pages)(struct drm_i915_gem_object *); void (*put_pages)(struct drm_i915_gem_object *); + int (*dmabuf_export)(struct drm_i915_gem_object *); + void (*release)(struct drm_i915_gem_object *); }; struct drm_i915_gem_object { @@ -1693,8 +1701,20 @@ struct drm_i915_gem_object { /** for phy allocated objects */ struct drm_i915_gem_phys_object *phys_obj; -}; + union { + struct i915_gem_userptr { + uintptr_t ptr; + unsigned read_only :1; + unsigned workers :4; +#define I915_GEM_USERPTR_MAX_WORKERS 15 + + struct mm_struct *mm; + struct i915_mmu_object *mn; + struct work_struct *work; + } userptr; + }; +}; #define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base) /** @@ -2119,6 +2139,9 @@ int i915_gem_set_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_get_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_init_userptr(struct drm_device *dev); +int i915_gem_userptr_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_wait_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f6c03513de52..d5aad0bc71f7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4263,6 +4263,9 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) if (obj->base.import_attach) drm_prime_gem_destroy(&obj->base, NULL); + if (obj->ops->release) + obj->ops->release(obj); + drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(dev_priv, obj->base.size); @@ -4542,6 +4545,7 @@ int i915_gem_init(struct drm_device *dev) DRM_DEBUG_DRIVER("allow wake ack timed out\n"); } + i915_gem_init_userptr(dev); i915_gem_init_global_gtt(dev); ret = i915_gem_context_init(dev); diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index 321102a8374b..580aa42443ed 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -229,6 +229,14 @@ static const struct dma_buf_ops i915_dmabuf_ops = { struct dma_buf *i915_gem_prime_export(struct drm_device *dev, struct drm_gem_object *gem_obj, int flags) { + struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); + + if (obj->ops->dmabuf_export) { + int ret = obj->ops->dmabuf_export(obj); + if (ret) + return ERR_PTR(ret); + } + return dma_buf_export(gem_obj, &i915_dmabuf_ops, gem_obj->size, flags); } diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c new file mode 100644 index 000000000000..21ea92886a56 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -0,0 +1,711 @@ +/* + * Copyright © 2012-2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include "drmP.h" +#include "i915_drm.h" +#include "i915_drv.h" +#include "i915_trace.h" +#include "intel_drv.h" +#include +#include +#include +#include + +#if defined(CONFIG_MMU_NOTIFIER) +#include + +struct i915_mmu_notifier { + spinlock_t lock; + struct hlist_node node; + struct mmu_notifier mn; + struct rb_root objects; + struct drm_device *dev; + struct mm_struct *mm; + struct work_struct work; + unsigned long count; + unsigned long serial; +}; + +struct i915_mmu_object { + struct i915_mmu_notifier *mmu; + struct interval_tree_node it; + struct drm_i915_gem_object *obj; +}; + +static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, + struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + struct i915_mmu_notifier *mn = container_of(_mn, struct i915_mmu_notifier, mn); + struct interval_tree_node *it = NULL; + unsigned long serial = 0; + + end--; /* interval ranges are inclusive, but invalidate range is exclusive */ + while (start < end) { + struct drm_i915_gem_object *obj; + + obj = NULL; + spin_lock(&mn->lock); + if (serial == mn->serial) + it = interval_tree_iter_next(it, start, end); + else + 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); + serial = mn->serial; + } + spin_unlock(&mn->lock); + if (obj == NULL) + return; + + mutex_lock(&mn->dev->struct_mutex); + /* Cancel any active worker and force us to re-evaluate gup */ + obj->userptr.work = NULL; + + if (obj->pages != NULL) { + struct drm_i915_private *dev_priv = to_i915(mn->dev); + struct i915_vma *vma, *tmp; + bool was_interruptible; + + was_interruptible = dev_priv->mm.interruptible; + dev_priv->mm.interruptible = false; + + list_for_each_entry_safe(vma, tmp, &obj->vma_list, vma_link) { + int ret = i915_vma_unbind(vma); + WARN_ON(ret && ret != -EIO); + } + WARN_ON(i915_gem_object_put_pages(obj)); + + dev_priv->mm.interruptible = was_interruptible; + } + + start = obj->userptr.ptr + obj->base.size; + + drm_gem_object_unreference(&obj->base); + mutex_unlock(&mn->dev->struct_mutex); + } +} + +static const struct mmu_notifier_ops i915_gem_userptr_notifier = { + .invalidate_range_start = i915_gem_userptr_mn_invalidate_range_start, +}; + +static struct i915_mmu_notifier * +__i915_mmu_notifier_lookup(struct drm_device *dev, struct mm_struct *mm) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct i915_mmu_notifier *mmu; + + /* Protected by dev->struct_mutex */ + hash_for_each_possible(dev_priv->mmu_notifiers, mmu, node, (unsigned long)mm) + if (mmu->mm == mm) + return mmu; + + return NULL; +} + +static struct i915_mmu_notifier * +i915_mmu_notifier_get(struct drm_device *dev, struct mm_struct *mm) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct i915_mmu_notifier *mmu; + int ret; + + lockdep_assert_held(&dev->struct_mutex); + + mmu = __i915_mmu_notifier_lookup(dev, mm); + if (mmu) + return mmu; + + mmu = kmalloc(sizeof(*mmu), GFP_KERNEL); + if (mmu == NULL) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&mmu->lock); + mmu->dev = dev; + mmu->mn.ops = &i915_gem_userptr_notifier; + mmu->mm = mm; + mmu->objects = RB_ROOT; + mmu->count = 0; + mmu->serial = 0; + + /* Protected by mmap_sem (write-lock) */ + ret = __mmu_notifier_register(&mmu->mn, mm); + if (ret) { + kfree(mmu); + return ERR_PTR(ret); + } + + /* Protected by dev->struct_mutex */ + hash_add(dev_priv->mmu_notifiers, &mmu->node, (unsigned long)mm); + return mmu; +} + +static void +__i915_mmu_notifier_destroy_worker(struct work_struct *work) +{ + struct i915_mmu_notifier *mmu = container_of(work, typeof(*mmu), work); + mmu_notifier_unregister(&mmu->mn, mmu->mm); + kfree(mmu); +} + +static void +__i915_mmu_notifier_destroy(struct i915_mmu_notifier *mmu) +{ + lockdep_assert_held(&mmu->dev->struct_mutex); + + /* Protected by dev->struct_mutex */ + hash_del(&mmu->node); + + /* Our lock ordering is: mmap_sem, mmu_notifier_scru, struct_mutex. + * We enter the function holding struct_mutex, therefore we need + * to drop our mutex prior to calling mmu_notifier_unregister in + * order to prevent lock inversion (and system-wide deadlock) + * between the mmap_sem and struct-mutex. Hence we defer the + * unregistration to a workqueue where we hold no locks. + */ + INIT_WORK(&mmu->work, __i915_mmu_notifier_destroy_worker); + schedule_work(&mmu->work); +} + +static void __i915_mmu_notifier_update_serial(struct i915_mmu_notifier *mmu) +{ + if (++mmu->serial == 0) + mmu->serial = 1; +} + +static void +i915_mmu_notifier_del(struct i915_mmu_notifier *mmu, + struct i915_mmu_object *mn) +{ + lockdep_assert_held(&mmu->dev->struct_mutex); + + spin_lock(&mmu->lock); + interval_tree_remove(&mn->it, &mmu->objects); + __i915_mmu_notifier_update_serial(mmu); + spin_unlock(&mmu->lock); + + /* Protected against _add() by dev->struct_mutex */ + if (--mmu->count == 0) + __i915_mmu_notifier_destroy(mmu); +} + +static int +i915_mmu_notifier_add(struct i915_mmu_notifier *mmu, + struct i915_mmu_object *mn) +{ + struct interval_tree_node *it; + int ret; + + ret = i915_mutex_lock_interruptible(mmu->dev); + if (ret) + return ret; + + /* Make sure we drop the final active reference (and thereby + * remove the objects from the interval tree) before we do + * the check for overlapping objects. + */ + i915_gem_retire_requests(mmu->dev); + + /* Disallow overlapping userptr objects */ + spin_lock(&mmu->lock); + it = interval_tree_iter_first(&mmu->objects, + mn->it.start, mn->it.last); + if (it) { + struct drm_i915_gem_object *obj; + + /* We only need to check the first object in the range as it + * either has cancelled gup work queued and we need to + * return back to the user to give time for the gup-workers + * to flush their object references upon which the object will + * be removed from the interval-tree, or the the range is + * still in use by another client and the overlap is invalid. + */ + + obj = container_of(it, struct i915_mmu_object, it)->obj; + ret = obj->userptr.workers ? -EAGAIN : -EINVAL; + } else { + interval_tree_insert(&mn->it, &mmu->objects); + __i915_mmu_notifier_update_serial(mmu); + ret = 0; + } + spin_unlock(&mmu->lock); + mutex_unlock(&mmu->dev->struct_mutex); + + return ret; +} + +static void +i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj) +{ + struct i915_mmu_object *mn; + + mn = obj->userptr.mn; + if (mn == NULL) + return; + + i915_mmu_notifier_del(mn->mmu, mn); + obj->userptr.mn = NULL; +} + +static int +i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj, + unsigned flags) +{ + struct i915_mmu_notifier *mmu; + struct i915_mmu_object *mn; + int ret; + + if (flags & I915_USERPTR_UNSYNCHRONIZED) + return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; + + down_write(&obj->userptr.mm->mmap_sem); + ret = i915_mutex_lock_interruptible(obj->base.dev); + if (ret == 0) { + mmu = i915_mmu_notifier_get(obj->base.dev, obj->userptr.mm); + if (!IS_ERR(mmu)) + mmu->count++; /* preemptive add to act as a refcount */ + else + ret = PTR_ERR(mmu); + mutex_unlock(&obj->base.dev->struct_mutex); + } + up_write(&obj->userptr.mm->mmap_sem); + if (ret) + return ret; + + mn = kzalloc(sizeof(*mn), GFP_KERNEL); + if (mn == NULL) { + ret = -ENOMEM; + goto destroy_mmu; + } + + mn->mmu = mmu; + mn->it.start = obj->userptr.ptr; + mn->it.last = mn->it.start + obj->base.size - 1; + mn->obj = obj; + + ret = i915_mmu_notifier_add(mmu, mn); + if (ret) + goto free_mn; + + obj->userptr.mn = mn; + return 0; + +free_mn: + kfree(mn); +destroy_mmu: + mutex_lock(&obj->base.dev->struct_mutex); + if (--mmu->count == 0) + __i915_mmu_notifier_destroy(mmu); + mutex_unlock(&obj->base.dev->struct_mutex); + return ret; +} + +#else + +static void +i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj) +{ +} + +static int +i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj, + unsigned flags) +{ + if ((flags & I915_USERPTR_UNSYNCHRONIZED) == 0) + return -ENODEV; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + return 0; +} +#endif + +struct get_pages_work { + struct work_struct work; + struct drm_i915_gem_object *obj; + struct task_struct *task; +}; + + +#if IS_ENABLED(CONFIG_SWIOTLB) +#define swiotlb_active() swiotlb_nr_tbl() +#else +#define swiotlb_active() 0 +#endif + +static int +st_set_pages(struct sg_table **st, struct page **pvec, int num_pages) +{ + struct scatterlist *sg; + int ret, n; + + *st = kmalloc(sizeof(**st), GFP_KERNEL); + if (*st == NULL) + return -ENOMEM; + + if (swiotlb_active()) { + ret = sg_alloc_table(*st, num_pages, GFP_KERNEL); + if (ret) + goto err; + + for_each_sg((*st)->sgl, sg, num_pages, n) + sg_set_page(sg, pvec[n], PAGE_SIZE, 0); + } else { + ret = sg_alloc_table_from_pages(*st, pvec, num_pages, + 0, num_pages << PAGE_SHIFT, + GFP_KERNEL); + if (ret) + goto err; + } + + return 0; + +err: + kfree(*st); + *st = NULL; + return ret; +} + +static void +__i915_gem_userptr_get_pages_worker(struct work_struct *_work) +{ + struct get_pages_work *work = container_of(_work, typeof(*work), work); + struct drm_i915_gem_object *obj = work->obj; + struct drm_device *dev = obj->base.dev; + const int num_pages = obj->base.size >> PAGE_SHIFT; + struct page **pvec; + int pinned, ret; + + ret = -ENOMEM; + pinned = 0; + + pvec = kmalloc(num_pages*sizeof(struct page *), + GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); + if (pvec == NULL) + pvec = drm_malloc_ab(num_pages, sizeof(struct page *)); + if (pvec != NULL) { + struct mm_struct *mm = obj->userptr.mm; + + down_read(&mm->mmap_sem); + while (pinned < num_pages) { + ret = get_user_pages(work->task, mm, + obj->userptr.ptr + pinned * PAGE_SIZE, + num_pages - pinned, + !obj->userptr.read_only, 0, + pvec + pinned, NULL); + if (ret < 0) + break; + + pinned += ret; + } + up_read(&mm->mmap_sem); + } + + mutex_lock(&dev->struct_mutex); + if (obj->userptr.work != &work->work) { + ret = 0; + } else if (pinned == num_pages) { + ret = st_set_pages(&obj->pages, pvec, num_pages); + if (ret == 0) { + list_add_tail(&obj->global_list, &to_i915(dev)->mm.unbound_list); + pinned = 0; + } + } + + obj->userptr.work = ERR_PTR(ret); + obj->userptr.workers--; + drm_gem_object_unreference(&obj->base); + mutex_unlock(&dev->struct_mutex); + + release_pages(pvec, pinned, 0); + drm_free_large(pvec); + + put_task_struct(work->task); + kfree(work); +} + +static int +i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj) +{ + const int num_pages = obj->base.size >> PAGE_SHIFT; + struct page **pvec; + int pinned, ret; + + /* If userspace should engineer that these pages are replaced in + * the vma between us binding this page into the GTT and completion + * of rendering... Their loss. If they change the mapping of their + * pages they need to create a new bo to point to the new vma. + * + * However, that still leaves open the possibility of the vma + * being copied upon fork. Which falls under the same userspace + * synchronisation issue as a regular bo, except that this time + * the process may not be expecting that a particular piece of + * memory is tied to the GPU. + * + * Fortunately, we can hook into the mmu_notifier in order to + * discard the page references prior to anything nasty happening + * to the vma (discard or cloning) which should prevent the more + * egregious cases from causing harm. + */ + + pvec = NULL; + pinned = 0; + if (obj->userptr.mm == current->mm) { + pvec = kmalloc(num_pages*sizeof(struct page *), + GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); + if (pvec == NULL) { + pvec = drm_malloc_ab(num_pages, sizeof(struct page *)); + if (pvec == NULL) + return -ENOMEM; + } + + pinned = __get_user_pages_fast(obj->userptr.ptr, num_pages, + !obj->userptr.read_only, pvec); + } + if (pinned < num_pages) { + if (pinned < 0) { + ret = pinned; + pinned = 0; + } else { + /* Spawn a worker so that we can acquire the + * user pages without holding our mutex. Access + * to the user pages requires mmap_sem, and we have + * a strict lock ordering of mmap_sem, struct_mutex - + * we already hold struct_mutex here and so cannot + * call gup without encountering a lock inversion. + * + * Userspace will keep on repeating the operation + * (thanks to EAGAIN) until either we hit the fast + * path or the worker completes. If the worker is + * cancelled or superseded, the task is still run + * but the results ignored. (This leads to + * complications that we may have a stray object + * refcount that we need to be wary of when + * checking for existing objects during creation.) + * If the worker encounters an error, it reports + * that error back to this function through + * obj->userptr.work = ERR_PTR. + */ + ret = -EAGAIN; + if (obj->userptr.work == NULL && + obj->userptr.workers < I915_GEM_USERPTR_MAX_WORKERS) { + struct get_pages_work *work; + + work = kmalloc(sizeof(*work), GFP_KERNEL); + if (work != NULL) { + obj->userptr.work = &work->work; + obj->userptr.workers++; + + work->obj = obj; + drm_gem_object_reference(&obj->base); + + work->task = current; + get_task_struct(work->task); + + INIT_WORK(&work->work, __i915_gem_userptr_get_pages_worker); + schedule_work(&work->work); + } else + ret = -ENOMEM; + } else { + if (IS_ERR(obj->userptr.work)) { + ret = PTR_ERR(obj->userptr.work); + obj->userptr.work = NULL; + } + } + } + } else { + ret = st_set_pages(&obj->pages, pvec, num_pages); + if (ret == 0) { + obj->userptr.work = NULL; + pinned = 0; + } + } + + release_pages(pvec, pinned, 0); + drm_free_large(pvec); + return ret; +} + +static void +i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj) +{ + struct scatterlist *sg; + int i; + + BUG_ON(obj->userptr.work != NULL); + + if (obj->madv != I915_MADV_WILLNEED) + obj->dirty = 0; + + for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) { + struct page *page = sg_page(sg); + + if (obj->dirty) + set_page_dirty(page); + + mark_page_accessed(page); + page_cache_release(page); + } + obj->dirty = 0; + + sg_free_table(obj->pages); + kfree(obj->pages); +} + +static void +i915_gem_userptr_release(struct drm_i915_gem_object *obj) +{ + i915_gem_userptr_release__mmu_notifier(obj); + + if (obj->userptr.mm) { + mmput(obj->userptr.mm); + obj->userptr.mm = NULL; + } +} + +static int +i915_gem_userptr_dmabuf_export(struct drm_i915_gem_object *obj) +{ + if (obj->userptr.mn) + return 0; + + return i915_gem_userptr_init__mmu_notifier(obj, 0); +} + +static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = { + .dmabuf_export = i915_gem_userptr_dmabuf_export, + .get_pages = i915_gem_userptr_get_pages, + .put_pages = i915_gem_userptr_put_pages, + .release = i915_gem_userptr_release, +}; + +/** + * Creates a new mm object that wraps some normal memory from the process + * context - user memory. + * + * We impose several restrictions upon the memory being mapped + * into the GPU. + * 1. It must be page aligned (both start/end addresses, i.e ptr and size). + * 2. It cannot overlap any other userptr object in the same address space. + * 3. It must be normal system memory, not a pointer into another map of IO + * space (e.g. it must not be a GTT mmapping of another object). + * 4. We only allow a bo as large as we could in theory map into the GTT, + * that is we limit the size to the total size of the GTT. + * 5. The bo is marked as being snoopable. The backing pages are left + * accessible directly by the CPU, but reads and writes by the GPU may + * incur the cost of a snoop (unless you have an LLC architecture). + * + * Synchronisation between multiple users and the GPU is left to userspace + * through the normal set-domain-ioctl. The kernel will enforce that the + * GPU relinquishes the VMA before it is returned back to the system + * i.e. upon free(), munmap() or process termination. However, the userspace + * malloc() library may not immediately relinquish the VMA after free() and + * instead reuse it whilst the GPU is still reading and writing to the VMA. + * Caveat emptor. + * + * Also note, that the object created here is not currently a "first class" + * object, in that several ioctls are banned. These are the CPU access + * ioctls: mmap(), pwrite and pread. In practice, you are expected to use + * direct access via your pointer rather than use those ioctls. + * + * If you think this is a good interface to use to pass GPU memory between + * drivers, please use dma-buf instead. In fact, wherever possible use + * dma-buf instead. + */ +int +i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_userptr *args = data; + struct drm_i915_gem_object *obj; + int ret; + u32 handle; + + if (args->flags & ~(I915_USERPTR_READ_ONLY | + I915_USERPTR_UNSYNCHRONIZED)) + return -EINVAL; + + if (offset_in_page(args->user_ptr | args->user_size)) + return -EINVAL; + + if (args->user_size > dev_priv->gtt.base.total) + return -E2BIG; + + if (!access_ok(args->flags & I915_USERPTR_READ_ONLY ? VERIFY_READ : VERIFY_WRITE, + (char __user *)(unsigned long)args->user_ptr, args->user_size)) + return -EFAULT; + + if (args->flags & I915_USERPTR_READ_ONLY) { + /* On almost all of the current hw, we cannot tell the GPU that a + * page is readonly, so this is just a placeholder in the uAPI. + */ + return -ENODEV; + } + + /* Allocate the new object */ + obj = i915_gem_object_alloc(dev); + if (obj == NULL) + return -ENOMEM; + + drm_gem_private_object_init(dev, &obj->base, args->user_size); + i915_gem_object_init(obj, &i915_gem_userptr_ops); + obj->cache_level = I915_CACHE_LLC; + obj->base.write_domain = I915_GEM_DOMAIN_CPU; + obj->base.read_domains = I915_GEM_DOMAIN_CPU; + + obj->userptr.ptr = args->user_ptr; + obj->userptr.read_only = !!(args->flags & I915_USERPTR_READ_ONLY); + + /* And keep a pointer to the current->mm for resolving the user pages + * at binding. This means that we need to hook into the mmu_notifier + * in order to detect if the mmu is destroyed. + */ + ret = -ENOMEM; + if ((obj->userptr.mm = get_task_mm(current))) + ret = i915_gem_userptr_init__mmu_notifier(obj, args->flags); + if (ret == 0) + ret = drm_gem_handle_create(file, &obj->base, &handle); + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(&obj->base); + if (ret) + return ret; + + args->handle = handle; + return 0; +} + +int +i915_gem_init_userptr(struct drm_device *dev) +{ +#if defined(CONFIG_MMU_NOTIFIER) + struct drm_i915_private *dev_priv = to_i915(dev); + hash_init(dev_priv->mmu_notifiers); +#endif + return 0; +} diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 2d819858c19b..dcbec692f60f 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -205,6 +205,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, err_puts(m, tiling_flag(err->tiling)); err_puts(m, dirty_flag(err->dirty)); err_puts(m, purgeable_flag(err->purgeable)); + err_puts(m, err->userptr ? " userptr" : ""); err_puts(m, err->ring != -1 ? " " : ""); err_puts(m, ring_str(err->ring)); err_puts(m, i915_cache_level_str(err->cache_level)); @@ -641,6 +642,7 @@ static void capture_bo(struct drm_i915_error_buffer *err, err->tiling = obj->tiling_mode; err->dirty = obj->dirty; err->purgeable = obj->madv != I915_MADV_WILLNEED; + err->userptr = obj->userptr.mm != NULL; err->ring = obj->ring ? obj->ring->id : -1; err->cache_level = obj->cache_level; } diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 8a3e4ef00c3d..ff57f07c3249 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -223,6 +223,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GEM_GET_CACHING 0x30 #define DRM_I915_REG_READ 0x31 #define DRM_I915_GET_RESET_STATS 0x32 +#define DRM_I915_GEM_USERPTR 0x33 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -273,6 +274,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy) #define DRM_IOCTL_I915_REG_READ DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read) #define DRM_IOCTL_I915_GET_RESET_STATS DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats) +#define DRM_IOCTL_I915_GEM_USERPTR DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR, struct drm_i915_gem_userptr) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -1050,4 +1052,18 @@ struct drm_i915_reset_stats { __u32 pad; }; +struct drm_i915_gem_userptr { + __u64 user_ptr; + __u64 user_size; + __u32 flags; +#define I915_USERPTR_READ_ONLY 0x1 +#define I915_USERPTR_UNSYNCHRONIZED 0x80000000 + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; +}; + #endif /* _UAPI_I915_DRM_H_ */ -- cgit v1.2.3 From 1836eea209546b870dd83f3f4ef234d6598a560d Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Sat, 10 May 2014 10:32:57 -0400 Subject: lib/crc7: Shift crc7() output left 1 bit This eliminates a 1-bit left shift in every single caller, and makes the inner loop of the CRC computation more efficient. Renamed crc7 to crc7_be (big-endian) since the interface changed. Also purged #include from files that don't use it at all. Signed-off-by: George Spelvin Reviewed-by: Pavel Machek Acked-by: Ulf Hansson Signed-off-by: John W. Linville --- drivers/mmc/host/mmc_spi.c | 2 +- drivers/net/wireless/ti/wl1251/acx.c | 1 - drivers/net/wireless/ti/wl1251/cmd.c | 1 - drivers/net/wireless/ti/wl1251/spi.c | 3 +- drivers/net/wireless/ti/wlcore/spi.c | 3 +- include/linux/crc7.h | 8 ++-- lib/crc7.c | 84 ++++++++++++++++++++---------------- 7 files changed, 53 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 0a87e5691341..338e2202eaaa 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -472,7 +472,7 @@ mmc_spi_command_send(struct mmc_spi_host *host, *cp++ = (u8)(arg >> 16); *cp++ = (u8)(arg >> 8); *cp++ = (u8)arg; - *cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01; + *cp++ = crc7_be(0, &data->status[1], 5) | 0x01; /* Then, read up to 13 bytes (while writing all-ones): * - N(CR) (== 1..8) bytes of all-ones diff --git a/drivers/net/wireless/ti/wl1251/acx.c b/drivers/net/wireless/ti/wl1251/acx.c index 5a4ec56c83d0..5695628757ee 100644 --- a/drivers/net/wireless/ti/wl1251/acx.c +++ b/drivers/net/wireless/ti/wl1251/acx.c @@ -2,7 +2,6 @@ #include #include -#include #include "wl1251.h" #include "reg.h" diff --git a/drivers/net/wireless/ti/wl1251/cmd.c b/drivers/net/wireless/ti/wl1251/cmd.c index bf1fa18b9786..ede31f048ef9 100644 --- a/drivers/net/wireless/ti/wl1251/cmd.c +++ b/drivers/net/wireless/ti/wl1251/cmd.c @@ -2,7 +2,6 @@ #include #include -#include #include #include "wl1251.h" diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index b06d36d99362..e94b57cd5a22 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -122,8 +122,7 @@ static void wl1251_spi_wake(struct wl1251 *wl) crc[3] = cmd[6]; crc[4] = cmd[5]; - cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; - cmd[4] |= WSPI_INIT_CMD_END; + cmd[4] = crc7_be(0, crc, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END; t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 5f3a389dd74c..1d4ddabe6063 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -151,8 +151,7 @@ static void wl12xx_spi_init(struct device *child) crc[3] = cmd[6]; crc[4] = cmd[5]; - cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; - cmd[4] |= WSPI_INIT_CMD_END; + cmd[4] = crc7_be(0, crc, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END; t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; diff --git a/include/linux/crc7.h b/include/linux/crc7.h index 1786e772d5c6..d590765106f3 100644 --- a/include/linux/crc7.h +++ b/include/linux/crc7.h @@ -2,13 +2,13 @@ #define _LINUX_CRC7_H #include -extern const u8 crc7_syndrome_table[256]; +extern const u8 crc7_be_syndrome_table[256]; -static inline u8 crc7_byte(u8 crc, u8 data) +static inline u8 crc7_be_byte(u8 crc, u8 data) { - return crc7_syndrome_table[(crc << 1) ^ data]; + return crc7_be_syndrome_table[crc ^ data]; } -extern u8 crc7(u8 crc, const u8 *buffer, size_t len); +extern u8 crc7_be(u8 crc, const u8 *buffer, size_t len); #endif diff --git a/lib/crc7.c b/lib/crc7.c index f1c3a144cec1..bf6255e23919 100644 --- a/lib/crc7.c +++ b/lib/crc7.c @@ -10,42 +10,47 @@ #include -/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */ -const u8 crc7_syndrome_table[256] = { - 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, - 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, - 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, - 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, - 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, - 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, - 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, - 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, - 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, - 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, - 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, - 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, - 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, - 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, - 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, - 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, - 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, - 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, - 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, - 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, - 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, - 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, - 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, - 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, - 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, - 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, - 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, - 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, - 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, - 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, - 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, - 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 +/* + * Table for CRC-7 (polynomial x^7 + x^3 + 1). + * This is a big-endian CRC (msbit is highest power of x), + * aligned so the msbit of the byte is the x^6 coefficient + * and the lsbit is not used. + */ +const u8 crc7_be_syndrome_table[256] = { + 0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, + 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee, + 0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c, + 0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc, + 0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a, + 0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a, + 0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28, + 0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8, + 0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6, + 0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26, + 0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84, + 0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14, + 0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2, + 0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42, + 0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0, + 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70, + 0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc, + 0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c, + 0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce, + 0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e, + 0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98, + 0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08, + 0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa, + 0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a, + 0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34, + 0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4, + 0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06, + 0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96, + 0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50, + 0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0, + 0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62, + 0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2 }; -EXPORT_SYMBOL(crc7_syndrome_table); +EXPORT_SYMBOL(crc7_be_syndrome_table); /** * crc7 - update the CRC7 for the data buffer @@ -55,14 +60,17 @@ EXPORT_SYMBOL(crc7_syndrome_table); * Context: any * * Returns the updated CRC7 value. + * The CRC7 is left-aligned in the byte (the lsbit is always 0), as that + * makes the computation easier, and all callers want it in that form. + * */ -u8 crc7(u8 crc, const u8 *buffer, size_t len) +u8 crc7_be(u8 crc, const u8 *buffer, size_t len) { while (len--) - crc = crc7_byte(crc, *buffer++); + crc = crc7_be_byte(crc, *buffer++); return crc; } -EXPORT_SYMBOL(crc7); +EXPORT_SYMBOL(crc7_be); MODULE_DESCRIPTION("CRC7 calculations"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ce8d9e0d6746ff67c1870386b7121a4448f21130 Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Thu, 15 May 2014 15:29:27 +0300 Subject: net/mlx4_core: Add UPDATE_QP SRIOV wrapper support This patch adds UPDATE_QP SRIOV wrapper support. The mechanism is a general one, but currently only source MAC index changes are allowed for VFs. Signed-off-by: Matan Barak Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 4 +- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 6 +++ drivers/net/ethernet/mellanox/mlx4/qp.c | 35 ++++++++++++++ .../net/ethernet/mellanox/mlx4/resource_tracker.c | 54 ++++++++++++++++++++++ include/linux/mlx4/qp.h | 11 +++++ 5 files changed, 108 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 78099eab7673..92d3249f63f1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1253,12 +1253,12 @@ static struct mlx4_cmd_info cmd_info[] = { }, { .opcode = MLX4_CMD_UPDATE_QP, - .has_inbox = false, + .has_inbox = true, .has_outbox = false, .out_is_imm = false, .encode_slave_id = false, .verify = NULL, - .wrapper = mlx4_CMD_EPERM_wrapper + .wrapper = mlx4_UPDATE_QP_wrapper }, { .opcode = MLX4_CMD_GET_OP_REQ, diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index f9c465101963..212cea440f90 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1195,6 +1195,12 @@ int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd); +int mlx4_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd); + int mlx4_PROMISC_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 61d64ebffd56..fbd32af89c7c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -389,6 +389,41 @@ err_icm: EXPORT_SYMBOL_GPL(mlx4_qp_alloc); +#define MLX4_UPDATE_QP_SUPPORTED_ATTRS MLX4_UPDATE_QP_SMAC +int mlx4_update_qp(struct mlx4_dev *dev, struct mlx4_qp *qp, + enum mlx4_update_qp_attr attr, + struct mlx4_update_qp_params *params) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_update_qp_context *cmd; + u64 pri_addr_path_mask = 0; + int err = 0; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + cmd = (struct mlx4_update_qp_context *)mailbox->buf; + + if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS)) + return -EINVAL; + + if (attr & MLX4_UPDATE_QP_SMAC) { + pri_addr_path_mask |= 1ULL << MLX4_UPD_QP_PATH_MASK_MAC_INDEX; + cmd->qp_context.pri_path.grh_mylmc = params->smac_index; + } + + cmd->primary_addr_path_mask = cpu_to_be64(pri_addr_path_mask); + + err = mlx4_cmd(dev, mailbox->dma, qp->qpn & 0xffffff, 0, + MLX4_CMD_UPDATE_QP, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_update_qp); + void mlx4_qp_remove(struct mlx4_dev *dev, struct mlx4_qp *qp) { struct mlx4_qp_table *qp_table = &mlx4_priv(dev)->qp_table; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 1c3fdd4a1f7d..8f1254a79832 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -3895,6 +3895,60 @@ static int add_eth_header(struct mlx4_dev *dev, int slave, } +#define MLX4_UPD_QP_PATH_MASK_SUPPORTED (1ULL << MLX4_UPD_QP_PATH_MASK_MAC_INDEX) +int mlx4_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd_info) +{ + int err; + u32 qpn = vhcr->in_modifier & 0xffffff; + struct res_qp *rqp; + u64 mac; + unsigned port; + u64 pri_addr_path_mask; + struct mlx4_update_qp_context *cmd; + int smac_index; + + cmd = (struct mlx4_update_qp_context *)inbox->buf; + + pri_addr_path_mask = be64_to_cpu(cmd->primary_addr_path_mask); + if (cmd->qp_mask || cmd->secondary_addr_path_mask || + (pri_addr_path_mask & ~MLX4_UPD_QP_PATH_MASK_SUPPORTED)) + return -EPERM; + + /* Just change the smac for the QP */ + err = get_res(dev, slave, qpn, RES_QP, &rqp); + if (err) { + mlx4_err(dev, "Updating qpn 0x%x for slave %d rejected\n", qpn, slave); + return err; + } + + port = (rqp->sched_queue >> 6 & 1) + 1; + smac_index = cmd->qp_context.pri_path.grh_mylmc; + err = mac_find_smac_ix_in_slave(dev, slave, port, + smac_index, &mac); + if (err) { + mlx4_err(dev, "Failed to update qpn 0x%x, MAC is invalid. smac_ix: %d\n", + qpn, smac_index); + goto err_mac; + } + + err = mlx4_cmd(dev, inbox->dma, + vhcr->in_modifier, 0, + MLX4_CMD_UPDATE_QP, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) { + mlx4_err(dev, "Failed to update qpn on qpn 0x%x, command failed\n", qpn); + goto err_mac; + } + +err_mac: + put_res(dev, slave, qpn, RES_QP); + return err; +} + int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index b66e7610d4ee..7040dc98ff8b 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -421,6 +421,17 @@ struct mlx4_wqe_inline_seg { __be32 byte_count; }; +enum mlx4_update_qp_attr { + MLX4_UPDATE_QP_SMAC = 1 << 0, +}; + +struct mlx4_update_qp_params { + u8 smac_index; +}; + +int mlx4_update_qp(struct mlx4_dev *dev, struct mlx4_qp *qp, + enum mlx4_update_qp_attr attr, + struct mlx4_update_qp_params *params); int mlx4_qp_modify(struct mlx4_dev *dev, struct mlx4_mtt *mtt, enum mlx4_qp_state cur_state, enum mlx4_qp_state new_state, struct mlx4_qp_context *context, enum mlx4_qp_optpar optpar, -- cgit v1.2.3 From aae4518b3124b29f8dc81c829c704fd2df72e98b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 16 May 2014 02:46:50 +0200 Subject: PM / sleep: Mechanism to avoid resuming runtime-suspended devices unnecessarily Currently, some subsystems (e.g. PCI and the ACPI PM domain) have to resume all runtime-suspended devices during system suspend, mostly because those devices may need to be reprogrammed due to different wakeup settings for system sleep and for runtime PM. For some devices, though, it's OK to remain in runtime suspend throughout a complete system suspend/resume cycle (if the device was in runtime suspend at the start of the cycle). We would like to do this whenever possible, to avoid the overhead of extra power-up and power-down events. However, problems may arise because the device's descendants may require it to be at full power at various points during the cycle. Therefore the most straightforward way to do this safely is if the device and all its descendants can remain runtime suspended until the complete stage of system resume. To this end, introduce a new device PM flag, power.direct_complete and modify the PM core to use that flag as follows. If the ->prepare() callback of a device returns a positive number, the PM core will regard that as an indication that it may leave the device runtime-suspended. It will then check if the system power transition in progress is a suspend (and not hibernation in particular) and if the device is, indeed, runtime-suspended. In that case, the PM core will set the device's power.direct_complete flag. Otherwise it will clear power.direct_complete for the device and it also will later clear it for the device's parent (if there's one). Next, the PM core will not invoke the ->suspend() ->suspend_late(), ->suspend_irq(), ->resume_irq(), ->resume_early(), or ->resume() callbacks for all devices having power.direct_complete set. It will invoke their ->complete() callbacks, however, and those callbacks are then responsible for resuming the devices as appropriate, if necessary. For example, in some cases they may need to queue up runtime resume requests for the devices using pm_request_resume(). Changelog partly based on an Alan Stern's description of the idea (http://marc.info/?l=linux-pm&m=139940466625569&w=2). Signed-off-by: Rafael J. Wysocki Acked-by: Alan Stern --- drivers/base/power/main.c | 66 +++++++++++++++++++++++++++++++++++----------- include/linux/pm.h | 36 +++++++++++++++++++------ include/linux/pm_runtime.h | 6 +++++ 3 files changed, 85 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 86d5e4fb5b98..343ffad59377 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -479,7 +479,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn TRACE_DEVICE(dev); TRACE_RESUME(0); - if (dev->power.syscore) + if (dev->power.syscore || dev->power.direct_complete) goto Out; if (!dev->power.is_noirq_suspended) @@ -605,7 +605,7 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn TRACE_DEVICE(dev); TRACE_RESUME(0); - if (dev->power.syscore) + if (dev->power.syscore || dev->power.direct_complete) goto Out; if (!dev->power.is_late_suspended) @@ -735,6 +735,12 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) if (dev->power.syscore) goto Complete; + if (dev->power.direct_complete) { + /* Match the pm_runtime_disable() in __device_suspend(). */ + pm_runtime_enable(dev); + goto Complete; + } + dpm_wait(dev->parent, async); dpm_watchdog_set(&wd, dev); device_lock(dev); @@ -1007,7 +1013,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a goto Complete; } - if (dev->power.syscore) + if (dev->power.syscore || dev->power.direct_complete) goto Complete; dpm_wait_for_children(dev, async); @@ -1146,7 +1152,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as goto Complete; } - if (dev->power.syscore) + if (dev->power.syscore || dev->power.direct_complete) goto Complete; dpm_wait_for_children(dev, async); @@ -1332,6 +1338,17 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (dev->power.syscore) goto Complete; + if (dev->power.direct_complete) { + if (pm_runtime_status_suspended(dev)) { + pm_runtime_disable(dev); + if (pm_runtime_suspended_if_enabled(dev)) + goto Complete; + + pm_runtime_enable(dev); + } + dev->power.direct_complete = false; + } + dpm_watchdog_set(&wd, dev); device_lock(dev); @@ -1382,10 +1399,19 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) End: if (!error) { + struct device *parent = dev->parent; + dev->power.is_suspended = true; - if (dev->power.wakeup_path - && dev->parent && !dev->parent->power.ignore_children) - dev->parent->power.wakeup_path = true; + if (parent) { + spin_lock_irq(&parent->power.lock); + + dev->parent->power.direct_complete = false; + if (dev->power.wakeup_path + && !dev->parent->power.ignore_children) + dev->parent->power.wakeup_path = true; + + spin_unlock_irq(&parent->power.lock); + } } device_unlock(dev); @@ -1487,7 +1513,7 @@ static int device_prepare(struct device *dev, pm_message_t state) { int (*callback)(struct device *) = NULL; char *info = NULL; - int error = 0; + int ret = 0; if (dev->power.syscore) return 0; @@ -1523,17 +1549,27 @@ static int device_prepare(struct device *dev, pm_message_t state) callback = dev->driver->pm->prepare; } - if (callback) { - error = callback(dev); - suspend_report_result(callback, error); - } + if (callback) + ret = callback(dev); device_unlock(dev); - if (error) + if (ret < 0) { + suspend_report_result(callback, ret); pm_runtime_put(dev); - - return error; + return ret; + } + /* + * A positive return value from ->prepare() means "this device appears + * to be runtime-suspended and its state is fine, so if it really is + * runtime-suspended, you can leave it in that state provided that you + * will do the same thing with all of its descendants". This only + * applies to suspend transitions, however. + */ + spin_lock_irq(&dev->power.lock); + dev->power.direct_complete = ret > 0 && state.event == PM_EVENT_SUSPEND; + spin_unlock_irq(&dev->power.lock); + return 0; } /** diff --git a/include/linux/pm.h b/include/linux/pm.h index d915d0345fa1..72c0fe098a27 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -93,13 +93,23 @@ typedef struct pm_message { * been registered) to recover from the race condition. * This method is executed for all kinds of suspend transitions and is * followed by one of the suspend callbacks: @suspend(), @freeze(), or - * @poweroff(). The PM core executes subsystem-level @prepare() for all - * devices before starting to invoke suspend callbacks for any of them, so - * generally devices may be assumed to be functional or to respond to - * runtime resume requests while @prepare() is being executed. However, - * device drivers may NOT assume anything about the availability of user - * space at that time and it is NOT valid to request firmware from within - * @prepare() (it's too late to do that). It also is NOT valid to allocate + * @poweroff(). If the transition is a suspend to memory or standby (that + * is, not related to hibernation), the return value of @prepare() may be + * used to indicate to the PM core to leave the device in runtime suspend + * if applicable. Namely, if @prepare() returns a positive number, the PM + * core will understand that as a declaration that the device appears to be + * runtime-suspended and it may be left in that state during the entire + * transition and during the subsequent resume if all of its descendants + * are left in runtime suspend too. If that happens, @complete() will be + * executed directly after @prepare() and it must ensure the proper + * functioning of the device after the system resume. + * The PM core executes subsystem-level @prepare() for all devices before + * starting to invoke suspend callbacks for any of them, so generally + * devices may be assumed to be functional or to respond to runtime resume + * requests while @prepare() is being executed. However, device drivers + * may NOT assume anything about the availability of user space at that + * time and it is NOT valid to request firmware from within @prepare() + * (it's too late to do that). It also is NOT valid to allocate * substantial amounts of memory from @prepare() in the GFP_KERNEL mode. * [To work around these limitations, drivers may register suspend and * hibernation notifiers to be executed before the freezing of tasks.] @@ -112,7 +122,16 @@ typedef struct pm_message { * of the other devices that the PM core has unsuccessfully attempted to * suspend earlier). * The PM core executes subsystem-level @complete() after it has executed - * the appropriate resume callbacks for all devices. + * the appropriate resume callbacks for all devices. If the corresponding + * @prepare() at the beginning of the suspend transition returned a + * positive number and the device was left in runtime suspend (without + * executing any suspend and resume callbacks for it), @complete() will be + * the only callback executed for the device during resume. In that case, + * @complete() must be prepared to do whatever is necessary to ensure the + * proper functioning of the device after the system resume. To this end, + * @complete() can check the power.direct_complete flag of the device to + * learn whether (unset) or not (set) the previous suspend and resume + * callbacks have been executed for it. * * @suspend: Executed before putting the system into a sleep state in which the * contents of main memory are preserved. The exact action to perform @@ -546,6 +565,7 @@ struct dev_pm_info { bool is_late_suspended:1; bool ignore_children:1; bool early_init:1; /* Owned by the PM core */ + bool direct_complete:1; /* Owned by the PM core */ spinlock_t lock; #ifdef CONFIG_PM_SLEEP struct list_head entry; diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 2a5897a4afbc..43fd6716f662 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -101,6 +101,11 @@ static inline bool pm_runtime_status_suspended(struct device *dev) return dev->power.runtime_status == RPM_SUSPENDED; } +static inline bool pm_runtime_suspended_if_enabled(struct device *dev) +{ + return pm_runtime_status_suspended(dev) && dev->power.disable_depth == 1; +} + static inline bool pm_runtime_enabled(struct device *dev) { return !dev->power.disable_depth; @@ -150,6 +155,7 @@ static inline void device_set_run_wake(struct device *dev, bool enable) {} static inline bool pm_runtime_suspended(struct device *dev) { return false; } static inline bool pm_runtime_active(struct device *dev) { return true; } static inline bool pm_runtime_status_suspended(struct device *dev) { return false; } +static inline bool pm_runtime_suspended_if_enabled(struct device *dev) { return false; } static inline bool pm_runtime_enabled(struct device *dev) { return false; } static inline void pm_runtime_no_callbacks(struct device *dev) {} -- cgit v1.2.3 From a75951217472c522c324adb0a4de3ba69d656ef5 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 16 May 2014 16:14:04 +0200 Subject: net: phy: extend fixed driver with fixed_phy_register() The existing fixed_phy_add() function has several drawbacks that prevents it from being used as is for OF-based declaration of fixed PHYs: * The address of the PHY on the fake bus needs to be passed, while a dynamic allocation is desired. * Since the phy_device instantiation is post-poned until the next mdiobus scan, there is no way to associate the fixed PHY with its OF node, which later prevents of_phy_connect() from finding this fixed PHY from a given OF node. To solve this, this commit introduces fixed_phy_register(), which will allocate an available PHY address, add the PHY using fixed_phy_add() and instantiate the phy_device structure associated with the provided OF node. Signed-off-by: Thomas Petazzoni Acked-by: Florian Fainelli Acked-by: Grant Likely Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/fixed.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/phy_fixed.h | 11 +++++++++ 2 files changed, 72 insertions(+) (limited to 'include') diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index e41546da105e..d60d875cb445 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -21,6 +21,7 @@ #include #include #include +#include #define MII_REGS_NUM 29 @@ -203,6 +204,66 @@ err_regs: } EXPORT_SYMBOL_GPL(fixed_phy_add); +void fixed_phy_del(int phy_addr) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct fixed_phy *fp, *tmp; + + list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { + if (fp->addr == phy_addr) { + list_del(&fp->node); + kfree(fp); + return; + } + } +} +EXPORT_SYMBOL_GPL(fixed_phy_del); + +static int phy_fixed_addr; +static DEFINE_SPINLOCK(phy_fixed_addr_lock); + +int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct phy_device *phy; + int phy_addr; + int ret; + + /* Get the next available PHY address, up to PHY_MAX_ADDR */ + spin_lock(&phy_fixed_addr_lock); + if (phy_fixed_addr == PHY_MAX_ADDR) { + spin_unlock(&phy_fixed_addr_lock); + return -ENOSPC; + } + phy_addr = phy_fixed_addr++; + spin_unlock(&phy_fixed_addr_lock); + + ret = fixed_phy_add(PHY_POLL, phy_addr, status); + if (ret < 0) + return ret; + + phy = get_phy_device(fmb->mii_bus, phy_addr, false); + if (!phy || IS_ERR(phy)) { + fixed_phy_del(phy_addr); + return -EINVAL; + } + + of_node_get(np); + phy->dev.of_node = np; + + ret = phy_device_register(phy); + if (ret) { + phy_device_free(phy); + of_node_put(np); + fixed_phy_del(phy_addr); + return ret; + } + + return 0; +} + static int __init fixed_mdio_bus_init(void) { struct fixed_mdio_bus *fmb = &platform_fmb; diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h index 509d8f5f984e..4f2478b47136 100644 --- a/include/linux/phy_fixed.h +++ b/include/linux/phy_fixed.h @@ -9,15 +9,26 @@ struct fixed_phy_status { int asym_pause; }; +struct device_node; + #ifdef CONFIG_FIXED_PHY extern int fixed_phy_add(unsigned int irq, int phy_id, struct fixed_phy_status *status); +extern int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np); #else static inline int fixed_phy_add(unsigned int irq, int phy_id, struct fixed_phy_status *status) { return -ENODEV; } +static inline int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np) +{ + return -ENODEV; +} #endif /* CONFIG_FIXED_PHY */ /* -- cgit v1.2.3 From 3be2a49e5c08d268f8af0dd4fe89a24ea8cdc339 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 16 May 2014 16:14:05 +0200 Subject: of: provide a binding for fixed link PHYs Some Ethernet MACs have a "fixed link", and are not connected to a normal MDIO-managed PHY device. For those situations, a Device Tree binding allows to describe a "fixed link" using a special PHY node. This patch adds: * A documentation for the fixed PHY Device Tree binding. * An of_phy_is_fixed_link() function that an Ethernet driver can call on its PHY phandle to find out whether it's a fixed link PHY or not. It should typically be used to know if of_phy_register_fixed_link() should be called. * An of_phy_register_fixed_link() function that instantiates the fixed PHY into the PHY subsystem, so that when the driver calls of_phy_connect(), the PHY device associated to the OF node will be found. These two additional functions also support the old fixed-link Device Tree binding used on PowerPC platforms, so that ultimately, the network device drivers for those platforms could be converted to use of_phy_is_fixed_link() and of_phy_register_fixed_link() instead of of_phy_connect_fixed_link(), while keeping compatibility with their respective Device Tree bindings. Signed-off-by: Thomas Petazzoni Reviewed-by: Florian Fainelli Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- .../devicetree/bindings/net/fixed-link.txt | 30 ++++++++++ drivers/of/of_mdio.c | 67 ++++++++++++++++++++++ include/linux/of_mdio.h | 15 +++++ 3 files changed, 112 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/fixed-link.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/net/fixed-link.txt b/Documentation/devicetree/bindings/net/fixed-link.txt new file mode 100644 index 000000000000..e956de1be935 --- /dev/null +++ b/Documentation/devicetree/bindings/net/fixed-link.txt @@ -0,0 +1,30 @@ +Fixed link Device Tree binding +------------------------------ + +Some Ethernet MACs have a "fixed link", and are not connected to a +normal MDIO-managed PHY device. For those situations, a Device Tree +binding allows to describe a "fixed link". + +Such a fixed link situation is described by creating a 'fixed-link' +sub-node of the Ethernet MAC device node, with the following +properties: + +* 'speed' (integer, mandatory), to indicate the link speed. Accepted + values are 10, 100 and 1000 +* 'full-duplex' (boolean, optional), to indicate that full duplex is + used. When absent, half duplex is assumed. +* 'pause' (boolean, optional), to indicate that pause should be + enabled. +* 'asym-pause' (boolean, optional), to indicate that asym_pause should + be enabled. + +Example: + +ethernet@0 { + ... + fixed-link { + speed = <1000>; + full-duplex; + }; + ... +}; diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 9a95831bd065..1def0bb5cb37 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -301,3 +302,69 @@ struct phy_device *of_phy_attach(struct net_device *dev, return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy; } EXPORT_SYMBOL(of_phy_attach); + +#if defined(CONFIG_FIXED_PHY) +/* + * of_phy_is_fixed_link() and of_phy_register_fixed_link() must + * support two DT bindings: + * - the old DT binding, where 'fixed-link' was a property with 5 + * cells encoding various informations about the fixed PHY + * - the new DT binding, where 'fixed-link' is a sub-node of the + * Ethernet device. + */ +bool of_phy_is_fixed_link(struct device_node *np) +{ + struct device_node *dn; + int len; + + /* New binding */ + dn = of_get_child_by_name(np, "fixed-link"); + if (dn) { + of_node_put(dn); + return true; + } + + /* Old binding */ + if (of_get_property(np, "fixed-link", &len) && + len == (5 * sizeof(__be32))) + return true; + + return false; +} +EXPORT_SYMBOL(of_phy_is_fixed_link); + +int of_phy_register_fixed_link(struct device_node *np) +{ + struct fixed_phy_status status = {}; + struct device_node *fixed_link_node; + const __be32 *fixed_link_prop; + int len; + + /* New binding */ + fixed_link_node = of_get_child_by_name(np, "fixed-link"); + if (fixed_link_node) { + status.link = 1; + status.duplex = of_property_read_bool(np, "full-duplex"); + if (of_property_read_u32(fixed_link_node, "speed", &status.speed)) + return -EINVAL; + status.pause = of_property_read_bool(np, "pause"); + status.asym_pause = of_property_read_bool(np, "asym-pause"); + of_node_put(fixed_link_node); + return fixed_phy_register(PHY_POLL, &status, np); + } + + /* Old binding */ + fixed_link_prop = of_get_property(np, "fixed-link", &len); + if (fixed_link_prop && len == (5 * sizeof(__be32))) { + status.link = 1; + status.duplex = be32_to_cpu(fixed_link_prop[1]); + status.speed = be32_to_cpu(fixed_link_prop[2]); + status.pause = be32_to_cpu(fixed_link_prop[3]); + status.asym_pause = be32_to_cpu(fixed_link_prop[4]); + return fixed_phy_register(PHY_POLL, &status, np); + } + + return -ENODEV; +} +EXPORT_SYMBOL(of_phy_register_fixed_link); +#endif diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index 881a7c3571f4..0aa367e316cb 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -72,4 +72,19 @@ static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np) } #endif /* CONFIG_OF */ +#if defined(CONFIG_OF) && defined(CONFIG_FIXED_PHY) +extern int of_phy_register_fixed_link(struct device_node *np); +extern bool of_phy_is_fixed_link(struct device_node *np); +#else +static inline int of_phy_register_fixed_link(struct device_node *np) +{ + return -ENOSYS; +} +static inline bool of_phy_is_fixed_link(struct device_node *np) +{ + return false; +} +#endif + + #endif /* __LINUX_OF_MDIO_H */ -- cgit v1.2.3 From dc20759f281c0f9e6c4fca8be251deca2954862a Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 16 May 2014 17:46:35 +0200 Subject: ieee802154: add types for link-layer security The added structures match 802.15.4-2011 link-layer security PIBs as closely as is reasonable. Some lists required by the standard were modeled as bitmaps (frame_types and command_frame_ids in *llsec_key, 802.15.4-2011 7.5/Table 61), since using lists for those seems a bit excessive and not particularly useful. The DeviceDescriptorHandleList was inverted and is here a per-device list, since operations on this list are likely to have both a key and a device at hand, and per-device lists of keys are shorter than per-key lists of devices. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 95 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'include') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index bc9a7475e57e..6f8f9c2f6037 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -242,6 +242,88 @@ static inline struct ieee802154_mac_cb *mac_cb_init(struct sk_buff *skb) return mac_cb(skb); } +#define IEEE802154_LLSEC_KEY_SIZE 16 + +struct ieee802154_llsec_key_id { + u8 mode; + u8 id; + union { + struct ieee802154_addr device_addr; + __le32 short_source; + __le64 extended_source; + }; +}; + +struct ieee802154_llsec_key { + u8 frame_types; + u32 cmd_frame_ids; + u8 key[IEEE802154_LLSEC_KEY_SIZE]; +}; + +struct ieee802154_llsec_key_entry { + struct list_head list; + + struct ieee802154_llsec_key_id id; + struct ieee802154_llsec_key *key; +}; + +struct ieee802154_llsec_device_key { + struct list_head list; + + struct ieee802154_llsec_key_id key_id; + u32 frame_counter; +}; + +enum { + IEEE802154_LLSEC_DEVKEY_IGNORE, + IEEE802154_LLSEC_DEVKEY_RESTRICT, + + __IEEE802154_LLSEC_DEVKEY_MAX, +}; + +struct ieee802154_llsec_device { + struct list_head list; + + __le16 pan_id; + __le16 short_addr; + __le64 hwaddr; + u32 frame_counter; + bool seclevel_exempt; + + u8 key_mode; + struct list_head keys; +}; + +struct ieee802154_llsec_seclevel { + struct list_head list; + + u8 frame_type; + u8 cmd_frame_id; + bool device_override; + u32 sec_levels; +}; + +struct ieee802154_llsec_params { + bool enabled; + + __be32 frame_counter; + u8 out_level; + struct ieee802154_llsec_key_id out_key; + + __le64 default_key_source; + + __le16 pan_id; + __le64 hwaddr; + __le64 coord_hwaddr; + __le16 coord_shortaddr; +}; + +struct ieee802154_llsec_table { + struct list_head keys; + struct list_head devices; + struct list_head security_levels; +}; + #define IEEE802154_MAC_SCAN_ED 0 #define IEEE802154_MAC_SCAN_ACTIVE 1 #define IEEE802154_MAC_SCAN_PASSIVE 2 @@ -260,6 +342,19 @@ struct ieee802154_mac_params { }; struct wpan_phy; + +enum { + IEEE802154_LLSEC_PARAM_ENABLED = 1 << 0, + IEEE802154_LLSEC_PARAM_FRAME_COUNTER = 1 << 1, + IEEE802154_LLSEC_PARAM_OUT_LEVEL = 1 << 2, + IEEE802154_LLSEC_PARAM_OUT_KEY = 1 << 3, + IEEE802154_LLSEC_PARAM_KEY_SOURCE = 1 << 4, + IEEE802154_LLSEC_PARAM_PAN_ID = 1 << 5, + IEEE802154_LLSEC_PARAM_HWADDR = 1 << 6, + IEEE802154_LLSEC_PARAM_COORD_HWADDR = 1 << 7, + IEEE802154_LLSEC_PARAM_COORD_SHORTADDR = 1 << 8, +}; + /* * This should be located at net_device->ml_priv * -- cgit v1.2.3 From f30be4d53cada48598dab0983866ae4b16af46dc Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 16 May 2014 17:46:40 +0200 Subject: mac802154: integrate llsec with wpan devices Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 3 + net/mac802154/mac802154.h | 10 ++++ net/mac802154/wpan.c | 118 ++++++++++++++++++++++++++++++---------- 3 files changed, 103 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 6f8f9c2f6037..000c8552d5de 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -225,6 +225,9 @@ struct ieee802154_mac_cb { u8 type; bool ackreq; bool secen; + bool secen_override; + u8 seclevel; + bool seclevel_override; struct ieee802154_addr source; struct ieee802154_addr dest; }; diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h index e05f66e2eda3..a8d7cbc701a0 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/mac802154.h @@ -23,9 +23,12 @@ #ifndef MAC802154_H #define MAC802154_H +#include #include #include +#include "llsec.h" + /* mac802154 device private data */ struct mac802154_priv { struct ieee802154_dev hw; @@ -91,6 +94,13 @@ struct mac802154_sub_if_data { u8 bsn; /* MAC DSN field */ u8 dsn; + + /* protects sec from concurrent access by netlink. access by + * encrypt/decrypt/header_create safe without additional protection. + */ + struct mutex sec_mtx; + + struct mac802154_llsec sec; }; #define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw) diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index ed105774c15d..00729ca1e30a 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -183,6 +183,38 @@ out: return rc; } +static int mac802154_set_header_security(struct mac802154_sub_if_data *priv, + struct ieee802154_hdr *hdr, + const struct ieee802154_mac_cb *cb) +{ + struct ieee802154_llsec_params params; + u8 level; + + mac802154_llsec_get_params(&priv->sec, ¶ms); + + if (!params.enabled && cb->secen_override && cb->secen) + return -EINVAL; + if (!params.enabled || + (cb->secen_override && !cb->secen) || + !params.out_level) + return 0; + if (cb->seclevel_override && !cb->seclevel) + return -EINVAL; + + level = cb->seclevel_override ? cb->seclevel : params.out_level; + + hdr->fc.security_enabled = 1; + hdr->sec.level = level; + hdr->sec.key_id_mode = params.out_key.mode; + if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX) + hdr->sec.short_src = params.out_key.short_source; + else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX) + hdr->sec.extended_src = params.out_key.extended_source; + hdr->sec.key_id = params.out_key.id; + + return 0; +} + static int mac802154_header_create(struct sk_buff *skb, struct net_device *dev, unsigned short type, @@ -204,6 +236,9 @@ static int mac802154_header_create(struct sk_buff *skb, hdr.fc.ack_request = cb->ackreq; hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev); + if (mac802154_set_header_security(priv, &hdr, cb) < 0) + return -EINVAL; + if (!saddr) { spin_lock_bh(&priv->mib_lock); @@ -259,6 +294,7 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) { struct mac802154_sub_if_data *priv; u8 chan, page; + int rc; priv = netdev_priv(dev); @@ -274,6 +310,13 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } + rc = mac802154_llsec_encrypt(&priv->sec, skb); + if (rc) { + pr_warn("encryption failed: %i\n", rc); + kfree_skb(skb); + return NETDEV_TX_OK; + } + skb->skb_iif = dev->ifindex; dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; @@ -294,6 +337,15 @@ static const struct net_device_ops mac802154_wpan_ops = { .ndo_set_mac_address = mac802154_wpan_mac_addr, }; +static void mac802154_wpan_free(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + mac802154_llsec_destroy(&priv->sec); + + free_netdev(dev); +} + void mac802154_wpan_setup(struct net_device *dev) { struct mac802154_sub_if_data *priv; @@ -303,14 +355,14 @@ void mac802154_wpan_setup(struct net_device *dev) dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN; dev->header_ops = &mac802154_header_ops; - dev->needed_tailroom = 2; /* FCS */ + dev->needed_tailroom = 2 + 16; /* FCS + MIC */ dev->mtu = IEEE802154_MTU; dev->tx_queue_len = 300; dev->type = ARPHRD_IEEE802154; dev->flags = IFF_NOARP | IFF_BROADCAST; dev->watchdog_timeo = 0; - dev->destructor = free_netdev; + dev->destructor = mac802154_wpan_free; dev->netdev_ops = &mac802154_wpan_ops; dev->ml_priv = &mac802154_mlme_wpan; @@ -321,6 +373,7 @@ void mac802154_wpan_setup(struct net_device *dev) priv->page = 0; spin_lock_init(&priv->mib_lock); + mutex_init(&priv->sec_mtx); get_random_bytes(&priv->bsn, 1); get_random_bytes(&priv->dsn, 1); @@ -333,6 +386,8 @@ void mac802154_wpan_setup(struct net_device *dev) priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); + + mac802154_llsec_init(&priv->sec); } static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) @@ -341,9 +396,11 @@ static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) } static int -mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) +mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb, + const struct ieee802154_hdr *hdr) { __le16 span, sshort; + int rc; pr_debug("getting packet via slave interface %s\n", sdata->dev->name); @@ -390,6 +447,12 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) skb->dev = sdata->dev; + rc = mac802154_llsec_decrypt(&sdata->sec, skb); + if (rc) { + pr_debug("decryption failed: %i\n", rc); + return NET_RX_DROP; + } + sdata->dev->stats.rx_packets++; sdata->dev->stats.rx_bytes += skb->len; @@ -421,60 +484,58 @@ static void mac802154_print_addr(const char *name, } } -static int mac802154_parse_frame_start(struct sk_buff *skb) +static int mac802154_parse_frame_start(struct sk_buff *skb, + struct ieee802154_hdr *hdr) { int hlen; - struct ieee802154_hdr hdr; struct ieee802154_mac_cb *cb = mac_cb_init(skb); - hlen = ieee802154_hdr_pull(skb, &hdr); + hlen = ieee802154_hdr_pull(skb, hdr); if (hlen < 0) return -EINVAL; skb->mac_len = hlen; - pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr.fc), - hdr.seq); + pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc), + hdr->seq); - cb->type = hdr.fc.type; - cb->ackreq = hdr.fc.ack_request; - cb->secen = hdr.fc.security_enabled; + cb->type = hdr->fc.type; + cb->ackreq = hdr->fc.ack_request; + cb->secen = hdr->fc.security_enabled; - mac802154_print_addr("destination", &hdr.dest); - mac802154_print_addr("source", &hdr.source); + mac802154_print_addr("destination", &hdr->dest); + mac802154_print_addr("source", &hdr->source); - cb->source = hdr.source; - cb->dest = hdr.dest; + cb->source = hdr->source; + cb->dest = hdr->dest; - if (hdr.fc.security_enabled) { + if (hdr->fc.security_enabled) { u64 key; - pr_debug("seclevel %i\n", hdr.sec.level); + pr_debug("seclevel %i\n", hdr->sec.level); - switch (hdr.sec.key_id_mode) { + switch (hdr->sec.key_id_mode) { case IEEE802154_SCF_KEY_IMPLICIT: pr_debug("implicit key\n"); break; case IEEE802154_SCF_KEY_INDEX: - pr_debug("key %02x\n", hdr.sec.key_id); + pr_debug("key %02x\n", hdr->sec.key_id); break; case IEEE802154_SCF_KEY_SHORT_INDEX: pr_debug("key %04x:%04x %02x\n", - le32_to_cpu(hdr.sec.short_src) >> 16, - le32_to_cpu(hdr.sec.short_src) & 0xffff, - hdr.sec.key_id); + le32_to_cpu(hdr->sec.short_src) >> 16, + le32_to_cpu(hdr->sec.short_src) & 0xffff, + hdr->sec.key_id); break; case IEEE802154_SCF_KEY_HW_INDEX: - key = swab64((__force u64) hdr.sec.extended_src); + key = swab64((__force u64) hdr->sec.extended_src); pr_debug("key source %8phC %02x\n", &key, - hdr.sec.key_id); + hdr->sec.key_id); break; } - - return -EINVAL; } return 0; @@ -485,8 +546,9 @@ void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) int ret; struct sk_buff *sskb; struct mac802154_sub_if_data *sdata; + struct ieee802154_hdr hdr; - ret = mac802154_parse_frame_start(skb); + ret = mac802154_parse_frame_start(skb, &hdr); if (ret) { pr_debug("got invalid frame\n"); return; @@ -499,7 +561,7 @@ void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) sskb = skb_clone(skb, GFP_ATOMIC); if (sskb) - mac802154_subif_frame(sdata, sskb); + mac802154_subif_frame(sdata, sskb, &hdr); } rcu_read_unlock(); } -- cgit v1.2.3 From af9eed5bbf0fb4e398081e79a707545dcca5ebda Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 16 May 2014 17:46:41 +0200 Subject: ieee802154: add dgram sockopts for security control Allow datagram sockets to override the security settings of the device they send from on a per-socket basis. Requires CAP_NET_ADMIN or CAP_NET_RAW, since raw sockets can send arbitrary packets anyway. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/af_ieee802154.h | 10 ++++++- net/ieee802154/dgram.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/af_ieee802154.h b/include/net/af_ieee802154.h index f79ae2aa76d6..085940f7eeec 100644 --- a/include/net/af_ieee802154.h +++ b/include/net/af_ieee802154.h @@ -57,6 +57,14 @@ struct sockaddr_ieee802154 { /* get/setsockopt */ #define SOL_IEEE802154 0 -#define WPAN_WANTACK 0 +#define WPAN_WANTACK 0 +#define WPAN_SECURITY 1 +#define WPAN_SECURITY_LEVEL 2 + +#define WPAN_SECURITY_DEFAULT 0 +#define WPAN_SECURITY_OFF 1 +#define WPAN_SECURITY_ON 2 + +#define WPAN_SECURITY_LEVEL_DEFAULT (-1) #endif diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 76c77256a56e..4f0ed8780194 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -21,6 +21,7 @@ * Dmitry Eremin-Solenikov */ +#include #include #include #include @@ -47,6 +48,10 @@ struct dgram_sock { unsigned int bound:1; unsigned int connected:1; unsigned int want_ack:1; + unsigned int secen:1; + unsigned int secen_override:1; + unsigned int seclevel:3; + unsigned int seclevel_override:1; }; static inline struct dgram_sock *dgram_sk(const struct sock *sk) @@ -264,6 +269,11 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, dst_addr = ro->dst_addr; } + cb->secen = ro->secen; + cb->secen_override = ro->secen_override; + cb->seclevel = ro->seclevel; + cb->seclevel_override = ro->seclevel_override; + err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr, ro->bound ? &ro->src_addr : NULL, size); if (err < 0) @@ -427,6 +437,20 @@ static int dgram_getsockopt(struct sock *sk, int level, int optname, case WPAN_WANTACK: val = ro->want_ack; break; + case WPAN_SECURITY: + if (!ro->secen_override) + val = WPAN_SECURITY_DEFAULT; + else if (ro->secen) + val = WPAN_SECURITY_ON; + else + val = WPAN_SECURITY_OFF; + break; + case WPAN_SECURITY_LEVEL: + if (!ro->seclevel_override) + val = WPAN_SECURITY_LEVEL_DEFAULT; + else + val = ro->seclevel; + break; default: return -ENOPROTOOPT; } @@ -442,6 +466,7 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen) { struct dgram_sock *ro = dgram_sk(sk); + struct net *net = sock_net(sk); int val; int err = 0; @@ -457,6 +482,47 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname, case WPAN_WANTACK: ro->want_ack = !!val; break; + case WPAN_SECURITY: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && + !ns_capable(net->user_ns, CAP_NET_RAW)) { + err = -EPERM; + break; + } + + switch (val) { + case WPAN_SECURITY_DEFAULT: + ro->secen_override = 0; + break; + case WPAN_SECURITY_ON: + ro->secen_override = 1; + ro->secen = 1; + break; + case WPAN_SECURITY_OFF: + ro->secen_override = 1; + ro->secen = 0; + break; + default: + err = -EINVAL; + break; + } + break; + case WPAN_SECURITY_LEVEL: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN) && + !ns_capable(net->user_ns, CAP_NET_RAW)) { + err = -EPERM; + break; + } + + if (val < WPAN_SECURITY_LEVEL_DEFAULT || + val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) { + err = -EINVAL; + } else if (val == WPAN_SECURITY_LEVEL_DEFAULT) { + ro->seclevel_override = 0; + } else { + ro->seclevel_override = 1; + ro->seclevel = val; + } + break; default: err = -ENOPROTOOPT; break; -- cgit v1.2.3 From 29e023746a672e4ff702ca9dc63a06145fd8f4b0 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 16 May 2014 17:46:42 +0200 Subject: mac802154: add llsec configuration functions Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 36 ++++++++ net/mac802154/mac802154.h | 33 +++++++ net/mac802154/mac_cmd.c | 18 ++++ net/mac802154/mib.c | 187 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+) (limited to 'include') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 000c8552d5de..eb9f850a51b6 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -358,6 +358,40 @@ enum { IEEE802154_LLSEC_PARAM_COORD_SHORTADDR = 1 << 8, }; +struct ieee802154_llsec_ops { + int (*get_params)(struct net_device *dev, + struct ieee802154_llsec_params *params); + int (*set_params)(struct net_device *dev, + const struct ieee802154_llsec_params *params, + int changed); + + int (*add_key)(struct net_device *dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key); + int (*del_key)(struct net_device *dev, + const struct ieee802154_llsec_key_id *id); + + int (*add_dev)(struct net_device *dev, + const struct ieee802154_llsec_device *llsec_dev); + int (*del_dev)(struct net_device *dev, __le64 dev_addr); + + int (*add_devkey)(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key); + int (*del_devkey)(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key); + + int (*add_seclevel)(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl); + int (*del_seclevel)(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl); + + void (*lock_table)(struct net_device *dev); + void (*get_table)(struct net_device *dev, + struct ieee802154_llsec_table **t); + void (*unlock_table)(struct net_device *dev); +}; /* * This should be located at net_device->ml_priv * @@ -388,6 +422,8 @@ struct ieee802154_mlme_ops { void (*get_mac_params)(struct net_device *dev, struct ieee802154_mac_params *params); + struct ieee802154_llsec_ops *llsec; + /* The fields below are required. */ struct wpan_phy *(*get_phy)(const struct net_device *dev); diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h index a8d7cbc701a0..762a6f849c6b 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/mac802154.h @@ -136,4 +136,37 @@ int mac802154_set_mac_params(struct net_device *dev, void mac802154_get_mac_params(struct net_device *dev, struct ieee802154_mac_params *params); +int mac802154_get_params(struct net_device *dev, + struct ieee802154_llsec_params *params); +int mac802154_set_params(struct net_device *dev, + const struct ieee802154_llsec_params *params, + int changed); + +int mac802154_add_key(struct net_device *dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key); +int mac802154_del_key(struct net_device *dev, + const struct ieee802154_llsec_key_id *id); + +int mac802154_add_dev(struct net_device *dev, + const struct ieee802154_llsec_device *llsec_dev); +int mac802154_del_dev(struct net_device *dev, __le64 dev_addr); + +int mac802154_add_devkey(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key); +int mac802154_del_devkey(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key); + +int mac802154_add_seclevel(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl); +int mac802154_del_seclevel(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl); + +void mac802154_lock_table(struct net_device *dev); +void mac802154_get_table(struct net_device *dev, + struct ieee802154_llsec_table **t); +void mac802154_unlock_table(struct net_device *dev); + #endif /* MAC802154_H */ diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index d40c0928bc62..afb4e2cbc00a 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -64,6 +64,22 @@ static struct wpan_phy *mac802154_get_phy(const struct net_device *dev) return to_phy(get_device(&priv->hw->phy->dev)); } +static struct ieee802154_llsec_ops mac802154_llsec_ops = { + .get_params = mac802154_get_params, + .set_params = mac802154_set_params, + .add_key = mac802154_add_key, + .del_key = mac802154_del_key, + .add_dev = mac802154_add_dev, + .del_dev = mac802154_del_dev, + .add_devkey = mac802154_add_devkey, + .del_devkey = mac802154_del_devkey, + .add_seclevel = mac802154_add_seclevel, + .del_seclevel = mac802154_del_seclevel, + .lock_table = mac802154_lock_table, + .get_table = mac802154_get_table, + .unlock_table = mac802154_unlock_table, +}; + struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = { .get_phy = mac802154_get_phy, }; @@ -75,6 +91,8 @@ struct ieee802154_mlme_ops mac802154_mlme_wpan = { .get_short_addr = mac802154_dev_get_short_addr, .get_dsn = mac802154_dev_get_dsn, + .llsec = &mac802154_llsec_ops, + .set_mac_params = mac802154_set_mac_params, .get_mac_params = mac802154_get_mac_params, }; diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c index f0991f2344d4..15aa2f2b03a7 100644 --- a/net/mac802154/mib.c +++ b/net/mac802154/mib.c @@ -213,3 +213,190 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan) } else mutex_unlock(&priv->hw->phy->pib_lock); } + + +int mac802154_get_params(struct net_device *dev, + struct ieee802154_llsec_params *params) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_get_params(&priv->sec, params); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_set_params(struct net_device *dev, + const struct ieee802154_llsec_params *params, + int changed) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_set_params(&priv->sec, params, changed); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +int mac802154_add_key(struct net_device *dev, + const struct ieee802154_llsec_key_id *id, + const struct ieee802154_llsec_key *key) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_key_add(&priv->sec, id, key); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_del_key(struct net_device *dev, + const struct ieee802154_llsec_key_id *id) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_key_del(&priv->sec, id); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +int mac802154_add_dev(struct net_device *dev, + const struct ieee802154_llsec_device *llsec_dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_dev_add(&priv->sec, llsec_dev); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_del_dev(struct net_device *dev, __le64 dev_addr) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_dev_del(&priv->sec, dev_addr); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +int mac802154_add_devkey(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_del_devkey(struct net_device *dev, + __le64 device_addr, + const struct ieee802154_llsec_device_key *key) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +int mac802154_add_seclevel(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_seclevel_add(&priv->sec, sl); + mutex_unlock(&priv->sec_mtx); + + return res; +} + +int mac802154_del_seclevel(struct net_device *dev, + const struct ieee802154_llsec_seclevel *sl) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int res; + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); + res = mac802154_llsec_seclevel_del(&priv->sec, sl); + mutex_unlock(&priv->sec_mtx); + + return res; +} + + +void mac802154_lock_table(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_lock(&priv->sec_mtx); +} + +void mac802154_get_table(struct net_device *dev, + struct ieee802154_llsec_table **t) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + *t = &priv->sec.table; +} + +void mac802154_unlock_table(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + mutex_unlock(&priv->sec_mtx); +} -- cgit v1.2.3 From 3e9c156e2c210ab67b12b1b692983a6b97c19d3f Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 16 May 2014 17:46:44 +0200 Subject: ieee802154: add netlink interfaces for llsec This patch adds user-visible interfaces for the llsec infrastructure. For the added methods, the only major difference between all add/remove implementation lies in how the specific object is parsed, and for dump requests, how objects are written into netlink messages. To save on boilerplate code, table dumps are routed through a helper function that handles netlink dump state, leaving the actual dumping code to care only about iterating over the table to be dumped and filling netlink messages. For add/remove methods, the boilerplate required to work is not quite as large, but still enough to also move into a local helper. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/linux/nl802154.h | 31 ++ net/ieee802154/ieee802154.h | 19 ++ net/ieee802154/netlink.c | 20 ++ net/ieee802154/nl-mac.c | 807 ++++++++++++++++++++++++++++++++++++++++++++ net/ieee802154/nl_policy.c | 16 + 5 files changed, 893 insertions(+) (limited to 'include') diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index c8d7f3965fff..20163b9a0eae 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -80,6 +80,22 @@ enum { IEEE802154_ATTR_FRAME_RETRIES, + IEEE802154_ATTR_LLSEC_ENABLED, + IEEE802154_ATTR_LLSEC_SECLEVEL, + IEEE802154_ATTR_LLSEC_KEY_MODE, + IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT, + IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED, + IEEE802154_ATTR_LLSEC_KEY_ID, + IEEE802154_ATTR_LLSEC_FRAME_COUNTER, + IEEE802154_ATTR_LLSEC_KEY_BYTES, + IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES, + IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, + IEEE802154_ATTR_LLSEC_FRAME_TYPE, + IEEE802154_ATTR_LLSEC_CMD_FRAME_ID, + IEEE802154_ATTR_LLSEC_SECLEVELS, + IEEE802154_ATTR_LLSEC_DEV_OVERRIDE, + IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, + __IEEE802154_ATTR_MAX, }; @@ -134,6 +150,21 @@ enum { IEEE802154_SET_MACPARAMS, + IEEE802154_LLSEC_GETPARAMS, + IEEE802154_LLSEC_SETPARAMS, + IEEE802154_LLSEC_LIST_KEY, + IEEE802154_LLSEC_ADD_KEY, + IEEE802154_LLSEC_DEL_KEY, + IEEE802154_LLSEC_LIST_DEV, + IEEE802154_LLSEC_ADD_DEV, + IEEE802154_LLSEC_DEL_DEV, + IEEE802154_LLSEC_LIST_DEVKEY, + IEEE802154_LLSEC_ADD_DEVKEY, + IEEE802154_LLSEC_DEL_DEVKEY, + IEEE802154_LLSEC_LIST_SECLEVEL, + IEEE802154_LLSEC_ADD_SECLEVEL, + IEEE802154_LLSEC_DEL_SECLEVEL, + __IEEE802154_CMD_MAX, }; diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index 6693a5cf01ce..8b83a231299e 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -68,4 +68,23 @@ int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info); int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb); int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_keys(struct sk_buff *skb, + struct netlink_callback *cb); +int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_devs(struct sk_buff *skb, + struct netlink_callback *cb); +int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_devkeys(struct sk_buff *skb, + struct netlink_callback *cb); +int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info); +int ieee802154_llsec_dump_seclevels(struct sk_buff *skb, + struct netlink_callback *cb); + #endif diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 04b20589d97a..26efcf4fd2ff 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -124,6 +124,26 @@ static const struct genl_ops ieee8021154_ops[] = { IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, ieee802154_dump_iface), IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams), + IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams), + IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams), + IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL, + ieee802154_llsec_dump_keys), + IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key), + IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key), + IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL, + ieee802154_llsec_dump_devs), + IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev), + IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev), + IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL, + ieee802154_llsec_dump_devkeys), + IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey), + IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey), + IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL, + ieee802154_llsec_dump_seclevels), + IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL, + ieee802154_llsec_add_seclevel), + IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL, + ieee802154_llsec_del_seclevel), }; static const struct genl_multicast_group ieee802154_mcgrps[] = { diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index 5d285498c0f6..5617b4c6d6d5 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -715,3 +715,810 @@ out: dev_put(dev); return rc; } + + + +static int +ieee802154_llsec_parse_key_id(struct genl_info *info, + struct ieee802154_llsec_key_id *desc) +{ + memset(desc, 0, sizeof(*desc)); + + if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) + return -EINVAL; + + desc->mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]); + + if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) { + if (!info->attrs[IEEE802154_ATTR_PAN_ID] && + !(info->attrs[IEEE802154_ATTR_SHORT_ADDR] || + info->attrs[IEEE802154_ATTR_HW_ADDR])) + return -EINVAL; + + desc->device_addr.pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]); + + if (info->attrs[IEEE802154_ATTR_SHORT_ADDR]) { + desc->device_addr.mode = IEEE802154_ADDR_SHORT; + desc->device_addr.short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]); + } else { + desc->device_addr.mode = IEEE802154_ADDR_LONG; + desc->device_addr.extended_addr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + } + } + + if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]) + return -EINVAL; + + if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]) + return -EINVAL; + + if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]) + return -EINVAL; + + if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT) + desc->id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]); + + switch (desc->mode) { + case IEEE802154_SCF_KEY_SHORT_INDEX: + { + u32 source = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]); + desc->short_source = cpu_to_le32(source); + break; + } + case IEEE802154_SCF_KEY_HW_INDEX: + desc->extended_source = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]); + break; + } + + return 0; +} + +static int +ieee802154_llsec_fill_key_id(struct sk_buff *msg, + const struct ieee802154_llsec_key_id *desc) +{ + if (nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_MODE, desc->mode)) + return -EMSGSIZE; + + if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) { + if (nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, + desc->device_addr.pan_id)) + return -EMSGSIZE; + + if (desc->device_addr.mode == IEEE802154_ADDR_SHORT && + nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, + desc->device_addr.short_addr)) + return -EMSGSIZE; + + if (desc->device_addr.mode == IEEE802154_ADDR_LONG && + nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, + desc->device_addr.extended_addr)) + return -EMSGSIZE; + } + + if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT && + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_ID, desc->id)) + return -EMSGSIZE; + + if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX && + nla_put_u32(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT, + le32_to_cpu(desc->short_source))) + return -EMSGSIZE; + + if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX && + nla_put_hwaddr(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED, + desc->extended_source)) + return -EMSGSIZE; + + return 0; +} + +int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + struct net_device *dev = NULL; + int rc = -ENOBUFS; + struct ieee802154_mlme_ops *ops; + void *hdr; + struct ieee802154_llsec_params params; + + pr_debug("%s\n", __func__); + + dev = ieee802154_nl_get_dev(info); + if (!dev) + return -ENODEV; + + ops = ieee802154_mlme_ops(dev); + if (!ops->llsec) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + goto out_dev; + + hdr = genlmsg_put(msg, 0, info->snd_seq, &nl802154_family, 0, + IEEE802154_LLSEC_GETPARAMS); + if (!hdr) + goto out_free; + + rc = ops->llsec->get_params(dev, ¶ms); + if (rc < 0) + goto out_free; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_ENABLED, params.enabled) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVEL, params.out_level) || + nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, + be32_to_cpu(params.frame_counter)) || + ieee802154_llsec_fill_key_id(msg, ¶ms.out_key)) + goto out_free; + + dev_put(dev); + + return ieee802154_nl_reply(msg, info); +out_free: + nlmsg_free(msg); +out_dev: + dev_put(dev); + return rc; +} + +int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info) +{ + struct net_device *dev = NULL; + int rc = -EINVAL; + struct ieee802154_mlme_ops *ops; + struct ieee802154_llsec_params params; + int changed = 0; + + pr_debug("%s\n", __func__); + + dev = ieee802154_nl_get_dev(info); + if (!dev) + return -ENODEV; + + if (!info->attrs[IEEE802154_ATTR_LLSEC_ENABLED] && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE] && + !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) + goto out; + + ops = ieee802154_mlme_ops(dev); + if (!ops->llsec) { + rc = -EOPNOTSUPP; + goto out; + } + + if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL] && + nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) > 7) + goto out; + + if (info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]) { + params.enabled = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]); + changed |= IEEE802154_LLSEC_PARAM_ENABLED; + } + + if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) { + if (ieee802154_llsec_parse_key_id(info, ¶ms.out_key)) + goto out; + + changed |= IEEE802154_LLSEC_PARAM_OUT_KEY; + } + + if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) { + params.out_level = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]); + changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL; + } + + if (info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]) { + u32 fc = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); + + params.frame_counter = cpu_to_be32(fc); + changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER; + } + + rc = ops->llsec->set_params(dev, ¶ms, changed); + + dev_put(dev); + + return rc; +out: + dev_put(dev); + return rc; +} + + + +struct llsec_dump_data { + struct sk_buff *skb; + int s_idx, s_idx2; + int portid; + int nlmsg_seq; + struct net_device *dev; + struct ieee802154_mlme_ops *ops; + struct ieee802154_llsec_table *table; +}; + +static int +ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb, + int (*step)(struct llsec_dump_data*)) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev; + struct llsec_dump_data data; + int idx = 0; + int first_dev = cb->args[0]; + int rc; + + for_each_netdev(net, dev) { + if (idx < first_dev || dev->type != ARPHRD_IEEE802154) + goto skip; + + data.ops = ieee802154_mlme_ops(dev); + if (!data.ops->llsec) + goto skip; + + data.skb = skb; + data.s_idx = cb->args[1]; + data.s_idx2 = cb->args[2]; + data.dev = dev; + data.portid = NETLINK_CB(cb->skb).portid; + data.nlmsg_seq = cb->nlh->nlmsg_seq; + + data.ops->llsec->lock_table(dev); + data.ops->llsec->get_table(data.dev, &data.table); + rc = step(&data); + data.ops->llsec->unlock_table(dev); + + if (rc < 0) + break; + +skip: + idx++; + } + cb->args[0] = idx; + + return skb->len; +} + +static int +ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info, + int (*fn)(struct net_device*, struct genl_info*)) +{ + struct net_device *dev = NULL; + int rc = -EINVAL; + + dev = ieee802154_nl_get_dev(info); + if (!dev) + return -ENODEV; + + if (!ieee802154_mlme_ops(dev)->llsec) + rc = -EOPNOTSUPP; + else + rc = fn(dev, info); + + dev_put(dev); + return rc; +} + + + +static int +ieee802154_llsec_parse_key(struct genl_info *info, + struct ieee802154_llsec_key *key) +{ + u8 frames; + u32 commands[256 / 32]; + + memset(key, 0, sizeof(*key)); + + if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] || + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES]) + return -EINVAL; + + frames = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES]); + if ((frames & BIT(IEEE802154_FC_TYPE_MAC_CMD)) && + !info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) + return -EINVAL; + + if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) { + nla_memcpy(commands, + info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS], + 256 / 8); + + if (commands[0] || commands[1] || commands[2] || commands[3] || + commands[4] || commands[5] || commands[6] || + commands[7] >= BIT(IEEE802154_CMD_GTS_REQ + 1)) + return -EINVAL; + + key->cmd_frame_ids = commands[7]; + } + + key->frame_types = frames; + + nla_memcpy(key->key, info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES], + IEEE802154_LLSEC_KEY_SIZE); + + return 0; +} + +static int llsec_add_key(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_key key; + struct ieee802154_llsec_key_id id; + + if (ieee802154_llsec_parse_key(info, &key) || + ieee802154_llsec_parse_key_id(info, &id)) + return -EINVAL; + + return ops->llsec->add_key(dev, &id, &key); +} + +int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info) +{ + if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != + (NLM_F_CREATE | NLM_F_EXCL)) + return -EINVAL; + + return ieee802154_nl_llsec_change(skb, info, llsec_add_key); +} + +static int llsec_remove_key(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_key_id id; + + if (ieee802154_llsec_parse_key_id(info, &id)) + return -EINVAL; + + return ops->llsec->del_key(dev, &id); +} + +int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info) +{ + return ieee802154_nl_llsec_change(skb, info, llsec_remove_key); +} + +static int +ieee802154_nl_fill_key(struct sk_buff *msg, u32 portid, u32 seq, + const struct ieee802154_llsec_key_entry *key, + const struct net_device *dev) +{ + void *hdr; + u32 commands[256 / 32]; + + hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, + IEEE802154_LLSEC_LIST_KEY); + if (!hdr) + goto out; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + ieee802154_llsec_fill_key_id(msg, &key->id) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES, + key->key->frame_types)) + goto nla_put_failure; + + if (key->key->frame_types & BIT(IEEE802154_FC_TYPE_MAC_CMD)) { + memset(commands, 0, sizeof(commands)); + commands[7] = key->key->cmd_frame_ids; + if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, + sizeof(commands), commands)) + goto nla_put_failure; + } + + if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_BYTES, + IEEE802154_LLSEC_KEY_SIZE, key->key->key)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + +static int llsec_iter_keys(struct llsec_dump_data *data) +{ + struct ieee802154_llsec_key_entry *pos; + int rc = 0, idx = 0; + + list_for_each_entry(pos, &data->table->keys, list) { + if (idx++ < data->s_idx) + continue; + + if (ieee802154_nl_fill_key(data->skb, data->portid, + data->nlmsg_seq, pos, data->dev)) { + rc = -EMSGSIZE; + break; + } + + data->s_idx++; + } + + return rc; +} + +int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb) +{ + return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys); +} + + + +static int +llsec_parse_dev(struct genl_info *info, + struct ieee802154_llsec_device *dev) +{ + memset(dev, 0, sizeof(*dev)); + + if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] || + !info->attrs[IEEE802154_ATTR_HW_ADDR] || + !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] || + !info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] || + (!!info->attrs[IEEE802154_ATTR_PAN_ID] != + !!info->attrs[IEEE802154_ATTR_SHORT_ADDR])) + return -EINVAL; + + if (info->attrs[IEEE802154_ATTR_PAN_ID]) { + dev->pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]); + dev->short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]); + } else { + dev->short_addr = cpu_to_le16(IEEE802154_ADDR_UNDEF); + } + + dev->hwaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + dev->frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); + dev->seclevel_exempt = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]); + dev->key_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE]); + + if (dev->key_mode >= __IEEE802154_LLSEC_DEVKEY_MAX) + return -EINVAL; + + return 0; +} + +static int llsec_add_dev(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_device desc; + + if (llsec_parse_dev(info, &desc)) + return -EINVAL; + + return ops->llsec->add_dev(dev, &desc); +} + +int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info) +{ + if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != + (NLM_F_CREATE | NLM_F_EXCL)) + return -EINVAL; + + return ieee802154_nl_llsec_change(skb, info, llsec_add_dev); +} + +static int llsec_del_dev(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + __le64 devaddr; + + if (!info->attrs[IEEE802154_ATTR_HW_ADDR]) + return -EINVAL; + + devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + + return ops->llsec->del_dev(dev, devaddr); +} + +int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info) +{ + return ieee802154_nl_llsec_change(skb, info, llsec_del_dev); +} + +static int +ieee802154_nl_fill_dev(struct sk_buff *msg, u32 portid, u32 seq, + const struct ieee802154_llsec_device *desc, + const struct net_device *dev) +{ + void *hdr; + + hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, + IEEE802154_LLSEC_LIST_DEV); + if (!hdr) + goto out; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, desc->pan_id) || + nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, + desc->short_addr) || + nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, desc->hwaddr) || + nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, + desc->frame_counter) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE, + desc->seclevel_exempt) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, desc->key_mode)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + +static int llsec_iter_devs(struct llsec_dump_data *data) +{ + struct ieee802154_llsec_device *pos; + int rc = 0, idx = 0; + + list_for_each_entry(pos, &data->table->devices, list) { + if (idx++ < data->s_idx) + continue; + + if (ieee802154_nl_fill_dev(data->skb, data->portid, + data->nlmsg_seq, pos, data->dev)) { + rc = -EMSGSIZE; + break; + } + + data->s_idx++; + } + + return rc; +} + +int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb) +{ + return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs); +} + + + +static int llsec_add_devkey(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_device_key key; + __le64 devaddr; + + if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] || + !info->attrs[IEEE802154_ATTR_HW_ADDR] || + ieee802154_llsec_parse_key_id(info, &key.key_id)) + return -EINVAL; + + devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + key.frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]); + + return ops->llsec->add_devkey(dev, devaddr, &key); +} + +int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info) +{ + if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != + (NLM_F_CREATE | NLM_F_EXCL)) + return -EINVAL; + + return ieee802154_nl_llsec_change(skb, info, llsec_add_devkey); +} + +static int llsec_del_devkey(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_device_key key; + __le64 devaddr; + + if (!info->attrs[IEEE802154_ATTR_HW_ADDR] || + ieee802154_llsec_parse_key_id(info, &key.key_id)) + return -EINVAL; + + devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]); + + return ops->llsec->del_devkey(dev, devaddr, &key); +} + +int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info) +{ + return ieee802154_nl_llsec_change(skb, info, llsec_del_devkey); +} + +static int +ieee802154_nl_fill_devkey(struct sk_buff *msg, u32 portid, u32 seq, + __le64 devaddr, + const struct ieee802154_llsec_device_key *devkey, + const struct net_device *dev) +{ + void *hdr; + + hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, + IEEE802154_LLSEC_LIST_DEVKEY); + if (!hdr) + goto out; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, devaddr) || + nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER, + devkey->frame_counter) || + ieee802154_llsec_fill_key_id(msg, &devkey->key_id)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + +static int llsec_iter_devkeys(struct llsec_dump_data *data) +{ + struct ieee802154_llsec_device *dpos; + struct ieee802154_llsec_device_key *kpos; + int rc = 0, idx = 0, idx2; + + list_for_each_entry(dpos, &data->table->devices, list) { + if (idx++ < data->s_idx) + continue; + + idx2 = 0; + + list_for_each_entry(kpos, &dpos->keys, list) { + if (idx2++ < data->s_idx2) + continue; + + if (ieee802154_nl_fill_devkey(data->skb, data->portid, + data->nlmsg_seq, + dpos->hwaddr, kpos, + data->dev)) { + return rc = -EMSGSIZE; + } + + data->s_idx2++; + } + + data->s_idx++; + } + + return rc; +} + +int ieee802154_llsec_dump_devkeys(struct sk_buff *skb, + struct netlink_callback *cb) +{ + return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys); +} + + + +static int +llsec_parse_seclevel(struct genl_info *info, + struct ieee802154_llsec_seclevel *sl) +{ + memset(sl, 0, sizeof(*sl)); + + if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE] || + !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS] || + !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]) + return -EINVAL; + + sl->frame_type = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE]); + if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD) { + if (!info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]) + return -EINVAL; + + sl->cmd_frame_id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]); + } + + sl->sec_levels = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS]); + sl->device_override = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]); + + return 0; +} + +static int llsec_add_seclevel(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_seclevel sl; + + if (llsec_parse_seclevel(info, &sl)) + return -EINVAL; + + return ops->llsec->add_seclevel(dev, &sl); +} + +int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info) +{ + if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) != + (NLM_F_CREATE | NLM_F_EXCL)) + return -EINVAL; + + return ieee802154_nl_llsec_change(skb, info, llsec_add_seclevel); +} + +static int llsec_del_seclevel(struct net_device *dev, struct genl_info *info) +{ + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct ieee802154_llsec_seclevel sl; + + if (llsec_parse_seclevel(info, &sl)) + return -EINVAL; + + return ops->llsec->del_seclevel(dev, &sl); +} + +int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info) +{ + return ieee802154_nl_llsec_change(skb, info, llsec_del_seclevel); +} + +static int +ieee802154_nl_fill_seclevel(struct sk_buff *msg, u32 portid, u32 seq, + const struct ieee802154_llsec_seclevel *sl, + const struct net_device *dev) +{ + void *hdr; + + hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI, + IEEE802154_LLSEC_LIST_SECLEVEL); + if (!hdr) + goto out; + + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || + nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_FRAME_TYPE, sl->frame_type) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVELS, sl->sec_levels) || + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE, + sl->device_override)) + goto nla_put_failure; + + if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD && + nla_put_u8(msg, IEEE802154_ATTR_LLSEC_CMD_FRAME_ID, + sl->cmd_frame_id)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +out: + return -EMSGSIZE; +} + +static int llsec_iter_seclevels(struct llsec_dump_data *data) +{ + struct ieee802154_llsec_seclevel *pos; + int rc = 0, idx = 0; + + list_for_each_entry(pos, &data->table->security_levels, list) { + if (idx++ < data->s_idx) + continue; + + if (ieee802154_nl_fill_seclevel(data->skb, data->portid, + data->nlmsg_seq, pos, + data->dev)) { + rc = -EMSGSIZE; + break; + } + + data->s_idx++; + } + + return rc; +} + +int ieee802154_llsec_dump_seclevels(struct sk_buff *skb, + struct netlink_callback *cb) +{ + return ieee802154_llsec_dump_table(skb, cb, llsec_iter_seclevels); +} diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index fd7be5e45cef..3a703ab88348 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -62,5 +62,21 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_CSMA_MAX_BE] = { .type = NLA_U8, }, [IEEE802154_ATTR_FRAME_RETRIES] = { .type = NLA_S8, }, + + [IEEE802154_ATTR_LLSEC_ENABLED] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_SECLEVEL] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_KEY_MODE] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT] = { .type = NLA_U32, }, + [IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED] = { .type = NLA_HW_ADDR, }, + [IEEE802154_ATTR_LLSEC_KEY_ID] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_FRAME_COUNTER] = { .type = NLA_U32 }, + [IEEE802154_ATTR_LLSEC_KEY_BYTES] = { .len = 16, }, + [IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS] = { .len = 258 / 8 }, + [IEEE802154_ATTR_LLSEC_FRAME_TYPE] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_CMD_FRAME_ID] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_SECLEVELS] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] = { .type = NLA_U8, }, + [IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] = { .type = NLA_U8, }, }; -- cgit v1.2.3 From f0f77dc6be76ed1854b08688390e156e4b351ab5 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 16 May 2014 17:46:45 +0200 Subject: ieee802154, mac802154: implement devkey record option The 802.15.4-2011 standard states that for each key, a list of devices that use this key shall be kept. Previous patches have only considered two options: * a device "uses" (or may use) all keys, rendering the list useless * a device is restricted to a certain set of keys Another option would be that a device *may* use all keys, but need not do so, and we are interested in the actual set of keys the device uses. Recording keys used by any given device may have a noticable performance impact and might not be needed as often. The common case, in which a device will not switch keys too often, should still perform well. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 1 + net/mac802154/llsec.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index eb9f850a51b6..3b53c8e405e4 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -280,6 +280,7 @@ struct ieee802154_llsec_device_key { enum { IEEE802154_LLSEC_DEVKEY_IGNORE, IEEE802154_LLSEC_DEVKEY_RESTRICT, + IEEE802154_LLSEC_DEVKEY_RECORD, __IEEE802154_LLSEC_DEVKEY_MAX, }; diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 392653b1b5a3..a83674edaafd 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -920,6 +920,37 @@ llsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec, return llsec_do_decrypt_auth(skb, sec, hdr, key, dev_addr); } +static int +llsec_update_devkey_record(struct mac802154_llsec_device *dev, + const struct ieee802154_llsec_key_id *in_key) +{ + struct mac802154_llsec_device_key *devkey; + + devkey = llsec_devkey_find(dev, in_key); + + if (!devkey) { + struct mac802154_llsec_device_key *next; + + next = kzalloc(sizeof(*devkey), GFP_ATOMIC); + if (!next) + return -ENOMEM; + + next->devkey.key_id = *in_key; + + spin_lock_bh(&dev->lock); + + devkey = llsec_devkey_find(dev, in_key); + if (!devkey) + list_add_rcu(&next->devkey.list, &dev->dev.keys); + else + kfree(next); + + spin_unlock_bh(&dev->lock); + } + + return 0; +} + static int llsec_update_devkey_info(struct mac802154_llsec_device *dev, const struct ieee802154_llsec_key_id *in_key, @@ -933,6 +964,13 @@ llsec_update_devkey_info(struct mac802154_llsec_device *dev, return -ENOENT; } + if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RECORD) { + int rc = llsec_update_devkey_record(dev, in_key); + + if (rc < 0) + return rc; + } + spin_lock_bh(&dev->lock); if ((!devkey && frame_counter < dev->dev.frame_counter) || -- cgit v1.2.3 From 4085ebe8c31face855fd01ee40372cb4aab1df3a Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 16 May 2014 17:04:53 -0400 Subject: net: Find the nesting level of a given device by type. Multiple devices in the kernel can be stacked/nested and they need to know their nesting level for the purposes of lockdep. This patch provides a generic function that determines a nesting level of a particular device by its type (ex: vlan, macvlan, etc). We only care about nesting of the same type of devices. For example: eth0 <- vlan0.10 <- macvlan0 <- vlan1.20 The nesting level of vlan1.20 would be 1, since there is another vlan in the stack under it. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/linux/netdevice.h | 10 ++++++++++ net/core/dev.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 20e99efb1ca6..fb912e8e5c7f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3077,6 +3077,14 @@ void *netdev_lower_get_next_private_rcu(struct net_device *dev, priv; \ priv = netdev_lower_get_next_private_rcu(dev, &(iter))) +void *netdev_lower_get_next(struct net_device *dev, + struct list_head **iter); +#define netdev_for_each_lower_dev(dev, ldev, iter) \ + for (iter = &(dev)->adj_list.lower, \ + ldev = netdev_lower_get_next(dev, &(iter)); \ + ldev; \ + ldev = netdev_lower_get_next(dev, &(iter))) + void *netdev_adjacent_get_private(struct list_head *adj_list); void *netdev_lower_get_first_private_rcu(struct net_device *dev); struct net_device *netdev_master_upper_dev_get(struct net_device *dev); @@ -3092,6 +3100,8 @@ void netdev_upper_dev_unlink(struct net_device *dev, void netdev_adjacent_rename_links(struct net_device *dev, char *oldname); void *netdev_lower_dev_get_private(struct net_device *dev, struct net_device *lower_dev); +int dev_get_nest_level(struct net_device *dev, + bool (*type_check)(struct net_device *dev)); int skb_checksum_help(struct sk_buff *skb); struct sk_buff *__skb_gso_segment(struct sk_buff *skb, netdev_features_t features, bool tx_path); diff --git a/net/core/dev.c b/net/core/dev.c index ed928e846559..6ee3ac25ed72 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4622,6 +4622,32 @@ void *netdev_lower_get_next_private_rcu(struct net_device *dev, } EXPORT_SYMBOL(netdev_lower_get_next_private_rcu); +/** + * netdev_lower_get_next - Get the next device from the lower neighbour + * list + * @dev: device + * @iter: list_head ** of the current position + * + * Gets the next netdev_adjacent from the dev's lower neighbour + * list, starting from iter position. The caller must hold RTNL lock or + * its own locking that guarantees that the neighbour lower + * list will remain unchainged. + */ +void *netdev_lower_get_next(struct net_device *dev, struct list_head **iter) +{ + struct netdev_adjacent *lower; + + lower = list_entry((*iter)->next, struct netdev_adjacent, list); + + if (&lower->list == &dev->adj_list.lower) + return NULL; + + *iter = &lower->list; + + return lower->dev; +} +EXPORT_SYMBOL(netdev_lower_get_next); + /** * netdev_lower_get_first_private_rcu - Get the first ->private from the * lower neighbour list, RCU @@ -5072,6 +5098,30 @@ void *netdev_lower_dev_get_private(struct net_device *dev, } EXPORT_SYMBOL(netdev_lower_dev_get_private); + +int dev_get_nest_level(struct net_device *dev, + bool (*type_check)(struct net_device *dev)) +{ + struct net_device *lower = NULL; + struct list_head *iter; + int max_nest = -1; + int nest; + + ASSERT_RTNL(); + + netdev_for_each_lower_dev(dev, lower, iter) { + nest = dev_get_nest_level(lower, type_check); + if (max_nest < nest) + max_nest = nest; + } + + if (type_check(dev)) + max_nest++; + + return max_nest; +} +EXPORT_SYMBOL(dev_get_nest_level); + static void dev_change_rx_flags(struct net_device *dev, int flags) { const struct net_device_ops *ops = dev->netdev_ops; -- cgit v1.2.3 From 25175ba5c9bff9aaf0229df34bb5d54c81633ec3 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 16 May 2014 17:04:54 -0400 Subject: net: Allow for more then a single subclass for netif_addr_lock Currently netif_addr_lock_nested assumes that there can be only a single nesting level between 2 devices. However, if we have multiple devices of the same type stacked, this fails. For example: eth0 <-- vlan0.10 <-- vlan0.10.20 A more complicated configuration may stack more then one type of device in different order. Ex: eth0 <-- vlan0.10 <-- macvlan0 <-- vlan1.10.20 <-- macvlan1 This patch adds an ndo_* function that allows each stackable device to report its nesting level. If the device doesn't provide this function default subclass of 1 is used. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/linux/netdevice.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fb912e8e5c7f..9d4b1f1b6b75 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1144,6 +1144,7 @@ struct net_device_ops { netdev_tx_t (*ndo_dfwd_start_xmit) (struct sk_buff *skb, struct net_device *dev, void *priv); + int (*ndo_get_lock_subclass)(struct net_device *dev); }; /** @@ -2950,7 +2951,12 @@ static inline void netif_addr_lock(struct net_device *dev) static inline void netif_addr_lock_nested(struct net_device *dev) { - spin_lock_nested(&dev->addr_list_lock, SINGLE_DEPTH_NESTING); + int subclass = SINGLE_DEPTH_NESTING; + + if (dev->netdev_ops->ndo_get_lock_subclass) + subclass = dev->netdev_ops->ndo_get_lock_subclass(dev); + + spin_lock_nested(&dev->addr_list_lock, subclass); } static inline void netif_addr_lock_bh(struct net_device *dev) -- cgit v1.2.3 From d38569ab2bba6e6b3233acfc3a84cdbcfbd1f79f Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 16 May 2014 17:04:55 -0400 Subject: vlan: Fix lockdep warning with stacked vlan devices. This reverts commit dc8eaaa006350d24030502a4521542e74b5cb39f. vlan: Fix lockdep warning when vlan dev handle notification Instead we use the new new API to find the lock subclass of our vlan device. This way we can support configurations where vlans are interspersed with other devices: bond -> vlan -> macvlan -> vlan Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 3 ++- net/8021q/vlan.c | 1 + net/8021q/vlan_dev.c | 52 +++++++++---------------------------------------- net/core/dev.c | 1 - 4 files changed, 12 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 13bbbde00e68..724bde8477b2 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -73,7 +73,7 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb) /* found in socket.c */ extern void vlan_ioctl_set(int (*hook)(struct net *, void __user *)); -static inline int is_vlan_dev(struct net_device *dev) +static inline bool is_vlan_dev(struct net_device *dev) { return dev->priv_flags & IFF_802_1Q_VLAN; } @@ -159,6 +159,7 @@ struct vlan_dev_priv { #ifdef CONFIG_NET_POLL_CONTROLLER struct netpoll *netpoll; #endif + unsigned int nest_level; }; static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev) diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 175273f38cb1..44ebd5c2cd4a 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -169,6 +169,7 @@ int register_vlan_dev(struct net_device *dev) if (err < 0) goto out_uninit_mvrp; + vlan->nest_level = dev_get_nest_level(real_dev, is_vlan_dev) + 1; err = register_netdevice(dev); if (err < 0) goto out_uninit_mvrp; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 733ec283ed1b..019efb79708f 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -493,48 +493,10 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change) } } -static int vlan_calculate_locking_subclass(struct net_device *real_dev) -{ - int subclass = 0; - - while (is_vlan_dev(real_dev)) { - subclass++; - real_dev = vlan_dev_priv(real_dev)->real_dev; - } - - return subclass; -} - -static void vlan_dev_mc_sync(struct net_device *to, struct net_device *from) -{ - int err = 0, subclass; - - subclass = vlan_calculate_locking_subclass(to); - - spin_lock_nested(&to->addr_list_lock, subclass); - err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len); - if (!err) - __dev_set_rx_mode(to); - spin_unlock(&to->addr_list_lock); -} - -static void vlan_dev_uc_sync(struct net_device *to, struct net_device *from) -{ - int err = 0, subclass; - - subclass = vlan_calculate_locking_subclass(to); - - spin_lock_nested(&to->addr_list_lock, subclass); - err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len); - if (!err) - __dev_set_rx_mode(to); - spin_unlock(&to->addr_list_lock); -} - static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) { - vlan_dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); - vlan_dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); + dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); + dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); } /* @@ -562,6 +524,11 @@ static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass) netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, &subclass); } +static int vlan_dev_get_lock_subclass(struct net_device *dev) +{ + return vlan_dev_priv(dev)->nest_level; +} + static const struct header_ops vlan_header_ops = { .create = vlan_dev_hard_header, .rebuild = vlan_dev_rebuild_header, @@ -597,7 +564,6 @@ static const struct net_device_ops vlan_netdev_ops; static int vlan_dev_init(struct net_device *dev) { struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; - int subclass = 0; netif_carrier_off(dev); @@ -646,8 +612,7 @@ static int vlan_dev_init(struct net_device *dev) SET_NETDEV_DEVTYPE(dev, &vlan_type); - subclass = vlan_calculate_locking_subclass(dev); - vlan_dev_set_lockdep_class(dev, subclass); + vlan_dev_set_lockdep_class(dev, vlan_dev_get_lock_subclass(dev)); vlan_dev_priv(dev)->vlan_pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats); if (!vlan_dev_priv(dev)->vlan_pcpu_stats) @@ -819,6 +784,7 @@ static const struct net_device_ops vlan_netdev_ops = { .ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup, #endif .ndo_fix_features = vlan_dev_fix_features, + .ndo_get_lock_subclass = vlan_dev_get_lock_subclass, }; void vlan_setup(struct net_device *dev) diff --git a/net/core/dev.c b/net/core/dev.c index 6ee3ac25ed72..2b872bfbd172 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5287,7 +5287,6 @@ void __dev_set_rx_mode(struct net_device *dev) if (ops->ndo_set_rx_mode) ops->ndo_set_rx_mode(dev); } -EXPORT_SYMBOL(__dev_set_rx_mode); void dev_set_rx_mode(struct net_device *dev) { -- cgit v1.2.3 From c674ac30c549596295eb0a5af7f4714c0b905b6f Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 16 May 2014 17:04:56 -0400 Subject: macvlan: Fix lockdep warnings with stacked macvlan devices Macvlan devices try to avoid stacking, but that's not always successfull or even desired. As an example, the following configuration is perefectly legal and valid: eth0 <--- macvlan0 <---- vlan0.10 <--- macvlan1 However, this configuration produces the following lockdep trace: [ 115.620418] ====================================================== [ 115.620477] [ INFO: possible circular locking dependency detected ] [ 115.620516] 3.15.0-rc1+ #24 Not tainted [ 115.620540] ------------------------------------------------------- [ 115.620577] ip/1704 is trying to acquire lock: [ 115.620604] (&vlan_netdev_addr_lock_key/1){+.....}, at: [] dev_uc_sync+0x3c/0x80 [ 115.620686] but task is already holding lock: [ 115.620723] (&macvlan_netdev_addr_lock_key){+.....}, at: [] dev_set_rx_mode+0x1e/0x40 [ 115.620795] which lock already depends on the new lock. [ 115.620853] the existing dependency chain (in reverse order) is: [ 115.620894] -> #1 (&macvlan_netdev_addr_lock_key){+.....}: [ 115.620935] [] lock_acquire+0xa2/0x130 [ 115.620974] [] _raw_spin_lock_nested+0x37/0x50 [ 115.621019] [] vlan_dev_set_rx_mode+0x53/0x110 [8021q] [ 115.621066] [] __dev_set_rx_mode+0x57/0xa0 [ 115.621105] [] dev_set_rx_mode+0x26/0x40 [ 115.621143] [] __dev_open+0xde/0x140 [ 115.621174] [] __dev_change_flags+0x9d/0x170 [ 115.621174] [] dev_change_flags+0x29/0x60 [ 115.621174] [] do_setlink+0x321/0x9a0 [ 115.621174] [] rtnl_newlink+0x51f/0x730 [ 115.621174] [] rtnetlink_rcv_msg+0x95/0x250 [ 115.621174] [] netlink_rcv_skb+0xa9/0xc0 [ 115.621174] [] rtnetlink_rcv+0x2a/0x40 [ 115.621174] [] netlink_unicast+0xf0/0x1c0 [ 115.621174] [] netlink_sendmsg+0x2ff/0x740 [ 115.621174] [] sock_sendmsg+0x8b/0xc0 [ 115.621174] [] ___sys_sendmsg+0x369/0x380 [ 115.621174] [] __sys_sendmsg+0x42/0x80 [ 115.621174] [] SyS_sendmsg+0x12/0x20 [ 115.621174] [] system_call_fastpath+0x16/0x1b [ 115.621174] -> #0 (&vlan_netdev_addr_lock_key/1){+.....}: [ 115.621174] [] __lock_acquire+0x1773/0x1a60 [ 115.621174] [] lock_acquire+0xa2/0x130 [ 115.621174] [] _raw_spin_lock_nested+0x37/0x50 [ 115.621174] [] dev_uc_sync+0x3c/0x80 [ 115.621174] [] macvlan_set_mac_lists+0xca/0x110 [macvlan] [ 115.621174] [] __dev_set_rx_mode+0x57/0xa0 [ 115.621174] [] dev_set_rx_mode+0x26/0x40 [ 115.621174] [] __dev_open+0xde/0x140 [ 115.621174] [] __dev_change_flags+0x9d/0x170 [ 115.621174] [] dev_change_flags+0x29/0x60 [ 115.621174] [] do_setlink+0x321/0x9a0 [ 115.621174] [] rtnl_newlink+0x51f/0x730 [ 115.621174] [] rtnetlink_rcv_msg+0x95/0x250 [ 115.621174] [] netlink_rcv_skb+0xa9/0xc0 [ 115.621174] [] rtnetlink_rcv+0x2a/0x40 [ 115.621174] [] netlink_unicast+0xf0/0x1c0 [ 115.621174] [] netlink_sendmsg+0x2ff/0x740 [ 115.621174] [] sock_sendmsg+0x8b/0xc0 [ 115.621174] [] ___sys_sendmsg+0x369/0x380 [ 115.621174] [] __sys_sendmsg+0x42/0x80 [ 115.621174] [] SyS_sendmsg+0x12/0x20 [ 115.621174] [] system_call_fastpath+0x16/0x1b [ 115.621174] other info that might help us debug this: [ 115.621174] Possible unsafe locking scenario: [ 115.621174] CPU0 CPU1 [ 115.621174] ---- ---- [ 115.621174] lock(&macvlan_netdev_addr_lock_key); [ 115.621174] lock(&vlan_netdev_addr_lock_key/1); [ 115.621174] lock(&macvlan_netdev_addr_lock_key); [ 115.621174] lock(&vlan_netdev_addr_lock_key/1); [ 115.621174] *** DEADLOCK *** [ 115.621174] 2 locks held by ip/1704: [ 115.621174] #0: (rtnl_mutex){+.+.+.}, at: [] rtnetlink_rcv+0x1b/0x40 [ 115.621174] #1: (&macvlan_netdev_addr_lock_key){+.....}, at: [] dev_set_rx_mode+0x1e/0x40 [ 115.621174] stack backtrace: [ 115.621174] CPU: 3 PID: 1704 Comm: ip Not tainted 3.15.0-rc1+ #24 [ 115.621174] Hardware name: Hewlett-Packard HP xw8400 Workstation/0A08h, BIOS 786D5 v02.38 10/25/2010 [ 115.621174] ffffffff82339ae0 ffff880465f79568 ffffffff816ee20c ffffffff82339ae0 [ 115.621174] ffff880465f795a8 ffffffff816e9e1b ffff880465f79600 ffff880465b019c8 [ 115.621174] 0000000000000001 0000000000000002 ffff880465b019c8 ffff880465b01230 [ 115.621174] Call Trace: [ 115.621174] [] dump_stack+0x4d/0x66 [ 115.621174] [] print_circular_bug+0x200/0x20e [ 115.621174] [] __lock_acquire+0x1773/0x1a60 [ 115.621174] [] ? trace_hardirqs_on_caller+0xb2/0x1d0 [ 115.621174] [] lock_acquire+0xa2/0x130 [ 115.621174] [] ? dev_uc_sync+0x3c/0x80 [ 115.621174] [] _raw_spin_lock_nested+0x37/0x50 [ 115.621174] [] ? dev_uc_sync+0x3c/0x80 [ 115.621174] [] dev_uc_sync+0x3c/0x80 [ 115.621174] [] macvlan_set_mac_lists+0xca/0x110 [macvlan] [ 115.621174] [] __dev_set_rx_mode+0x57/0xa0 [ 115.621174] [] dev_set_rx_mode+0x26/0x40 [ 115.621174] [] __dev_open+0xde/0x140 [ 115.621174] [] __dev_change_flags+0x9d/0x170 [ 115.621174] [] dev_change_flags+0x29/0x60 [ 115.621174] [] ? mem_cgroup_bad_page_check+0x21/0x30 [ 115.621174] [] do_setlink+0x321/0x9a0 [ 115.621174] [] ? __lock_acquire+0x37c/0x1a60 [ 115.621174] [] rtnl_newlink+0x51f/0x730 [ 115.621174] [] ? rtnl_newlink+0xe9/0x730 [ 115.621174] [] rtnetlink_rcv_msg+0x95/0x250 [ 115.621174] [] ? trace_hardirqs_on+0xd/0x10 [ 115.621174] [] ? rtnetlink_rcv+0x1b/0x40 [ 115.621174] [] ? rtnetlink_rcv+0x40/0x40 [ 115.621174] [] netlink_rcv_skb+0xa9/0xc0 [ 115.621174] [] rtnetlink_rcv+0x2a/0x40 [ 115.621174] [] netlink_unicast+0xf0/0x1c0 [ 115.621174] [] netlink_sendmsg+0x2ff/0x740 [ 115.621174] [] sock_sendmsg+0x8b/0xc0 [ 115.621174] [] ? might_fault+0x5f/0xb0 [ 115.621174] [] ? might_fault+0xa8/0xb0 [ 115.621174] [] ? might_fault+0x5f/0xb0 [ 115.621174] [] ? verify_iovec+0x5e/0xe0 [ 115.621174] [] ___sys_sendmsg+0x369/0x380 [ 115.621174] [] ? __do_page_fault+0x11d/0x570 [ 115.621174] [] ? up_read+0x1f/0x40 [ 115.621174] [] ? __do_page_fault+0x214/0x570 [ 115.621174] [] ? mntput_no_expire+0x6b/0x1c0 [ 115.621174] [] ? mntput_no_expire+0x17/0x1c0 [ 115.621174] [] ? mntput+0x24/0x40 [ 115.621174] [] __sys_sendmsg+0x42/0x80 [ 115.621174] [] SyS_sendmsg+0x12/0x20 [ 115.621174] [] system_call_fastpath+0x16/0x1b Fix this by correctly providing macvlan lockdep class. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 12 ++++++++++-- include/linux/if_macvlan.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index c5fb9cf95c12..d53e299ae1d9 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -517,6 +517,11 @@ static struct lock_class_key macvlan_netdev_addr_lock_key; #define MACVLAN_STATE_MASK \ ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT)) +static int macvlan_get_nest_level(struct net_device *dev) +{ + return ((struct macvlan_dev *)netdev_priv(dev))->nest_level; +} + static void macvlan_set_lockdep_class_one(struct net_device *dev, struct netdev_queue *txq, void *_unused) @@ -527,8 +532,9 @@ static void macvlan_set_lockdep_class_one(struct net_device *dev, static void macvlan_set_lockdep_class(struct net_device *dev) { - lockdep_set_class(&dev->addr_list_lock, - &macvlan_netdev_addr_lock_key); + lockdep_set_class_and_subclass(&dev->addr_list_lock, + &macvlan_netdev_addr_lock_key, + macvlan_get_nest_level(dev)); netdev_for_each_tx_queue(dev, macvlan_set_lockdep_class_one, NULL); } @@ -723,6 +729,7 @@ static const struct net_device_ops macvlan_netdev_ops = { .ndo_fdb_add = macvlan_fdb_add, .ndo_fdb_del = macvlan_fdb_del, .ndo_fdb_dump = ndo_dflt_fdb_dump, + .ndo_get_lock_subclass = macvlan_get_nest_level, }; void macvlan_common_setup(struct net_device *dev) @@ -851,6 +858,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, vlan->dev = dev; vlan->port = port; vlan->set_features = MACVLAN_FEATURES; + vlan->nest_level = dev_get_nest_level(lowerdev, netif_is_macvlan) + 1; vlan->mode = MACVLAN_MODE_VEPA; if (data && data[IFLA_MACVLAN_MODE]) diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 7c8b20b120ea..a9a53b12397b 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -56,6 +56,7 @@ struct macvlan_dev { int numqueues; netdev_features_t tap_features; int minor; + int nest_level; }; static inline void macvlan_count_rx(const struct macvlan_dev *vlan, -- cgit v1.2.3 From 44a4085538c844e79d6ee6bcf46fabf7c57a9a38 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 16 May 2014 17:20:38 -0400 Subject: bonding: Fix stacked device detection in arp monitoring Prior to commit fbd929f2dce460456807a51e18d623db3db9f077 bonding: support QinQ for bond arp interval the arp monitoring code allowed for proper detection of devices stacked on top of vlans. Since the above commit, the code can still detect a device stacked on top of single vlan, but not a device stacked on top of Q-in-Q configuration. The search will only set the inner vlan tag if the route device is the vlan device. However, this is not always the case, as it is possible to extend the stacked configuration. With this patch it is possible to provision devices on top Q-in-Q vlan configuration that should be used as a source of ARP monitoring information. For example: ip link add link bond0 vlan10 type vlan proto 802.1q id 10 ip link add link vlan10 vlan100 type vlan proto 802.1q id 100 ip link add link vlan100 type macvlan Note: This patch limites the number of stacked VLANs to 2, just like before. The original, however had another issue in that if we had more then 2 levels of VLANs, we would end up generating incorrectly tagged traffic. This is no longer possible. Fixes: fbd929f2dce460456807a51e18d623db3db9f077 (bonding: support QinQ for bond arp interval) CC: Jay Vosburgh CC: Veaceslav Falico CC: Andy Gospodarek CC: Ding Tianhong CC: Patric McHardy Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 134 +++++++++++++++++++--------------------- drivers/net/bonding/bonding.h | 1 + include/linux/if_vlan.h | 6 ++ include/linux/netdevice.h | 9 +++ net/core/dev.c | 26 ++++++++ 5 files changed, 107 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 69aff72c8957..d3a67896d435 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2126,10 +2126,10 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip) */ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_ip, __be32 src_ip, - struct bond_vlan_tag *inner, - struct bond_vlan_tag *outer) + struct bond_vlan_tag *tags) { struct sk_buff *skb; + int i; pr_debug("arp %d on slave %s: dst %pI4 src %pI4\n", arp_op, slave_dev->name, &dest_ip, &src_ip); @@ -2141,21 +2141,26 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, net_err_ratelimited("ARP packet allocation failed\n"); return; } - if (outer->vlan_id) { - if (inner->vlan_id) { - pr_debug("inner tag: proto %X vid %X\n", - ntohs(inner->vlan_proto), inner->vlan_id); - skb = __vlan_put_tag(skb, inner->vlan_proto, - inner->vlan_id); - if (!skb) { - net_err_ratelimited("failed to insert inner VLAN tag\n"); - return; - } - } - pr_debug("outer reg: proto %X vid %X\n", - ntohs(outer->vlan_proto), outer->vlan_id); - skb = vlan_put_tag(skb, outer->vlan_proto, outer->vlan_id); + /* Go through all the tags backwards and add them to the packet */ + for (i = BOND_MAX_VLAN_ENCAP - 1; i > 0; i--) { + if (!tags[i].vlan_id) + continue; + + pr_debug("inner tag: proto %X vid %X\n", + ntohs(tags[i].vlan_proto), tags[i].vlan_id); + skb = __vlan_put_tag(skb, tags[i].vlan_proto, + tags[i].vlan_id); + if (!skb) { + net_err_ratelimited("failed to insert inner VLAN tag\n"); + return; + } + } + /* Set the outer tag */ + if (tags[0].vlan_id) { + pr_debug("outer tag: proto %X vid %X\n", + ntohs(tags[0].vlan_proto), tags[0].vlan_id); + skb = vlan_put_tag(skb, tags[0].vlan_proto, tags[0].vlan_id); if (!skb) { net_err_ratelimited("failed to insert outer VLAN tag\n"); return; @@ -2164,22 +2169,52 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, arp_xmit(skb); } +/* Validate the device path between the @start_dev and the @end_dev. + * The path is valid if the @end_dev is reachable through device + * stacking. + * When the path is validated, collect any vlan information in the + * path. + */ +static bool bond_verify_device_path(struct net_device *start_dev, + struct net_device *end_dev, + struct bond_vlan_tag *tags) +{ + struct net_device *upper; + struct list_head *iter; + int idx; + + if (start_dev == end_dev) + return true; + + netdev_for_each_upper_dev_rcu(start_dev, upper, iter) { + if (bond_verify_device_path(upper, end_dev, tags)) { + if (is_vlan_dev(upper)) { + idx = vlan_get_encap_level(upper); + if (idx >= BOND_MAX_VLAN_ENCAP) + return false; + + tags[idx].vlan_proto = + vlan_dev_vlan_proto(upper); + tags[idx].vlan_id = vlan_dev_vlan_id(upper); + } + return true; + } + } + + return false; +} static void bond_arp_send_all(struct bonding *bond, struct slave *slave) { - struct net_device *upper, *vlan_upper; - struct list_head *iter, *vlan_iter; struct rtable *rt; - struct bond_vlan_tag inner, outer; + struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP]; __be32 *targets = bond->params.arp_targets, addr; int i; + bool ret; for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) { pr_debug("basa: target %pI4\n", &targets[i]); - inner.vlan_proto = 0; - inner.vlan_id = 0; - outer.vlan_proto = 0; - outer.vlan_id = 0; + memset(tags, 0, sizeof(tags)); /* Find out through which dev should the packet go */ rt = ip_route_output(dev_net(bond->dev), targets[i], 0, @@ -2192,7 +2227,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) net_warn_ratelimited("%s: no route to arp_ip_target %pI4 and arp_validate is set\n", bond->dev->name, &targets[i]); - bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], 0, &inner, &outer); + bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], + 0, tags); continue; } @@ -2201,52 +2237,12 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) goto found; rcu_read_lock(); - /* first we search only for vlan devices. for every vlan - * found we verify its upper dev list, searching for the - * rt->dst.dev. If found we save the tag of the vlan and - * proceed to send the packet. - */ - netdev_for_each_all_upper_dev_rcu(bond->dev, vlan_upper, - vlan_iter) { - if (!is_vlan_dev(vlan_upper)) - continue; - - if (vlan_upper == rt->dst.dev) { - outer.vlan_proto = vlan_dev_vlan_proto(vlan_upper); - outer.vlan_id = vlan_dev_vlan_id(vlan_upper); - rcu_read_unlock(); - goto found; - } - netdev_for_each_all_upper_dev_rcu(vlan_upper, upper, - iter) { - if (upper == rt->dst.dev) { - /* If the upper dev is a vlan dev too, - * set the vlan tag to inner tag. - */ - if (is_vlan_dev(upper)) { - inner.vlan_proto = vlan_dev_vlan_proto(upper); - inner.vlan_id = vlan_dev_vlan_id(upper); - } - outer.vlan_proto = vlan_dev_vlan_proto(vlan_upper); - outer.vlan_id = vlan_dev_vlan_id(vlan_upper); - rcu_read_unlock(); - goto found; - } - } - } - - /* if the device we're looking for is not on top of any of - * our upper vlans, then just search for any dev that - * matches, and in case it's a vlan - save the id - */ - netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) { - if (upper == rt->dst.dev) { - rcu_read_unlock(); - goto found; - } - } + ret = bond_verify_device_path(bond->dev, rt->dst.dev, tags); rcu_read_unlock(); + if (ret) + goto found; + /* Not our device - skip */ pr_debug("%s: no path to arp_ip_target %pI4 via rt.dev %s\n", bond->dev->name, &targets[i], @@ -2259,7 +2255,7 @@ found: addr = bond_confirm_addr(rt->dst.dev, targets[i], 0); ip_rt_put(rt); bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], - addr, &inner, &outer); + addr, tags); } } diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index b8bdd0acc8f3..00bea320e3b5 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -36,6 +36,7 @@ #define bond_version DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n" +#define BOND_MAX_VLAN_ENCAP 2 #define BOND_MAX_ARP_TARGETS 16 #define BOND_DEFAULT_MIIMON 100 diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 724bde8477b2..c901b13b6f03 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -484,4 +484,10 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb, */ skb->protocol = htons(ETH_P_802_2); } + +static inline int vlan_get_encap_level(struct net_device *dev) +{ + BUG_ON(!is_vlan_dev(dev)); + return vlan_dev_priv(dev)->nest_level; +} #endif /* !(_LINUX_IF_VLAN_H_) */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9d4b1f1b6b75..b42d07b0390b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3056,9 +3056,18 @@ extern int weight_p; extern int bpf_jit_enable; bool netdev_has_upper_dev(struct net_device *dev, struct net_device *upper_dev); +struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, + struct list_head **iter); struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev, struct list_head **iter); +/* iterate through upper list, must be called under RCU read lock */ +#define netdev_for_each_upper_dev_rcu(dev, updev, iter) \ + for (iter = &(dev)->adj_list.upper, \ + updev = netdev_upper_get_next_dev_rcu(dev, &(iter)); \ + updev; \ + updev = netdev_upper_get_next_dev_rcu(dev, &(iter))) + /* iterate through upper list, must be called under RCU read lock */ #define netdev_for_each_all_upper_dev_rcu(dev, updev, iter) \ for (iter = &(dev)->all_adj_list.upper, \ diff --git a/net/core/dev.c b/net/core/dev.c index 2b872bfbd172..9abc503b19b7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4541,6 +4541,32 @@ void *netdev_adjacent_get_private(struct list_head *adj_list) } EXPORT_SYMBOL(netdev_adjacent_get_private); +/** + * netdev_upper_get_next_dev_rcu - Get the next dev from upper list + * @dev: device + * @iter: list_head ** of the current position + * + * Gets the next device from the dev's upper list, starting from iter + * position. The caller must hold RCU read lock. + */ +struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, + struct list_head **iter) +{ + struct netdev_adjacent *upper; + + WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held()); + + upper = list_entry_rcu((*iter)->next, struct netdev_adjacent, list); + + if (&upper->list == &dev->adj_list.upper) + return NULL; + + *iter = &upper->list; + + return upper->dev; +} +EXPORT_SYMBOL(netdev_upper_get_next_dev_rcu); + /** * netdev_all_upper_get_next_dev_rcu - Get the next dev from upper list * @dev: device -- cgit v1.2.3 From 6c4e548ff36672eeb78f8288a2920d66fa4a6a66 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 16 May 2014 21:48:22 +0200 Subject: net: cdc_ncm: use ethtool to tune coalescing settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Datagram coalescing is an integral part of the NCM and MBIM protocols, intended to reduce the interrupt load primarily on the device end of the USB link. As with all coalescing solutions, there is a trade-off between buffering and interrupts. The current defaults are based on the assumption that device side buffers should be the limiting factor. However, many modern high speed LTE modems suffers from buffer-bloat, making this assumption fail. This results in sub-optimal performance due to excessive coalescing. And in cases where such modems are connected to cheap embedded hosts there is often severe buffer allocation issues, giving very noticeable performance degradation . A start on improving this is going from build time hard coded limits to per device user configurable limits. The ethtool coalescing API was selected as user interface because, although the tuned values are buffer sizes, these settings directly control datagram coalescing. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 71 +++++++++++++++++++++++++++++++++++++++++++-- include/linux/usb/cdc_ncm.h | 6 +++- 2 files changed, 74 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 2ec3790a4db8..141dbec912be 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -65,6 +65,67 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx); static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer); static struct usb_driver cdc_ncm_driver; +static int cdc_ncm_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct usbnet *dev = netdev_priv(netdev); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + + /* assuming maximum sized dgrams and ignoring NDPs */ + ec->rx_max_coalesced_frames = ctx->rx_max / ctx->max_datagram_size; + ec->tx_max_coalesced_frames = ctx->tx_max / ctx->max_datagram_size; + + /* the timer will fire CDC_NCM_TIMER_PENDING_CNT times in a row */ + ec->tx_coalesce_usecs = (ctx->timer_interval * CDC_NCM_TIMER_PENDING_CNT) / NSEC_PER_USEC; + return 0; +} + +static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx); + +static int cdc_ncm_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct usbnet *dev = netdev_priv(netdev); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u32 new_rx_max = ctx->rx_max; + u32 new_tx_max = ctx->tx_max; + + /* assuming maximum sized dgrams and a single NDP */ + if (ec->rx_max_coalesced_frames) + new_rx_max = ec->rx_max_coalesced_frames * ctx->max_datagram_size; + if (ec->tx_max_coalesced_frames) + new_tx_max = ec->tx_max_coalesced_frames * ctx->max_datagram_size; + + if (ec->tx_coalesce_usecs && + (ec->tx_coalesce_usecs < CDC_NCM_TIMER_INTERVAL_MIN * CDC_NCM_TIMER_PENDING_CNT || + ec->tx_coalesce_usecs > CDC_NCM_TIMER_INTERVAL_MAX * CDC_NCM_TIMER_PENDING_CNT)) + return -EINVAL; + + spin_lock_bh(&ctx->mtx); + ctx->timer_interval = ec->tx_coalesce_usecs * NSEC_PER_USEC / CDC_NCM_TIMER_PENDING_CNT; + if (!ctx->timer_interval) + ctx->tx_timer_pending = 0; + spin_unlock_bh(&ctx->mtx); + + /* inform device of new values */ + if (new_rx_max != ctx->rx_max || new_tx_max != ctx->tx_max) + cdc_ncm_update_rxtx_max(dev, new_rx_max, new_tx_max); + return 0; +} + +static const struct ethtool_ops cdc_ncm_ethtool_ops = { + .get_settings = usbnet_get_settings, + .set_settings = usbnet_set_settings, + .get_link = usbnet_get_link, + .nway_reset = usbnet_nway_reset, + .get_drvinfo = usbnet_get_drvinfo, + .get_msglevel = usbnet_get_msglevel, + .set_msglevel = usbnet_set_msglevel, + .get_ts_info = ethtool_op_get_ts_info, + .get_coalesce = cdc_ncm_get_coalesce, + .set_coalesce = cdc_ncm_set_coalesce, +}; + /* handle rx_max and tx_max changes */ static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx) { @@ -257,6 +318,9 @@ static int cdc_ncm_init(struct usbnet *dev) (ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX)) ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX; + /* initial coalescing timer interval */ + ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC; + return 0; } @@ -596,6 +660,9 @@ advance: /* finish setting up the device specific data */ cdc_ncm_setup(dev); + /* override ethtool_ops */ + dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; + return 0; error2: @@ -863,7 +930,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) ctx->tx_curr_skb = skb_out; goto exit_no_skb; - } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) { + } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) { /* wait for more frames */ /* push variables */ ctx->tx_curr_skb = skb_out; @@ -915,7 +982,7 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx) /* start timer, if not already started */ if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop))) hrtimer_start(&ctx->tx_timer, - ktime_set(0, CDC_NCM_TIMER_INTERVAL), + ktime_set(0, ctx->timer_interval), HRTIMER_MODE_REL); } diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 55b6feead93b..5c1066b4dc41 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -72,7 +72,9 @@ /* Restart the timer, if amount of datagrams is less than given value */ #define CDC_NCM_RESTART_TIMER_DATAGRAM_CNT 3 #define CDC_NCM_TIMER_PENDING_CNT 2 -#define CDC_NCM_TIMER_INTERVAL (400UL * NSEC_PER_USEC) +#define CDC_NCM_TIMER_INTERVAL_USEC 400UL +#define CDC_NCM_TIMER_INTERVAL_MIN 5UL +#define CDC_NCM_TIMER_INTERVAL_MAX (15UL * USEC_PER_SEC) /* The following macro defines the minimum header space */ #define CDC_NCM_MIN_HDR_SIZE \ @@ -107,6 +109,8 @@ struct cdc_ncm_ctx { spinlock_t mtx; atomic_t stop; + u64 timer_interval; + u32 tx_timer_pending; u32 tx_curr_frame_num; u32 rx_max; -- cgit v1.2.3 From 70559b8970e52aa9962dc823fd4498af06809544 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 16 May 2014 21:48:23 +0200 Subject: net: cdc_ncm: use true max dgram count for header estimates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many newer NCM and MBIM devices will request a maximum tx datagram count which is much smaller than our hard-coded absolute max. We can reduce the overhead without sacrificing any of the simplicity for these devices, by simply using the true negotiated count in when calculated the maximum NTH and NDP header sizes. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 9 ++++++--- include/linux/usb/cdc_ncm.h | 10 +--------- 2 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 141dbec912be..b9b562b9128a 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -174,7 +174,7 @@ static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx) } /* clamp new_tx to sane values */ - min = CDC_NCM_MIN_HDR_SIZE + ctx->max_datagram_size; + min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); /* some devices set dwNtbOutMaxSize too low for the above default */ @@ -318,6 +318,9 @@ static int cdc_ncm_init(struct usbnet *dev) (ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX)) ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX; + /* set up maximum NDP size */ + ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16); + /* initial coalescing timer interval */ ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC; @@ -800,7 +803,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max); /* verify that there is room for the NDP and the datagram (reserve) */ - if ((ctx->tx_max - skb->len - reserve) < CDC_NCM_NDP_SIZE) + if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size) return NULL; /* link to it */ @@ -810,7 +813,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ nth16->wNdpIndex = cpu_to_le16(skb->len); /* push a new empty NDP */ - ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE); + ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size); ndp16->dwSignature = sign; ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16)); return ndp16; diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 5c1066b4dc41..60a44b8a464e 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -76,15 +76,6 @@ #define CDC_NCM_TIMER_INTERVAL_MIN 5UL #define CDC_NCM_TIMER_INTERVAL_MAX (15UL * USEC_PER_SEC) -/* The following macro defines the minimum header space */ -#define CDC_NCM_MIN_HDR_SIZE \ - (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \ - (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) - -#define CDC_NCM_NDP_SIZE \ - (sizeof(struct usb_cdc_ncm_ndp16) + \ - (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16)) - #define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \ (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE) #define cdc_ncm_data_intf_is_mbim(x) ((x)->desc.bInterfaceProtocol == USB_CDC_MBIM_PROTO_NTB) @@ -110,6 +101,7 @@ struct cdc_ncm_ctx { atomic_t stop; u64 timer_interval; + u32 max_ndp_size; u32 tx_timer_pending; u32 tx_curr_frame_num; -- cgit v1.2.3 From 43e4c6dfc0fd781e68f20caf563a06f5c6ece995 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 16 May 2014 21:48:24 +0200 Subject: net: cdc_ncm: set reasonable padding limits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We pad frames larger than X to maximum size for devices which don't need a ZLP after maximum sized frames. This allows the device to optimize its transfers for one fixed buffer size. X was arbitrarily set at 512 bytes regardless of real buffer maximum, causing extreme overheads due to excessive padding of larger tx buffers. Limit the padding to at most 3 full USB packets, still allowing the overhead to payload ratio of 3/1. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 8 ++++++-- include/linux/usb/cdc_ncm.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index b9b562b9128a..9592d4669435 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -213,6 +213,10 @@ static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx) /* max qlen depend on hard_mtu and rx_urb_size */ usbnet_update_max_qlen(dev); + + /* never pad more than 3 full USB packets per transfer */ + ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1), + CDC_NCM_MIN_TX_PKT, ctx->tx_max); } /* helpers for NCM and MBIM differences */ @@ -947,7 +951,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* variables will be reset at next call */ } - /* If collected data size is less or equal CDC_NCM_MIN_TX_PKT + /* If collected data size is less or equal ctx->min_tx_pkt * bytes, we send buffers as it is. If we get more data, it * would be more efficient for USB HS mobile device with DMA * engine to receive a full size NTB, than canceling DMA @@ -957,7 +961,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) * a ZLP after full sized NTBs. */ if (!(dev->driver_info->flags & FLAG_SEND_ZLP) && - skb_out->len > CDC_NCM_MIN_TX_PKT) + skb_out->len > ctx->min_tx_pkt) memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len); else if (skb_out->len < ctx->tx_max && (skb_out->len % dev->maxpacket) == 0) diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 60a44b8a464e..79de6724d398 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -115,6 +115,7 @@ struct cdc_ncm_ctx { u16 tx_seq; u16 rx_seq; u16 connected; + u16 min_tx_pkt; }; u8 cdc_ncm_select_altsetting(struct usb_interface *intf); -- cgit v1.2.3 From beeecd42c3b41d17d0bf1d839db99274c287f514 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 16 May 2014 21:48:25 +0200 Subject: net: cdc_ncm/cdc_mbim: adding NCM protocol statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To have an idea of the effects of the protocol coalescing it's useful to have some counters showing the different aspects. Due to the asymmetrical usbnet interface the netdev rx_bytes counter has been counting real received payload, while the tx_bytes counter has included the NCM/MBIM framing overhead. This overhead can be many times the payload because of the aggressive padding strategy of this driver, and will vary a lot depending on device and traffic. With very few exceptions, users are only interested in the payload size. Having an somewhat accurate payload byte counter is particularly important for mobile broadband devices, which many NCM devices and of course all MBIM devices are. Users and userspace applications will use this counter to monitor account quotas. Having protocol specific counters for the overhead, we are now able to correct the tx_bytes netdev counter so that it shows the real payload Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_mbim.c | 6 +++ drivers/net/usb/cdc_ncm.c | 91 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/cdc_ncm.h | 11 ++++++ 3 files changed, 108 insertions(+) (limited to 'include') diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index bc23273d0455..5ee7a1dbc023 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -420,6 +420,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) struct usb_cdc_ncm_dpe16 *dpe16; int ndpoffset; int loopcount = 50; /* arbitrary max preventing infinite loop */ + u32 payload = 0; u8 *c; u16 tci; @@ -482,6 +483,7 @@ next_ndp: if (!skb) goto error; usbnet_skb_return(dev, skb); + payload += len; /* count payload bytes in this NTB */ } } err_ndp: @@ -490,6 +492,10 @@ err_ndp: if (ndpoffset && loopcount--) goto next_ndp; + /* update stats */ + ctx->rx_overhead += skb_in->len - payload; + ctx->rx_ntbs++; + return 1; error: return 0; diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 9592d4669435..f4b439847d04 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -65,6 +65,68 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx); static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer); static struct usb_driver cdc_ncm_driver; +struct cdc_ncm_stats { + char stat_string[ETH_GSTRING_LEN]; + int sizeof_stat; + int stat_offset; +}; + +#define CDC_NCM_STAT(str, m) { \ + .stat_string = str, \ + .sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \ + .stat_offset = offsetof(struct cdc_ncm_ctx, m) } +#define CDC_NCM_SIMPLE_STAT(m) CDC_NCM_STAT(__stringify(m), m) + +static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = { + CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full), + CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full), + CDC_NCM_SIMPLE_STAT(tx_reason_timeout), + CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram), + CDC_NCM_SIMPLE_STAT(tx_overhead), + CDC_NCM_SIMPLE_STAT(tx_ntbs), + CDC_NCM_SIMPLE_STAT(rx_overhead), + CDC_NCM_SIMPLE_STAT(rx_ntbs), +}; + +static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(cdc_ncm_gstrings_stats); + default: + return -EOPNOTSUPP; + } +} + +static void cdc_ncm_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats __always_unused *stats, + u64 *data) +{ + struct usbnet *dev = netdev_priv(netdev); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + int i; + char *p = NULL; + + for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { + p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset; + data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } +} + +static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data) +{ + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) { + memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + } +} + static int cdc_ncm_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) { @@ -122,6 +184,9 @@ static const struct ethtool_ops cdc_ncm_ethtool_ops = { .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, .get_ts_info = ethtool_op_get_ts_info, + .get_sset_count = cdc_ncm_get_sset_count, + .get_strings = cdc_ncm_get_strings, + .get_ethtool_stats = cdc_ncm_get_ethtool_stats, .get_coalesce = cdc_ncm_get_coalesce, .set_coalesce = cdc_ncm_set_coalesce, }; @@ -862,6 +927,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* count total number of frames in this NTB */ ctx->tx_curr_frame_num = 0; + + /* recent payload counter for this skb_out */ + ctx->tx_curr_frame_payload = 0; } for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) { @@ -899,6 +967,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) ctx->tx_rem_sign = sign; skb = NULL; ready2send = 1; + ctx->tx_reason_ntb_full++; /* count reason for transmitting */ } break; } @@ -912,12 +981,14 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); memcpy(skb_put(skb_out, skb->len), skb->data, skb->len); + ctx->tx_curr_frame_payload += skb->len; /* count real tx payload data */ dev_kfree_skb_any(skb); skb = NULL; /* send now if this NDP is full */ if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) { ready2send = 1; + ctx->tx_reason_ndp_full++; /* count reason for transmitting */ break; } } @@ -947,6 +1018,8 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) goto exit_no_skb; } else { + if (n == ctx->tx_max_datagrams) + ctx->tx_reason_max_datagram++; /* count reason for transmitting */ /* frame goes out */ /* variables will be reset at next call */ } @@ -974,6 +1047,17 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* return skb */ ctx->tx_curr_skb = NULL; dev->net->stats.tx_packets += ctx->tx_curr_frame_num; + + /* keep private stats: framing overhead and number of NTBs */ + ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; + ctx->tx_ntbs++; + + /* usbnet has already counted all the framing overhead. + * Adjust the stats so that the tx_bytes counter show real + * payload data instead. + */ + dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload; + return skb_out; exit_no_skb: @@ -1014,6 +1098,7 @@ static void cdc_ncm_txpath_bh(unsigned long param) cdc_ncm_tx_timeout_start(ctx); spin_unlock_bh(&ctx->mtx); } else if (dev->net != NULL) { + ctx->tx_reason_timeout++; /* count reason for transmitting */ spin_unlock_bh(&ctx->mtx); netif_tx_lock_bh(dev->net); usbnet_start_xmit(NULL, dev->net); @@ -1149,6 +1234,7 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) struct usb_cdc_ncm_dpe16 *dpe16; int ndpoffset; int loopcount = 50; /* arbitrary max preventing infinite loop */ + u32 payload = 0; ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); if (ndpoffset < 0) @@ -1201,6 +1287,7 @@ next_ndp: skb->data = ((u8 *)skb_in->data) + offset; skb_set_tail_pointer(skb, len); usbnet_skb_return(dev, skb); + payload += len; /* count payload bytes in this NTB */ } } err_ndp: @@ -1209,6 +1296,10 @@ err_ndp: if (ndpoffset && loopcount--) goto next_ndp; + /* update stats */ + ctx->rx_overhead += skb_in->len - payload; + ctx->rx_ntbs++; + return 1; error: return 0; diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 79de6724d398..88d2d7f1820f 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -116,6 +116,17 @@ struct cdc_ncm_ctx { u16 rx_seq; u16 connected; u16 min_tx_pkt; + + /* statistics */ + u32 tx_curr_frame_payload; + u32 tx_reason_ntb_full; + u32 tx_reason_ndp_full; + u32 tx_reason_timeout; + u32 tx_reason_max_datagram; + u64 tx_overhead; + u64 tx_ntbs; + u64 rx_overhead; + u64 rx_ntbs; }; u8 cdc_ncm_select_altsetting(struct usb_interface *intf); -- cgit v1.2.3 From 50f1cb1cc8f50fa88dbeaf990ae2bae91b9ff306 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 16 May 2014 21:48:26 +0200 Subject: net: cdc_ncm: use sane defaults for rx/tx buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lots of devices request much larger buffers than reasonable. This cause real problems for users of hosts with limited resources. Reducing the default buffer size to 16kB for such devices is a reasonable trade-off between allowing them to aggregate traffic and avoiding memory exhaustion on resource restrained hosts. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 12 ++++++++++-- include/linux/usb/cdc_ncm.h | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index f4b439847d04..bb53abe1f3a1 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -492,10 +492,18 @@ static void cdc_ncm_fix_modulus(struct usbnet *dev) static int cdc_ncm_setup(struct usbnet *dev) { struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + u32 def_rx, def_tx; + + /* be conservative when selecting intial buffer size to + * increase the number of hosts this will work for + */ + def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX, + le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)); + def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX, + le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); /* clamp rx_max and tx_max and inform device */ - cdc_ncm_update_rxtx_max(dev, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), - le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); + cdc_ncm_update_rxtx_max(dev, def_rx, def_tx); /* sanitize the modulus and remainder values */ cdc_ncm_fix_modulus(dev); diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 88d2d7f1820f..cde506731c48 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -52,6 +52,10 @@ #define CDC_NCM_NTB_MAX_SIZE_TX 32768 /* bytes */ #define CDC_NCM_NTB_MAX_SIZE_RX 32768 /* bytes */ +/* Initial NTB length */ +#define CDC_NCM_NTB_DEF_SIZE_TX 16384 /* bytes */ +#define CDC_NCM_NTB_DEF_SIZE_RX 16384 /* bytes */ + /* Minimum value for MaxDatagramSize, ch. 6.2.9 */ #define CDC_NCM_MIN_DATAGRAM_SIZE 1514 /* bytes */ -- cgit v1.2.3 From fa83dbeee55865678025b6c1637ca08860209f87 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 16 May 2014 21:48:28 +0200 Subject: net: cdc_ncm: remove redundant "disconnected" flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling netif_carrier_{on,off} is sufficient. There is no need to duplicate the carrier state in a driver specific flag. Acked-by: Enrico Mioso Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 19 ++----------------- drivers/net/usb/huawei_cdc_ncm.c | 13 ------------- include/linux/usb/cdc_ncm.h | 1 - 3 files changed, 2 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 1d1ff2fa8ae1..783c4ed96395 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1364,11 +1364,10 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb) * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE. */ - ctx->connected = le16_to_cpu(event->wValue); netif_info(dev, link, dev->net, "network connection: %sconnected\n", - ctx->connected ? "" : "dis"); - usbnet_link_change(dev, ctx->connected, 0); + !!event->wValue ? "" : "dis"); + usbnet_link_change(dev, !!event->wValue, 0); break; case USB_CDC_NOTIFY_SPEED_CHANGE: @@ -1388,23 +1387,11 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb) } } -static int cdc_ncm_check_connect(struct usbnet *dev) -{ - struct cdc_ncm_ctx *ctx; - - ctx = (struct cdc_ncm_ctx *)dev->data[0]; - if (ctx == NULL) - return 1; /* disconnected */ - - return !ctx->connected; -} - static const struct driver_info cdc_ncm_info = { .description = "CDC NCM", .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, - .check_connect = cdc_ncm_check_connect, .manage_power = usbnet_manage_power, .status = cdc_ncm_status, .rx_fixup = cdc_ncm_rx_fixup, @@ -1418,7 +1405,6 @@ static const struct driver_info wwan_info = { | FLAG_WWAN, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, - .check_connect = cdc_ncm_check_connect, .manage_power = usbnet_manage_power, .status = cdc_ncm_status, .rx_fixup = cdc_ncm_rx_fixup, @@ -1432,7 +1418,6 @@ static const struct driver_info wwan_noarp_info = { | FLAG_WWAN | FLAG_NOARP, .bind = cdc_ncm_bind, .unbind = cdc_ncm_unbind, - .check_connect = cdc_ncm_check_connect, .manage_power = usbnet_manage_power, .status = cdc_ncm_status, .rx_fixup = cdc_ncm_rx_fixup, diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c index 312178d7b698..f9822bc75425 100644 --- a/drivers/net/usb/huawei_cdc_ncm.c +++ b/drivers/net/usb/huawei_cdc_ncm.c @@ -172,24 +172,11 @@ err: return ret; } -static int huawei_cdc_ncm_check_connect(struct usbnet *usbnet_dev) -{ - struct cdc_ncm_ctx *ctx; - - ctx = (struct cdc_ncm_ctx *)usbnet_dev->data[0]; - - if (ctx == NULL) - return 1; /* disconnected */ - - return !ctx->connected; -} - static const struct driver_info huawei_cdc_ncm_info = { .description = "Huawei CDC NCM device", .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, .bind = huawei_cdc_ncm_bind, .unbind = huawei_cdc_ncm_unbind, - .check_connect = huawei_cdc_ncm_check_connect, .manage_power = huawei_cdc_ncm_manage_power, .rx_fixup = cdc_ncm_rx_fixup, .tx_fixup = cdc_ncm_tx_fixup, diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index cde506731c48..8c5e38819828 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -118,7 +118,6 @@ struct cdc_ncm_ctx { u16 tx_ndp_modulus; u16 tx_seq; u16 rx_seq; - u16 connected; u16 min_tx_pkt; /* statistics */ -- cgit v1.2.3 From a17597d3b418ca5a394d14724ccfc295cb3186c8 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 16 May 2014 11:42:43 +0930 Subject: virtio-rng: fixes for device registration/unregistration There are several fixes in this patch (mostly because it's hard splitting them up): - Revert the name field in struct hwrng back to 'const'. Also, don't do an extra kmalloc for the name - just wasteful. - Deal with allocation failures properly. - Use IDA to allocate device number instead of brute forcing one. Signed-off-by: Sasha Levin Signed-off-by: Rusty Russell --- drivers/char/hw_random/virtio-rng.c | 41 +++++++++++++++++++++---------------- include/linux/hw_random.h | 2 +- 2 files changed, 24 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 5b25daa7f798..f3e71501de54 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -25,6 +25,7 @@ #include #include +static DEFINE_IDA(rng_index_ida); struct virtrng_info { struct virtio_device *vdev; @@ -33,6 +34,8 @@ struct virtrng_info { unsigned int data_avail; struct completion have_data; bool busy; + char name[25]; + int index; }; static void random_recv_done(struct virtqueue *vq) @@ -92,41 +95,45 @@ static void virtio_cleanup(struct hwrng *rng) static int probe_common(struct virtio_device *vdev) { - int err, i; + int err, index; struct virtrng_info *vi = NULL; vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL); - vi->hwrng.name = kmalloc(40, GFP_KERNEL); + if (!vi) + return -ENOMEM; + + vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL); + if (index < 0) { + kfree(vi); + return index; + } + sprintf(vi->name, "virtio_rng.%d", index); init_completion(&vi->have_data); - vi->hwrng.read = virtio_read; - vi->hwrng.cleanup = virtio_cleanup; - vi->hwrng.priv = (unsigned long)vi; + vi->hwrng = (struct hwrng) { + .read = virtio_read, + .cleanup = virtio_cleanup, + .priv = (unsigned long)vi, + .name = vi->name, + }; vdev->priv = vi; /* We expect a single virtqueue. */ vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input"); if (IS_ERR(vi->vq)) { err = PTR_ERR(vi->vq); - kfree(vi->hwrng.name); vi->vq = NULL; kfree(vi); - vi = NULL; + ida_simple_remove(&rng_index_ida, index); return err; } - i = 0; - do { - sprintf(vi->hwrng.name, "virtio_rng.%d", i++); - err = hwrng_register(&vi->hwrng); - } while (err == -EEXIST); - + err = hwrng_register(&vi->hwrng); if (err) { vdev->config->del_vqs(vdev); - kfree(vi->hwrng.name); vi->vq = NULL; kfree(vi); - vi = NULL; + ida_simple_remove(&rng_index_ida, index); return err; } @@ -140,10 +147,8 @@ static void remove_common(struct virtio_device *vdev) vi->busy = false; hwrng_unregister(&vi->hwrng); vdev->config->del_vqs(vdev); - kfree(vi->hwrng.name); - vi->vq = NULL; + ida_simple_remove(&rng_index_ida, vi->index); kfree(vi); - vi = NULL; } static int virtrng_probe(struct virtio_device *vdev) diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h index 02d9c87be54c..b4b0eef5fddf 100644 --- a/include/linux/hw_random.h +++ b/include/linux/hw_random.h @@ -31,7 +31,7 @@ * @priv: Private data, for use by the RNG driver. */ struct hwrng { - char *name; + const char *name; int (*init)(struct hwrng *rng); void (*cleanup)(struct hwrng *rng); int (*data_present)(struct hwrng *rng, int wait); -- cgit v1.2.3 From 7455fa2422898eee3464032351d20695930d9542 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 15 May 2014 01:41:23 +0100 Subject: ethtool: Name the 'no change' value for setting RSS hash key but not indir table We usually allocate special values of u32 fields starting from the top down, so also change the value to 0xffffffff. As these operations haven't been included in a stable release yet, it's not too late to change. Signed-off-by: Ben Hutchings --- include/uapi/linux/ethtool.h | 12 +++++++----- net/core/ethtool.c | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index d47d31d6fa0e..cba18e3f8fab 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -850,7 +850,8 @@ struct ethtool_rxfh_indir { * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key. * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH * @rss_context: RSS context identifier. - * @indir_size: On entry, the array size of the user buffer, which may be zero. + * @indir_size: On entry, the array size of the user buffer, which may be zero, + * or (for %ETHTOOL_SRSSH), %ETH_RXFH_INDIR_NO_CHANGE. * On return from %ETHTOOL_GRSSH, the array size of the hardware * indirection table. * @key_size: On entry, the array size of the user buffer in bytes, @@ -861,10 +862,10 @@ struct ethtool_rxfh_indir { * of size @indir_size followed by hash key of size @key_size. * * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the - * size should be returned. For %ETHTOOL_SRSSH, a @indir_size of 0xDEADBEEF - * means that indir table setting is not requested and a @indir_size of zero - * means the indir table should be reset to default values. This last feature - * is not supported by the original implementations. + * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of + * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested + * and a @indir_size of zero means the indir table should be reset to default + * values. */ struct ethtool_rxfh { __u32 cmd; @@ -874,6 +875,7 @@ struct ethtool_rxfh { __u32 rsvd[2]; __u32 rss_config[0]; }; +#define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff /** * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter diff --git a/net/core/ethtool.c b/net/core/ethtool.c index c834cb29f682..7156fe5ca876 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -803,12 +803,13 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, /* If either indir or hash key is valid, proceed further. */ - if ((user_indir_size && ((user_indir_size != 0xDEADBEEF) && - user_indir_size != dev_indir_size)) || + if ((user_indir_size && + user_indir_size != ETH_RXFH_INDIR_NO_CHANGE && + user_indir_size != dev_indir_size) || (user_key_size && (user_key_size != dev_key_size))) return -EINVAL; - if (user_indir_size != 0xDEADBEEF) + if (user_indir_size != ETH_RXFH_INDIR_NO_CHANGE) indir_bytes = dev_indir_size * sizeof(indir[0]); rss_config = kzalloc(indir_bytes + user_key_size, GFP_USER); @@ -821,9 +822,10 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, goto out; /* user_indir_size == 0 means reset the indir table to default. - * user_indir_size == 0xDEADBEEF means indir setting is not requested. + * user_indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged. */ - if (user_indir_size && user_indir_size != 0xDEADBEEF) { + if (user_indir_size && + user_indir_size != ETH_RXFH_INDIR_NO_CHANGE) { indir = (u32 *)rss_config; ret = ethtool_copy_validate_indir(indir, useraddr + rss_cfg_offset, -- cgit v1.2.3 From 38c891a49dec43dbb1575cc40d10dbd49c4961ab Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 15 May 2014 01:07:16 +0100 Subject: ethtool: Improve explanation of the two arrays following struct ethtool_rxfh The use of two variable-length arrays is unusual so deserves a bit more explanation. Signed-off-by: Ben Hutchings --- include/uapi/linux/ethtool.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index cba18e3f8fab..e3c7a719c76b 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -850,16 +850,17 @@ struct ethtool_rxfh_indir { * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key. * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH * @rss_context: RSS context identifier. - * @indir_size: On entry, the array size of the user buffer, which may be zero, - * or (for %ETHTOOL_SRSSH), %ETH_RXFH_INDIR_NO_CHANGE. - * On return from %ETHTOOL_GRSSH, the array size of the hardware - * indirection table. - * @key_size: On entry, the array size of the user buffer in bytes, - * which may be zero. - * On return from %ETHTOOL_GRSSH, the size of the RSS hash key. + * @indir_size: On entry, the array size of the user buffer for the + * indirection table, which may be zero, or (for %ETHTOOL_SRSSH), + * %ETH_RXFH_INDIR_NO_CHANGE. On return from %ETHTOOL_GRSSH, + * the array size of the hardware indirection table. + * @key_size: On entry, the array size of the user buffer for the hash key, + * which may be zero. On return from %ETHTOOL_GRSSH, the size of the + * hardware hash key. * @rsvd: Reserved for future extensions. * @rss_config: RX ring/queue index for each hash value i.e., indirection table - * of size @indir_size followed by hash key of size @key_size. + * of @indir_size __u32 elements, followed by hash key of @key_size + * bytes. * * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of -- cgit v1.2.3 From 678e30df2e5664619e06fcfea5490a476826d8fe Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 19 May 2014 01:25:59 +0100 Subject: ethtool: Expand documentation of ethtool_ops::{get,set}_rxfh() Some corner-cases are not explained properly. Signed-off-by: Ben Hutchings --- include/linux/ethtool.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 212f537fc686..886e127d51a6 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -162,15 +162,16 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings) * Will not be called if @get_rxfh_indir_size returns zero. * @get_rxfh: Get the contents of the RX flow hash indirection table and hash * key. - * Will not be called if @get_rxfh_indir_size and @get_rxfh_key_size - * returns zero. + * Will only be called if one or both of @get_rxfh_indir_size and + * @get_rxfh_key_size are implemented and return non-zero. * Returns a negative error code or zero. * @set_rxfh_indir: Set the contents of the RX flow hash indirection table. * Will not be called if @get_rxfh_indir_size returns zero. - * @set_rxfh: Set the contents of the RX flow hash indirection table and - * hash key. - * Will not be called if @get_rxfh_indir_size and @get_rxfh_key_size - * returns zero. + * @set_rxfh: Set the contents of the RX flow hash indirection table and/or + * hash key. Either or both arguments may be %NULL if that attribute + * is not to be changed. + * Will only be called if one or both of @get_rxfh_indir_size and + * @get_rxfh_key_size are implemented and return non-zero. * Returns a negative error code or zero. * @get_channels: Get number of channels. * @set_channels: Set number of channels. Returns a negative error code or @@ -244,8 +245,8 @@ struct ethtool_ops { int (*reset)(struct net_device *, u32 *); u32 (*get_rxfh_key_size)(struct net_device *); u32 (*get_rxfh_indir_size)(struct net_device *); - int (*get_rxfh)(struct net_device *, u32 *, u8 *); - int (*set_rxfh)(struct net_device *, u32 *, u8 *); + int (*get_rxfh)(struct net_device *, u32 *indir, u8 *key); + int (*set_rxfh)(struct net_device *, u32 *indir, u8 *key); int (*get_rxfh_indir)(struct net_device *, u32 *); int (*set_rxfh_indir)(struct net_device *, const u32 *); void (*get_channels)(struct net_device *, struct ethtool_channels *); -- cgit v1.2.3 From 61d88c6811f216de4ec26aafe24e650dc1aeb00e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 19 May 2014 01:29:42 +0100 Subject: ethtool: Disallow ETHTOOL_SRSSH with both indir table and hash key unchanged This would be a no-op, so there is no reason to request it. This also allows conversion of the current implementations of ethtool_ops::{get,set}_rxfh_indir to ethtool_ops::{get,set}_rxfh with no change other than their parameters. Signed-off-by: Ben Hutchings --- include/linux/ethtool.h | 4 ++-- net/core/ethtool.c | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 886e127d51a6..de687a97c6e7 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -168,8 +168,8 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings) * @set_rxfh_indir: Set the contents of the RX flow hash indirection table. * Will not be called if @get_rxfh_indir_size returns zero. * @set_rxfh: Set the contents of the RX flow hash indirection table and/or - * hash key. Either or both arguments may be %NULL if that attribute - * is not to be changed. + * hash key. In case only the indirection table or hash key is to be + * changed, the other argument will be %NULL. * Will only be called if one or both of @get_rxfh_indir_size and * @get_rxfh_key_size are implemented and return non-zero. * Returns a negative error code or zero. diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 7156fe5ca876..b8857348bdf3 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -802,11 +802,14 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, return -EFAULT; /* If either indir or hash key is valid, proceed further. + * It is not valid to request that both be unchanged. */ if ((user_indir_size && user_indir_size != ETH_RXFH_INDIR_NO_CHANGE && user_indir_size != dev_indir_size) || - (user_key_size && (user_key_size != dev_key_size))) + (user_key_size && (user_key_size != dev_key_size)) || + (user_indir_size == ETH_RXFH_INDIR_NO_CHANGE && + user_key_size == 0)) return -EINVAL; if (user_indir_size != ETH_RXFH_INDIR_NO_CHANGE) -- cgit v1.2.3 From 33cb0fa7888510b5bd2096352b200cfe29db10fe Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 15 May 2014 02:01:23 +0100 Subject: ethtool, be2net: constify array pointer parameters to ethtool_ops::set_rxfh Signed-off-by: Ben Hutchings --- drivers/net/ethernet/emulex/benet/be_cmds.c | 2 +- drivers/net/ethernet/emulex/benet/be_cmds.h | 2 +- drivers/net/ethernet/emulex/benet/be_ethtool.c | 3 ++- include/linux/ethtool.h | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 476752d0a6a4..7b59da241ccb 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -2033,7 +2033,7 @@ int be_cmd_reset_function(struct be_adapter *adapter) } int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable, - u32 rss_hash_opts, u16 table_size, u8 *rss_hkey) + u32 rss_hash_opts, u16 table_size, const u8 *rss_hkey) { struct be_mcc_wrb *wrb; struct be_cmd_req_rss_config *req; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 228d4b611084..451f3138b8fb 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -2068,7 +2068,7 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num, u32 *function_mode, u32 *function_caps, u16 *asic_rev); int be_cmd_reset_function(struct be_adapter *adapter); int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable, - u32 rss_hash_opts, u16 table_size, u8 *rss_hkey); + u32 rss_hash_opts, u16 table_size, const u8 *rss_hkey); int be_process_mcc(struct be_adapter *adapter); int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num, u8 beacon, u8 status, u8 state); diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 970ae337daac..e2da4d20dd3d 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -1117,7 +1117,8 @@ static int be_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey) return 0; } -static int be_set_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey) +static int be_set_rxfh(struct net_device *netdev, const u32 *indir, + const u8 *hkey) { int rc = 0, i, j; struct be_adapter *adapter = netdev_priv(netdev); diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index de687a97c6e7..874fde01d398 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -246,7 +246,8 @@ struct ethtool_ops { u32 (*get_rxfh_key_size)(struct net_device *); u32 (*get_rxfh_indir_size)(struct net_device *); int (*get_rxfh)(struct net_device *, u32 *indir, u8 *key); - int (*set_rxfh)(struct net_device *, u32 *indir, u8 *key); + int (*set_rxfh)(struct net_device *, const u32 *indir, + const u8 *key); int (*get_rxfh_indir)(struct net_device *, u32 *); int (*set_rxfh_indir)(struct net_device *, const u32 *); void (*get_channels)(struct net_device *, struct ethtool_channels *); -- cgit v1.2.3 From b8802f76fe473d91886220498aeda157c492f2d1 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Sun, 11 May 2014 16:05:58 +0800 Subject: irqchip: gic: Use mask field in GICC_IAR Bit[9:0] is interrupt ID field in GICC_IAR. Bit[12:10] is CPU ID field, and others are reserved. So we should use GICC_IAR_INT_ID_MASK to get interrupt ID. It's not a good way to use ~0x1c00 (CPU ID field) to get interrupt ID. Signed-off-by: Haojian Zhuang Link: https://lkml.kernel.org/r/1399795571-17231-3-git-send-email-haojian.zhuang@linaro.org Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic.c | 2 +- include/linux/irqchip/arm-gic.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4300b6606f5e..f711fb6af7a9 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -287,7 +287,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) do { irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); - irqnr = irqstat & ~0x1c00; + irqnr = irqstat & GICC_IAR_INT_ID_MASK; if (likely(irqnr > 15 && irqnr < 1021)) { irqnr = irq_find_mapping(gic->domain, irqnr); diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 7ed92d0560d5..45e2d8c15bd2 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -21,6 +21,8 @@ #define GIC_CPU_ACTIVEPRIO 0xd0 #define GIC_CPU_IDENT 0xfc +#define GICC_IAR_INT_ID_MASK 0x3ff + #define GIC_DIST_CTRL 0x000 #define GIC_DIST_CTR 0x004 #define GIC_DIST_IGROUP 0x080 -- cgit v1.2.3 From d3091298570006fa538ec9beacbfb1098964962e Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Fri, 16 May 2014 23:26:05 +0200 Subject: sparc: fix sparse warnings in smp_32.c + smp_64.c Fix following warnings: smp_32.c:177:5: warning: symbol 'setup_profiling_timer' was not declared. Should it be static? smp_64.c:1202:5: warning: symbol 'setup_profiling_timer' was not declared. Should it be static? smp_64.c:989:6: warning: symbol 'kgdb_roundup_cpus' was not declared. Should it be static? Add prototype to include/linux/profile.h of setup_profiling_timer Add missing include to smp_64.c Signed-off-by: Sam Ravnborg Signed-off-by: David S. Miller --- arch/sparc/kernel/smp_32.c | 1 + arch/sparc/kernel/smp_64.c | 1 + include/linux/profile.h | 1 + 3 files changed, 3 insertions(+) (limited to 'include') diff --git a/arch/sparc/kernel/smp_32.c b/arch/sparc/kernel/smp_32.c index 9d3297d8d730..7958242d63c5 100644 --- a/arch/sparc/kernel/smp_32.c +++ b/arch/sparc/kernel/smp_32.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c index df91e78dbd95..afc71bf719b1 100644 --- a/arch/sparc/kernel/smp_64.c +++ b/arch/sparc/kernel/smp_64.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/include/linux/profile.h b/include/linux/profile.h index aaad3861beb8..b537a25ffa17 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -44,6 +44,7 @@ extern int prof_on __read_mostly; int profile_init(void); int profile_setup(char *str); void profile_tick(int type); +int setup_profiling_timer(unsigned int multiplier); /* * Add multiple profiler hits to a given address: -- cgit v1.2.3 From 3b36fbb01dc50f58e7803006f5a99683daf26c8c Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Sun, 18 May 2014 22:44:35 -0700 Subject: Input: pixcir_i2c_ts - initialize interrupt mode and power mode Introduce helper functions to configure power and interrupt registers. Default to IDLE mode on probe as device supports auto wakeup to ACVIE mode on detecting finger touch. Configure interrupt mode and polarity on start up. Power down on device closure or module removal. Signed-off-by: Roger Quadros Acked-by: Mugunthan V N Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/pixcir_i2c_ts.c | 182 ++++++++++++++++++++++++++++-- include/linux/input/pixcir_ts.h | 42 +++++++ 2 files changed, 216 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 5d36243bdc79..6c6f6dacb858 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -29,7 +29,7 @@ struct pixcir_i2c_ts_data { struct i2c_client *client; struct input_dev *input; const struct pixcir_ts_platform_data *chip; - bool exiting; + bool running; }; static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) @@ -88,7 +88,7 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) { struct pixcir_i2c_ts_data *tsdata = dev_id; - while (!tsdata->exiting) { + while (tsdata->running) { pixcir_ts_poscheck(tsdata); if (tsdata->chip->attb_read_val()) @@ -100,6 +100,164 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, + enum pixcir_power_mode mode) +{ + struct device *dev = &ts->client->dev; + int ret; + + ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE); + if (ret < 0) { + dev_err(dev, "%s: can't read reg 0x%x : %d\n", + __func__, PIXCIR_REG_POWER_MODE, ret); + return ret; + } + + ret &= ~PIXCIR_POWER_MODE_MASK; + ret |= mode; + + /* Always AUTO_IDLE */ + ret |= PIXCIR_POWER_ALLOW_IDLE; + + ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_POWER_MODE, ret); + if (ret < 0) { + dev_err(dev, "%s: can't write reg 0x%x : %d\n", + __func__, PIXCIR_REG_POWER_MODE, ret); + return ret; + } + + return 0; +} + +/* + * Set the interrupt mode for the device i.e. ATTB line behaviour + * + * @polarity : 1 for active high, 0 for active low. + */ +static int pixcir_set_int_mode(struct pixcir_i2c_ts_data *ts, + enum pixcir_int_mode mode, bool polarity) +{ + struct device *dev = &ts->client->dev; + int ret; + + ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE); + if (ret < 0) { + dev_err(dev, "%s: can't read reg 0x%x : %d\n", + __func__, PIXCIR_REG_INT_MODE, ret); + return ret; + } + + ret &= ~PIXCIR_INT_MODE_MASK; + ret |= mode; + + if (polarity) + ret |= PIXCIR_INT_POL_HIGH; + else + ret &= ~PIXCIR_INT_POL_HIGH; + + ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret); + if (ret < 0) { + dev_err(dev, "%s: can't write reg 0x%x : %d\n", + __func__, PIXCIR_REG_INT_MODE, ret); + return ret; + } + + return 0; +} + +/* + * Enable/disable interrupt generation + */ +static int pixcir_int_enable(struct pixcir_i2c_ts_data *ts, bool enable) +{ + struct device *dev = &ts->client->dev; + int ret; + + ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE); + if (ret < 0) { + dev_err(dev, "%s: can't read reg 0x%x : %d\n", + __func__, PIXCIR_REG_INT_MODE, ret); + return ret; + } + + if (enable) + ret |= PIXCIR_INT_ENABLE; + else + ret &= ~PIXCIR_INT_ENABLE; + + ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret); + if (ret < 0) { + dev_err(dev, "%s: can't write reg 0x%x : %d\n", + __func__, PIXCIR_REG_INT_MODE, ret); + return ret; + } + + return 0; +} + +static int pixcir_start(struct pixcir_i2c_ts_data *ts) +{ + struct device *dev = &ts->client->dev; + int error; + + /* LEVEL_TOUCH interrupt with active low polarity */ + error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0); + if (error) { + dev_err(dev, "Failed to set interrupt mode: %d\n", error); + return error; + } + + ts->running = true; + mb(); /* Update status before IRQ can fire */ + + /* enable interrupt generation */ + error = pixcir_int_enable(ts, true); + if (error) { + dev_err(dev, "Failed to enable interrupt generation: %d\n", + error); + return error; + } + + return 0; +} + +static int pixcir_stop(struct pixcir_i2c_ts_data *ts) +{ + int error; + + /* Disable interrupt generation */ + error = pixcir_int_enable(ts, false); + if (error) { + dev_err(&ts->client->dev, + "Failed to disable interrupt generation: %d\n", + error); + return error; + } + + /* Exit ISR if running, no more report parsing */ + ts->running = false; + mb(); /* update status before we synchronize irq */ + + /* Wait till running ISR is complete */ + synchronize_irq(ts->client->irq); + + return 0; +} + +static int pixcir_input_open(struct input_dev *dev) +{ + struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev); + + return pixcir_start(ts); +} + +static void pixcir_input_close(struct input_dev *dev) +{ + struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev); + + pixcir_stop(ts); +} + #ifdef CONFIG_PM_SLEEP static int pixcir_i2c_ts_suspend(struct device *dev) { @@ -156,6 +314,8 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input->name = client->name; input->id.bustype = BUS_I2C; + input->open = pixcir_input_open; + input->close = pixcir_input_close; input->dev.parent = &client->dev; __set_bit(EV_KEY, input->evbit); @@ -176,11 +336,22 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return error; } + /* Always be in IDLE mode to save power, device supports auto wake */ + error = pixcir_set_power_mode(tsdata, PIXCIR_POWER_IDLE); + if (error) { + dev_err(dev, "Failed to set IDLE mode\n"); + return error; + } + + /* Stop device till opened */ + error = pixcir_stop(tsdata); + if (error) + return error; + error = input_register_device(input); if (error) return error; - i2c_set_clientdata(client, tsdata); device_init_wakeup(&client->dev, 1); return 0; @@ -188,13 +359,8 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, static int pixcir_i2c_ts_remove(struct i2c_client *client) { - struct pixcir_i2c_ts_data *tsdata = i2c_get_clientdata(client); - device_init_wakeup(&client->dev, 0); - tsdata->exiting = true; - mb(); - return 0; } diff --git a/include/linux/input/pixcir_ts.h b/include/linux/input/pixcir_ts.h index 7163d91c0373..7942804464d3 100644 --- a/include/linux/input/pixcir_ts.h +++ b/include/linux/input/pixcir_ts.h @@ -1,6 +1,48 @@ #ifndef _PIXCIR_I2C_TS_H #define _PIXCIR_I2C_TS_H +/* + * Register map + */ +#define PIXCIR_REG_POWER_MODE 51 +#define PIXCIR_REG_INT_MODE 52 + +/* + * Power modes: + * active: max scan speed + * idle: lower scan speed with automatic transition to active on touch + * halt: datasheet says sleep but this is more like halt as the chip + * clocks are cut and it can only be brought out of this mode + * using the RESET pin. + */ +enum pixcir_power_mode { + PIXCIR_POWER_ACTIVE, + PIXCIR_POWER_IDLE, + PIXCIR_POWER_HALT, +}; + +#define PIXCIR_POWER_MODE_MASK 0x03 +#define PIXCIR_POWER_ALLOW_IDLE (1UL << 2) + +/* + * Interrupt modes: + * periodical: interrupt is asserted periodicaly + * diff coordinates: interrupt is asserted when coordinates change + * level on touch: interrupt level asserted during touch + * pulse on touch: interrupt pulse asserted druing touch + * + */ +enum pixcir_int_mode { + PIXCIR_INT_PERIODICAL, + PIXCIR_INT_DIFF_COORD, + PIXCIR_INT_LEVEL_TOUCH, + PIXCIR_INT_PULSE_TOUCH, +}; + +#define PIXCIR_INT_MODE_MASK 0x03 +#define PIXCIR_INT_ENABLE (1UL << 3) +#define PIXCIR_INT_POL_HIGH (1UL << 2) + struct pixcir_ts_platform_data { int (*attb_read_val)(void); int x_max; -- cgit v1.2.3 From 0dfc8d41bfa091a61354eea73199a5af0eaae9c0 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Sun, 18 May 2014 22:46:43 -0700 Subject: Input: pixcir_i2c_ts - get rid of pdata->attb_read_val() Get rid of the attb_read_val() platform hook. Instead, read the ATTB gpio directly from the driver. Fail if valid ATTB gpio is not provided by patform data. Signed-off-by: Roger Quadros Acked-by: Mugunthan V N Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/pixcir_i2c_ts.c | 16 +++++++++++++++- include/linux/input/pixcir_ts.h | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 6c6f6dacb858..f2c0ae18e24a 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -24,6 +24,7 @@ #include #include #include +#include struct pixcir_i2c_ts_data { struct i2c_client *client; @@ -87,11 +88,12 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) { struct pixcir_i2c_ts_data *tsdata = dev_id; + const struct pixcir_ts_platform_data *pdata = tsdata->chip; while (tsdata->running) { pixcir_ts_poscheck(tsdata); - if (tsdata->chip->attb_read_val()) + if (gpio_get_value(pdata->gpio_attb)) break; msleep(20); @@ -298,6 +300,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return -EINVAL; } + if (!gpio_is_valid(pdata->gpio_attb)) { + dev_err(dev, "Invalid gpio_attb in pdata\n"); + return -EINVAL; + } + tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); if (!tsdata) return -ENOMEM; @@ -328,6 +335,13 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input_set_drvdata(input, tsdata); + error = devm_gpio_request_one(dev, pdata->gpio_attb, + GPIOF_DIR_IN, "pixcir_i2c_attb"); + if (error) { + dev_err(dev, "Failed to request ATTB gpio\n"); + return error; + } + error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, tsdata); diff --git a/include/linux/input/pixcir_ts.h b/include/linux/input/pixcir_ts.h index 7942804464d3..160cf353aa39 100644 --- a/include/linux/input/pixcir_ts.h +++ b/include/linux/input/pixcir_ts.h @@ -44,9 +44,9 @@ enum pixcir_int_mode { #define PIXCIR_INT_POL_HIGH (1UL << 2) struct pixcir_ts_platform_data { - int (*attb_read_val)(void); int x_max; int y_max; + int gpio_attb; /* GPIO connected to ATTB line */ }; #endif -- cgit v1.2.3 From 2cefdb1f0a27150755ef2730bafc58bf2ed16571 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sun, 18 May 2014 22:59:20 -0700 Subject: Input: atmel_mxt_ts - remove unnecessary platform data It is not necessary to download these values to the maXTouch chip on every probe, since they are stored in NVRAM. It makes life difficult when tuning the device to keep them in sync with the config array/file, and requires a new kernel build for minor tweaks. These parameters only represent a tiny subset of the available configuration options, tracking all of these options in platform data would be a endless task. In addition, different versions of maXTouch chips may have these values in different places or may not even have them at all. Having these values also makes life more complex for device tree and other platforms where having to define a static configuration isn't helpful. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- arch/arm/mach-s5pv210/mach-goni.c | 5 ---- drivers/input/touchscreen/atmel_mxt_ts.c | 50 ------------------------------- drivers/platform/chrome/chromeos_laptop.c | 10 ------- include/linux/i2c/atmel_mxt_ts.h | 6 +--- 4 files changed, 1 insertion(+), 70 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index b41a38a75844..e549ecf0e5dc 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -239,13 +239,8 @@ static void __init goni_radio_init(void) /* TSP */ static struct mxt_platform_data qt602240_platform_data = { - .x_line = 17, - .y_line = 11, .x_size = 800, .y_size = 480, - .blen = 0x21, - .threshold = 0x28, - .voltage = 2800000, /* 2.8V */ .orient = MXT_DIAGONAL, .irqflags = IRQF_TRIGGER_FALLING, }; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a70400754e92..7eb515caf215 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -685,54 +685,6 @@ static int mxt_make_highchg(struct mxt_data *data) return 0; } -static void mxt_handle_pdata(struct mxt_data *data) -{ - const struct mxt_platform_data *pdata = data->pdata; - u8 voltage; - - /* Set touchscreen lines */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE, - pdata->x_line); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE, - pdata->y_line); - - /* Set touchscreen orient */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT, - pdata->orient); - - /* Set touchscreen burst length */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_BLEN, pdata->blen); - - /* Set touchscreen threshold */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_TCHTHR, pdata->threshold); - - /* Set touchscreen resolution */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); - - /* Set touchscreen voltage */ - if (pdata->voltage) { - if (pdata->voltage < MXT_VOLTAGE_DEFAULT) { - voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) / - MXT_VOLTAGE_STEP; - voltage = 0xff - voltage + 1; - } else - voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) / - MXT_VOLTAGE_STEP; - - mxt_write_object(data, MXT_SPT_CTECONFIG_T28, - MXT_CTE_VOLTAGE, voltage); - } -} - static int mxt_get_info(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -840,8 +792,6 @@ static int mxt_initialize(struct mxt_data *data) if (error) goto err_free_object_table; - mxt_handle_pdata(data); - /* Backup to memory */ mxt_write_object(data, MXT_GEN_COMMAND_T6, MXT_COMMAND_BACKUPNV, diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 7f3aad0e115c..2559a0407c58 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -85,13 +85,8 @@ static struct i2c_board_info tsl2563_als_device = { }; static struct mxt_platform_data atmel_224s_tp_platform_data = { - .x_line = 18, - .y_line = 12, .x_size = 102*20, .y_size = 68*20, - .blen = 0x80, /* Gain setting is in upper 4 bits */ - .threshold = 0x32, - .voltage = 0, /* 3.3V */ .orient = MXT_VERTICAL_FLIP, .irqflags = IRQF_TRIGGER_FALLING, .is_tp = true, @@ -110,13 +105,8 @@ static struct i2c_board_info atmel_224s_tp_device = { }; static struct mxt_platform_data atmel_1664s_platform_data = { - .x_line = 32, - .y_line = 50, .x_size = 1700, .y_size = 2560, - .blen = 0x89, /* Gain setting is in upper 4 bits */ - .threshold = 0x28, - .voltage = 0, /* 3.3V */ .orient = MXT_ROTATED_90_COUNTER, .irqflags = IRQF_TRIGGER_FALLING, .is_tp = false, diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 99e379b74398..eff0cdc08843 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -33,14 +33,10 @@ struct mxt_platform_data { const u8 *config; size_t config_length; - unsigned int x_line; - unsigned int y_line; unsigned int x_size; unsigned int y_size; - unsigned int blen; - unsigned int threshold; - unsigned int voltage; unsigned char orient; + unsigned long irqflags; bool is_tp; const unsigned int key_map[MXT_NUM_GPIO]; -- cgit v1.2.3 From fb5e4c3ee140b29e1935b4bbb19c319177bed231 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sun, 18 May 2014 23:00:15 -0700 Subject: Input: atmel_mxt_ts - improve T19 GPIO keys handling * The mapping of the GPIO numbers into the T19 status byte varies between different maXTouch chips. Some have up to 7 GPIOs. Allowing a keycode array of up to 8 items is simpler and more generic. So replace #define with configurable number of keys which also allows the removal of is_tp. * Rename platform data parameters to include "t19" to prevent confusion with T15 key array. * Probe aborts early on when pdata is NULL, so no need to check. * Move "int i" to beginning of function (mixed declarations and code) * Use API calls rather than __set_bit() * Remove unused dev variable. Signed-off-by: Nick Dyer Acked-by: Yufeng Shen Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 44 ++++++++++++------------------- drivers/platform/chrome/chromeos_laptop.c | 17 +++++++----- include/linux/i2c/atmel_mxt_ts.h | 7 ++--- 3 files changed, 30 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7eb515caf215..65df362cf327 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -180,12 +180,6 @@ #define MXT_FWRESET_TIME 175 /* msec */ -/* MXT_SPT_GPIOPWM_T19 field */ -#define MXT_GPIO0_MASK 0x04 -#define MXT_GPIO1_MASK 0x08 -#define MXT_GPIO2_MASK 0x10 -#define MXT_GPIO3_MASK 0x20 - /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa #define MXT_UNLOCK_CMD_LSB 0xdc @@ -250,7 +244,6 @@ struct mxt_data { const struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info info; - bool is_tp; unsigned int irq; unsigned int max_x; @@ -515,15 +508,16 @@ static int mxt_write_object(struct mxt_data *data, static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) { struct input_dev *input = data->input_dev; + const struct mxt_platform_data *pdata = data->pdata; bool button; int i; /* Active-low switch */ - for (i = 0; i < MXT_NUM_GPIO; i++) { - if (data->pdata->key_map[i] == KEY_RESERVED) + for (i = 0; i < pdata->t19_num_keys; i++) { + if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message->message[0] & MXT_GPIO0_MASK << i); - input_report_key(input, data->pdata->key_map[i], button); + button = !(message->message[0] & (1 << i)); + input_report_key(input, pdata->t19_keymap[i], button); } } @@ -1084,6 +1078,8 @@ static int mxt_probe(struct i2c_client *client, struct input_dev *input_dev; int error; unsigned int num_mt_slots; + unsigned int mt_flags = 0; + int i; if (!pdata) return -EINVAL; @@ -1096,10 +1092,7 @@ static int mxt_probe(struct i2c_client *client, goto err_free_mem; } - data->is_tp = pdata && pdata->is_tp; - - input_dev->name = (data->is_tp) ? "Atmel maXTouch Touchpad" : - "Atmel maXTouch Touchscreen"; + input_dev->name = "Atmel maXTouch Touchscreen"; snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); @@ -1125,20 +1118,15 @@ static int mxt_probe(struct i2c_client *client, __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); - if (data->is_tp) { - int i; - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + if (pdata->t19_num_keys) { __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - for (i = 0; i < MXT_NUM_GPIO; i++) - if (pdata->key_map[i] != KEY_RESERVED) - __set_bit(pdata->key_map[i], input_dev->keybit); + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); - __set_bit(BTN_TOOL_FINGER, input_dev->keybit); - __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); - __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); - __set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit); + mt_flags |= INPUT_MT_POINTER; input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); @@ -1146,6 +1134,8 @@ static int mxt_probe(struct i2c_client *client, MXT_PIXELS_PER_MM); input_abs_set_res(input_dev, ABS_MT_POSITION_Y, MXT_PIXELS_PER_MM); + + input_dev->name = "Atmel maXTouch Touchpad"; } /* For single touch */ @@ -1158,7 +1148,7 @@ static int mxt_probe(struct i2c_client *client, /* For multi touch */ num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; - error = input_mt_init_slots(input_dev, num_mt_slots, 0); + error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) goto err_free_object; input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 2559a0407c58..8b7523ab62e5 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -84,16 +84,22 @@ static struct i2c_board_info tsl2563_als_device = { I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), }; +static int mxt_t19_keys[] = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + BTN_LEFT +}; + static struct mxt_platform_data atmel_224s_tp_platform_data = { .x_size = 102*20, .y_size = 68*20, .orient = MXT_VERTICAL_FLIP, .irqflags = IRQF_TRIGGER_FALLING, - .is_tp = true, - .key_map = { KEY_RESERVED, - KEY_RESERVED, - KEY_RESERVED, - BTN_LEFT }, + .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), + .t19_keymap = mxt_t19_keys, .config = NULL, .config_length = 0, }; @@ -109,7 +115,6 @@ static struct mxt_platform_data atmel_1664s_platform_data = { .y_size = 2560, .orient = MXT_ROTATED_90_COUNTER, .irqflags = IRQF_TRIGGER_FALLING, - .is_tp = false, .config = NULL, .config_length = 0, }; diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index eff0cdc08843..d26080dc606c 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -15,9 +15,6 @@ #include -/* For key_map array */ -#define MXT_NUM_GPIO 4 - /* Orient */ #define MXT_NORMAL 0x0 #define MXT_DIAGONAL 0x1 @@ -38,8 +35,8 @@ struct mxt_platform_data { unsigned char orient; unsigned long irqflags; - bool is_tp; - const unsigned int key_map[MXT_NUM_GPIO]; + u8 t19_num_keys; + const unsigned int *t19_keymap; }; #endif /* __LINUX_ATMEL_MXT_TS_H */ -- cgit v1.2.3 From c3f78043d5aea39205a14c580babd87fbdcfa148 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sun, 18 May 2014 23:04:46 -0700 Subject: Input: atmel_mxt_ts - implement CRC check for configuration data The configuration is stored in NVRAM on the maXTouch chip. When the device is reset it reports a CRC of the stored configuration values. Therefore it isn't necessary to send the configuration on each probe - we can check the CRC matches and avoid a timeconsuming backup/reset cycle. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 60 +++++++++++++++++++++++++++----- include/linux/i2c/atmel_mxt_ts.h | 1 + 2 files changed, 53 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 278f364ec6e1..61f9ef221d12 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -188,6 +188,7 @@ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ #define MXT_RESET_TIMEOUT 3000 /* msec */ +#define MXT_CRC_TIMEOUT 1000 /* msec */ #define MXT_FW_RESET_TIME 3000 /* msec */ #define MXT_FW_CHG_TIMEOUT 300 /* msec */ @@ -259,6 +260,7 @@ struct mxt_data { unsigned int max_x; unsigned int max_y; bool in_bootloader; + u32 config_crc; /* Cached parameters from object table */ u8 T6_reportid; @@ -272,6 +274,9 @@ struct mxt_data { /* for reset handling */ struct completion reset_completion; + + /* for config update handling */ + struct completion crc_completion; }; static size_t mxt_obj_size(const struct mxt_object *obj) @@ -636,7 +641,7 @@ static void mxt_input_touchevent(struct mxt_data *data, } } -static unsigned mxt_extract_T6_csum(const u8 *csum) +static u16 mxt_extract_T6_csum(const u8 *csum) { return csum[0] | (csum[1] << 8) | (csum[2] << 16); } @@ -654,6 +659,7 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) struct device *dev = &data->client->dev; u8 reportid; bool update_input = false; + u32 crc; do { if (mxt_read_message(data, &message)) { @@ -665,9 +671,15 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) if (reportid == data->T6_reportid) { u8 status = payload[0]; - unsigned csum = mxt_extract_T6_csum(&payload[1]); + + crc = mxt_extract_T6_csum(&payload[1]); + if (crc != data->config_crc) { + data->config_crc = crc; + complete(&data->crc_completion); + } + dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", - status, csum); + status, data->config_crc); if (status & MXT_T6_STATUS_RESET) complete(&data->reset_completion); @@ -757,6 +769,24 @@ static int mxt_soft_reset(struct mxt_data *data) return 0; } +static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) +{ + /* + * On failure, CRC is set to 0 and config will always be + * downloaded. + */ + data->config_crc = 0; + reinit_completion(&data->crc_completion); + + mxt_t6_command(data, cmd, value, true); + + /* + * Wait for crc message. On failure, CRC is set to 0 and config will + * always be downloaded. + */ + mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); +} + static int mxt_check_reg_init(struct mxt_data *data) { const struct mxt_platform_data *pdata = data->pdata; @@ -771,6 +801,16 @@ static int mxt_check_reg_init(struct mxt_data *data) return 0; } + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + + if (data->config_crc == pdata->config_crc) { + dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); + return 0; + } + + dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", + data->config_crc, pdata->config_crc); + for (i = 0; i < data->info.object_num; i++) { object = data->object_table + i; @@ -790,6 +830,14 @@ static int mxt_check_reg_init(struct mxt_data *data) index += size; } + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + + ret = mxt_soft_reset(data); + if (ret) + return ret; + + dev_info(dev, "Config successfully updated\n"); + return 0; } @@ -929,11 +977,6 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; } - error = mxt_t6_command(data, MXT_COMMAND_BACKUPNV, - MXT_BACKUP_VALUE, false); - if (!error) - mxt_soft_reset(data); - /* Update matrix size at info struct */ error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); if (error) @@ -1263,6 +1306,7 @@ static int mxt_probe(struct i2c_client *client, init_completion(&data->bl_completion); init_completion(&data->reset_completion); + init_completion(&data->crc_completion); mxt_calc_resolution(data); diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index d26080dc606c..9f92135b6620 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -29,6 +29,7 @@ struct mxt_platform_data { const u8 *config; size_t config_length; + u32 config_crc; unsigned int x_size; unsigned int y_size; -- cgit v1.2.3 From fd1159318e55e901cf269de90163b19fd62938cb Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 17 May 2014 00:03:54 +0400 Subject: can: add Renesas R-Car CAN driver Add support for the CAN controller found in Renesas R-Car SoCs. Signed-off-by: Sergei Shtylyov Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 10 + drivers/net/can/Makefile | 1 + drivers/net/can/rcar_can.c | 876 ++++++++++++++++++++++++++++++++++ include/linux/can/platform/rcar_can.h | 17 + 4 files changed, 904 insertions(+) create mode 100644 drivers/net/can/rcar_can.c create mode 100644 include/linux/can/platform/rcar_can.h (limited to 'include') diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index ac67afa7735c..714b18790caf 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -119,6 +119,16 @@ config CAN_GRCAN endian syntheses of the cores would need some modifications on the hardware level to work. +config CAN_RCAR + tristate "Renesas R-Car CAN controller" + depends on ARM + ---help--- + Say Y here if you want to use CAN controller found on Renesas R-Car + SoCs. + + To compile this driver as a module, choose M here: the module will + be called rcar_can. + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index c42058868b0f..90f538c73f8c 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -25,5 +25,6 @@ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_PCH_CAN) += pch_can.o obj-$(CONFIG_CAN_GRCAN) += grcan.o +obj-$(CONFIG_CAN_RCAR) += rcar_can.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/rcar_can.c b/drivers/net/can/rcar_can.c new file mode 100644 index 000000000000..5268d216ecfa --- /dev/null +++ b/drivers/net/can/rcar_can.c @@ -0,0 +1,876 @@ +/* Renesas R-Car CAN device driver + * + * Copyright (C) 2013 Cogent Embedded, Inc. + * Copyright (C) 2013 Renesas Solutions Corp. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RCAR_CAN_DRV_NAME "rcar_can" + +/* Mailbox configuration: + * mailbox 60 - 63 - Rx FIFO mailboxes + * mailbox 56 - 59 - Tx FIFO mailboxes + * non-FIFO mailboxes are not used + */ +#define RCAR_CAN_N_MBX 64 /* Number of mailboxes in non-FIFO mode */ +#define RCAR_CAN_RX_FIFO_MBX 60 /* Mailbox - window to Rx FIFO */ +#define RCAR_CAN_TX_FIFO_MBX 56 /* Mailbox - window to Tx FIFO */ +#define RCAR_CAN_FIFO_DEPTH 4 + +/* Mailbox registers structure */ +struct rcar_can_mbox_regs { + u32 id; /* IDE and RTR bits, SID and EID */ + u8 stub; /* Not used */ + u8 dlc; /* Data Length Code - bits [0..3] */ + u8 data[8]; /* Data Bytes */ + u8 tsh; /* Time Stamp Higher Byte */ + u8 tsl; /* Time Stamp Lower Byte */ +}; + +struct rcar_can_regs { + struct rcar_can_mbox_regs mb[RCAR_CAN_N_MBX]; /* Mailbox registers */ + u32 mkr_2_9[8]; /* Mask Registers 2-9 */ + u32 fidcr[2]; /* FIFO Received ID Compare Register */ + u32 mkivlr1; /* Mask Invalid Register 1 */ + u32 mier1; /* Mailbox Interrupt Enable Register 1 */ + u32 mkr_0_1[2]; /* Mask Registers 0-1 */ + u32 mkivlr0; /* Mask Invalid Register 0*/ + u32 mier0; /* Mailbox Interrupt Enable Register 0 */ + u8 pad_440[0x3c0]; + u8 mctl[64]; /* Message Control Registers */ + u16 ctlr; /* Control Register */ + u16 str; /* Status register */ + u8 bcr[3]; /* Bit Configuration Register */ + u8 clkr; /* Clock Select Register */ + u8 rfcr; /* Receive FIFO Control Register */ + u8 rfpcr; /* Receive FIFO Pointer Control Register */ + u8 tfcr; /* Transmit FIFO Control Register */ + u8 tfpcr; /* Transmit FIFO Pointer Control Register */ + u8 eier; /* Error Interrupt Enable Register */ + u8 eifr; /* Error Interrupt Factor Judge Register */ + u8 recr; /* Receive Error Count Register */ + u8 tecr; /* Transmit Error Count Register */ + u8 ecsr; /* Error Code Store Register */ + u8 cssr; /* Channel Search Support Register */ + u8 mssr; /* Mailbox Search Status Register */ + u8 msmr; /* Mailbox Search Mode Register */ + u16 tsr; /* Time Stamp Register */ + u8 afsr; /* Acceptance Filter Support Register */ + u8 pad_857; + u8 tcr; /* Test Control Register */ + u8 pad_859[7]; + u8 ier; /* Interrupt Enable Register */ + u8 isr; /* Interrupt Status Register */ + u8 pad_862; + u8 mbsmr; /* Mailbox Search Mask Register */ +}; + +struct rcar_can_priv { + struct can_priv can; /* Must be the first member! */ + struct net_device *ndev; + struct napi_struct napi; + struct rcar_can_regs __iomem *regs; + struct clk *clk; + u8 tx_dlc[RCAR_CAN_FIFO_DEPTH]; + u32 tx_head; + u32 tx_tail; + u8 clock_select; + u8 ier; +}; + +static const struct can_bittiming_const rcar_can_bittiming_const = { + .name = RCAR_CAN_DRV_NAME, + .tseg1_min = 4, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +/* Control Register bits */ +#define RCAR_CAN_CTLR_BOM (3 << 11) /* Bus-Off Recovery Mode Bits */ +#define RCAR_CAN_CTLR_BOM_ENT (1 << 11) /* Entry to halt mode */ + /* at bus-off entry */ +#define RCAR_CAN_CTLR_SLPM (1 << 10) +#define RCAR_CAN_CTLR_CANM (3 << 8) /* Operating Mode Select Bit */ +#define RCAR_CAN_CTLR_CANM_HALT (1 << 9) +#define RCAR_CAN_CTLR_CANM_RESET (1 << 8) +#define RCAR_CAN_CTLR_CANM_FORCE_RESET (3 << 8) +#define RCAR_CAN_CTLR_MLM (1 << 3) /* Message Lost Mode Select */ +#define RCAR_CAN_CTLR_IDFM (3 << 1) /* ID Format Mode Select Bits */ +#define RCAR_CAN_CTLR_IDFM_MIXED (1 << 2) /* Mixed ID mode */ +#define RCAR_CAN_CTLR_MBM (1 << 0) /* Mailbox Mode select */ + +/* Status Register bits */ +#define RCAR_CAN_STR_RSTST (1 << 8) /* Reset Status Bit */ + +/* FIFO Received ID Compare Registers 0 and 1 bits */ +#define RCAR_CAN_FIDCR_IDE (1 << 31) /* ID Extension Bit */ +#define RCAR_CAN_FIDCR_RTR (1 << 30) /* Remote Transmission Request Bit */ + +/* Receive FIFO Control Register bits */ +#define RCAR_CAN_RFCR_RFEST (1 << 7) /* Receive FIFO Empty Status Flag */ +#define RCAR_CAN_RFCR_RFE (1 << 0) /* Receive FIFO Enable */ + +/* Transmit FIFO Control Register bits */ +#define RCAR_CAN_TFCR_TFUST (7 << 1) /* Transmit FIFO Unsent Message */ + /* Number Status Bits */ +#define RCAR_CAN_TFCR_TFUST_SHIFT 1 /* Offset of Transmit FIFO Unsent */ + /* Message Number Status Bits */ +#define RCAR_CAN_TFCR_TFE (1 << 0) /* Transmit FIFO Enable */ + +#define RCAR_CAN_N_RX_MKREGS1 2 /* Number of mask registers */ + /* for Rx mailboxes 0-31 */ +#define RCAR_CAN_N_RX_MKREGS2 8 + +/* Bit Configuration Register settings */ +#define RCAR_CAN_BCR_TSEG1(x) (((x) & 0x0f) << 20) +#define RCAR_CAN_BCR_BPR(x) (((x) & 0x3ff) << 8) +#define RCAR_CAN_BCR_SJW(x) (((x) & 0x3) << 4) +#define RCAR_CAN_BCR_TSEG2(x) ((x) & 0x07) + +/* Mailbox and Mask Registers bits */ +#define RCAR_CAN_IDE (1 << 31) +#define RCAR_CAN_RTR (1 << 30) +#define RCAR_CAN_SID_SHIFT 18 + +/* Mailbox Interrupt Enable Register 1 bits */ +#define RCAR_CAN_MIER1_RXFIE (1 << 28) /* Receive FIFO Interrupt Enable */ +#define RCAR_CAN_MIER1_TXFIE (1 << 24) /* Transmit FIFO Interrupt Enable */ + +/* Interrupt Enable Register bits */ +#define RCAR_CAN_IER_ERSIE (1 << 5) /* Error (ERS) Interrupt Enable Bit */ +#define RCAR_CAN_IER_RXFIE (1 << 4) /* Reception FIFO Interrupt */ + /* Enable Bit */ +#define RCAR_CAN_IER_TXFIE (1 << 3) /* Transmission FIFO Interrupt */ + /* Enable Bit */ +/* Interrupt Status Register bits */ +#define RCAR_CAN_ISR_ERSF (1 << 5) /* Error (ERS) Interrupt Status Bit */ +#define RCAR_CAN_ISR_RXFF (1 << 4) /* Reception FIFO Interrupt */ + /* Status Bit */ +#define RCAR_CAN_ISR_TXFF (1 << 3) /* Transmission FIFO Interrupt */ + /* Status Bit */ + +/* Error Interrupt Enable Register bits */ +#define RCAR_CAN_EIER_BLIE (1 << 7) /* Bus Lock Interrupt Enable */ +#define RCAR_CAN_EIER_OLIE (1 << 6) /* Overload Frame Transmit */ + /* Interrupt Enable */ +#define RCAR_CAN_EIER_ORIE (1 << 5) /* Receive Overrun Interrupt Enable */ +#define RCAR_CAN_EIER_BORIE (1 << 4) /* Bus-Off Recovery Interrupt Enable */ +#define RCAR_CAN_EIER_BOEIE (1 << 3) /* Bus-Off Entry Interrupt Enable */ +#define RCAR_CAN_EIER_EPIE (1 << 2) /* Error Passive Interrupt Enable */ +#define RCAR_CAN_EIER_EWIE (1 << 1) /* Error Warning Interrupt Enable */ +#define RCAR_CAN_EIER_BEIE (1 << 0) /* Bus Error Interrupt Enable */ + +/* Error Interrupt Factor Judge Register bits */ +#define RCAR_CAN_EIFR_BLIF (1 << 7) /* Bus Lock Detect Flag */ +#define RCAR_CAN_EIFR_OLIF (1 << 6) /* Overload Frame Transmission */ + /* Detect Flag */ +#define RCAR_CAN_EIFR_ORIF (1 << 5) /* Receive Overrun Detect Flag */ +#define RCAR_CAN_EIFR_BORIF (1 << 4) /* Bus-Off Recovery Detect Flag */ +#define RCAR_CAN_EIFR_BOEIF (1 << 3) /* Bus-Off Entry Detect Flag */ +#define RCAR_CAN_EIFR_EPIF (1 << 2) /* Error Passive Detect Flag */ +#define RCAR_CAN_EIFR_EWIF (1 << 1) /* Error Warning Detect Flag */ +#define RCAR_CAN_EIFR_BEIF (1 << 0) /* Bus Error Detect Flag */ + +/* Error Code Store Register bits */ +#define RCAR_CAN_ECSR_EDPM (1 << 7) /* Error Display Mode Select Bit */ +#define RCAR_CAN_ECSR_ADEF (1 << 6) /* ACK Delimiter Error Flag */ +#define RCAR_CAN_ECSR_BE0F (1 << 5) /* Bit Error (dominant) Flag */ +#define RCAR_CAN_ECSR_BE1F (1 << 4) /* Bit Error (recessive) Flag */ +#define RCAR_CAN_ECSR_CEF (1 << 3) /* CRC Error Flag */ +#define RCAR_CAN_ECSR_AEF (1 << 2) /* ACK Error Flag */ +#define RCAR_CAN_ECSR_FEF (1 << 1) /* Form Error Flag */ +#define RCAR_CAN_ECSR_SEF (1 << 0) /* Stuff Error Flag */ + +#define RCAR_CAN_NAPI_WEIGHT 4 +#define MAX_STR_READS 0x100 + +static void tx_failure_cleanup(struct net_device *ndev) +{ + int i; + + for (i = 0; i < RCAR_CAN_FIFO_DEPTH; i++) + can_free_echo_skb(ndev, i); +} + +static void rcar_can_error(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 eifr, txerr = 0, rxerr = 0; + + /* Propagate the error condition to the CAN stack */ + skb = alloc_can_err_skb(ndev, &cf); + + eifr = readb(&priv->regs->eifr); + if (eifr & (RCAR_CAN_EIFR_EWIF | RCAR_CAN_EIFR_EPIF)) { + txerr = readb(&priv->regs->tecr); + rxerr = readb(&priv->regs->recr); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } + if (eifr & RCAR_CAN_EIFR_BEIF) { + int rx_errors = 0, tx_errors = 0; + u8 ecsr; + + netdev_dbg(priv->ndev, "Bus error interrupt:\n"); + if (skb) { + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_UNSPEC; + } + ecsr = readb(&priv->regs->ecsr); + if (ecsr & RCAR_CAN_ECSR_ADEF) { + netdev_dbg(priv->ndev, "ACK Delimiter Error\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_ADEF, &priv->regs->ecsr); + if (skb) + cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL; + } + if (ecsr & RCAR_CAN_ECSR_BE0F) { + netdev_dbg(priv->ndev, "Bit Error (dominant)\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_BE0F, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_BIT0; + } + if (ecsr & RCAR_CAN_ECSR_BE1F) { + netdev_dbg(priv->ndev, "Bit Error (recessive)\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_BE1F, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_BIT1; + } + if (ecsr & RCAR_CAN_ECSR_CEF) { + netdev_dbg(priv->ndev, "CRC Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_CEF, &priv->regs->ecsr); + if (skb) + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + if (ecsr & RCAR_CAN_ECSR_AEF) { + netdev_dbg(priv->ndev, "ACK Error\n"); + tx_errors++; + writeb(~RCAR_CAN_ECSR_AEF, &priv->regs->ecsr); + if (skb) { + cf->can_id |= CAN_ERR_ACK; + cf->data[3] |= CAN_ERR_PROT_LOC_ACK; + } + } + if (ecsr & RCAR_CAN_ECSR_FEF) { + netdev_dbg(priv->ndev, "Form Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_FEF, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_FORM; + } + if (ecsr & RCAR_CAN_ECSR_SEF) { + netdev_dbg(priv->ndev, "Stuff Error\n"); + rx_errors++; + writeb(~RCAR_CAN_ECSR_SEF, &priv->regs->ecsr); + if (skb) + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + + priv->can.can_stats.bus_error++; + ndev->stats.rx_errors += rx_errors; + ndev->stats.tx_errors += tx_errors; + writeb(~RCAR_CAN_EIFR_BEIF, &priv->regs->eifr); + } + if (eifr & RCAR_CAN_EIFR_EWIF) { + netdev_dbg(priv->ndev, "Error warning interrupt\n"); + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_EWIF, &priv->regs->eifr); + if (skb) + cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } + if (eifr & RCAR_CAN_EIFR_EPIF) { + netdev_dbg(priv->ndev, "Error passive interrupt\n"); + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_EPIF, &priv->regs->eifr); + if (skb) + cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + if (eifr & RCAR_CAN_EIFR_BOEIF) { + netdev_dbg(priv->ndev, "Bus-off entry interrupt\n"); + tx_failure_cleanup(ndev); + priv->ier = RCAR_CAN_IER_ERSIE; + writeb(priv->ier, &priv->regs->ier); + priv->can.state = CAN_STATE_BUS_OFF; + /* Clear interrupt condition */ + writeb(~RCAR_CAN_EIFR_BOEIF, &priv->regs->eifr); + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + } + if (eifr & RCAR_CAN_EIFR_ORIF) { + netdev_dbg(priv->ndev, "Receive overrun error interrupt\n"); + ndev->stats.rx_over_errors++; + ndev->stats.rx_errors++; + writeb(~RCAR_CAN_EIFR_ORIF, &priv->regs->eifr); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + } + if (eifr & RCAR_CAN_EIFR_OLIF) { + netdev_dbg(priv->ndev, + "Overload Frame Transmission error interrupt\n"); + ndev->stats.rx_over_errors++; + ndev->stats.rx_errors++; + writeb(~RCAR_CAN_EIFR_OLIF, &priv->regs->eifr); + if (skb) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] |= CAN_ERR_PROT_OVERLOAD; + } + } + + if (skb) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +static void rcar_can_tx_done(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u8 isr; + + while (1) { + u8 unsent = readb(&priv->regs->tfcr); + + unsent = (unsent & RCAR_CAN_TFCR_TFUST) >> + RCAR_CAN_TFCR_TFUST_SHIFT; + if (priv->tx_head - priv->tx_tail <= unsent) + break; + stats->tx_packets++; + stats->tx_bytes += priv->tx_dlc[priv->tx_tail % + RCAR_CAN_FIFO_DEPTH]; + priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH] = 0; + can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH); + priv->tx_tail++; + netif_wake_queue(ndev); + } + /* Clear interrupt */ + isr = readb(&priv->regs->isr); + writeb(isr & ~RCAR_CAN_ISR_TXFF, &priv->regs->isr); + can_led_event(ndev, CAN_LED_EVENT_TX); +} + +static irqreturn_t rcar_can_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct rcar_can_priv *priv = netdev_priv(ndev); + u8 isr; + + isr = readb(&priv->regs->isr); + if (!(isr & priv->ier)) + return IRQ_NONE; + + if (isr & RCAR_CAN_ISR_ERSF) + rcar_can_error(ndev); + + if (isr & RCAR_CAN_ISR_TXFF) + rcar_can_tx_done(ndev); + + if (isr & RCAR_CAN_ISR_RXFF) { + if (napi_schedule_prep(&priv->napi)) { + /* Disable Rx FIFO interrupts */ + priv->ier &= ~RCAR_CAN_IER_RXFIE; + writeb(priv->ier, &priv->regs->ier); + __napi_schedule(&priv->napi); + } + } + + return IRQ_HANDLED; +} + +static void rcar_can_set_bittiming(struct net_device *dev) +{ + struct rcar_can_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 bcr; + + bcr = RCAR_CAN_BCR_TSEG1(bt->phase_seg1 + bt->prop_seg - 1) | + RCAR_CAN_BCR_BPR(bt->brp - 1) | RCAR_CAN_BCR_SJW(bt->sjw - 1) | + RCAR_CAN_BCR_TSEG2(bt->phase_seg2 - 1); + /* Don't overwrite CLKR with 32-bit BCR access; CLKR has 8-bit access. + * All the registers are big-endian but they get byte-swapped on 32-bit + * read/write (but not on 8-bit, contrary to the manuals)... + */ + writel((bcr << 8) | priv->clock_select, &priv->regs->bcr); +} + +static void rcar_can_start(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int i; + + /* Set controller to known mode: + * - FIFO mailbox mode + * - accept all messages + * - overrun mode + * CAN is in sleep mode after MCU hardware or software reset. + */ + ctlr = readw(&priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + /* Go to reset mode */ + ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET; + writew(ctlr, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST) + break; + } + rcar_can_set_bittiming(ndev); + ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */ + ctlr |= RCAR_CAN_CTLR_BOM_ENT; /* Entry to halt mode automatically */ + /* at bus-off */ + ctlr |= RCAR_CAN_CTLR_MBM; /* Select FIFO mailbox mode */ + ctlr |= RCAR_CAN_CTLR_MLM; /* Overrun mode */ + writew(ctlr, &priv->regs->ctlr); + + /* Accept all SID and EID */ + writel(0, &priv->regs->mkr_2_9[6]); + writel(0, &priv->regs->mkr_2_9[7]); + /* In FIFO mailbox mode, write "0" to bits 24 to 31 */ + writel(0, &priv->regs->mkivlr1); + /* Accept all frames */ + writel(0, &priv->regs->fidcr[0]); + writel(RCAR_CAN_FIDCR_IDE | RCAR_CAN_FIDCR_RTR, &priv->regs->fidcr[1]); + /* Enable and configure FIFO mailbox interrupts */ + writel(RCAR_CAN_MIER1_RXFIE | RCAR_CAN_MIER1_TXFIE, &priv->regs->mier1); + + priv->ier = RCAR_CAN_IER_ERSIE | RCAR_CAN_IER_RXFIE | + RCAR_CAN_IER_TXFIE; + writeb(priv->ier, &priv->regs->ier); + + /* Accumulate error codes */ + writeb(RCAR_CAN_ECSR_EDPM, &priv->regs->ecsr); + /* Enable error interrupts */ + writeb(RCAR_CAN_EIER_EWIE | RCAR_CAN_EIER_EPIE | RCAR_CAN_EIER_BOEIE | + (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING ? + RCAR_CAN_EIER_BEIE : 0) | RCAR_CAN_EIER_ORIE | + RCAR_CAN_EIER_OLIE, &priv->regs->eier); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* Go to operation mode */ + writew(ctlr & ~RCAR_CAN_CTLR_CANM, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (!(readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)) + break; + } + /* Enable Rx and Tx FIFO */ + writeb(RCAR_CAN_RFCR_RFE, &priv->regs->rfcr); + writeb(RCAR_CAN_TFCR_TFE, &priv->regs->tfcr); +} + +static int rcar_can_open(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(ndev, "clk_prepare_enable() failed, error %d\n", + err); + goto out; + } + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed, error %d\n", err); + goto out_clock; + } + napi_enable(&priv->napi); + err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev); + if (err) { + netdev_err(ndev, "error requesting interrupt %x\n", ndev->irq); + goto out_close; + } + can_led_event(ndev, CAN_LED_EVENT_OPEN); + rcar_can_start(ndev); + netif_start_queue(ndev); + return 0; +out_close: + napi_disable(&priv->napi); + close_candev(ndev); +out_clock: + clk_disable_unprepare(priv->clk); +out: + return err; +} + +static void rcar_can_stop(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int i; + + /* Go to (force) reset mode */ + ctlr = readw(&priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET; + writew(ctlr, &priv->regs->ctlr); + for (i = 0; i < MAX_STR_READS; i++) { + if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST) + break; + } + writel(0, &priv->regs->mier0); + writel(0, &priv->regs->mier1); + writeb(0, &priv->regs->ier); + writeb(0, &priv->regs->eier); + /* Go to sleep mode */ + ctlr |= RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_STOPPED; +} + +static int rcar_can_close(struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + rcar_can_stop(ndev); + free_irq(ndev->irq, ndev); + napi_disable(&priv->napi); + clk_disable_unprepare(priv->clk); + close_candev(ndev); + can_led_event(ndev, CAN_LED_EVENT_STOP); + return 0; +} + +static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct rcar_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + u32 data, i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + data = (cf->can_id & CAN_EFF_MASK) | RCAR_CAN_IDE; + else /* Standard frame format */ + data = (cf->can_id & CAN_SFF_MASK) << RCAR_CAN_SID_SHIFT; + + if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */ + data |= RCAR_CAN_RTR; + } else { + for (i = 0; i < cf->can_dlc; i++) + writeb(cf->data[i], + &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].data[i]); + } + + writel(data, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].id); + + writeb(cf->can_dlc, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc); + + priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->can_dlc; + can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH); + priv->tx_head++; + /* Start Tx: write 0xff to the TFPCR register to increment + * the CPU-side pointer for the transmit FIFO to the next + * mailbox location + */ + writeb(0xff, &priv->regs->tfpcr); + /* Stop the queue if we've filled all FIFO entries */ + if (priv->tx_head - priv->tx_tail >= RCAR_CAN_FIFO_DEPTH) + netif_stop_queue(ndev); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops rcar_can_netdev_ops = { + .ndo_open = rcar_can_open, + .ndo_stop = rcar_can_close, + .ndo_start_xmit = rcar_can_start_xmit, +}; + +static void rcar_can_rx_pkt(struct rcar_can_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 data; + u8 dlc; + + skb = alloc_can_skb(priv->ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + data = readl(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].id); + if (data & RCAR_CAN_IDE) + cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK; + + dlc = readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].dlc); + cf->can_dlc = get_can_dlc(dlc); + if (data & RCAR_CAN_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (dlc = 0; dlc < cf->can_dlc; dlc++) + cf->data[dlc] = + readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].data[dlc]); + } + + can_led_event(priv->ndev, CAN_LED_EVENT_RX); + + stats->rx_bytes += cf->can_dlc; + stats->rx_packets++; + netif_receive_skb(skb); +} + +static int rcar_can_rx_poll(struct napi_struct *napi, int quota) +{ + struct rcar_can_priv *priv = container_of(napi, + struct rcar_can_priv, napi); + int num_pkts; + + for (num_pkts = 0; num_pkts < quota; num_pkts++) { + u8 rfcr, isr; + + isr = readb(&priv->regs->isr); + /* Clear interrupt bit */ + if (isr & RCAR_CAN_ISR_RXFF) + writeb(isr & ~RCAR_CAN_ISR_RXFF, &priv->regs->isr); + rfcr = readb(&priv->regs->rfcr); + if (rfcr & RCAR_CAN_RFCR_RFEST) + break; + rcar_can_rx_pkt(priv); + /* Write 0xff to the RFPCR register to increment + * the CPU-side pointer for the receive FIFO + * to the next mailbox location + */ + writeb(0xff, &priv->regs->rfpcr); + } + /* All packets processed */ + if (num_pkts < quota) { + napi_complete(napi); + priv->ier |= RCAR_CAN_IER_RXFIE; + writeb(priv->ier, &priv->regs->ier); + } + return num_pkts; +} + +static int rcar_can_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + rcar_can_start(ndev); + netif_wake_queue(ndev); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int rcar_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct rcar_can_priv *priv = netdev_priv(dev); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + bec->txerr = readb(&priv->regs->tecr); + bec->rxerr = readb(&priv->regs->recr); + clk_disable_unprepare(priv->clk); + return 0; +} + +static int rcar_can_probe(struct platform_device *pdev) +{ + struct rcar_can_platform_data *pdata; + struct rcar_can_priv *priv; + struct net_device *ndev; + struct resource *mem; + void __iomem *addr; + int err = -ENODEV; + int irq; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + goto fail; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "No IRQ resource\n"); + goto fail; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(addr)) { + err = PTR_ERR(addr); + goto fail; + } + + ndev = alloc_candev(sizeof(struct rcar_can_priv), RCAR_CAN_FIFO_DEPTH); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev() failed\n"); + err = -ENOMEM; + goto fail; + } + + priv = netdev_priv(ndev); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + err = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "cannot get clock: %d\n", err); + goto fail_clk; + } + + ndev->netdev_ops = &rcar_can_netdev_ops; + ndev->irq = irq; + ndev->flags |= IFF_ECHO; + priv->ndev = ndev; + priv->regs = addr; + priv->clock_select = pdata->clock_select; + priv->can.clock.freq = clk_get_rate(priv->clk); + priv->can.bittiming_const = &rcar_can_bittiming_const; + priv->can.do_set_mode = rcar_can_do_set_mode; + priv->can.do_get_berr_counter = rcar_can_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING; + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + netif_napi_add(ndev, &priv->napi, rcar_can_rx_poll, + RCAR_CAN_NAPI_WEIGHT); + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed, error %d\n", + err); + goto fail_candev; + } + + devm_can_led_init(ndev); + + dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n", + priv->regs, ndev->irq); + + return 0; +fail_candev: + netif_napi_del(&priv->napi); +fail_clk: + free_candev(ndev); +fail: + return err; +} + +static int rcar_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct rcar_can_priv *priv = netdev_priv(ndev); + + unregister_candev(ndev); + netif_napi_del(&priv->napi); + free_candev(ndev); + return 0; +} + +static int __maybe_unused rcar_can_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + } + ctlr = readw(&priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_CANM_HALT; + writew(ctlr, &priv->regs->ctlr); + ctlr |= RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->clk); + return 0; +} + +static int __maybe_unused rcar_can_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rcar_can_priv *priv = netdev_priv(ndev); + u16 ctlr; + int err; + + err = clk_enable(priv->clk); + if (err) { + netdev_err(ndev, "clk_enable() failed, error %d\n", err); + return err; + } + + ctlr = readw(&priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_SLPM; + writew(ctlr, &priv->regs->ctlr); + ctlr &= ~RCAR_CAN_CTLR_CANM; + writew(ctlr, &priv->regs->ctlr); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(ndev)) { + netif_device_attach(ndev); + netif_start_queue(ndev); + } + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_can_pm_ops, rcar_can_suspend, rcar_can_resume); + +static struct platform_driver rcar_can_driver = { + .driver = { + .name = RCAR_CAN_DRV_NAME, + .owner = THIS_MODULE, + .pm = &rcar_can_pm_ops, + }, + .probe = rcar_can_probe, + .remove = rcar_can_remove, +}; + +module_platform_driver(rcar_can_driver); + +MODULE_AUTHOR("Cogent Embedded, Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CAN driver for Renesas R-Car SoC"); +MODULE_ALIAS("platform:" RCAR_CAN_DRV_NAME); diff --git a/include/linux/can/platform/rcar_can.h b/include/linux/can/platform/rcar_can.h new file mode 100644 index 000000000000..0f4a2f3df504 --- /dev/null +++ b/include/linux/can/platform/rcar_can.h @@ -0,0 +1,17 @@ +#ifndef _CAN_PLATFORM_RCAR_CAN_H_ +#define _CAN_PLATFORM_RCAR_CAN_H_ + +#include + +/* Clock Select Register settings */ +enum CLKR { + CLKR_CLKP1 = 0, /* Peripheral clock (clkp1) */ + CLKR_CLKP2 = 1, /* Peripheral clock (clkp2) */ + CLKR_CLKEXT = 3 /* Externally input clock */ +}; + +struct rcar_can_platform_data { + enum CLKR clock_select; /* Clock source select */ +}; + +#endif /* !_CAN_PLATFORM_RCAR_CAN_H_ */ -- cgit v1.2.3 From 42193e3efb632c84d686acacd7b2327f2b1f8c63 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 15 May 2014 20:31:56 +0200 Subject: can: unify identifiers to ensure unique include processing Armin pointed me to the fact that the identifier which is used to ensure the unique include processing in lunux/include/uapi/linux/can.h is CAN_H. This clashed with his own source as includes from libraries and APIs should use an underscore '_' at the identifier start. This patch fixes the protection identifiers in all CAN relavant includes. Reported-by: Armin Burchardt Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can/core.h | 6 +++--- include/linux/can/dev.h | 6 +++--- include/linux/can/led.h | 6 +++--- include/linux/can/platform/cc770.h | 6 +++--- include/linux/can/platform/mcp251x.h | 6 +++--- include/linux/can/platform/sja1000.h | 6 +++--- include/linux/can/platform/ti_hecc.h | 6 +++--- include/linux/can/skb.h | 6 +++--- include/uapi/linux/can.h | 6 +++--- include/uapi/linux/can/bcm.h | 6 +++--- include/uapi/linux/can/error.h | 6 +++--- include/uapi/linux/can/gw.h | 6 +++--- include/uapi/linux/can/netlink.h | 6 +++--- include/uapi/linux/can/raw.h | 6 +++--- 14 files changed, 42 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/linux/can/core.h b/include/linux/can/core.h index 78c6c52073ad..a0875001b13c 100644 --- a/include/linux/can/core.h +++ b/include/linux/can/core.h @@ -10,8 +10,8 @@ * */ -#ifndef CAN_CORE_H -#define CAN_CORE_H +#ifndef _CAN_CORE_H +#define _CAN_CORE_H #include #include @@ -58,4 +58,4 @@ extern void can_rx_unregister(struct net_device *dev, canid_t can_id, extern int can_send(struct sk_buff *skb, int loop); extern int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); -#endif /* CAN_CORE_H */ +#endif /* !_CAN_CORE_H */ diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 3ce5e526525f..6992afc6ba7f 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -10,8 +10,8 @@ * */ -#ifndef CAN_DEV_H -#define CAN_DEV_H +#ifndef _CAN_DEV_H +#define _CAN_DEV_H #include #include @@ -132,4 +132,4 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev, struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf); -#endif /* CAN_DEV_H */ +#endif /* !_CAN_DEV_H */ diff --git a/include/linux/can/led.h b/include/linux/can/led.h index 9c1167baf273..e0475c5cbb92 100644 --- a/include/linux/can/led.h +++ b/include/linux/can/led.h @@ -6,8 +6,8 @@ * published by the Free Software Foundation. */ -#ifndef CAN_LED_H -#define CAN_LED_H +#ifndef _CAN_LED_H +#define _CAN_LED_H #include #include @@ -48,4 +48,4 @@ static inline void can_led_notifier_exit(void) #endif -#endif +#endif /* !_CAN_LED_H */ diff --git a/include/linux/can/platform/cc770.h b/include/linux/can/platform/cc770.h index 7702641f87ee..78b2d44f04cf 100644 --- a/include/linux/can/platform/cc770.h +++ b/include/linux/can/platform/cc770.h @@ -1,5 +1,5 @@ -#ifndef _CAN_PLATFORM_CC770_H_ -#define _CAN_PLATFORM_CC770_H_ +#ifndef _CAN_PLATFORM_CC770_H +#define _CAN_PLATFORM_CC770_H /* CPU Interface Register (0x02) */ #define CPUIF_CEN 0x01 /* Clock Out Enable */ @@ -30,4 +30,4 @@ struct cc770_platform_data { u8 bcr; /* Bus Configuration Register */ }; -#endif /* !_CAN_PLATFORM_CC770_H_ */ +#endif /* !_CAN_PLATFORM_CC770_H */ diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h index dc029dba7a03..d44fcae274ff 100644 --- a/include/linux/can/platform/mcp251x.h +++ b/include/linux/can/platform/mcp251x.h @@ -1,5 +1,5 @@ -#ifndef __CAN_PLATFORM_MCP251X_H__ -#define __CAN_PLATFORM_MCP251X_H__ +#ifndef _CAN_PLATFORM_MCP251X_H +#define _CAN_PLATFORM_MCP251X_H /* * @@ -18,4 +18,4 @@ struct mcp251x_platform_data { unsigned long oscillator_frequency; }; -#endif /* __CAN_PLATFORM_MCP251X_H__ */ +#endif /* !_CAN_PLATFORM_MCP251X_H */ diff --git a/include/linux/can/platform/sja1000.h b/include/linux/can/platform/sja1000.h index 96f8fcc78d78..93570b61ec6c 100644 --- a/include/linux/can/platform/sja1000.h +++ b/include/linux/can/platform/sja1000.h @@ -1,5 +1,5 @@ -#ifndef _CAN_PLATFORM_SJA1000_H_ -#define _CAN_PLATFORM_SJA1000_H_ +#ifndef _CAN_PLATFORM_SJA1000_H +#define _CAN_PLATFORM_SJA1000_H /* clock divider register */ #define CDR_CLKOUT_MASK 0x07 @@ -32,4 +32,4 @@ struct sja1000_platform_data { u8 cdr; /* clock divider register */ }; -#endif /* !_CAN_PLATFORM_SJA1000_H_ */ +#endif /* !_CAN_PLATFORM_SJA1000_H */ diff --git a/include/linux/can/platform/ti_hecc.h b/include/linux/can/platform/ti_hecc.h index af17cb3f7a84..a52f47ca6c8a 100644 --- a/include/linux/can/platform/ti_hecc.h +++ b/include/linux/can/platform/ti_hecc.h @@ -1,5 +1,5 @@ -#ifndef __CAN_PLATFORM_TI_HECC_H__ -#define __CAN_PLATFORM_TI_HECC_H__ +#ifndef _CAN_PLATFORM_TI_HECC_H +#define _CAN_PLATFORM_TI_HECC_H /* * TI HECC (High End CAN Controller) driver platform header @@ -41,4 +41,4 @@ struct ti_hecc_platform_data { u32 version; void (*transceiver_switch) (int); }; -#endif +#endif /* !_CAN_PLATFORM_TI_HECC_H */ diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index f9bbbb472663..cc00d15c6107 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -7,8 +7,8 @@ * */ -#ifndef CAN_SKB_H -#define CAN_SKB_H +#ifndef _CAN_SKB_H +#define _CAN_SKB_H #include #include @@ -80,4 +80,4 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb) return skb; } -#endif /* CAN_SKB_H */ +#endif /* !_CAN_SKB_H */ diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h index 5d9d1d140718..41892f720057 100644 --- a/include/uapi/linux/can.h +++ b/include/uapi/linux/can.h @@ -42,8 +42,8 @@ * DAMAGE. */ -#ifndef CAN_H -#define CAN_H +#ifndef _UAPI_CAN_H +#define _UAPI_CAN_H #include #include @@ -191,4 +191,4 @@ struct can_filter { #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ -#endif /* CAN_H */ +#endif /* !_UAPI_CAN_H */ diff --git a/include/uapi/linux/can/bcm.h b/include/uapi/linux/can/bcm.h index 382251a1d214..89ddb9dc9bdf 100644 --- a/include/uapi/linux/can/bcm.h +++ b/include/uapi/linux/can/bcm.h @@ -41,8 +41,8 @@ * DAMAGE. */ -#ifndef CAN_BCM_H -#define CAN_BCM_H +#ifndef _UAPI_CAN_BCM_H +#define _UAPI_CAN_BCM_H #include #include @@ -95,4 +95,4 @@ enum { #define TX_RESET_MULTI_IDX 0x0200 #define RX_RTR_FRAME 0x0400 -#endif /* CAN_BCM_H */ +#endif /* !_UAPI_CAN_BCM_H */ diff --git a/include/uapi/linux/can/error.h b/include/uapi/linux/can/error.h index b63204545320..c247446ab25a 100644 --- a/include/uapi/linux/can/error.h +++ b/include/uapi/linux/can/error.h @@ -41,8 +41,8 @@ * DAMAGE. */ -#ifndef CAN_ERROR_H -#define CAN_ERROR_H +#ifndef _UAPI_CAN_ERROR_H +#define _UAPI_CAN_ERROR_H #define CAN_ERR_DLC 8 /* dlc for error message frames */ @@ -120,4 +120,4 @@ /* controller specific additional information / data[5..7] */ -#endif /* CAN_ERROR_H */ +#endif /* _UAPI_CAN_ERROR_H */ diff --git a/include/uapi/linux/can/gw.h b/include/uapi/linux/can/gw.h index 844c8964bdfe..3e6184cf2f6d 100644 --- a/include/uapi/linux/can/gw.h +++ b/include/uapi/linux/can/gw.h @@ -41,8 +41,8 @@ * DAMAGE. */ -#ifndef CAN_GW_H -#define CAN_GW_H +#ifndef _UAPI_CAN_GW_H +#define _UAPI_CAN_GW_H #include #include @@ -200,4 +200,4 @@ enum { * Beware of sending unpacked or aligned structs! */ -#endif +#endif /* !_UAPI_CAN_GW_H */ diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h index 7e2e1863db16..813d11f54977 100644 --- a/include/uapi/linux/can/netlink.h +++ b/include/uapi/linux/can/netlink.h @@ -15,8 +15,8 @@ * GNU General Public License for more details. */ -#ifndef CAN_NETLINK_H -#define CAN_NETLINK_H +#ifndef _UAPI_CAN_NETLINK_H +#define _UAPI_CAN_NETLINK_H #include @@ -130,4 +130,4 @@ enum { #define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) -#endif /* CAN_NETLINK_H */ +#endif /* !_UAPI_CAN_NETLINK_H */ diff --git a/include/uapi/linux/can/raw.h b/include/uapi/linux/can/raw.h index c7d8c334e0ce..78ec76fd89a6 100644 --- a/include/uapi/linux/can/raw.h +++ b/include/uapi/linux/can/raw.h @@ -42,8 +42,8 @@ * DAMAGE. */ -#ifndef CAN_RAW_H -#define CAN_RAW_H +#ifndef _UAPI_CAN_RAW_H +#define _UAPI_CAN_RAW_H #include @@ -59,4 +59,4 @@ enum { CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */ }; -#endif +#endif /* !_UAPI_CAN_RAW_H */ -- cgit v1.2.3 From 7c95f6d866d861268a217003c5202009fa76f252 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 4 Apr 2014 01:22:45 +0200 Subject: netfilter: nf_tables: deconstify table and chain in context structure The new transaction infrastructure updates the family, table and chain objects in the context structure, so let's deconstify them. While at it, move the context structure initialization routine to the top of the source file as it will be also used from the table and chain routines. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 6 ++-- net/netfilter/nf_tables_api.c | 58 +++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 29ff1dc41ef3..91505231a105 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -83,9 +83,9 @@ struct nft_ctx { struct net *net; const struct sk_buff *skb; const struct nlmsghdr *nlh; - const struct nft_af_info *afi; - const struct nft_table *table; - const struct nft_chain *chain; + struct nft_af_info *afi; + struct nft_table *table; + struct nft_chain *chain; const struct nlattr * const *nla; }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index a5ca900912e1..3643bbc720bc 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -88,6 +88,23 @@ nf_tables_afinfo_lookup(struct net *net, int family, bool autoload) return ERR_PTR(-EAFNOSUPPORT); } +static void nft_ctx_init(struct nft_ctx *ctx, + const struct sk_buff *skb, + const struct nlmsghdr *nlh, + struct nft_af_info *afi, + struct nft_table *table, + struct nft_chain *chain, + const struct nlattr * const *nla) +{ + ctx->net = sock_net(skb->sk); + ctx->skb = skb; + ctx->nlh = nlh; + ctx->afi = afi; + ctx->table = table; + ctx->chain = chain; + ctx->nla = nla; +} + /* * Tables */ @@ -812,7 +829,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nlattr * uninitialized_var(name); - const struct nft_af_info *afi; + struct nft_af_info *afi; struct nft_table *table; struct nft_chain *chain; struct nft_base_chain *basechain = NULL; @@ -1024,7 +1041,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; + struct nft_af_info *afi; struct nft_table *table; struct nft_chain *chain; struct net *net = sock_net(skb->sk); @@ -1062,23 +1079,6 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, return 0; } -static void nft_ctx_init(struct nft_ctx *ctx, - const struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nft_af_info *afi, - const struct nft_table *table, - const struct nft_chain *chain, - const struct nlattr * const *nla) -{ - ctx->net = sock_net(skb->sk); - ctx->skb = skb; - ctx->nlh = nlh; - ctx->afi = afi; - ctx->table = table; - ctx->chain = chain; - ctx->nla = nla; -} - /* * Expressions */ @@ -1582,7 +1582,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; + struct nft_af_info *afi; struct net *net = sock_net(skb->sk); struct nft_table *table; struct nft_chain *chain; @@ -1763,9 +1763,9 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; + struct nft_af_info *afi; struct net *net = sock_net(skb->sk); - const struct nft_table *table; + struct nft_table *table; struct nft_chain *chain = NULL; struct nft_rule *rule; int family = nfmsg->nfgen_family, err = 0; @@ -2009,8 +2009,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, { struct net *net = sock_net(skb->sk); const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi = NULL; - const struct nft_table *table = NULL; + struct nft_af_info *afi = NULL; + struct nft_table *table = NULL; if (nfmsg->nfgen_family != NFPROTO_UNSPEC) { afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); @@ -2244,7 +2244,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, { const struct nft_set *set; unsigned int idx, s_idx = cb->args[0]; - const struct nft_af_info *afi; + struct nft_af_info *afi; struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; struct net *net = sock_net(skb->sk); int cur_family = cb->args[3]; @@ -2389,7 +2389,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nft_set_ops *ops; - const struct nft_af_info *afi; + struct nft_af_info *afi; struct net *net = sock_net(skb->sk); struct nft_table *table; struct nft_set *set; @@ -2651,8 +2651,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, const struct nlattr * const nla[]) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - const struct nft_table *table; + struct nft_af_info *afi; + struct nft_table *table; struct net *net = sock_net(skb->sk); afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); @@ -2959,7 +2959,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, struct nft_ctx bind_ctx = { .afi = ctx->afi, .table = ctx->table, - .chain = binding->chain, + .chain = (struct nft_chain *)binding->chain, }; err = nft_validate_data_load(&bind_ctx, dreg, -- cgit v1.2.3 From 1081d11b086afb73e1d8f52f9047d661d8770b82 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 4 Apr 2014 01:24:07 +0200 Subject: netfilter: nf_tables: generalise transaction infrastructure This patch generalises the existing rule transaction infrastructure so it can be used to handle set, table and chain object transactions as well. The transaction provides a data area that stores private information depending on the transaction type. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 15 +++-- net/netfilter/nf_tables_api.c | 123 +++++++++++++++++++++----------------- 2 files changed, 80 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 91505231a105..246dbd48825f 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -387,18 +387,25 @@ struct nft_rule { }; /** - * struct nft_rule_trans - nf_tables rule update in transaction + * struct nft_trans - nf_tables object update in transaction * * @list: used internally - * @ctx: rule context - * @rule: rule that needs to be updated + * @ctx: transaction context + * @data: internal information related to the transaction */ -struct nft_rule_trans { +struct nft_trans { struct list_head list; struct nft_ctx ctx; + char data[0]; +}; + +struct nft_trans_rule { struct nft_rule *rule; }; +#define nft_trans_rule(trans) \ + (((struct nft_trans_rule *)trans->data)->rule) + static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) { return (struct nft_expr *)&rule->data[0]; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 3643bbc720bc..424a6f3cb6c5 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -105,6 +105,25 @@ static void nft_ctx_init(struct nft_ctx *ctx, ctx->nla = nla; } +static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, u32 size) +{ + struct nft_trans *trans; + + trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL); + if (trans == NULL) + return NULL; + + trans->ctx = *ctx; + + return trans; +} + +static void nft_trans_destroy(struct nft_trans *trans) +{ + list_del(&trans->list); + kfree(trans); +} + /* * Tables */ @@ -1557,26 +1576,25 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, kfree(rule); } -#define NFT_RULE_MAXEXPRS 128 - -static struct nft_expr_info *info; - -static struct nft_rule_trans * -nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule) +static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, + struct nft_rule *rule) { - struct nft_rule_trans *rupd; + struct nft_trans *trans; - rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL); - if (rupd == NULL) - return NULL; + trans = nft_trans_alloc(ctx, sizeof(struct nft_trans_rule)); + if (trans == NULL) + return NULL; - rupd->ctx = *ctx; - rupd->rule = rule; - list_add_tail(&rupd->list, &ctx->net->nft.commit_list); + nft_trans_rule(trans) = rule; + list_add_tail(&trans->list, &ctx->net->nft.commit_list); - return rupd; + return trans; } +#define NFT_RULE_MAXEXPRS 128 + +static struct nft_expr_info *info; + static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -1587,7 +1605,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, struct nft_table *table; struct nft_chain *chain; struct nft_rule *rule, *old_rule = NULL; - struct nft_rule_trans *repl = NULL; + struct nft_trans *trans = NULL; struct nft_expr *expr; struct nft_ctx ctx; struct nlattr *tmp; @@ -1685,8 +1703,8 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_REPLACE) { if (nft_rule_is_active_next(net, old_rule)) { - repl = nf_tables_trans_add(&ctx, old_rule); - if (repl == NULL) { + trans = nft_trans_rule_add(&ctx, old_rule); + if (trans == NULL) { err = -ENOMEM; goto err2; } @@ -1708,7 +1726,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, list_add_rcu(&rule->list, &chain->rules); } - if (nf_tables_trans_add(&ctx, rule) == NULL) { + if (nft_trans_rule_add(&ctx, rule) == NULL) { err = -ENOMEM; goto err3; } @@ -1716,11 +1734,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, err3: list_del_rcu(&rule->list); - if (repl) { - list_del_rcu(&repl->rule->list); - list_del(&repl->list); - nft_rule_clear(net, repl->rule); - kfree(repl); + if (trans) { + list_del_rcu(&nft_trans_rule(trans)->list); + nft_rule_clear(net, nft_trans_rule(trans)); + nft_trans_destroy(trans); } err2: nf_tables_rule_destroy(&ctx, rule); @@ -1737,7 +1754,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule) { /* You cannot delete the same rule twice */ if (nft_rule_is_active_next(ctx->net, rule)) { - if (nf_tables_trans_add(ctx, rule) == NULL) + if (nft_trans_rule_add(ctx, rule) == NULL) return -ENOMEM; nft_rule_disactivate_next(ctx->net, rule); return 0; @@ -1813,7 +1830,7 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_commit(struct sk_buff *skb) { struct net *net = sock_net(skb->sk); - struct nft_rule_trans *rupd, *tmp; + struct nft_trans *trans, *next; /* Bump generation counter, invalidate any dump in progress */ net->nft.genctr++; @@ -1826,38 +1843,38 @@ static int nf_tables_commit(struct sk_buff *skb) */ synchronize_rcu(); - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { /* This rule was inactive in the past and just became active. * Clear the next bit of the genmask since its meaning has * changed, now it is the future. */ - if (nft_rule_is_active(net, rupd->rule)) { - nft_rule_clear(net, rupd->rule); - nf_tables_rule_notify(skb, rupd->ctx.nlh, - rupd->ctx.table, rupd->ctx.chain, - rupd->rule, NFT_MSG_NEWRULE, 0, - rupd->ctx.afi->family); - list_del(&rupd->list); - kfree(rupd); + if (nft_rule_is_active(net, nft_trans_rule(trans))) { + nft_rule_clear(net, nft_trans_rule(trans)); + nf_tables_rule_notify(skb, trans->ctx.nlh, + trans->ctx.table, + trans->ctx.chain, + nft_trans_rule(trans), + NFT_MSG_NEWRULE, 0, + trans->ctx.afi->family); + nft_trans_destroy(trans); continue; } /* This rule is in the past, get rid of it */ - list_del_rcu(&rupd->rule->list); - nf_tables_rule_notify(skb, rupd->ctx.nlh, - rupd->ctx.table, rupd->ctx.chain, - rupd->rule, NFT_MSG_DELRULE, 0, - rupd->ctx.afi->family); + list_del_rcu(&nft_trans_rule(trans)->list); + nf_tables_rule_notify(skb, trans->ctx.nlh, + trans->ctx.table, trans->ctx.chain, + nft_trans_rule(trans), NFT_MSG_DELRULE, + 0, trans->ctx.afi->family); } /* Make sure we don't see any packet traversing old rules */ synchronize_rcu(); /* Now we can safely release unused old rules */ - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - nf_tables_rule_destroy(&rupd->ctx, rupd->rule); - list_del(&rupd->list); - kfree(rupd); + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { + nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); + nft_trans_destroy(trans); } return 0; @@ -1866,27 +1883,25 @@ static int nf_tables_commit(struct sk_buff *skb) static int nf_tables_abort(struct sk_buff *skb) { struct net *net = sock_net(skb->sk); - struct nft_rule_trans *rupd, *tmp; + struct nft_trans *trans, *next; - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - if (!nft_rule_is_active_next(net, rupd->rule)) { - nft_rule_clear(net, rupd->rule); - list_del(&rupd->list); - kfree(rupd); + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { + if (!nft_rule_is_active_next(net, nft_trans_rule(trans))) { + nft_rule_clear(net, nft_trans_rule(trans)); + nft_trans_destroy(trans); continue; } /* This rule is inactive, get rid of it */ - list_del_rcu(&rupd->rule->list); + list_del_rcu(&nft_trans_rule(trans)->list); } /* Make sure we don't see any packet accessing aborted rules */ synchronize_rcu(); - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - nf_tables_rule_destroy(&rupd->ctx, rupd->rule); - list_del(&rupd->list); - kfree(rupd); + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { + nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); + nft_trans_destroy(trans); } return 0; -- cgit v1.2.3 From b380e5c733b9f18a6a3ebb97963b6dd037339bc0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 4 Apr 2014 01:38:51 +0200 Subject: netfilter: nf_tables: add message type to transactions The patch adds message type to the transaction to simplify the commit the and abort routines. Yet another step forward in the generalisation of the transaction infrastructure. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 ++ net/netfilter/nf_tables_api.c | 74 +++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 246dbd48825f..d8dfb2695e0f 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -390,11 +390,13 @@ struct nft_rule { * struct nft_trans - nf_tables object update in transaction * * @list: used internally + * @msg_type: message type * @ctx: transaction context * @data: internal information related to the transaction */ struct nft_trans { struct list_head list; + int msg_type; struct nft_ctx ctx; char data[0]; }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 17df4b807f84..34aa3d507542 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -105,7 +105,8 @@ static void nft_ctx_init(struct nft_ctx *ctx, ctx->nla = nla; } -static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, u32 size) +static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type, + u32 size) { struct nft_trans *trans; @@ -113,6 +114,7 @@ static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, u32 size) if (trans == NULL) return NULL; + trans->msg_type = msg_type; trans->ctx = *ctx; return trans; @@ -1576,12 +1578,12 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, kfree(rule); } -static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, +static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type, struct nft_rule *rule) { struct nft_trans *trans; - trans = nft_trans_alloc(ctx, sizeof(struct nft_trans_rule)); + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule)); if (trans == NULL) return NULL; @@ -1703,7 +1705,8 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_REPLACE) { if (nft_rule_is_active_next(net, old_rule)) { - trans = nft_trans_rule_add(&ctx, old_rule); + trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, + old_rule); if (trans == NULL) { err = -ENOMEM; goto err2; @@ -1726,7 +1729,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, list_add_rcu(&rule->list, &chain->rules); } - if (nft_trans_rule_add(&ctx, rule) == NULL) { + if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) { err = -ENOMEM; goto err3; } @@ -1754,7 +1757,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule) { /* You cannot delete the same rule twice */ if (nft_rule_is_active_next(ctx->net, rule)) { - if (nft_trans_rule_add(ctx, rule) == NULL) + if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL) return -ENOMEM; nft_rule_disactivate_next(ctx->net, rule); return 0; @@ -3114,28 +3117,26 @@ static int nf_tables_commit(struct sk_buff *skb) synchronize_rcu(); list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { - /* This rule was inactive in the past and just became active. - * Clear the next bit of the genmask since its meaning has - * changed, now it is the future. - */ - if (nft_rule_is_active(net, nft_trans_rule(trans))) { - nft_rule_clear(net, nft_trans_rule(trans)); - nf_tables_rule_notify(skb, trans->ctx.nlh, + switch (trans->msg_type) { + case NFT_MSG_NEWRULE: + nft_rule_clear(trans->ctx.net, nft_trans_rule(trans)); + nf_tables_rule_notify(trans->ctx.skb, trans->ctx.nlh, trans->ctx.table, trans->ctx.chain, nft_trans_rule(trans), NFT_MSG_NEWRULE, 0, trans->ctx.afi->family); nft_trans_destroy(trans); - continue; + break; + case NFT_MSG_DELRULE: + list_del_rcu(&nft_trans_rule(trans)->list); + nf_tables_rule_notify(trans->ctx.skb, trans->ctx.nlh, + trans->ctx.table, + trans->ctx.chain, + nft_trans_rule(trans), NFT_MSG_DELRULE, 0, + trans->ctx.afi->family); + break; } - - /* This rule is in the past, get rid of it */ - list_del_rcu(&nft_trans_rule(trans)->list); - nf_tables_rule_notify(skb, trans->ctx.nlh, - trans->ctx.table, trans->ctx.chain, - nft_trans_rule(trans), NFT_MSG_DELRULE, - 0, trans->ctx.afi->family); } /* Make sure we don't see any packet traversing old rules */ @@ -3143,8 +3144,13 @@ static int nf_tables_commit(struct sk_buff *skb) /* Now we can safely release unused old rules */ list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { - nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); - nft_trans_destroy(trans); + switch (trans->msg_type) { + case NFT_MSG_DELRULE: + nf_tables_rule_destroy(&trans->ctx, + nft_trans_rule(trans)); + nft_trans_destroy(trans); + break; + } } return 0; @@ -3156,22 +3162,28 @@ static int nf_tables_abort(struct sk_buff *skb) struct nft_trans *trans, *next; list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { - if (!nft_rule_is_active_next(net, nft_trans_rule(trans))) { - nft_rule_clear(net, nft_trans_rule(trans)); + switch (trans->msg_type) { + case NFT_MSG_NEWRULE: + list_del_rcu(&nft_trans_rule(trans)->list); + break; + case NFT_MSG_DELRULE: + nft_rule_clear(trans->ctx.net, nft_trans_rule(trans)); nft_trans_destroy(trans); - continue; + break; } - - /* This rule is inactive, get rid of it */ - list_del_rcu(&nft_trans_rule(trans)->list); } /* Make sure we don't see any packet accessing aborted rules */ synchronize_rcu(); list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { - nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); - nft_trans_destroy(trans); + switch (trans->msg_type) { + case NFT_MSG_NEWRULE: + nf_tables_rule_destroy(&trans->ctx, + nft_trans_rule(trans)); + nft_trans_destroy(trans); + break; + } } return 0; -- cgit v1.2.3 From 958bee14d0718ca7a5002c0f48a099d1d345812a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Apr 2014 11:48:44 +0200 Subject: netfilter: nf_tables: use new transaction infrastructure to handle sets This patch reworks the nf_tables API so set updates are included in the same batch that contains rule updates. This speeds up rule-set updates since we skip a dialog of four messages between kernel and user-space (two on each direction), from: 1) create the set and send netlink message to the kernel 2) process the response from the kernel that contains the allocated name. 3) add the set elements and send netlink message to the kernel. 4) process the response from the kernel (to check for errors). To: 1) add the set to the batch. 2) add the set elements to the batch. 3) add the rule that points to the set. 4) send batch to the kernel. This also introduces an internal set ID (NFTA_SET_ID) that is unique in the batch so set elements and rules can refer to new sets. Backward compatibility has been only retained in userspace, this means that new nft versions can talk to the kernel both in the new and the old fashion. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 12 +++ include/uapi/linux/netfilter/nf_tables.h | 6 ++ net/netfilter/nf_tables_api.c | 123 +++++++++++++++++++++++++++---- net/netfilter/nft_lookup.c | 10 ++- 4 files changed, 133 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index d8dfb2695e0f..0f472d668cbe 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -268,6 +268,8 @@ static inline void *nft_set_priv(const struct nft_set *set) struct nft_set *nf_tables_set_lookup(const struct nft_table *table, const struct nlattr *nla); +struct nft_set *nf_tables_set_lookup_byid(const struct net *net, + const struct nlattr *nla); /** * struct nft_set_binding - nf_tables set binding @@ -408,6 +410,16 @@ struct nft_trans_rule { #define nft_trans_rule(trans) \ (((struct nft_trans_rule *)trans->data)->rule) +struct nft_trans_set { + struct nft_set *set; + u32 set_id; +}; + +#define nft_trans_set(trans) \ + (((struct nft_trans_set *)trans->data)->set) +#define nft_trans_set_id(trans) \ + (((struct nft_trans_set *)trans->data)->set_id) + static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) { return (struct nft_expr *)&rule->data[0]; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 7d6433f66bf8..2a88f645a5d8 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -246,6 +246,7 @@ enum nft_set_desc_attributes { * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32) * @NFTA_SET_POLICY: selection policy (NLA_U32) * @NFTA_SET_DESC: set description (NLA_NESTED) + * @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32) */ enum nft_set_attributes { NFTA_SET_UNSPEC, @@ -258,6 +259,7 @@ enum nft_set_attributes { NFTA_SET_DATA_LEN, NFTA_SET_POLICY, NFTA_SET_DESC, + NFTA_SET_ID, __NFTA_SET_MAX }; #define NFTA_SET_MAX (__NFTA_SET_MAX - 1) @@ -293,12 +295,14 @@ enum nft_set_elem_attributes { * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING) * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING) * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes) + * @NFTA_SET_ELEM_LIST_SET_ID: uniquely identifies a set in a transaction (NLA_U32) */ enum nft_set_elem_list_attributes { NFTA_SET_ELEM_LIST_UNSPEC, NFTA_SET_ELEM_LIST_TABLE, NFTA_SET_ELEM_LIST_SET, NFTA_SET_ELEM_LIST_ELEMENTS, + NFTA_SET_ELEM_LIST_SET_ID, __NFTA_SET_ELEM_LIST_MAX }; #define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1) @@ -484,12 +488,14 @@ enum nft_cmp_attributes { * @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING) * @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers) * @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers) + * @NFTA_LOOKUP_SET_ID: uniquely identifies a set in a transaction (NLA_U32) */ enum nft_lookup_attributes { NFTA_LOOKUP_UNSPEC, NFTA_LOOKUP_SET, NFTA_LOOKUP_SREG, NFTA_LOOKUP_DREG, + NFTA_LOOKUP_SET_ID, __NFTA_LOOKUP_MAX }; #define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 34aa3d507542..02d3cc65690c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1934,6 +1934,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { [NFTA_SET_DATA_LEN] = { .type = NLA_U32 }, [NFTA_SET_POLICY] = { .type = NLA_U32 }, [NFTA_SET_DESC] = { .type = NLA_NESTED }, + [NFTA_SET_ID] = { .type = NLA_U32 }, }; static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { @@ -1984,6 +1985,20 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table, return ERR_PTR(-ENOENT); } +struct nft_set *nf_tables_set_lookup_byid(const struct net *net, + const struct nlattr *nla) +{ + struct nft_trans *trans; + u32 id = ntohl(nla_get_be32(nla)); + + list_for_each_entry(trans, &net->nft.commit_list, list) { + if (trans->msg_type == NFT_MSG_NEWSET && + id == nft_trans_set_id(trans)) + return nft_trans_set(trans); + } + return ERR_PTR(-ENOENT); +} + static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, const char *name) { @@ -2259,6 +2274,8 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb) return ret; } +#define NFT_SET_INACTIVE (1 << 15) /* Internal set flag */ + static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -2288,6 +2305,8 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); if (IS_ERR(set)) return PTR_ERR(set); + if (set->flags & NFT_SET_INACTIVE) + return -ENOENT; skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (skb2 == NULL) @@ -2321,6 +2340,26 @@ static int nf_tables_set_desc_parse(const struct nft_ctx *ctx, return 0; } +static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type, + struct nft_set *set) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set)); + if (trans == NULL) + return -ENOMEM; + + if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) { + nft_trans_set_id(trans) = + ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID])); + set->flags |= NFT_SET_INACTIVE; + } + nft_trans_set(trans) = set; + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + + return 0; +} + static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -2341,7 +2380,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, if (nla[NFTA_SET_TABLE] == NULL || nla[NFTA_SET_NAME] == NULL || - nla[NFTA_SET_KEY_LEN] == NULL) + nla[NFTA_SET_KEY_LEN] == NULL || + nla[NFTA_SET_ID] == NULL) return -EINVAL; memset(&desc, 0, sizeof(desc)); @@ -2458,8 +2498,11 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, if (err < 0) goto err2; + err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set); + if (err < 0) + goto err2; + list_add_tail(&set->list, &table->sets); - nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET); return 0; err2: @@ -2469,16 +2512,20 @@ err1: return err; } -static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) +static void nft_set_destroy(struct nft_set *set) { - list_del(&set->list); - nf_tables_set_notify(ctx, set, NFT_MSG_DELSET); - set->ops->destroy(set); module_put(set->ops->owner); kfree(set); } +static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) +{ + list_del(&set->list); + nf_tables_set_notify(ctx, set, NFT_MSG_DELSET); + nft_set_destroy(set); +} + static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -2500,10 +2547,16 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); if (IS_ERR(set)) return PTR_ERR(set); + if (set->flags & NFT_SET_INACTIVE) + return -ENOENT; if (!list_empty(&set->bindings)) return -EBUSY; - nf_tables_set_destroy(&ctx, set); + err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set); + if (err < 0) + return err; + + list_del(&set->list); return 0; } @@ -2563,7 +2616,8 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, { list_del(&binding->list); - if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS) + if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS && + !(set->flags & NFT_SET_INACTIVE)) nf_tables_set_destroy(ctx, set); } @@ -2581,6 +2635,7 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + [NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING }, [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING }, [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED }, + [NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 }, }; static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, @@ -2680,6 +2735,8 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); if (IS_ERR(set)) return PTR_ERR(set); + if (set->flags & NFT_SET_INACTIVE) + return -ENOENT; event = NFT_MSG_NEWSETELEM; event |= NFNL_SUBSYS_NFTABLES << 8; @@ -2743,6 +2800,8 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb, set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); if (IS_ERR(set)) return PTR_ERR(set); + if (set->flags & NFT_SET_INACTIVE) + return -ENOENT; if (nlh->nlmsg_flags & NLM_F_DUMP) { struct netlink_dump_control c = { @@ -2928,6 +2987,7 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) { + struct net *net = sock_net(skb->sk); const struct nlattr *attr; struct nft_set *set; struct nft_ctx ctx; @@ -2938,8 +2998,15 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, return err; set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); + if (IS_ERR(set)) { + if (nla[NFTA_SET_ELEM_LIST_SET_ID]) { + set = nf_tables_set_lookup_byid(net, + nla[NFTA_SET_ELEM_LIST_SET_ID]); + } + if (IS_ERR(set)) + return PTR_ERR(set); + } + if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) return -EBUSY; @@ -3069,7 +3136,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_rule_policy, }, [NFT_MSG_NEWSET] = { - .call = nf_tables_newset, + .call_batch = nf_tables_newset, .attr_count = NFTA_SET_MAX, .policy = nft_set_policy, }, @@ -3079,12 +3146,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_set_policy, }, [NFT_MSG_DELSET] = { - .call = nf_tables_delset, + .call_batch = nf_tables_delset, .attr_count = NFTA_SET_MAX, .policy = nft_set_policy, }, [NFT_MSG_NEWSETELEM] = { - .call = nf_tables_newsetelem, + .call_batch = nf_tables_newsetelem, .attr_count = NFTA_SET_ELEM_LIST_MAX, .policy = nft_set_elem_list_policy, }, @@ -3094,7 +3161,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_set_elem_list_policy, }, [NFT_MSG_DELSETELEM] = { - .call = nf_tables_delsetelem, + .call_batch = nf_tables_delsetelem, .attr_count = NFTA_SET_ELEM_LIST_MAX, .policy = nft_set_elem_list_policy, }, @@ -3136,6 +3203,16 @@ static int nf_tables_commit(struct sk_buff *skb) nft_trans_rule(trans), NFT_MSG_DELRULE, 0, trans->ctx.afi->family); break; + case NFT_MSG_NEWSET: + nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE; + nf_tables_set_notify(&trans->ctx, nft_trans_set(trans), + NFT_MSG_NEWSET); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELSET: + nf_tables_set_notify(&trans->ctx, nft_trans_set(trans), + NFT_MSG_DELSET); + break; } } @@ -3148,9 +3225,12 @@ static int nf_tables_commit(struct sk_buff *skb) case NFT_MSG_DELRULE: nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); - nft_trans_destroy(trans); + break; + case NFT_MSG_DELSET: + nft_set_destroy(nft_trans_set(trans)); break; } + nft_trans_destroy(trans); } return 0; @@ -3170,6 +3250,14 @@ static int nf_tables_abort(struct sk_buff *skb) nft_rule_clear(trans->ctx.net, nft_trans_rule(trans)); nft_trans_destroy(trans); break; + case NFT_MSG_NEWSET: + list_del(&nft_trans_set(trans)->list); + break; + case NFT_MSG_DELSET: + list_add_tail(&nft_trans_set(trans)->list, + &trans->ctx.table->sets); + nft_trans_destroy(trans); + break; } } @@ -3181,9 +3269,12 @@ static int nf_tables_abort(struct sk_buff *skb) case NFT_MSG_NEWRULE: nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); - nft_trans_destroy(trans); + break; + case NFT_MSG_NEWSET: + nft_set_destroy(nft_trans_set(trans)); break; } + nft_trans_destroy(trans); } return 0; diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 7fd2bea8aa23..6404a726d17b 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -56,8 +56,14 @@ static int nft_lookup_init(const struct nft_ctx *ctx, return -EINVAL; set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); + if (IS_ERR(set)) { + if (tb[NFTA_LOOKUP_SET_ID]) { + set = nf_tables_set_lookup_byid(ctx->net, + tb[NFTA_LOOKUP_SET_ID]); + } + if (IS_ERR(set)) + return PTR_ERR(set); + } priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG])); err = nft_validate_input_register(priv->sreg); -- cgit v1.2.3 From 91c7b38dc9f0de4f7f444b796d14476bc12df7bc Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 9 Apr 2014 11:58:08 +0200 Subject: netfilter: nf_tables: use new transaction infrastructure to handle chain This patch speeds up rule-set updates and it also introduces a way to revert chain updates if the batch is aborted. The idea is to store the changes in the transaction to apply that in the commit step. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 17 ++++ net/netfilter/nf_tables_api.c | 203 +++++++++++++++++++++++++++++--------- 2 files changed, 175 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 0f472d668cbe..7b2361c559b5 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -420,6 +420,22 @@ struct nft_trans_set { #define nft_trans_set_id(trans) \ (((struct nft_trans_set *)trans->data)->set_id) +struct nft_trans_chain { + bool update; + char name[NFT_CHAIN_MAXNAMELEN]; + struct nft_stats __percpu *stats; + u8 policy; +}; + +#define nft_trans_chain_update(trans) \ + (((struct nft_trans_chain *)trans->data)->update) +#define nft_trans_chain_name(trans) \ + (((struct nft_trans_chain *)trans->data)->name) +#define nft_trans_chain_stats(trans) \ + (((struct nft_trans_chain *)trans->data)->stats) +#define nft_trans_chain_policy(trans) \ + (((struct nft_trans_chain *)trans->data)->policy) + static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) { return (struct nft_expr *)&rule->data[0]; @@ -452,6 +468,7 @@ static inline void *nft_userdata(const struct nft_rule *rule) enum nft_chain_flags { NFT_BASE_CHAIN = 0x1, + NFT_CHAIN_INACTIVE = 0x2, }; /** diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 92cec68c03ec..7a1149fe2100 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -782,6 +782,8 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb, chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); if (IS_ERR(chain)) return PTR_ERR(chain); + if (chain->flags & NFT_CHAIN_INACTIVE) + return -ENOENT; skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb2) @@ -847,6 +849,34 @@ static void nft_chain_stats_replace(struct nft_base_chain *chain, rcu_assign_pointer(chain->stats, newstats); } +static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain)); + if (trans == NULL) + return -ENOMEM; + + if (msg_type == NFT_MSG_NEWCHAIN) + ctx->chain->flags |= NFT_CHAIN_INACTIVE; + + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + return 0; +} + +static void nf_tables_chain_destroy(struct nft_chain *chain) +{ + BUG_ON(chain->use > 0); + + if (chain->flags & NFT_BASE_CHAIN) { + module_put(nft_base_chain(chain)->type->owner); + free_percpu(nft_base_chain(chain)->stats); + kfree(nft_base_chain(chain)); + } else { + kfree(chain); + } +} + static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -866,6 +896,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, struct nft_stats __percpu *stats; int err; bool create; + struct nft_ctx ctx; create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; @@ -911,6 +942,11 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, } if (chain != NULL) { + struct nft_stats *stats = NULL; + struct nft_trans *trans; + + if (chain->flags & NFT_CHAIN_INACTIVE) + return -ENOENT; if (nlh->nlmsg_flags & NLM_F_EXCL) return -EEXIST; if (nlh->nlmsg_flags & NLM_F_REPLACE) @@ -927,17 +963,28 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); if (IS_ERR(stats)) return PTR_ERR(stats); - - nft_chain_stats_replace(nft_base_chain(chain), stats); } - if (nla[NFTA_CHAIN_POLICY]) - nft_base_chain(chain)->policy = policy; + nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); + trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN, + sizeof(struct nft_trans_chain)); + if (trans == NULL) + return -ENOMEM; - if (nla[NFTA_CHAIN_HANDLE] && name) - nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); + nft_trans_chain_stats(trans) = stats; + nft_trans_chain_update(trans) = true; - goto notify; + if (nla[NFTA_CHAIN_POLICY]) + nft_trans_chain_policy(trans) = policy; + else + nft_trans_chain_policy(trans) = -1; + + if (nla[NFTA_CHAIN_HANDLE] && name) { + nla_strlcpy(nft_trans_chain_name(trans), name, + NFT_CHAIN_MAXNAMELEN); + } + list_add_tail(&trans->list, &net->nft.commit_list); + return 0; } if (table->use == UINT_MAX) @@ -1033,31 +1080,26 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, if (!(table->flags & NFT_TABLE_F_DORMANT) && chain->flags & NFT_BASE_CHAIN) { err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops); - if (err < 0) { - module_put(basechain->type->owner); - free_percpu(basechain->stats); - kfree(basechain); - return err; - } + if (err < 0) + goto err1; } - list_add_tail(&chain->list, &table->chains); - table->use++; -notify: - nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, - family); - return 0; -} -static void nf_tables_chain_destroy(struct nft_chain *chain) -{ - BUG_ON(chain->use > 0); + nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); + err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN); + if (err < 0) + goto err2; - if (chain->flags & NFT_BASE_CHAIN) { - module_put(nft_base_chain(chain)->type->owner); - free_percpu(nft_base_chain(chain)->stats); - kfree(nft_base_chain(chain)); - } else - kfree(chain); + list_add_tail(&chain->list, &table->chains); + return 0; +err2: + if (!(table->flags & NFT_TABLE_F_DORMANT) && + chain->flags & NFT_BASE_CHAIN) { + nf_unregister_hooks(nft_base_chain(chain)->ops, + afi->nops); + } +err1: + nf_tables_chain_destroy(chain); + return err; } static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, @@ -1070,6 +1112,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, struct nft_chain *chain; struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; + struct nft_ctx ctx; + int err; afi = nf_tables_afinfo_lookup(net, family, false); if (IS_ERR(afi)) @@ -1082,24 +1126,17 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); if (IS_ERR(chain)) return PTR_ERR(chain); - + if (chain->flags & NFT_CHAIN_INACTIVE) + return -ENOENT; if (!list_empty(&chain->rules) || chain->use > 0) return -EBUSY; - list_del(&chain->list); - table->use--; - - if (!(table->flags & NFT_TABLE_F_DORMANT) && - chain->flags & NFT_BASE_CHAIN) - nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops); - - nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN, - family); - - /* Make sure all rule references are gone before this is released */ - synchronize_rcu(); + nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); + err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN); + if (err < 0) + return err; - nf_tables_chain_destroy(chain); + list_del(&chain->list); return 0; } @@ -1542,6 +1579,8 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb, chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); if (IS_ERR(chain)) return PTR_ERR(chain); + if (chain->flags & NFT_CHAIN_INACTIVE) + return -ENOENT; rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); if (IS_ERR(rule)) @@ -3109,7 +3148,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_table_policy, }, [NFT_MSG_NEWCHAIN] = { - .call = nf_tables_newchain, + .call_batch = nf_tables_newchain, .attr_count = NFTA_CHAIN_MAX, .policy = nft_chain_policy, }, @@ -3119,7 +3158,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_chain_policy, }, [NFT_MSG_DELCHAIN] = { - .call = nf_tables_delchain, + .call_batch = nf_tables_delchain, .attr_count = NFTA_CHAIN_MAX, .policy = nft_chain_policy, }, @@ -3170,6 +3209,27 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { }, }; +static void nft_chain_commit_update(struct nft_trans *trans) +{ + struct nft_base_chain *basechain; + + if (nft_trans_chain_name(trans)[0]) + strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans)); + + if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN)) + return; + + basechain = nft_base_chain(trans->ctx.chain); + nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans)); + + switch (nft_trans_chain_policy(trans)) { + case NF_DROP: + case NF_ACCEPT: + basechain->policy = nft_trans_chain_policy(trans); + break; + } +} + static int nf_tables_commit(struct sk_buff *skb) { struct net *net = sock_net(skb->sk); @@ -3188,6 +3248,33 @@ static int nf_tables_commit(struct sk_buff *skb) list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { + case NFT_MSG_NEWCHAIN: + if (nft_trans_chain_update(trans)) + nft_chain_commit_update(trans); + else { + trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE; + trans->ctx.table->use++; + } + nf_tables_chain_notify(trans->ctx.skb, trans->ctx.nlh, + trans->ctx.table, + trans->ctx.chain, + NFT_MSG_NEWCHAIN, + trans->ctx.afi->family); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELCHAIN: + trans->ctx.table->use--; + nf_tables_chain_notify(trans->ctx.skb, trans->ctx.nlh, + trans->ctx.table, + trans->ctx.chain, + NFT_MSG_DELCHAIN, + trans->ctx.afi->family); + if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) && + trans->ctx.chain->flags & NFT_BASE_CHAIN) { + nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops, + trans->ctx.afi->nops); + } + break; case NFT_MSG_NEWRULE: nft_rule_clear(trans->ctx.net, nft_trans_rule(trans)); nf_tables_rule_notify(trans->ctx.skb, trans->ctx.nlh, @@ -3225,6 +3312,9 @@ static int nf_tables_commit(struct sk_buff *skb) /* Now we can safely release unused old rules */ list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { + case NFT_MSG_DELCHAIN: + nf_tables_chain_destroy(trans->ctx.chain); + break; case NFT_MSG_DELRULE: nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); @@ -3246,6 +3336,26 @@ static int nf_tables_abort(struct sk_buff *skb) list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { + case NFT_MSG_NEWCHAIN: + if (nft_trans_chain_update(trans)) { + if (nft_trans_chain_stats(trans)) + free_percpu(nft_trans_chain_stats(trans)); + + nft_trans_destroy(trans); + } else { + list_del(&trans->ctx.chain->list); + if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) && + trans->ctx.chain->flags & NFT_BASE_CHAIN) { + nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops, + trans->ctx.afi->nops); + } + } + break; + case NFT_MSG_DELCHAIN: + list_add_tail(&trans->ctx.chain->list, + &trans->ctx.table->chains); + nft_trans_destroy(trans); + break; case NFT_MSG_NEWRULE: list_del_rcu(&nft_trans_rule(trans)->list); break; @@ -3269,6 +3379,9 @@ static int nf_tables_abort(struct sk_buff *skb) list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { + case NFT_MSG_NEWCHAIN: + nf_tables_chain_destroy(trans->ctx.chain); + break; case NFT_MSG_NEWRULE: nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); -- cgit v1.2.3 From 55dd6f93076bb82aa8911191125418dcfcbf2c9b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Apr 2014 11:53:37 +0200 Subject: netfilter: nf_tables: use new transaction infrastructure to handle table This patch speeds up rule-set updates and it also provides a way to revert updates and leave things in consistent state in case that the batch needs to be aborted. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 10 +++ net/netfilter/nf_tables_api.c | 145 +++++++++++++++++++++++++++++++++----- 2 files changed, 136 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 7b2361c559b5..15bf745f198d 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -436,6 +436,16 @@ struct nft_trans_chain { #define nft_trans_chain_policy(trans) \ (((struct nft_trans_chain *)trans->data)->policy) +struct nft_trans_table { + bool update; + bool enable; +}; + +#define nft_trans_table_update(trans) \ + (((struct nft_trans_table *)trans->data)->update) +#define nft_trans_table_enable(trans) \ + (((struct nft_trans_table *)trans->data)->enable) + static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) { return (struct nft_expr *)&rule->data[0]; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index d71125a0a341..3e685dbb2921 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -307,6 +307,9 @@ done: return skb->len; } +/* Internal table flags */ +#define NFT_TABLE_INACTIVE (1 << 15) + static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -333,6 +336,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb2) @@ -395,9 +400,9 @@ static void nf_tables_table_disable(const struct nft_af_info *afi, static int nf_tables_updtable(struct nft_ctx *ctx) { - const struct nfgenmsg *nfmsg = nlmsg_data(ctx->nlh); - int family = nfmsg->nfgen_family, ret = 0; + struct nft_trans *trans; u32 flags; + int ret = 0; if (!ctx->nla[NFTA_TABLE_FLAGS]) return 0; @@ -406,25 +411,48 @@ static int nf_tables_updtable(struct nft_ctx *ctx) if (flags & ~NFT_TABLE_F_DORMANT) return -EINVAL; + trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE, + sizeof(struct nft_trans_table)); + if (trans == NULL) + return -ENOMEM; + if ((flags & NFT_TABLE_F_DORMANT) && !(ctx->table->flags & NFT_TABLE_F_DORMANT)) { - nf_tables_table_disable(ctx->afi, ctx->table); - ctx->table->flags |= NFT_TABLE_F_DORMANT; + nft_trans_table_enable(trans) = false; } else if (!(flags & NFT_TABLE_F_DORMANT) && ctx->table->flags & NFT_TABLE_F_DORMANT) { ret = nf_tables_table_enable(ctx->afi, ctx->table); - if (ret >= 0) + if (ret >= 0) { ctx->table->flags &= ~NFT_TABLE_F_DORMANT; + nft_trans_table_enable(trans) = true; + } } if (ret < 0) goto err; - nf_tables_table_notify(ctx->skb, ctx->nlh, ctx->table, - NFT_MSG_NEWTABLE, family); + nft_trans_table_update(trans) = true; + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + return 0; err: + nft_trans_destroy(trans); return ret; } +static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table)); + if (trans == NULL) + return -ENOMEM; + + if (msg_type == NFT_MSG_NEWTABLE) + ctx->table->flags |= NFT_TABLE_INACTIVE; + + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + return 0; +} + static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -437,6 +465,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, int family = nfmsg->nfgen_family; u32 flags = 0; struct nft_ctx ctx; + int err; afi = nf_tables_afinfo_lookup(net, family, true); if (IS_ERR(afi)) @@ -451,6 +480,8 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, } if (table != NULL) { + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; if (nlh->nlmsg_flags & NLM_F_EXCL) return -EEXIST; if (nlh->nlmsg_flags & NLM_F_REPLACE) @@ -480,8 +511,14 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, INIT_LIST_HEAD(&table->sets); table->flags = flags; + nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); + err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE); + if (err < 0) { + kfree(table); + module_put(afi->owner); + return err; + } list_add_tail(&table->list, &afi->tables); - nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); return 0; } @@ -493,7 +530,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, struct nft_af_info *afi; struct nft_table *table; struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; + int family = nfmsg->nfgen_family, err; + struct nft_ctx ctx; afi = nf_tables_afinfo_lookup(net, family, false); if (IS_ERR(afi)) @@ -502,17 +540,27 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; if (!list_empty(&table->chains) || !list_empty(&table->sets)) return -EBUSY; + nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); + err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE); + if (err < 0) + return err; + list_del(&table->list); - nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family); - kfree(table); - module_put(afi->owner); return 0; } +static void nf_tables_table_destroy(struct nft_ctx *ctx) +{ + kfree(ctx->table); + module_put(ctx->afi->owner); +} + int nft_register_chain_type(const struct nf_chain_type *ctype) { int err = 0; @@ -776,6 +824,8 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); if (IS_ERR(chain)) @@ -1120,6 +1170,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); if (IS_ERR(chain)) @@ -1573,6 +1625,8 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); if (IS_ERR(chain)) @@ -1838,6 +1892,8 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; if (nla[NFTA_RULE_CHAIN]) { chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); @@ -2004,6 +2060,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (table->flags & NFT_TABLE_INACTIVE) + return -ENOENT; } nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); @@ -2681,7 +2739,8 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, const struct sk_buff *skb, const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) + const struct nlattr * const nla[], + bool trans) { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); struct nft_af_info *afi; @@ -2695,6 +2754,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]); if (IS_ERR(table)) return PTR_ERR(table); + if (!trans && (table->flags & NFT_TABLE_INACTIVE)) + return -ENOENT; nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); return 0; @@ -2768,7 +2829,8 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) if (err < 0) return err; - err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla); + err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla, + false); if (err < 0) return err; @@ -2833,7 +2895,7 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb, struct nft_ctx ctx; int err; - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false); if (err < 0) return err; @@ -3033,7 +3095,7 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, struct nft_ctx ctx; int rem, err; - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true); if (err < 0) return err; @@ -3111,7 +3173,7 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, struct nft_ctx ctx; int rem, err; - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false); if (err < 0) return err; @@ -3131,7 +3193,7 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { [NFT_MSG_NEWTABLE] = { - .call = nf_tables_newtable, + .call_batch = nf_tables_newtable, .attr_count = NFTA_TABLE_MAX, .policy = nft_table_policy, }, @@ -3141,7 +3203,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .policy = nft_table_policy, }, [NFT_MSG_DELTABLE] = { - .call = nf_tables_deltable, + .call_batch = nf_tables_deltable, .attr_count = NFTA_TABLE_MAX, .policy = nft_table_policy, }, @@ -3246,6 +3308,28 @@ static int nf_tables_commit(struct sk_buff *skb) list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: + if (nft_trans_table_update(trans)) { + if (!nft_trans_table_enable(trans)) { + nf_tables_table_disable(trans->ctx.afi, + trans->ctx.table); + trans->ctx.table->flags |= NFT_TABLE_F_DORMANT; + } + } else { + trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE; + } + nf_tables_table_notify(trans->ctx.skb, trans->ctx.nlh, + trans->ctx.table, + NFT_MSG_NEWTABLE, + trans->ctx.afi->family); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELTABLE: + nf_tables_table_notify(trans->ctx.skb, trans->ctx.nlh, + trans->ctx.table, + NFT_MSG_DELTABLE, + trans->ctx.afi->family); + break; case NFT_MSG_NEWCHAIN: if (nft_trans_chain_update(trans)) nft_chain_commit_update(trans); @@ -3310,6 +3394,9 @@ static int nf_tables_commit(struct sk_buff *skb) /* Now we can safely release unused old rules */ list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { + case NFT_MSG_DELTABLE: + nf_tables_table_destroy(&trans->ctx); + break; case NFT_MSG_DELCHAIN: nf_tables_chain_destroy(trans->ctx.chain); break; @@ -3334,6 +3421,23 @@ static int nf_tables_abort(struct sk_buff *skb) list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: + if (nft_trans_table_update(trans)) { + if (nft_trans_table_enable(trans)) { + nf_tables_table_disable(trans->ctx.afi, + trans->ctx.table); + trans->ctx.table->flags |= NFT_TABLE_F_DORMANT; + } + nft_trans_destroy(trans); + } else { + list_del(&trans->ctx.table->list); + } + break; + case NFT_MSG_DELTABLE: + list_add_tail(&trans->ctx.table->list, + &trans->ctx.afi->tables); + nft_trans_destroy(trans); + break; case NFT_MSG_NEWCHAIN: if (nft_trans_chain_update(trans)) { if (nft_trans_chain_stats(trans)) @@ -3377,6 +3481,9 @@ static int nf_tables_abort(struct sk_buff *skb) list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: + nf_tables_table_destroy(&trans->ctx); + break; case NFT_MSG_NEWCHAIN: nf_tables_chain_destroy(trans->ctx.chain); break; -- cgit v1.2.3 From 60319eb1ca351aa36e29d58d2e60ba9a9836265a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 4 Apr 2014 03:36:42 +0200 Subject: netfilter: nf_tables: use new transaction infrastructure to handle elements Leave the set content in consistent state if we fail to load the batch. Use the new generic transaction infrastructure to achieve this. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 10 +++++ net/netfilter/nf_tables_api.c | 82 ++++++++++++++++++++++++++++++++------- 2 files changed, 78 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 15bf745f198d..b08f2a941007 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -446,6 +446,16 @@ struct nft_trans_table { #define nft_trans_table_enable(trans) \ (((struct nft_trans_table *)trans->data)->enable) +struct nft_trans_elem { + struct nft_set *set; + struct nft_set_elem elem; +}; + +#define nft_trans_elem_set(trans) \ + (((struct nft_trans_elem *)trans->data)->set) +#define nft_trans_elem(trans) \ + (((struct nft_trans_elem *)trans->data)->elem) + static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) { return (struct nft_expr *)&rule->data[0]; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 3e685dbb2921..cd002935e6b2 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2993,7 +2993,21 @@ err: return err; } -static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, +static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx, + int msg_type, + struct nft_set *set) +{ + struct nft_trans *trans; + + trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_elem)); + if (trans == NULL) + return NULL; + + nft_trans_elem_set(trans) = set; + return trans; +} + +static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr) { struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; @@ -3001,6 +3015,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_elem elem; struct nft_set_binding *binding; enum nft_registers dreg; + struct nft_trans *trans; int err; if (set->size && set->nelems == set->size) @@ -3068,14 +3083,20 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, } } + trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); + if (trans == NULL) + goto err3; + err = set->ops->insert(set, &elem); if (err < 0) - goto err3; - set->nelems++; + goto err4; - nf_tables_setelem_notify(ctx, set, &elem, NFT_MSG_NEWSETELEM, 0); + nft_trans_elem(trans) = elem; + list_add(&trans->list, &ctx->net->nft.commit_list); return 0; +err4: + kfree(trans); err3: if (nla[NFTA_SET_ELEM_DATA] != NULL) nft_data_uninit(&elem.data, d2.type); @@ -3093,7 +3114,7 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, const struct nlattr *attr; struct nft_set *set; struct nft_ctx ctx; - int rem, err; + int rem, err = 0; err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true); if (err < 0) @@ -3115,17 +3136,18 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { err = nft_add_set_elem(&ctx, set, attr); if (err < 0) - return err; + break; } - return 0; + return err; } -static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, +static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr) { struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; struct nft_data_desc desc; struct nft_set_elem elem; + struct nft_trans *trans; int err; err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, @@ -3149,10 +3171,12 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, if (err < 0) goto err2; - set->ops->remove(set, &elem); - set->nelems--; + trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set); + if (trans == NULL) + goto err2; - nf_tables_setelem_notify(ctx, set, &elem, NFT_MSG_DELSETELEM, 0); + nft_trans_elem(trans) = elem; + list_add(&trans->list, &ctx->net->nft.commit_list); nft_data_uninit(&elem.key, NFT_DATA_VALUE); if (set->flags & NFT_SET_MAP) @@ -3171,7 +3195,7 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, const struct nlattr *attr; struct nft_set *set; struct nft_ctx ctx; - int rem, err; + int rem, err = 0; err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false); if (err < 0) @@ -3186,9 +3210,9 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { err = nft_del_setelem(&ctx, set, attr); if (err < 0) - return err; + break; } - return 0; + return err; } static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { @@ -3294,6 +3318,7 @@ static int nf_tables_commit(struct sk_buff *skb) { struct net *net = sock_net(skb->sk); struct nft_trans *trans, *next; + struct nft_set *set; /* Bump generation counter, invalidate any dump in progress */ net->nft.genctr++; @@ -3385,6 +3410,25 @@ static int nf_tables_commit(struct sk_buff *skb) nf_tables_set_notify(&trans->ctx, nft_trans_set(trans), NFT_MSG_DELSET); break; + case NFT_MSG_NEWSETELEM: + nft_trans_elem_set(trans)->nelems++; + nf_tables_setelem_notify(&trans->ctx, + nft_trans_elem_set(trans), + &nft_trans_elem(trans), + NFT_MSG_NEWSETELEM, 0); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELSETELEM: + nft_trans_elem_set(trans)->nelems--; + nf_tables_setelem_notify(&trans->ctx, + nft_trans_elem_set(trans), + &nft_trans_elem(trans), + NFT_MSG_DELSETELEM, 0); + set = nft_trans_elem_set(trans); + set->ops->get(set, &nft_trans_elem(trans)); + set->ops->remove(set, &nft_trans_elem(trans)); + nft_trans_destroy(trans); + break; } } @@ -3418,6 +3462,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; list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { switch (trans->msg_type) { @@ -3473,6 +3518,15 @@ static int nf_tables_abort(struct sk_buff *skb) &trans->ctx.table->sets); nft_trans_destroy(trans); break; + case NFT_MSG_NEWSETELEM: + set = nft_trans_elem_set(trans); + set->ops->get(set, &nft_trans_elem(trans)); + set->ops->remove(set, &nft_trans_elem(trans)); + nft_trans_destroy(trans); + break; + case NFT_MSG_DELSETELEM: + nft_trans_destroy(trans); + break; } } -- cgit v1.2.3 From 128ad3322ba5de8fa346203c9931d1fdcab8da87 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 9 May 2014 17:14:24 +0200 Subject: netfilter: nf_tables: remove skb and nlh from context structure Instead of caching the original skbuff that contains the netlink messages, this stores the netlink message sequence number, the netlink portID and the report flag. This helps to prepare the introduction of the object release via call_rcu. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 10 ++-- net/netfilter/nf_tables_api.c | 101 +++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index b08f2a941007..1ed2797fb964 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -72,21 +72,23 @@ static inline void nft_data_debug(const struct nft_data *data) * struct nft_ctx - nf_tables rule/set context * * @net: net namespace - * @skb: netlink skb - * @nlh: netlink message header * @afi: address family info * @table: the table the chain is contained in * @chain: the chain the rule is contained in * @nla: netlink attributes + * @portid: netlink portID of the original message + * @seq: netlink sequence number + * @report: notify via unicast netlink message */ struct nft_ctx { struct net *net; - const struct sk_buff *skb; - const struct nlmsghdr *nlh; struct nft_af_info *afi; struct nft_table *table; struct nft_chain *chain; const struct nlattr * const *nla; + u32 portid; + u32 seq; + bool report; }; struct nft_data_desc { diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 86d055a51f0b..4147166ad17c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -96,13 +96,14 @@ static void nft_ctx_init(struct nft_ctx *ctx, struct nft_chain *chain, const struct nlattr * const *nla) { - ctx->net = sock_net(skb->sk); - ctx->skb = skb; - ctx->nlh = nlh; - ctx->afi = afi; - ctx->table = table; - ctx->chain = chain; - ctx->nla = nla; + ctx->net = sock_net(skb->sk); + ctx->afi = afi; + ctx->table = table; + ctx->chain = chain; + ctx->nla = nla; + ctx->portid = NETLINK_CB(skb).portid; + ctx->report = nlmsg_report(nlh); + ctx->seq = nlh->nlmsg_seq; } static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type, @@ -238,14 +239,10 @@ nla_put_failure: static int nf_tables_table_notify(const struct nft_ctx *ctx, int event) { struct sk_buff *skb; - u32 portid = NETLINK_CB(ctx->skb).portid; - u32 seq = ctx->nlh->nlmsg_seq; - struct net *net = sock_net(ctx->skb->sk); - bool report; int err; - report = nlmsg_report(ctx->nlh); - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -253,18 +250,20 @@ static int nf_tables_table_notify(const struct nft_ctx *ctx, int event) if (skb == NULL) goto err; - err = nf_tables_fill_table_info(skb, portid, seq, event, 0, + err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0, ctx->afi->family, ctx->table); if (err < 0) { kfree_skb(skb); goto err; } - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); + err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + if (err < 0) { + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, + err); + } return err; } @@ -721,14 +720,10 @@ nla_put_failure: static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event) { struct sk_buff *skb; - u32 portid = NETLINK_CB(ctx->skb).portid; - struct net *net = sock_net(ctx->skb->sk); - u32 seq = ctx->nlh->nlmsg_seq; - bool report; int err; - report = nlmsg_report(ctx->nlh); - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -736,7 +731,7 @@ static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event) if (skb == NULL) goto err; - err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, + err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0, ctx->afi->family, ctx->table, ctx->chain); if (err < 0) { @@ -744,11 +739,13 @@ static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event) goto err; } - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); + err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + if (err < 0) { + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, + err); + } return err; } @@ -1473,16 +1470,11 @@ static int nf_tables_rule_notify(const struct nft_ctx *ctx, const struct nft_rule *rule, int event) { - const struct sk_buff *oskb = ctx->skb; struct sk_buff *skb; - u32 portid = NETLINK_CB(oskb).portid; - struct net *net = sock_net(oskb->sk); - u32 seq = ctx->nlh->nlmsg_seq; - bool report; int err; - report = nlmsg_report(ctx->nlh); - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -1490,7 +1482,7 @@ static int nf_tables_rule_notify(const struct nft_ctx *ctx, if (skb == NULL) goto err; - err = nf_tables_fill_rule_info(skb, portid, seq, event, 0, + err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0, ctx->afi->family, ctx->table, ctx->chain, rule); if (err < 0) { @@ -1498,11 +1490,13 @@ static int nf_tables_rule_notify(const struct nft_ctx *ctx, goto err; } - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); + err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + if (err < 0) { + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, + err); + } return err; } @@ -2141,8 +2135,8 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, struct nfgenmsg *nfmsg; struct nlmsghdr *nlh; struct nlattr *desc; - u32 portid = NETLINK_CB(ctx->skb).portid; - u32 seq = ctx->nlh->nlmsg_seq; + u32 portid = ctx->portid; + u32 seq = ctx->seq; event |= NFNL_SUBSYS_NFTABLES << 8; nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), @@ -2194,12 +2188,11 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx, int event) { struct sk_buff *skb; - u32 portid = NETLINK_CB(ctx->skb).portid; - bool report; + u32 portid = ctx->portid; int err; - report = nlmsg_report(ctx->nlh); - if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) + if (!ctx->report && + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -2213,8 +2206,8 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx, goto err; } - err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); + err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, + ctx->report, GFP_KERNEL); err: if (err < 0) nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err); @@ -2956,14 +2949,12 @@ static int nf_tables_setelem_notify(const struct nft_ctx *ctx, const struct nft_set_elem *elem, int event, u16 flags) { - const struct sk_buff *oskb = ctx->skb; - struct net *net = sock_net(oskb->sk); - u32 portid = NETLINK_CB(oskb).portid; - bool report = nlmsg_report(ctx->nlh); + struct net *net = ctx->net; + u32 portid = ctx->portid; struct sk_buff *skb; int err; - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) return 0; err = -ENOBUFS; @@ -2978,7 +2969,7 @@ static int nf_tables_setelem_notify(const struct nft_ctx *ctx, goto err; } - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, + err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, ctx->report, GFP_KERNEL); err: if (err < 0) -- cgit v1.2.3 From c7c32e72cbe23cea97c5d87ffcf6e23cc1ec1a65 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 10 Apr 2014 00:31:10 +0200 Subject: netfilter: nf_tables: defer all object release via rcu Now that all objects are released in the reverse order via the transaction infrastructure, we can enqueue the release via call_rcu to save one synchronize_rcu. For small rule-sets loaded via nft -f, it now takes around 50ms less here. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 + net/netfilter/nf_tables_api.c | 93 +++++++++++++++++++++++---------------- 2 files changed, 56 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 1ed2797fb964..7ee6ce6564ae 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -393,12 +393,14 @@ struct nft_rule { /** * struct nft_trans - nf_tables object update in transaction * + * @rcu_head: rcu head to defer release of transaction data * @list: used internally * @msg_type: message type * @ctx: transaction context * @data: internal information related to the transaction */ struct nft_trans { + struct rcu_head rcu_head; struct list_head list; int msg_type; struct nft_ctx ctx; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 4147166ad17c..e171c635d08c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3298,6 +3298,30 @@ static void nft_chain_commit_update(struct nft_trans *trans) } } +/* Schedule objects for release via rcu to make sure no packets are accesing + * removed rules. + */ +static void nf_tables_commit_release_rcu(struct rcu_head *rt) +{ + struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); + + switch (trans->msg_type) { + case NFT_MSG_DELTABLE: + nf_tables_table_destroy(&trans->ctx); + break; + case NFT_MSG_DELCHAIN: + nf_tables_chain_destroy(trans->ctx.chain); + break; + case NFT_MSG_DELRULE: + nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); + break; + case NFT_MSG_DELSET: + nft_set_destroy(nft_trans_set(trans)); + break; + } + kfree(trans); +} + static int nf_tables_commit(struct sk_buff *skb) { struct net *net = sock_net(skb->sk); @@ -3397,32 +3421,39 @@ static int nf_tables_commit(struct sk_buff *skb) } } - /* Make sure we don't see any packet traversing old rules */ - synchronize_rcu(); - - /* Now we can safely release unused old rules */ list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { - switch (trans->msg_type) { - case NFT_MSG_DELTABLE: - nf_tables_table_destroy(&trans->ctx); - break; - case NFT_MSG_DELCHAIN: - nf_tables_chain_destroy(trans->ctx.chain); - break; - case NFT_MSG_DELRULE: - nf_tables_rule_destroy(&trans->ctx, - nft_trans_rule(trans)); - break; - case NFT_MSG_DELSET: - nft_set_destroy(nft_trans_set(trans)); - break; - } - nft_trans_destroy(trans); + list_del(&trans->list); + trans->ctx.nla = NULL; + call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu); } return 0; } +/* Schedule objects for release via rcu to make sure no packets are accesing + * aborted rules. + */ +static void nf_tables_abort_release_rcu(struct rcu_head *rt) +{ + struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); + + switch (trans->msg_type) { + case NFT_MSG_NEWTABLE: + nf_tables_table_destroy(&trans->ctx); + break; + case NFT_MSG_NEWCHAIN: + nf_tables_chain_destroy(trans->ctx.chain); + break; + case NFT_MSG_NEWRULE: + nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); + break; + case NFT_MSG_NEWSET: + nft_set_destroy(nft_trans_set(trans)); + break; + } + kfree(trans); +} + static int nf_tables_abort(struct sk_buff *skb) { struct net *net = sock_net(skb->sk); @@ -3495,26 +3526,10 @@ static int nf_tables_abort(struct sk_buff *skb) } } - /* Make sure we don't see any packet accessing aborted rules */ - synchronize_rcu(); - list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { - switch (trans->msg_type) { - case NFT_MSG_NEWTABLE: - nf_tables_table_destroy(&trans->ctx); - break; - case NFT_MSG_NEWCHAIN: - nf_tables_chain_destroy(trans->ctx.chain); - break; - case NFT_MSG_NEWRULE: - nf_tables_rule_destroy(&trans->ctx, - nft_trans_rule(trans)); - break; - case NFT_MSG_NEWSET: - nft_set_destroy(nft_trans_set(trans)); - break; - } - nft_trans_destroy(trans); + list_del(&trans->list); + trans->ctx.nla = NULL; + call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu); } return 0; -- cgit v1.2.3 From a1b73fc194e73ed33c8b77bf09374cb05b58151b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 1 May 2014 16:51:04 +0200 Subject: scsi: reintroduce scsi_driver.init_command Instead of letting the ULD play games with the prep_fn move back to the model of a central prep_fn with a callback to the ULD. This already cleans up and shortens the code by itself, and will be required to properly support blk-mq in the SCSI midlayer. Signed-off-by: Christoph Hellwig Reviewed-by: Nicholas Bellinger Reviewed-by: Mike Christie Reviewed-by: Hannes Reinecke --- drivers/scsi/scsi_lib.c | 66 +++++++++++++++++++++++++++------------------- drivers/scsi/sd.c | 44 ++++++++++--------------------- drivers/scsi/sr.c | 19 +++++-------- include/scsi/scsi_driver.h | 9 +++---- 4 files changed, 62 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 045822befad9..9f841df6add8 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1073,15 +1073,7 @@ static struct scsi_cmnd *scsi_get_cmd_from_req(struct scsi_device *sdev, int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) { - struct scsi_cmnd *cmd; - int ret = scsi_prep_state_check(sdev, req); - - if (ret != BLKPREP_OK) - return ret; - - cmd = scsi_get_cmd_from_req(sdev, req); - if (unlikely(!cmd)) - return BLKPREP_DEFER; + struct scsi_cmnd *cmd = req->special; /* * BLOCK_PC requests may transfer data, in which case they must @@ -1125,15 +1117,11 @@ EXPORT_SYMBOL(scsi_setup_blk_pc_cmnd); */ int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) { - struct scsi_cmnd *cmd; - int ret = scsi_prep_state_check(sdev, req); - - if (ret != BLKPREP_OK) - return ret; + struct scsi_cmnd *cmd = req->special; if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh && sdev->scsi_dh_data->scsi_dh->prep_fn)) { - ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req); + int ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req); if (ret != BLKPREP_OK) return ret; } @@ -1143,16 +1131,13 @@ int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) */ BUG_ON(!req->nr_phys_segments); - cmd = scsi_get_cmd_from_req(sdev, req); - if (unlikely(!cmd)) - return BLKPREP_DEFER; - memset(cmd->cmnd, 0, BLK_MAX_CDB); return scsi_init_io(cmd, GFP_ATOMIC); } EXPORT_SYMBOL(scsi_setup_fs_cmnd); -int scsi_prep_state_check(struct scsi_device *sdev, struct request *req) +static int +scsi_prep_state_check(struct scsi_device *sdev, struct request *req) { int ret = BLKPREP_OK; @@ -1204,9 +1189,9 @@ int scsi_prep_state_check(struct scsi_device *sdev, struct request *req) } return ret; } -EXPORT_SYMBOL(scsi_prep_state_check); -int scsi_prep_return(struct request_queue *q, struct request *req, int ret) +static int +scsi_prep_return(struct request_queue *q, struct request *req, int ret) { struct scsi_device *sdev = q->queuedata; @@ -1237,18 +1222,44 @@ int scsi_prep_return(struct request_queue *q, struct request *req, int ret) return ret; } -EXPORT_SYMBOL(scsi_prep_return); -int scsi_prep_fn(struct request_queue *q, struct request *req) +static int scsi_prep_fn(struct request_queue *q, struct request *req) { struct scsi_device *sdev = q->queuedata; - int ret = BLKPREP_KILL; + struct scsi_cmnd *cmd; + int ret; - if (req->cmd_type == REQ_TYPE_BLOCK_PC) + ret = scsi_prep_state_check(sdev, req); + if (ret != BLKPREP_OK) + goto out; + + cmd = scsi_get_cmd_from_req(sdev, req); + if (unlikely(!cmd)) { + ret = BLKPREP_DEFER; + goto out; + } + + if (req->cmd_type == REQ_TYPE_FS) + ret = scsi_cmd_to_driver(cmd)->init_command(cmd); + else if (req->cmd_type == REQ_TYPE_BLOCK_PC) ret = scsi_setup_blk_pc_cmnd(sdev, req); + else + ret = BLKPREP_KILL; + +out: return scsi_prep_return(q, req, ret); } -EXPORT_SYMBOL(scsi_prep_fn); + +static void scsi_unprep_fn(struct request_queue *q, struct request *req) +{ + if (req->cmd_type == REQ_TYPE_FS) { + struct scsi_cmnd *cmd = req->special; + struct scsi_driver *drv = scsi_cmd_to_driver(cmd); + + if (drv->uninit_command) + drv->uninit_command(cmd); + } +} /* * scsi_dev_queue_ready: if we can send requests to sdev, return 1 else @@ -1669,6 +1680,7 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) return NULL; blk_queue_prep_rq(q, scsi_prep_fn); + blk_queue_unprep_rq(q, scsi_unprep_fn); blk_queue_softirq_done(q, scsi_softirq_done); blk_queue_rq_timed_out(q, scsi_times_out); blk_queue_lld_busy(q, scsi_lld_busy); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index efcbcd182863..aa028d8ae774 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -109,6 +109,8 @@ static int sd_suspend_system(struct device *); static int sd_suspend_runtime(struct device *); static int sd_resume(struct device *); static void sd_rescan(struct device *); +static int sd_init_command(struct scsi_cmnd *SCpnt); +static void sd_uninit_command(struct scsi_cmnd *SCpnt); static int sd_done(struct scsi_cmnd *); static int sd_eh_action(struct scsi_cmnd *, int); static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer); @@ -503,6 +505,8 @@ static struct scsi_driver sd_template = { .pm = &sd_pm_ops, }, .rescan = sd_rescan, + .init_command = sd_init_command, + .uninit_command = sd_uninit_command, .done = sd_done, .eh_action = sd_eh_action, }; @@ -838,9 +842,9 @@ static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq) return scsi_setup_blk_pc_cmnd(sdp, rq); } -static void sd_unprep_fn(struct request_queue *q, struct request *rq) +static void sd_uninit_command(struct scsi_cmnd *SCpnt) { - struct scsi_cmnd *SCpnt = rq->special; + struct request *rq = SCpnt->request; if (rq->cmd_flags & REQ_DISCARD) { free_page((unsigned long)rq->buffer); @@ -853,18 +857,10 @@ static void sd_unprep_fn(struct request_queue *q, struct request *rq) } } -/** - * sd_prep_fn - build a scsi (read or write) command from - * information in the request structure. - * @SCpnt: pointer to mid-level's per scsi command structure that - * contains request and into which the scsi command is written - * - * Returns 1 if successful and 0 if error (or cannot be done now). - **/ -static int sd_prep_fn(struct request_queue *q, struct request *rq) +static int sd_init_command(struct scsi_cmnd *SCpnt) { - struct scsi_cmnd *SCpnt; - struct scsi_device *sdp = q->queuedata; + struct request *rq = SCpnt->request; + struct scsi_device *sdp = SCpnt->device; struct gendisk *disk = rq->rq_disk; struct scsi_disk *sdkp; sector_t block = blk_rq_pos(rq); @@ -886,12 +882,6 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) } else if (rq->cmd_flags & REQ_FLUSH) { ret = scsi_setup_flush_cmnd(sdp, rq); goto out; - } else if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { - ret = scsi_setup_blk_pc_cmnd(sdp, rq); - goto out; - } else if (rq->cmd_type != REQ_TYPE_FS) { - ret = BLKPREP_KILL; - goto out; } ret = scsi_setup_fs_cmnd(sdp, rq); if (ret != BLKPREP_OK) @@ -903,11 +893,10 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) * is used for a killable error condition */ ret = BLKPREP_KILL; - SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt, - "sd_prep_fn: block=%llu, " - "count=%d\n", - (unsigned long long)block, - this_count)); + SCSI_LOG_HLQUEUE(1, + scmd_printk(KERN_INFO, SCpnt, + "%s: block=%llu, count=%d\n", + __func__, (unsigned long long)block, this_count)); if (!sdp || !scsi_device_online(sdp) || block + blk_rq_sectors(rq) > get_capacity(disk)) { @@ -1127,7 +1116,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) */ ret = BLKPREP_OK; out: - return scsi_prep_return(q, rq, ret); + return ret; } /** @@ -2878,9 +2867,6 @@ static void sd_probe_async(void *data, async_cookie_t cookie) sd_revalidate_disk(gd); - blk_queue_prep_rq(sdp->request_queue, sd_prep_fn); - blk_queue_unprep_rq(sdp->request_queue, sd_unprep_fn); - gd->driverfs_dev = &sdp->sdev_gendev; gd->flags = GENHD_FL_EXT_DEVT; if (sdp->removable) { @@ -3028,8 +3014,6 @@ static int sd_remove(struct device *dev) async_synchronize_full_domain(&scsi_sd_pm_domain); async_synchronize_full_domain(&scsi_sd_probe_domain); - blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn); - blk_queue_unprep_rq(sdkp->device->request_queue, NULL); device_del(&sdkp->dev); del_gendisk(sdkp->disk); sd_shutdown(dev); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 40d85929aefe..93cbd36c990b 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -79,6 +79,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_WORM); static DEFINE_MUTEX(sr_mutex); static int sr_probe(struct device *); static int sr_remove(struct device *); +static int sr_init_command(struct scsi_cmnd *SCpnt); static int sr_done(struct scsi_cmnd *); static int sr_runtime_suspend(struct device *dev); @@ -94,6 +95,7 @@ static struct scsi_driver sr_template = { .remove = sr_remove, .pm = &sr_pm_ops, }, + .init_command = sr_init_command, .done = sr_done, }; @@ -378,21 +380,14 @@ static int sr_done(struct scsi_cmnd *SCpnt) return good_bytes; } -static int sr_prep_fn(struct request_queue *q, struct request *rq) +static int sr_init_command(struct scsi_cmnd *SCpnt) { int block = 0, this_count, s_size; struct scsi_cd *cd; - struct scsi_cmnd *SCpnt; - struct scsi_device *sdp = q->queuedata; + struct request *rq = SCpnt->request; + struct scsi_device *sdp = SCpnt->device; int ret; - if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { - ret = scsi_setup_blk_pc_cmnd(sdp, rq); - goto out; - } else if (rq->cmd_type != REQ_TYPE_FS) { - ret = BLKPREP_KILL; - goto out; - } ret = scsi_setup_fs_cmnd(sdp, rq); if (ret != BLKPREP_OK) goto out; @@ -517,7 +512,7 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq) */ ret = BLKPREP_OK; out: - return scsi_prep_return(q, rq, ret); + return ret; } static int sr_block_open(struct block_device *bdev, fmode_t mode) @@ -718,7 +713,6 @@ static int sr_probe(struct device *dev) /* FIXME: need to handle a get_capabilities failure properly ?? */ get_capabilities(cd); - blk_queue_prep_rq(sdev->request_queue, sr_prep_fn); sr_vendor_init(cd); disk->driverfs_dev = &sdev->sdev_gendev; @@ -993,7 +987,6 @@ static int sr_remove(struct device *dev) scsi_autopm_get_device(cd->device); - blk_queue_prep_rq(cd->device->request_queue, scsi_prep_fn); del_gendisk(cd->disk); mutex_lock(&sr_ref_mutex); diff --git a/include/scsi/scsi_driver.h b/include/scsi/scsi_driver.h index 20fdfc2526ad..36c4114ed9bc 100644 --- a/include/scsi/scsi_driver.h +++ b/include/scsi/scsi_driver.h @@ -4,17 +4,17 @@ #include struct module; +struct request; struct scsi_cmnd; struct scsi_device; -struct request; -struct request_queue; - struct scsi_driver { struct module *owner; struct device_driver gendrv; void (*rescan)(struct device *); + int (*init_command)(struct scsi_cmnd *); + void (*uninit_command)(struct scsi_cmnd *); int (*done)(struct scsi_cmnd *); int (*eh_action)(struct scsi_cmnd *, int); }; @@ -31,8 +31,5 @@ extern int scsi_register_interface(struct class_interface *); int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req); int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req); -int scsi_prep_state_check(struct scsi_device *sdev, struct request *req); -int scsi_prep_return(struct request_queue *q, struct request *req, int ret); -int scsi_prep_fn(struct request_queue *, struct request *); #endif /* _SCSI_SCSI_DRIVER_H */ -- cgit v1.2.3 From c2e4323b3316b9daec7824802ca0dd9eae4317c5 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 15 May 2014 20:18:09 +0300 Subject: cfg80211: add documentation for max_num_csa_counters Move the comment in the structure to a description of the max_num_csa_counters field in the docbook area. This fixes a warning when building htmldocs (at least): Warning(include/net/cfg80211.h:3064): No description found for parameter 'max_num_csa_counters' Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 447cb58f0d77..955fdec5a1b6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2936,6 +2936,11 @@ struct wiphy_vendor_command { * (including P2P GO) or 0 to indicate no such limit is advertised. The * driver is allowed to advertise a theoretical limit that it can reach in * some cases, but may not always reach. + * + * @max_num_csa_counters: Number of supported csa_counters in beacons + * and probe responses. This value should be set if the driver + * wishes to limit the number of csa counters. Default (0) means + * infinite. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -3053,11 +3058,6 @@ struct wiphy { u16 max_ap_assoc_sta; - /* - * Number of supported csa_counters in beacons and probe responses. - * This value should be set if the driver wishes to limit the number of - * csa counters. Default (0) means infinite. - */ u8 max_num_csa_counters; char priv[0] __aligned(NETDEV_ALIGN); -- cgit v1.2.3 From 8d77ec856200df31623074de3fde44519df7725b Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 15 May 2014 20:32:08 +0300 Subject: mac80211: fix csa_counter_offs argument name in docbook The csa_counter_offs was erroneously described as csa_offs in the docbook section. This fixes two warnings when making htmldocs (at least): Warning(include/net/mac80211.h:3428): No description found for parameter 'csa_counter_offs[IEEE80211_MAX_CSA_COUNTERS_NUM]' Warning(include/net/mac80211.h:3428): Excess struct/union/enum/typedef member 'csa_offs' description in 'ieee80211_mutable_offsets' Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 982d2cd80166..a34f26a4ed18 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3417,8 +3417,9 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); * struct ieee80211_mutable_offsets - mutable beacon offsets * @tim_offset: position of TIM element * @tim_length: size of TIM element - * @csa_offs: array of IEEE80211_MAX_CSA_COUNTERS_NUM offsets to CSA counters. - * This array can contain zero values which should be ignored. + * @csa_counter_offs: array of IEEE80211_MAX_CSA_COUNTERS_NUM offsets + * to CSA counters. This array can contain zero values which + * should be ignored. */ struct ieee80211_mutable_offsets { u16 tim_offset; -- cgit v1.2.3 From b69cf53640da2b86439596118cfa95233154ee76 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 14 Mar 2014 10:50:33 +0100 Subject: perf: Fix a race between ring_buffer_detach() and ring_buffer_attach() Alexander noticed that we use RCU iteration on rb->event_list but do not use list_{add,del}_rcu() to add,remove entries to that list, nor do we observe proper grace periods when re-using the entries. Merge ring_buffer_detach() into ring_buffer_attach() such that attaching to the NULL buffer is detaching. Furthermore, ensure that between any 'detach' and 'attach' of the same event we observe the required grace period, but only when strictly required. In effect this means that only ioctl(.request = PERF_EVENT_IOC_SET_OUTPUT) will wait for a grace period, while the normal initial attach and final detach will not be delayed. This patch should, I think, do the right thing under all circumstances, the 'normal' cases all should never see the extra grace period, but the two cases: 1) PERF_EVENT_IOC_SET_OUTPUT on an event which already has a ring_buffer set, will now observe the required grace period between removing itself from the old and attaching itself to the new buffer. This case is 'simple' in that both buffers are present in perf_event_set_output() one could think an unconditional synchronize_rcu() would be sufficient; however... 2) an event that has a buffer attached, the buffer is destroyed (munmap) and then the event is attached to a new/different buffer using PERF_EVENT_IOC_SET_OUTPUT. This case is more complex because the buffer destruction does: ring_buffer_attach(.rb = NULL) followed by the ioctl() doing: ring_buffer_attach(.rb = foo); and we still need to observe the grace period between these two calls due to us reusing the event->rb_entry list_head. In order to make 2 happen we use Paul's latest cond_synchronize_rcu() call. Cc: Paul Mackerras Cc: Stephane Eranian Cc: Andi Kleen Cc: "Paul E. McKenney" Cc: Ingo Molnar Cc: Frederic Weisbecker Cc: Mike Galbraith Reported-by: Alexander Shishkin Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20140507123526.GD13658@twins.programming.kicks-ass.net Signed-off-by: Thomas Gleixner --- include/linux/perf_event.h | 2 + kernel/events/core.c | 109 ++++++++++++++++++++------------------------- 2 files changed, 51 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3356abcfff18..3ef6ea12806a 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -402,6 +402,8 @@ struct perf_event { struct ring_buffer *rb; struct list_head rb_entry; + unsigned long rcu_batches; + int rcu_pending; /* poll related */ wait_queue_head_t waitq; diff --git a/kernel/events/core.c b/kernel/events/core.c index feb1329ca331..440eefc67397 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3192,7 +3192,8 @@ static void free_event_rcu(struct rcu_head *head) } static void ring_buffer_put(struct ring_buffer *rb); -static void ring_buffer_detach(struct perf_event *event, struct ring_buffer *rb); +static void ring_buffer_attach(struct perf_event *event, + struct ring_buffer *rb); static void unaccount_event_cpu(struct perf_event *event, int cpu) { @@ -3252,8 +3253,6 @@ static void free_event(struct perf_event *event) unaccount_event(event); if (event->rb) { - struct ring_buffer *rb; - /* * Can happen when we close an event with re-directed output. * @@ -3261,12 +3260,7 @@ static void free_event(struct perf_event *event) * over us; possibly making our ring_buffer_put() the last. */ mutex_lock(&event->mmap_mutex); - rb = event->rb; - if (rb) { - rcu_assign_pointer(event->rb, NULL); - ring_buffer_detach(event, rb); - ring_buffer_put(rb); /* could be last */ - } + ring_buffer_attach(event, NULL); mutex_unlock(&event->mmap_mutex); } @@ -3850,28 +3844,47 @@ unlock: static void ring_buffer_attach(struct perf_event *event, struct ring_buffer *rb) { + struct ring_buffer *old_rb = NULL; unsigned long flags; - if (!list_empty(&event->rb_entry)) - return; + if (event->rb) { + /* + * Should be impossible, we set this when removing + * event->rb_entry and wait/clear when adding event->rb_entry. + */ + WARN_ON_ONCE(event->rcu_pending); - spin_lock_irqsave(&rb->event_lock, flags); - if (list_empty(&event->rb_entry)) - list_add(&event->rb_entry, &rb->event_list); - spin_unlock_irqrestore(&rb->event_lock, flags); -} + old_rb = event->rb; + event->rcu_batches = get_state_synchronize_rcu(); + event->rcu_pending = 1; -static void ring_buffer_detach(struct perf_event *event, struct ring_buffer *rb) -{ - unsigned long flags; + spin_lock_irqsave(&old_rb->event_lock, flags); + list_del_rcu(&event->rb_entry); + spin_unlock_irqrestore(&old_rb->event_lock, flags); + } - if (list_empty(&event->rb_entry)) - return; + if (event->rcu_pending && rb) { + cond_synchronize_rcu(event->rcu_batches); + event->rcu_pending = 0; + } - spin_lock_irqsave(&rb->event_lock, flags); - list_del_init(&event->rb_entry); - wake_up_all(&event->waitq); - spin_unlock_irqrestore(&rb->event_lock, flags); + if (rb) { + spin_lock_irqsave(&rb->event_lock, flags); + list_add_rcu(&event->rb_entry, &rb->event_list); + spin_unlock_irqrestore(&rb->event_lock, flags); + } + + rcu_assign_pointer(event->rb, rb); + + if (old_rb) { + ring_buffer_put(old_rb); + /* + * Since we detached before setting the new rb, so that we + * could attach the new rb, we could have missed a wakeup. + * Provide it now. + */ + wake_up_all(&event->waitq); + } } static void ring_buffer_wakeup(struct perf_event *event) @@ -3940,7 +3953,7 @@ static void perf_mmap_close(struct vm_area_struct *vma) { struct perf_event *event = vma->vm_file->private_data; - struct ring_buffer *rb = event->rb; + struct ring_buffer *rb = ring_buffer_get(event); struct user_struct *mmap_user = rb->mmap_user; int mmap_locked = rb->mmap_locked; unsigned long size = perf_data_size(rb); @@ -3948,18 +3961,14 @@ static void perf_mmap_close(struct vm_area_struct *vma) atomic_dec(&rb->mmap_count); if (!atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) - return; + goto out_put; - /* Detach current event from the buffer. */ - rcu_assign_pointer(event->rb, NULL); - ring_buffer_detach(event, rb); + ring_buffer_attach(event, NULL); mutex_unlock(&event->mmap_mutex); /* If there's still other mmap()s of this buffer, we're done. */ - if (atomic_read(&rb->mmap_count)) { - ring_buffer_put(rb); /* can't be last */ - return; - } + if (atomic_read(&rb->mmap_count)) + goto out_put; /* * No other mmap()s, detach from all other events that might redirect @@ -3989,11 +3998,9 @@ again: * still restart the iteration to make sure we're not now * iterating the wrong list. */ - if (event->rb == rb) { - rcu_assign_pointer(event->rb, NULL); - ring_buffer_detach(event, rb); - ring_buffer_put(rb); /* can't be last, we still have one */ - } + if (event->rb == rb) + ring_buffer_attach(event, NULL); + mutex_unlock(&event->mmap_mutex); put_event(event); @@ -4018,6 +4025,7 @@ again: vma->vm_mm->pinned_vm -= mmap_locked; free_uid(mmap_user); +out_put: ring_buffer_put(rb); /* could be last */ } @@ -4135,7 +4143,6 @@ again: vma->vm_mm->pinned_vm += extra; ring_buffer_attach(event, rb); - rcu_assign_pointer(event->rb, rb); perf_event_init_userpage(event); perf_event_update_userpage(event); @@ -6934,7 +6941,7 @@ err_size: static int perf_event_set_output(struct perf_event *event, struct perf_event *output_event) { - struct ring_buffer *rb = NULL, *old_rb = NULL; + struct ring_buffer *rb = NULL; int ret = -EINVAL; if (!output_event) @@ -6962,8 +6969,6 @@ set: if (atomic_read(&event->mmap_count)) goto unlock; - old_rb = event->rb; - if (output_event) { /* get the rb we want to redirect to */ rb = ring_buffer_get(output_event); @@ -6971,23 +6976,7 @@ set: goto unlock; } - if (old_rb) - ring_buffer_detach(event, old_rb); - - if (rb) - ring_buffer_attach(event, rb); - - rcu_assign_pointer(event->rb, rb); - - if (old_rb) { - ring_buffer_put(old_rb); - /* - * Since we detached before setting the new rb, so that we - * could attach the new rb, we could have missed a wakeup. - * Provide it now. - */ - wake_up_all(&event->waitq); - } + ring_buffer_attach(event, rb); ret = 0; unlock: -- cgit v1.2.3 From 643fd0b9f5dc40fedbfbb908ebe6f1169284f7d8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 23 Apr 2014 12:22:54 +0200 Subject: perf: Fix perf_event_open(.flags) test Vince noticed that we test the (unsigned long) flags field against an (unsigned int) constant. This would allow setting the high bits on 64bit platforms and not get an error. There is nothing that uses the high bits, so it should be entirely harmless, but we don't want userspace to accidentally set them anyway, so fix the constants. Cc: Ingo Molnar Cc: Thomas Gleixner Reported-by: Vince Weaver Tested-by: Vince Weaver Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20140423102254.GL11096@twins.programming.kicks-ass.net Signed-off-by: Thomas Gleixner --- include/uapi/linux/perf_event.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 853bc1ccb395..e3fc8f09d110 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -722,10 +722,10 @@ enum perf_callchain_context { PERF_CONTEXT_MAX = (__u64)-4095, }; -#define PERF_FLAG_FD_NO_GROUP (1U << 0) -#define PERF_FLAG_FD_OUTPUT (1U << 1) -#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ -#define PERF_FLAG_FD_CLOEXEC (1U << 3) /* O_CLOEXEC */ +#define PERF_FLAG_FD_NO_GROUP (1UL << 0) +#define PERF_FLAG_FD_OUTPUT (1UL << 1) +#define PERF_FLAG_PID_CGROUP (1UL << 2) /* pid=cgroup id, per-cpu mode only */ +#define PERF_FLAG_FD_CLOEXEC (1UL << 3) /* O_CLOEXEC */ union perf_mem_data_src { __u64 val; -- cgit v1.2.3 From 6520e968eef4f88c076a84a80e026049d157132e Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Mon, 19 May 2014 22:15:08 +0900 Subject: clk: exynos5420: Add 5800 specific clocks Exynos5800 clock structure is mostly similar to 5420 with only a small delta changes. So the 5420 clock file is re-used for 5800 also. The common clocks for both are seggreagated and few clocks which are different for both are separately initialized. Signed-off-by: Alim Akhtar Signed-off-by: Arun Kumar K Acked-by: Tomasz Figa Signed-off-by: Kukjin Kim --- .../devicetree/bindings/clock/exynos5420-clock.txt | 3 +- drivers/clk/samsung/clk-exynos5420.c | 309 +++++++++++++++++---- include/dt-bindings/clock/exynos5420.h | 4 + 3 files changed, 259 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos5420-clock.txt b/Documentation/devicetree/bindings/clock/exynos5420-clock.txt index ca88c97a8562..d54f42cf0440 100644 --- a/Documentation/devicetree/bindings/clock/exynos5420-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5420-clock.txt @@ -1,12 +1,13 @@ * Samsung Exynos5420 Clock Controller The Exynos5420 clock controller generates and supplies clock to various -controllers within the Exynos5420 SoC. +controllers within the Exynos5420 SoC and for the Exynos5800 SoC. Required Properties: - compatible: should be one of the following. - "samsung,exynos5420-clock" - controller compatible with Exynos5420 SoC. + - "samsung,exynos5800-clock" - controller compatible with Exynos5800 SoC. - reg: physical base address of the controller and length of memory mapped region. diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 1c3674ecc0dc..9d7d7eed03fd 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -57,15 +57,19 @@ #define SRC_TOP5 0x10214 #define SRC_TOP6 0x10218 #define SRC_TOP7 0x1021c +#define SRC_TOP8 0x10220 /* 5800 specific */ +#define SRC_TOP9 0x10224 /* 5800 specific */ #define SRC_DISP10 0x1022c #define SRC_MAU 0x10240 #define SRC_FSYS 0x10244 #define SRC_PERIC0 0x10250 #define SRC_PERIC1 0x10254 #define SRC_ISP 0x10270 +#define SRC_CAM 0x10274 /* 5800 specific */ #define SRC_TOP10 0x10280 #define SRC_TOP11 0x10284 #define SRC_TOP12 0x10288 +#define SRC_TOP13 0x1028c /* 5800 specific */ #define SRC_MASK_TOP2 0x10308 #define SRC_MASK_TOP7 0x1031c #define SRC_MASK_DISP10 0x1032c @@ -76,6 +80,8 @@ #define DIV_TOP0 0x10500 #define DIV_TOP1 0x10504 #define DIV_TOP2 0x10508 +#define DIV_TOP8 0x10520 /* 5800 specific */ +#define DIV_TOP9 0x10524 /* 5800 specific */ #define DIV_DISP10 0x1052c #define DIV_MAU 0x10544 #define DIV_FSYS0 0x10548 @@ -86,6 +92,7 @@ #define DIV_PERIC2 0x10560 #define DIV_PERIC3 0x10564 #define DIV_PERIC4 0x10568 +#define DIV_CAM 0x10574 /* 5800 specific */ #define SCLK_DIV_ISP0 0x10580 #define SCLK_DIV_ISP1 0x10584 #define DIV2_RATIO0 0x10590 @@ -102,6 +109,7 @@ #define GATE_TOP_SCLK_ISP 0x10870 #define GATE_IP_GSCL0 0x10910 #define GATE_IP_GSCL1 0x10920 +#define GATE_IP_CAM 0x10924 /* 5800 specific */ #define GATE_IP_MFC 0x1092c #define GATE_IP_DISP1 0x10928 #define GATE_IP_G3D 0x10930 @@ -123,23 +131,31 @@ #define SRC_KFC 0x28200 #define DIV_KFC0 0x28500 +/* Exynos5x SoC type */ +enum exynos5x_soc { + EXYNOS5420, + EXYNOS5800, +}; + /* list of PLLs */ -enum exynos5420_plls { +enum exynos5x_plls { apll, cpll, dpll, epll, rpll, ipll, spll, vpll, mpll, bpll, kpll, nr_plls /* number of PLLs */ }; static void __iomem *reg_base; +static enum exynos5x_soc exynos5x_soc; #ifdef CONFIG_PM_SLEEP -static struct samsung_clk_reg_dump *exynos5420_save; +static struct samsung_clk_reg_dump *exynos5x_save; +static struct samsung_clk_reg_dump *exynos5800_save; /* * list of controller registers to be saved and restored during a * suspend/resume cycle. */ -static unsigned long exynos5420_clk_regs[] __initdata = { +static unsigned long exynos5x_clk_regs[] __initdata = { SRC_CPU, DIV_CPU0, DIV_CPU1, @@ -222,18 +238,37 @@ static unsigned long exynos5420_clk_regs[] __initdata = { DIV_KFC0, }; +static unsigned long exynos5800_clk_regs[] __initdata = { + SRC_TOP8, + SRC_TOP9, + SRC_CAM, + SRC_TOP1, + DIV_TOP8, + DIV_TOP9, + DIV_CAM, + GATE_IP_CAM, +}; + static int exynos5420_clk_suspend(void) { - samsung_clk_save(reg_base, exynos5420_save, - ARRAY_SIZE(exynos5420_clk_regs)); + samsung_clk_save(reg_base, exynos5x_save, + ARRAY_SIZE(exynos5x_clk_regs)); + + if (exynos5x_soc == EXYNOS5800) + samsung_clk_save(reg_base, exynos5800_save, + ARRAY_SIZE(exynos5800_clk_regs)); return 0; } static void exynos5420_clk_resume(void) { - samsung_clk_restore(reg_base, exynos5420_save, - ARRAY_SIZE(exynos5420_clk_regs)); + samsung_clk_restore(reg_base, exynos5x_save, + ARRAY_SIZE(exynos5x_clk_regs)); + + if (exynos5x_soc == EXYNOS5800) + samsung_clk_restore(reg_base, exynos5800_save, + ARRAY_SIZE(exynos5800_clk_regs)); } static struct syscore_ops exynos5420_clk_syscore_ops = { @@ -243,15 +278,29 @@ static struct syscore_ops exynos5420_clk_syscore_ops = { static void exynos5420_clk_sleep_init(void) { - exynos5420_save = samsung_clk_alloc_reg_dump(exynos5420_clk_regs, - ARRAY_SIZE(exynos5420_clk_regs)); - if (!exynos5420_save) { + exynos5x_save = samsung_clk_alloc_reg_dump(exynos5x_clk_regs, + ARRAY_SIZE(exynos5x_clk_regs)); + if (!exynos5x_save) { pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", __func__); return; } + if (exynos5x_soc == EXYNOS5800) { + exynos5800_save = + samsung_clk_alloc_reg_dump(exynos5800_clk_regs, + ARRAY_SIZE(exynos5800_clk_regs)); + if (!exynos5800_save) + goto err_soc; + } + register_syscore_ops(&exynos5420_clk_syscore_ops); + return; +err_soc: + kfree(exynos5x_save); + pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", + __func__); + return; } #else static void exynos5420_clk_sleep_init(void) {} @@ -369,14 +418,43 @@ PNAME(mout_maudio0_p) = {"fin_pll", "maudio_clk", "mout_sclk_dpll", "mout_sclk_epll", "mout_sclk_rpll"}; PNAME(mout_mau_epll_clk_p) = {"mout_sclk_epll", "mout_sclk_dpll", "mout_sclk_mpll", "mout_sclk_spll"}; +/* List of parents specific to exynos5800 */ +PNAME(mout_epll2_5800_p) = { "mout_sclk_epll", "ff_dout_epll2" }; +PNAME(mout_group1_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll", + "mout_sclk_mpll", "ff_dout_spll2" }; +PNAME(mout_group2_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll", + "mout_sclk_mpll", "ff_dout_spll2", + "mout_epll2", "mout_sclk_ipll" }; +PNAME(mout_group3_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll", + "mout_sclk_mpll", "ff_dout_spll2", + "mout_epll2" }; +PNAME(mout_group5_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll", + "mout_sclk_mpll", "mout_sclk_spll" }; +PNAME(mout_group6_5800_p) = { "mout_sclk_ipll", "mout_sclk_dpll", + "mout_sclk_mpll", "ff_dout_spll2" }; +PNAME(mout_group7_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll", + "mout_sclk_mpll", "mout_sclk_spll", + "mout_epll2", "mout_sclk_ipll" }; +PNAME(mout_mau_epll_clk_5800_p) = { "mout_sclk_epll", "mout_sclk_dpll", + "mout_sclk_mpll", + "ff_dout_spll2" }; +PNAME(mout_group8_5800_p) = { "dout_aclk432_scaler", "dout_sclk_sw" }; +PNAME(mout_group9_5800_p) = { "dout_osc_div", "mout_sw_aclk432_scaler" }; +PNAME(mout_group10_5800_p) = { "dout_aclk432_cam", "dout_sclk_sw" }; +PNAME(mout_group11_5800_p) = { "dout_osc_div", "mout_sw_aclk432_cam" }; +PNAME(mout_group12_5800_p) = { "dout_aclkfl1_550_cam", "dout_sclk_sw" }; +PNAME(mout_group13_5800_p) = { "dout_osc_div", "mout_sw_aclkfl1_550_cam" }; +PNAME(mout_group14_5800_p) = { "dout_aclk550_cam", "dout_sclk_sw" }; +PNAME(mout_group15_5800_p) = { "dout_osc_div", "mout_sw_aclk550_cam" }; /* fixed rate clocks generated outside the soc */ -static struct samsung_fixed_rate_clock exynos5420_fixed_rate_ext_clks[] __initdata = { +static struct samsung_fixed_rate_clock + exynos5x_fixed_rate_ext_clks[] __initdata = { FRATE(CLK_FIN_PLL, "fin_pll", NULL, CLK_IS_ROOT, 0), }; /* fixed rate clocks generated inside the soc */ -static struct samsung_fixed_rate_clock exynos5420_fixed_rate_clks[] __initdata = { +static struct samsung_fixed_rate_clock exynos5x_fixed_rate_clks[] __initdata = { FRATE(CLK_SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000), FRATE(0, "sclk_pwi", NULL, CLK_IS_ROOT, 24000000), FRATE(0, "sclk_usbh20", NULL, CLK_IS_ROOT, 48000000), @@ -384,52 +462,146 @@ static struct samsung_fixed_rate_clock exynos5420_fixed_rate_clks[] __initdata = FRATE(0, "sclk_usbh20_scan_clk", NULL, CLK_IS_ROOT, 480000000), }; -static struct samsung_fixed_factor_clock exynos5420_fixed_factor_clks[] __initdata = { +static struct samsung_fixed_factor_clock + exynos5x_fixed_factor_clks[] __initdata = { FFACTOR(0, "ff_hsic_12m", "fin_pll", 1, 2, 0), FFACTOR(0, "ff_sw_aclk66", "mout_sw_aclk66", 1, 2, 0), }; -static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { - MUX(0, "mout_user_pclk66_gpio", mout_user_pclk66_gpio_p, - SRC_TOP7, 4, 1), - MUX(0, "mout_mspll_kfc", mout_mspll_cpu_p, SRC_TOP7, 8, 2), - MUX(0, "mout_mspll_cpu", mout_mspll_cpu_p, SRC_TOP7, 12, 2), - MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2), +static struct samsung_fixed_factor_clock + exynos5800_fixed_factor_clks[] __initdata = { + FFACTOR(0, "ff_dout_epll2", "mout_sclk_epll", 1, 2, 0), + FFACTOR(0, "ff_dout_spll2", "mout_sclk_spll", 1, 2, 0), +}; - MUX(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), - MUX(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1), - MUX(0, "mout_kpll", mout_kpll_p, SRC_KFC, 0, 1), - MUX(0, "mout_kfc", mout_kfc_p, SRC_KFC, 16, 1), +struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { + MUX(0, "mout_aclk400_isp", mout_group3_5800_p, SRC_TOP0, 0, 3), + MUX(0, "mout_aclk400_mscl", mout_group3_5800_p, SRC_TOP0, 4, 3), + MUX(0, "mout_aclk400_wcore", mout_group2_5800_p, SRC_TOP0, 16, 3), + MUX(0, "mout_aclk100_noc", mout_group1_5800_p, SRC_TOP0, 20, 2), + + MUX(0, "mout_aclk333_432_gscl", mout_group6_5800_p, SRC_TOP1, 0, 2), + MUX(0, "mout_aclk333_432_isp", mout_group6_5800_p, SRC_TOP1, 4, 2), + MUX(0, "mout_aclk333_432_isp0", mout_group6_5800_p, SRC_TOP1, 12, 2), + MUX(0, "mout_aclk266", mout_group5_5800_p, SRC_TOP1, 20, 2), + MUX(0, "mout_aclk333", mout_group1_5800_p, SRC_TOP1, 28, 2), + + MUX(0, "mout_aclk400_disp1", mout_group7_5800_p, SRC_TOP2, 4, 3), + MUX(0, "mout_aclk333_g2d", mout_group5_5800_p, SRC_TOP2, 8, 2), + MUX(0, "mout_aclk266_g2d", mout_group5_5800_p, SRC_TOP2, 12, 2), + MUX(0, "mout_aclk300_jpeg", mout_group5_5800_p, SRC_TOP2, 20, 2), + MUX(0, "mout_aclk300_disp1", mout_group5_5800_p, SRC_TOP2, 24, 2), + MUX(0, "mout_aclk300_gscl", mout_group5_5800_p, SRC_TOP2, 28, 2), + + MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, SRC_TOP7, + 20, 2), + MUX(0, "sclk_bpll", mout_bpll_p, SRC_TOP7, 24, 1), + MUX(0, "mout_epll2", mout_epll2_5800_p, SRC_TOP7, 28, 1), + + MUX(0, "mout_aclk550_cam", mout_group3_5800_p, SRC_TOP8, 16, 3), + MUX(0, "mout_aclkfl1_550_cam", mout_group3_5800_p, SRC_TOP8, 20, 3), + MUX(0, "mout_aclk432_cam", mout_group6_5800_p, SRC_TOP8, 24, 2), + MUX(0, "mout_aclk432_scaler", mout_group6_5800_p, SRC_TOP8, 28, 2), + + MUX(0, "mout_user_aclk550_cam", mout_group15_5800_p, + SRC_TOP9, 16, 1), + MUX(0, "mout_user_aclkfl1_550_cam", mout_group13_5800_p, + SRC_TOP9, 20, 1), + MUX(0, "mout_user_aclk432_cam", mout_group11_5800_p, + SRC_TOP9, 24, 1), + MUX(0, "mout_user_aclk432_scaler", mout_group9_5800_p, + SRC_TOP9, 28, 1), + + MUX(0, "mout_sw_aclk550_cam", mout_group14_5800_p, SRC_TOP13, 16, 1), + MUX(0, "mout_sw_aclkfl1_550_cam", mout_group12_5800_p, + SRC_TOP13, 20, 1), + MUX(0, "mout_sw_aclk432_cam", mout_group10_5800_p, + SRC_TOP13, 24, 1), + MUX(0, "mout_sw_aclk432_scaler", mout_group8_5800_p, + SRC_TOP13, 28, 1), + + MUX(0, "mout_fimd1", mout_group2_p, SRC_DISP10, 4, 3), +}; +struct samsung_div_clock exynos5800_div_clks[] __initdata = { + DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore", DIV_TOP0, 16, 3), + + DIV(0, "dout_aclk550_cam", "mout_aclk550_cam", + DIV_TOP8, 16, 3), + DIV(0, "dout_aclkfl1_550_cam", "mout_aclkfl1_550_cam", + DIV_TOP8, 20, 3), + DIV(0, "dout_aclk432_cam", "mout_aclk432_cam", + DIV_TOP8, 24, 3), + DIV(0, "dout_aclk432_scaler", "mout_aclk432_scaler", + DIV_TOP8, 28, 3), + + DIV(0, "dout_osc_div", "fin_pll", DIV_TOP9, 20, 3), + DIV(0, "dout_sclk_sw", "sclk_spll", DIV_TOP9, 24, 6), +}; + +struct samsung_gate_clock exynos5800_gate_clks[] __initdata = { + GATE(CLK_ACLK550_CAM, "aclk550_cam", "mout_user_aclk550_cam", + GATE_BUS_TOP, 24, 0, 0), + GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler", + GATE_BUS_TOP, 27, 0, 0), +}; + +struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "sclk_bpll", mout_bpll_p, TOP_SPARE2, 0, 1), + MUX(0, "mout_aclk400_wcore_bpll", mout_aclk400_wcore_bpll_p, + TOP_SPARE2, 4, 1), MUX(0, "mout_aclk400_isp", mout_group1_p, SRC_TOP0, 0, 2), MUX_A(0, "mout_aclk400_mscl", mout_group1_p, - SRC_TOP0, 4, 2, "aclk400_mscl"), - MUX(0, "mout_aclk200", mout_group1_p, SRC_TOP0, 8, 2), - MUX(0, "mout_aclk200_fsys2", mout_group1_p, SRC_TOP0, 12, 2), + SRC_TOP0, 4, 2, "aclk400_mscl"), MUX(0, "mout_aclk400_wcore", mout_group1_p, SRC_TOP0, 16, 2), MUX(0, "mout_aclk100_noc", mout_group1_p, SRC_TOP0, 20, 2), - MUX(0, "mout_pclk200_fsys", mout_group1_p, SRC_TOP0, 24, 2), - MUX(0, "mout_aclk200_fsys", mout_group1_p, SRC_TOP0, 28, 2), MUX(0, "mout_aclk333_432_gscl", mout_group4_p, SRC_TOP1, 0, 2), MUX(0, "mout_aclk333_432_isp", mout_group4_p, - SRC_TOP1, 4, 2), - MUX(0, "mout_aclk66", mout_group1_p, SRC_TOP1, 8, 2), + SRC_TOP1, 4, 2), MUX(0, "mout_aclk333_432_isp0", mout_group4_p, SRC_TOP1, 12, 2), MUX(0, "mout_aclk266", mout_group1_p, SRC_TOP1, 20, 2), - MUX(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2), MUX(0, "mout_aclk333", mout_group1_p, SRC_TOP1, 28, 2), MUX(0, "mout_aclk400_disp1", mout_group1_p, SRC_TOP2, 4, 2), MUX(0, "mout_aclk333_g2d", mout_group1_p, SRC_TOP2, 8, 2), MUX(0, "mout_aclk266_g2d", mout_group1_p, SRC_TOP2, 12, 2), - MUX(0, "mout_aclk_g3d", mout_group5_p, SRC_TOP2, 16, 1), MUX(0, "mout_aclk300_jpeg", mout_group1_p, SRC_TOP2, 20, 2), MUX(0, "mout_aclk300_disp1", mout_group1_p, SRC_TOP2, 24, 2), MUX(0, "mout_aclk300_gscl", mout_group1_p, SRC_TOP2, 28, 2), + MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2), + + MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1), +}; + +struct samsung_div_clock exynos5420_div_clks[] __initdata = { + DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll", + DIV_TOP0, 16, 3), +}; + +static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = { + MUX(0, "mout_user_pclk66_gpio", mout_user_pclk66_gpio_p, + SRC_TOP7, 4, 1), + MUX(0, "mout_mspll_kfc", mout_mspll_cpu_p, SRC_TOP7, 8, 2), + MUX(0, "mout_mspll_cpu", mout_mspll_cpu_p, SRC_TOP7, 12, 2), + + MUX(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), + MUX(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1), + MUX(0, "mout_kpll", mout_kpll_p, SRC_KFC, 0, 1), + MUX(0, "mout_kfc", mout_kfc_p, SRC_KFC, 16, 1), + + MUX(0, "mout_aclk200", mout_group1_p, SRC_TOP0, 8, 2), + MUX(0, "mout_aclk200_fsys2", mout_group1_p, SRC_TOP0, 12, 2), + MUX(0, "mout_pclk200_fsys", mout_group1_p, SRC_TOP0, 24, 2), + MUX(0, "mout_aclk200_fsys", mout_group1_p, SRC_TOP0, 28, 2), + + MUX(0, "mout_aclk66", mout_group1_p, SRC_TOP1, 8, 2), + MUX(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2), + + MUX(0, "mout_aclk_g3d", mout_group5_p, SRC_TOP2, 16, 1), + MUX(0, "mout_user_aclk400_isp", mout_user_aclk400_isp_p, SRC_TOP3, 0, 1), MUX(0, "mout_user_aclk400_mscl", mout_user_aclk400_mscl_p, @@ -529,15 +701,12 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { SRC_TOP12, 28, 1), /* DISP1 Block */ - MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1), MUX(0, "mout_mipi1", mout_group2_p, SRC_DISP10, 16, 3), MUX(0, "mout_dp1", mout_group2_p, SRC_DISP10, 20, 3), MUX(0, "mout_pixel", mout_group2_p, SRC_DISP10, 24, 3), MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_DISP10, 28, 1), MUX(0, "mout_fimd1_opt", mout_group2_p, SRC_DISP10, 8, 3), - MUX(0, "mout_aclk400_wcore_bpll", mout_aclk400_wcore_bpll_p, - TOP_SPARE2, 4, 1), MUX(0, "mout_fimd1_final", mout_fimd1_final_p, TOP_SPARE2, 8, 1), /* MAU Block */ @@ -574,7 +743,7 @@ static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_isp_sensor", mout_group2_p, SRC_ISP, 28, 3), }; -static struct samsung_div_clock exynos5420_div_clks[] __initdata = { +static struct samsung_div_clock exynos5x_div_clks[] __initdata = { DIV(0, "div_arm", "mout_cpu", DIV_CPU0, 0, 3), DIV(0, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3), DIV(0, "armclk2", "div_arm", DIV_CPU0, 28, 3), @@ -585,8 +754,6 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_aclk400_mscl", "mout_aclk400_mscl", DIV_TOP0, 4, 3), DIV(0, "dout_aclk200", "mout_aclk200", DIV_TOP0, 8, 3), DIV(0, "dout_aclk200_fsys2", "mout_aclk200_fsys2", DIV_TOP0, 12, 3), - DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll", - DIV_TOP0, 16, 3), DIV(0, "dout_aclk100_noc", "mout_aclk100_noc", DIV_TOP0, 20, 3), DIV(0, "dout_pclk200_fsys", "mout_pclk200_fsys", DIV_TOP0, 24, 3), DIV(0, "dout_aclk200_fsys", "mout_aclk200_fsys", DIV_TOP0, 28, 3), @@ -692,7 +859,7 @@ static struct samsung_div_clock exynos5420_div_clks[] __initdata = { CLK_SET_RATE_PARENT, 0), }; -static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { +static struct samsung_gate_clock exynos5x_gate_clks[] __initdata = { /* G2D */ GATE(CLK_MDMA0, "mdma0", "aclk266_g2d", GATE_IP_G2D, 1, 0, 0), GATE(CLK_SSS, "sss", "aclk266_g2d", GATE_IP_G2D, 2, 0, 0), @@ -975,7 +1142,7 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_G3D, "g3d", "mout_user_aclk_g3d", GATE_IP_G3D, 9, 0, 0), }; -static struct samsung_pll_clock exynos5420_plls[nr_plls] __initdata = { +static struct samsung_pll_clock exynos5x_plls[nr_plls] __initdata = { [apll] = PLL(pll_2550, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), [cpll] = PLL(pll_2550, CLK_FOUT_CPLL, "fout_cpll", "fin_pll", CPLL_LOCK, @@ -1006,7 +1173,8 @@ static struct of_device_id ext_clk_match[] __initdata = { }; /* register exynos5420 clocks */ -static void __init exynos5420_clk_init(struct device_node *np) +static void __init exynos5x_clk_init(struct device_node *np, + enum exynos5x_soc soc) { struct samsung_clk_provider *ctx; @@ -1018,27 +1186,56 @@ static void __init exynos5420_clk_init(struct device_node *np) panic("%s: unable to determine soc\n", __func__); } + exynos5x_soc = soc; + ctx = samsung_clk_init(np, reg_base, CLK_NR_CLKS); if (!ctx) panic("%s: unable to allocate context.\n", __func__); - samsung_clk_of_register_fixed_ext(ctx, exynos5420_fixed_rate_ext_clks, - ARRAY_SIZE(exynos5420_fixed_rate_ext_clks), + samsung_clk_of_register_fixed_ext(ctx, exynos5x_fixed_rate_ext_clks, + ARRAY_SIZE(exynos5x_fixed_rate_ext_clks), ext_clk_match); - samsung_clk_register_pll(ctx, exynos5420_plls, - ARRAY_SIZE(exynos5420_plls), - reg_base); - samsung_clk_register_fixed_rate(ctx, exynos5420_fixed_rate_clks, - ARRAY_SIZE(exynos5420_fixed_rate_clks)); - samsung_clk_register_fixed_factor(ctx, exynos5420_fixed_factor_clks, - ARRAY_SIZE(exynos5420_fixed_factor_clks)); - samsung_clk_register_mux(ctx, exynos5420_mux_clks, - ARRAY_SIZE(exynos5420_mux_clks)); - samsung_clk_register_div(ctx, exynos5420_div_clks, - ARRAY_SIZE(exynos5420_div_clks)); - samsung_clk_register_gate(ctx, exynos5420_gate_clks, - ARRAY_SIZE(exynos5420_gate_clks)); + samsung_clk_register_pll(ctx, exynos5x_plls, ARRAY_SIZE(exynos5x_plls), + reg_base); + samsung_clk_register_fixed_rate(ctx, exynos5x_fixed_rate_clks, + ARRAY_SIZE(exynos5x_fixed_rate_clks)); + samsung_clk_register_fixed_factor(ctx, exynos5x_fixed_factor_clks, + ARRAY_SIZE(exynos5x_fixed_factor_clks)); + samsung_clk_register_mux(ctx, exynos5x_mux_clks, + ARRAY_SIZE(exynos5x_mux_clks)); + samsung_clk_register_div(ctx, exynos5x_div_clks, + ARRAY_SIZE(exynos5x_div_clks)); + samsung_clk_register_gate(ctx, exynos5x_gate_clks, + ARRAY_SIZE(exynos5x_gate_clks)); + + if (soc == EXYNOS5420) { + samsung_clk_register_mux(ctx, exynos5420_mux_clks, + ARRAY_SIZE(exynos5420_mux_clks)); + samsung_clk_register_div(ctx, exynos5420_div_clks, + ARRAY_SIZE(exynos5420_div_clks)); + } else { + samsung_clk_register_fixed_factor( + ctx, exynos5800_fixed_factor_clks, + ARRAY_SIZE(exynos5800_fixed_factor_clks)); + samsung_clk_register_mux(ctx, exynos5800_mux_clks, + ARRAY_SIZE(exynos5800_mux_clks)); + samsung_clk_register_div(ctx, exynos5800_div_clks, + ARRAY_SIZE(exynos5800_div_clks)); + samsung_clk_register_gate(ctx, exynos5800_gate_clks, + ARRAY_SIZE(exynos5800_gate_clks)); + } exynos5420_clk_sleep_init(); } + +static void __init exynos5420_clk_init(struct device_node *np) +{ + exynos5x_clk_init(np, EXYNOS5420); +} CLK_OF_DECLARE(exynos5420_clk, "samsung,exynos5420-clock", exynos5420_clk_init); + +static void __init exynos5800_clk_init(struct device_node *np) +{ + exynos5x_clk_init(np, EXYNOS5800); +} +CLK_OF_DECLARE(exynos5800_clk, "samsung,exynos5800-clock", exynos5800_clk_init); diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 7dd1cc3b5c57..97dcb89d37d3 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -193,6 +193,10 @@ #define CLK_SCLK_ISP_SENSOR0 514 #define CLK_SCLK_ISP_SENSOR1 515 #define CLK_SCLK_ISP_SENSOR2 516 +#define CLK_ACLK432_SCALER 517 +#define CLK_ACLK432_CAM 518 +#define CLK_ACLK_FL1550_CAM 519 +#define CLK_ACLK550_CAM 520 /* mux clocks */ #define CLK_MOUT_HDMI 640 -- cgit v1.2.3 From 3b3a0162fade6b83d5c83efafcd5adb9e4537047 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 19 May 2014 17:19:31 +0200 Subject: cfg80211: constify MAC addresses in cfg80211 ops This propagates through all the drivers and mac80211. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 7 ++-- drivers/net/wireless/ath/wil6210/cfg80211.c | 4 +- drivers/net/wireless/ath/wil6210/main.c | 4 +- drivers/net/wireless/ath/wil6210/wil6210.h | 2 +- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 6 +-- drivers/net/wireless/libertas/cfg.c | 2 +- drivers/net/wireless/mwifiex/11n.h | 2 +- drivers/net/wireless/mwifiex/cfg80211.c | 19 +++++----- drivers/net/wireless/mwifiex/main.h | 22 +++++------ drivers/net/wireless/mwifiex/tdls.c | 44 ++++++++++++---------- drivers/net/wireless/mwifiex/util.c | 6 +-- drivers/net/wireless/mwifiex/wmm.c | 10 ++--- drivers/net/wireless/mwifiex/wmm.h | 5 ++- drivers/net/wireless/rndis_wlan.c | 4 +- drivers/staging/wlan-ng/cfg80211.c | 2 +- include/net/cfg80211.h | 29 +++++++------- net/mac80211/cfg.c | 24 ++++++------ net/mac80211/ieee80211_i.h | 4 +- net/mac80211/tdls.c | 12 +++--- 19 files changed, 106 insertions(+), 102 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index c2c6f4604958..1fffbde4b01e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1759,7 +1759,7 @@ static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi) } static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2975,7 +2975,7 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac) + const u8 *mac) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2986,7 +2986,8 @@ static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, } static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params) + const u8 *mac, + struct station_parameters *params) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 4806a49cb61b..6e699d050d1e 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -172,7 +172,7 @@ static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, static int wil_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; @@ -671,7 +671,7 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, } static int wil_cfg80211_del_station(struct wiphy *wiphy, - struct net_device *dev, u8 *mac) + struct net_device *dev, const u8 *mac) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 95f4efe9ef37..a6b1d5872786 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -81,7 +81,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) memset(&sta->stats, 0, sizeof(sta->stats)); } -static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) { int cid = -ENOENT; struct net_device *ndev = wil_to_ndev(wil); @@ -252,7 +252,7 @@ int wil_priv_init(struct wil6210_priv *wil) return 0; } -void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) { del_timer_sync(&wil->connect_timer); _wil6210_disconnect(wil, bssid); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 2a2dec75f026..6b353be64579 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -507,7 +507,7 @@ void wil_wdev_free(struct wil6210_priv *wil); int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan); int wmi_pcp_stop(struct wil6210_priv *wil); -void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); +void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid); int wil_rx_init(struct wil6210_priv *wil); void wil_rx_fini(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index afb3d15e38ff..befa9c04166d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2182,7 +2182,7 @@ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; @@ -3975,7 +3975,7 @@ brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, static int brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, - u8 *mac) + const u8 *mac) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_scb_val_le scbval; @@ -4203,7 +4203,7 @@ static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) } static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, - struct net_device *ndev, u8 *peer, + struct net_device *ndev, const u8 *peer, enum nl80211_tdls_operation oper) { struct brcmf_if *ifp; diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 54e344aed6e0..e4d7031df05c 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1610,7 +1610,7 @@ static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, */ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct lbs_private *priv = wiphy_priv(wiphy); s8 signal, noise; diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 40b007a00f4b..0a46aab24214 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -199,7 +199,7 @@ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, } static inline u8 -mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, u8 *ra) +mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra) { struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra); if (node) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 21ee27ab7b74..e95dec91a561 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -994,7 +994,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, */ static int mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -1270,7 +1270,7 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, */ static int mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac) + const u8 *mac) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct mwifiex_sta_node *sta_node; @@ -2629,7 +2629,7 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, */ static int mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, const u8 *extra_ies, size_t extra_ies_len) { @@ -2701,7 +2701,7 @@ mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, static int mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, enum nl80211_tdls_operation action) + const u8 *peer, enum nl80211_tdls_operation action) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -2748,9 +2748,8 @@ mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, } static int -mwifiex_cfg80211_add_station(struct wiphy *wiphy, - struct net_device *dev, - u8 *mac, struct station_parameters *params) +mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -2765,9 +2764,9 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy, } static int -mwifiex_cfg80211_change_station(struct wiphy *wiphy, - struct net_device *dev, - u8 *mac, struct station_parameters *params) +mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, + struct station_parameters *params) { int ret; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index d53e1e8c9467..86c6b8cdec22 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -910,8 +910,6 @@ int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, struct sk_buff *skb); int mwifiex_process_sta_event(struct mwifiex_private *); int mwifiex_process_uap_event(struct mwifiex_private *); -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); void mwifiex_delete_all_station_list(struct mwifiex_private *priv); void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); @@ -1220,26 +1218,26 @@ void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); extern const struct ethtool_ops mwifiex_ethtool_ops; void mwifiex_del_all_sta_list(struct mwifiex_private *priv); -void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac); +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac); void mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, int ies_len, struct mwifiex_sta_node *node); struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac); +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac); struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); -int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, u8 *peer, +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac); +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, const u8 *extra_ies, size_t extra_ies_len); -int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len); +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *buf, int len); -int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action); -int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac); +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 97662a1ba58c..49c8a2c64eeb 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -25,8 +25,8 @@ #define TDLS_RESP_FIX_LEN 8 #define TDLS_CONFIRM_FIX_LEN 6 -static void -mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status) +static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, + const u8 *mac, u8 status) { struct mwifiex_ra_list_tbl *ra_list; struct list_head *tid_list; @@ -84,7 +84,8 @@ mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status) return; } -static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, u8 *mac) +static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, + const u8 *mac) { struct mwifiex_ra_list_tbl *ra_list; struct list_head *ra_list_head; @@ -186,7 +187,7 @@ static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, } static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, - u8 *mac, struct sk_buff *skb) + const u8 *mac, struct sk_buff *skb) { struct mwifiex_bssdescriptor *bss_desc; struct ieee80211_vht_operation *vht_oper; @@ -325,8 +326,9 @@ static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) } static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, struct sk_buff *skb) + const u8 *peer, u8 action_code, + u8 dialog_token, + u16 status_code, struct sk_buff *skb) { struct ieee80211_tdls_data *tf; int ret; @@ -453,7 +455,8 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, } static void -mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid) +mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, + const u8 *peer, const u8 *bssid) { struct ieee80211_tdls_lnkie *lnkid; @@ -467,8 +470,8 @@ mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid) memcpy(lnkid->resp_sta, peer, ETH_ALEN); } -int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, - u8 *peer, u8 action_code, u8 dialog_token, +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, u16 status_code, const u8 *extra_ies, size_t extra_ies_len) { @@ -560,7 +563,8 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, } static int -mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, +mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, struct sk_buff *skb) { @@ -638,10 +642,10 @@ mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, return 0; } -int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, - u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len) +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) { struct sk_buff *skb; struct mwifiex_txinfo *tx_info; @@ -848,7 +852,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, } static int -mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer) +mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct mwifiex_ds_tdls_oper tdls_oper; @@ -869,7 +873,7 @@ mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer) } static int -mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer) +mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct mwifiex_ds_tdls_oper tdls_oper; @@ -896,7 +900,7 @@ mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer) } static int -mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer) +mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct mwifiex_ds_tdls_oper tdls_oper; @@ -925,7 +929,7 @@ mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer) } static int -mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer) +mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct ieee80211_mcs_info mcs; @@ -982,7 +986,7 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer) return 0; } -int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action) { switch (action) { case MWIFIEX_TDLS_ENABLE_LINK: @@ -997,7 +1001,7 @@ int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) return 0; } -int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac) +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *sta_ptr; diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index c3824e37f3f2..6da5abf52e61 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -259,7 +259,7 @@ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, * NULL is returned if station entry is not found in associated STA list. */ struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac) +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *node; @@ -280,7 +280,7 @@ mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac) * If received mac address is NULL, NULL is returned. */ struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac) +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *node; unsigned long flags; @@ -332,7 +332,7 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, } /* This function will delete a station entry from station list */ -void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac) +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *node; unsigned long flags; diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 0a7cc742aed7..c6bc5b3191d6 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -92,7 +92,7 @@ mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) * The function also initializes the list with the provided RA. */ static struct mwifiex_ra_list_tbl * -mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra) +mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) { struct mwifiex_ra_list_tbl *ra_list; @@ -139,8 +139,7 @@ static u8 mwifiex_get_random_ba_threshold(void) * This function allocates and adds a RA list for all TIDs * with the given RA. */ -void -mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) { int i; struct mwifiex_ra_list_tbl *ra_list; @@ -575,7 +574,7 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) */ static struct mwifiex_ra_list_tbl * mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, - u8 *ra_addr) + const u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; @@ -596,7 +595,8 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, * retrieved. */ struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr) +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index 83e42083ebff..eca56e371a57 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -99,7 +99,7 @@ mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, struct sk_buff *skb); -void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra); +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra); void mwifiex_rotate_priolists(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra, int tid); @@ -123,7 +123,8 @@ void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, const struct host_cmd_ds_command *resp); struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr); +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr); u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); #endif /* !_MWIFIEX_WMM_H_ */ diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 39d22a154341..d2a9a08210be 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -517,7 +517,7 @@ static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast); static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo); + const u8 *mac, struct station_info *sinfo); static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo); @@ -2490,7 +2490,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev, } static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index f76f95c29617..f7eb8163d4fb 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -298,7 +298,7 @@ static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev, static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { wlandevice_t *wlandev = dev->ml_priv; struct p80211msg_lnxreq_commsquality quality; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 955fdec5a1b6..d4a602b92edf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2345,28 +2345,29 @@ struct cfg80211_ops { int (*add_station)(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params); + const u8 *mac, + struct station_parameters *params); int (*del_station)(struct wiphy *wiphy, struct net_device *dev, - u8 *mac); + const u8 *mac); int (*change_station)(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params); + const u8 *mac, + struct station_parameters *params); int (*get_station)(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo); + const u8 *mac, struct station_info *sinfo); int (*dump_station)(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo); + int idx, u8 *mac, struct station_info *sinfo); int (*add_mpath)(struct wiphy *wiphy, struct net_device *dev, - u8 *dst, u8 *next_hop); + const u8 *dst, const u8 *next_hop); int (*del_mpath)(struct wiphy *wiphy, struct net_device *dev, - u8 *dst); + const u8 *dst); int (*change_mpath)(struct wiphy *wiphy, struct net_device *dev, - u8 *dst, u8 *next_hop); + const u8 *dst, const u8 *next_hop); int (*get_mpath)(struct wiphy *wiphy, struct net_device *dev, - u8 *dst, u8 *next_hop, - struct mpath_info *pinfo); + u8 *dst, u8 *next_hop, struct mpath_info *pinfo); int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *dst, u8 *next_hop, - struct mpath_info *pinfo); + int idx, u8 *dst, u8 *next_hop, + struct mpath_info *pinfo); int (*get_mesh_config)(struct wiphy *wiphy, struct net_device *dev, struct mesh_config *conf); @@ -2496,11 +2497,11 @@ struct cfg80211_ops { struct cfg80211_gtk_rekey_data *data); int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, const u8 *buf, size_t len); int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, enum nl80211_tdls_operation oper); + const u8 *peer, enum nl80211_tdls_operation oper); int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u64 *cookie); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index bfd2534e5a4d..ac45304590d8 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -777,7 +777,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy, } static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo) + int idx, u8 *mac, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -807,7 +807,7 @@ static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -1457,7 +1457,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, } static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params) + const u8 *mac, + struct station_parameters *params) { struct ieee80211_local *local = wiphy_priv(wiphy); struct sta_info *sta; @@ -1526,7 +1527,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac) + const u8 *mac) { struct ieee80211_sub_if_data *sdata; @@ -1540,7 +1541,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_change_station(struct wiphy *wiphy, - struct net_device *dev, u8 *mac, + struct net_device *dev, const u8 *mac, struct station_parameters *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1665,7 +1666,7 @@ out_err: #ifdef CONFIG_MAC80211_MESH static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, - u8 *dst, u8 *next_hop) + const u8 *dst, const u8 *next_hop) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; @@ -1693,7 +1694,7 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, - u8 *dst) + const u8 *dst) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1704,9 +1705,8 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, return 0; } -static int ieee80211_change_mpath(struct wiphy *wiphy, - struct net_device *dev, - u8 *dst, u8 *next_hop) +static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev, + const u8 *dst, const u8 *next_hop) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; @@ -1798,8 +1798,8 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *dst, u8 *next_hop, - struct mpath_info *pinfo) + int idx, u8 *dst, u8 *next_hop, + struct mpath_info *pinfo) { struct ieee80211_sub_if_data *sdata; struct mesh_path *mpath; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 57e0b2682cef..ed2b817d5ece 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1839,11 +1839,11 @@ int ieee80211_max_num_channels(struct ieee80211_local *local); /* TDLS */ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, const u8 *extra_ies, size_t extra_ies_len); int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, enum nl80211_tdls_operation oper); + const u8 *peer, enum nl80211_tdls_operation oper); #ifdef CONFIG_MAC80211_NOINLINE diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 8e14e2aaea11..652813b2d3df 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -40,8 +40,8 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) return capab; } -static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, - u8 *peer, u8 *bssid) +static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, + const u8 *peer, const u8 *bssid) { struct ieee80211_tdls_lnkie *lnkid; @@ -57,7 +57,7 @@ static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, static int ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -130,7 +130,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -169,7 +169,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, } int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, u8 action_code, u8 dialog_token, + const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, const u8 *extra_ies, size_t extra_ies_len) { @@ -285,7 +285,7 @@ fail: } int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, - u8 *peer, enum nl80211_tdls_operation oper) + const u8 *peer, enum nl80211_tdls_operation oper) { struct sta_info *sta; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -- cgit v1.2.3 From c1e5f4714d591cc0a5e986613fdefa61abe98ac2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 19 May 2014 17:53:16 +0200 Subject: cfg80211: constify more pointers in the cfg80211 API This also propagates through the drivers. The orinoco driver uses the cfg80211 API structs for internal bookkeeping, and so needs a (void *) cast that removes the const - but that's OK because it allocates those pointers. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/wmi.c | 2 +- drivers/net/wireless/ath/ath6kl/wmi.h | 2 +- drivers/net/wireless/libertas/cfg.c | 5 ++--- drivers/net/wireless/libertas/defs.h | 3 ++- drivers/net/wireless/orinoco/hw.c | 4 ++-- drivers/net/wireless/orinoco/hw.h | 4 ++-- drivers/net/wireless/orinoco/wext.c | 4 ++-- drivers/staging/wlan-ng/cfg80211.c | 2 +- include/net/cfg80211.h | 23 ++++++++++++----------- net/wireless/ibss.c | 2 +- net/wireless/sme.c | 2 +- net/wireless/util.c | 3 ++- 12 files changed, 29 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 8b4ce28e3ce8..051094604e21 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2322,7 +2322,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, return ret; } -int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk) +int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk) { struct sk_buff *skb; struct wmi_add_krk_cmd *cmd; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index b5f226503baf..39ee35e60c1d 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2617,7 +2617,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, u8 *key_material, u8 key_op_ctrl, u8 *mac_addr, enum wmi_sync_flag sync_flag); -int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk); +int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk); int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index); int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid, const u8 *pmkid, bool set); diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index e4d7031df05c..47a998d8f99e 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1006,9 +1006,8 @@ struct cmd_key_material { } __packed; static int lbs_set_key_material(struct lbs_private *priv, - int key_type, - int key_info, - u8 *key, u16 key_len) + int key_type, int key_info, + const u8 *key, u16 key_len) { struct cmd_key_material cmd; int ret; diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index ab966f08024a..407784aca627 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -90,7 +90,8 @@ do { if ((lbs_debug & (grp)) == (grp)) \ #define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) #ifdef DEBUG -static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len) +static inline void lbs_deb_hex(unsigned int grp, const char *prompt, + const u8 *buf, int len) { int i = 0; diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c index 49300d04efdf..e27e32851f1e 100644 --- a/drivers/net/wireless/orinoco/hw.c +++ b/drivers/net/wireless/orinoco/hw.c @@ -988,8 +988,8 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv) * tsc must be NULL or up to 8 bytes */ int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, - int set_tx, u8 *key, u8 *rsc, size_t rsc_len, - u8 *tsc, size_t tsc_len) + int set_tx, const u8 *key, const u8 *rsc, + size_t rsc_len, const u8 *tsc, size_t tsc_len) { struct { __le16 idx; diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h index 8f6831f4e328..466d1ede76f1 100644 --- a/drivers/net/wireless/orinoco/hw.h +++ b/drivers/net/wireless/orinoco/hw.h @@ -38,8 +38,8 @@ int __orinoco_hw_set_wap(struct orinoco_private *priv); int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv); int __orinoco_hw_setup_enc(struct orinoco_private *priv); int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, - int set_tx, u8 *key, u8 *rsc, size_t rsc_len, - u8 *tsc, size_t tsc_len); + int set_tx, const u8 *key, const u8 *rsc, + size_t rsc_len, const u8 *tsc, size_t tsc_len); int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx); int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, struct net_device *dev, diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c index b7a867b50b94..6abdaf0aa052 100644 --- a/drivers/net/wireless/orinoco/wext.c +++ b/drivers/net/wireless/orinoco/wext.c @@ -52,9 +52,9 @@ static int orinoco_set_key(struct orinoco_private *priv, int index, priv->keys[index].seq_len = seq_len; if (key_len) - memcpy(priv->keys[index].key, key, key_len); + memcpy((void *)priv->keys[index].key, key, key_len); if (seq_len) - memcpy(priv->keys[index].seq, seq, seq_len); + memcpy((void *)priv->keys[index].seq, seq, seq_len); switch (alg) { case ORINOCO_ALG_TKIP: diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index f7eb8163d4fb..723319ee08f3 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -84,7 +84,7 @@ static int prism2_domibset_uint32(wlandevice_t *wlandev, u32 did, u32 data) } static int prism2_domibset_pstr32(wlandevice_t *wlandev, - u32 did, u8 len, u8 *data) + u32 did, u8 len, const u8 *data) { struct p80211msg_dot11req_mibset msg; p80211item_pstr32_t *mibitem = diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d4a602b92edf..3299d1b731ef 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -341,8 +341,8 @@ struct vif_params { * @seq_len: length of @seq. */ struct key_params { - u8 *key; - u8 *seq; + const u8 *key; + const u8 *seq; int key_len; int seq_len; u32 cipher; @@ -1169,7 +1169,7 @@ struct bss_parameters { int use_cts_prot; int use_short_preamble; int use_short_slot_time; - u8 *basic_rates; + const u8 *basic_rates; u8 basic_rates_len; int ap_isolate; int ht_opmode; @@ -1699,10 +1699,10 @@ struct cfg80211_disassoc_request { * @ht_capa_mask: The bits of ht_capa which are to be used. */ struct cfg80211_ibss_params { - u8 *ssid; - u8 *bssid; + const u8 *ssid; + const u8 *bssid; struct cfg80211_chan_def chandef; - u8 *ie; + const u8 *ie; u8 ssid_len, ie_len; u16 beacon_interval; u32 basic_rates; @@ -1811,8 +1811,8 @@ struct cfg80211_bitrate_mask { * @pmkid: The PMK material itself. */ struct cfg80211_pmksa { - u8 *bssid; - u8 *pmkid; + const u8 *bssid; + const u8 *pmkid; }; /** @@ -3289,7 +3289,7 @@ struct wireless_dev { struct cfg80211_ibss_params ibss; struct cfg80211_connect_params connect; struct cfg80211_cached_keys *keys; - u8 *ie; + const u8 *ie; size_t ie_len; u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; @@ -3530,7 +3530,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, * Return: 0 on success, or a negative error code. */ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype, u8 *bssid, bool qos); + enum nl80211_iftype iftype, const u8 *bssid, + bool qos); /** * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame @@ -4319,7 +4320,7 @@ void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss, * and not try to connect to any AP any more. */ void cfg80211_disconnected(struct net_device *dev, u16 reason, - u8 *ie, size_t ie_len, gfp_t gfp); + const u8 *ie, size_t ie_len, gfp_t gfp); /** * cfg80211_ready_on_channel - notification of remain_on_channel start diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 6b50588b709f..8f345da3ea5f 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -420,8 +420,8 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, if (len > 0 && ssid[len - 1] == '\0') len--; + memcpy(wdev->ssid, ssid, len); wdev->wext.ibss.ssid = wdev->ssid; - memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; wdev_lock(wdev); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 4bc21a2b1989..ea701bbacc6a 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -879,7 +879,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, } void cfg80211_disconnected(struct net_device *dev, u16 reason, - u8 *ie, size_t ie_len, gfp_t gfp) + const u8 *ie, size_t ie_len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); diff --git a/net/wireless/util.c b/net/wireless/util.c index 8c61d5c6fad3..fa61ac9c9b26 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -476,7 +476,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, EXPORT_SYMBOL(ieee80211_data_to_8023); int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype, u8 *bssid, bool qos) + enum nl80211_iftype iftype, + const u8 *bssid, bool qos) { struct ieee80211_hdr hdr; u16 hdrlen, ethertype; -- cgit v1.2.3 From 922bd80fc33b5b90eb34b1485ebcf3c7b2e61618 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 19 May 2014 17:59:50 +0200 Subject: cfg80211: constify wowlan/coalesce mask/pattern pointers This requires changing the nl80211 parsing code a bit to use intermediate pointers for the allocation, but clarifies the API towards the drivers. Signed-off-by: Johannes Berg --- drivers/net/wireless/ti/wlcore/main.c | 2 +- drivers/net/wireless/ti/wlcore/wlcore_i.h | 4 ++-- include/net/cfg80211.h | 2 +- net/wireless/nl80211.c | 39 +++++++++++++++++-------------- 4 files changed, 26 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 077eb5b9cd74..02c91d6db753 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1416,7 +1416,7 @@ void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter) int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, u16 offset, u8 flags, - u8 *pattern, u8 len) + const u8 *pattern, u8 len) { struct wl12xx_rx_filter_field *field; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 756e890bc5ee..c2c34a84ff3d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -512,8 +512,8 @@ int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_queue_recovery_work(struct wl1271 *wl); size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, - u16 offset, u8 flags, - u8 *pattern, u8 len); + u16 offset, u8 flags, + const u8 *pattern, u8 len); void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter); struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void); int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3299d1b731ef..fe4fa287f788 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1827,7 +1827,7 @@ struct cfg80211_pmksa { * memory, free @mask only! */ struct cfg80211_pkt_pattern { - u8 *mask, *pattern; + const u8 *mask, *pattern; int pattern_len; int pkt_offset; }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ca19b1520389..49adf58646e6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8554,6 +8554,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], rem) { + u8 *mask_pat; + nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), nla_len(pat), NULL); err = -EINVAL; @@ -8577,19 +8579,18 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) goto error; new_triggers.patterns[i].pkt_offset = pkt_offset; - new_triggers.patterns[i].mask = - kmalloc(mask_len + pat_len, GFP_KERNEL); - if (!new_triggers.patterns[i].mask) { + mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL); + if (!mask_pat) { err = -ENOMEM; goto error; } - new_triggers.patterns[i].pattern = - new_triggers.patterns[i].mask + mask_len; - memcpy(new_triggers.patterns[i].mask, - nla_data(pat_tb[NL80211_PKTPAT_MASK]), + new_triggers.patterns[i].mask = mask_pat; + memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); + mask_pat += mask_len; + new_triggers.patterns[i].pattern = mask_pat; new_triggers.patterns[i].pattern_len = pat_len; - memcpy(new_triggers.patterns[i].pattern, + memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); i++; @@ -8781,6 +8782,8 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], rem) { + u8 *mask_pat; + nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), nla_len(pat), NULL); if (!pat_tb[NL80211_PKTPAT_MASK] || @@ -8802,17 +8805,19 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, return -EINVAL; new_rule->patterns[i].pkt_offset = pkt_offset; - new_rule->patterns[i].mask = - kmalloc(mask_len + pat_len, GFP_KERNEL); - if (!new_rule->patterns[i].mask) + mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL); + if (!mask_pat) return -ENOMEM; - new_rule->patterns[i].pattern = - new_rule->patterns[i].mask + mask_len; - memcpy(new_rule->patterns[i].mask, - nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); + + new_rule->patterns[i].mask = mask_pat; + memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]), + mask_len); + + mask_pat += mask_len; + new_rule->patterns[i].pattern = mask_pat; new_rule->patterns[i].pattern_len = pat_len; - memcpy(new_rule->patterns[i].pattern, - nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); + memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), + pat_len); i++; } -- cgit v1.2.3 From 1429d7c9467e1e3de0b0ff91d7e4d67c1a92f8a3 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 19 May 2014 09:23:55 -0600 Subject: blk-mq: switch ctx pending map to the sparser blk_align_bitmap Each hardware queue has a bitmap of software queues with pending requests. When new IO is queued on a software queue, the bit is set, and when IO is pruned on a hardware queue run, the bit is cleared. This causes a lot of traffic. Switch this from the regular BITS_PER_LONG bitmap to a sparser layout, similarly to what was done for blk-mq tagging. 20% performance increase was observed for single threaded IO, and about 15% performanc increase on multiple threads driving the same device. Signed-off-by: Jens Axboe --- block/blk-mq.c | 119 +++++++++++++++++++++++++++++++++++++------------ include/linux/blk-mq.h | 10 ++++- 2 files changed, 99 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 526feee31bff..e862c4408427 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -56,21 +56,40 @@ static bool blk_mq_hctx_has_pending(struct blk_mq_hw_ctx *hctx) { unsigned int i; - for (i = 0; i < hctx->nr_ctx_map; i++) - if (hctx->ctx_map[i]) + for (i = 0; i < hctx->ctx_map.map_size; i++) + if (hctx->ctx_map.map[i].word) return true; return false; } +static inline struct blk_align_bitmap *get_bm(struct blk_mq_hw_ctx *hctx, + struct blk_mq_ctx *ctx) +{ + return &hctx->ctx_map.map[ctx->index_hw / hctx->ctx_map.bits_per_word]; +} + +#define CTX_TO_BIT(hctx, ctx) \ + ((ctx)->index_hw & ((hctx)->ctx_map.bits_per_word - 1)) + /* * Mark this ctx as having pending work in this hardware queue */ static void blk_mq_hctx_mark_pending(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx) { - if (!test_bit(ctx->index_hw, hctx->ctx_map)) - set_bit(ctx->index_hw, hctx->ctx_map); + struct blk_align_bitmap *bm = get_bm(hctx, ctx); + + if (!test_bit(CTX_TO_BIT(hctx, ctx), &bm->word)) + set_bit(CTX_TO_BIT(hctx, ctx), &bm->word); +} + +static void blk_mq_hctx_clear_pending(struct blk_mq_hw_ctx *hctx, + struct blk_mq_ctx *ctx) +{ + struct blk_align_bitmap *bm = get_bm(hctx, ctx); + + clear_bit(CTX_TO_BIT(hctx, ctx), &bm->word); } static struct request *__blk_mq_alloc_request(struct blk_mq_hw_ctx *hctx, @@ -614,6 +633,40 @@ static bool blk_mq_attempt_merge(struct request_queue *q, return false; } +/* + * Process software queues that have been marked busy, splicing them + * to the for-dispatch + */ +static void flush_busy_ctxs(struct blk_mq_hw_ctx *hctx, struct list_head *list) +{ + struct blk_mq_ctx *ctx; + int i; + + for (i = 0; i < hctx->ctx_map.map_size; i++) { + struct blk_align_bitmap *bm = &hctx->ctx_map.map[i]; + unsigned int off, bit; + + if (!bm->word) + continue; + + bit = 0; + off = i * hctx->ctx_map.bits_per_word; + do { + bit = find_next_bit(&bm->word, bm->depth, bit); + if (bit >= bm->depth) + break; + + ctx = hctx->ctxs[bit + off]; + clear_bit(bit, &bm->word); + spin_lock(&ctx->lock); + list_splice_tail_init(&ctx->rq_list, list); + spin_unlock(&ctx->lock); + + bit++; + } while (1); + } +} + /* * Run this hardware queue, pulling any software queues mapped to it in. * Note that this function currently has various problems around ordering @@ -623,10 +676,9 @@ static bool blk_mq_attempt_merge(struct request_queue *q, static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) { struct request_queue *q = hctx->queue; - struct blk_mq_ctx *ctx; struct request *rq; LIST_HEAD(rq_list); - int bit, queued; + int queued; WARN_ON(!cpumask_test_cpu(raw_smp_processor_id(), hctx->cpumask)); @@ -638,14 +690,7 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) /* * Touch any software queue that has pending entries. */ - for_each_set_bit(bit, hctx->ctx_map, hctx->nr_ctx) { - clear_bit(bit, hctx->ctx_map); - ctx = hctx->ctxs[bit]; - - spin_lock(&ctx->lock); - list_splice_tail_init(&ctx->rq_list, &rq_list); - spin_unlock(&ctx->lock); - } + flush_busy_ctxs(hctx, &rq_list); /* * If we have previous entries on our dispatch list, grab them @@ -658,14 +703,10 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) spin_unlock(&hctx->lock); } - /* - * Delete and return all entries from our dispatch list - */ - queued = 0; - /* * Now process all the entries, sending them to the driver. */ + queued = 0; while (!list_empty(&rq_list)) { int ret; @@ -1158,7 +1199,7 @@ static void blk_mq_hctx_notify(void *data, unsigned long action, spin_lock(&ctx->lock); if (!list_empty(&ctx->rq_list)) { list_splice_init(&ctx->rq_list, &tmp); - clear_bit(ctx->index_hw, hctx->ctx_map); + blk_mq_hctx_clear_pending(hctx, ctx); } spin_unlock(&ctx->lock); @@ -1298,6 +1339,34 @@ fail: return NULL; } +static void blk_mq_free_bitmap(struct blk_mq_ctxmap *bitmap) +{ + kfree(bitmap->map); +} + +static int blk_mq_alloc_bitmap(struct blk_mq_ctxmap *bitmap, int node) +{ + unsigned int bpw = 8, total, num_maps, i; + + bitmap->bits_per_word = bpw; + + num_maps = ALIGN(nr_cpu_ids, bpw) / bpw; + bitmap->map = kzalloc_node(num_maps * sizeof(struct blk_align_bitmap), + GFP_KERNEL, node); + if (!bitmap->map) + return -ENOMEM; + + bitmap->map_size = num_maps; + + total = nr_cpu_ids; + for (i = 0; i < num_maps; i++) { + bitmap->map[i].depth = min(total, bitmap->bits_per_word); + total -= bitmap->map[i].depth; + } + + return 0; +} + static int blk_mq_init_hw_queues(struct request_queue *q, struct blk_mq_tag_set *set) { @@ -1308,7 +1377,6 @@ static int blk_mq_init_hw_queues(struct request_queue *q, * Initialize hardware queues */ queue_for_each_hw_ctx(q, hctx, i) { - unsigned int num_maps; int node; node = hctx->numa_node; @@ -1339,13 +1407,9 @@ static int blk_mq_init_hw_queues(struct request_queue *q, if (!hctx->ctxs) break; - num_maps = ALIGN(nr_cpu_ids, BITS_PER_LONG) / BITS_PER_LONG; - hctx->ctx_map = kzalloc_node(num_maps * sizeof(unsigned long), - GFP_KERNEL, node); - if (!hctx->ctx_map) + if (blk_mq_alloc_bitmap(&hctx->ctx_map, node)) break; - hctx->nr_ctx_map = num_maps; hctx->nr_ctx = 0; if (set->ops->init_hctx && @@ -1368,7 +1432,7 @@ static int blk_mq_init_hw_queues(struct request_queue *q, blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); kfree(hctx->ctxs); - kfree(hctx->ctx_map); + blk_mq_free_bitmap(&hctx->ctx_map); } return 1; @@ -1542,7 +1606,6 @@ void blk_mq_free_queue(struct request_queue *q) int i; queue_for_each_hw_ctx(q, hctx, i) { - kfree(hctx->ctx_map); kfree(hctx->ctxs); blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); if (q->mq_ops->exit_hctx) diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index f83d15f6e1c1..952e558ee598 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -11,6 +11,12 @@ struct blk_mq_cpu_notifier { void (*notify)(void *data, unsigned long action, unsigned int cpu); }; +struct blk_mq_ctxmap { + unsigned int map_size; + unsigned int bits_per_word; + struct blk_align_bitmap *map; +}; + struct blk_mq_hw_ctx { struct { spinlock_t lock; @@ -31,8 +37,8 @@ struct blk_mq_hw_ctx { void *driver_data; - unsigned int nr_ctx_map; - unsigned long *ctx_map; + struct blk_mq_ctxmap ctx_map; + unsigned int nr_ctx; struct blk_mq_ctx **ctxs; -- cgit v1.2.3 From 61f38db3e3c0e4c3be0858750e2cabeadaecac0c Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Sat, 26 Apr 2014 23:15:35 -0700 Subject: rcu: Provide API to suppress stall warnings while sysrc runs Some sysrq handlers can run for a long time, because they dump a lot of data onto a serial console. Having RCU stall warnings pop up in the middle of them only makes the problem worse. This commit provides rcu_sysrq_start() and rcu_sysrq_end() APIs to temporarily suppress RCU CPU stall warnings while a sysrq request is handled. Signed-off-by: Rik van Riel [ paulmck: Fix TINY_RCU build error. ] Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 12 ++++++++++++ kernel/rcu/update.c | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 9ccd644c1234..5a75d19aa661 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -248,6 +248,18 @@ void rcu_idle_exit(void); void rcu_irq_enter(void); void rcu_irq_exit(void); +#ifdef CONFIG_RCU_STALL_COMMON +void rcu_sysrq_start(void); +void rcu_sysrq_end(void); +#else /* #ifdef CONFIG_RCU_STALL_COMMON */ +static inline void rcu_sysrq_start(void) +{ +} +static inline void rcu_sysrq_end(void) +{ +} +#endif /* #else #ifdef CONFIG_RCU_STALL_COMMON */ + #ifdef CONFIG_RCU_USER_QS void rcu_user_enter(void); void rcu_user_exit(void); diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index ed7a0d72562c..a2aeb4df0f60 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -320,6 +320,18 @@ int rcu_jiffies_till_stall_check(void) return till_stall_check * HZ + RCU_STALL_DELAY_DELTA; } +void rcu_sysrq_start(void) +{ + if (!rcu_cpu_stall_suppress) + rcu_cpu_stall_suppress = 2; +} + +void rcu_sysrq_end(void) +{ + if (rcu_cpu_stall_suppress == 2) + rcu_cpu_stall_suppress = 0; +} + static int rcu_panic(struct notifier_block *this, unsigned long ev, void *ptr) { rcu_cpu_stall_suppress = 1; -- cgit v1.2.3 From b54197c43db88f4436717f554d623189cddce29e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 1 May 2014 16:51:50 +0200 Subject: virtio_scsi: use cmd_size Taken almost entirely from Nicholas Bellinger's scsi-mq conversion. Signed-off-by: Christoph Hellwig Acked-by: Paolo Bonzini Reviewed-by: Nicholas Bellinger --- drivers/scsi/virtio_scsi.c | 25 +++++++------------------ include/scsi/scsi_cmnd.h | 9 +++++++++ 2 files changed, 16 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index fc054935eb1f..d4727b339474 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -199,7 +199,6 @@ static void virtscsi_complete_cmd(struct virtio_scsi *vscsi, void *buf) set_driver_byte(sc, DRIVER_SENSE); } - mempool_free(cmd, virtscsi_cmd_pool); sc->scsi_done(sc); atomic_dec(&tgt->reqs); @@ -242,8 +241,6 @@ static void virtscsi_complete_free(struct virtio_scsi *vscsi, void *buf) if (cmd->comp) complete_all(cmd->comp); - else - mempool_free(cmd, virtscsi_cmd_pool); } static void virtscsi_ctrl_done(struct virtqueue *vq) @@ -459,10 +456,9 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi, struct virtio_scsi_vq *req_vq, struct scsi_cmnd *sc) { - struct virtio_scsi_cmd *cmd; - int ret; - struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); + struct virtio_scsi_cmd *cmd = scsi_cmd_priv(sc); + BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize); /* TODO: check feature bit and fail if unsupported? */ @@ -471,11 +467,6 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi, dev_dbg(&sc->device->sdev_gendev, "cmd %p CDB: %#02x\n", sc, sc->cmnd[0]); - ret = SCSI_MLQUEUE_HOST_BUSY; - cmd = mempool_alloc(virtscsi_cmd_pool, GFP_ATOMIC); - if (!cmd) - goto out; - memset(cmd, 0, sizeof(*cmd)); cmd->sc = sc; cmd->req.cmd = (struct virtio_scsi_cmd_req){ @@ -494,13 +485,9 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi, if (virtscsi_kick_cmd(req_vq, cmd, sizeof cmd->req.cmd, sizeof cmd->resp.cmd, - GFP_ATOMIC) == 0) - ret = 0; - else - mempool_free(cmd, virtscsi_cmd_pool); - -out: - return ret; + GFP_ATOMIC) != 0) + return SCSI_MLQUEUE_HOST_BUSY; + return 0; } static int virtscsi_queuecommand_single(struct Scsi_Host *sh, @@ -642,6 +629,7 @@ static struct scsi_host_template virtscsi_host_template_single = { .name = "Virtio SCSI HBA", .proc_name = "virtio_scsi", .this_id = -1, + .cmd_size = sizeof(struct virtio_scsi_cmd), .queuecommand = virtscsi_queuecommand_single, .eh_abort_handler = virtscsi_abort, .eh_device_reset_handler = virtscsi_device_reset, @@ -658,6 +646,7 @@ static struct scsi_host_template virtscsi_host_template_multi = { .name = "Virtio SCSI HBA", .proc_name = "virtio_scsi", .this_id = -1, + .cmd_size = sizeof(struct virtio_scsi_cmd), .queuecommand = virtscsi_queuecommand_multi, .eh_abort_handler = virtscsi_abort, .eh_device_reset_handler = virtscsi_device_reset, diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index dd7c998221b3..e016e2ac38df 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -133,6 +133,15 @@ struct scsi_cmnd { unsigned char tag; /* SCSI-II queued command tag */ }; +/* + * Return the driver private allocation behind the command. + * Only works if cmd_size is set in the host template. + */ +static inline void *scsi_cmd_priv(struct scsi_cmnd *cmd) +{ + return cmd + 1; +} + /* make sure not to use it with REQ_TYPE_BLOCK_PC commands */ static inline struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) { -- cgit v1.2.3 From 5533e0114425dcdb878f11b291f2727af8667a7c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 14 May 2014 19:33:07 -0400 Subject: cgroup: disallow debug controller on the default hierarchy The debug controller, as its name suggests, exposes cgroup core internals to userland to aid debugging. Unfortunately, except for the name, there's no provision to prevent its usage in production configurations and the controller is widely enabled and mounted leaking internal details to userland. Like most other debug information, the information exposed by debug isn't interesting even for debugging itself once the related parts are working reliably. This controller has no reason for existing. This patch implements cgrp_dfl_root_inhibit_ss_mask which can suppress specific subsystems on the default hierarchy and adds the debug subsystem to it so that it can be gradually deprecated as usages move towards the unified hierarchy. Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 2 ++ include/linux/cgroup_subsys.h | 11 +++++++---- kernel/cgroup.c | 21 ++++++++++++++++++--- 3 files changed, 27 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 4afe544d3547..8a111dd42d7a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -305,6 +305,8 @@ enum { * the flag is not created. * * - blkcg: blk-throttle becomes properly hierarchical. + * + * - debug: disallowed on the default hierarchy. */ CGRP_ROOT_SANE_BEHAVIOR = (1 << 0), diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 768fe44e19f0..98c4f9b12b03 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -7,10 +7,6 @@ SUBSYS(cpuset) #endif -#if IS_ENABLED(CONFIG_CGROUP_DEBUG) -SUBSYS(debug) -#endif - #if IS_ENABLED(CONFIG_CGROUP_SCHED) SUBSYS(cpu) #endif @@ -50,6 +46,13 @@ SUBSYS(net_prio) #if IS_ENABLED(CONFIG_CGROUP_HUGETLB) SUBSYS(hugetlb) #endif + +/* + * The following subsystems are not supported on the default hierarchy. + */ +#if IS_ENABLED(CONFIG_CGROUP_DEBUG) +SUBSYS(debug) +#endif /* * DO NOT ADD ANY SUBSYSTEM WITHOUT EXPLICIT ACKS FROM CGROUP MAINTAINERS. */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 082bb842b11a..a5f75ac4e793 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -148,6 +148,13 @@ struct cgroup_root cgrp_dfl_root; */ static bool cgrp_dfl_root_visible; +/* some controllers are not supported in the default hierarchy */ +static const unsigned int cgrp_dfl_root_inhibit_ss_mask = 0 +#ifdef CONFIG_CGROUP_DEBUG + | (1 << debug_cgrp_id) +#endif + ; + /* The list of hierarchy roots */ static LIST_HEAD(cgroup_roots); @@ -1126,6 +1133,7 @@ static void cgroup_clear_dir(struct cgroup *cgrp, unsigned int subsys_mask) static int rebind_subsystems(struct cgroup_root *dst_root, unsigned int ss_mask) { struct cgroup_subsys *ss; + unsigned int tmp_ss_mask; int ssid, i, ret; lockdep_assert_held(&cgroup_mutex); @@ -1143,7 +1151,12 @@ static int rebind_subsystems(struct cgroup_root *dst_root, unsigned int ss_mask) return -EBUSY; } - ret = cgroup_populate_dir(&dst_root->cgrp, ss_mask); + /* skip creating root files on dfl_root for inhibited subsystems */ + tmp_ss_mask = ss_mask; + if (dst_root == &cgrp_dfl_root) + tmp_ss_mask &= ~cgrp_dfl_root_inhibit_ss_mask; + + ret = cgroup_populate_dir(&dst_root->cgrp, tmp_ss_mask); if (ret) { if (dst_root != &cgrp_dfl_root) return ret; @@ -2426,7 +2439,8 @@ static int cgroup_root_controllers_show(struct seq_file *seq, void *v) { struct cgroup *cgrp = seq_css(seq)->cgroup; - cgroup_print_ss_mask(seq, cgrp->root->subsys_mask); + cgroup_print_ss_mask(seq, cgrp->root->subsys_mask & + ~cgrp_dfl_root_inhibit_ss_mask); return 0; } @@ -2564,7 +2578,8 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, if (tok[0] == '\0') continue; for_each_subsys(ss, ssid) { - if (ss->disabled || strcmp(tok + 1, ss->name)) + if (ss->disabled || strcmp(tok + 1, ss->name) || + ((1 << ss->id) & cgrp_dfl_root_inhibit_ss_mask)) continue; if (*tok == '+') { -- cgit v1.2.3 From 4f4aa2ec24dc45881849833a439558d3a378028c Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sun, 18 May 2014 00:22:38 +0200 Subject: ssb: sprom: add dev_id field for value overriding standard ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices may have different features despite sharing the same ID (e.g. PCI ID). For example 14e4:4331 is usually a dual band, but this can be "limited". Device with "pci/x/y/devid=0x4332" supports 2.4 GHz only. Similarly 0x4333 will mean support for 5 GHz only. Add entry in SPROM so info described above can be extracted and stored. Signed-off-by: Rafał Miłecki Acked-by: Hauke Mehrtens Signed-off-by: John W. Linville --- arch/mips/bcm47xx/sprom.c | 1 + include/linux/ssb/ssb.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c index a8b5408dd349..da4cdb16844e 100644 --- a/arch/mips/bcm47xx/sprom.c +++ b/arch/mips/bcm47xx/sprom.c @@ -168,6 +168,7 @@ static void nvram_read_alpha2(const char *prefix, const char *name, static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom, const char *prefix, bool fallback) { + nvram_read_u16(prefix, NULL, "devid", &sprom->dev_id, 0, fallback); nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff, fallback); nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff, fallback); nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff, fallback); diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 07ef9b82b66d..4568a5cc9ab8 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -33,6 +33,7 @@ struct ssb_sprom { u8 et1phyaddr; /* MII address for enet1 */ u8 et0mdcport; /* MDIO for enet0 */ u8 et1mdcport; /* MDIO for enet1 */ + u16 dev_id; /* Device ID overriding e.g. PCI ID */ u16 board_rev; /* Board revision number from SPROM. */ u16 board_num; /* Board number from SPROM. */ u16 board_type; /* Board type from SPROM. */ -- cgit v1.2.3 From d8c64c21b4221efe087af3b183563b47d8f7d280 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 14 May 2014 21:04:16 +0200 Subject: clk: berlin: add binding include for Berlin SoC clock ids This adds a dt-binding include for Marvell Berlin BG2/BG2CD and BG2Q core clock IDs. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Alexandre Belloni --- include/dt-bindings/clock/berlin2.h | 45 ++++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/berlin2q.h | 31 +++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 include/dt-bindings/clock/berlin2.h create mode 100644 include/dt-bindings/clock/berlin2q.h (limited to 'include') diff --git a/include/dt-bindings/clock/berlin2.h b/include/dt-bindings/clock/berlin2.h new file mode 100644 index 000000000000..0c30800175df --- /dev/null +++ b/include/dt-bindings/clock/berlin2.h @@ -0,0 +1,45 @@ +/* + * Berlin2 BG2/BG2CD clock tree IDs + */ + +#define CLKID_SYS 0 +#define CLKID_CPU 1 +#define CLKID_DRMFIGO 2 +#define CLKID_CFG 3 +#define CLKID_GFX 4 +#define CLKID_ZSP 5 +#define CLKID_PERIF 6 +#define CLKID_PCUBE 7 +#define CLKID_VSCOPE 8 +#define CLKID_NFC_ECC 9 +#define CLKID_VPP 10 +#define CLKID_APP 11 +#define CLKID_AUDIO0 12 +#define CLKID_AUDIO2 13 +#define CLKID_AUDIO3 14 +#define CLKID_AUDIO1 15 +#define CLKID_GFX3D_CORE 16 +#define CLKID_GFX3D_SYS 17 +#define CLKID_ARC 18 +#define CLKID_VIP 19 +#define CLKID_SDIO0XIN 20 +#define CLKID_SDIO1XIN 21 +#define CLKID_GFX3D_EXTRA 22 +#define CLKID_GC360 23 +#define CLKID_SDIO_DLLMST 24 +#define CLKID_GETH0 25 +#define CLKID_GETH1 26 +#define CLKID_SATA 27 +#define CLKID_AHBAPB 28 +#define CLKID_USB0 29 +#define CLKID_USB1 30 +#define CLKID_PBRIDGE 31 +#define CLKID_SDIO0 32 +#define CLKID_SDIO1 33 +#define CLKID_NFC 34 +#define CLKID_SMEMC 35 +#define CLKID_AUDIOHD 36 +#define CLKID_VIDEO0 37 +#define CLKID_VIDEO1 38 +#define CLKID_VIDEO2 39 +#define CLKID_TWD 40 diff --git a/include/dt-bindings/clock/berlin2q.h b/include/dt-bindings/clock/berlin2q.h new file mode 100644 index 000000000000..287fc3b4afb2 --- /dev/null +++ b/include/dt-bindings/clock/berlin2q.h @@ -0,0 +1,31 @@ +/* + * Berlin2 BG2Q clock tree IDs + */ + +#define CLKID_SYS 0 +#define CLKID_DRMFIGO 1 +#define CLKID_CFG 2 +#define CLKID_GFX2D 3 +#define CLKID_ZSP 4 +#define CLKID_PERIF 5 +#define CLKID_PCUBE 6 +#define CLKID_VSCOPE 7 +#define CLKID_NFC_ECC 8 +#define CLKID_VPP 9 +#define CLKID_APP 10 +#define CLKID_SDIO0XIN 11 +#define CLKID_SDIO1XIN 12 +#define CLKID_GFX2DAXI 13 +#define CLKID_GETH0 14 +#define CLKID_SATA 15 +#define CLKID_AHBAPB 16 +#define CLKID_USB0 17 +#define CLKID_USB1 18 +#define CLKID_USB2 19 +#define CLKID_USB3 20 +#define CLKID_PBRIDGE 21 +#define CLKID_SDIO 22 +#define CLKID_NFC 23 +#define CLKID_SMEMC 24 +#define CLKID_PCIE 25 +#define CLKID_TWD 26 -- cgit v1.2.3 From 57be1f3f3ec1ccab6432615ca161c4c9ece2a2aa Mon Sep 17 00:00:00 2001 From: Hiren Tandel Date: Mon, 5 May 2014 19:43:31 +0900 Subject: NFC: Add RAW socket type support for SOCKPROTO_RAW This allows for a more generic NFC sniffing by using SOCKPROTO_RAW SOCK_RAW to read RAW NFC frames. This is for sniffing anything but LLCP (HCI, NCI, etc...). Signed-off-by: Hiren Tandel Signed-off-by: Rahul Tank Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 3 ++ include/uapi/linux/nfc.h | 16 ++++++--- net/nfc/llcp_commands.c | 2 +- net/nfc/llcp_core.c | 11 +++--- net/nfc/nfc.h | 6 ++++ net/nfc/rawsock.c | 94 +++++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 117 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 2e8b40c16274..6c583e244de2 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -264,4 +264,7 @@ int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type); int nfc_remove_se(struct nfc_dev *dev, u32 se_idx); struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx); +void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb, + u8 payload_type, u8 direction); + #endif /* __NET_NFC_H */ diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 9789dc95b6a8..9b19b4461928 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -273,11 +273,19 @@ struct sockaddr_nfc_llcp { * First byte is the adapter index * Second byte contains flags * - 0x01 - Direction (0=RX, 1=TX) - * - 0x02-0x80 - Reserved + * - 0x02-0x04 - Payload type (000=LLCP, 001=NCI, 010=HCI, 011=Digital, + * 100=Proprietary) + * - 0x05-0x80 - Reserved **/ -#define NFC_LLCP_RAW_HEADER_SIZE 2 -#define NFC_LLCP_DIRECTION_RX 0x00 -#define NFC_LLCP_DIRECTION_TX 0x01 +#define NFC_RAW_HEADER_SIZE 2 +#define NFC_DIRECTION_RX 0x00 +#define NFC_DIRECTION_TX 0x01 + +#define RAW_PAYLOAD_LLCP 0 +#define RAW_PAYLOAD_NCI 1 +#define RAW_PAYLOAD_HCI 2 +#define RAW_PAYLOAD_DIGITAL 3 +#define RAW_PAYLOAD_PROPRIETARY 4 /* socket option names */ #define NFC_LLCP_RW 0 diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index bec6ed15f503..a3ad69a4c648 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -387,7 +387,7 @@ int nfc_llcp_send_symm(struct nfc_dev *dev) __net_timestamp(skb); - nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); + nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX); return nfc_data_exchange(dev, local->target_idx, skb, nfc_llcp_recv, local); diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index b4671958fcf9..f6278da68763 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -680,16 +680,17 @@ void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local, continue; if (skb_copy == NULL) { - skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE, + skb_copy = __pskb_copy(skb, NFC_RAW_HEADER_SIZE, GFP_ATOMIC); if (skb_copy == NULL) continue; - data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE); + data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE); data[0] = local->dev ? local->dev->idx : 0xFF; - data[1] = direction; + data[1] = direction & 0x01; + data[1] |= (RAW_PAYLOAD_LLCP << 1); } nskb = skb_clone(skb_copy, GFP_ATOMIC); @@ -747,7 +748,7 @@ static void nfc_llcp_tx_work(struct work_struct *work) __net_timestamp(skb); nfc_llcp_send_to_raw_sock(local, skb, - NFC_LLCP_DIRECTION_TX); + NFC_DIRECTION_TX); ret = nfc_data_exchange(local->dev, local->target_idx, skb, nfc_llcp_recv, local); @@ -1476,7 +1477,7 @@ static void nfc_llcp_rx_work(struct work_struct *work) __net_timestamp(skb); - nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX); + nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_RX); nfc_llcp_rx_skb(local, skb); diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 9d6e74f7e6b3..88d60064890e 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -40,6 +40,12 @@ struct nfc_rawsock { struct work_struct tx_work; bool tx_work_scheduled; }; + +struct nfc_sock_list { + struct hlist_head head; + rwlock_t lock; +}; + #define nfc_rawsock(sk) ((struct nfc_rawsock *) sk) #define to_rawsock_sk(_tx_work) \ ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work)) diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index c27a6e86cae4..8627c75063e2 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -27,6 +27,24 @@ #include "nfc.h" +static struct nfc_sock_list raw_sk_list = { + .lock = __RW_LOCK_UNLOCKED(raw_sk_list.lock) +}; + +void nfc_sock_link(struct nfc_sock_list *l, struct sock *sk) +{ + write_lock(&l->lock); + sk_add_node(sk, &l->head); + write_unlock(&l->lock); +} + +void nfc_sock_unlink(struct nfc_sock_list *l, struct sock *sk) +{ + write_lock(&l->lock); + sk_del_node_init(sk); + write_unlock(&l->lock); +} + static void rawsock_write_queue_purge(struct sock *sk) { pr_debug("sk=%p\n", sk); @@ -57,6 +75,9 @@ static int rawsock_release(struct socket *sock) if (!sk) return 0; + if (sock->type == SOCK_RAW) + nfc_sock_unlink(&raw_sk_list, sk); + sock_orphan(sk); sock_put(sk); @@ -275,6 +296,26 @@ static const struct proto_ops rawsock_ops = { .mmap = sock_no_mmap, }; +static const struct proto_ops rawsock_raw_ops = { + .family = PF_NFC, + .owner = THIS_MODULE, + .release = rawsock_release, + .bind = sock_no_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = datagram_poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = rawsock_recvmsg, + .mmap = sock_no_mmap, +}; + static void rawsock_destruct(struct sock *sk) { pr_debug("sk=%p\n", sk); @@ -300,10 +341,13 @@ static int rawsock_create(struct net *net, struct socket *sock, pr_debug("sock=%p\n", sock); - if (sock->type != SOCK_SEQPACKET) + if ((sock->type != SOCK_SEQPACKET) && (sock->type != SOCK_RAW)) return -ESOCKTNOSUPPORT; - sock->ops = &rawsock_ops; + if (sock->type == SOCK_RAW) + sock->ops = &rawsock_raw_ops; + else + sock->ops = &rawsock_ops; sk = sk_alloc(net, PF_NFC, GFP_ATOMIC, nfc_proto->proto); if (!sk) @@ -313,13 +357,53 @@ static int rawsock_create(struct net *net, struct socket *sock, sk->sk_protocol = nfc_proto->id; sk->sk_destruct = rawsock_destruct; sock->state = SS_UNCONNECTED; - - INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); - nfc_rawsock(sk)->tx_work_scheduled = false; + if (sock->type == SOCK_RAW) + nfc_sock_link(&raw_sk_list, sk); + else { + INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work); + nfc_rawsock(sk)->tx_work_scheduled = false; + } return 0; } +void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb, + u8 payload_type, u8 direction) +{ + struct sk_buff *skb_copy = NULL, *nskb; + struct sock *sk; + u8 *data; + + read_lock(&raw_sk_list.lock); + + sk_for_each(sk, &raw_sk_list.head) { + if (!skb_copy) { + skb_copy = __pskb_copy(skb, NFC_RAW_HEADER_SIZE, + GFP_ATOMIC); + if (!skb_copy) + continue; + + data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE); + + data[0] = dev ? dev->idx : 0xFF; + data[1] = direction & 0x01; + data[1] |= (payload_type << 1); + } + + nskb = skb_clone(skb_copy, GFP_ATOMIC); + if (!nskb) + continue; + + if (sock_queue_rcv_skb(sk, nskb)) + kfree_skb(nskb); + } + + read_unlock(&raw_sk_list.lock); + + kfree_skb(skb_copy); +} +EXPORT_SYMBOL(nfc_send_to_raw_sock); + static struct proto rawsock_proto = { .name = "NFC_RAW", .owner = THIS_MODULE, -- cgit v1.2.3 From 4b466297f08d01e6bb3d2815a919e7e0b2975d9e Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Mon, 12 May 2014 20:16:27 +0200 Subject: ARM: dts: Change IOPAD macro's for OMAP4/5 The OMAP4/5 TRMs primarily list address offsets from the padconf physical address (which is not driver base address) and not always the absolute physical address for padconf registers like some other OMAP TRMs. So create a new macro to use this offset and to avoid confusion between different OMAP parts. For more information, see the tables in TRM for named something like "Device Core Control Module Pad Configuration Register Fields" and "Device Wake-Up Control Module Pad Configuration Register Fields" Note that we now also have to update cm-t54 for the fixed up offsets. Signed-off-by: Joachim Eastwood [tony@atomide.com: updated comments, updated cm-t54] Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/omap5-cm-t54.dts | 66 ++++++++++++++++++------------------- arch/arm/boot/dts/omap5-sbc-t54.dts | 8 ++--- include/dt-bindings/pinctrl/omap.h | 13 +++++--- 3 files changed, 46 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/omap5-cm-t54.dts b/arch/arm/boot/dts/omap5-cm-t54.dts index 8fec6a49b688..b8698ca68647 100644 --- a/arch/arm/boot/dts/omap5-cm-t54.dts +++ b/arch/arm/boot/dts/omap5-cm-t54.dts @@ -77,71 +77,71 @@ led_gpio_pins: pinmux_led_gpio_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x28b0, PIN_OUTPUT | MUX_MODE6) /* hsi2_caflag.gpio3_80 */ + OMAP5_IOPAD(0x00b0, PIN_OUTPUT | MUX_MODE6) /* hsi2_caflag.gpio3_80 */ >; }; i2c1_pins: pinmux_i2c1_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x29f2, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c1_pmic_scl */ - OMAP5_CORE_IOPAD(0x29f4, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c1_pmic_sda */ + OMAP5_IOPAD(0x01f2, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c1_pmic_scl */ + OMAP5_IOPAD(0x01f4, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c1_pmic_sda */ >; }; mmc1_pins: pinmux_mmc1_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x29e2, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_clk */ - OMAP5_CORE_IOPAD(0x29e4, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_cmd */ - OMAP5_CORE_IOPAD(0x29e6, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_data2 */ - OMAP5_CORE_IOPAD(0x29e8, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_data3 */ - OMAP5_CORE_IOPAD(0x29ea, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_data0 */ - OMAP5_CORE_IOPAD(0x29ec, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_data1 */ + OMAP5_IOPAD(0x01e2, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_clk */ + OMAP5_IOPAD(0x01e4, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_cmd */ + OMAP5_IOPAD(0x01e6, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_data2 */ + OMAP5_IOPAD(0x01e8, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_data3 */ + OMAP5_IOPAD(0x01ea, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_data0 */ + OMAP5_IOPAD(0x01ec, PIN_INPUT_PULLUP | MUX_MODE0) /* sdcard_data1 */ >; }; mmc2_pins: pinmux_mmc2_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x2840, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_clk */ - OMAP5_CORE_IOPAD(0x2842, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_cmd */ - OMAP5_CORE_IOPAD(0x2844, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data0 */ - OMAP5_CORE_IOPAD(0x2846, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data1 */ - OMAP5_CORE_IOPAD(0x2848, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data2 */ - OMAP5_CORE_IOPAD(0x284a, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data3 */ - OMAP5_CORE_IOPAD(0x284c, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data4 */ - OMAP5_CORE_IOPAD(0x284e, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data5 */ - OMAP5_CORE_IOPAD(0x2850, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data6 */ - OMAP5_CORE_IOPAD(0x2852, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data7 */ + OMAP5_IOPAD(0x0040, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_clk */ + OMAP5_IOPAD(0x0042, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_cmd */ + OMAP5_IOPAD(0x0044, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data0 */ + OMAP5_IOPAD(0x0046, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data1 */ + OMAP5_IOPAD(0x0048, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data2 */ + OMAP5_IOPAD(0x004a, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data3 */ + OMAP5_IOPAD(0x004c, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data4 */ + OMAP5_IOPAD(0x004e, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data5 */ + OMAP5_IOPAD(0x0050, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data6 */ + OMAP5_IOPAD(0x0052, PIN_INPUT_PULLUP | MUX_MODE0) /* emmc_data7 */ >; }; mmc3_pins: pinmux_mmc3_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x29a4, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_clk */ - OMAP5_CORE_IOPAD(0x29a6, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_cmd */ - OMAP5_CORE_IOPAD(0x29a8, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_data0 */ - OMAP5_CORE_IOPAD(0x29aa, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_data1 */ - OMAP5_CORE_IOPAD(0x29ac, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_data2 */ - OMAP5_CORE_IOPAD(0x29ae, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_data3 */ + OMAP5_IOPAD(0x01a4, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_clk */ + OMAP5_IOPAD(0x01a6, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_cmd */ + OMAP5_IOPAD(0x01a8, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_data0 */ + OMAP5_IOPAD(0x01aa, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_data1 */ + OMAP5_IOPAD(0x01ac, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_data2 */ + OMAP5_IOPAD(0x01ae, PIN_INPUT_PULLUP | MUX_MODE0) /* wlsdio_data3 */ >; }; wlan_gpios_pins: pinmux_wlan_gpios_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x299c, PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* gpio4_109 */ - OMAP5_CORE_IOPAD(0x299e, PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* gpio4_110 */ + OMAP5_IOPAD(0x019c, PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* gpio4_109 */ + OMAP5_IOPAD(0x019e, PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* gpio4_110 */ >; }; usbhost_pins: pinmux_usbhost_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x28c4, PIN_INPUT | MUX_MODE0) /* usbb2_hsic_strobe */ - OMAP5_CORE_IOPAD(0x28c6, PIN_INPUT | MUX_MODE0) /* usbb2_hsic_data */ + OMAP5_IOPAD(0x00c4, PIN_INPUT | MUX_MODE0) /* usbb2_hsic_strobe */ + OMAP5_IOPAD(0x00c6, PIN_INPUT | MUX_MODE0) /* usbb2_hsic_data */ - OMAP5_CORE_IOPAD(0x29dc, PIN_INPUT | MUX_MODE0) /* usbb3_hsic_strobe */ - OMAP5_CORE_IOPAD(0x29de, PIN_INPUT | MUX_MODE0) /* usbb3_hsic_data */ + OMAP5_IOPAD(0x01dc, PIN_INPUT | MUX_MODE0) /* usbb3_hsic_strobe */ + OMAP5_IOPAD(0x01de, PIN_INPUT | MUX_MODE0) /* usbb3_hsic_data */ - OMAP5_CORE_IOPAD(0x28a8, PIN_OUTPUT | MUX_MODE6) /* hsi2_caready.gpio3_76 */ - OMAP5_CORE_IOPAD(0x28b6, PIN_OUTPUT | MUX_MODE6) /* hsi2_acdata.gpio3_83 */ + OMAP5_IOPAD(0x00a8, PIN_OUTPUT | MUX_MODE6) /* hsi2_caready.gpio3_76 */ + OMAP5_IOPAD(0x00b6, PIN_OUTPUT | MUX_MODE6) /* hsi2_acdata.gpio3_83 */ >; }; }; diff --git a/arch/arm/boot/dts/omap5-sbc-t54.dts b/arch/arm/boot/dts/omap5-sbc-t54.dts index 9fd0b3c3abac..aa98fea3f2b3 100644 --- a/arch/arm/boot/dts/omap5-sbc-t54.dts +++ b/arch/arm/boot/dts/omap5-sbc-t54.dts @@ -12,15 +12,15 @@ &omap5_pmx_core { i2c4_pins: pinmux_i2c4_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x28f8, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c4_scl */ - OMAP5_CORE_IOPAD(0x28fa, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c4_sda */ + OMAP5_IOPAD(0x00f8, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c4_scl */ + OMAP5_IOPAD(0x00fa, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c4_sda */ >; }; mmc1_aux_pins: pinmux_mmc1_aux_pins { pinctrl-single,pins = < - OMAP5_CORE_IOPAD(0x2974, PIN_INPUT_PULLUP | MUX_MODE6) /* gpio8_228 */ - OMAP5_CORE_IOPAD(0x2976, PIN_INPUT_PULLUP | MUX_MODE6) /* gpio8_229 */ + OMAP5_IOPAD(0x0174, PIN_INPUT_PULLUP | MUX_MODE6) /* gpio8_228 */ + OMAP5_IOPAD(0x0176, PIN_INPUT_PULLUP | MUX_MODE6) /* gpio8_229 */ >; }; }; diff --git a/include/dt-bindings/pinctrl/omap.h b/include/dt-bindings/pinctrl/omap.h index b04528cd033c..827e80964a35 100644 --- a/include/dt-bindings/pinctrl/omap.h +++ b/include/dt-bindings/pinctrl/omap.h @@ -62,12 +62,17 @@ #define OMAP3630_CORE2_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x25a0) (val) #define OMAP3_WKUP_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x2a00) (val) #define AM33XX_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) -#define OMAP4_CORE_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0040) (val) -#define OMAP4_WKUP_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0xe040) (val) #define AM4372_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) -#define OMAP5_CORE_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x2840) (val) -#define OMAP5_WKUP_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0xc840) (val) #define DRA7XX_CORE_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x3400) (val) +/* + * Macros to allow using the offset from the padconf physical address + * instead of the offset from padconf base. + */ +#define OMAP_PADCONF_OFFSET(offset, base_offset) ((offset) - (base_offset)) + +#define OMAP4_IOPAD(offset, val) OMAP_PADCONF_OFFSET((offset), 0x0040) (val) +#define OMAP5_IOPAD(offset, val) OMAP_PADCONF_OFFSET((offset), 0x0040) (val) + #endif -- cgit v1.2.3 From 8d9e9857c576d8d710ae6a6152a6ddcd29772bb1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 19 May 2014 14:34:09 +0100 Subject: goldfish: fix >> 32 warning We should be checking for a 64bit platform not 64bit DMA address types in the case of Goldfish. The Goldfish virtual platform is either 32/32 or 64/64. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/goldfish.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/goldfish.h b/include/linux/goldfish.h index 9cc28902b54c..569236e6b2bc 100644 --- a/include/linux/goldfish.h +++ b/include/linux/goldfish.h @@ -7,7 +7,7 @@ static inline void gf_write64(unsigned long data, void __iomem *portl, void __iomem *porth) { writel((u32)data, portl); -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT +#ifdef CONFIG_64BIT writel(data>>32, porth); #endif } -- cgit v1.2.3 From 5080a08d0f8a4b2ba3a15e5ddc5ece84a444cad8 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 21 Mar 2014 10:46:39 +0100 Subject: mmc: mmci: Enforce max frequency configuration through DT Remove the option to provide a maximum frequency as platform data, enforce it through DT. Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 5 +---- include/linux/amba/mmci.h | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 9c60325f1a30..758efea184c9 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1500,13 +1500,10 @@ static int mmci_probe(struct amba_device *dev, * If no maximum operating frequency is supplied, fall back to use * the module parameter, which has a (low) default value in case it * is not specified. Either value must not exceed the clock rate into - * the block, of course. Also note that DT takes precedence over - * platform data. + * the block, of course. */ if (mmc->f_max) mmc->f_max = min(host->mclk, mmc->f_max); - else if (plat->f_max) - mmc->f_max = min(host->mclk, plat->f_max); else mmc->f_max = min(host->mclk, fmax); dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index b992fc931295..3f95d32d5277 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -12,9 +12,6 @@ struct dma_chan; /** * struct mmci_platform_data - platform configuration for the MMCI * (also known as PL180) block. - * @f_max: the maximum operational frequency for this host in this - * platform configuration. When this is specified it takes precedence - * over the module parameter for the same frequency. * @ocr_mask: available voltages on the 4 pins from the block, this * is ignored if a regulator is used, see the MMC_VDD_* masks in * mmc/host.h @@ -42,7 +39,6 @@ struct dma_chan; * bidirectional channel */ struct mmci_platform_data { - unsigned int f_max; unsigned int ocr_mask; int (*ios_handler)(struct device *, struct mmc_ios *); unsigned int (*status)(struct device *); -- cgit v1.2.3 From f1af9d3af308145478749194346f11efad1134b2 Mon Sep 17 00:00:00 2001 From: Philipp Hachtmann Date: Wed, 29 Jan 2014 18:16:01 +0100 Subject: mm/memblock: Do some refactoring, enhance API Refactor the memblock code and extend the memblock API to make it more flexible. With the extended API it is simple to define and work with additional memory lists. The static functions memblock_add_region and __memblock_remove are renamed to memblock_add_range and meblock_remove_range and added to the memblock API. The __next_free_mem_range and __next_free_mem_range_rev functions are replaced with calls to the more generic list walkers __next_mem_range and __next_mem_range_rev. To walk an arbitrary memory list two new macros for_each_mem_range and for_each_mem_range_rev are added. These new macros are used to define for_each_free_mem_range and for_each_free_mem_range_reverse. Signed-off-by: Philipp Hachtmann Signed-off-by: Martin Schwidefsky --- include/linux/memblock.h | 75 ++++++++++++++---- mm/memblock.c | 193 ++++++++++++++++++++++++++++++----------------- 2 files changed, 183 insertions(+), 85 deletions(-) (limited to 'include') diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 8a20a51ed42d..f669016874b3 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -71,6 +71,63 @@ int memblock_reserve(phys_addr_t base, phys_addr_t size); void memblock_trim_memory(phys_addr_t align); int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size); int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size); + +/* Low level functions */ +int memblock_add_range(struct memblock_type *type, + phys_addr_t base, phys_addr_t size, + int nid, unsigned long flags); + +int memblock_remove_range(struct memblock_type *type, + phys_addr_t base, + phys_addr_t size); + +void __next_mem_range(u64 *idx, int nid, struct memblock_type *type_a, + struct memblock_type *type_b, phys_addr_t *out_start, + phys_addr_t *out_end, int *out_nid); + +void __next_mem_range_rev(u64 *idx, int nid, struct memblock_type *type_a, + struct memblock_type *type_b, phys_addr_t *out_start, + phys_addr_t *out_end, int *out_nid); + +/** + * for_each_mem_range - iterate through memblock areas from type_a and not + * included in type_b. Or just type_a if type_b is NULL. + * @i: u64 used as loop variable + * @type_a: ptr to memblock_type to iterate + * @type_b: ptr to memblock_type which excludes from the iteration + * @nid: node selector, %NUMA_NO_NODE for all nodes + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * @p_nid: ptr to int for nid of the range, can be %NULL + */ +#define for_each_mem_range(i, type_a, type_b, nid, \ + p_start, p_end, p_nid) \ + for (i = 0, __next_mem_range(&i, nid, type_a, type_b, \ + p_start, p_end, p_nid); \ + i != (u64)ULLONG_MAX; \ + __next_mem_range(&i, nid, type_a, type_b, \ + p_start, p_end, p_nid)) + +/** + * for_each_mem_range_rev - reverse iterate through memblock areas from + * type_a and not included in type_b. Or just type_a if type_b is NULL. + * @i: u64 used as loop variable + * @type_a: ptr to memblock_type to iterate + * @type_b: ptr to memblock_type which excludes from the iteration + * @nid: node selector, %NUMA_NO_NODE for all nodes + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * @p_nid: ptr to int for nid of the range, can be %NULL + */ +#define for_each_mem_range_rev(i, type_a, type_b, nid, \ + p_start, p_end, p_nid) \ + for (i = (u64)ULLONG_MAX, \ + __next_mem_range_rev(&i, nid, type_a, type_b, \ + p_start, p_end, p_nid); \ + i != (u64)ULLONG_MAX; \ + __next_mem_range_rev(&i, nid, type_a, type_b, \ + p_start, p_end, p_nid)) + #ifdef CONFIG_MOVABLE_NODE static inline bool memblock_is_hotpluggable(struct memblock_region *m) { @@ -113,9 +170,6 @@ void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn, i >= 0; __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid)) #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ -void __next_free_mem_range(u64 *idx, int nid, phys_addr_t *out_start, - phys_addr_t *out_end, int *out_nid); - /** * for_each_free_mem_range - iterate through free memblock areas * @i: u64 used as loop variable @@ -128,13 +182,8 @@ void __next_free_mem_range(u64 *idx, int nid, phys_addr_t *out_start, * soon as memblock is initialized. */ #define for_each_free_mem_range(i, nid, p_start, p_end, p_nid) \ - for (i = 0, \ - __next_free_mem_range(&i, nid, p_start, p_end, p_nid); \ - i != (u64)ULLONG_MAX; \ - __next_free_mem_range(&i, nid, p_start, p_end, p_nid)) - -void __next_free_mem_range_rev(u64 *idx, int nid, phys_addr_t *out_start, - phys_addr_t *out_end, int *out_nid); + for_each_mem_range(i, &memblock.memory, &memblock.reserved, \ + nid, p_start, p_end, p_nid) /** * for_each_free_mem_range_reverse - rev-iterate through free memblock areas @@ -148,10 +197,8 @@ void __next_free_mem_range_rev(u64 *idx, int nid, phys_addr_t *out_start, * order. Available as soon as memblock is initialized. */ #define for_each_free_mem_range_reverse(i, nid, p_start, p_end, p_nid) \ - for (i = (u64)ULLONG_MAX, \ - __next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid); \ - i != (u64)ULLONG_MAX; \ - __next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid)) + for_each_mem_range_rev(i, &memblock.memory, &memblock.reserved, \ + nid, p_start, p_end, p_nid) static inline void memblock_set_region_flags(struct memblock_region *r, unsigned long flags) diff --git a/mm/memblock.c b/mm/memblock.c index e9d6ca9a01a9..9edd0928daab 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -472,7 +472,7 @@ static void __init_memblock memblock_insert_region(struct memblock_type *type, } /** - * memblock_add_region - add new memblock region + * memblock_add_range - add new memblock region * @type: memblock type to add new region into * @base: base address of the new region * @size: size of the new region @@ -487,7 +487,7 @@ static void __init_memblock memblock_insert_region(struct memblock_type *type, * RETURNS: * 0 on success, -errno on failure. */ -static int __init_memblock memblock_add_region(struct memblock_type *type, +int __init_memblock memblock_add_range(struct memblock_type *type, phys_addr_t base, phys_addr_t size, int nid, unsigned long flags) { @@ -569,12 +569,12 @@ repeat: int __init_memblock memblock_add_node(phys_addr_t base, phys_addr_t size, int nid) { - return memblock_add_region(&memblock.memory, base, size, nid, 0); + return memblock_add_range(&memblock.memory, base, size, nid, 0); } int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size) { - return memblock_add_region(&memblock.memory, base, size, + return memblock_add_range(&memblock.memory, base, size, MAX_NUMNODES, 0); } @@ -654,8 +654,8 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type, return 0; } -static int __init_memblock __memblock_remove(struct memblock_type *type, - phys_addr_t base, phys_addr_t size) +int __init_memblock memblock_remove_range(struct memblock_type *type, + phys_addr_t base, phys_addr_t size) { int start_rgn, end_rgn; int i, ret; @@ -671,9 +671,10 @@ static int __init_memblock __memblock_remove(struct memblock_type *type, int __init_memblock memblock_remove(phys_addr_t base, phys_addr_t size) { - return __memblock_remove(&memblock.memory, base, size); + return memblock_remove_range(&memblock.memory, base, size); } + int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size) { memblock_dbg(" memblock_free: [%#016llx-%#016llx] %pF\n", @@ -681,7 +682,7 @@ int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size) (unsigned long long)base + size - 1, (void *)_RET_IP_); - return __memblock_remove(&memblock.reserved, base, size); + return memblock_remove_range(&memblock.reserved, base, size); } static int __init_memblock memblock_reserve_region(phys_addr_t base, @@ -696,7 +697,7 @@ static int __init_memblock memblock_reserve_region(phys_addr_t base, (unsigned long long)base + size - 1, flags, (void *)_RET_IP_); - return memblock_add_region(_rgn, base, size, nid, flags); + return memblock_add_range(_rgn, base, size, nid, flags); } int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size) @@ -758,17 +759,19 @@ int __init_memblock memblock_clear_hotplug(phys_addr_t base, phys_addr_t size) } /** - * __next_free_mem_range - next function for for_each_free_mem_range() + * __next__mem_range - next function for for_each_free_mem_range() etc. * @idx: pointer to u64 loop variable * @nid: node selector, %NUMA_NO_NODE for all nodes + * @type_a: pointer to memblock_type from where the range is taken + * @type_b: pointer to memblock_type which excludes memory from being taken * @out_start: ptr to phys_addr_t for start address of the range, can be %NULL * @out_end: ptr to phys_addr_t for end address of the range, can be %NULL * @out_nid: ptr to int for nid of the range, can be %NULL * - * Find the first free area from *@idx which matches @nid, fill the out + * Find the first area from *@idx which matches @nid, fill the out * parameters, and update *@idx for the next iteration. The lower 32bit of - * *@idx contains index into memory region and the upper 32bit indexes the - * areas before each reserved region. For example, if reserved regions + * *@idx contains index into type_a and the upper 32bit indexes the + * areas before each region in type_b. For example, if type_b regions * look like the following, * * 0:[0-16), 1:[32-48), 2:[128-130) @@ -780,53 +783,77 @@ int __init_memblock memblock_clear_hotplug(phys_addr_t base, phys_addr_t size) * As both region arrays are sorted, the function advances the two indices * in lockstep and returns each intersection. */ -void __init_memblock __next_free_mem_range(u64 *idx, int nid, - phys_addr_t *out_start, - phys_addr_t *out_end, int *out_nid) +void __init_memblock __next_mem_range(u64 *idx, int nid, + struct memblock_type *type_a, + struct memblock_type *type_b, + phys_addr_t *out_start, + phys_addr_t *out_end, int *out_nid) { - struct memblock_type *mem = &memblock.memory; - struct memblock_type *rsv = &memblock.reserved; - int mi = *idx & 0xffffffff; - int ri = *idx >> 32; + int idx_a = *idx & 0xffffffff; + int idx_b = *idx >> 32; - if (WARN_ONCE(nid == MAX_NUMNODES, "Usage of MAX_NUMNODES is deprecated. Use NUMA_NO_NODE instead\n")) + if (WARN_ONCE(nid == MAX_NUMNODES, + "Usage of MAX_NUMNODES is deprecated. Use NUMA_NO_NODE instead\n")) nid = NUMA_NO_NODE; - for ( ; mi < mem->cnt; mi++) { - struct memblock_region *m = &mem->regions[mi]; + for (; idx_a < type_a->cnt; idx_a++) { + struct memblock_region *m = &type_a->regions[idx_a]; + phys_addr_t m_start = m->base; phys_addr_t m_end = m->base + m->size; + int m_nid = memblock_get_region_node(m); /* only memory regions are associated with nodes, check it */ - if (nid != NUMA_NO_NODE && nid != memblock_get_region_node(m)) + if (nid != NUMA_NO_NODE && nid != m_nid) continue; - /* scan areas before each reservation for intersection */ - for ( ; ri < rsv->cnt + 1; ri++) { - struct memblock_region *r = &rsv->regions[ri]; - phys_addr_t r_start = ri ? r[-1].base + r[-1].size : 0; - phys_addr_t r_end = ri < rsv->cnt ? r->base : ULLONG_MAX; + if (!type_b) { + if (out_start) + *out_start = m_start; + if (out_end) + *out_end = m_end; + if (out_nid) + *out_nid = m_nid; + idx_a++; + *idx = (u32)idx_a | (u64)idx_b << 32; + return; + } + + /* scan areas before each reservation */ + for (; idx_b < type_b->cnt + 1; idx_b++) { + struct memblock_region *r; + phys_addr_t r_start; + phys_addr_t r_end; + + r = &type_b->regions[idx_b]; + r_start = idx_b ? r[-1].base + r[-1].size : 0; + r_end = idx_b < type_b->cnt ? + r->base : ULLONG_MAX; - /* if ri advanced past mi, break out to advance mi */ + /* + * if idx_b advanced past idx_a, + * break out to advance idx_a + */ if (r_start >= m_end) break; /* if the two regions intersect, we're done */ if (m_start < r_end) { if (out_start) - *out_start = max(m_start, r_start); + *out_start = + max(m_start, r_start); if (out_end) *out_end = min(m_end, r_end); if (out_nid) - *out_nid = memblock_get_region_node(m); + *out_nid = m_nid; /* - * The region which ends first is advanced - * for the next iteration. + * The region which ends first is + * advanced for the next iteration. */ if (m_end <= r_end) - mi++; + idx_a++; else - ri++; - *idx = (u32)mi | (u64)ri << 32; + idx_b++; + *idx = (u32)idx_a | (u64)idx_b << 32; return; } } @@ -837,57 +864,80 @@ void __init_memblock __next_free_mem_range(u64 *idx, int nid, } /** - * __next_free_mem_range_rev - next function for for_each_free_mem_range_reverse() + * __next_mem_range_rev - generic next function for for_each_*_range_rev() + * + * Finds the next range from type_a which is not marked as unsuitable + * in type_b. + * * @idx: pointer to u64 loop variable * @nid: nid: node selector, %NUMA_NO_NODE for all nodes + * @type_a: pointer to memblock_type from where the range is taken + * @type_b: pointer to memblock_type which excludes memory from being taken * @out_start: ptr to phys_addr_t for start address of the range, can be %NULL * @out_end: ptr to phys_addr_t for end address of the range, can be %NULL * @out_nid: ptr to int for nid of the range, can be %NULL * - * Reverse of __next_free_mem_range(). - * - * Linux kernel cannot migrate pages used by itself. Memory hotplug users won't - * be able to hot-remove hotpluggable memory used by the kernel. So this - * function skip hotpluggable regions if needed when allocating memory for the - * kernel. + * Reverse of __next_mem_range(). */ -void __init_memblock __next_free_mem_range_rev(u64 *idx, int nid, - phys_addr_t *out_start, - phys_addr_t *out_end, int *out_nid) +void __init_memblock __next_mem_range_rev(u64 *idx, int nid, + struct memblock_type *type_a, + struct memblock_type *type_b, + phys_addr_t *out_start, + phys_addr_t *out_end, int *out_nid) { - struct memblock_type *mem = &memblock.memory; - struct memblock_type *rsv = &memblock.reserved; - int mi = *idx & 0xffffffff; - int ri = *idx >> 32; + int idx_a = *idx & 0xffffffff; + int idx_b = *idx >> 32; if (WARN_ONCE(nid == MAX_NUMNODES, "Usage of MAX_NUMNODES is deprecated. Use NUMA_NO_NODE instead\n")) nid = NUMA_NO_NODE; if (*idx == (u64)ULLONG_MAX) { - mi = mem->cnt - 1; - ri = rsv->cnt; + idx_a = type_a->cnt - 1; + idx_b = type_b->cnt; } - for ( ; mi >= 0; mi--) { - struct memblock_region *m = &mem->regions[mi]; + for (; idx_a >= 0; idx_a--) { + struct memblock_region *m = &type_a->regions[idx_a]; + phys_addr_t m_start = m->base; phys_addr_t m_end = m->base + m->size; + int m_nid = memblock_get_region_node(m); /* only memory regions are associated with nodes, check it */ - if (nid != NUMA_NO_NODE && nid != memblock_get_region_node(m)) + if (nid != NUMA_NO_NODE && nid != m_nid) continue; /* skip hotpluggable memory regions if needed */ if (movable_node_is_enabled() && memblock_is_hotpluggable(m)) continue; - /* scan areas before each reservation for intersection */ - for ( ; ri >= 0; ri--) { - struct memblock_region *r = &rsv->regions[ri]; - phys_addr_t r_start = ri ? r[-1].base + r[-1].size : 0; - phys_addr_t r_end = ri < rsv->cnt ? r->base : ULLONG_MAX; + if (!type_b) { + if (out_start) + *out_start = m_start; + if (out_end) + *out_end = m_end; + if (out_nid) + *out_nid = m_nid; + idx_a++; + *idx = (u32)idx_a | (u64)idx_b << 32; + return; + } + + /* scan areas before each reservation */ + for (; idx_b >= 0; idx_b--) { + struct memblock_region *r; + phys_addr_t r_start; + phys_addr_t r_end; + + r = &type_b->regions[idx_b]; + r_start = idx_b ? r[-1].base + r[-1].size : 0; + r_end = idx_b < type_b->cnt ? + r->base : ULLONG_MAX; + /* + * if idx_b advanced past idx_a, + * break out to advance idx_a + */ - /* if ri advanced past mi, break out to advance mi */ if (r_end <= m_start) break; /* if the two regions intersect, we're done */ @@ -897,18 +947,17 @@ void __init_memblock __next_free_mem_range_rev(u64 *idx, int nid, if (out_end) *out_end = min(m_end, r_end); if (out_nid) - *out_nid = memblock_get_region_node(m); - + *out_nid = m_nid; if (m_start >= r_start) - mi--; + idx_a--; else - ri--; - *idx = (u32)mi | (u64)ri << 32; + idx_b--; + *idx = (u32)idx_a | (u64)idx_b << 32; return; } } } - + /* signal end of iteration */ *idx = ULLONG_MAX; } @@ -1201,7 +1250,7 @@ void __init __memblock_free_early(phys_addr_t base, phys_addr_t size) __func__, (u64)base, (u64)base + size - 1, (void *)_RET_IP_); kmemleak_free_part(__va(base), size); - __memblock_remove(&memblock.reserved, base, size); + memblock_remove_range(&memblock.reserved, base, size); } /* @@ -1287,8 +1336,10 @@ void __init memblock_enforce_memory_limit(phys_addr_t limit) } /* truncate both memory and reserved regions */ - __memblock_remove(&memblock.memory, max_addr, (phys_addr_t)ULLONG_MAX); - __memblock_remove(&memblock.reserved, max_addr, (phys_addr_t)ULLONG_MAX); + memblock_remove_range(&memblock.memory, max_addr, + (phys_addr_t)ULLONG_MAX); + memblock_remove_range(&memblock.reserved, max_addr, + (phys_addr_t)ULLONG_MAX); } static int __init_memblock memblock_search(struct memblock_type *type, phys_addr_t addr) -- cgit v1.2.3 From 70210ed950b538ee7eb811dccc402db9df1c9be4 Mon Sep 17 00:00:00 2001 From: Philipp Hachtmann Date: Wed, 29 Jan 2014 18:16:01 +0100 Subject: mm/memblock: add physical memory list Add the physmem list to the memblock structure. This list only exists if HAVE_MEMBLOCK_PHYS_MAP is selected and contains the unmodified list of physically available memory. It differs from the memblock memory list as it always contains all memory ranges even if the memory has been restricted, e.g. by use of the mem= kernel parameter. Signed-off-by: Philipp Hachtmann Signed-off-by: Martin Schwidefsky --- include/linux/memblock.h | 4 ++++ mm/Kconfig | 3 +++ mm/memblock.c | 12 ++++++++++++ 3 files changed, 19 insertions(+) (limited to 'include') diff --git a/include/linux/memblock.h b/include/linux/memblock.h index f669016874b3..73dc382e72d8 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -18,6 +18,7 @@ #include #define INIT_MEMBLOCK_REGIONS 128 +#define INIT_PHYSMEM_REGIONS 4 /* Definition of memblock flags. */ #define MEMBLOCK_HOTPLUG 0x1 /* hotpluggable region */ @@ -43,6 +44,9 @@ struct memblock { phys_addr_t current_limit; struct memblock_type memory; struct memblock_type reserved; +#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP + struct memblock_type physmem; +#endif }; extern struct memblock memblock; diff --git a/mm/Kconfig b/mm/Kconfig index 1b5a95f0fa01..28cec518f4d4 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -134,6 +134,9 @@ config HAVE_MEMBLOCK config HAVE_MEMBLOCK_NODE_MAP boolean +config HAVE_MEMBLOCK_PHYS_MAP + boolean + config ARCH_DISCARD_MEMBLOCK boolean diff --git a/mm/memblock.c b/mm/memblock.c index 9edd0928daab..a810ba923cdd 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -27,6 +27,9 @@ static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock; static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock; +#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP +static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS] __initdata_memblock; +#endif struct memblock memblock __initdata_memblock = { .memory.regions = memblock_memory_init_regions, @@ -37,6 +40,12 @@ struct memblock memblock __initdata_memblock = { .reserved.cnt = 1, /* empty dummy entry */ .reserved.max = INIT_MEMBLOCK_REGIONS, +#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP + .physmem.regions = memblock_physmem_init_regions, + .physmem.cnt = 1, /* empty dummy entry */ + .physmem.max = INIT_PHYSMEM_REGIONS, +#endif + .bottom_up = false, .current_limit = MEMBLOCK_ALLOC_ANYWHERE, }; @@ -1553,6 +1562,9 @@ static int __init memblock_init_debugfs(void) return -ENXIO; debugfs_create_file("memory", S_IRUGO, root, &memblock.memory, &memblock_debug_fops); debugfs_create_file("reserved", S_IRUGO, root, &memblock.reserved, &memblock_debug_fops); +#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP + debugfs_create_file("physmem", S_IRUGO, root, &memblock.physmem, &memblock_debug_fops); +#endif return 0; } -- cgit v1.2.3 From f25c0ae2b4c41996c1a6b609132c1788a6eea080 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 17 May 2014 00:18:13 +0200 Subject: ACPI / PM: Avoid resuming devices in ACPI PM domain during system suspend Rework the ACPI PM domain's PM callbacks to avoid resuming devices during system suspend (in order to modify their wakeup settings etc.) if that isn't necessary. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 43 ++++++++++++++++++++++++++++++++++++------- drivers/acpi/scan.c | 4 ++++ include/acpi/acpi_bus.h | 3 ++- 3 files changed, 42 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index d047739f3380..9e5fd9c440b7 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -900,17 +900,45 @@ EXPORT_SYMBOL_GPL(acpi_dev_resume_early); */ int acpi_subsys_prepare(struct device *dev) { - /* - * Devices having power.ignore_children set may still be necessary for - * suspending their children in the next phase of device suspend. - */ - if (dev->power.ignore_children) - pm_runtime_resume(dev); + struct acpi_device *adev = ACPI_COMPANION(dev); + u32 sys_target; + int ret, state; + + ret = pm_generic_prepare(dev); + if (ret < 0) + return ret; + + if (!adev || !pm_runtime_suspended(dev) + || device_may_wakeup(dev) != !!adev->wakeup.prepare_count) + return 0; + + sys_target = acpi_target_system_state(); + if (sys_target == ACPI_STATE_S0) + return 1; - return pm_generic_prepare(dev); + if (adev->power.flags.dsw_present) + return 0; + + ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state); + return !ret && state == adev->power.state; } EXPORT_SYMBOL_GPL(acpi_subsys_prepare); +/** + * acpi_subsys_complete - Finalize device's resume during system resume. + * @dev: Device to handle. + */ +static void acpi_subsys_complete(struct device *dev) +{ + /* + * If the device had been runtime-suspended before the system went into + * the sleep state it is going out of and it has never been resumed till + * now, resume it in case the firmware powered it up. + */ + if (dev->power.direct_complete) + pm_request_resume(dev); +} + /** * acpi_subsys_suspend - Run the device driver's suspend callback. * @dev: Device to handle. @@ -979,6 +1007,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { #endif #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, + .complete = acpi_subsys_complete, .suspend = acpi_subsys_suspend, .suspend_late = acpi_subsys_suspend_late, .resume_early = acpi_subsys_resume_early, diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7efe546a8c42..df6e4c924b35 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1551,9 +1551,13 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) */ if (acpi_has_method(device->handle, "_PSC")) device->power.flags.explicit_get = 1; + if (acpi_has_method(device->handle, "_IRC")) device->power.flags.inrush_current = 1; + if (acpi_has_method(device->handle, "_DSW")) + device->power.flags.dsw_present = 1; + /* * Enumerate supported power management states */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 84a2e29a2314..7417a16c8d86 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -261,7 +261,8 @@ struct acpi_device_power_flags { u32 inrush_current:1; /* Serialize Dx->D0 */ u32 power_removed:1; /* Optimize Dx->D0 */ u32 ignore_parent:1; /* Power is independent of parent power state */ - u32 reserved:27; + u32 dsw_present:1; /* _DSW present? */ + u32 reserved:26; }; struct acpi_device_power_state { -- cgit v1.2.3 From 4cf563c5d97c83d4b2fb3a778dd7d5e362cc3e34 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 15 May 2014 16:40:23 +0300 Subject: ACPI / PM: Export rest of the subsys PM callbacks No reason for excluding the remaining ones. Signed-off-by: Heikki Krogerus [rjw: Rebased and exported the new acpi_subsys_complete() too.] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 5 ++++- include/linux/acpi.h | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 9e5fd9c440b7..49a51277f81d 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -928,7 +928,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_prepare); * acpi_subsys_complete - Finalize device's resume during system resume. * @dev: Device to handle. */ -static void acpi_subsys_complete(struct device *dev) +void acpi_subsys_complete(struct device *dev) { /* * If the device had been runtime-suspended before the system went into @@ -938,6 +938,7 @@ static void acpi_subsys_complete(struct device *dev) if (dev->power.direct_complete) pm_request_resume(dev); } +EXPORT_SYMBOL_GPL(acpi_subsys_complete); /** * acpi_subsys_suspend - Run the device driver's suspend callback. @@ -951,6 +952,7 @@ int acpi_subsys_suspend(struct device *dev) pm_runtime_resume(dev); return pm_generic_suspend(dev); } +EXPORT_SYMBOL_GPL(acpi_subsys_suspend); /** * acpi_subsys_suspend_late - Suspend device using ACPI. @@ -996,6 +998,7 @@ int acpi_subsys_freeze(struct device *dev) pm_runtime_resume(dev); return pm_generic_freeze(dev); } +EXPORT_SYMBOL_GPL(acpi_subsys_freeze); #endif /* CONFIG_PM_SLEEP */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7a8f2cd66c8b..4c007262e891 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -554,14 +554,20 @@ static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } int acpi_dev_suspend_late(struct device *dev); int acpi_dev_resume_early(struct device *dev); int acpi_subsys_prepare(struct device *dev); +void acpi_subsys_complete(struct device *dev); int acpi_subsys_suspend_late(struct device *dev); int acpi_subsys_resume_early(struct device *dev); +int acpi_subsys_suspend(struct device *dev); +int acpi_subsys_freeze(struct device *dev); #else static inline int acpi_dev_suspend_late(struct device *dev) { return 0; } static inline int acpi_dev_resume_early(struct device *dev) { return 0; } static inline int acpi_subsys_prepare(struct device *dev) { return 0; } +static inline void acpi_subsys_complete(struct device *dev) {} static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } +static inline int acpi_subsys_suspend(struct device *dev) { return 0; } +static inline int acpi_subsys_freeze(struct device *dev) { return 0; } #endif #if defined(CONFIG_ACPI) && defined(CONFIG_PM) -- cgit v1.2.3 From e2d0e90fae82809667f1dcf4d0d9baa421691c7a Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 15 May 2014 16:40:25 +0300 Subject: clk: new basic clk type for fractional divider Fractional divider clocks are fairly common. This adds basic type for them. Signed-off-by: Heikki Krogerus Acked-by: Mike Turquette Signed-off-by: Rafael J. Wysocki --- drivers/clk/Makefile | 1 + drivers/clk/clk-fractional-divider.c | 135 +++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 31 ++++++++ 3 files changed, 167 insertions(+) create mode 100644 drivers/clk/clk-fractional-divider.c (limited to 'include') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 5f8a28735c96..0745059b1834 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o obj-$(CONFIG_COMMON_CLK) += clk-composite.o +obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o # hardware specific clock types # please keep this section sorted lexicographically by file/directory path name diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c new file mode 100644 index 000000000000..ede685ca0d20 --- /dev/null +++ b/drivers/clk/clk-fractional-divider.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * 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. + * + * Adjustable fractional divider clock implementation. + * Output rate = (m / n) * parent_rate. + */ + +#include +#include +#include +#include +#include + +#define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw) + +static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long flags = 0; + u32 val, m, n; + u64 ret; + + if (fd->lock) + spin_lock_irqsave(fd->lock, flags); + + val = clk_readl(fd->reg); + + if (fd->lock) + spin_unlock_irqrestore(fd->lock, flags); + + m = (val & fd->mmask) >> fd->mshift; + n = (val & fd->nmask) >> fd->nshift; + + ret = parent_rate * m; + do_div(ret, n); + + return ret; +} + +static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned maxn = (fd->nmask >> fd->nshift) + 1; + unsigned div; + + if (!rate || rate >= *prate) + return *prate; + + div = gcd(*prate, rate); + + while ((*prate / div) > maxn) { + div <<= 1; + rate <<= 1; + } + + return rate; +} + +static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long flags = 0; + unsigned long div; + unsigned n, m; + u32 val; + + div = gcd(parent_rate, rate); + m = rate / div; + n = parent_rate / div; + + if (fd->lock) + spin_lock_irqsave(fd->lock, flags); + + val = clk_readl(fd->reg); + val &= ~(fd->mmask | fd->nmask); + val |= (m << fd->mshift) | (n << fd->nshift); + clk_writel(val, fd->reg); + + if (fd->lock) + spin_unlock_irqrestore(fd->lock, flags); + + return 0; +} + +const struct clk_ops clk_fractional_divider_ops = { + .recalc_rate = clk_fd_recalc_rate, + .round_rate = clk_fd_round_rate, + .set_rate = clk_fd_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_fractional_divider_ops); + +struct clk *clk_register_fractional_divider(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, + u8 clk_divider_flags, spinlock_t *lock) +{ + struct clk_fractional_divider *fd; + struct clk_init_data init; + struct clk *clk; + + fd = kzalloc(sizeof(*fd), GFP_KERNEL); + if (!fd) { + dev_err(dev, "could not allocate fractional divider clk\n"); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &clk_fractional_divider_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + fd->reg = reg; + fd->mshift = mshift; + fd->mmask = (BIT(mwidth) - 1) << mshift; + fd->nshift = nshift; + fd->nmask = (BIT(nwidth) - 1) << nshift; + fd->flags = clk_divider_flags; + fd->lock = lock; + fd->hw.init = &init; + + clk = clk_register(dev, &fd->hw); + if (IS_ERR(clk)) + kfree(fd); + + return clk; +} +EXPORT_SYMBOL_GPL(clk_register_fractional_divider); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 511917416fb0..fb4eca6907cd 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -413,6 +413,37 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div); +/** + * struct clk_fractional_divider - adjustable fractional divider clock + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing the divider + * @mshift: shift to the numerator bit field + * @mwidth: width of the numerator bit field + * @nshift: shift to the denominator bit field + * @nwidth: width of the denominator bit field + * @lock: register lock + * + * Clock with adjustable fractional divider affecting its output frequency. + */ + +struct clk_fractional_divider { + struct clk_hw hw; + void __iomem *reg; + u8 mshift; + u32 mmask; + u8 nshift; + u32 nmask; + u8 flags; + spinlock_t *lock; +}; + +extern const struct clk_ops clk_fractional_divider_ops; +struct clk *clk_register_fractional_divider(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth, + u8 clk_divider_flags, spinlock_t *lock); + /*** * struct clk_composite - aggregate clock of mux, divider and gate clocks * -- cgit v1.2.3 From 1b7f37e1276bee9043d35a4b39ef2f43d3b2e133 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2014 10:48:01 +0200 Subject: ACPI / video: Add an acpi_video_unregister_backlight function Add an acpi_video_unregister_backlight function, which only unregisters the backlight device, and leaves the acpi_notifier in place. Some acpi_vendor driver need this as they don't want the acpi_video# backlight device, but do need the acpi-video driver for hotkey handling. Chances are that this new acpi_video_unregister_backlight() is actually what existing acpi_vendor drivers have wanted all along. Currently acpi_vendor drivers which want to disable the acpi_video# backlight device, make 2 calls: acpi_video_dmi_promote_vendor(); acpi_video_unregister(); The intention here is to make things independent of when acpi_video_register() gets called. As acpi_video_register() will get called on acpi-video load time on non intel gfx machines, while it gets called on i915 load time on intel gfx machines. This leads to the following 2 interesting scenarios: a) intel gfx: 1) acpi-video module gets loaded (as it is a dependency of acpi_vendor and i915) 2) acpi-video does NOT call acpi_video_register() 3) acpi_vendor loads (lets assume it loads before i915), calls acpi_video_dmi_promote_vendor(); which sets ACPI_VIDEO_BACKLIGHT_DMI_VENDOR 4) calls acpi_video_unregister -> not registered, nop 5) i915 loads, calls acpi_video_register 6) acpi_video_register registers the acpi_notifier for the hotkeys, does NOT register a backlight device because of ACPI_VIDEO_BACKLIGHT_DMI_VENDOR b) non intel gfx 1) acpi-video module gets loaded (as it is a dependency acpi_vendor) 2) acpi-video calls acpi_video_register() 3) acpi_video_register registers the acpi_notifier for the hotkeys, and a backlight device 4) acpi_vendor loads, calls acpi_video_dmi_promote_vendor() 5) calls acpi_video_unregister, this unregisters BOTH the acpi_notifier for the hotkeys AND the backlight device So here we have possibly the same acpi_vendor module, making the same calls, but with different results, in one cases acpi-video does handle hotkeys, in the other it does not. Note that the a) scenario turns into b) if we assume the i915 module loads before the vendor_acpi module, so we also have different behavior depending on module loading order! So as said I believe that quite a few existing acpi_vendor modules really always want the behavior of a), hence this patch adds a new acpi_video_unregister_backlight() which gives the behavior of a) independent of module loading order. Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- drivers/acpi/video.c | 14 ++++++++++++++ include/acpi/video.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index ff79dff371fd..adbfe39f5454 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -2078,6 +2078,20 @@ void acpi_video_unregister(void) } EXPORT_SYMBOL(acpi_video_unregister); +void acpi_video_unregister_backlight(void) +{ + struct acpi_video_bus *video; + + if (!register_count) + return; + + mutex_lock(&video_list_lock); + list_for_each_entry(video, &video_bus_head, entry) + acpi_video_bus_unregister_backlight(video); + mutex_unlock(&video_list_lock); +} +EXPORT_SYMBOL(acpi_video_unregister_backlight); + /* * This is kind of nasty. Hardware using Intel chipsets may require * the video opregion code to be run first in order to initialise diff --git a/include/acpi/video.h b/include/acpi/video.h index 61109f2609fc..ea4c7bbded4d 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -19,11 +19,13 @@ struct acpi_device; #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) extern int acpi_video_register(void); extern void acpi_video_unregister(void); +extern void acpi_video_unregister_backlight(void); extern int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid); #else static inline int acpi_video_register(void) { return 0; } static inline void acpi_video_unregister(void) { return; } +static inline void acpi_video_unregister_backlight(void) { return; } static inline int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid) { -- cgit v1.2.3 From 867d849fc844623a88ec7b380442952b5ffe5e68 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 19 May 2014 21:53:19 +0200 Subject: cfg80211: export expected throughput through get_station() Users may need information about the expected throughput towards a given peer. This value is supposed to consider the size overhead generated by the 802.11 header. This value is exported in kbps through the get_station() API by including it into the station_info object. Moreover, it is sent to user space when replying to the nl80211 GET_STATION command. This information will be useful to the batman-adv module which will use it for its new metric computation. Signed-off-by: Antonio Quartulli Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 62 ++++++++++++++++++++++++-------------------- include/uapi/linux/nl80211.h | 3 +++ net/wireless/nl80211.c | 4 +++ 3 files changed, 41 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fe4fa287f788..857d6476a128 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -873,36 +873,38 @@ int cfg80211_check_station_change(struct wiphy *wiphy, * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled + * @STATION_INFO_EXPECTED_THROUGHPUT: @expected_throughput filled */ enum station_info_flags { - STATION_INFO_INACTIVE_TIME = 1<<0, - STATION_INFO_RX_BYTES = 1<<1, - STATION_INFO_TX_BYTES = 1<<2, - STATION_INFO_LLID = 1<<3, - STATION_INFO_PLID = 1<<4, - STATION_INFO_PLINK_STATE = 1<<5, - STATION_INFO_SIGNAL = 1<<6, - STATION_INFO_TX_BITRATE = 1<<7, - STATION_INFO_RX_PACKETS = 1<<8, - STATION_INFO_TX_PACKETS = 1<<9, - STATION_INFO_TX_RETRIES = 1<<10, - STATION_INFO_TX_FAILED = 1<<11, - STATION_INFO_RX_DROP_MISC = 1<<12, - STATION_INFO_SIGNAL_AVG = 1<<13, - STATION_INFO_RX_BITRATE = 1<<14, - STATION_INFO_BSS_PARAM = 1<<15, - STATION_INFO_CONNECTED_TIME = 1<<16, - STATION_INFO_ASSOC_REQ_IES = 1<<17, - STATION_INFO_STA_FLAGS = 1<<18, - STATION_INFO_BEACON_LOSS_COUNT = 1<<19, - STATION_INFO_T_OFFSET = 1<<20, - STATION_INFO_LOCAL_PM = 1<<21, - STATION_INFO_PEER_PM = 1<<22, - STATION_INFO_NONPEER_PM = 1<<23, - STATION_INFO_RX_BYTES64 = 1<<24, - STATION_INFO_TX_BYTES64 = 1<<25, - STATION_INFO_CHAIN_SIGNAL = 1<<26, - STATION_INFO_CHAIN_SIGNAL_AVG = 1<<27, + STATION_INFO_INACTIVE_TIME = BIT(0), + STATION_INFO_RX_BYTES = BIT(1), + STATION_INFO_TX_BYTES = BIT(2), + STATION_INFO_LLID = BIT(3), + STATION_INFO_PLID = BIT(4), + STATION_INFO_PLINK_STATE = BIT(5), + STATION_INFO_SIGNAL = BIT(6), + STATION_INFO_TX_BITRATE = BIT(7), + STATION_INFO_RX_PACKETS = BIT(8), + STATION_INFO_TX_PACKETS = BIT(9), + STATION_INFO_TX_RETRIES = BIT(10), + STATION_INFO_TX_FAILED = BIT(11), + STATION_INFO_RX_DROP_MISC = BIT(12), + STATION_INFO_SIGNAL_AVG = BIT(13), + STATION_INFO_RX_BITRATE = BIT(14), + STATION_INFO_BSS_PARAM = BIT(15), + STATION_INFO_CONNECTED_TIME = BIT(16), + STATION_INFO_ASSOC_REQ_IES = BIT(17), + STATION_INFO_STA_FLAGS = BIT(18), + STATION_INFO_BEACON_LOSS_COUNT = BIT(19), + STATION_INFO_T_OFFSET = BIT(20), + STATION_INFO_LOCAL_PM = BIT(21), + STATION_INFO_PEER_PM = BIT(22), + STATION_INFO_NONPEER_PM = BIT(23), + STATION_INFO_RX_BYTES64 = BIT(24), + STATION_INFO_TX_BYTES64 = BIT(25), + STATION_INFO_CHAIN_SIGNAL = BIT(26), + STATION_INFO_CHAIN_SIGNAL_AVG = BIT(27), + STATION_INFO_EXPECTED_THROUGHPUT = BIT(28), }; /** @@ -1024,6 +1026,8 @@ struct sta_bss_parameters { * @local_pm: local mesh STA power save mode * @peer_pm: peer mesh STA power save mode * @nonpeer_pm: non-peer mesh STA power save mode + * @expected_throughput: expected throughput in kbps (including 802.11 headers) + * towards this station. */ struct station_info { u32 filled; @@ -1062,6 +1066,8 @@ struct station_info { enum nl80211_mesh_power_mode peer_pm; enum nl80211_mesh_power_mode nonpeer_pm; + u32 expected_throughput; + /* * Note: Add a new enum station_info_flags value for each new field and * use it to check which fields are initialized. diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 0cfa827123ff..fb0efa1f9066 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2199,6 +2199,8 @@ enum nl80211_sta_bss_param { * Contains a nested array of signal strength attributes (u8, dBm) * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. + * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the + * 802.11 header (u32, kbps) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2230,6 +2232,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_BYTES64, NL80211_STA_INFO_CHAIN_SIGNAL, NL80211_STA_INFO_CHAIN_SIGNAL_AVG, + NL80211_STA_INFO_EXPECTED_THROUGHPUT, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 49adf58646e6..62bdb1adaa4d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3650,6 +3650,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED, sinfo->tx_failed)) goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) && + nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT, + sinfo->expected_throughput)) + goto nla_put_failure; if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) && nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, sinfo->beacon_loss_count)) -- cgit v1.2.3 From e1618d461ca18d40f9c3ef70598abb72e75d27ae Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Tue, 20 May 2014 10:59:26 -0400 Subject: vlan: Fix build error wth vlan_get_encap_level() The new function vlan_get_encap_level() uses vlan_dev_priv() which is only conditionally avaialble when VLAN support is enabled. Make vlan_get_encap_level() conditionally available as well. Fixes: 44a4085538c8 ("bonding: Fix stacked device detection in arp monitoring") Reported-by: Stephen Rothwell CC: Stephen Rothwell Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index c901b13b6f03..b2acc4a1b13c 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -198,6 +198,12 @@ extern void vlan_vids_del_by_dev(struct net_device *dev, const struct net_device *by_dev); extern bool vlan_uses_dev(const struct net_device *dev); + +static inline int vlan_get_encap_level(struct net_device *dev) +{ + BUG_ON(!is_vlan_dev(dev)); + return vlan_dev_priv(dev)->nest_level; +} #else static inline struct net_device * __vlan_find_dev_deep(struct net_device *real_dev, @@ -264,6 +270,11 @@ static inline bool vlan_uses_dev(const struct net_device *dev) { return false; } +static inline int vlan_get_encap_level(struct net_device *dev) +{ + BUG(); + return 0; +} #endif static inline bool vlan_hw_offload_capable(netdev_features_t features, @@ -485,9 +496,4 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb, skb->protocol = htons(ETH_P_802_2); } -static inline int vlan_get_encap_level(struct net_device *dev) -{ - BUG_ON(!is_vlan_dev(dev)); - return vlan_dev_priv(dev)->nest_level; -} #endif /* !(_LINUX_IF_VLAN_H_) */ -- cgit v1.2.3 From e3a2b3f931f59d5284abd13faf8bded726884ffd Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 20 May 2014 11:49:02 -0600 Subject: blk-mq: allow changing of queue depth through sysfs For request_fn based devices, the block layer exports a 'nr_requests' file through sysfs to allow adjusting of queue depth on the fly. Currently this returns -EINVAL for blk-mq, since it's not wired up. Wire this up for blk-mq, so that it now also always dynamic adjustments of the allowed queue depth for any given block device managed by blk-mq. Signed-off-by: Jens Axboe --- block/blk-core.c | 41 ++++++++++++++++++++++++++ block/blk-mq-tag.c | 80 +++++++++++++++++++++++++++++++++++--------------- block/blk-mq-tag.h | 1 + block/blk-mq.c | 22 ++++++++++++++ block/blk-mq.h | 1 + block/blk-sysfs.c | 45 ++++++---------------------- block/blk.h | 2 ++ include/linux/blk-mq.h | 2 +- 8 files changed, 134 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index a6bd3e702201..fe81e19099a1 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -848,6 +848,47 @@ static void freed_request(struct request_list *rl, unsigned int flags) __freed_request(rl, sync ^ 1); } +int blk_update_nr_requests(struct request_queue *q, unsigned int nr) +{ + struct request_list *rl; + + spin_lock_irq(q->queue_lock); + q->nr_requests = nr; + blk_queue_congestion_threshold(q); + + /* congestion isn't cgroup aware and follows root blkcg for now */ + rl = &q->root_rl; + + if (rl->count[BLK_RW_SYNC] >= queue_congestion_on_threshold(q)) + blk_set_queue_congested(q, BLK_RW_SYNC); + else if (rl->count[BLK_RW_SYNC] < queue_congestion_off_threshold(q)) + blk_clear_queue_congested(q, BLK_RW_SYNC); + + if (rl->count[BLK_RW_ASYNC] >= queue_congestion_on_threshold(q)) + blk_set_queue_congested(q, BLK_RW_ASYNC); + else if (rl->count[BLK_RW_ASYNC] < queue_congestion_off_threshold(q)) + blk_clear_queue_congested(q, BLK_RW_ASYNC); + + blk_queue_for_each_rl(rl, q) { + if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { + blk_set_rl_full(rl, BLK_RW_SYNC); + } else { + blk_clear_rl_full(rl, BLK_RW_SYNC); + wake_up(&rl->wait[BLK_RW_SYNC]); + } + + if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { + blk_set_rl_full(rl, BLK_RW_ASYNC); + } else { + blk_clear_rl_full(rl, BLK_RW_ASYNC); + wake_up(&rl->wait[BLK_RW_ASYNC]); + } + } + + spin_unlock_irq(q->queue_lock); + return 0; +} + /* * Determine if elevator data should be initialized when allocating the * request associated with @bio. diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index e6b3fbae9862..f6dea968b710 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -57,23 +57,13 @@ bool __blk_mq_tag_busy(struct blk_mq_hw_ctx *hctx) } /* - * If a previously busy queue goes inactive, potential waiters could now - * be allowed to queue. Wake them up and check. + * Wakeup all potentially sleeping on normal (non-reserved) tags */ -void __blk_mq_tag_idle(struct blk_mq_hw_ctx *hctx) +static void blk_mq_tag_wakeup_all(struct blk_mq_tags *tags) { - struct blk_mq_tags *tags = hctx->tags; struct blk_mq_bitmap_tags *bt; int i, wake_index; - if (!test_and_clear_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state)) - return; - - atomic_dec(&tags->active_queues); - - /* - * Will only throttle depth on non-reserved tags - */ bt = &tags->bitmap_tags; wake_index = bt->wake_index; for (i = 0; i < BT_WAIT_QUEUES; i++) { @@ -86,6 +76,22 @@ void __blk_mq_tag_idle(struct blk_mq_hw_ctx *hctx) } } +/* + * If a previously busy queue goes inactive, potential waiters could now + * be allowed to queue. Wake them up and check. + */ +void __blk_mq_tag_idle(struct blk_mq_hw_ctx *hctx) +{ + struct blk_mq_tags *tags = hctx->tags; + + if (!test_and_clear_bit(BLK_MQ_S_TAG_ACTIVE, &hctx->state)) + return; + + atomic_dec(&tags->active_queues); + + blk_mq_tag_wakeup_all(tags); +} + /* * For shared tag users, we track the number of currently active users * and attempt to provide a fair share of the tag depth for each of them. @@ -408,6 +414,28 @@ static unsigned int bt_unused_tags(struct blk_mq_bitmap_tags *bt) return bt->depth - used; } +static void bt_update_count(struct blk_mq_bitmap_tags *bt, + unsigned int depth) +{ + unsigned int tags_per_word = 1U << bt->bits_per_word; + unsigned int map_depth = depth; + + if (depth) { + int i; + + for (i = 0; i < bt->map_nr; i++) { + bt->map[i].depth = min(map_depth, tags_per_word); + map_depth -= bt->map[i].depth; + } + } + + bt->wake_cnt = BT_WAIT_BATCH; + if (bt->wake_cnt > depth / 4) + bt->wake_cnt = max(1U, depth / 4); + + bt->depth = depth; +} + static int bt_alloc(struct blk_mq_bitmap_tags *bt, unsigned int depth, int node, bool reserved) { @@ -420,7 +448,7 @@ static int bt_alloc(struct blk_mq_bitmap_tags *bt, unsigned int depth, * condition. */ if (depth) { - unsigned int nr, i, map_depth, tags_per_word; + unsigned int nr, tags_per_word; tags_per_word = (1 << bt->bits_per_word); @@ -444,11 +472,6 @@ static int bt_alloc(struct blk_mq_bitmap_tags *bt, unsigned int depth, return -ENOMEM; bt->map_nr = nr; - map_depth = depth; - for (i = 0; i < nr; i++) { - bt->map[i].depth = min(map_depth, tags_per_word); - map_depth -= tags_per_word; - } } bt->bs = kzalloc(BT_WAIT_QUEUES * sizeof(*bt->bs), GFP_KERNEL); @@ -460,11 +483,7 @@ static int bt_alloc(struct blk_mq_bitmap_tags *bt, unsigned int depth, for (i = 0; i < BT_WAIT_QUEUES; i++) init_waitqueue_head(&bt->bs[i].wait); - bt->wake_cnt = BT_WAIT_BATCH; - if (bt->wake_cnt > depth / 4) - bt->wake_cnt = max(1U, depth / 4); - - bt->depth = depth; + bt_update_count(bt, depth); return 0; } @@ -525,6 +544,21 @@ void blk_mq_tag_init_last_tag(struct blk_mq_tags *tags, unsigned int *tag) *tag = prandom_u32() % depth; } +int blk_mq_tag_update_depth(struct blk_mq_tags *tags, unsigned int tdepth) +{ + tdepth -= tags->nr_reserved_tags; + if (tdepth > tags->nr_tags) + return -EINVAL; + + /* + * Don't need (or can't) update reserved tags here, they remain + * static and should never need resizing. + */ + bt_update_count(&tags->bitmap_tags, tdepth); + blk_mq_tag_wakeup_all(tags); + return 0; +} + ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page) { char *orig_page = page; diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h index e144f68ec45f..e7ff5ceeeb97 100644 --- a/block/blk-mq-tag.h +++ b/block/blk-mq-tag.h @@ -55,6 +55,7 @@ extern void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, void (*fn)(void *data extern bool blk_mq_has_free_tags(struct blk_mq_tags *tags); extern ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page); extern void blk_mq_tag_init_last_tag(struct blk_mq_tags *tags, unsigned int *last_tag); +extern int blk_mq_tag_update_depth(struct blk_mq_tags *tags, unsigned int depth); enum { BLK_MQ_TAG_CACHE_MIN = 1, diff --git a/block/blk-mq.c b/block/blk-mq.c index 0fbef7e9bef1..7b71ab1b1536 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1789,6 +1789,28 @@ void blk_mq_free_tag_set(struct blk_mq_tag_set *set) } EXPORT_SYMBOL(blk_mq_free_tag_set); +int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr) +{ + struct blk_mq_tag_set *set = q->tag_set; + struct blk_mq_hw_ctx *hctx; + int i, ret; + + if (!set || nr > set->queue_depth) + return -EINVAL; + + ret = 0; + queue_for_each_hw_ctx(q, hctx, i) { + ret = blk_mq_tag_update_depth(hctx->tags, nr); + if (ret) + break; + } + + if (!ret) + q->nr_requests = nr; + + return ret; +} + void blk_mq_disable_hotplug(void) { mutex_lock(&all_q_mutex); diff --git a/block/blk-mq.h b/block/blk-mq.h index 5e5a378962b7..7db4fe4bd002 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -32,6 +32,7 @@ void blk_mq_drain_queue(struct request_queue *q); void blk_mq_free_queue(struct request_queue *q); void blk_mq_clone_flush_request(struct request *flush_rq, struct request *orig_rq); +int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr); /* * CPU hotplug helpers diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 7500f876dae4..4d6811ac13fd 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -48,11 +48,10 @@ static ssize_t queue_requests_show(struct request_queue *q, char *page) static ssize_t queue_requests_store(struct request_queue *q, const char *page, size_t count) { - struct request_list *rl; unsigned long nr; - int ret; + int ret, err; - if (!q->request_fn) + if (!q->request_fn && !q->mq_ops) return -EINVAL; ret = queue_var_store(&nr, page, count); @@ -62,40 +61,14 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) if (nr < BLKDEV_MIN_RQ) nr = BLKDEV_MIN_RQ; - spin_lock_irq(q->queue_lock); - q->nr_requests = nr; - blk_queue_congestion_threshold(q); - - /* congestion isn't cgroup aware and follows root blkcg for now */ - rl = &q->root_rl; - - if (rl->count[BLK_RW_SYNC] >= queue_congestion_on_threshold(q)) - blk_set_queue_congested(q, BLK_RW_SYNC); - else if (rl->count[BLK_RW_SYNC] < queue_congestion_off_threshold(q)) - blk_clear_queue_congested(q, BLK_RW_SYNC); - - if (rl->count[BLK_RW_ASYNC] >= queue_congestion_on_threshold(q)) - blk_set_queue_congested(q, BLK_RW_ASYNC); - else if (rl->count[BLK_RW_ASYNC] < queue_congestion_off_threshold(q)) - blk_clear_queue_congested(q, BLK_RW_ASYNC); - - blk_queue_for_each_rl(rl, q) { - if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { - blk_set_rl_full(rl, BLK_RW_SYNC); - } else { - blk_clear_rl_full(rl, BLK_RW_SYNC); - wake_up(&rl->wait[BLK_RW_SYNC]); - } - - if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { - blk_set_rl_full(rl, BLK_RW_ASYNC); - } else { - blk_clear_rl_full(rl, BLK_RW_ASYNC); - wake_up(&rl->wait[BLK_RW_ASYNC]); - } - } + if (q->request_fn) + err = blk_update_nr_requests(q, nr); + else + err = blk_mq_update_nr_requests(q, nr); + + if (err) + return err; - spin_unlock_irq(q->queue_lock); return ret; } diff --git a/block/blk.h b/block/blk.h index 95cab70000e3..45385e9abf6f 100644 --- a/block/blk.h +++ b/block/blk.h @@ -188,6 +188,8 @@ static inline int queue_congestion_off_threshold(struct request_queue *q) return q->nr_congestion_off; } +extern int blk_update_nr_requests(struct request_queue *, unsigned int); + /* * Contribute to IO statistics IFF: * diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index a06ca7b5ea05..f45424453338 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -63,7 +63,7 @@ struct blk_mq_hw_ctx { struct blk_mq_tag_set { struct blk_mq_ops *ops; unsigned int nr_hw_queues; - unsigned int queue_depth; + unsigned int queue_depth; /* max hw supported */ unsigned int reserved_tags; unsigned int cmd_size; /* per-request extra data */ int numa_node; -- cgit v1.2.3 From 78d683e838a60ec4ba4591cca4364cba84a9e626 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 19 May 2014 15:58:32 -0700 Subject: mm, fs: Add vm_ops->name as an alternative to arch_vma_name arch_vma_name sucks. It's a silly hack, and it's annoying to implement correctly. In fact, AFAICS, even the straightforward x86 implementation is incorrect (I suspect that it breaks if the vdso mapping is split or gets remapped). This adds a new vm_ops->name operation that can replace it. The followup patches will remove all uses of arch_vma_name on x86, fixing a couple of annoyances in the process. Signed-off-by: Andy Lutomirski Link: http://lkml.kernel.org/r/2eee21791bb36a0a408c5c2bdb382a9e6a41ca4a.1400538962.git.luto@amacapital.net Signed-off-by: H. Peter Anvin --- fs/binfmt_elf.c | 8 ++++++++ fs/proc/task_mmu.c | 6 ++++++ include/linux/mm.h | 6 ++++++ 3 files changed, 20 insertions(+) (limited to 'include') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index aa3cb626671e..df9ea4186d75 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1108,6 +1108,14 @@ static bool always_dump_vma(struct vm_area_struct *vma) /* Any vsyscall mappings? */ if (vma == get_gate_vma(vma->vm_mm)) return true; + + /* + * Assume that all vmas with a .name op should always be dumped. + * If this changes, a new vm_ops field can easily be added. + */ + if (vma->vm_ops && vma->vm_ops->name && vma->vm_ops->name(vma)) + return true; + /* * arch_vma_name() returns non-NULL for special architecture mappings, * such as vDSO sections. diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 442177b1119a..9b2f5d62ce63 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -300,6 +300,12 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) goto done; } + if (vma->vm_ops && vma->vm_ops->name) { + name = vma->vm_ops->name(vma); + if (name) + goto done; + } + name = arch_vma_name(vma); if (!name) { pid_t tid; diff --git a/include/linux/mm.h b/include/linux/mm.h index bf9811e1321a..63f8d4efe303 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -239,6 +239,12 @@ struct vm_operations_struct { */ int (*access)(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write); + + /* Called by the /proc/PID/maps code to ask the vma whether it + * has a special name. Returning non-NULL will also cause this + * vma to be dumped unconditionally. */ + const char *(*name)(struct vm_area_struct *vma); + #ifdef CONFIG_NUMA /* * set_policy() op must add a reference to any non-NULL @new mempolicy -- cgit v1.2.3 From a62c34bd2a8a3f159945becd57401e478818d51c Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 19 May 2014 15:58:33 -0700 Subject: x86, mm: Improve _install_special_mapping and fix x86 vdso naming Using arch_vma_name to give special mappings a name is awkward. x86 currently implements it by comparing the start address of the vma to the expected address of the vdso. This requires tracking the start address of special mappings and is probably buggy if a special vma is split or moved. Improve _install_special_mapping to just name the vma directly. Use it to give the x86 vvar area a name, which should make CRIU's life easier. As a side effect, the vvar area will show up in core dumps. This could be considered weird and is fixable. [hpa: I say we accept this as-is but be prepared to deal with knocking out the vvars from core dumps if this becomes a problem.] Cc: Cyrill Gorcunov Cc: Pavel Emelyanov Signed-off-by: Andy Lutomirski Link: http://lkml.kernel.org/r/276b39b6b645fb11e345457b503f17b83c2c6fd0.1400538962.git.luto@amacapital.net Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/vdso.h | 6 ++- arch/x86/mm/init_64.c | 3 -- arch/x86/vdso/vdso2c.h | 5 ++- arch/x86/vdso/vdso32-setup.c | 7 ---- arch/x86/vdso/vma.c | 25 ++++++++----- include/linux/mm.h | 4 +- include/linux/mm_types.h | 6 +++ mm/mmap.c | 89 +++++++++++++++++++++++++++++--------------- 8 files changed, 94 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h index d0a2c909c72d..30be253dd283 100644 --- a/arch/x86/include/asm/vdso.h +++ b/arch/x86/include/asm/vdso.h @@ -7,10 +7,14 @@ #ifndef __ASSEMBLER__ +#include + struct vdso_image { void *data; unsigned long size; /* Always a multiple of PAGE_SIZE */ - struct page **pages; /* Big enough for data/size page pointers */ + + /* text_mapping.pages is big enough for data/size page pointers */ + struct vm_special_mapping text_mapping; unsigned long alt, alt_len; diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 6f881842116c..9deb59b0baea 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1223,9 +1223,6 @@ int in_gate_area_no_mm(unsigned long addr) const char *arch_vma_name(struct vm_area_struct *vma) { - if (vma->vm_mm && vma->vm_start == - (long __force)vma->vm_mm->context.vdso) - return "[vdso]"; if (vma == &gate_vma) return "[vsyscall]"; return NULL; diff --git a/arch/x86/vdso/vdso2c.h b/arch/x86/vdso/vdso2c.h index ed2e894e89ab..3dcc61e796e9 100644 --- a/arch/x86/vdso/vdso2c.h +++ b/arch/x86/vdso/vdso2c.h @@ -136,7 +136,10 @@ static int GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) fprintf(outfile, "const struct vdso_image %s = {\n", name); fprintf(outfile, "\t.data = raw_data,\n"); fprintf(outfile, "\t.size = %lu,\n", data_size); - fprintf(outfile, "\t.pages = pages,\n"); + fprintf(outfile, "\t.text_mapping = {\n"); + fprintf(outfile, "\t\t.name = \"[vdso]\",\n"); + fprintf(outfile, "\t\t.pages = pages,\n"); + fprintf(outfile, "\t},\n"); if (alt_sec) { fprintf(outfile, "\t.alt = %lu,\n", (unsigned long)alt_sec->sh_offset); diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c index c3ed708e50f4..e4f7781ee162 100644 --- a/arch/x86/vdso/vdso32-setup.c +++ b/arch/x86/vdso/vdso32-setup.c @@ -119,13 +119,6 @@ __initcall(ia32_binfmt_init); #else /* CONFIG_X86_32 */ -const char *arch_vma_name(struct vm_area_struct *vma) -{ - if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso) - return "[vdso]"; - return NULL; -} - struct vm_area_struct *get_gate_vma(struct mm_struct *mm) { return NULL; diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c index 8ad0081df7a8..e1513c47872a 100644 --- a/arch/x86/vdso/vma.c +++ b/arch/x86/vdso/vma.c @@ -30,7 +30,8 @@ void __init init_vdso_image(const struct vdso_image *image) BUG_ON(image->size % PAGE_SIZE != 0); for (i = 0; i < npages; i++) - image->pages[i] = virt_to_page(image->data + i*PAGE_SIZE); + image->text_mapping.pages[i] = + virt_to_page(image->data + i*PAGE_SIZE); apply_alternatives((struct alt_instr *)(image->data + image->alt), (struct alt_instr *)(image->data + image->alt + @@ -91,6 +92,10 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr) unsigned long addr; int ret = 0; static struct page *no_pages[] = {NULL}; + static struct vm_special_mapping vvar_mapping = { + .name = "[vvar]", + .pages = no_pages, + }; if (calculate_addr) { addr = vdso_addr(current->mm->start_stack, @@ -112,21 +117,23 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr) /* * MAYWRITE to allow gdb to COW and set breakpoints */ - ret = install_special_mapping(mm, - addr, - image->size, - VM_READ|VM_EXEC| - VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, - image->pages); + vma = _install_special_mapping(mm, + addr, + image->size, + VM_READ|VM_EXEC| + VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, + &image->text_mapping); - if (ret) + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); goto up_fail; + } vma = _install_special_mapping(mm, addr + image->size, image->sym_end_mapping - image->size, VM_READ, - no_pages); + &vvar_mapping); if (IS_ERR(vma)) { ret = PTR_ERR(vma); diff --git a/include/linux/mm.h b/include/linux/mm.h index 63f8d4efe303..05aab09803e6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1782,7 +1782,9 @@ extern struct file *get_mm_exe_file(struct mm_struct *mm); extern int may_expand_vm(struct mm_struct *mm, unsigned long npages); extern struct vm_area_struct *_install_special_mapping(struct mm_struct *mm, unsigned long addr, unsigned long len, - unsigned long flags, struct page **pages); + unsigned long flags, + const struct vm_special_mapping *spec); +/* This is an obsolete alternative to _install_special_mapping. */ extern int install_special_mapping(struct mm_struct *mm, unsigned long addr, unsigned long len, unsigned long flags, struct page **pages); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 8967e20cbe57..22c6f4e16d10 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -510,4 +510,10 @@ static inline void clear_tlb_flush_pending(struct mm_struct *mm) } #endif +struct vm_special_mapping +{ + const char *name; + struct page **pages; +}; + #endif /* _LINUX_MM_TYPES_H */ diff --git a/mm/mmap.c b/mm/mmap.c index b1202cf81f4b..52bbc9514d9d 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2872,6 +2872,31 @@ int may_expand_vm(struct mm_struct *mm, unsigned long npages) return 1; } +static int special_mapping_fault(struct vm_area_struct *vma, + struct vm_fault *vmf); + +/* + * Having a close hook prevents vma merging regardless of flags. + */ +static void special_mapping_close(struct vm_area_struct *vma) +{ +} + +static const char *special_mapping_name(struct vm_area_struct *vma) +{ + return ((struct vm_special_mapping *)vma->vm_private_data)->name; +} + +static const struct vm_operations_struct special_mapping_vmops = { + .close = special_mapping_close, + .fault = special_mapping_fault, + .name = special_mapping_name, +}; + +static const struct vm_operations_struct legacy_special_mapping_vmops = { + .close = special_mapping_close, + .fault = special_mapping_fault, +}; static int special_mapping_fault(struct vm_area_struct *vma, struct vm_fault *vmf) @@ -2887,7 +2912,13 @@ static int special_mapping_fault(struct vm_area_struct *vma, */ pgoff = vmf->pgoff - vma->vm_pgoff; - for (pages = vma->vm_private_data; pgoff && *pages; ++pages) + if (vma->vm_ops == &legacy_special_mapping_vmops) + pages = vma->vm_private_data; + else + pages = ((struct vm_special_mapping *)vma->vm_private_data)-> + pages; + + for (; pgoff && *pages; ++pages) pgoff--; if (*pages) { @@ -2900,30 +2931,11 @@ static int special_mapping_fault(struct vm_area_struct *vma, return VM_FAULT_SIGBUS; } -/* - * Having a close hook prevents vma merging regardless of flags. - */ -static void special_mapping_close(struct vm_area_struct *vma) -{ -} - -static const struct vm_operations_struct special_mapping_vmops = { - .close = special_mapping_close, - .fault = special_mapping_fault, -}; - -/* - * Called with mm->mmap_sem held for writing. - * Insert a new vma covering the given region, with the given flags. - * Its pages are supplied by the given array of struct page *. - * The array can be shorter than len >> PAGE_SHIFT if it's null-terminated. - * The region past the last page supplied will always produce SIGBUS. - * The array pointer and the pages it points to are assumed to stay alive - * for as long as this mapping might exist. - */ -struct vm_area_struct *_install_special_mapping(struct mm_struct *mm, - unsigned long addr, unsigned long len, - unsigned long vm_flags, struct page **pages) +static struct vm_area_struct *__install_special_mapping( + struct mm_struct *mm, + unsigned long addr, unsigned long len, + unsigned long vm_flags, const struct vm_operations_struct *ops, + void *priv) { int ret; struct vm_area_struct *vma; @@ -2940,8 +2952,8 @@ struct vm_area_struct *_install_special_mapping(struct mm_struct *mm, vma->vm_flags = vm_flags | mm->def_flags | VM_DONTEXPAND | VM_SOFTDIRTY; vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); - vma->vm_ops = &special_mapping_vmops; - vma->vm_private_data = pages; + vma->vm_ops = ops; + vma->vm_private_data = priv; ret = insert_vm_struct(mm, vma); if (ret) @@ -2958,12 +2970,31 @@ out: return ERR_PTR(ret); } +/* + * Called with mm->mmap_sem held for writing. + * Insert a new vma covering the given region, with the given flags. + * Its pages are supplied by the given array of struct page *. + * The array can be shorter than len >> PAGE_SHIFT if it's null-terminated. + * The region past the last page supplied will always produce SIGBUS. + * The array pointer and the pages it points to are assumed to stay alive + * for as long as this mapping might exist. + */ +struct vm_area_struct *_install_special_mapping( + struct mm_struct *mm, + unsigned long addr, unsigned long len, + unsigned long vm_flags, const struct vm_special_mapping *spec) +{ + return __install_special_mapping(mm, addr, len, vm_flags, + &special_mapping_vmops, (void *)spec); +} + int install_special_mapping(struct mm_struct *mm, unsigned long addr, unsigned long len, unsigned long vm_flags, struct page **pages) { - struct vm_area_struct *vma = _install_special_mapping(mm, - addr, len, vm_flags, pages); + struct vm_area_struct *vma = __install_special_mapping( + mm, addr, len, vm_flags, &legacy_special_mapping_vmops, + (void *)pages); if (IS_ERR(vma)) return PTR_ERR(vma); -- cgit v1.2.3 From e69595c2501094d85d5bbca87592acb8a481109a Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 19 Feb 2014 19:36:08 +0200 Subject: drm: Make the vblank disable timer per-crtc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently there's one per-device vblank disable timer, and it gets reset wheneven the vblank refcount for any crtc drops to zero. That means that one crtc could accidentally be keeping the vblank interrupts for other crtcs enabled even if there are no users for them. Make the disable timer per-crtc to avoid this issue. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 40 +++++++++++++++++++++------------------- include/drm/drmP.h | 4 +++- 2 files changed, 24 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index ea20c4aa1b6b..90c59a8c820f 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -140,33 +140,34 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) static void vblank_disable_fn(unsigned long arg) { - struct drm_device *dev = (struct drm_device *)arg; + struct drm_vblank_crtc *vblank = (void *)arg; + struct drm_device *dev = vblank->dev; unsigned long irqflags; - int i; + int crtc = vblank->crtc; if (!dev->vblank_disable_allowed) return; - for (i = 0; i < dev->num_crtcs; i++) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); - if (atomic_read(&dev->vblank[i].refcount) == 0 && - dev->vblank[i].enabled) { - DRM_DEBUG("disabling vblank on crtc %d\n", i); - vblank_disable_and_save(dev, i); - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_lock_irqsave(&dev->vbl_lock, irqflags); + if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) { + DRM_DEBUG("disabling vblank on crtc %d\n", crtc); + vblank_disable_and_save(dev, crtc); } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); } void drm_vblank_cleanup(struct drm_device *dev) { + int crtc; + /* Bail if the driver didn't call drm_vblank_init() */ if (dev->num_crtcs == 0) return; - del_timer_sync(&dev->vblank_disable_timer); - - vblank_disable_fn((unsigned long)dev); + for (crtc = 0; crtc < dev->num_crtcs; crtc++) { + del_timer_sync(&dev->vblank[crtc].disable_timer); + vblank_disable_fn((unsigned long)&dev->vblank[crtc]); + } kfree(dev->vblank); @@ -178,8 +179,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) { int i, ret = -ENOMEM; - setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, - (unsigned long)dev); spin_lock_init(&dev->vbl_lock); spin_lock_init(&dev->vblank_time_lock); @@ -189,8 +188,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->vblank) goto err; - for (i = 0; i < num_crtcs; i++) + for (i = 0; i < num_crtcs; i++) { + dev->vblank[i].dev = dev; + dev->vblank[i].crtc = i; init_waitqueue_head(&dev->vblank[i].queue); + setup_timer(&dev->vblank[i].disable_timer, vblank_disable_fn, + (unsigned long)&dev->vblank[i]); + } DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n"); @@ -900,7 +904,7 @@ void drm_vblank_put(struct drm_device *dev, int crtc) /* Last user schedules interrupt disable */ if (atomic_dec_and_test(&dev->vblank[crtc].refcount) && (drm_vblank_offdelay > 0)) - mod_timer(&dev->vblank_disable_timer, + mod_timer(&dev->vblank[crtc].disable_timer, jiffies + ((drm_vblank_offdelay * HZ)/1000)); } EXPORT_SYMBOL(drm_vblank_put); @@ -909,8 +913,6 @@ EXPORT_SYMBOL(drm_vblank_put); * drm_vblank_off - disable vblank events on a CRTC * @dev: DRM device * @crtc: CRTC in question - * - * Caller must hold event lock. */ void drm_vblank_off(struct drm_device *dev, int crtc) { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 12f10bc2395f..9d982d483f12 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1024,14 +1024,17 @@ struct drm_pending_vblank_event { }; struct drm_vblank_crtc { + struct drm_device *dev; /* pointer to the drm_device */ wait_queue_head_t queue; /**< VBLANK wait queue */ struct timeval time[DRM_VBLANKTIME_RBSIZE]; /**< timestamp of current count */ + struct timer_list disable_timer; /* delayed disable timer */ atomic_t count; /**< number of VBLANK interrupts */ atomic_t refcount; /* number of users of vblank interruptsper crtc */ u32 last; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ u32 last_wait; /* Last vblank seqno waited per CRTC */ unsigned int inmodeset; /* Display driver is setting mode */ + int crtc; /* crtc index */ bool enabled; /* so we don't call enable more than once per disable */ }; @@ -1119,7 +1122,6 @@ struct drm_device { spinlock_t vblank_time_lock; /**< Protects vblank count and time updates during vblank enable/disable */ spinlock_t vbl_lock; - struct timer_list vblank_disable_timer; u32 max_vblank_count; /**< size of vblank counter register */ -- cgit v1.2.3 From f2752282f7f7b566b07113dd5aa130725aa95ac4 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 19 Feb 2014 21:29:49 +0200 Subject: drm: Add drm_vblank_on() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_vblank_off() will turn off vblank interrupts, but as long as the refcount is elevated drm_vblank_get() will not re-enable them. This is a problem is someone is holding a vblank reference while a modeset is happening, and the driver requires vblank interrupt to work during that time. Add drm_vblank_on() as a counterpart to drm_vblank_off() which will re-enabled vblank interrupts if the refcount is already elevated. This will allow drivers to choose the specific places in the modeset sequence at which vblank interrupts get disabled and enabled. Testcase: igt/kms_flip/*-vs-suspend Signed-off-by: Ville Syrjälä [danvet: Add Testcase tag for the igt I've written.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 72 ++++++++++++++++++++++++++---------- drivers/gpu/drm/i915/intel_display.c | 8 ++++ include/drm/drmP.h | 1 + 3 files changed, 62 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 13d671ed3421..dd786d84daab 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -839,6 +839,41 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) smp_mb__after_atomic_inc(); } +/** + * drm_vblank_enable - enable the vblank interrupt on a CRTC + * @dev: DRM device + * @crtc: CRTC in question + */ +static int drm_vblank_enable(struct drm_device *dev, int crtc) +{ + int ret = 0; + + assert_spin_locked(&dev->vbl_lock); + + spin_lock(&dev->vblank_time_lock); + + if (!dev->vblank[crtc].enabled) { + /* Enable vblank irqs under vblank_time_lock protection. + * All vblank count & timestamp updates are held off + * until we are done reinitializing master counter and + * timestamps. Filtercode in drm_handle_vblank() will + * prevent double-accounting of same vblank interval. + */ + ret = dev->driver->enable_vblank(dev, crtc); + DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret); + if (ret) + atomic_dec(&dev->vblank[crtc].refcount); + else { + dev->vblank[crtc].enabled = true; + drm_update_vblank_count(dev, crtc); + } + } + + spin_unlock(&dev->vblank_time_lock); + + return ret; +} + /** * drm_vblank_get - get a reference count on vblank events * @dev: DRM device @@ -858,25 +893,7 @@ int drm_vblank_get(struct drm_device *dev, int crtc) spin_lock_irqsave(&dev->vbl_lock, irqflags); /* Going from 0->1 means we have to enable interrupts again */ if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) { - spin_lock(&dev->vblank_time_lock); - if (!dev->vblank[crtc].enabled) { - /* Enable vblank irqs under vblank_time_lock protection. - * All vblank count & timestamp updates are held off - * until we are done reinitializing master counter and - * timestamps. Filtercode in drm_handle_vblank() will - * prevent double-accounting of same vblank interval. - */ - ret = dev->driver->enable_vblank(dev, crtc); - DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", - crtc, ret); - if (ret) - atomic_dec(&dev->vblank[crtc].refcount); - else { - dev->vblank[crtc].enabled = true; - drm_update_vblank_count(dev, crtc); - } - } - spin_unlock(&dev->vblank_time_lock); + ret = drm_vblank_enable(dev, crtc); } else { if (!dev->vblank[crtc].enabled) { atomic_dec(&dev->vblank[crtc].refcount); @@ -945,6 +962,23 @@ void drm_vblank_off(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_off); +/** + * drm_vblank_on - enable vblank events on a CRTC + * @dev: DRM device + * @crtc: CRTC in question + */ +void drm_vblank_on(struct drm_device *dev, int crtc) +{ + unsigned long irqflags; + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + /* re-enable interrupts if there's are users left */ + if (atomic_read(&dev->vblank[crtc].refcount) != 0) + WARN_ON(drm_vblank_enable(dev, crtc)); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); +} +EXPORT_SYMBOL(drm_vblank_on); + /** * drm_vblank_pre_modeset - account for vblanks across mode sets * @dev: DRM device diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 72b5c34d8ce7..313f29d1aae2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3737,6 +3737,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) * happening. */ intel_wait_for_vblank(dev, intel_crtc->pipe); + + drm_vblank_on(dev, pipe); } /* IPS only exists on ULT machines and is tied to pipe A. */ @@ -3828,6 +3830,8 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) * to change the workaround. */ haswell_mode_set_planes_workaround(intel_crtc); ilk_crtc_enable_planes(crtc); + + drm_vblank_on(dev, pipe); } static void ironlake_pfit_disable(struct intel_crtc *crtc) @@ -4351,6 +4355,8 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); + + drm_vblank_on(dev, pipe); } static void i9xx_crtc_enable(struct drm_crtc *crtc) @@ -4398,6 +4404,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); + + drm_vblank_on(dev, pipe); } static void i9xx_pfit_disable(struct intel_crtc *crtc) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 9d982d483f12..7339b2b00724 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1360,6 +1360,7 @@ extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); extern void drm_vblank_off(struct drm_device *dev, int crtc); +extern void drm_vblank_on(struct drm_device *dev, int crtc); extern void drm_vblank_cleanup(struct drm_device *dev); extern u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, struct timeval *tvblank, unsigned flags); -- cgit v1.2.3 From 735e0da7fc55a0456476f6b40f85024f68f87092 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 24 Mar 2014 16:06:42 -0500 Subject: irqchip: align irqchip OF match table section naming Make the irqchip OF match table section naming aligned with other OF match table sections in preparation to have a common definition. Acked-by: Arnd Bergmann Signed-off-by: Rob Herring --- drivers/irqchip/irqchip.c | 6 +++--- include/asm-generic/vmlinux.lds.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index cad3e2495552..0fe2f718d81c 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -19,11 +19,11 @@ * special section. */ static const struct of_device_id -irqchip_of_match_end __used __section(__irqchip_of_end); +irqchip_of_match_end __used __section(__irqchip_of_table_end); -extern struct of_device_id __irqchip_begin[]; +extern struct of_device_id __irqchip_of_table[]; void __init irqchip_init(void) { - of_irq_init(__irqchip_begin); + of_irq_init(__irqchip_of_table); } diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 146e4fffd710..b1c6f9d0c4ff 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -151,9 +151,9 @@ #ifdef CONFIG_IRQCHIP #define IRQCHIP_OF_MATCH_TABLE() \ . = ALIGN(8); \ - VMLINUX_SYMBOL(__irqchip_begin) = .; \ + VMLINUX_SYMBOL(__irqchip_of_table) = .; \ *(__irqchip_of_table) \ - *(__irqchip_of_end) + *(__irqchip_of_table_end) #else #define IRQCHIP_OF_MATCH_TABLE() #endif -- cgit v1.2.3 From 9a721c41113a50ccbe184d67a5e551feb99e36a9 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 24 Mar 2014 16:11:54 -0500 Subject: ARM: align cpu_method_of_table naming The cpu_method_of_table is the oddball of the various OF linker sections. In preparation to have common linker section definitions, align the cpu_method_of_table with the other definitions for the naming and ending with a blank struct. Acked-by: Arnd Bergmann Signed-off-by: Rob Herring Cc: Russell King --- arch/arm/kernel/devtree.c | 11 +++++++---- include/asm-generic/vmlinux.lds.h | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c index 3e5a2056a466..ea9ce92a4b52 100644 --- a/arch/arm/kernel/devtree.c +++ b/arch/arm/kernel/devtree.c @@ -33,18 +33,21 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size) } #ifdef CONFIG_SMP -extern struct of_cpu_method __cpu_method_of_table_begin[]; -extern struct of_cpu_method __cpu_method_of_table_end[]; +extern struct of_cpu_method __cpu_method_of_table[]; + +static const struct of_cpu_method __cpu_method_of_table_sentinel + __used __section(__cpu_method_of_table_end); + static int __init set_smp_ops_by_method(struct device_node *node) { const char *method; - struct of_cpu_method *m = __cpu_method_of_table_begin; + struct of_cpu_method *m = __cpu_method_of_table; if (of_property_read_string(node, "enable-method", &method)) return 0; - for (; m < __cpu_method_of_table_end; m++) + for (; m->method; m++) if (!strcmp(m->method, method)) { smp_set_ops(m->ops); return 1; diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index b1c6f9d0c4ff..fe57c5f1bd1a 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -179,9 +179,9 @@ #ifdef CONFIG_SMP #define CPU_METHOD_OF_TABLES() . = ALIGN(8); \ - VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \ + VMLINUX_SYMBOL(__cpu_method_of_table) = .; \ *(__cpu_method_of_table) \ - VMLINUX_SYMBOL(__cpu_method_of_table_end) = .; + *(__cpu_method_of_table_end) #else #define CPU_METHOD_OF_TABLES() #endif -- cgit v1.2.3 From 063092883039e49e5edc1f63133dc5ad437361a2 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 24 Mar 2014 16:59:20 -0500 Subject: vmlinuz.lds: define OF table sections with macros OF table sections all have the same pattern, so create a macro to define them and insure consistency. Acked-by: Arnd Bergmann Signed-off-by: Rob Herring --- include/asm-generic/vmlinux.lds.h | 58 ++++++++++----------------------------- 1 file changed, 14 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index fe57c5f1bd1a..b9404f6590f1 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -139,52 +139,22 @@ #define TRACE_SYSCALLS() #endif -#ifdef CONFIG_CLKSRC_OF -#define CLKSRC_OF_TABLES() . = ALIGN(8); \ - VMLINUX_SYMBOL(__clksrc_of_table) = .; \ - *(__clksrc_of_table) \ - *(__clksrc_of_table_end) -#else -#define CLKSRC_OF_TABLES() -#endif -#ifdef CONFIG_IRQCHIP -#define IRQCHIP_OF_MATCH_TABLE() \ +#define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name) +#define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name) +#define OF_TABLE(cfg, name) __OF_TABLE(config_enabled(cfg), name) +#define _OF_TABLE_0(name) +#define _OF_TABLE_1(name) \ . = ALIGN(8); \ - VMLINUX_SYMBOL(__irqchip_of_table) = .; \ - *(__irqchip_of_table) \ - *(__irqchip_of_table_end) -#else -#define IRQCHIP_OF_MATCH_TABLE() -#endif - -#ifdef CONFIG_COMMON_CLK -#define CLK_OF_TABLES() . = ALIGN(8); \ - VMLINUX_SYMBOL(__clk_of_table) = .; \ - *(__clk_of_table) \ - *(__clk_of_table_end) -#else -#define CLK_OF_TABLES() -#endif - -#ifdef CONFIG_OF_RESERVED_MEM -#define RESERVEDMEM_OF_TABLES() \ - . = ALIGN(8); \ - VMLINUX_SYMBOL(__reservedmem_of_table) = .; \ - *(__reservedmem_of_table) \ - *(__reservedmem_of_table_end) -#else -#define RESERVEDMEM_OF_TABLES() -#endif - -#ifdef CONFIG_SMP -#define CPU_METHOD_OF_TABLES() . = ALIGN(8); \ - VMLINUX_SYMBOL(__cpu_method_of_table) = .; \ - *(__cpu_method_of_table) \ - *(__cpu_method_of_table_end) -#else -#define CPU_METHOD_OF_TABLES() -#endif + VMLINUX_SYMBOL(__##name##_of_table) = .; \ + *(__##name##_of_table) \ + *(__##name##_of_table_end) + +#define CLKSRC_OF_TABLES() OF_TABLE(CONFIG_CLKSRC_OF, clksrc) +#define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip) +#define CLK_OF_TABLES() OF_TABLE(CONFIG_COMMON_CLK, clk) +#define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) +#define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) #define KERNEL_DTB() \ STRUCT_ALIGN(); \ -- cgit v1.2.3 From 9dd3107576c4bbd40e1c2c8b24d560abf9a7b991 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 8 May 2014 16:06:17 -0500 Subject: of: align RESERVEDMEM_OF_DECLARE function callbacks to other callbacks All the parameters for RESERVEDMEM_OF_DECLARE function callbacks are members of struct reserved_mem, so just pass the struct ptr to callback functions so the function callback is more in line with other OF match table callbacks. Acked-by: Marek Szyprowski Acked-by: Grant Likely Signed-off-by: Rob Herring --- drivers/of/of_reserved_mem.c | 2 +- include/linux/of_reserved_mem.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index e420eb52e5c9..632aae861375 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -188,7 +188,7 @@ static int __init __reserved_mem_init_node(struct reserved_mem *rmem) if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) continue; - if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) { + if (initfn(rmem) == 0) { pr_info("Reserved memory: initialized node %s, compatible id %s\n", rmem->name, compat); return 0; diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index 9b1fbb7f29fc..4c81b84e95ff 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -21,8 +21,8 @@ struct reserved_mem_ops { struct device *dev); }; -typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem, - unsigned long node, const char *uname); +typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem); + #ifdef CONFIG_OF_RESERVED_MEM void fdt_init_reserved_mem(void); -- cgit v1.2.3 From 54196ccbe0ba1f268a646059473313589db35b01 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 8 May 2014 16:09:24 -0500 Subject: of: consolidate linker section OF match table declarations We now have several OF match tables using linker sections that are nearly the same definition. The only variation is the callback function prototype. Create a common define for creating linker section OF match table entries which each table declaration can use. Acked-by: Grant Likely Signed-off-by: Rob Herring --- drivers/clocksource/clksrc-of.c | 2 +- drivers/irqchip/irqchip.h | 7 +++---- include/linux/clk-provider.h | 5 +---- include/linux/clocksource.h | 16 +++------------- include/linux/of.h | 22 ++++++++++++++++++++++ include/linux/of_reserved_mem.h | 18 ++---------------- 6 files changed, 32 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index ae2e4278c42a..0093a8e49e14 100644 --- a/drivers/clocksource/clksrc-of.c +++ b/drivers/clocksource/clksrc-of.c @@ -27,7 +27,7 @@ void __init clocksource_of_init(void) { struct device_node *np; const struct of_device_id *match; - clocksource_of_init_fn init_func; + of_init_fn_1 init_func; unsigned clocksources = 0; for_each_matching_node_and_match(np, __clksrc_of_table, &match) { diff --git a/drivers/irqchip/irqchip.h b/drivers/irqchip/irqchip.h index e445ba2d6add..0f6486d4f1b0 100644 --- a/drivers/irqchip/irqchip.h +++ b/drivers/irqchip/irqchip.h @@ -11,6 +11,8 @@ #ifndef _IRQCHIP_H #define _IRQCHIP_H +#include + /* * This macro must be used by the different irqchip drivers to declare * the association between their DT compatible string and their @@ -21,9 +23,6 @@ * @compstr: compatible string of the irqchip driver * @fn: initialization function */ -#define IRQCHIP_DECLARE(name,compstr,fn) \ - static const struct of_device_id irqchip_of_match_##name \ - __used __section(__irqchip_of_table) \ - = { .compatible = compstr, .data = fn } +#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn) #endif diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 511917416fb0..a6e4008a0bf7 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -498,10 +498,7 @@ struct clk_onecell_data { extern struct of_device_id __clk_of_table; -#define CLK_OF_DECLARE(name, compat, fn) \ - static const struct of_device_id __clk_of_table_##name \ - __used __section(__clk_of_table) \ - = { .compatible = compat, .data = fn }; +#define CLK_OF_DECLARE(name, compat, fn) OF_DECLARE_1(clk, name, compat, fn) #ifdef CONFIG_OF int of_clk_add_provider(struct device_node *np, diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 67301a405712..a16b497d5159 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -339,23 +339,13 @@ extern int clocksource_mmio_init(void __iomem *, const char *, extern int clocksource_i8253_init(void); -struct device_node; -typedef void(*clocksource_of_init_fn)(struct device_node *); +#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ + OF_DECLARE_1(clksrc, name, compat, fn) + #ifdef CONFIG_CLKSRC_OF extern void clocksource_of_init(void); - -#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ - static const struct of_device_id __clksrc_of_table_##name \ - __used __section(__clksrc_of_table) \ - = { .compatible = compat, \ - .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn } #else static inline void clocksource_of_init(void) {} -#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ - static const struct of_device_id __clksrc_of_table_##name \ - __attribute__((unused)) \ - = { .compatible = compat, \ - .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn } #endif #endif /* _LINUX_CLOCKSOURCE_H */ diff --git a/include/linux/of.h b/include/linux/of.h index 3bad8d106e0e..bf65335b4d05 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -757,4 +757,26 @@ static inline int of_get_available_child_count(const struct device_node *np) return num; } +#ifdef CONFIG_OF +#define _OF_DECLARE(table, name, compat, fn, fn_type) \ + static const struct of_device_id __of_table_##name \ + __used __section(__##table##_of_table) \ + = { .compatible = compat, \ + .data = (fn == (fn_type)NULL) ? fn : fn } +#else +#define _OF_DECLARE(table, name, compat, fn, fn_type) \ + static const struct of_device_id __of_table_##name \ + __attribute__((unused)) \ + = { .compatible = compat, \ + .data = (fn == (fn_type)NULL) ? fn : fn } +#endif + +typedef int (*of_init_fn_2)(struct device_node *, struct device_node *); +typedef void (*of_init_fn_1)(struct device_node *); + +#define OF_DECLARE_1(table, name, compat, fn) \ + _OF_DECLARE(table, name, compat, fn, of_init_fn_1) +#define OF_DECLARE_2(table, name, compat, fn) \ + _OF_DECLARE(table, name, compat, fn, of_init_fn_2) + #endif /* _LINUX_OF_H */ diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index 4c81b84e95ff..4669ddfdd5af 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -23,31 +23,17 @@ struct reserved_mem_ops { typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem); +#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ + _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn) #ifdef CONFIG_OF_RESERVED_MEM void fdt_init_reserved_mem(void); void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size); - -#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ - static const struct of_device_id __reservedmem_of_table_##name \ - __used __section(__reservedmem_of_table) \ - = { .compatible = compat, \ - .data = (init == (reservedmem_of_init_fn)NULL) ? \ - init : init } - #else static inline void fdt_init_reserved_mem(void) { } static inline void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size) { } - -#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ - static const struct of_device_id __reservedmem_of_table_##name \ - __attribute__((unused)) \ - = { .compatible = compat, \ - .data = (init == (reservedmem_of_init_fn)NULL) ? \ - init : init } - #endif #endif /* __OF_RESERVED_MEM_H */ -- cgit v1.2.3 From b0b6abd34c1b508d4ac95dbc614f36c49d29e65a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 27 Mar 2014 08:06:16 -0500 Subject: serial: earlycon: add DT support This adds the infrastructure to generic earlycon for earlycon setup using DT. The actual setup is not enabled until a following commit to add the FDT parsing. Signed-off-by: Rob Herring Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Arnd Bergmann Acked-by: Grant Likely --- drivers/tty/serial/earlycon.c | 28 ++++++++++++++++++++++++++++ include/asm-generic/vmlinux.lds.h | 4 +++- include/linux/serial_core.h | 6 ++++++ 3 files changed, 37 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index c92e83088adb..5131b5ee6164 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #ifdef CONFIG_FIX_EARLYCON_MEM #include @@ -32,6 +34,9 @@ static struct earlycon_device early_console_dev = { .con = &early_con, }; +static const struct of_device_id __earlycon_of_table_sentinel + __used __section(__earlycon_of_table_end); + static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) { void __iomem *base; @@ -142,3 +147,26 @@ int __init setup_earlycon(char *buf, const char *match, register_console(early_console_dev.con); return 0; } + +int __init of_setup_earlycon(unsigned long addr, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + struct uart_port *port = &early_console_dev.port; + + port->iotype = UPIO_MEM; + port->mapbase = addr; + port->uartclk = BASE_BAUD * 16; + port->membase = earlycon_map(addr, SZ_4K); + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, NULL); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + + register_console(early_console_dev.con); + return 0; +} diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index b9404f6590f1..d647637cd699 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -155,6 +155,7 @@ #define CLK_OF_TABLES() OF_TABLE(CONFIG_COMMON_CLK, clk) #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) #define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) +#define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) #define KERNEL_DTB() \ STRUCT_ALIGN(); \ @@ -483,7 +484,8 @@ CLKSRC_OF_TABLES() \ CPU_METHOD_OF_TABLES() \ KERNEL_DTB() \ - IRQCHIP_OF_MATCH_TABLE() + IRQCHIP_OF_MATCH_TABLE() \ + EARLYCON_OF_TABLES() #define INIT_TEXT \ *(.init.text) \ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 7a15b5b24c0b..5bbb809ee197 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -294,6 +294,9 @@ struct earlycon_device { int setup_earlycon(char *buf, const char *match, int (*setup)(struct earlycon_device *, const char *)); +extern int of_setup_earlycon(unsigned long addr, + int (*setup)(struct earlycon_device *, const char *)); + #define EARLYCON_DECLARE(name, func) \ static int __init name ## _setup_earlycon(char *buf) \ { \ @@ -301,6 +304,9 @@ static int __init name ## _setup_earlycon(char *buf) \ } \ early_param("earlycon", name ## _setup_earlycon); +#define OF_EARLYCON_DECLARE(name, compat, fn) \ + _OF_DECLARE(earlycon, name, compat, fn, void *) + struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *c); void uart_parse_options(char *options, int *baud, int *parity, int *bits, -- cgit v1.2.3 From e06e8b27082852bdab417af884241a4ed2037c73 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 27 Mar 2014 07:37:43 -0500 Subject: of/fdt: add FDT address translation support Copy u-boot's FDT address translation code from common/fdt_support. This code was originally based on the kernel's unflattened DT address parsing code. This commit can be reverted once relicensing of this code to GPLv2/BSD is done and it is added to libfdt. Signed-off-by: Rob Herring Acked-by: Grant Likely --- drivers/of/Makefile | 2 + drivers/of/fdt_address.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_fdt.h | 1 + 3 files changed, 244 insertions(+) create mode 100644 drivers/of/fdt_address.c (limited to 'include') diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 9891232f999e..099b1fb00af4 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,5 +1,6 @@ obj-y = base.o device.o platform.o obj-$(CONFIG_OF_FLATTREE) += fdt.o +obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o @@ -12,3 +13,4 @@ obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt +CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c new file mode 100644 index 000000000000..8d3dc6fbdb7a --- /dev/null +++ b/drivers/of/fdt_address.c @@ -0,0 +1,241 @@ +/* + * FDT Address translation based on u-boot fdt_support.c which in turn was + * based on the kernel unflattened DT address translation code. + * + * (C) Copyright 2007 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * + * 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, or (at your option) + * any later version. + */ +#include +#include +#include +#include +#include + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 +#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ + (ns) > 0) + +/* Debug utility */ +#ifdef DEBUG +static void __init of_dump_addr(const char *s, const __be32 *addr, int na) +{ + pr_debug("%s", s); + while(na--) + pr_cont(" %08x", *(addr++)); + pr_debug("\n"); +} +#else +static void __init of_dump_addr(const char *s, const __be32 *addr, int na) { } +#endif + +/* Callbacks for bus specific translators */ +struct of_bus { + void (*count_cells)(const void *blob, int parentoffset, + int *addrc, int *sizec); + u64 (*map)(__be32 *addr, const __be32 *range, + int na, int ns, int pna); + int (*translate)(__be32 *addr, u64 offset, int na); +}; + +/* Default translator (generic bus) */ +static void __init fdt_bus_default_count_cells(const void *blob, int parentoffset, + int *addrc, int *sizec) +{ + const __be32 *prop; + + if (addrc) { + prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL); + if (prop) + *addrc = be32_to_cpup(prop); + else + *addrc = dt_root_addr_cells; + } + + if (sizec) { + prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); + if (prop) + *sizec = be32_to_cpup(prop); + else + *sizec = dt_root_size_cells; + } +} + +static u64 __init fdt_bus_default_map(__be32 *addr, const __be32 *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = of_read_number(range, na); + s = of_read_number(range + na + pna, ns); + da = of_read_number(addr, na); + + pr_debug("FDT: default map, cp=%llx, s=%llx, da=%llx\n", + cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int __init fdt_bus_default_translate(__be32 *addr, u64 offset, int na) +{ + u64 a = of_read_number(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = cpu_to_fdt32(a >> 32); + addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu); + + return 0; +} + +/* Array of bus specific translators */ +static const struct of_bus of_busses[] __initconst = { + /* Default */ + { + .count_cells = fdt_bus_default_count_cells, + .map = fdt_bus_default_map, + .translate = fdt_bus_default_translate, + }, +}; + +static int __init fdt_translate_one(const void *blob, int parent, + const struct of_bus *bus, + const struct of_bus *pbus, __be32 *addr, + int na, int ns, int pna, const char *rprop) +{ + const __be32 *ranges; + int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + ranges = fdt_getprop(blob, parent, rprop, &rlen); + if (!ranges) + return 1; + if (rlen == 0) { + offset = of_read_number(addr, na); + memset(addr, 0, pna * 4); + pr_debug("FDT: empty ranges, 1:1 translation\n"); + goto finish; + } + + pr_debug("FDT: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) { + pr_debug("FDT: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + + finish: + of_dump_addr("FDT: parent translation for:", addr, pna); + pr_debug("FDT: with offset: %llx\n", offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +u64 __init fdt_translate_address(const void *blob, int node_offset) +{ + int parent, len; + const struct of_bus *bus, *pbus; + const __be32 *reg; + __be32 addr[OF_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = OF_BAD_ADDR; + + pr_debug("FDT: ** translation for device %s **\n", + fdt_get_name(blob, node_offset, NULL)); + + reg = fdt_getprop(blob, node_offset, "reg", &len); + if (!reg) { + pr_err("FDT: warning: device tree node '%s' has no address.\n", + fdt_get_name(blob, node_offset, NULL)); + goto bail; + } + + /* Get parent & match bus type */ + parent = fdt_parent_offset(blob, node_offset); + if (parent < 0) + goto bail; + bus = &of_busses[0]; + + /* Cound address cells & copy address locally */ + bus->count_cells(blob, parent, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + pr_err("FDT: Bad cell count for %s\n", + fdt_get_name(blob, node_offset, NULL)); + goto bail; + } + memcpy(addr, reg, na * 4); + + pr_debug("FDT: bus (na=%d, ns=%d) on %s\n", + na, ns, fdt_get_name(blob, parent, NULL)); + of_dump_addr("OF: translating address:", addr, na); + + /* Translate */ + for (;;) { + /* Switch to parent bus */ + node_offset = parent; + parent = fdt_parent_offset(blob, node_offset); + + /* If root, we have finished */ + if (parent < 0) { + pr_debug("FDT: reached root node\n"); + result = of_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = &of_busses[0]; + pbus->count_cells(blob, parent, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + pr_err("FDT: Bad cell count for %s\n", + fdt_get_name(blob, node_offset, NULL)); + break; + } + + pr_debug("FDT: parent bus (na=%d, ns=%d) on %s\n", + pna, pns, fdt_get_name(blob, parent, NULL)); + + /* Apply bus translation */ + if (fdt_translate_one(blob, node_offset, bus, pbus, + addr, na, ns, pna, "ranges")) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + of_dump_addr("FDT: one level translation:", addr, na); + } + bail: + return result; +} diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 5c0ab057eecf..05117899fcb4 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -83,6 +83,7 @@ extern void unflatten_device_tree(void); extern void unflatten_and_copy_device_tree(void); extern void early_init_devtree(void *); extern void early_get_first_memblock_info(void *, phys_addr_t *); +extern u64 fdt_translate_address(const void *blob, int node_offset); #else /* CONFIG_OF_FLATTREE */ static inline void early_init_fdt_scan_reserved_mem(void) {} static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } -- cgit v1.2.3 From 77f2ea2f8d0833f9e976368481fb9a0775acf9e7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 30 Apr 2014 11:20:53 -0600 Subject: DMA-API: Clarify physical/bus address distinction The DMA-API documentation sometimes refers to "physical addresses" when it really means "bus addresses." Sometimes these are identical, but they may be different if the bridge leading to the bus performs address translation. Update the documentation to use "bus address" when appropriate. Also, consistently capitalize "DMA", use parens with function names, use dev_printk() in examples, and reword a few sections for clarity. No functional change; documentation changes only. Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman Acked-by: Arnd Bergmann Acked-by: James Bottomley Acked-by: Randy Dunlap --- Documentation/DMA-API-HOWTO.txt | 192 +++++++++++++++++++++++++--------------- Documentation/DMA-API.txt | 139 +++++++++++++++-------------- Documentation/DMA-ISA-LPC.txt | 4 +- include/linux/dma-mapping.h | 6 ++ include/linux/types.h | 1 + 5 files changed, 204 insertions(+), 138 deletions(-) (limited to 'include') diff --git a/Documentation/DMA-API-HOWTO.txt b/Documentation/DMA-API-HOWTO.txt index 5e983031cc11..fd3727b94ac2 100644 --- a/Documentation/DMA-API-HOWTO.txt +++ b/Documentation/DMA-API-HOWTO.txt @@ -9,16 +9,76 @@ This is a guide to device driver writers on how to use the DMA API with example pseudo-code. For a concise description of the API, see DMA-API.txt. -Most of the 64bit platforms have special hardware that translates bus -addresses (DMA addresses) into physical addresses. This is similar to -how page tables and/or a TLB translates virtual addresses to physical -addresses on a CPU. This is needed so that e.g. PCI devices can -access with a Single Address Cycle (32bit DMA address) any page in the -64bit physical address space. Previously in Linux those 64bit -platforms had to set artificial limits on the maximum RAM size in the -system, so that the virt_to_bus() static scheme works (the DMA address -translation tables were simply filled on bootup to map each bus -address to the physical page __pa(bus_to_virt())). + CPU and DMA addresses + +There are several kinds of addresses involved in the DMA API, and it's +important to understand the differences. + +The kernel normally uses virtual addresses. Any address returned by +kmalloc(), vmalloc(), and similar interfaces is a virtual address and can +be stored in a "void *". + +The virtual memory system (TLB, page tables, etc.) translates virtual +addresses to CPU physical addresses, which are stored as "phys_addr_t" or +"resource_size_t". The kernel manages device resources like registers as +physical addresses. These are the addresses in /proc/iomem. The physical +address is not directly useful to a driver; it must use ioremap() to map +the space and produce a virtual address. + +I/O devices use a third kind of address: a "bus address" or "DMA address". +If a device has registers at an MMIO address, or if it performs DMA to read +or write system memory, the addresses used by the device are bus addresses. +In some systems, bus addresses are identical to CPU physical addresses, but +in general they are not. IOMMUs and host bridges can produce arbitrary +mappings between physical and bus addresses. + +Here's a picture and some examples: + + CPU CPU Bus + Virtual Physical Address + Address Address Space + Space Space + + +-------+ +------+ +------+ + | | |MMIO | Offset | | + | | Virtual |Space | applied | | + C +-------+ --------> B +------+ ----------> +------+ A + | | mapping | | by host | | + +-----+ | | | | bridge | | +--------+ + | | | | +------+ | | | | + | CPU | | | | RAM | | | | Device | + | | | | | | | | | | + +-----+ +-------+ +------+ +------+ +--------+ + | | Virtual |Buffer| Mapping | | + X +-------+ --------> Y +------+ <---------- +------+ Z + | | mapping | RAM | by IOMMU + | | | | + | | | | + +-------+ +------+ + +During the enumeration process, the kernel learns about I/O devices and +their MMIO space and the host bridges that connect them to the system. For +example, if a PCI device has a BAR, the kernel reads the bus address (A) +from the BAR and converts it to a CPU physical address (B). The address B +is stored in a struct resource and usually exposed via /proc/iomem. When a +driver claims a device, it typically uses ioremap() to map physical address +B at a virtual address (C). It can then use, e.g., ioread32(C), to access +the device registers at bus address A. + +If the device supports DMA, the driver sets up a buffer using kmalloc() or +a similar interface, which returns a virtual address (X). The virtual +memory system maps X to a physical address (Y) in system RAM. The driver +can use virtual address X to access the buffer, but the device itself +cannot because DMA doesn't go through the CPU virtual memory system. + +In some simple systems, the device can do DMA directly to physical address +Y. But in many others, there is IOMMU hardware that translates bus +addresses to physical addresses, e.g., it translates Z to Y. This is part +of the reason for the DMA API: the driver can give a virtual address X to +an interface like dma_map_single(), which sets up any required IOMMU +mapping and returns the bus address Z. The driver then tells the device to +do DMA to Z, and the IOMMU maps it to the buffer at address Y in system +RAM. So that Linux can use the dynamic DMA mapping, it needs some help from the drivers, namely it has to take into account that DMA addresses should be @@ -29,17 +89,17 @@ The following API will work of course even on platforms where no such hardware exists. Note that the DMA API works with any bus independent of the underlying -microprocessor architecture. You should use the DMA API rather than -the bus specific DMA API (e.g. pci_dma_*). +microprocessor architecture. You should use the DMA API rather than the +bus-specific DMA API, i.e., use the dma_map_*() interfaces rather than the +pci_map_*() interfaces. First of all, you should make sure #include -is in your driver. This file will obtain for you the definition of the -dma_addr_t (which can hold any valid DMA address for the platform) -type which should be used everywhere you hold a DMA (bus) address -returned from the DMA mapping functions. +is in your driver, which provides the definition of dma_addr_t. This type +can hold any valid DMA or bus address for the platform and should be used +everywhere you hold a DMA address returned from the DMA mapping functions. What memory is DMA'able? @@ -123,9 +183,9 @@ Here, dev is a pointer to the device struct of your device, and mask is a bit mask describing which bits of an address your device supports. It returns zero if your card can perform DMA properly on the machine given the address mask you provided. In general, the -device struct of your device is embedded in the bus specific device -struct of your device. For example, a pointer to the device struct of -your PCI device is pdev->dev (pdev is a pointer to the PCI device +device struct of your device is embedded in the bus-specific device +struct of your device. For example, &pdev->dev is a pointer to the +device struct of a PCI device (pdev is a pointer to the PCI device struct of your device). If it returns non-zero, your device cannot perform DMA properly on @@ -147,8 +207,7 @@ exactly why. The standard 32-bit addressing device would do something like this: if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) { - printk(KERN_WARNING - "mydev: No suitable DMA available.\n"); + dev_warn(dev, "mydev: No suitable DMA available\n"); goto ignore_this_device; } @@ -170,8 +229,7 @@ all 64-bits when accessing streaming DMA: } else if (!dma_set_mask(dev, DMA_BIT_MASK(32))) { using_dac = 0; } else { - printk(KERN_WARNING - "mydev: No suitable DMA available.\n"); + dev_warn(dev, "mydev: No suitable DMA available\n"); goto ignore_this_device; } @@ -187,8 +245,7 @@ the case would look like this: using_dac = 0; consistent_using_dac = 0; } else { - printk(KERN_WARNING - "mydev: No suitable DMA available.\n"); + dev_warn(dev, "mydev: No suitable DMA available\n"); goto ignore_this_device; } @@ -201,8 +258,7 @@ Finally, if your device can only drive the low 24-bits of address you might do something like: if (dma_set_mask(dev, DMA_BIT_MASK(24))) { - printk(KERN_WARNING - "mydev: 24-bit DMA addressing not available.\n"); + dev_warn(dev, "mydev: 24-bit DMA addressing not available\n"); goto ignore_this_device; } @@ -232,14 +288,14 @@ Here is pseudo-code showing how this might be done: card->playback_enabled = 1; } else { card->playback_enabled = 0; - printk(KERN_WARNING "%s: Playback disabled due to DMA limitations.\n", + dev_warn(dev, "%s: Playback disabled due to DMA limitations\n", card->name); } if (!dma_set_mask(dev, RECORD_ADDRESS_BITS)) { card->record_enabled = 1; } else { card->record_enabled = 0; - printk(KERN_WARNING "%s: Record disabled due to DMA limitations.\n", + dev_warn(dev, "%s: Record disabled due to DMA limitations\n", card->name); } @@ -331,7 +387,7 @@ context with the GFP_ATOMIC flag. Size is the length of the region you want to allocate, in bytes. This routine will allocate RAM for that region, so it acts similarly to -__get_free_pages (but takes size instead of a page order). If your +__get_free_pages() (but takes size instead of a page order). If your driver needs regions sized smaller than a page, you may prefer using the dma_pool interface, described below. @@ -343,11 +399,11 @@ the consistent DMA mask has been explicitly changed via dma_set_coherent_mask(). This is true of the dma_pool interface as well. -dma_alloc_coherent returns two values: the virtual address which you +dma_alloc_coherent() returns two values: the virtual address which you can use to access it from the CPU and dma_handle which you pass to the card. -The cpu return address and the DMA bus master address are both +The CPU virtual address and the DMA bus address are both guaranteed to be aligned to the smallest PAGE_SIZE order which is greater than or equal to the requested size. This invariant exists (for example) to guarantee that if you allocate a chunk @@ -359,13 +415,13 @@ To unmap and free such a DMA region, you call: dma_free_coherent(dev, size, cpu_addr, dma_handle); where dev, size are the same as in the above call and cpu_addr and -dma_handle are the values dma_alloc_coherent returned to you. +dma_handle are the values dma_alloc_coherent() returned to you. This function may not be called in interrupt context. If your driver needs lots of smaller memory regions, you can write -custom code to subdivide pages returned by dma_alloc_coherent, +custom code to subdivide pages returned by dma_alloc_coherent(), or you can use the dma_pool API to do that. A dma_pool is like -a kmem_cache, but it uses dma_alloc_coherent not __get_free_pages. +a kmem_cache, but it uses dma_alloc_coherent(), not __get_free_pages(). Also, it understands common hardware constraints for alignment, like queue heads needing to be aligned on N byte boundaries. @@ -381,29 +437,29 @@ type of data is "align" (which is expressed in bytes, and must be a power of two). If your device has no boundary crossing restrictions, pass 0 for alloc; passing 4096 says memory allocated from this pool must not cross 4KByte boundaries (but at that time it may be better to -go for dma_alloc_coherent directly instead). +use dma_alloc_coherent() directly instead). -Allocate memory from a dma pool like this: +Allocate memory from a DMA pool like this: cpu_addr = dma_pool_alloc(pool, flags, &dma_handle); flags are SLAB_KERNEL if blocking is permitted (not in_interrupt nor -holding SMP locks), SLAB_ATOMIC otherwise. Like dma_alloc_coherent, +holding SMP locks), SLAB_ATOMIC otherwise. Like dma_alloc_coherent(), this returns two values, cpu_addr and dma_handle. Free memory that was allocated from a dma_pool like this: dma_pool_free(pool, cpu_addr, dma_handle); -where pool is what you passed to dma_pool_alloc, and cpu_addr and -dma_handle are the values dma_pool_alloc returned. This function +where pool is what you passed to dma_pool_alloc(), and cpu_addr and +dma_handle are the values dma_pool_alloc() returned. This function may be called in interrupt context. Destroy a dma_pool by calling: dma_pool_destroy(pool); -Make sure you've called dma_pool_free for all memory allocated +Make sure you've called dma_pool_free() for all memory allocated from a pool before you destroy the pool. This function may not be called in interrupt context. @@ -418,7 +474,7 @@ one of the following values: DMA_FROM_DEVICE DMA_NONE -One should provide the exact DMA direction if you know it. +You should provide the exact DMA direction if you know it. DMA_TO_DEVICE means "from main memory to the device" DMA_FROM_DEVICE means "from the device to main memory" @@ -489,14 +545,14 @@ and to unmap it: dma_unmap_single(dev, dma_handle, size, direction); You should call dma_mapping_error() as dma_map_single() could fail and return -error. Not all dma implementations support dma_mapping_error() interface. +error. Not all DMA implementations support the dma_mapping_error() interface. However, it is a good practice to call dma_mapping_error() interface, which will invoke the generic mapping error check interface. Doing so will ensure -that the mapping code will work correctly on all dma implementations without +that the mapping code will work correctly on all DMA implementations without any dependency on the specifics of the underlying implementation. Using the returned address without checking for errors could result in failures ranging from panics to silent data corruption. A couple of examples of incorrect ways -to check for errors that make assumptions about the underlying dma +to check for errors that make assumptions about the underlying DMA implementation are as follows and these are applicable to dma_map_page() as well. @@ -516,12 +572,12 @@ Incorrect example 2: goto map_error; } -You should call dma_unmap_single when the DMA activity is finished, e.g. +You should call dma_unmap_single() when the DMA activity is finished, e.g., from the interrupt which told you that the DMA transfer is done. -Using cpu pointers like this for single mappings has a disadvantage, +Using cpu pointers like this for single mappings has a disadvantage: you cannot reference HIGHMEM memory in this way. Thus, there is a -map/unmap interface pair akin to dma_{map,unmap}_single. These +map/unmap interface pair akin to dma_{map,unmap}_single(). These interfaces deal with page/offset pairs instead of cpu pointers. Specifically: @@ -550,7 +606,7 @@ Here, "offset" means byte offset within the given page. You should call dma_mapping_error() as dma_map_page() could fail and return error as outlined under the dma_map_single() discussion. -You should call dma_unmap_page when the DMA activity is finished, e.g. +You should call dma_unmap_page() when the DMA activity is finished, e.g., from the interrupt which told you that the DMA transfer is done. With scatterlists, you map a region gathered from several regions by: @@ -588,18 +644,16 @@ PLEASE NOTE: The 'nents' argument to the dma_unmap_sg call must be it should _NOT_ be the 'count' value _returned_ from the dma_map_sg call. -Every dma_map_{single,sg} call should have its dma_unmap_{single,sg} -counterpart, because the bus address space is a shared resource (although -in some ports the mapping is per each BUS so less devices contend for the -same bus address space) and you could render the machine unusable by eating -all bus addresses. +Every dma_map_{single,sg}() call should have its dma_unmap_{single,sg}() +counterpart, because the bus address space is a shared resource and +you could render the machine unusable by consuming all bus addresses. If you need to use the same streaming DMA region multiple times and touch the data in between the DMA transfers, the buffer needs to be synced -properly in order for the cpu and device to see the most uptodate and +properly in order for the cpu and device to see the most up-to-date and correct copy of the DMA buffer. -So, firstly, just map it with dma_map_{single,sg}, and after each DMA +So, firstly, just map it with dma_map_{single,sg}(), and after each DMA transfer call either: dma_sync_single_for_cpu(dev, dma_handle, size, direction); @@ -623,9 +677,9 @@ or: as appropriate. After the last DMA transfer call one of the DMA unmap routines -dma_unmap_{single,sg}. If you don't touch the data from the first dma_map_* -call till dma_unmap_*, then you don't have to call the dma_sync_* -routines at all. +dma_unmap_{single,sg}(). If you don't touch the data from the first +dma_map_*() call till dma_unmap_*(), then you don't have to call the +dma_sync_*() routines at all. Here is pseudo code which shows a situation in which you would need to use the dma_sync_*() interfaces. @@ -690,12 +744,12 @@ to use the dma_sync_*() interfaces. } } -Drivers converted fully to this interface should not use virt_to_bus any -longer, nor should they use bus_to_virt. Some drivers have to be changed a -little bit, because there is no longer an equivalent to bus_to_virt in the +Drivers converted fully to this interface should not use virt_to_bus() any +longer, nor should they use bus_to_virt(). Some drivers have to be changed a +little bit, because there is no longer an equivalent to bus_to_virt() in the dynamic DMA mapping scheme - you have to always store the DMA addresses -returned by the dma_alloc_coherent, dma_pool_alloc, and dma_map_single -calls (dma_map_sg stores them in the scatterlist itself if the platform +returned by the dma_alloc_coherent(), dma_pool_alloc(), and dma_map_single() +calls (dma_map_sg() stores them in the scatterlist itself if the platform supports dynamic DMA mapping in hardware) in your driver structures and/or in the card registers. @@ -709,9 +763,9 @@ as it is impossible to correctly support them. DMA address space is limited on some architectures and an allocation failure can be determined by: -- checking if dma_alloc_coherent returns NULL or dma_map_sg returns 0 +- checking if dma_alloc_coherent() returns NULL or dma_map_sg returns 0 -- checking the returned dma_addr_t of dma_map_single and dma_map_page +- checking the dma_addr_t returned from dma_map_single() and dma_map_page() by using dma_mapping_error(): dma_addr_t dma_handle; @@ -794,7 +848,7 @@ Example 2: (if buffers are allocated in a loop, unmap all mapped buffers when dma_unmap_single(array[i].dma_addr); } -Networking drivers must call dev_kfree_skb to free the socket buffer +Networking drivers must call dev_kfree_skb() to free the socket buffer and return NETDEV_TX_OK if the DMA mapping fails on the transmit hook (ndo_start_xmit). This means that the socket buffer is just dropped in the failure case. @@ -831,7 +885,7 @@ transform some example code. DEFINE_DMA_UNMAP_LEN(len); }; -2) Use dma_unmap_{addr,len}_set to set these values. +2) Use dma_unmap_{addr,len}_set() to set these values. Example, before: ringp->mapping = FOO; @@ -842,7 +896,7 @@ transform some example code. dma_unmap_addr_set(ringp, mapping, FOO); dma_unmap_len_set(ringp, len, BAR); -3) Use dma_unmap_{addr,len} to access these values. +3) Use dma_unmap_{addr,len}() to access these values. Example, before: dma_unmap_single(dev, ringp->mapping, ringp->len, diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index e865279cec58..1147eba43128 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -4,22 +4,26 @@ James E.J. Bottomley This document describes the DMA API. For a more gentle introduction -of the API (and actual examples) see -Documentation/DMA-API-HOWTO.txt. +of the API (and actual examples), see Documentation/DMA-API-HOWTO.txt. -This API is split into two pieces. Part I describes the API. Part II -describes the extensions to the API for supporting non-consistent -memory machines. Unless you know that your driver absolutely has to -support non-consistent platforms (this is usually only legacy -platforms) you should only use the API described in part I. +This API is split into two pieces. Part I describes the basic API. +Part II describes extensions for supporting non-consistent memory +machines. Unless you know that your driver absolutely has to support +non-consistent platforms (this is usually only legacy platforms) you +should only use the API described in part I. Part I - dma_ API ------------------------------------- -To get the dma_ API, you must #include +To get the dma_ API, you must #include . This +provides dma_addr_t and the interfaces described below. +A dma_addr_t can hold any valid DMA or bus address for the platform. It +can be given to a device to use as a DMA source or target. A cpu cannot +reference a dma_addr_t directly because there may be translation between +its physical address space and the bus address space. -Part Ia - Using large dma-coherent buffers +Part Ia - Using large DMA-coherent buffers ------------------------------------------ void * @@ -33,20 +37,21 @@ to make sure to flush the processor's write buffers before telling devices to read that memory.) This routine allocates a region of bytes of consistent memory. -It also returns a which may be cast to an unsigned -integer the same width as the bus and used as the physical address -base of the region. -Returns: a pointer to the allocated region (in the processor's virtual +It returns a pointer to the allocated region (in the processor's virtual address space) or NULL if the allocation failed. +It also returns a which may be cast to an unsigned integer the +same width as the bus and given to the device as the bus address base of +the region. + Note: consistent memory can be expensive on some platforms, and the minimum allocation length may be as big as a page, so you should consolidate your requests for consistent memory as much as possible. The simplest way to do that is to use the dma_pool calls (see below). -The flag parameter (dma_alloc_coherent only) allows the caller to -specify the GFP_ flags (see kmalloc) for the allocation (the +The flag parameter (dma_alloc_coherent() only) allows the caller to +specify the GFP_ flags (see kmalloc()) for the allocation (the implementation may choose to ignore flags that affect the location of the returned memory, like GFP_DMA). @@ -61,24 +66,24 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle) -Free the region of consistent memory you previously allocated. dev, -size and dma_handle must all be the same as those passed into the -consistent allocate. cpu_addr must be the virtual address returned by -the consistent allocate. +Free a region of consistent memory you previously allocated. dev, +size and dma_handle must all be the same as those passed into +dma_alloc_coherent(). cpu_addr must be the virtual address returned by +the dma_alloc_coherent(). Note that unlike their sibling allocation calls, these routines may only be called with IRQs enabled. -Part Ib - Using small dma-coherent buffers +Part Ib - Using small DMA-coherent buffers ------------------------------------------ To get this part of the dma_ API, you must #include -Many drivers need lots of small dma-coherent memory regions for DMA +Many drivers need lots of small DMA-coherent memory regions for DMA descriptors or I/O buffers. Rather than allocating in units of a page or more using dma_alloc_coherent(), you can use DMA pools. These work -much like a struct kmem_cache, except that they use the dma-coherent allocator, +much like a struct kmem_cache, except that they use the DMA-coherent allocator, not __get_free_pages(). Also, they understand common hardware constraints for alignment, like queue heads needing to be aligned on N-byte boundaries. @@ -87,7 +92,7 @@ for alignment, like queue heads needing to be aligned on N-byte boundaries. dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t alloc); -The pool create() routines initialize a pool of dma-coherent buffers +dma_pool_create() initializes a pool of DMA-coherent buffers for use with a given device. It must be called in a context which can sleep. @@ -102,25 +107,26 @@ from this pool must not cross 4KByte boundaries. void *dma_pool_alloc(struct dma_pool *pool, gfp_t gfp_flags, dma_addr_t *dma_handle); -This allocates memory from the pool; the returned memory will meet the size -and alignment requirements specified at creation time. Pass GFP_ATOMIC to -prevent blocking, or if it's permitted (not in_interrupt, not holding SMP locks), -pass GFP_KERNEL to allow blocking. Like dma_alloc_coherent(), this returns -two values: an address usable by the cpu, and the dma address usable by the -pool's device. +This allocates memory from the pool; the returned memory will meet the +size and alignment requirements specified at creation time. Pass +GFP_ATOMIC to prevent blocking, or if it's permitted (not +in_interrupt, not holding SMP locks), pass GFP_KERNEL to allow +blocking. Like dma_alloc_coherent(), this returns two values: an +address usable by the cpu, and the DMA address usable by the pool's +device. void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr); This puts memory back into the pool. The pool is what was passed to -the pool allocation routine; the cpu (vaddr) and dma addresses are what +dma_pool_alloc(); the cpu (vaddr) and DMA addresses are what were returned when that routine allocated the memory being freed. void dma_pool_destroy(struct dma_pool *pool); -The pool destroy() routines free the resources of the pool. They must be +dma_pool_destroy() frees the resources of the pool. It must be called in a context which can sleep. Make sure you've freed all allocated memory back to the pool before you destroy it. @@ -187,9 +193,9 @@ dma_map_single(struct device *dev, void *cpu_addr, size_t size, enum dma_data_direction direction) Maps a piece of processor virtual memory so it can be accessed by the -device and returns the physical handle of the memory. +device and returns the bus address of the memory. -The direction for both api's may be converted freely by casting. +The direction for both APIs may be converted freely by casting. However the dma_ API uses a strongly typed enumerator for its direction: @@ -198,31 +204,30 @@ DMA_TO_DEVICE data is going from the memory to the device DMA_FROM_DEVICE data is coming from the device to the memory DMA_BIDIRECTIONAL direction isn't known -Notes: Not all memory regions in a machine can be mapped by this -API. Further, regions that appear to be physically contiguous in -kernel virtual space may not be contiguous as physical memory. Since -this API does not provide any scatter/gather capability, it will fail -if the user tries to map a non-physically contiguous piece of memory. -For this reason, it is recommended that memory mapped by this API be -obtained only from sources which guarantee it to be physically contiguous -(like kmalloc). - -Further, the physical address of the memory must be within the -dma_mask of the device (the dma_mask represents a bit mask of the -addressable region for the device. I.e., if the physical address of -the memory anded with the dma_mask is still equal to the physical -address, then the device can perform DMA to the memory). In order to +Notes: Not all memory regions in a machine can be mapped by this API. +Further, contiguous kernel virtual space may not be contiguous as +physical memory. Since this API does not provide any scatter/gather +capability, it will fail if the user tries to map a non-physically +contiguous piece of memory. For this reason, memory to be mapped by +this API should be obtained from sources which guarantee it to be +physically contiguous (like kmalloc). + +Further, the bus address of the memory must be within the +dma_mask of the device (the dma_mask is a bit mask of the +addressable region for the device, i.e., if the bus address of +the memory ANDed with the dma_mask is still equal to the bus +address, then the device can perform DMA to the memory). To ensure that the memory allocated by kmalloc is within the dma_mask, the driver may specify various platform-dependent flags to restrict -the physical memory range of the allocation (e.g. on x86, GFP_DMA -guarantees to be within the first 16Mb of available physical memory, +the bus address range of the allocation (e.g., on x86, GFP_DMA +guarantees to be within the first 16MB of available bus addresses, as required by ISA devices). Note also that the above constraints on physical contiguity and dma_mask may not apply if the platform has an IOMMU (a device which -supplies a physical to virtual mapping between the I/O memory bus and -the device). However, to be portable, device driver writers may *not* -assume that such an IOMMU exists. +maps an I/O bus address to a physical memory address). However, to be +portable, device driver writers may *not* assume that such an IOMMU +exists. Warnings: Memory coherency operates at a granularity called the cache line width. In order for memory mapped by this API to operate @@ -281,9 +286,9 @@ cache width is. int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) -In some circumstances dma_map_single and dma_map_page will fail to create +In some circumstances dma_map_single() and dma_map_page() will fail to create a mapping. A driver can check for these errors by testing the returned -dma address with dma_mapping_error(). A non-zero return value means the mapping +DMA address with dma_mapping_error(). A non-zero return value means the mapping could not be created and the driver should take appropriate action (e.g. reduce current DMA mapping usage or delay and try again later). @@ -291,7 +296,7 @@ reduce current DMA mapping usage or delay and try again later). dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction) -Returns: the number of physical segments mapped (this may be shorter +Returns: the number of bus address segments mapped (this may be shorter than passed in if some elements of the scatter/gather list are physically or virtually adjacent and an IOMMU maps them with a single entry). @@ -299,7 +304,7 @@ entry). Please note that the sg cannot be mapped again if it has been mapped once. The mapping process is allowed to destroy information in the sg. -As with the other mapping interfaces, dma_map_sg can fail. When it +As with the other mapping interfaces, dma_map_sg() can fail. When it does, 0 is returned and a driver must take appropriate action. It is critical that the driver do something, in the case of a block driver aborting the request or even oopsing is better than doing nothing and @@ -335,7 +340,7 @@ must be the same as those and passed in to the scatter/gather mapping API. Note: must be the number you passed in, *not* the number of -physical entries returned. +bus address entries returned. void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, @@ -391,10 +396,10 @@ The four functions above are just like the counterpart functions without the _attrs suffixes, except that they pass an optional struct dma_attrs*. -struct dma_attrs encapsulates a set of "dma attributes". For the +struct dma_attrs encapsulates a set of "DMA attributes". For the definition of struct dma_attrs see linux/dma-attrs.h. -The interpretation of dma attributes is architecture-specific, and +The interpretation of DMA attributes is architecture-specific, and each attribute should be documented in Documentation/DMA-attributes.txt. If struct dma_attrs* is NULL, the semantics of each of these @@ -458,7 +463,7 @@ Note: where the platform can return consistent memory, it will guarantee that the sync points become nops. Warning: Handling non-consistent memory is a real pain. You should -only ever use this API if you positively know your driver will be +only use this API if you positively know your driver will be required to work on one of the rare (usually non-PCI) architectures that simply cannot make consistent memory. @@ -496,26 +501,26 @@ dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, dma_addr_t device_addr, size_t size, int flags) -Declare region of memory to be handed out by dma_alloc_coherent when +Declare region of memory to be handed out by dma_alloc_coherent() when it's asked for coherent memory for this device. bus_addr is the physical address to which the memory is currently assigned in the bus responding region (this will be used by the platform to perform the mapping). -device_addr is the physical address the device needs to be programmed +device_addr is the bus address the device needs to be programmed with actually to address this memory (this will be handed out as the dma_addr_t in dma_alloc_coherent()). size is the size of the area (must be multiples of PAGE_SIZE). -flags can be or'd together and are: +flags can be ORed together and are: DMA_MEMORY_MAP - request that the memory returned from dma_alloc_coherent() be directly writable. DMA_MEMORY_IO - request that the memory returned from -dma_alloc_coherent() be addressable using read/write/memcpy_toio etc. +dma_alloc_coherent() be addressable using read()/write()/memcpy_toio() etc. One or both of these flags must be present. @@ -572,7 +577,7 @@ region is occupied. Part III - Debug drivers use of the DMA-API ------------------------------------------- -The DMA-API as described above as some constraints. DMA addresses must be +The DMA-API as described above has some constraints. DMA addresses must be released with the corresponding function with the same size for example. With the advent of hardware IOMMUs it becomes more and more important that drivers do not violate those constraints. In the worst case such a violation can @@ -690,11 +695,11 @@ architectural default. void debug_dmap_mapping_error(struct device *dev, dma_addr_t dma_addr); dma-debug interface debug_dma_mapping_error() to debug drivers that fail -to check dma mapping errors on addresses returned by dma_map_single() and +to check DMA mapping errors on addresses returned by dma_map_single() and dma_map_page() interfaces. This interface clears a flag set by debug_dma_map_page() to indicate that dma_mapping_error() has been called by the driver. When driver does unmap, debug_dma_unmap() checks the flag and if this flag is still set, prints warning message that includes call trace that leads up to the unmap. This interface can be called from dma_mapping_error() -routines to enable dma mapping error check debugging. +routines to enable DMA mapping error check debugging. diff --git a/Documentation/DMA-ISA-LPC.txt b/Documentation/DMA-ISA-LPC.txt index e767805b4182..b1a19835e907 100644 --- a/Documentation/DMA-ISA-LPC.txt +++ b/Documentation/DMA-ISA-LPC.txt @@ -16,7 +16,7 @@ To do ISA style DMA you need to include two headers: #include The first is the generic DMA API used to convert virtual addresses to -physical addresses (see Documentation/DMA-API.txt for details). +bus addresses (see Documentation/DMA-API.txt for details). The second contains the routines specific to ISA DMA transfers. Since this is not present on all platforms make sure you construct your @@ -50,7 +50,7 @@ early as possible and not release it until the driver is unloaded.) Part III - Address translation ------------------------------ -To translate the virtual address to a physical use the normal DMA +To translate the virtual address to a bus address, use the normal DMA API. Do _not_ use isa_virt_to_phys() even though it does the same thing. The reason for this is that the function isa_virt_to_phys() will require a Kconfig dependency to ISA, not just ISA_DMA_API which diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index fd4aee29ad10..b9aa2b97aab5 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -8,6 +8,12 @@ #include #include +/* + * A dma_addr_t can hold any valid DMA or bus address for the platform. + * It can be given to a device to use as a DMA source or target. A CPU cannot + * reference a dma_addr_t directly because there may be translation between + * its physical address space and the bus address space. + */ struct dma_map_ops { void* (*alloc)(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp, diff --git a/include/linux/types.h b/include/linux/types.h index 4d118ba11349..a0bb7048687f 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -142,6 +142,7 @@ typedef unsigned long blkcnt_t; #define pgoff_t unsigned long #endif +/* A dma_addr_t can hold any valid DMA or bus address for the platform */ #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT typedef u64 dma_addr_t; #else -- cgit v1.2.3 From 88a984ba0795f14a3847edbd7fabe652289ea89b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 20 May 2014 16:54:22 -0600 Subject: DMA-API: Change dma_declare_coherent_memory() CPU address to phys_addr_t dma_declare_coherent_memory() takes two addresses for a region of memory: a "bus_addr" and a "device_addr". I think the intent is that "bus_addr" is the physical address a *CPU* would use to access the region, and "device_addr" is the bus address the *device* would use to address the region. Rename "bus_addr" to "phys_addr" and change its type to phys_addr_t. Most callers already supply a phys_addr_t for this argument. The others supply a 32-bit integer (a constant, unsigned int, or __u32) and need no change. Use "unsigned long", not phys_addr_t, to hold PFNs. No functional change (this could theoretically fix a truncation in a config with 32-bit dma_addr_t and 64-bit phys_addr_t, but I don't think there are any such cases involving this code). Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann Acked-by: Greg Kroah-Hartman Acked-by: James Bottomley Acked-by: Randy Dunlap --- Documentation/DMA-API.txt | 9 ++++----- drivers/base/dma-coherent.c | 10 +++++----- drivers/base/dma-mapping.c | 6 +++--- include/asm-generic/dma-coherent.h | 13 +++++-------- include/linux/dma-mapping.h | 7 ++++--- 5 files changed, 21 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index 1147eba43128..4f1cdc5febd1 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -497,19 +497,18 @@ continuing on for size. Again, you *must* observe the cache line boundaries when doing this. int -dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, +dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags) Declare region of memory to be handed out by dma_alloc_coherent() when it's asked for coherent memory for this device. -bus_addr is the physical address to which the memory is currently -assigned in the bus responding region (this will be used by the -platform to perform the mapping). +phys_addr is the cpu physical address to which the memory is currently +assigned (this will be ioremapped so the cpu can access the region). device_addr is the bus address the device needs to be programmed -with actually to address this memory (this will be handed out as the +with to actually address this memory (this will be handed out as the dma_addr_t in dma_alloc_coherent()). size is the size of the area (must be multiples of PAGE_SIZE). diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index bc256b641027..7d6e84a51424 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -10,13 +10,13 @@ struct dma_coherent_mem { void *virt_base; dma_addr_t device_base; - phys_addr_t pfn_base; + unsigned long pfn_base; int size; int flags; unsigned long *bitmap; }; -int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, +int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags) { void __iomem *mem_base = NULL; @@ -32,7 +32,7 @@ int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ - mem_base = ioremap(bus_addr, size); + mem_base = ioremap(phys_addr, size); if (!mem_base) goto out; @@ -45,7 +45,7 @@ int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, dev->dma_mem->virt_base = mem_base; dev->dma_mem->device_base = device_addr; - dev->dma_mem->pfn_base = PFN_DOWN(bus_addr); + dev->dma_mem->pfn_base = PFN_DOWN(phys_addr); dev->dma_mem->size = pages; dev->dma_mem->flags = flags; @@ -208,7 +208,7 @@ int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma, *ret = -ENXIO; if (off < count && user_count <= count - off) { - unsigned pfn = mem->pfn_base + start + off; + unsigned long pfn = mem->pfn_base + start + off; *ret = remap_pfn_range(vma, vma->vm_start, pfn, user_count << PAGE_SHIFT, vma->vm_page_prot); diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index 0ce39a33b3c2..6cd08e145bfa 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -175,7 +175,7 @@ static void dmam_coherent_decl_release(struct device *dev, void *res) /** * dmam_declare_coherent_memory - Managed dma_declare_coherent_memory() * @dev: Device to declare coherent memory for - * @bus_addr: Bus address of coherent memory to be declared + * @phys_addr: Physical address of coherent memory to be declared * @device_addr: Device address of coherent memory to be declared * @size: Size of coherent memory to be declared * @flags: Flags @@ -185,7 +185,7 @@ static void dmam_coherent_decl_release(struct device *dev, void *res) * RETURNS: * 0 on success, -errno on failure. */ -int dmam_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, +int dmam_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags) { void *res; @@ -195,7 +195,7 @@ int dmam_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, if (!res) return -ENOMEM; - rc = dma_declare_coherent_memory(dev, bus_addr, device_addr, size, + rc = dma_declare_coherent_memory(dev, phys_addr, device_addr, size, flags); if (rc == 0) devres_add(dev, res); diff --git a/include/asm-generic/dma-coherent.h b/include/asm-generic/dma-coherent.h index 2be8a2dbc868..0297e5875798 100644 --- a/include/asm-generic/dma-coherent.h +++ b/include/asm-generic/dma-coherent.h @@ -16,16 +16,13 @@ int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma, * Standard interface */ #define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY -extern int -dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, - dma_addr_t device_addr, size_t size, int flags); +int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, + dma_addr_t device_addr, size_t size, int flags); -extern void -dma_release_declared_memory(struct device *dev); +void dma_release_declared_memory(struct device *dev); -extern void * -dma_mark_declared_memory_occupied(struct device *dev, - dma_addr_t device_addr, size_t size); +void *dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size); #else #define dma_alloc_from_coherent(dev, size, handle, ret) (0) #define dma_release_from_coherent(dev, order, vaddr) (0) diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index b9aa2b97aab5..0c3eab1e39ac 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -192,7 +192,7 @@ static inline int dma_get_cache_alignment(void) #ifndef ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY static inline int -dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, +dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags) { return 0; @@ -223,13 +223,14 @@ extern void *dmam_alloc_noncoherent(struct device *dev, size_t size, extern void dmam_free_noncoherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle); #ifdef ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY -extern int dmam_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, +extern int dmam_declare_coherent_memory(struct device *dev, + phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags); extern void dmam_release_declared_memory(struct device *dev); #else /* ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY */ static inline int dmam_declare_coherent_memory(struct device *dev, - dma_addr_t bus_addr, dma_addr_t device_addr, + phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, gfp_t gfp) { return 0; -- cgit v1.2.3 From 66507c7bc8895f0da6b4ad87e96d61a9f7d7a118 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Thu, 1 May 2014 20:51:19 -0400 Subject: mtd: nand: Add support to use nand_base poi databuf as bounce buffer nand_base can be passed a kmap()'d buffers from highmem by filesystems like jffs2. This results in failure to map the physical address of the DMA buffer on various contoller driver on different platforms. This change adds a chip option to use preallocated databuf as bounce buffers used in nand_do_read_ops() and nand_do_write_ops(). This allows for specific nand controller driver to set this option as needed. Signed-off-by: Kamal Dasu Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 37 +++++++++++++++++++++++++++++++------ include/linux/mtd/nand.h | 5 +++++ 2 files changed, 36 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7853b9b0a05e..1b844b8c621f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1500,6 +1501,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, mtd->oobavail : mtd->oobsize; uint8_t *bufpoi, *oob, *buf; + int use_bufpoi; unsigned int max_bitflips = 0; int retry_mode = 0; bool ecc_fail = false; @@ -1522,9 +1524,20 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); + if (!aligned) + use_bufpoi = 1; + else if (chip->options & NAND_USE_BOUNCE_BUFFER) + use_bufpoi = !virt_addr_valid(buf); + else + use_bufpoi = 0; + /* Is the current page in the buffer? */ if (realpage != chip->pagebuf || oob) { - bufpoi = aligned ? buf : chip->buffers->databuf; + bufpoi = use_bufpoi ? chip->buffers->databuf : buf; + + if (use_bufpoi && aligned) + pr_debug("%s: using read bounce buffer for buf@%p\n", + __func__, buf); read_retry: chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); @@ -1546,7 +1559,7 @@ read_retry: ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page); if (ret < 0) { - if (!aligned) + if (use_bufpoi) /* Invalidate page cache */ chip->pagebuf = -1; break; @@ -1555,7 +1568,7 @@ read_retry: max_bitflips = max_t(unsigned int, max_bitflips, ret); /* Transfer not aligned data */ - if (!aligned) { + if (use_bufpoi) { if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && !(mtd->ecc_stats.failed - ecc_failures) && (ops->mode != MTD_OPS_RAW)) { @@ -2375,11 +2388,23 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; uint8_t *wbuf = buf; + int use_bufpoi; + int part_pagewr = (column || writelen < (mtd->writesize - 1)); + + if (part_pagewr) + use_bufpoi = 1; + else if (chip->options & NAND_USE_BOUNCE_BUFFER) + use_bufpoi = !virt_addr_valid(buf); + else + use_bufpoi = 0; - /* Partial page write? */ - if (unlikely(column || writelen < (mtd->writesize - 1))) { + /* Partial page write?, or need to use bounce buffer */ + if (use_bufpoi) { + pr_debug("%s: using write bounce buffer for buf@%p\n", + __func__, buf); cached = 0; - bytes = min_t(int, bytes - column, (int) writelen); + if (part_pagewr) + bytes = min_t(int, bytes - column, writelen); chip->pagebuf = -1; memset(chip->buffers->databuf, 0xff, mtd->writesize); memcpy(&chip->buffers->databuf[column], buf, bytes); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 7a922e6c4e4b..2f0af2891f0f 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -175,6 +175,11 @@ typedef enum { #define NAND_OWN_BUFFERS 0x00020000 /* Chip may not exist, so silence any errors in scan */ #define NAND_SCAN_SILENT_NODEV 0x00040000 +/* + * This option could be defined by controller drivers to protect against + * kmap'ed, vmalloc'ed highmem buffers being passed from upper layers + */ +#define NAND_USE_BOUNCE_BUFFER 0x00080000 /* * Autodetect nand buswidth with readid/onfi. * This suppose the driver will configure the hardware in 8 bits mode -- cgit v1.2.3 From 96ba9dd65788a0bd2a7d1e57ec78b7642f0ccc25 Mon Sep 17 00:00:00 2001 From: Vincenzo Aliberti Date: Thu, 15 May 2014 23:19:32 +0200 Subject: mtd: lpddr: add driver for LPDDR2-NVM PCM memories Signed-off-by: Vincenzo Aliberti Signed-off-by: Brian Norris --- drivers/mtd/lpddr/Kconfig | 12 +- drivers/mtd/lpddr/Makefile | 1 + drivers/mtd/lpddr/lpddr2_nvm.c | 507 +++++++++++++++++++++++++++++++++++++++++ include/uapi/mtd/mtd-abi.h | 1 + 4 files changed, 519 insertions(+), 2 deletions(-) create mode 100644 drivers/mtd/lpddr/lpddr2_nvm.c (limited to 'include') diff --git a/drivers/mtd/lpddr/Kconfig b/drivers/mtd/lpddr/Kconfig index 265f969817e3..468f06dea45d 100644 --- a/drivers/mtd/lpddr/Kconfig +++ b/drivers/mtd/lpddr/Kconfig @@ -1,5 +1,5 @@ -menu "LPDDR flash memory drivers" - depends on MTD!=n +menu "LPDDR & LPDDR2 PCM memory drivers" + depends on MTD config MTD_LPDDR tristate "Support for LPDDR flash chips" @@ -17,4 +17,12 @@ config MTD_QINFO_PROBE Window QINFO interface, permits software to be used for entire families of devices. This serves similar purpose of CFI on legacy Flash products + +config MTD_LPDDR2_NVM + depends on MTD + tristate "Support for LPDDR2-NVM flash chips" + help + This option enables support of PCM memories with a LPDDR2-NVM + (Low power double data rate 2) interface. + endmenu diff --git a/drivers/mtd/lpddr/Makefile b/drivers/mtd/lpddr/Makefile index da48e46b5812..881d440d483e 100644 --- a/drivers/mtd/lpddr/Makefile +++ b/drivers/mtd/lpddr/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_MTD_QINFO_PROBE) += qinfo_probe.o obj-$(CONFIG_MTD_LPDDR) += lpddr_cmds.o +obj-$(CONFIG_MTD_LPDDR2_NVM) += lpddr2_nvm.o diff --git a/drivers/mtd/lpddr/lpddr2_nvm.c b/drivers/mtd/lpddr/lpddr2_nvm.c new file mode 100644 index 000000000000..063cec40d0ae --- /dev/null +++ b/drivers/mtd/lpddr/lpddr2_nvm.c @@ -0,0 +1,507 @@ +/* + * LPDDR2-NVM MTD driver. This module provides read, write, erase, lock/unlock + * support for LPDDR2-NVM PCM memories + * + * Copyright © 2012 Micron Technology, Inc. + * + * Vincenzo Aliberti + * Domenico Manna + * Many thanks to Andrea Vigilante for initial enabling + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Parameters */ +#define ERASE_BLOCKSIZE (0x00020000/2) /* in Word */ +#define WRITE_BUFFSIZE (0x00000400/2) /* in Word */ +#define OW_BASE_ADDRESS 0x00000000 /* OW offset */ +#define BUS_WIDTH 0x00000020 /* x32 devices */ + +/* PFOW symbols address offset */ +#define PFOW_QUERY_STRING_P (0x0000/2) /* in Word */ +#define PFOW_QUERY_STRING_F (0x0002/2) /* in Word */ +#define PFOW_QUERY_STRING_O (0x0004/2) /* in Word */ +#define PFOW_QUERY_STRING_W (0x0006/2) /* in Word */ + +/* OW registers address */ +#define CMD_CODE_OFS (0x0080/2) /* in Word */ +#define CMD_DATA_OFS (0x0084/2) /* in Word */ +#define CMD_ADD_L_OFS (0x0088/2) /* in Word */ +#define CMD_ADD_H_OFS (0x008A/2) /* in Word */ +#define MPR_L_OFS (0x0090/2) /* in Word */ +#define MPR_H_OFS (0x0092/2) /* in Word */ +#define CMD_EXEC_OFS (0x00C0/2) /* in Word */ +#define STATUS_REG_OFS (0x00CC/2) /* in Word */ +#define PRG_BUFFER_OFS (0x0010/2) /* in Word */ + +/* Datamask */ +#define MR_CFGMASK 0x8000 +#define SR_OK_DATAMASK 0x0080 + +/* LPDDR2-NVM Commands */ +#define LPDDR2_NVM_LOCK 0x0061 +#define LPDDR2_NVM_UNLOCK 0x0062 +#define LPDDR2_NVM_SW_PROGRAM 0x0041 +#define LPDDR2_NVM_SW_OVERWRITE 0x0042 +#define LPDDR2_NVM_BUF_PROGRAM 0x00E9 +#define LPDDR2_NVM_BUF_OVERWRITE 0x00EA +#define LPDDR2_NVM_ERASE 0x0020 + +/* LPDDR2-NVM Registers offset */ +#define LPDDR2_MODE_REG_DATA 0x0040 +#define LPDDR2_MODE_REG_CFG 0x0050 + +/* + * Internal Type Definitions + * pcm_int_data contains memory controller details: + * @reg_data : LPDDR2_MODE_REG_DATA register address after remapping + * @reg_cfg : LPDDR2_MODE_REG_CFG register address after remapping + * &bus_width: memory bus-width (eg: x16 2 Bytes, x32 4 Bytes) + */ +struct pcm_int_data { + void __iomem *ctl_regs; + int bus_width; +}; + +static DEFINE_MUTEX(lpdd2_nvm_mutex); + +/* + * Build a map_word starting from an u_long + */ +static inline map_word build_map_word(u_long myword) +{ + map_word val = { {0} }; + val.x[0] = myword; + return val; +} + +/* + * Build Mode Register Configuration DataMask based on device bus-width + */ +static inline u_int build_mr_cfgmask(u_int bus_width) +{ + u_int val = MR_CFGMASK; + + if (bus_width == 0x0004) /* x32 device */ + val = val << 16; + + return val; +} + +/* + * Build Status Register OK DataMask based on device bus-width + */ +static inline u_int build_sr_ok_datamask(u_int bus_width) +{ + u_int val = SR_OK_DATAMASK; + + if (bus_width == 0x0004) /* x32 device */ + val = (val << 16)+val; + + return val; +} + +/* + * Evaluates Overlay Window Control Registers address + */ +static inline u_long ow_reg_add(struct map_info *map, u_long offset) +{ + u_long val = 0; + struct pcm_int_data *pcm_data = map->fldrv_priv; + + val = map->pfow_base + offset*pcm_data->bus_width; + + return val; +} + +/* + * Enable lpddr2-nvm Overlay Window + * Overlay Window is a memory mapped area containing all LPDDR2-NVM registers + * used by device commands as well as uservisible resources like Device Status + * Register, Device ID, etc + */ +static inline void ow_enable(struct map_info *map) +{ + struct pcm_int_data *pcm_data = map->fldrv_priv; + + writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18, + pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG); + writel_relaxed(0x01, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA); +} + +/* + * Disable lpddr2-nvm Overlay Window + * Overlay Window is a memory mapped area containing all LPDDR2-NVM registers + * used by device commands as well as uservisible resources like Device Status + * Register, Device ID, etc + */ +static inline void ow_disable(struct map_info *map) +{ + struct pcm_int_data *pcm_data = map->fldrv_priv; + + writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18, + pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG); + writel_relaxed(0x02, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA); +} + +/* + * Execute lpddr2-nvm operations + */ +static int lpddr2_nvm_do_op(struct map_info *map, u_long cmd_code, + u_long cmd_data, u_long cmd_add, u_long cmd_mpr, u_char *buf) +{ + map_word add_l = { {0} }, add_h = { {0} }, mpr_l = { {0} }, + mpr_h = { {0} }, data_l = { {0} }, cmd = { {0} }, + exec_cmd = { {0} }, sr; + map_word data_h = { {0} }; /* only for 2x x16 devices stacked */ + u_long i, status_reg, prg_buff_ofs; + struct pcm_int_data *pcm_data = map->fldrv_priv; + u_int sr_ok_datamask = build_sr_ok_datamask(pcm_data->bus_width); + + /* Builds low and high words for OW Control Registers */ + add_l.x[0] = cmd_add & 0x0000FFFF; + add_h.x[0] = (cmd_add >> 16) & 0x0000FFFF; + mpr_l.x[0] = cmd_mpr & 0x0000FFFF; + mpr_h.x[0] = (cmd_mpr >> 16) & 0x0000FFFF; + cmd.x[0] = cmd_code & 0x0000FFFF; + exec_cmd.x[0] = 0x0001; + data_l.x[0] = cmd_data & 0x0000FFFF; + data_h.x[0] = (cmd_data >> 16) & 0x0000FFFF; /* only for 2x x16 */ + + /* Set Overlay Window Control Registers */ + map_write(map, cmd, ow_reg_add(map, CMD_CODE_OFS)); + map_write(map, data_l, ow_reg_add(map, CMD_DATA_OFS)); + map_write(map, add_l, ow_reg_add(map, CMD_ADD_L_OFS)); + map_write(map, add_h, ow_reg_add(map, CMD_ADD_H_OFS)); + map_write(map, mpr_l, ow_reg_add(map, MPR_L_OFS)); + map_write(map, mpr_h, ow_reg_add(map, MPR_H_OFS)); + if (pcm_data->bus_width == 0x0004) { /* 2x16 devices stacked */ + map_write(map, cmd, ow_reg_add(map, CMD_CODE_OFS) + 2); + map_write(map, data_h, ow_reg_add(map, CMD_DATA_OFS) + 2); + map_write(map, add_l, ow_reg_add(map, CMD_ADD_L_OFS) + 2); + map_write(map, add_h, ow_reg_add(map, CMD_ADD_H_OFS) + 2); + map_write(map, mpr_l, ow_reg_add(map, MPR_L_OFS) + 2); + map_write(map, mpr_h, ow_reg_add(map, MPR_H_OFS) + 2); + } + + /* Fill Program Buffer */ + if ((cmd_code == LPDDR2_NVM_BUF_PROGRAM) || + (cmd_code == LPDDR2_NVM_BUF_OVERWRITE)) { + prg_buff_ofs = (map_read(map, + ow_reg_add(map, PRG_BUFFER_OFS))).x[0]; + for (i = 0; i < cmd_mpr; i++) { + map_write(map, build_map_word(buf[i]), map->pfow_base + + prg_buff_ofs + i); + } + } + + /* Command Execute */ + map_write(map, exec_cmd, ow_reg_add(map, CMD_EXEC_OFS)); + if (pcm_data->bus_width == 0x0004) /* 2x16 devices stacked */ + map_write(map, exec_cmd, ow_reg_add(map, CMD_EXEC_OFS) + 2); + + /* Status Register Check */ + do { + sr = map_read(map, ow_reg_add(map, STATUS_REG_OFS)); + status_reg = sr.x[0]; + if (pcm_data->bus_width == 0x0004) {/* 2x16 devices stacked */ + sr = map_read(map, ow_reg_add(map, + STATUS_REG_OFS) + 2); + status_reg += sr.x[0] << 16; + } + } while ((status_reg & sr_ok_datamask) != sr_ok_datamask); + + return (((status_reg & sr_ok_datamask) == sr_ok_datamask) ? 0 : -EIO); +} + +/* + * Execute lpddr2-nvm operations @ block level + */ +static int lpddr2_nvm_do_block_op(struct mtd_info *mtd, loff_t start_add, + uint64_t len, u_char block_op) +{ + struct map_info *map = mtd->priv; + u_long add, end_add; + int ret = 0; + + mutex_lock(&lpdd2_nvm_mutex); + + ow_enable(map); + + add = start_add; + end_add = add + len; + + do { + ret = lpddr2_nvm_do_op(map, block_op, 0x00, add, add, NULL); + if (ret) + goto out; + add += mtd->erasesize; + } while (add < end_add); + +out: + ow_disable(map); + mutex_unlock(&lpdd2_nvm_mutex); + return ret; +} + +/* + * verify presence of PFOW string + */ +static int lpddr2_nvm_pfow_present(struct map_info *map) +{ + map_word pfow_val[4]; + unsigned int found = 1; + + mutex_lock(&lpdd2_nvm_mutex); + + ow_enable(map); + + /* Load string from array */ + pfow_val[0] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_P)); + pfow_val[1] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_F)); + pfow_val[2] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_O)); + pfow_val[3] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_W)); + + /* Verify the string loaded vs expected */ + if (!map_word_equal(map, build_map_word('P'), pfow_val[0])) + found = 0; + if (!map_word_equal(map, build_map_word('F'), pfow_val[1])) + found = 0; + if (!map_word_equal(map, build_map_word('O'), pfow_val[2])) + found = 0; + if (!map_word_equal(map, build_map_word('W'), pfow_val[3])) + found = 0; + + ow_disable(map); + + mutex_unlock(&lpdd2_nvm_mutex); + + return found; +} + +/* + * lpddr2_nvm driver read method + */ +static int lpddr2_nvm_read(struct mtd_info *mtd, loff_t start_add, + size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + + mutex_lock(&lpdd2_nvm_mutex); + + *retlen = len; + + map_copy_from(map, buf, start_add, *retlen); + + mutex_unlock(&lpdd2_nvm_mutex); + return 0; +} + +/* + * lpddr2_nvm driver write method + */ +static int lpddr2_nvm_write(struct mtd_info *mtd, loff_t start_add, + size_t len, size_t *retlen, const u_char *buf) +{ + struct map_info *map = mtd->priv; + struct pcm_int_data *pcm_data = map->fldrv_priv; + u_long add, current_len, tot_len, target_len, my_data; + u_char *write_buf = (u_char *)buf; + int ret = 0; + + mutex_lock(&lpdd2_nvm_mutex); + + ow_enable(map); + + /* Set start value for the variables */ + add = start_add; + target_len = len; + tot_len = 0; + + while (tot_len < target_len) { + if (!(IS_ALIGNED(add, mtd->writesize))) { /* do sw program */ + my_data = write_buf[tot_len]; + my_data += (write_buf[tot_len+1]) << 8; + if (pcm_data->bus_width == 0x0004) {/* 2x16 devices */ + my_data += (write_buf[tot_len+2]) << 16; + my_data += (write_buf[tot_len+3]) << 24; + } + ret = lpddr2_nvm_do_op(map, LPDDR2_NVM_SW_OVERWRITE, + my_data, add, 0x00, NULL); + if (ret) + goto out; + + add += pcm_data->bus_width; + tot_len += pcm_data->bus_width; + } else { /* do buffer program */ + current_len = min(target_len - tot_len, + (u_long) mtd->writesize); + ret = lpddr2_nvm_do_op(map, LPDDR2_NVM_BUF_OVERWRITE, + 0x00, add, current_len, write_buf + tot_len); + if (ret) + goto out; + + add += current_len; + tot_len += current_len; + } + } + +out: + *retlen = tot_len; + ow_disable(map); + mutex_unlock(&lpdd2_nvm_mutex); + return ret; +} + +/* + * lpddr2_nvm driver erase method + */ +static int lpddr2_nvm_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + int ret = lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len, + LPDDR2_NVM_ERASE); + if (!ret) { + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + } + + return ret; +} + +/* + * lpddr2_nvm driver unlock method + */ +static int lpddr2_nvm_unlock(struct mtd_info *mtd, loff_t start_add, + uint64_t len) +{ + return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_UNLOCK); +} + +/* + * lpddr2_nvm driver lock method + */ +static int lpddr2_nvm_lock(struct mtd_info *mtd, loff_t start_add, + uint64_t len) +{ + return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_LOCK); +} + +/* + * lpddr2_nvm driver probe method + */ +static int lpddr2_nvm_probe(struct platform_device *pdev) +{ + struct map_info *map; + struct mtd_info *mtd; + struct resource *add_range; + struct resource *control_regs; + struct pcm_int_data *pcm_data; + + /* Allocate memory control_regs data structures */ + pcm_data = devm_kzalloc(&pdev->dev, sizeof(*pcm_data), GFP_KERNEL); + if (!pcm_data) + return -ENOMEM; + + pcm_data->bus_width = BUS_WIDTH; + + /* Allocate memory for map_info & mtd_info data structures */ + map = devm_kzalloc(&pdev->dev, sizeof(*map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + mtd = devm_kzalloc(&pdev->dev, sizeof(*mtd), GFP_KERNEL); + if (!mtd) + return -ENOMEM; + + /* lpddr2_nvm address range */ + add_range = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + /* Populate map_info data structure */ + *map = (struct map_info) { + .virt = devm_ioremap_resource(&pdev->dev, add_range), + .name = pdev->dev.init_name, + .phys = add_range->start, + .size = resource_size(add_range), + .bankwidth = pcm_data->bus_width / 2, + .pfow_base = OW_BASE_ADDRESS, + .fldrv_priv = pcm_data, + }; + if (IS_ERR(map->virt)) + return PTR_ERR(map->virt); + + simple_map_init(map); /* fill with default methods */ + + control_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pcm_data->ctl_regs = devm_ioremap_resource(&pdev->dev, control_regs); + if (IS_ERR(pcm_data->ctl_regs)) + return PTR_ERR(pcm_data->ctl_regs); + + /* Populate mtd_info data structure */ + *mtd = (struct mtd_info) { + .name = pdev->dev.init_name, + .type = MTD_RAM, + .priv = map, + .size = resource_size(add_range), + .erasesize = ERASE_BLOCKSIZE * pcm_data->bus_width, + .writesize = 1, + .writebufsize = WRITE_BUFFSIZE * pcm_data->bus_width, + .flags = (MTD_CAP_NVRAM | MTD_POWERUP_LOCK), + ._read = lpddr2_nvm_read, + ._write = lpddr2_nvm_write, + ._erase = lpddr2_nvm_erase, + ._unlock = lpddr2_nvm_unlock, + ._lock = lpddr2_nvm_lock, + }; + + /* Verify the presence of the device looking for PFOW string */ + if (!lpddr2_nvm_pfow_present(map)) { + pr_err("device not recognized\n"); + return -EINVAL; + } + /* Parse partitions and register the MTD device */ + return mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); +} + +/* + * lpddr2_nvm driver remove method + */ +static int lpddr2_nvm_remove(struct platform_device *pdev) +{ + return mtd_device_unregister(dev_get_drvdata(&pdev->dev)); +} + +/* Initialize platform_driver data structure for lpddr2_nvm */ +static struct platform_driver lpddr2_nvm_drv = { + .driver = { + .name = "lpddr2_nvm", + }, + .probe = lpddr2_nvm_probe, + .remove = lpddr2_nvm_remove, +}; + +module_platform_driver(lpddr2_nvm_drv); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vincenzo Aliberti "); +MODULE_DESCRIPTION("MTD driver for LPDDR2-NVM PCM memories"); diff --git a/include/uapi/mtd/mtd-abi.h b/include/uapi/mtd/mtd-abi.h index e272ea060e38..763bb6950402 100644 --- a/include/uapi/mtd/mtd-abi.h +++ b/include/uapi/mtd/mtd-abi.h @@ -109,6 +109,7 @@ struct mtd_write_req { #define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE) #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) #define MTD_CAP_NANDFLASH (MTD_WRITEABLE) +#define MTD_CAP_NVRAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE) /* Obsolete ECC byte placement modes (used with obsolete MEMGETOOBSEL) */ #define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) -- cgit v1.2.3 From 27c9fd607587e6c3b517590df4cd35ac85f3d0bd Mon Sep 17 00:00:00 2001 From: pekon gupta Date: Mon, 19 May 2014 13:24:39 +0530 Subject: mtd: nand: omap: add support for BCH16_ECC - GPMC driver updates This patch add support for BCH16_ECC in GPMC (controller) driver: - extends configuration space to include BCH16 registers - extends parsing of DT binding for selecting BCH16 ecc-scheme Signed-off-by: Pekon Gupta Acked-by: Tony Lindgren Signed-off-by: Brian Norris --- arch/arm/mach-omap2/gpmc.c | 15 +++++++++++++++ include/linux/platform_data/mtd-nand-omap2.h | 5 +++++ 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index ab43755364f5..9b27773db040 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -68,6 +68,9 @@ #define GPMC_ECC_BCH_RESULT_1 0x244 /* not available on OMAP2 */ #define GPMC_ECC_BCH_RESULT_2 0x248 /* not available on OMAP2 */ #define GPMC_ECC_BCH_RESULT_3 0x24c /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_4 0x300 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_5 0x304 /* not available on OMAP2 */ +#define GPMC_ECC_BCH_RESULT_6 0x308 /* not available on OMAP2 */ /* GPMC ECC control settings */ #define GPMC_ECC_CTRL_ECCCLEAR 0x100 @@ -666,6 +669,12 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) GPMC_BCH_SIZE * i; reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 + GPMC_BCH_SIZE * i; + reg->gpmc_bch_result4[i] = gpmc_base + GPMC_ECC_BCH_RESULT_4 + + i * GPMC_BCH_SIZE; + reg->gpmc_bch_result5[i] = gpmc_base + GPMC_ECC_BCH_RESULT_5 + + i * GPMC_BCH_SIZE; + reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 + + i * GPMC_BCH_SIZE; } } @@ -1401,6 +1410,12 @@ static int gpmc_probe_nand_child(struct platform_device *pdev, else gpmc_nand_data->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; + else if (!strcmp(s, "bch16")) + if (gpmc_nand_data->elm_of_node) + gpmc_nand_data->ecc_opt = + OMAP_ECC_BCH16_CODE_HW; + else + pr_err("%s: BCH16 requires ELM support\n", __func__); else pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__); diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index 3e9dd6676b97..660c029d694f 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -31,6 +31,8 @@ enum omap_ecc { OMAP_ECC_BCH8_CODE_HW_DETECTION_SW, /* 8-bit ECC calculation by GPMC, Error detection by ELM */ OMAP_ECC_BCH8_CODE_HW, + /* 16-bit ECC calculation by GPMC, Error detection by ELM */ + OMAP_ECC_BCH16_CODE_HW, }; struct gpmc_nand_regs { @@ -50,6 +52,9 @@ struct gpmc_nand_regs { void __iomem *gpmc_bch_result1[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result2[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result3[GPMC_BCH_NUM_REMAINDER]; + void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER]; + void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER]; + void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER]; }; struct omap_nand_platform_data { -- cgit v1.2.3 From 2be589e4b28457f148640dc6addf6da24af64b7f Mon Sep 17 00:00:00 2001 From: pekon gupta Date: Mon, 19 May 2014 13:24:40 +0530 Subject: mtd: nand: omap: add support for BCH16_ECC - ELM driver updates ELM hardware engine is used to detect ECC errors for BCHx ecc-schemes (like BCH4/BCH8/BCH16). This patch extends configuration of ELM registers for adding support of BCH16_HW ecc-scheme. Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/devices/elm.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/platform_data/elm.h | 3 ++- 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index 0a037b15c11b..7df86948e6d4 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -213,6 +213,28 @@ static void elm_load_syndrome(struct elm_info *info, val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; elm_write_reg(info, offset, val); break; + case BCH16_ECC: + val = cpu_to_be32(*(u32 *) &ecc[22]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[18]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[14]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[10]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[6]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[2]); + elm_write_reg(info, offset, val); + offset += 4; + val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; + elm_write_reg(info, offset, val); + break; default: pr_err("invalid config bch_type\n"); } @@ -436,6 +458,13 @@ static int elm_context_save(struct elm_info *info) for (i = 0; i < ERROR_VECTOR_MAX; i++) { offset = i * SYNDROME_FRAGMENT_REG_SIZE; switch (bch_type) { + case BCH16_ECC: + regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_6 + offset); + regs->elm_syndrome_fragment_5[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_5 + offset); + regs->elm_syndrome_fragment_4[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_4 + offset); case BCH8_ECC: regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset); @@ -474,6 +503,13 @@ static int elm_context_restore(struct elm_info *info) for (i = 0; i < ERROR_VECTOR_MAX; i++) { offset = i * SYNDROME_FRAGMENT_REG_SIZE; switch (bch_type) { + case BCH16_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, + regs->elm_syndrome_fragment_6[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset, + regs->elm_syndrome_fragment_5[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset, + regs->elm_syndrome_fragment_4[i]); case BCH8_ECC: elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, regs->elm_syndrome_fragment_3[i]); diff --git a/include/linux/platform_data/elm.h b/include/linux/platform_data/elm.h index 4edb40676b3f..780d1e97f620 100644 --- a/include/linux/platform_data/elm.h +++ b/include/linux/platform_data/elm.h @@ -21,6 +21,7 @@ enum bch_ecc { BCH4_ECC = 0, BCH8_ECC, + BCH16_ECC, }; /* ELM support 8 error syndrome process */ @@ -38,7 +39,7 @@ struct elm_errorvec { bool error_reported; bool error_uncorrectable; int error_count; - int error_loc[ERROR_VECTOR_MAX]; + int error_loc[16]; }; void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, -- cgit v1.2.3 From f0ba3d05c9c647ab42ed6a0dbdfdeae42bfbd6de Mon Sep 17 00:00:00 2001 From: Eyal Perry Date: Tue, 20 May 2014 17:57:00 +0300 Subject: genirq: Provide !SMP stub for irq_set_affinity_notifier() Instead of requiring each consumer of the IRQ affinity notifier to have themselves be explicitly dependent on CONFIG_SMP, make the definition of struct irq_affinity_notify to exist independently of that config option and introduce a stub for irq_set_affinity_notifier() under non SMP configuration. Fixes: 2eacc23 ("net/mlx4_core: Enforce irq affinity changes immediatly") Signed-off-by: Eyal Perry Signed-off-by: Amir Vadai Cc: Ben Hutchings Cc: Yevgeny Petrilin Cc: Or Gerlitz Cc: David S. Miller Link: http://lkml.kernel.org/r/1400597820-30685-1-git-send-email-amirv@mellanox.com Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 97ac926c78a7..3f74c0593171 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -199,6 +199,26 @@ extern int check_wakeup_irqs(void); static inline int check_wakeup_irqs(void) { return 0; } #endif +/** + * struct irq_affinity_notify - context for notification of IRQ affinity changes + * @irq: Interrupt to which notification applies + * @kref: Reference count, for internal use + * @work: Work item, for internal use + * @notify: Function to be called on change. This will be + * called in process context. + * @release: Function to be called on release. This will be + * called in process context. Once registered, the + * structure must only be freed when this function is + * called or later. + */ +struct irq_affinity_notify { + unsigned int irq; + struct kref kref; + struct work_struct work; + void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask); + void (*release)(struct kref *ref); +}; + #if defined(CONFIG_SMP) extern cpumask_var_t irq_default_affinity; @@ -242,26 +262,6 @@ extern int irq_select_affinity(unsigned int irq); extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m); -/** - * struct irq_affinity_notify - context for notification of IRQ affinity changes - * @irq: Interrupt to which notification applies - * @kref: Reference count, for internal use - * @work: Work item, for internal use - * @notify: Function to be called on change. This will be - * called in process context. - * @release: Function to be called on release. This will be - * called in process context. Once registered, the - * structure must only be freed when this function is - * called or later. - */ -struct irq_affinity_notify { - unsigned int irq; - struct kref kref; - struct work_struct work; - void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask); - void (*release)(struct kref *ref); -}; - extern int irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify); @@ -284,6 +284,12 @@ static inline int irq_set_affinity_hint(unsigned int irq, { return -EINVAL; } + +static inline int +irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify) +{ + return 0; +} #endif /* CONFIG_SMP */ /* -- cgit v1.2.3 From 7d10d2610cc02d432168ca0c5d964cd9e85c1b06 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Mon, 19 May 2014 09:21:09 +0200 Subject: net: cdc_ncm: fix 64bit division build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upper timer_interval limit is arbitrary and much higher than anything usable in the real world. Reducing it from 15s to ~4s to make the timer_interval fit in an u32 does not make much difference. The limit is still outside the practical bounds. This eliminates the need for a 64bit timer_interval, fixing a build error related to 64bit division: drivers/built-in.o: In function `cdc_ncm_get_coalesce': ak8975.c:(.text+0x1ac994): undefined reference to `__aeabi_uldivmod' Reported-by: Stephen Rothwell Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 4 ++-- include/linux/usb/cdc_ncm.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 2d0caf1eea25..ad2a386a6e92 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -138,7 +138,7 @@ static int cdc_ncm_get_coalesce(struct net_device *netdev, ec->tx_max_coalesced_frames = ctx->tx_max / ctx->max_datagram_size; /* the timer will fire CDC_NCM_TIMER_PENDING_CNT times in a row */ - ec->tx_coalesce_usecs = (ctx->timer_interval * CDC_NCM_TIMER_PENDING_CNT) / NSEC_PER_USEC; + ec->tx_coalesce_usecs = ctx->timer_interval / (NSEC_PER_USEC / CDC_NCM_TIMER_PENDING_CNT); return 0; } @@ -164,7 +164,7 @@ static int cdc_ncm_set_coalesce(struct net_device *netdev, return -EINVAL; spin_lock_bh(&ctx->mtx); - ctx->timer_interval = ec->tx_coalesce_usecs * NSEC_PER_USEC / CDC_NCM_TIMER_PENDING_CNT; + ctx->timer_interval = ec->tx_coalesce_usecs * (NSEC_PER_USEC / CDC_NCM_TIMER_PENDING_CNT); if (!ctx->timer_interval) ctx->tx_timer_pending = 0; spin_unlock_bh(&ctx->mtx); diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 8c5e38819828..7c9b484735c5 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -78,7 +78,7 @@ #define CDC_NCM_TIMER_PENDING_CNT 2 #define CDC_NCM_TIMER_INTERVAL_USEC 400UL #define CDC_NCM_TIMER_INTERVAL_MIN 5UL -#define CDC_NCM_TIMER_INTERVAL_MAX (15UL * USEC_PER_SEC) +#define CDC_NCM_TIMER_INTERVAL_MAX (U32_MAX / NSEC_PER_USEC) #define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \ (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE) @@ -104,7 +104,7 @@ struct cdc_ncm_ctx { spinlock_t mtx; atomic_t stop; - u64 timer_interval; + u32 timer_interval; u32 max_ndp_size; u32 tx_timer_pending; -- cgit v1.2.3 From a8246fedacadaab18b23b280ea3cf916ef5fc30e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 8 May 2014 16:56:12 +0200 Subject: dmaengine: omap: hide filter_fn for built-in drivers It is not possible to reference the omap_dma_filter_fn filter function from a built-in driver if the dmaengine driver itself is a loadable module, which is a valid configuration otherwise. This provides only the dummy alternative if the function is referenced by a built-in driver to allow a successful build. The filter function is only required by ATAGS based platforms, which will continue to be broken after this change for the bogus configuration. When booting from DT, with the dma channels correctly listed there, it will work fine. Signed-off-by: Arnd Bergmann Acked-by: Tony Lindgren Cc: Russell King Cc: Vinod Koul Cc: dmaengine@vger.kernel.org Signed-off-by: Vinod Koul --- include/linux/omap-dma.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h index 41a13e70f41f..7944cdc27bed 100644 --- a/include/linux/omap-dma.h +++ b/include/linux/omap-dma.h @@ -10,7 +10,7 @@ struct dma_chan; -#if defined(CONFIG_DMA_OMAP) || defined(CONFIG_DMA_OMAP_MODULE) +#if defined(CONFIG_DMA_OMAP) || (defined(CONFIG_DMA_OMAP_MODULE) && defined(MODULE)) bool omap_dma_filter_fn(struct dma_chan *, void *); #else static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d) -- cgit v1.2.3 From 3796ce1d4d4b330a75005c5eda105603ce9d4071 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 19 May 2014 22:42:32 +0200 Subject: pwm: add period and polarity to struct pwm_lookup Add period and polarity members to struct pwm_lookup so that platforms using the lookup table can be treated the same way as those using the device tree. Signed-off-by: Alexandre Belloni Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 8 +++++++- include/linux/pwm.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a80471399c20..4b66bf09ee55 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -661,10 +661,16 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) } } + mutex_unlock(&pwm_lookup_lock); + if (chip) pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id); + if (IS_ERR(pwm)) + return pwm; + + pwm_set_period(pwm, p->period); + pwm_set_polarity(pwm, p->polarity); - mutex_unlock(&pwm_lookup_lock); return pwm; } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 4717f54051cb..2f45e2fe5b93 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -274,6 +274,8 @@ struct pwm_lookup { unsigned int index; const char *dev_id; const char *con_id; + unsigned int period; + enum pwm_polarity polarity; }; #define PWM_LOOKUP(_provider, _index, _dev_id, _con_id) \ -- cgit v1.2.3 From eed542d6962ba33a689b4007a389f466e407bd74 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 20 May 2014 20:31:04 +0900 Subject: ftrace: Make CALLER_ADDRx macros more generic Most archs with HAVE_ARCH_CALLER_ADDR have pretty much the same definitions of CALLER_ADDRx(n). Instead of duplicating the code for all the archs, define a ftrace_return_address0() and ftrace_return_address(n) that can be overwritten by the archs if they need to do something different. Instead of 7 macros in every arch, we now only have at most 2 (and actually only 1 as ftrace_return_address0() should be the same for all archs). The CALLER_ADDRx(n) will now be defined in linux/ftrace.h and use the ftrace_return_address*(n?) macros. This removes a lot of the duplicate code. Link: http://lkml.kernel.org/p/1400585464-30333-1-git-send-email-takahiro.akashi@linaro.org Signed-off-by: AKASHI Takahiro Signed-off-by: Steven Rostedt --- arch/arm/include/asm/ftrace.h | 10 +--------- arch/blackfin/include/asm/ftrace.h | 11 +---------- arch/parisc/include/asm/ftrace.h | 10 +--------- arch/sh/include/asm/ftrace.h | 10 +--------- arch/xtensa/include/asm/ftrace.h | 14 ++++---------- include/linux/ftrace.h | 34 ++++++++++++++++++---------------- 6 files changed, 26 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index f89515adac60..eb577f4f5f70 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,15 +52,7 @@ extern inline void *return_address(unsigned int level) #endif -#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_addr(n) return_address(n) #endif /* ifndef __ASSEMBLY__ */ diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h index 8a029505d7b7..2f1c3c2657ad 100644 --- a/arch/blackfin/include/asm/ftrace.h +++ b/arch/blackfin/include/asm/ftrace.h @@ -66,16 +66,7 @@ extern inline void *return_address(unsigned int level) #endif /* CONFIG_FRAME_POINTER */ -#define HAVE_ARCH_CALLER_ADDR - -/* inline function or macro may lead to unexpected result */ -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h index 72c0fafaa039..544ed8ef87eb 100644 --- a/arch/parisc/include/asm/ftrace.h +++ b/arch/parisc/include/asm/ftrace.h @@ -24,15 +24,7 @@ extern void return_to_handler(void); extern unsigned long return_address(unsigned int); -#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#define CALLER_ADDR4 return_address(4) -#define CALLER_ADDR5 return_address(5) -#define CALLER_ADDR6 return_address(6) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h index 13e9966464c2..e79fb6ebaa42 100644 --- a/arch/sh/include/asm/ftrace.h +++ b/arch/sh/include/asm/ftrace.h @@ -40,15 +40,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) /* arch/sh/kernel/return_address.c */ extern void *return_address(unsigned int); -#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 736b9d214d80..6c6d9a9f185f 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -12,24 +12,18 @@ #include -#define HAVE_ARCH_CALLER_ADDR #ifndef __ASSEMBLY__ -#define CALLER_ADDR0 ({ unsigned long a0, a1; \ +#define ftrace_return_address0 ({ unsigned long a0, a1; \ __asm__ __volatile__ ( \ "mov %0, a0\n" \ "mov %1, a1\n" \ : "=r"(a0), "=r"(a1)); \ MAKE_PC_FROM_RA(a0, a1); }) + #ifdef CONFIG_FRAME_POINTER extern unsigned long return_address(unsigned level); -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#else /* CONFIG_FRAME_POINTER */ -#define CALLER_ADDR1 (0) -#define CALLER_ADDR2 (0) -#define CALLER_ADDR3 (0) -#endif /* CONFIG_FRAME_POINTER */ +#define ftrace_return_address(n) return_address(n) +#endif #endif /* __ASSEMBLY__ */ #ifdef CONFIG_FUNCTION_TRACER diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index ae9504b4b67d..2018751cad9e 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -616,25 +616,27 @@ static inline void __ftrace_enabled_restore(int enabled) #endif } -#ifndef HAVE_ARCH_CALLER_ADDR +/* All archs should have this, but we define it for consistency */ +#ifndef ftrace_return_address0 +# define ftrace_return_address0 __builtin_return_address(0) +#endif + +/* Archs may use other ways for ADDR1 and beyond */ +#ifndef ftrace_return_address # ifdef CONFIG_FRAME_POINTER -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) -# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) -# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) -# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) -# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) -# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6)) +# define ftrace_return_address(n) __builtin_return_address(n) # else -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 0UL -# define CALLER_ADDR2 0UL -# define CALLER_ADDR3 0UL -# define CALLER_ADDR4 0UL -# define CALLER_ADDR5 0UL -# define CALLER_ADDR6 0UL +# define ftrace_return_address(n) 0UL # endif -#endif /* ifndef HAVE_ARCH_CALLER_ADDR */ +#endif + +#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) +#define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) +#define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2)) +#define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3)) +#define CALLER_ADDR4 ((unsigned long)ftrace_return_address(4)) +#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5)) +#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6)) #ifdef CONFIG_IRQSOFF_TRACER extern void time_hardirqs_on(unsigned long a0, unsigned long a1); -- cgit v1.2.3 From cca674d47e59665630f3005291b61bb883015fc5 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 19 May 2014 21:53:20 +0200 Subject: mac80211: export the expected throughput Add get_expected_throughput() API to mac80211 so that each driver can implement its own version based on the RC algorithm they are using (might be using an HW RC algo). The API returns a value expressed in Kbps. Also, add the new get_expected_throughput() member to the rate_control_ops structure in order to be able to query the RC algorithm (this patch provides an implementation of this API for both minstrel and minstrel_ht). The related member in the station_info object is now filled accordingly when dumping a station. Cc: Felix Fietkau Signed-off-by: Antonio Quartulli Signed-off-by: Johannes Berg --- include/net/mac80211.h | 7 +++++++ net/mac80211/cfg.c | 13 +++++++++++++ net/mac80211/driver-ops.h | 13 +++++++++++++ net/mac80211/rc80211_minstrel.c | 12 ++++++++++++ net/mac80211/rc80211_minstrel_ht.c | 17 +++++++++++++++++ net/mac80211/trace.h | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 94 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a34f26a4ed18..2c78997bc48d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2769,6 +2769,10 @@ enum ieee80211_roc_type { * information in bss_conf is set up and the beacon can be retrieved. A * channel context is bound before this is called. * @leave_ibss: Leave the IBSS again. + * + * @get_expected_throughput: extract the expected throughput towards the + * specified station. The returned value is expressed in Kbps. It returns 0 + * if the RC algorithm does not have proper data to provide. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2962,6 +2966,7 @@ struct ieee80211_ops { int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + u32 (*get_expected_throughput)(struct ieee80211_sta *sta); }; /** @@ -4535,6 +4540,8 @@ struct rate_control_ops { void (*add_sta_debugfs)(void *priv, void *priv_sta, struct dentry *dir); void (*remove_sta_debugfs)(void *priv, void *priv_sta); + + u32 (*get_expected_throughput)(void *priv_sta); }; static inline int rate_supported(struct ieee80211_sta *sta, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ac45304590d8..d7513a503be1 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -472,8 +472,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; + struct rate_control_ref *ref = local->rate_ctrl; struct timespec uptime; u64 packets = 0; + u32 thr = 0; int i, ac; sinfo->generation = sdata->local->sta_generation; @@ -587,6 +589,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); + + /* check if the driver has a SW RC implementation */ + if (ref && ref->ops->get_expected_throughput) + thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); + else + thr = drv_get_expected_throughput(local, &sta->sta); + + if (thr != 0) { + sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; + sinfo->expected_throughput = thr; + } } static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index df1d50291344..696ef78b1fb7 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1156,4 +1156,17 @@ static inline void drv_leave_ibss(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline u32 drv_get_expected_throughput(struct ieee80211_local *local, + struct ieee80211_sta *sta) +{ + u32 ret = 0; + + trace_drv_get_expected_throughput(sta); + if (local->ops->get_expected_throughput) + ret = local->ops->get_expected_throughput(sta); + trace_drv_return_u32(local, ret); + + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 26fd94fa0aed..1c1469c36dca 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -657,6 +657,17 @@ minstrel_free(void *priv) kfree(priv); } +static u32 minstrel_get_expected_throughput(void *priv_sta) +{ + struct minstrel_sta_info *mi = priv_sta; + int idx = mi->max_tp_rate[0]; + + /* convert pkt per sec in kbps (1200 is the average pkt size used for + * computing cur_tp + */ + return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024; +} + const struct rate_control_ops mac80211_minstrel = { .name = "minstrel", .tx_status = minstrel_tx_status, @@ -670,6 +681,7 @@ const struct rate_control_ops mac80211_minstrel = { .add_sta_debugfs = minstrel_add_sta_debugfs, .remove_sta_debugfs = minstrel_remove_sta_debugfs, #endif + .get_expected_throughput = minstrel_get_expected_throughput, }; int __init diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 4e916ad51a20..85c1e74b7714 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1032,6 +1032,22 @@ minstrel_ht_free(void *priv) mac80211_minstrel.free(priv); } +static u32 minstrel_ht_get_expected_throughput(void *priv_sta) +{ + struct minstrel_ht_sta_priv *msp = priv_sta; + struct minstrel_ht_sta *mi = &msp->ht; + int i, j; + + if (!msp->is_ht) + return mac80211_minstrel.get_expected_throughput(priv_sta); + + i = mi->max_tp_rate / MCS_GROUP_RATES; + j = mi->max_tp_rate % MCS_GROUP_RATES; + + /* convert cur_tp from pkt per second in kbps */ + return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024; +} + static const struct rate_control_ops mac80211_minstrel_ht = { .name = "minstrel_ht", .tx_status = minstrel_ht_tx_status, @@ -1046,6 +1062,7 @@ static const struct rate_control_ops mac80211_minstrel_ht = { .add_sta_debugfs = minstrel_ht_add_sta_debugfs, .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs, #endif + .get_expected_throughput = minstrel_ht_get_expected_throughput, }; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index a0b0aea76525..942f64b8ce0e 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -184,6 +184,20 @@ TRACE_EVENT(drv_return_bool, "true" : "false") ); +TRACE_EVENT(drv_return_u32, + TP_PROTO(struct ieee80211_local *local, u32 ret), + TP_ARGS(local, ret), + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u32, ret) + ), + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + ), + TP_printk(LOCAL_PR_FMT " - %u", LOCAL_PR_ARG, __entry->ret) +); + TRACE_EVENT(drv_return_u64, TP_PROTO(struct ieee80211_local *local, u64 ret), TP_ARGS(local, ret), @@ -1499,6 +1513,24 @@ DEFINE_EVENT(local_sdata_evt, drv_leave_ibss, TP_ARGS(local, sdata) ); +TRACE_EVENT(drv_get_expected_throughput, + TP_PROTO(struct ieee80211_sta *sta), + + TP_ARGS(sta), + + TP_STRUCT__entry( + STA_ENTRY + ), + + TP_fast_assign( + STA_ASSIGN; + ), + + TP_printk( + STA_PR_FMT, STA_PR_ARG + ) +); + /* * Tracing for API calls that drivers call. */ -- cgit v1.2.3 From 7406353d43c8e2faf478721e87aeb6f2f9685de0 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 19 May 2014 21:53:21 +0200 Subject: cfg80211: implement cfg80211_get_station cfg80211 API Implement and export the new cfg80211_get_station() API. This utility can be used by other kernel modules to obtain detailed information about a given wireless station. It will be in particular useful to batman-adv which will implement a wireless rate based metric. Signed-off-by: Antonio Quartulli Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 13 +++++++++++++ net/wireless/rdev-ops.h | 2 +- net/wireless/util.c | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 857d6476a128..a75fabd18502 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1074,6 +1074,19 @@ struct station_info { */ }; +/** + * cfg80211_get_station - retrieve information about a given station + * @dev: the device where the station is supposed to be connected to + * @mac_addr: the mac address of the station of interest + * @sinfo: pointer to the structure to fill with the information + * + * Returns 0 on success and sinfo is filled with the available information + * otherwise returns a negative error code and the content of sinfo has to be + * considered undefined. + */ +int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo); + /** * enum monitor_flags - monitor flags * diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 00cdf73ba6c4..d95bbe348138 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -199,7 +199,7 @@ static inline int rdev_change_station(struct cfg80211_registered_device *rdev, } static inline int rdev_get_station(struct cfg80211_registered_device *rdev, - struct net_device *dev, u8 *mac, + struct net_device *dev, const u8 *mac, struct station_info *sinfo) { int ret; diff --git a/net/wireless/util.c b/net/wireless/util.c index fa61ac9c9b26..728f1c0dc70d 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1546,6 +1546,24 @@ unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy) } EXPORT_SYMBOL(ieee80211_get_num_supported_channels); +int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, + struct station_info *sinfo) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + + wdev = dev->ieee80211_ptr; + if (!wdev) + return -EOPNOTSUPP; + + rdev = wiphy_to_rdev(wdev->wiphy); + if (!rdev->ops->get_station) + return -EOPNOTSUPP; + + return rdev_get_station(rdev, dev, mac_addr, sinfo); +} +EXPORT_SYMBOL(cfg80211_get_station); + /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ const unsigned char rfc1042_header[] __aligned(2) = -- cgit v1.2.3 From f01d907582f8461546379aa415a7c6d5cfb8e5fd Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sat, 17 May 2014 14:54:50 +0900 Subject: gpio: make of_get_named_gpiod_flags() private of_get_named_gpiod_flags() is visible and directly usable by GPIO consumers, but it really should not as the gpiod interface relies on the simpler gpiod_get() to provide properly-configured GPIOs. of_get_named_gpiod_flags() is just used internally by gpiolib to implement gpiod_get(), and by the old of_get_named_gpio_flags() function, therefore it makes sense to make it gpiolib-private. As a side-effect, the unused (and unneeded) of_get_gpiod_flags() inline function is also removed, and of_get_named_gpio_flags() is moved from a static inline function to a regular one in gpiolib-of.c This results in all references to gpiod_* functions in of_gpio.h being gone, which is the way it should be since this file is part of the old integer GPIO interface. Changes since v1: - Fixed compilation error when CONFIG_OF_GPIO is not defined - Fixed warning due to of_gpio_flags enum not being declared in private gpiolib.h header Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 14 ++++++++++++++ drivers/gpio/gpiolib.h | 5 +++++ include/linux/of_gpio.h | 35 +++-------------------------------- 3 files changed, 22 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index db98d3a12f70..af7e25c9a9ae 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -96,6 +96,20 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, } EXPORT_SYMBOL(of_get_named_gpiod_flags); +int of_get_named_gpio_flags(struct device_node *np, const char *list_name, + int index, enum of_gpio_flags *flags) +{ + struct gpio_desc *desc; + + desc = of_get_named_gpiod_flags(np, list_name, index, flags); + + if (IS_ERR(desc)) + return PTR_ERR(desc); + else + return desc_to_gpio(desc); +} +EXPORT_SYMBOL(of_get_named_gpio_flags); + /** * of_gpio_simple_xlate - translate gpio_spec to the GPIO number and flags * @gc: pointer to the gpio_chip structure diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index cf092941a9fd..1a4103dd38df 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -15,6 +15,8 @@ #include #include +enum of_gpio_flags; + /** * struct acpi_gpio_info - ACPI GPIO specific information * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo @@ -46,4 +48,7 @@ acpi_get_gpiod_by_index(struct device *dev, int index, int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label); void gpiochip_free_own_desc(struct gpio_desc *desc); +struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, + const char *list_name, int index, enum of_gpio_flags *flags); + #endif /* GPIOLIB_H */ diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index f14123a5a9df..38fc05036015 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -19,7 +19,6 @@ #include #include #include -#include struct device_node; @@ -48,7 +47,7 @@ static inline struct of_mm_gpio_chip *to_of_mm_gpio_chip(struct gpio_chip *gc) return container_of(gc, struct of_mm_gpio_chip, gc); } -extern struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, +extern int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags); extern int of_mm_gpiochip_add(struct device_node *np, @@ -63,10 +62,10 @@ extern int of_gpio_simple_xlate(struct gpio_chip *gc, #else /* CONFIG_OF_GPIO */ /* Drivers may not strictly depend on the GPIO support, so let them link. */ -static inline struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, +static inline int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags) { - return ERR_PTR(-ENOSYS); + return -ENOSYS; } static inline int of_gpio_simple_xlate(struct gpio_chip *gc, @@ -81,18 +80,6 @@ static inline void of_gpiochip_remove(struct gpio_chip *gc) { } #endif /* CONFIG_OF_GPIO */ -static inline int of_get_named_gpio_flags(struct device_node *np, - const char *list_name, int index, enum of_gpio_flags *flags) -{ - struct gpio_desc *desc; - desc = of_get_named_gpiod_flags(np, list_name, index, flags); - - if (IS_ERR(desc)) - return PTR_ERR(desc); - else - return desc_to_gpio(desc); -} - /** * of_gpio_named_count() - Count GPIOs for a device * @np: device node to count GPIOs for @@ -129,22 +116,6 @@ static inline int of_gpio_count(struct device_node *np) return of_gpio_named_count(np, "gpios"); } -/** - * of_get_gpiod_flags() - Get a GPIO descriptor and flags to use with GPIO API - * @np: device node to get GPIO from - * @index: index of the GPIO - * @flags: a flags pointer to fill in - * - * Returns GPIO descriptor to use with Linux generic GPIO API, or a errno - * value on the error condition. If @flags is not NULL the function also fills - * in flags for the GPIO. - */ -static inline struct gpio_desc *of_get_gpiod_flags(struct device_node *np, - int index, enum of_gpio_flags *flags) -{ - return of_get_named_gpiod_flags(np, "gpios", index, flags); -} - static inline int of_get_gpio_flags(struct device_node *np, int index, enum of_gpio_flags *flags) { -- cgit v1.2.3 From dc671157139918eaf61f73db1bd6dd02960b66e2 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 19 May 2014 22:42:34 +0200 Subject: pwm: renesas-tpu: remove unused struct tpu_pwm_platform_data The struct is not used anymore and the polarity initialization will be done using the PWM lookup table (or device tree). Signed-off-by: Alexandre Belloni Acked-by: Laurent Pinchart Acked-by: Simon Horman Signed-off-by: Thierry Reding --- drivers/pwm/pwm-renesas-tpu.c | 19 +++---------------- include/linux/platform_data/pwm-renesas-tpu.h | 16 ---------------- 2 files changed, 3 insertions(+), 32 deletions(-) delete mode 100644 include/linux/platform_data/pwm-renesas-tpu.h (limited to 'include') diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index cc13ff4cfab0..3b71b42e89d5 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -21,13 +21,14 @@ #include #include #include -#include #include #include #include #include #include +#define TPU_CHANNEL_MAX 4 + #define TPU_TSTR 0x00 /* Timer start register (shared) */ #define TPU_TCRn 0x00 /* Timer control register */ @@ -87,7 +88,6 @@ struct tpu_pwm_device { struct tpu_device { struct platform_device *pdev; - enum pwm_polarity polarities[TPU_CHANNEL_MAX]; struct pwm_chip chip; spinlock_t lock; @@ -229,7 +229,7 @@ static int tpu_pwm_request(struct pwm_chip *chip, struct pwm_device *_pwm) pwm->tpu = tpu; pwm->channel = _pwm->hwpwm; - pwm->polarity = tpu->polarities[pwm->channel]; + pwm->polarity = PWM_POLARITY_NORMAL; pwm->prescaler = 0; pwm->period = 0; pwm->duty = 0; @@ -388,16 +388,6 @@ static const struct pwm_ops tpu_pwm_ops = { * Probe and remove */ -static void tpu_parse_pdata(struct tpu_device *tpu) -{ - struct tpu_pwm_platform_data *pdata = tpu->pdev->dev.platform_data; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(tpu->polarities); ++i) - tpu->polarities[i] = pdata ? pdata->channels[i].polarity - : PWM_POLARITY_NORMAL; -} - static int tpu_probe(struct platform_device *pdev) { struct tpu_device *tpu; @@ -411,9 +401,6 @@ static int tpu_probe(struct platform_device *pdev) spin_lock_init(&tpu->lock); tpu->pdev = pdev; - /* Initialize device configuration from platform data. */ - tpu_parse_pdata(tpu); - /* Map memory, get clock and pin control. */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); tpu->base = devm_ioremap_resource(&pdev->dev, res); diff --git a/include/linux/platform_data/pwm-renesas-tpu.h b/include/linux/platform_data/pwm-renesas-tpu.h deleted file mode 100644 index a7220b10ddab..000000000000 --- a/include/linux/platform_data/pwm-renesas-tpu.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __PWM_RENESAS_TPU_H__ -#define __PWM_RENESAS_TPU_H__ - -#include - -#define TPU_CHANNEL_MAX 4 - -struct tpu_pwm_channel_data { - enum pwm_polarity polarity; -}; - -struct tpu_pwm_platform_data { - struct tpu_pwm_channel_data channels[TPU_CHANNEL_MAX]; -}; - -#endif /* __PWM_RENESAS_TPU_H__ */ -- cgit v1.2.3 From 4284402924cc55e182008ca7e9d4fb1e891ff5ae Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 19 May 2014 22:42:37 +0200 Subject: pwm: modify PWM_LOOKUP to initialize all struct pwm_lookup members Now that PWM_LOOKUP is not used anymore, modify it to initialize all the members of struct pwm_lookup. Signed-off-by: Alexandre Belloni Signed-off-by: Thierry Reding --- Documentation/pwm.txt | 3 ++- include/linux/pwm.h | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt index 0527f615b115..ca895fd211e4 100644 --- a/Documentation/pwm.txt +++ b/Documentation/pwm.txt @@ -19,7 +19,8 @@ should instead register a static mapping that can be used to match PWM consumers to providers, as given in the following example: static struct pwm_lookup board_pwm_lookup[] = { - PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL), + PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL, + 50000, PWM_POLARITY_NORMAL), }; static void __init board_init(void) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 2f45e2fe5b93..e90628cac8fa 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -278,12 +278,14 @@ struct pwm_lookup { enum pwm_polarity polarity; }; -#define PWM_LOOKUP(_provider, _index, _dev_id, _con_id) \ +#define PWM_LOOKUP(_provider, _index, _dev_id, _con_id, _period, _polarity) \ { \ .provider = _provider, \ .index = _index, \ .dev_id = _dev_id, \ .con_id = _con_id, \ + .period = _period, \ + .polarity = _polarity \ } #if IS_ENABLED(CONFIG_PWM) -- cgit v1.2.3 From 89dd6a4b34c5a7658a4017c93b2fe66c964399d0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 May 2014 15:32:12 +0200 Subject: drm/irq: Add kms-native crtc interface functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to start somewhere ... With this the only places left in i915 where we use pipe integers is in the interrupt handling code. And there it actually makes some amount of sense. v2: - Polish kerneldoc a bit (Thierry). - Drop "dev" parameter since it's unecessary. - Split out i915 changes (Thierry). Cc: Thierry Reding Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_irq.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++ include/drm/drmP.h | 5 +++ 2 files changed, 82 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index f3dafccca456..5ba9a614e7ad 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -916,6 +916,8 @@ static int drm_vblank_enable(struct drm_device *dev, int crtc) * Acquire a reference count on vblank events to avoid having them disabled * while in use. * + * This is the legacy version of drm_crtc_vblank_get(). + * * Returns: * Zero on success, nonzero on failure. */ @@ -940,6 +942,24 @@ int drm_vblank_get(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_get); +/** + * drm_crtc_vblank_get - get a reference count on vblank events + * @crtc: which CRTC to own + * + * Acquire a reference count on vblank events to avoid having them disabled + * while in use. + * + * This is the native kms version of drm_vblank_off(). + * + * Returns: + * Zero on success, nonzero on failure. + */ +int drm_crtc_vblank_get(struct drm_crtc *crtc) +{ + return drm_vblank_get(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_get); + /** * drm_vblank_put - give up ownership of vblank events * @dev: DRM device @@ -947,6 +967,8 @@ EXPORT_SYMBOL(drm_vblank_get); * * Release ownership of a given vblank counter, turning off interrupts * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. + * + * This is the legacy version of drm_crtc_vblank_put(). */ void drm_vblank_put(struct drm_device *dev, int crtc) { @@ -960,6 +982,21 @@ void drm_vblank_put(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_put); +/** + * drm_crtc_vblank_put - give up ownership of vblank events + * @crtc: which counter to give up + * + * Release ownership of a given vblank counter, turning off interrupts + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. + * + * This is the native kms version of drm_vblank_put(). + */ +void drm_crtc_vblank_put(struct drm_crtc *crtc) +{ + drm_vblank_put(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_put); + /** * drm_vblank_off - disable vblank events on a CRTC * @dev: DRM device @@ -971,6 +1008,8 @@ EXPORT_SYMBOL(drm_vblank_put); * * Drivers must use this function when the hardware vblank counter can get * reset, e.g. when suspending. + * + * This is the legacy version of drm_crtc_vblank_off(). */ void drm_vblank_off(struct drm_device *dev, int crtc) { @@ -1003,6 +1042,25 @@ void drm_vblank_off(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_off); +/** + * drm_crtc_vblank_off - disable vblank events on a CRTC + * @crtc: CRTC in question + * + * Drivers can use this function to shut down the vblank interrupt handling when + * disabling a crtc. This function ensures that the latest vblank frame count is + * stored so that drm_vblank_on can restore it again. + * + * Drivers must use this function when the hardware vblank counter can get + * reset, e.g. when suspending. + * + * This is the native kms version of drm_vblank_off(). + */ +void drm_crtc_vblank_off(struct drm_crtc *crtc) +{ + drm_vblank_off(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_off); + /** * drm_vblank_on - enable vblank events on a CRTC * @dev: DRM device @@ -1012,6 +1070,8 @@ EXPORT_SYMBOL(drm_vblank_off); * drm_vblank_off() again. Note that calls to drm_vblank_on() and * drm_vblank_off() can be unbalanced and so can also be unconditionaly called * in driver load code to reflect the current hardware state of the crtc. + * + * This is the legacy version of drm_crtc_vblank_on(). */ void drm_vblank_on(struct drm_device *dev, int crtc) { @@ -1025,6 +1085,23 @@ void drm_vblank_on(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_on); +/** + * drm_crtc_vblank_on - enable vblank events on a CRTC + * @crtc: CRTC in question + * + * This functions restores the vblank interrupt state captured with + * drm_vblank_off() again. Note that calls to drm_vblank_on() and + * drm_vblank_off() can be unbalanced and so can also be unconditionaly called + * in driver load code to reflect the current hardware state of the crtc. + * + * This is the native kms version of drm_vblank_on(). + */ +void drm_crtc_vblank_on(struct drm_crtc *crtc) +{ + drm_vblank_on(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_on); + /** * drm_vblank_pre_modeset - account for vblanks across mode sets * @dev: DRM device diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 7339b2b00724..76ccaabd0418 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1359,9 +1359,14 @@ extern void drm_send_vblank_event(struct drm_device *dev, int crtc, extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); +extern int drm_crtc_vblank_get(struct drm_crtc *crtc); +extern void drm_crtc_vblank_put(struct drm_crtc *crtc); extern void drm_vblank_off(struct drm_device *dev, int crtc); extern void drm_vblank_on(struct drm_device *dev, int crtc); +extern void drm_crtc_vblank_off(struct drm_crtc *crtc); +extern void drm_crtc_vblank_on(struct drm_crtc *crtc); extern void drm_vblank_cleanup(struct drm_device *dev); + extern u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, struct timeval *tvblank, unsigned flags); extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, -- cgit v1.2.3 From 9e1e726311830bc5b8b568d5178f6a52c357fb6e Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 23 Apr 2014 19:21:31 -0400 Subject: mfd: bcm590xx: Add support for secondary I2C slave address BCM590xx utilizes a secondary I2C slave address to access additional register space. Add support for the secondary address space by instantiating a dummy I2C device with the appropriate secondary I2C slave address. Also expose a secondary regmap register space so that MFD drivers can access this secondary i2c slave address space. Signed-off-by: Matt Porter Signed-off-by: Lee Jones --- drivers/mfd/bcm590xx.c | 60 +++++++++++++++++++++++++++++++++----------- include/linux/mfd/bcm590xx.h | 9 ++++--- 2 files changed, 52 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c index e9a33c79431b..43cba1a1973c 100644 --- a/drivers/mfd/bcm590xx.c +++ b/drivers/mfd/bcm590xx.c @@ -28,39 +28,71 @@ static const struct mfd_cell bcm590xx_devs[] = { }, }; -static const struct regmap_config bcm590xx_regmap_config = { +static const struct regmap_config bcm590xx_regmap_config_pri = { .reg_bits = 8, .val_bits = 8, - .max_register = BCM590XX_MAX_REGISTER, + .max_register = BCM590XX_MAX_REGISTER_PRI, .cache_type = REGCACHE_RBTREE, }; -static int bcm590xx_i2c_probe(struct i2c_client *i2c, +static const struct regmap_config bcm590xx_regmap_config_sec = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BCM590XX_MAX_REGISTER_SEC, + .cache_type = REGCACHE_RBTREE, +}; + +static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri, const struct i2c_device_id *id) { struct bcm590xx *bcm590xx; int ret; - bcm590xx = devm_kzalloc(&i2c->dev, sizeof(*bcm590xx), GFP_KERNEL); + bcm590xx = devm_kzalloc(&i2c_pri->dev, sizeof(*bcm590xx), GFP_KERNEL); if (!bcm590xx) return -ENOMEM; - i2c_set_clientdata(i2c, bcm590xx); - bcm590xx->dev = &i2c->dev; - bcm590xx->i2c_client = i2c; + i2c_set_clientdata(i2c_pri, bcm590xx); + bcm590xx->dev = &i2c_pri->dev; + bcm590xx->i2c_pri = i2c_pri; - bcm590xx->regmap = devm_regmap_init_i2c(i2c, &bcm590xx_regmap_config); - if (IS_ERR(bcm590xx->regmap)) { - ret = PTR_ERR(bcm590xx->regmap); - dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); + bcm590xx->regmap_pri = devm_regmap_init_i2c(i2c_pri, + &bcm590xx_regmap_config_pri); + if (IS_ERR(bcm590xx->regmap_pri)) { + ret = PTR_ERR(bcm590xx->regmap_pri); + dev_err(&i2c_pri->dev, "primary regmap init failed: %d\n", ret); return ret; } - ret = mfd_add_devices(&i2c->dev, -1, bcm590xx_devs, + /* Secondary I2C slave address is the base address with A(2) asserted */ + bcm590xx->i2c_sec = i2c_new_dummy(i2c_pri->adapter, + i2c_pri->addr | BIT(2)); + if (IS_ERR_OR_NULL(bcm590xx->i2c_sec)) { + dev_err(&i2c_pri->dev, "failed to add secondary I2C device\n"); + return -ENODEV; + } + i2c_set_clientdata(bcm590xx->i2c_sec, bcm590xx); + + bcm590xx->regmap_sec = devm_regmap_init_i2c(bcm590xx->i2c_sec, + &bcm590xx_regmap_config_sec); + if (IS_ERR(bcm590xx->regmap_sec)) { + ret = PTR_ERR(bcm590xx->regmap_sec); + dev_err(&bcm590xx->i2c_sec->dev, + "secondary regmap init failed: %d\n", ret); + goto err; + } + + ret = mfd_add_devices(&i2c_pri->dev, -1, bcm590xx_devs, ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL); - if (ret < 0) - dev_err(&i2c->dev, "failed to add sub-devices: %d\n", ret); + if (ret < 0) { + dev_err(&i2c_pri->dev, "failed to add sub-devices: %d\n", ret); + goto err; + } + + return 0; +err: + i2c_unregister_device(bcm590xx->i2c_sec); return ret; } diff --git a/include/linux/mfd/bcm590xx.h b/include/linux/mfd/bcm590xx.h index 434df2d4e587..267aedee1c7a 100644 --- a/include/linux/mfd/bcm590xx.h +++ b/include/linux/mfd/bcm590xx.h @@ -19,12 +19,15 @@ #include /* max register address */ -#define BCM590XX_MAX_REGISTER 0xe7 +#define BCM590XX_MAX_REGISTER_PRI 0xe7 +#define BCM590XX_MAX_REGISTER_SEC 0xf0 struct bcm590xx { struct device *dev; - struct i2c_client *i2c_client; - struct regmap *regmap; + struct i2c_client *i2c_pri; + struct i2c_client *i2c_sec; + struct regmap *regmap_pri; + struct regmap *regmap_sec; unsigned int id; }; -- cgit v1.2.3 From ce73d9be6aa60b09ce6c54cf03e2b7e4d1c45dfa Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Fri, 11 Apr 2014 17:07:00 +0200 Subject: driver: reset: sti: add keyscan for stih415 Add keyscan reset on stih415 reset controller. Acked-by: Philipp Zabel Signed-off-by: Giuseppe Condorelli Signed-off-by: Gabriel Fernandez Signed-off-by: Maxime Coquelin --- drivers/reset/sti/reset-stih415.c | 1 + include/dt-bindings/reset-controller/stih415-resets.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/reset/sti/reset-stih415.c b/drivers/reset/sti/reset-stih415.c index e6f6c41abe12..c93fd260447e 100644 --- a/drivers/reset/sti/reset-stih415.c +++ b/drivers/reset/sti/reset-stih415.c @@ -73,6 +73,7 @@ static const struct syscfg_reset_channel_data stih415_softresets[] = { [STIH415_USB0_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 9), [STIH415_USB1_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 10), [STIH415_USB2_SOFTRESET] = STIH415_SRST_REAR(SYSCFG_376, 11), + [STIH415_KEYSCAN_SOFTRESET] = STIH415_SRST_LPM(LPM_SYSCFG_1, 8), }; static struct syscfg_reset_controller_data stih415_powerdown_controller = { diff --git a/include/dt-bindings/reset-controller/stih415-resets.h b/include/dt-bindings/reset-controller/stih415-resets.h index c2f8a66913c5..c2329fe29cf6 100644 --- a/include/dt-bindings/reset-controller/stih415-resets.h +++ b/include/dt-bindings/reset-controller/stih415-resets.h @@ -22,5 +22,6 @@ #define STIH415_USB0_SOFTRESET 3 #define STIH415_USB1_SOFTRESET 4 #define STIH415_USB2_SOFTRESET 5 +#define STIH415_KEYSCAN_SOFTRESET 6 #endif /* _DT_BINDINGS_RESET_CONTROLLER_STIH415 */ -- cgit v1.2.3 From d6c057f313cfe9484b46876dd13106ede6bc30e2 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Fri, 11 Apr 2014 17:07:00 +0200 Subject: driver: reset: sti: add keyscan for stih416 Add keyscan reset on stih416 reset controller. Acked-by: Philipp Zabel Signed-off-by: Giuseppe Condorelli Signed-off-by: Gabriel Fernandez Signed-off-by: Maxime Coquelin --- drivers/reset/sti/reset-stih416.c | 1 + include/dt-bindings/reset-controller/stih416-resets.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/reset/sti/reset-stih416.c b/drivers/reset/sti/reset-stih416.c index fe3bf02bdc8c..5fc987076a90 100644 --- a/drivers/reset/sti/reset-stih416.c +++ b/drivers/reset/sti/reset-stih416.c @@ -104,6 +104,7 @@ static const struct syscfg_reset_channel_data stih416_softresets[] = { [STIH416_COMPO_A_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 4), [STIH416_VP8_DEC_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 10), [STIH416_VTG_MAIN_SOFTRESET] = STIH416_SRST_CPU(SYSCFG_7564, 16), + [STIH416_KEYSCAN_SOFTRESET] = STIH416_SRST_LPM(LPM_SYSCFG_1, 8), }; static struct syscfg_reset_controller_data stih416_powerdown_controller = { diff --git a/include/dt-bindings/reset-controller/stih416-resets.h b/include/dt-bindings/reset-controller/stih416-resets.h index 2127743f23e3..fcf9af1ac0b2 100644 --- a/include/dt-bindings/reset-controller/stih416-resets.h +++ b/include/dt-bindings/reset-controller/stih416-resets.h @@ -46,5 +46,6 @@ #define STIH416_COMPO_A_SOFTRESET 25 #define STIH416_VP8_DEC_SOFTRESET 26 #define STIH416_VTG_MAIN_SOFTRESET 27 +#define STIH416_KEYSCAN_SOFTRESET 28 #endif /* _DT_BINDINGS_RESET_CONTROLLER_STIH416 */ -- cgit v1.2.3 From 08488e20cc649be3606d4d39a605e0933dcddbf0 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Tue, 20 May 2014 15:22:00 +0200 Subject: ARM: STi: DT: STiH416: 416 DT Entry for clockgen A0/1/10/11/12 Patch adds DT entries for clockgen A0/1/10/11/12 Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez Signed-off-by: Maxime Coquelin --- arch/arm/boot/dts/stih416-clock.dtsi | 475 +++++++++++++++++++++++++++++++ arch/arm/boot/dts/stih416.dtsi | 10 +- include/dt-bindings/clock/stih416-clks.h | 15 + 3 files changed, 495 insertions(+), 5 deletions(-) create mode 100644 include/dt-bindings/clock/stih416-clks.h (limited to 'include') diff --git a/arch/arm/boot/dts/stih416-clock.dtsi b/arch/arm/boot/dts/stih416-clock.dtsi index 10f8389ce9eb..c4e5d42a1aec 100644 --- a/arch/arm/boot/dts/stih416-clock.dtsi +++ b/arch/arm/boot/dts/stih416-clock.dtsi @@ -7,8 +7,13 @@ * published by the Free Software Foundation. */ +#include + / { clocks { + #address-cells = <1>; + #size-cells = <1>; + ranges; /* * Fixed 30MHz oscillator inputs to SoC @@ -52,5 +57,475 @@ clock-frequency = <25000000>; clock-output-names = "CLK_S_ETH1_PHY"; }; + + /* + * ClockGenAs on SASG2 + */ + clockgen-a@fee62000 { + reg = <0xfee62000 0xb48>; + + clk_s_a0_pll: clk-s-a0-pll { + #clock-cells = <1>; + compatible = "st,clkgena-plls-c65"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-s-a0-pll0-hs", + "clk-s-a0-pll0-ls", + "clk-s-a0-pll1"; + }; + + clk_s_a0_osc_prediv: clk-s-a0-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c65", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-s-a0-osc-prediv"; + }; + + clk_s_a0_hs: clk-s-a0-hs { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c65-hs", + "st,clkgena-divmux"; + + clocks = <&clk_s_a0_osc_prediv>, + <&clk_s_a0_pll 0>, /* PLL0 HS */ + <&clk_s_a0_pll 2>; /* PLL1 */ + + clock-output-names = "clk-s-fdma-0", + "clk-s-fdma-1", + ""; /* clk-s-jit-sense */ + /* Fourth output unused */ + }; + + clk_s_a0_ls: clk-s-a0-ls { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c65-ls", + "st,clkgena-divmux"; + + clocks = <&clk_s_a0_osc_prediv>, + <&clk_s_a0_pll 1>, /* PLL0 LS */ + <&clk_s_a0_pll 2>; /* PLL1 */ + + clock-output-names = "clk-s-icn-reg-0", + "clk-s-icn-if-0", + "clk-s-icn-reg-lp-0", + "clk-s-emiss", + "clk-s-eth1-phy", + "clk-s-mii-ref-out"; + /* Remaining outputs unused */ + }; + }; + + clockgen-a@fee81000 { + reg = <0xfee81000 0xb48>; + + clk_s_a1_pll: clk-s-a1-pll { + #clock-cells = <1>; + compatible = "st,clkgena-plls-c65"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-s-a1-pll0-hs", + "clk-s-a1-pll0-ls", + "clk-s-a1-pll1"; + }; + + clk_s_a1_osc_prediv: clk-s-a1-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c65", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-s-a1-osc-prediv"; + }; + + clk_s_a1_hs: clk-s-a1-hs { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c65-hs", + "st,clkgena-divmux"; + + clocks = <&clk_s_a1_osc_prediv>, + <&clk_s_a1_pll 0>, /* PLL0 HS */ + <&clk_s_a1_pll 2>; /* PLL1 */ + + clock-output-names = "", /* Reserved */ + "", /* Reserved */ + "clk-s-stac-phy", + "clk-s-vtac-tx-phy"; + }; + + clk_s_a1_ls: clk-s-a1-ls { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c65-ls", + "st,clkgena-divmux"; + + clocks = <&clk_s_a1_osc_prediv>, + <&clk_s_a1_pll 1>, /* PLL0 LS */ + <&clk_s_a1_pll 2>; /* PLL1 */ + + clock-output-names = "clk-s-icn-if-2", + "clk-s-card-mmc-0", + "clk-s-icn-if-1", + "clk-s-gmac0-phy", + "clk-s-nand-ctrl", + "", /* Reserved */ + "clk-s-mii0-ref-out", + "clk-s-stac-sys", + "clk-s-card-mmc-1"; + /* Remaining outputs unused */ + }; + }; + + /* + * ClockGenAs on MPE42 + */ + clockgen-a@fde12000 { + reg = <0xfde12000 0xb50>; + + clk_m_a0_pll0: clk-m-a0-pll0 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-0", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a0-pll0-phi0", + "clk-m-a0-pll0-phi1", + "clk-m-a0-pll0-phi2", + "clk-m-a0-pll0-phi3"; + }; + + clk_m_a0_pll1: clk-m-a0-pll1 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-1", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a0-pll1-phi0", + "clk-m-a0-pll1-phi1", + "clk-m-a0-pll1-phi2", + "clk-m-a0-pll1-phi3"; + }; + + clk_m_a0_osc_prediv: clk-m-a0-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c32", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a0-osc-prediv"; + }; + + clk_m_a0_div0: clk-m-a0-div0 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf0", + "st,clkgena-divmux"; + + clocks = <&clk_m_a0_osc_prediv>, + <&clk_m_a0_pll0 0>, /* PLL0 PHI0 */ + <&clk_m_a0_pll1 0>; /* PLL1 PHI0 */ + + clock-output-names = "", /* Unused */ + "", /* Unused */ + "clk-m-fdma-12", + "", /* Unused */ + "clk-m-pp-dmu-0", + "clk-m-pp-dmu-1", + "clk-m-icm-lmi", + "clk-m-vid-dmu-0"; + }; + + clk_m_a0_div1: clk-m-a0-div1 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf1", + "st,clkgena-divmux"; + + clocks = <&clk_m_a0_osc_prediv>, + <&clk_m_a0_pll0 1>, /* PLL0 PHI1 */ + <&clk_m_a0_pll1 1>; /* PLL1 PHI1 */ + + clock-output-names = "clk-m-vid-dmu-1", + "", /* Unused */ + "clk-m-a9-ext2f", + "clk-m-st40rt", + "clk-m-st231-dmu-0", + "clk-m-st231-dmu-1", + "clk-m-st231-aud", + "clk-m-st231-gp-0"; + }; + + clk_m_a0_div2: clk-m-a0-div2 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf2", + "st,clkgena-divmux"; + + clocks = <&clk_m_a0_osc_prediv>, + <&clk_m_a0_pll0 2>, /* PLL0 PHI2 */ + <&clk_m_a0_pll1 2>; /* PLL1 PHI2 */ + + clock-output-names = "clk-m-st231-gp-1", + "clk-m-icn-cpu", + "clk-m-icn-stac", + "clk-m-tx-icn-dmu-0", + "clk-m-tx-icn-dmu-1", + "clk-m-tx-icn-ts", + "clk-m-icn-vdp-0", + "clk-m-icn-vdp-1"; + }; + + clk_m_a0_div3: clk-m-a0-div3 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf3", + "st,clkgena-divmux"; + + clocks = <&clk_m_a0_osc_prediv>, + <&clk_m_a0_pll0 3>, /* PLL0 PHI3 */ + <&clk_m_a0_pll1 3>; /* PLL1 PHI3 */ + + clock-output-names = "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "clk-m-icn-vp8", + "", /* Unused */ + "clk-m-icn-reg-11", + "clk-m-a9-trace"; + }; + }; + + clockgen-a@fd6db000 { + reg = <0xfd6db000 0xb50>; + + clk_m_a1_pll0: clk-m-a1-pll0 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-0", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a1-pll0-phi0", + "clk-m-a1-pll0-phi1", + "clk-m-a1-pll0-phi2", + "clk-m-a1-pll0-phi3"; + }; + + clk_m_a1_pll1: clk-m-a1-pll1 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-1", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a1-pll1-phi0", + "clk-m-a1-pll1-phi1", + "clk-m-a1-pll1-phi2", + "clk-m-a1-pll1-phi3"; + }; + + clk_m_a1_osc_prediv: clk-m-a1-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c32", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a1-osc-prediv"; + }; + + clk_m_a1_div0: clk-m-a1-div0 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf0", + "st,clkgena-divmux"; + + clocks = <&clk_m_a1_osc_prediv>, + <&clk_m_a1_pll0 0>, /* PLL0 PHI0 */ + <&clk_m_a1_pll1 0>; /* PLL1 PHI0 */ + + clock-output-names = "", /* Unused */ + "clk-m-fdma-10", + "clk-m-fdma-11", + "clk-m-hva-alt", + "clk-m-proc-sc", + "clk-m-tp", + "clk-m-rx-icn-dmu-0", + "clk-m-rx-icn-dmu-1"; + }; + + clk_m_a1_div1: clk-m-a1-div1 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf1", + "st,clkgena-divmux"; + + clocks = <&clk_m_a1_osc_prediv>, + <&clk_m_a1_pll0 1>, /* PLL0 PHI1 */ + <&clk_m_a1_pll1 1>; /* PLL1 PHI1 */ + + clock-output-names = "clk-m-rx-icn-ts", + "clk-m-rx-icn-vdp-0", + "", /* Unused */ + "clk-m-prv-t1-bus", + "clk-m-icn-reg-12", + "clk-m-icn-reg-10", + "", /* Unused */ + "clk-m-icn-st231"; + }; + + clk_m_a1_div2: clk-m-a1-div2 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf2", + "st,clkgena-divmux"; + + clocks = <&clk_m_a1_osc_prediv>, + <&clk_m_a1_pll0 2>, /* PLL0 PHI2 */ + <&clk_m_a1_pll1 2>; /* PLL1 PHI2 */ + + clock-output-names = "clk-m-fvdp-proc-alt", + "clk-m-icn-reg-13", + "clk-m-tx-icn-gpu", + "clk-m-rx-icn-gpu", + "", /* Unused */ + "", /* Unused */ + "", /* clk-m-apb-pm-12 */ + ""; /* Unused */ + }; + + clk_m_a1_div3: clk-m-a1-div3 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf3", + "st,clkgena-divmux"; + + clocks = <&clk_m_a1_osc_prediv>, + <&clk_m_a1_pll0 3>, /* PLL0 PHI3 */ + <&clk_m_a1_pll1 3>; /* PLL1 PHI3 */ + + clock-output-names = "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + ""; /* clk-m-gpu-alt */ + }; + }; + + clk_m_a9_ext2f_div2: clk-m-a9-ext2f-div2 { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + clocks = <&clk_m_a0_div1 2>; + clock-div = <2>; + clock-mult = <1>; + }; + + clockgen-a@fd345000 { + reg = <0xfd345000 0xb50>; + + clk_m_a2_pll0: clk-m-a2-pll0 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-0", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a2-pll0-phi0", + "clk-m-a2-pll0-phi1", + "clk-m-a2-pll0-phi2", + "clk-m-a2-pll0-phi3"; + }; + + clk_m_a2_pll1: clk-m-a2-pll1 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-1", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a2-pll1-phi0", + "clk-m-a2-pll1-phi1", + "clk-m-a2-pll1-phi2", + "clk-m-a2-pll1-phi3"; + }; + + clk_m_a2_osc_prediv: clk-m-a2-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c32", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a2-osc-prediv"; + }; + + clk_m_a2_div0: clk-m-a2-div0 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf0", + "st,clkgena-divmux"; + + clocks = <&clk_m_a2_osc_prediv>, + <&clk_m_a2_pll0 0>, /* PLL0 PHI0 */ + <&clk_m_a2_pll1 0>; /* PLL1 PHI0 */ + + clock-output-names = "clk-m-vtac-main-phy", + "clk-m-vtac-aux-phy", + "clk-m-stac-phy", + "clk-m-stac-sys", + "", /* clk-m-mpestac-pg */ + "", /* clk-m-mpestac-wc */ + "", /* clk-m-mpevtacaux-pg*/ + ""; /* clk-m-mpevtacmain-pg*/ + }; + + clk_m_a2_div1: clk-m-a2-div1 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf1", + "st,clkgena-divmux"; + + clocks = <&clk_m_a2_osc_prediv>, + <&clk_m_a2_pll0 1>, /* PLL0 PHI1 */ + <&clk_m_a2_pll1 1>; /* PLL1 PHI1 */ + + clock-output-names = "", /* clk-m-mpevtacrx0-wc */ + "", /* clk-m-mpevtacrx1-wc */ + "clk-m-compo-main", + "clk-m-compo-aux", + "clk-m-bdisp-0", + "clk-m-bdisp-1", + "clk-m-icn-bdisp", + "clk-m-icn-compo"; + }; + + clk_m_a2_div2: clk-m-a2-div2 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf2", + "st,clkgena-divmux"; + + clocks = <&clk_m_a2_osc_prediv>, + <&clk_m_a2_pll0 2>, /* PLL0 PHI2 */ + <&clk_m_a2_pll1 2>; /* PLL1 PHI2 */ + + clock-output-names = "clk-m-icn-vdp-2", + "", /* Unused */ + "clk-m-icn-reg-14", + "clk-m-mdtp", + "clk-m-jpegdec", + "", /* Unused */ + "clk-m-dcephy-impctrl", + ""; /* Unused */ + }; + + clk_m_a2_div3: clk-m-a2-div3 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf3", + "st,clkgena-divmux"; + + clocks = <&clk_m_a2_osc_prediv>, + <&clk_m_a2_pll0 3>, /* PLL0 PHI3 */ + <&clk_m_a2_pll1 3>; /* PLL1 PHI3 */ + + clock-output-names = "", /* Unused */ + ""; /* clk-m-apb-pm-11 */ + /* Remaining outputs unused */ + }; + }; }; }; diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi index 09592a83fe15..06473c5d9ea9 100644 --- a/arch/arm/boot/dts/stih416.dtsi +++ b/arch/arm/boot/dts/stih416.dtsi @@ -89,7 +89,7 @@ status = "disabled"; reg = <0xfed32000 0x2c>; interrupts = <0 197 0>; - clocks = <&CLK_S_ICN_REG_0>; + clocks = <&clk_s_a0_ls CLK_ICN_REG>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_serial2 &pinctrl_serial2_oe>; }; @@ -109,7 +109,7 @@ compatible = "st,comms-ssc4-i2c"; reg = <0xfed40000 0x110>; interrupts = ; - clocks = <&CLK_S_ICN_REG_0>; + clocks = <&clk_s_a0_ls CLK_ICN_REG>; clock-names = "ssc"; clock-frequency = <400000>; pinctrl-names = "default"; @@ -122,7 +122,7 @@ compatible = "st,comms-ssc4-i2c"; reg = <0xfed41000 0x110>; interrupts = ; - clocks = <&CLK_S_ICN_REG_0>; + clocks = <&clk_s_a0_ls CLK_ICN_REG>; clock-names = "ssc"; clock-frequency = <400000>; pinctrl-names = "default"; @@ -176,7 +176,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii0>; clock-names = "stmmaceth"; - clocks = <&CLK_S_GMAC0_PHY>; + clocks = <&clk_s_a1_ls CLK_GMAC0_PHY>; }; ethernet1: dwmac@fef08000 { @@ -198,7 +198,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii1>; clock-names = "stmmaceth"; - clocks = <&CLK_S_ETH1_PHY>; + clocks = <&clk_s_a0_ls CLK_ETH1_PHY>; }; rc: rc@fe518000 { diff --git a/include/dt-bindings/clock/stih416-clks.h b/include/dt-bindings/clock/stih416-clks.h new file mode 100644 index 000000000000..552c779eb6af --- /dev/null +++ b/include/dt-bindings/clock/stih416-clks.h @@ -0,0 +1,15 @@ +/* + * This header provides constants clk index STMicroelectronics + * STiH416 SoC. + */ +#ifndef _CLK_STIH416 +#define _CLK_STIH416 + +/* CLOCKGEN A0 */ +#define CLK_ICN_REG 0 +#define CLK_ETH1_PHY 4 + +/* CLOCKGEN A1 */ +#define CLK_GMAC0_PHY 3 + +#endif -- cgit v1.2.3 From f317e689cba38b1d5cf9dd5763e7f91ed5168d63 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Tue, 20 May 2014 15:22:00 +0200 Subject: ARM: STi: DT: STiH415: 415 DT Entry for clockgen A0/1/10/11/12 Patch adds DT entries for clockgen A0/1/10/11/12 Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez Signed-off-by: Maxime Coquelin --- arch/arm/boot/dts/stih415-clock.dtsi | 475 +++++++++++++++++++++++++++++++ arch/arm/boot/dts/stih415.dtsi | 10 +- include/dt-bindings/clock/stih415-clks.h | 15 + 3 files changed, 495 insertions(+), 5 deletions(-) create mode 100644 include/dt-bindings/clock/stih415-clks.h (limited to 'include') diff --git a/arch/arm/boot/dts/stih415-clock.dtsi b/arch/arm/boot/dts/stih415-clock.dtsi index 8ef996483105..3016e537b2e3 100644 --- a/arch/arm/boot/dts/stih415-clock.dtsi +++ b/arch/arm/boot/dts/stih415-clock.dtsi @@ -5,8 +5,15 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#include + / { clocks { + #address-cells = <1>; + #size-cells = <1>; + ranges; + /* * Fixed 30MHz oscillator input to SoC */ @@ -48,5 +55,473 @@ clock-frequency = <25000000>; clock-output-names = "CLKS_ETH1_PHY"; }; + + /* + * ClockGenAs on SASG1 + */ + clockgen-a@fee62000 { + reg = <0xfee62000 0xb48>; + + clk_s_a0_pll: clk-s-a0-pll { + #clock-cells = <1>; + compatible = "st,clkgena-plls-c65"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-s-a0-pll0-hs", + "clk-s-a0-pll0-ls", + "clk-s-a0-pll1"; + }; + + clk_s_a0_osc_prediv: clk-s-a0-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c65", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-s-a0-osc-prediv"; + }; + + clk_s_a0_hs: clk-s-a0-hs { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c65-hs", + "st,clkgena-divmux"; + + clocks = <&clk_s_a0_osc_prediv>, + <&clk_s_a0_pll 0>, /* PLL0 HS */ + <&clk_s_a0_pll 2>; /* PLL1 */ + + clock-output-names = "clk-s-fdma-0", + "clk-s-fdma-1", + ""; /* clk-s-jit-sense */ + /* Fourth output unused */ + }; + + clk_s_a0_ls: clk-s-a0-ls { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c65-ls", + "st,clkgena-divmux"; + + clocks = <&clk_s_a0_osc_prediv>, + <&clk_s_a0_pll 1>, /* PLL0 LS */ + <&clk_s_a0_pll 2>; /* PLL1 */ + + clock-output-names = "clk-s-icn-reg-0", + "clk-s-icn-if-0", + "clk-s-icn-reg-lp-0", + "clk-s-emiss", + "clk-s-eth1-phy", + "clk-s-mii-ref-out"; + /* Remaining outputs unused */ + }; + }; + + clockgen-a@fee81000 { + reg = <0xfee81000 0xb48>; + + clk_s_a1_pll: clk-s-a1-pll { + #clock-cells = <1>; + compatible = "st,clkgena-plls-c65"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-s-a1-pll0-hs", + "clk-s-a1-pll0-ls", + "clk-s-a1-pll1"; + }; + + clk_s_a1_osc_prediv: clk-s-a1-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c65", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-s-a1-osc-prediv"; + }; + + clk_s_a1_hs: clk-s-a1-hs { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c65-hs", + "st,clkgena-divmux"; + + clocks = <&clk_s_a1_osc_prediv>, + <&clk_s_a1_pll 0>, /* PLL0 HS */ + <&clk_s_a1_pll 2>; /* PLL1 */ + + clock-output-names = "", /* Reserved */ + "", /* Reserved */ + "clk-s-stac-phy", + "clk-s-vtac-tx-phy"; + }; + + clk_s_a1_ls: clk-s-a1-ls { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c65-ls", + "st,clkgena-divmux"; + + clocks = <&clk_s_a1_osc_prediv>, + <&clk_s_a1_pll 1>, /* PLL0 LS */ + <&clk_s_a1_pll 2>; /* PLL1 */ + + clock-output-names = "clk-s-icn-if-2", + "clk-s-card-mmc", + "clk-s-icn-if-1", + "clk-s-gmac0-phy", + "clk-s-nand-ctrl", + "", /* Reserved */ + "clk-s-mii0-ref-out", + ""; /* clk-s-stac-sys */ + /* Remaining outputs unused */ + }; + }; + + /* + * ClockGenAs on MPE41 + */ + clockgen-a@fde12000 { + reg = <0xfde12000 0xb50>; + + clk_m_a0_pll0: clk-m-a0-pll0 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-0", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a0-pll0-phi0", + "clk-m-a0-pll0-phi1", + "clk-m-a0-pll0-phi2", + "clk-m-a0-pll0-phi3"; + }; + + clk_m_a0_pll1: clk-m-a0-pll1 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-1", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a0-pll1-phi0", + "clk-m-a0-pll1-phi1", + "clk-m-a0-pll1-phi2", + "clk-m-a0-pll1-phi3"; + }; + + clk_m_a0_osc_prediv: clk-m-a0-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c32", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a0-osc-prediv"; + }; + + clk_m_a0_div0: clk-m-a0-div0 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf0", + "st,clkgena-divmux"; + + clocks = <&clk_m_a0_osc_prediv>, + <&clk_m_a0_pll0 0>, /* PLL0 PHI0 */ + <&clk_m_a0_pll1 0>; /* PLL1 PHI0 */ + + clock-output-names = "clk-m-apb-pm", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "clk-m-pp-dmu-0", + "clk-m-pp-dmu-1", + "clk-m-icm-disp", + ""; /* Unused */ + }; + + clk_m_a0_div1: clk-m-a0-div1 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf1", + "st,clkgena-divmux"; + + clocks = <&clk_m_a0_osc_prediv>, + <&clk_m_a0_pll0 1>, /* PLL0 PHI1 */ + <&clk_m_a0_pll1 1>; /* PLL1 PHI1 */ + + clock-output-names = "", /* Unused */ + "", /* Unused */ + "clk-m-a9-ext2f", + "clk-m-st40rt", + "clk-m-st231-dmu-0", + "clk-m-st231-dmu-1", + "clk-m-st231-aud", + "clk-m-st231-gp-0"; + }; + + clk_m_a0_div2: clk-m-a0-div2 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf2", + "st,clkgena-divmux"; + + clocks = <&clk_m_a0_osc_prediv>, + <&clk_m_a0_pll0 2>, /* PLL0 PHI2 */ + <&clk_m_a0_pll1 2>; /* PLL1 PHI2 */ + + clock-output-names = "clk-m-st231-gp-1", + "clk-m-icn-cpu", + "clk-m-icn-stac", + "clk-m-icn-dmu-0", + "clk-m-icn-dmu-1", + "", /* Unused */ + "", /* Unused */ + ""; /* Unused */ + }; + + clk_m_a0_div3: clk-m-a0-div3 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf3", + "st,clkgena-divmux"; + + clocks = <&clk_m_a0_osc_prediv>, + <&clk_m_a0_pll0 3>, /* PLL0 PHI3 */ + <&clk_m_a0_pll1 3>; /* PLL1 PHI3 */ + + clock-output-names = "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "clk-m-icn-eram", + "clk-m-a9-trace"; + }; + }; + + clockgen-a@fd6db000 { + reg = <0xfd6db000 0xb50>; + + clk_m_a1_pll0: clk-m-a1-pll0 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-0", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a1-pll0-phi0", + "clk-m-a1-pll0-phi1", + "clk-m-a1-pll0-phi2", + "clk-m-a1-pll0-phi3"; + }; + + clk_m_a1_pll1: clk-m-a1-pll1 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-1", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a1-pll1-phi0", + "clk-m-a1-pll1-phi1", + "clk-m-a1-pll1-phi2", + "clk-m-a1-pll1-phi3"; + }; + + clk_m_a1_osc_prediv: clk-m-a1-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c32", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a1-osc-prediv"; + }; + + clk_m_a1_div0: clk-m-a1-div0 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf0", + "st,clkgena-divmux"; + + clocks = <&clk_m_a1_osc_prediv>, + <&clk_m_a1_pll0 0>, /* PLL0 PHI0 */ + <&clk_m_a1_pll1 0>; /* PLL1 PHI0 */ + + clock-output-names = "clk-m-fdma-12", + "clk-m-fdma-10", + "clk-m-fdma-11", + "clk-m-hva-lmi", + "clk-m-proc-sc", + "clk-m-tp", + "clk-m-icn-gpu", + "clk-m-icn-vdp-0"; + }; + + clk_m_a1_div1: clk-m-a1-div1 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf1", + "st,clkgena-divmux"; + + clocks = <&clk_m_a1_osc_prediv>, + <&clk_m_a1_pll0 1>, /* PLL0 PHI1 */ + <&clk_m_a1_pll1 1>; /* PLL1 PHI1 */ + + clock-output-names = "clk-m-icn-vdp-1", + "clk-m-icn-vdp-2", + "clk-m-icn-vdp-3", + "clk-m-prv-t1-bus", + "clk-m-icn-vdp-4", + "clk-m-icn-reg-10", + "", /* Unused */ + ""; /* clk-m-icn-st231 */ + }; + + clk_m_a1_div2: clk-m-a1-div2 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf2", + "st,clkgena-divmux"; + + clocks = <&clk_m_a1_osc_prediv>, + <&clk_m_a1_pll0 2>, /* PLL0 PHI2 */ + <&clk_m_a1_pll1 2>; /* PLL1 PHI2 */ + + clock-output-names = "clk-m-fvdp-proc-alt", + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + ""; /* Unused */ + }; + + clk_m_a1_div3: clk-m-a1-div3 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf3", + "st,clkgena-divmux"; + + clocks = <&clk_m_a1_osc_prediv>, + <&clk_m_a1_pll0 3>, /* PLL0 PHI3 */ + <&clk_m_a1_pll1 3>; /* PLL1 PHI3 */ + + clock-output-names = "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + "", /* Unused */ + ""; /* Unused */ + }; + }; + + clk_m_a9_ext2f_div2: clk-m-a9-ext2f-div2 { + #clock-cells = <0>; + compatible = "fixed-factor-clock"; + clocks = <&clk_m_a0_div1 2>; + clock-div = <2>; + clock-mult = <1>; + }; + + clockgen-a@fd345000 { + reg = <0xfd345000 0xb50>; + + clk_m_a2_pll0: clk-m-a2-pll0 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-0", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a2-pll0-phi0", + "clk-m-a2-pll0-phi1", + "clk-m-a2-pll0-phi2", + "clk-m-a2-pll0-phi3"; + }; + + clk_m_a2_pll1: clk-m-a2-pll1 { + #clock-cells = <1>; + compatible = "st,plls-c32-a1x-1", "st,clkgen-plls-c32"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a2-pll1-phi0", + "clk-m-a2-pll1-phi1", + "clk-m-a2-pll1-phi2", + "clk-m-a2-pll1-phi3"; + }; + + clk_m_a2_osc_prediv: clk-m-a2-osc-prediv { + #clock-cells = <0>; + compatible = "st,clkgena-prediv-c32", + "st,clkgena-prediv"; + + clocks = <&clk_sysin>; + + clock-output-names = "clk-m-a2-osc-prediv"; + }; + + clk_m_a2_div0: clk-m-a2-div0 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf0", + "st,clkgena-divmux"; + + clocks = <&clk_m_a2_osc_prediv>, + <&clk_m_a2_pll0 0>, /* PLL0 PHI0 */ + <&clk_m_a2_pll1 0>; /* PLL1 PHI0 */ + + clock-output-names = "clk-m-vtac-main-phy", + "clk-m-vtac-aux-phy", + "clk-m-stac-phy", + "clk-m-stac-sys", + "", /* clk-m-mpestac-pg */ + "", /* clk-m-mpestac-wc */ + "", /* clk-m-mpevtacaux-pg*/ + ""; /* clk-m-mpevtacmain-pg*/ + }; + + clk_m_a2_div1: clk-m-a2-div1 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf1", + "st,clkgena-divmux"; + + clocks = <&clk_m_a2_osc_prediv>, + <&clk_m_a2_pll0 1>, /* PLL0 PHI1 */ + <&clk_m_a2_pll1 1>; /* PLL1 PHI1 */ + + clock-output-names = "", /* clk-m-mpevtacrx0-wc */ + "", /* clk-m-mpevtacrx1-wc */ + "clk-m-compo-main", + "clk-m-compo-aux", + "clk-m-bdisp-0", + "clk-m-bdisp-1", + "clk-m-icn-bdisp-0", + "clk-m-icn-bdisp-1"; + }; + + clk_m_a2_div2: clk-m-a2-div2 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf2", + "st,clkgena-divmux"; + + clocks = <&clk_m_a2_osc_prediv>, + <&clk_m_a2_pll0 2>, /* PLL0 PHI2 */ + <&clk_m_a2_pll1 2>; /* PLL1 PHI2 */ + + clock-output-names = "", /* clk-m-icn-hqvdp0 */ + "", /* clk-m-icn-hqvdp1 */ + "clk-m-icn-compo", + "", /* clk-m-icn-vdpaux */ + "clk-m-icn-ts", + "clk-m-icn-reg-lp-10", + "clk-m-dcephy-impctrl", + ""; /* Unused */ + }; + + clk_m_a2_div3: clk-m-a2-div3 { + #clock-cells = <1>; + compatible = "st,clkgena-divmux-c32-odf3", + "st,clkgena-divmux"; + + clocks = <&clk_m_a2_osc_prediv>, + <&clk_m_a2_pll0 3>, /* PLL0 PHI3 */ + <&clk_m_a2_pll1 3>; /* PLL1 PHI3 */ + + clock-output-names = ""; /* Unused */ + /* Remaining outputs unused */ + }; + }; }; }; diff --git a/arch/arm/boot/dts/stih415.dtsi b/arch/arm/boot/dts/stih415.dtsi index 20425a7f3ed0..d6f254f302fe 100644 --- a/arch/arm/boot/dts/stih415.dtsi +++ b/arch/arm/boot/dts/stih415.dtsi @@ -82,7 +82,7 @@ interrupts = <0 197 0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_serial2>; - clocks = <&CLKS_ICN_REG_0>; + clocks = <&clk_s_a0_ls CLK_ICN_REG>; }; /* SBC comms block ASCs in SASG1 */ @@ -100,7 +100,7 @@ compatible = "st,comms-ssc4-i2c"; reg = <0xfed40000 0x110>; interrupts = ; - clocks = <&CLKS_ICN_REG_0>; + clocks = <&clk_s_a0_ls CLK_ICN_REG>; clock-names = "ssc"; clock-frequency = <400000>; pinctrl-names = "default"; @@ -113,7 +113,7 @@ compatible = "st,comms-ssc4-i2c"; reg = <0xfed41000 0x110>; interrupts = ; - clocks = <&CLKS_ICN_REG_0>; + clocks = <&clk_s_a0_ls CLK_ICN_REG>; clock-names = "ssc"; clock-frequency = <400000>; pinctrl-names = "default"; @@ -170,7 +170,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii0>; clock-names = "stmmaceth"; - clocks = <&CLKS_GMAC0_PHY>; + clocks = <&clk_s_a1_ls CLK_GMAC0_PHY>; }; ethernet1: dwmac@fef08000 { @@ -193,7 +193,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii1>; clock-names = "stmmaceth"; - clocks = <&CLKS_ETH1_PHY>; + clocks = <&clk_s_a0_ls CLK_ETH1_PHY>; }; rc: rc@fe518000 { diff --git a/include/dt-bindings/clock/stih415-clks.h b/include/dt-bindings/clock/stih415-clks.h new file mode 100644 index 000000000000..0d2c7397e028 --- /dev/null +++ b/include/dt-bindings/clock/stih415-clks.h @@ -0,0 +1,15 @@ +/* + * This header provides constants clk index STMicroelectronics + * STiH415 SoC. + */ +#ifndef _CLK_STIH415 +#define _CLK_STIH415 + +/* CLOCKGEN A0 */ +#define CLK_ICN_REG 0 +#define CLK_ETH1_PHY 4 + +/* CLOCKGEN A1 */ +#define CLK_GMAC0_PHY 3 + +#endif -- cgit v1.2.3 From 75ecb231ff45b54afa9f4ec9137965c3c00868f4 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 21 May 2014 20:56:12 +0800 Subject: crypto: hash - Add real ahash walk interface Although the existing hash walk interface has already been used by a number of ahash crypto drivers, it turns out that none of them were really asynchronous. They were all essentially polling for completion. That's why nobody has noticed until now that the walk interface couldn't work with a real asynchronous driver since the memory is mapped using kmap_atomic. As we now have a use-case for a real ahash implementation on x86, this patch creates a minimal ahash walk interface. Basically it just calls kmap instead of kmap_atomic and does away with the crypto_yield call. Real ahash crypto drivers don't need to yield since by definition they won't be hogging the CPU. Signed-off-by: Herbert Xu --- crypto/ahash.c | 41 ++++++++++++++++++++++++++++++++++++----- include/crypto/internal/hash.h | 13 +++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/crypto/ahash.c b/crypto/ahash.c index 6e7223392e80..f2a5d8f656ff 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -46,7 +47,10 @@ static int hash_walk_next(struct crypto_hash_walk *walk) unsigned int nbytes = min(walk->entrylen, ((unsigned int)(PAGE_SIZE)) - offset); - walk->data = kmap_atomic(walk->pg); + if (walk->flags & CRYPTO_ALG_ASYNC) + walk->data = kmap(walk->pg); + else + walk->data = kmap_atomic(walk->pg); walk->data += offset; if (offset & alignmask) { @@ -93,8 +97,16 @@ int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err) return nbytes; } - kunmap_atomic(walk->data); - crypto_yield(walk->flags); + if (walk->flags & CRYPTO_ALG_ASYNC) + kunmap(walk->pg); + else { + kunmap_atomic(walk->data); + /* + * The may sleep test only makes sense for sync users. + * Async users don't need to sleep here anyway. + */ + crypto_yield(walk->flags); + } if (err) return err; @@ -124,12 +136,31 @@ int crypto_hash_walk_first(struct ahash_request *req, walk->alignmask = crypto_ahash_alignmask(crypto_ahash_reqtfm(req)); walk->sg = req->src; - walk->flags = req->base.flags; + walk->flags = req->base.flags & CRYPTO_TFM_REQ_MASK; return hash_walk_new_entry(walk); } EXPORT_SYMBOL_GPL(crypto_hash_walk_first); +int crypto_ahash_walk_first(struct ahash_request *req, + struct crypto_hash_walk *walk) +{ + walk->total = req->nbytes; + + if (!walk->total) + return 0; + + walk->alignmask = crypto_ahash_alignmask(crypto_ahash_reqtfm(req)); + walk->sg = req->src; + walk->flags = req->base.flags & CRYPTO_TFM_REQ_MASK; + walk->flags |= CRYPTO_ALG_ASYNC; + + BUILD_BUG_ON(CRYPTO_TFM_REQ_MASK & CRYPTO_ALG_ASYNC); + + return hash_walk_new_entry(walk); +} +EXPORT_SYMBOL_GPL(crypto_ahash_walk_first); + int crypto_hash_walk_first_compat(struct hash_desc *hdesc, struct crypto_hash_walk *walk, struct scatterlist *sg, unsigned int len) @@ -141,7 +172,7 @@ int crypto_hash_walk_first_compat(struct hash_desc *hdesc, walk->alignmask = crypto_hash_alignmask(hdesc->tfm); walk->sg = sg; - walk->flags = hdesc->flags; + walk->flags = hdesc->flags & CRYPTO_TFM_REQ_MASK; return hash_walk_new_entry(walk); } diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index 821eae8cbd8c..9b6f32a6cad1 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -55,15 +55,28 @@ extern const struct crypto_type crypto_ahash_type; int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err); int crypto_hash_walk_first(struct ahash_request *req, struct crypto_hash_walk *walk); +int crypto_ahash_walk_first(struct ahash_request *req, + struct crypto_hash_walk *walk); int crypto_hash_walk_first_compat(struct hash_desc *hdesc, struct crypto_hash_walk *walk, struct scatterlist *sg, unsigned int len); +static inline int crypto_ahash_walk_done(struct crypto_hash_walk *walk, + int err) +{ + return crypto_hash_walk_done(walk, err); +} + static inline int crypto_hash_walk_last(struct crypto_hash_walk *walk) { return !(walk->entrylen | walk->total); } +static inline int crypto_ahash_walk_last(struct crypto_hash_walk *walk) +{ + return crypto_hash_walk_last(walk); +} + int crypto_register_ahash(struct ahash_alg *alg); int crypto_unregister_ahash(struct ahash_alg *alg); int ahash_register_instance(struct crypto_template *tmpl, -- cgit v1.2.3 From 5b3e507820c6e120bc2680c0d35f9d9d81fcb98d Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 14 May 2014 14:58:08 -0300 Subject: mtd: nand: pxa3xx: Use ECC strength and step size devicetree binding This commit adds support for the user to specify the ECC strength and step size through the devicetree. We keep the previous behavior, when there is no DT parameter provided. Signed-off-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/nand/pxa3xx_nand.c | 17 +++++++++++++++-- include/linux/platform_data/mtd-nand-pxa3xx.h | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 3b66a6460d67..2a9add06c2d5 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1519,8 +1519,13 @@ KEEP_CONFIG: } } - ecc_strength = chip->ecc_strength_ds; - ecc_step = chip->ecc_step_ds; + if (pdata->ecc_strength && pdata->ecc_step_size) { + ecc_strength = pdata->ecc_strength; + ecc_step = pdata->ecc_step_size; + } else { + ecc_strength = chip->ecc_strength_ds; + ecc_step = chip->ecc_step_ds; + } /* Set default ECC strength requirements on non-ONFI devices */ if (ecc_strength < 1 && ecc_step < 1) { @@ -1729,6 +1734,14 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev) of_property_read_u32(np, "num-cs", &pdata->num_cs); pdata->flash_bbt = of_get_nand_on_flash_bbt(np); + pdata->ecc_strength = of_get_nand_ecc_strength(np); + if (pdata->ecc_strength < 0) + pdata->ecc_strength = 0; + + pdata->ecc_step_size = of_get_nand_ecc_step_size(np); + if (pdata->ecc_step_size < 0) + pdata->ecc_step_size = 0; + pdev->dev.platform_data = pdata; return 0; diff --git a/include/linux/platform_data/mtd-nand-pxa3xx.h b/include/linux/platform_data/mtd-nand-pxa3xx.h index a94147124929..ac4ea2e641c7 100644 --- a/include/linux/platform_data/mtd-nand-pxa3xx.h +++ b/include/linux/platform_data/mtd-nand-pxa3xx.h @@ -58,6 +58,9 @@ struct pxa3xx_nand_platform_data { /* use an flash-based bad block table */ bool flash_bbt; + /* requested ECC strength and ECC step size */ + int ecc_strength, ecc_step_size; + const struct mtd_partition *parts[NUM_CHIP_SELECT]; unsigned int nr_parts[NUM_CHIP_SELECT]; -- cgit v1.2.3 From e814e71ba4a6e1d7509b0f4b1928365ea650cace Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 21 May 2014 13:59:08 -0600 Subject: blk-mq: allow the hctx cpu hotplug notifier to return errors Prepare this for the next patch which adds more smarts in the plugging logic, so that we can save some memory. Signed-off-by: Jens Axboe --- block/blk-mq-cpu.c | 12 ++++++++---- block/blk-mq.c | 9 +++++---- block/blk-mq.h | 2 +- include/linux/blk-mq.h | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/block/blk-mq-cpu.c b/block/blk-mq-cpu.c index 136ef8643bba..d2c253f71b86 100644 --- a/block/blk-mq-cpu.c +++ b/block/blk-mq-cpu.c @@ -18,14 +18,18 @@ static int blk_mq_main_cpu_notify(struct notifier_block *self, { unsigned int cpu = (unsigned long) hcpu; struct blk_mq_cpu_notifier *notify; + int ret = NOTIFY_OK; raw_spin_lock(&blk_mq_cpu_notify_lock); - list_for_each_entry(notify, &blk_mq_cpu_notify_list, list) - notify->notify(notify->data, action, cpu); + list_for_each_entry(notify, &blk_mq_cpu_notify_list, list) { + ret = notify->notify(notify->data, action, cpu); + if (ret != NOTIFY_OK) + break; + } raw_spin_unlock(&blk_mq_cpu_notify_lock); - return NOTIFY_OK; + return ret; } void blk_mq_register_cpu_notifier(struct blk_mq_cpu_notifier *notifier) @@ -45,7 +49,7 @@ void blk_mq_unregister_cpu_notifier(struct blk_mq_cpu_notifier *notifier) } void blk_mq_init_cpu_notifier(struct blk_mq_cpu_notifier *notifier, - void (*fn)(void *, unsigned long, unsigned int), + int (*fn)(void *, unsigned long, unsigned int), void *data) { notifier->notify = fn; diff --git a/block/blk-mq.c b/block/blk-mq.c index ef7ed5e95d6d..5a3683fc5bdb 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1196,8 +1196,8 @@ void blk_mq_free_single_hw_queue(struct blk_mq_hw_ctx *hctx, } EXPORT_SYMBOL(blk_mq_free_single_hw_queue); -static void blk_mq_hctx_notify(void *data, unsigned long action, - unsigned int cpu) +static int blk_mq_hctx_notify(void *data, unsigned long action, + unsigned int cpu) { struct blk_mq_hw_ctx *hctx = data; struct request_queue *q = hctx->queue; @@ -1205,7 +1205,7 @@ static void blk_mq_hctx_notify(void *data, unsigned long action, LIST_HEAD(tmp); if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) - return; + return NOTIFY_OK; /* * Move ctx entries to new CPU, if this one is going away. @@ -1220,7 +1220,7 @@ static void blk_mq_hctx_notify(void *data, unsigned long action, spin_unlock(&ctx->lock); if (list_empty(&tmp)) - return; + return NOTIFY_OK; ctx = blk_mq_get_ctx(q); spin_lock(&ctx->lock); @@ -1240,6 +1240,7 @@ static void blk_mq_hctx_notify(void *data, unsigned long action, blk_mq_run_hw_queue(hctx, true); blk_mq_put_ctx(ctx); + return NOTIFY_OK; } static void blk_mq_free_rq_map(struct blk_mq_tag_set *set, diff --git a/block/blk-mq.h b/block/blk-mq.h index 7db4fe4bd002..491dbd4e93f5 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -39,7 +39,7 @@ int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr); */ struct blk_mq_cpu_notifier; void blk_mq_init_cpu_notifier(struct blk_mq_cpu_notifier *notifier, - void (*fn)(void *, unsigned long, unsigned int), + int (*fn)(void *, unsigned long, unsigned int), void *data); void blk_mq_register_cpu_notifier(struct blk_mq_cpu_notifier *notifier); void blk_mq_unregister_cpu_notifier(struct blk_mq_cpu_notifier *notifier); diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index f45424453338..4d2800567aad 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -8,7 +8,7 @@ struct blk_mq_tags; struct blk_mq_cpu_notifier { struct list_head list; void *data; - void (*notify)(void *data, unsigned long action, unsigned int cpu); + int (*notify)(void *data, unsigned long action, unsigned int cpu); }; struct blk_mq_ctxmap { -- cgit v1.2.3 From c1f43dd9c20d85e66c4d77e284f64ac114abe3f8 Mon Sep 17 00:00:00 2001 From: Xuelin Shi Date: Wed, 21 May 2014 14:02:37 -0700 Subject: dmaengine: fix dmaengine_unmap failure The count which is used to get_unmap_data maybe not the same as the count computed in dmaengine_unmap which causes to free data in a wrong pool. This patch fixes this issue by keeping the map count with unmap_data structure and use this count to get the pool. Cc: Signed-off-by: Xuelin Shi Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 2 ++ include/linux/dmaengine.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index a886713937fd..d5d30ed863ce 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -1009,6 +1009,7 @@ static void dmaengine_unmap(struct kref *kref) dma_unmap_page(dev, unmap->addr[i], unmap->len, DMA_BIDIRECTIONAL); } + cnt = unmap->map_cnt; mempool_free(unmap, __get_unmap_pool(cnt)->pool); } @@ -1074,6 +1075,7 @@ dmaengine_get_unmap_data(struct device *dev, int nr, gfp_t flags) memset(unmap, 0, sizeof(*unmap)); kref_init(&unmap->kref); unmap->dev = dev; + unmap->map_cnt = nr; return unmap; } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 8300fb87b84a..72cb0ddb9678 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -429,6 +429,7 @@ typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param); typedef void (*dma_async_tx_callback)(void *dma_async_param); struct dmaengine_unmap_data { + u8 map_cnt; u8 to_cnt; u8 from_cnt; u8 bidi_cnt; -- cgit v1.2.3 From 5fe821a9dee241fa450703ab7015d970ee0cfb8d Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Mon, 19 May 2014 14:56:14 -0700 Subject: net: filter: cleanup invocation of internal BPF Kernel API for classic BPF socket filters is: sk_unattached_filter_create() - validate classic BPF, convert, JIT SK_RUN_FILTER() - run it sk_unattached_filter_destroy() - destroy socket filter Cleanup internal BPF kernel API as following: sk_filter_select_runtime() - final step of internal BPF creation. Try to JIT internal BPF program, if JIT is not available select interpreter SK_RUN_FILTER() - run it sk_filter_free() - free internal BPF program Disallow direct calls to BPF interpreter. Execution of the BPF program should be done with SK_RUN_FILTER() macro. Example of internal BPF create, run, destroy: struct sk_filter *fp; fp = kzalloc(sk_filter_size(prog_len), GFP_KERNEL); memcpy(fp->insni, prog, prog_len * sizeof(fp->insni[0])); fp->len = prog_len; sk_filter_select_runtime(fp); SK_RUN_FILTER(fp, ctx); sk_filter_free(fp); Sockets, seccomp, testsuite, tracing are using different ways to populate sk_filter, so first steps of program creation are not common. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/filter.h | 6 ++---- kernel/seccomp.c | 6 ++---- lib/test_bpf.c | 4 ++-- net/core/filter.c | 44 ++++++++++++++++++++++++++++---------------- 4 files changed, 34 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index 9d5ae0a2c954..7977b3958e25 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -184,10 +184,8 @@ static inline unsigned int sk_filter_size(unsigned int proglen) int sk_filter(struct sock *sk, struct sk_buff *skb); -u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx, - const struct sock_filter_int *insni); -u32 sk_run_filter_int_skb(const struct sk_buff *ctx, - const struct sock_filter_int *insni); +void sk_filter_select_runtime(struct sk_filter *fp); +void sk_filter_free(struct sk_filter *fp); int sk_convert_filter(struct sock_filter *prog, int len, struct sock_filter_int *new_prog, int *new_len); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 7e02d624cc50..1036b6f2fded 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -273,10 +273,8 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) atomic_set(&filter->usage, 1); filter->prog->len = new_len; - filter->prog->bpf_func = (void *)sk_run_filter_int_seccomp; - /* JIT internal BPF into native HW instructions */ - bpf_int_jit_compile(filter->prog); + sk_filter_select_runtime(filter->prog); /* * If there is an existing filter, make it the prev and don't drop its @@ -340,7 +338,7 @@ void put_seccomp_filter(struct task_struct *tsk) while (orig && atomic_dec_and_test(&orig->usage)) { struct seccomp_filter *freeme = orig; orig = orig->prev; - bpf_jit_free(freeme->prog); + sk_filter_free(freeme->prog); kfree(freeme); } } diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 3603ebcd5d65..e160934430eb 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -1489,7 +1489,7 @@ static __init int test_bpf(void) memcpy(fp_ext->insns, tests[i].insns_int, fprog.len * 8); fp->len = fprog.len; - fp->bpf_func = sk_run_filter_int_skb; + sk_filter_select_runtime(fp); } else { err = sk_unattached_filter_create(&fp, &fprog); if (tests[i].data_type == EXPECTED_FAIL) { @@ -1516,7 +1516,7 @@ static __init int test_bpf(void) if (tests[i].data_type != SKB_INT) sk_unattached_filter_destroy(fp); else - kfree(fp); + sk_filter_free(fp); if (err) { pr_cont("FAIL %d\n", err); diff --git a/net/core/filter.c b/net/core/filter.c index 32c5b44c537e..7067cb240d3e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -153,7 +153,7 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) * keep, 0 for none. @ctx is the data we are operating on, @insn is the * array of filter instructions. */ -unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn) +static unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn) { u64 stack[MAX_BPF_STACK / sizeof(u64)]; u64 regs[MAX_BPF_REG], tmp; @@ -571,15 +571,6 @@ load_byte: return 0; } -u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx, - const struct sock_filter_int *insni) - __attribute__ ((alias ("__sk_run_filter"))); - -u32 sk_run_filter_int_skb(const struct sk_buff *ctx, - const struct sock_filter_int *insni) - __attribute__ ((alias ("__sk_run_filter"))); -EXPORT_SYMBOL_GPL(sk_run_filter_int_skb); - /* Helper to find the offset of pkt_type in sk_buff structure. We want * to make sure its still a 3bit field starting at a byte boundary; * taken from arch/x86/net/bpf_jit_comp.c. @@ -1397,7 +1388,7 @@ static void sk_filter_release_rcu(struct rcu_head *rcu) struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); sk_release_orig_filter(fp); - bpf_jit_free(fp); + sk_filter_free(fp); } /** @@ -1497,7 +1488,6 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, goto out_err_free; } - fp->bpf_func = sk_run_filter_int_skb; fp->len = new_len; /* 2nd pass: remap sock_filter insns into sock_filter_int insns. */ @@ -1510,6 +1500,8 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, */ goto out_err_free; + sk_filter_select_runtime(fp); + kfree(old_prog); return fp; @@ -1528,6 +1520,29 @@ void __weak bpf_int_jit_compile(struct sk_filter *prog) { } +/** + * sk_filter_select_runtime - select execution runtime for BPF program + * @fp: sk_filter populated with internal BPF program + * + * try to JIT internal BPF program, if JIT is not available select interpreter + * BPF program will be executed via SK_RUN_FILTER() macro + */ +void sk_filter_select_runtime(struct sk_filter *fp) +{ + fp->bpf_func = (void *) __sk_run_filter; + + /* Probe if internal BPF can be JITed */ + bpf_int_jit_compile(fp); +} +EXPORT_SYMBOL_GPL(sk_filter_select_runtime); + +/* free internal BPF program */ +void sk_filter_free(struct sk_filter *fp) +{ + bpf_jit_free(fp); +} +EXPORT_SYMBOL_GPL(sk_filter_free); + static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, struct sock *sk) { @@ -1548,12 +1563,9 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, /* JIT compiler couldn't process this filter, so do the * internal BPF translation for the optimized interpreter. */ - if (!fp->jited) { + if (!fp->jited) fp = __sk_migrate_filter(fp, sk); - /* Probe if internal BPF can be jit-ed */ - bpf_int_jit_compile(fp); - } return fp; } -- cgit v1.2.3 From ba730340f96c01160b5f26f81e8fb38f8cb1821c Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Thu, 15 May 2014 18:15:31 +0400 Subject: dmaengine: fix comment typo Fix comment typo. Signed-off-by: Alexander Popov Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 8300fb87b84a..cbb168e04dc1 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -292,7 +292,7 @@ struct dma_chan_dev { }; /** - * enum dma_slave_buswidth - defines bus with of the DMA slave + * enum dma_slave_buswidth - defines bus width of the DMA slave * device, source or target buses */ enum dma_slave_buswidth { -- cgit v1.2.3 From 67af9811539be83dbdc0739215d29af23c870405 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 18 May 2014 10:15:24 +0300 Subject: cfg80211: allow RSSI compensation Channels in 2.4GHz band overlap, this means that if we send a probe request on channel 1 and then move to channel 2, we will hear the probe response on channel 2. In this case, the RSSI will be lower than if we had heard it on the channel on which it was sent (1 in this case). The firmware / low level driver can parse the channel in the DS IE or HT IE and compensate the RSSI so that it will still have a valid value even if we heard the frame on an adjacent channel. This can be done up to a certain offset. Add this offset as a configuration for the low level driver. A low level driver that can compensate the low RSSI in this case should assign the maximal offset for which the RSSI value is still valid. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 +++++++ net/wireless/scan.c | 12 ++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a75fabd18502..920ec8c1ce54 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2961,6 +2961,12 @@ struct wiphy_vendor_command { * and probe responses. This value should be set if the driver * wishes to limit the number of csa counters. Default (0) means * infinite. + * @max_adj_channel_rssi_comp: max offset of between the channel on which the + * frame was sent and the channel on which the frame was heard for which + * the reported rssi is still valid. If a driver is able to compensate the + * low rssi when a frame is heard on different channel, then it should set + * this variable to the maximal offset for which it can compensate. + * This value should be set in MHz. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -3079,6 +3085,7 @@ struct wiphy { u16 max_ap_assoc_sta; u8 max_num_csa_counters; + u8 max_adj_channel_rssi_comp; char priv[0] __aligned(NETDEV_ALIGN); }; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0f5da18cc619..77c56eef0574 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -883,6 +883,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, struct cfg80211_bss_ies *ies; struct ieee80211_channel *channel; struct cfg80211_internal_bss tmp = {}, *res; + bool signal_valid; if (WARN_ON(!wiphy)) return NULL; @@ -919,8 +920,9 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, rcu_assign_pointer(tmp.pub.beacon_ies, ies); rcu_assign_pointer(tmp.pub.ies, ies); - res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, - rx_channel == channel); + signal_valid = abs(rx_channel->center_freq - channel->center_freq) <= + wiphy->max_adj_channel_rssi_comp; + res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid); if (!res) return NULL; @@ -944,6 +946,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_bss_ies *ies; struct ieee80211_channel *channel; + bool signal_valid; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); @@ -991,8 +994,9 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); - res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, - rx_channel == channel); + signal_valid = abs(rx_channel->center_freq - channel->center_freq) <= + wiphy->max_adj_channel_rssi_comp; + res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid); if (!res) return NULL; -- cgit v1.2.3 From db885bf82883f9743efe09d91775c579c0ed6842 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:12 +0300 Subject: ARM: edma: Remove queue_tc_mapping data from edma_soc_info It is no longer in use by the driver or board files. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- include/linux/platform_data/edma.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 12f134b1493c..633e196ebdf2 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -175,7 +175,6 @@ struct edma_soc_info { /* Resource reservation for other cores */ struct edma_rsv_info *rsv; - s8 (*queue_tc_mapping)[2]; s8 (*queue_priority_mapping)[2]; const s16 (*xbar_chans)[2]; }; -- cgit v1.2.3 From ba391e5a5ac6697b8bcae8c0d01439cb765d9ef8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 21 May 2014 11:15:56 -0400 Subject: HID: rmi: do not handle touchscreens through hid-rmi Currently, hid-rmi drives every Synaptics product, but the touchscreens on the Windows tablets should be handled through hid-multitouch. Instead of providing a long list of PIDs, rely on the scan_report capability to detect which should go to hid-multitouch, and which should not go to hid-rmi. related bug: https://bugzilla.kernel.org/show_bug.cgi?id=74241 https://bugzilla.redhat.com/show_bug.cgi?id=1089583 Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 10 ++++++++-- drivers/hid/hid-rmi.c | 3 +-- include/linux/hid.h | 8 ++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f05255d92de7..64c71c866916 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -776,6 +776,14 @@ static int hid_scan_report(struct hid_device *hid) (hid->group == HID_GROUP_MULTITOUCH)) hid->group = HID_GROUP_MULTITOUCH_WIN_8; + /* + * Vendor specific handlings + */ + if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) && + (hid->group == HID_GROUP_GENERIC)) + /* hid-rmi should take care of them, not hid-generic */ + hid->group = HID_GROUP_RMI; + vfree(parser); return 0; } @@ -1882,8 +1890,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, - { HID_I2C_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index c529b033ba9e..2451c7e5febd 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -894,8 +894,7 @@ static void rmi_remove(struct hid_device *hdev) } static const struct hid_device_id rmi_id[] = { - { HID_I2C_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) }, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, rmi_id); diff --git a/include/linux/hid.h b/include/linux/hid.h index 54f855b2c902..8ce9ff4d50af 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -299,12 +299,20 @@ struct hid_item { /* * HID device groups + * + * Note: HID_GROUP_ANY is declared in linux/mod_devicetable.h + * and has a value of 0x0000 */ #define HID_GROUP_GENERIC 0x0001 #define HID_GROUP_MULTITOUCH 0x0002 #define HID_GROUP_SENSOR_HUB 0x0003 #define HID_GROUP_MULTITOUCH_WIN_8 0x0004 +/* + * Vendor specific HID device groups + */ +#define HID_GROUP_RMI 0x0100 + /* * This is the global environment of the parser. This information is * persistent for main-items. The global environment can be saved and -- cgit v1.2.3 From 7aa2c016db2162defff77f6f5731bff3f25e5175 Mon Sep 17 00:00:00 2001 From: Dongsheng Yang Date: Thu, 8 May 2014 18:33:49 +0900 Subject: sched: Consolidate open coded implementations of nice level frobbing into nice_to_rlimit() and rlimit_to_nice() Signed-off-by: Dongsheng Yang Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/a568a1e3cc8e78648f41b5035fa5e381d36274da.1399532322.git.yangds.fnst@cn.fujitsu.com Signed-off-by: Ingo Molnar --- drivers/staging/android/binder.c | 2 +- include/linux/sched/prio.h | 16 ++++++++++++++++ kernel/sched/core.c | 2 +- kernel/sys.c | 6 +++--- 4 files changed, 21 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 179b21b66504..9311bb67ec35 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -436,7 +436,7 @@ static void binder_set_nice(long nice) set_user_nice(current, nice); return; } - min_nice = 20 - current->signal->rlim[RLIMIT_NICE].rlim_cur; + min_nice = rlimit_to_nice(current->signal->rlim[RLIMIT_NICE].rlim_cur); binder_debug(BINDER_DEBUG_PRIORITY_CAP, "%d: nice value %ld not allowed use %ld instead\n", current->pid, nice, min_nice); diff --git a/include/linux/sched/prio.h b/include/linux/sched/prio.h index ac322583c820..d9cf5a5762d9 100644 --- a/include/linux/sched/prio.h +++ b/include/linux/sched/prio.h @@ -41,4 +41,20 @@ #define TASK_USER_PRIO(p) USER_PRIO((p)->static_prio) #define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)) +/* + * Convert nice value [19,-20] to rlimit style value [1,40]. + */ +static inline long nice_to_rlimit(long nice) +{ + return (MAX_NICE - nice + 1); +} + +/* + * Convert rlimit style value [1,40] to nice value [-20, 19]. + */ +static inline long rlimit_to_nice(long prio) +{ + return (MAX_NICE - prio + 1); +} + #endif /* _SCHED_PRIO_H */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index da302ca98f60..321d800e4baa 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3033,7 +3033,7 @@ EXPORT_SYMBOL(set_user_nice); int can_nice(const struct task_struct *p, const int nice) { /* convert nice value [19,-20] to rlimit style value [1,40] */ - int nice_rlim = 20 - nice; + int nice_rlim = nice_to_rlimit(nice); return (nice_rlim <= task_rlimit(p, RLIMIT_NICE) || capable(CAP_SYS_NICE)); diff --git a/kernel/sys.c b/kernel/sys.c index fba0f29401ea..66a751ebf9d9 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -250,7 +250,7 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) else p = current; if (p) { - niceval = 20 - task_nice(p); + niceval = nice_to_rlimit(task_nice(p)); if (niceval > retval) retval = niceval; } @@ -261,7 +261,7 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) else pgrp = task_pgrp(current); do_each_pid_thread(pgrp, PIDTYPE_PGID, p) { - niceval = 20 - task_nice(p); + niceval = nice_to_rlimit(task_nice(p)); if (niceval > retval) retval = niceval; } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); @@ -277,7 +277,7 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) do_each_thread(g, p) { if (uid_eq(task_uid(p), uid)) { - niceval = 20 - task_nice(p); + niceval = nice_to_rlimit(task_nice(p)); if (niceval > retval) retval = niceval; } -- cgit v1.2.3 From 4027d080854d1be96ef134a1c3024d5276114db6 Mon Sep 17 00:00:00 2001 From: "xiaofeng.yan" Date: Fri, 9 May 2014 03:21:27 +0000 Subject: sched/rt: Fix 'struct sched_dl_entity' and dl_task_time() comments, to match the current upstream code Signed-off-by: xiaofeng.yan Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1399605687-18094-1-git-send-email-xiaofeng.yan@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 4 ++-- kernel/sched/deadline.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 725eef121c9f..0f91d00efd87 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1175,8 +1175,8 @@ struct sched_dl_entity { /* * Original scheduling parameters. Copied here from sched_attr - * during sched_setscheduler2(), they will remain the same until - * the next sched_setscheduler2(). + * during sched_setattr(), they will remain the same until + * the next sched_setattr(). */ u64 dl_runtime; /* maximum runtime for each instance */ u64 dl_deadline; /* relative deadline of each instance */ diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index e0a04ae1e0dd..f9ca7d19781a 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -520,7 +520,7 @@ static enum hrtimer_restart dl_task_timer(struct hrtimer *timer) * We need to take care of a possible races here. In fact, the * task might have changed its scheduling policy to something * different from SCHED_DEADLINE or changed its reservation - * parameters (through sched_setscheduler()). + * parameters (through sched_setattr()). */ if (!dl_task(p) || dl_se->dl_new) goto unlock; -- cgit v1.2.3 From 903ed4913c7fe78d2746445564634264291c7493 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:20 +0300 Subject: ARM: edma: Remove redundant/unused parameters from edma_soc_info The following parameters are no longer needed by the edma driver since the information can be obtained from the IP's CCCFG register: n_channel, n_region, n_slot and n_tc. Remove the n_cc as well since in this context it has no meaning. We have separate edma_soc_info struct/eDMA3_CC instance so this member does not make any sense (and the driver no longer uses it). Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- include/linux/platform_data/edma.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 633e196ebdf2..eb8d5627d080 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -158,13 +158,6 @@ struct edma_rsv_info { /* platform_data for EDMA driver */ struct edma_soc_info { - - /* how many dma resources of each type */ - unsigned n_channel; - unsigned n_region; - unsigned n_slot; - unsigned n_tc; - unsigned n_cc; /* * Default queue is expected to be a low-priority queue. * This way, long transfers on the default queue started -- cgit v1.2.3 From bf3b5ec66bd03d66e9ea729aaca013ea1047a797 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 25 Apr 2014 12:55:30 +0100 Subject: mmc: sdio_irq: rework sdio irq handling Rather than the SDIO support spawning it's own thread for handling card interrupts, use the generic IRQ infrastructure for this, triggering it from the host interface's interrupt handling directly. This avoids a race between the parent thread waiting to receive an interrupt response from the card, and the slow startup from the sdio irq thread, which can occur as a result of high system load (eg, while udev is running.) Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren [Ulf Hansson] Resolved conflict Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sdio_irq.c | 41 +++++++++++++++++++++++++++++++---------- include/linux/mmc/host.h | 3 +++ 2 files changed, 34 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index aaa90460ed23..5cc13c8d35bb 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -90,6 +90,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host) return ret; } +void sdio_run_irqs(struct mmc_host *host) +{ + mmc_claim_host(host); + host->sdio_irq_pending = true; + process_sdio_pending_irqs(host); + mmc_release_host(host); +} +EXPORT_SYMBOL_GPL(sdio_run_irqs); + static int sdio_irq_thread(void *_host) { struct mmc_host *host = _host; @@ -189,14 +198,20 @@ static int sdio_card_irq_get(struct mmc_card *card) WARN_ON(!host->claimed); if (!host->sdio_irqs++) { - atomic_set(&host->sdio_irq_thread_abort, 0); - host->sdio_irq_thread = - kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", - mmc_hostname(host)); - if (IS_ERR(host->sdio_irq_thread)) { - int err = PTR_ERR(host->sdio_irq_thread); - host->sdio_irqs--; - return err; + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { + atomic_set(&host->sdio_irq_thread_abort, 0); + host->sdio_irq_thread = + kthread_run(sdio_irq_thread, host, + "ksdioirqd/%s", mmc_hostname(host)); + if (IS_ERR(host->sdio_irq_thread)) { + int err = PTR_ERR(host->sdio_irq_thread); + host->sdio_irqs--; + return err; + } + } else { + mmc_host_clk_hold(host); + host->ops->enable_sdio_irq(host, 1); + mmc_host_clk_release(host); } } @@ -211,8 +226,14 @@ static int sdio_card_irq_put(struct mmc_card *card) BUG_ON(host->sdio_irqs < 1); if (!--host->sdio_irqs) { - atomic_set(&host->sdio_irq_thread_abort, 1); - kthread_stop(host->sdio_irq_thread); + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { + atomic_set(&host->sdio_irq_thread_abort, 1); + kthread_stop(host->sdio_irq_thread); + } else { + mmc_host_clk_hold(host); + host->ops->enable_sdio_irq(host, 0); + mmc_host_clk_release(host); + } } return 0; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index cd595275e118..7960424d0bc0 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -282,6 +282,7 @@ struct mmc_host { #define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */ #define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \ MMC_CAP2_HS400_1_2V) +#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17) mmc_pm_flag_t pm_caps; /* supported pm features */ @@ -397,6 +398,8 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) wake_up_process(host->sdio_irq_thread); } +void sdio_run_irqs(struct mmc_host *host); + #ifdef CONFIG_REGULATOR int mmc_regulator_get_ocrmask(struct regulator *supply); int mmc_regulator_set_ocr(struct mmc_host *mmc, -- cgit v1.2.3 From 781e989cf593c71d26bdca74f5e77b3651fc060e Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 25 Apr 2014 12:55:46 +0100 Subject: mmc: sdhci: convert to new SDIO IRQ handling Use a generic threaded interrupt handler for SDIO interrupt handling, rather than allowing the SDIO core code to buggily spawn its own thread. This results in host drivers to be more in control of how SDIO interrupts are acknowledged in the hardware, rather than having the internals of the SDIO core placed upon them, possibly resulting in sub-standard handling. At least one SDHCI implementation specifies a very specific sequence to deal with a card interrupt. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 62 +++++++++++++++++++++++++++++------------------ include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 41 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4b6cca2130bc..4a0622d52ae5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2428,10 +2428,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) static irqreturn_t sdhci_irq(int irq, void *dev_id) { - irqreturn_t result; + irqreturn_t result = IRQ_NONE; struct sdhci_host *host = dev_id; u32 intmask, mask, unexpected = 0; - int cardint = 0, max_loops = 16; + int max_loops = 16; spin_lock(&host->lock); @@ -2490,8 +2490,11 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) pr_err("%s: Card is consuming too much power!\n", mmc_hostname(host->mmc)); - if (intmask & SDHCI_INT_CARD_INT) - cardint = 1; + if (intmask & SDHCI_INT_CARD_INT) { + sdhci_enable_sdio_irq_nolock(host, false); + host->thread_isr |= SDHCI_INT_CARD_INT; + result = IRQ_WAKE_THREAD; + } intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | @@ -2503,17 +2506,10 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) sdhci_writel(host, intmask, SDHCI_INT_STATUS); } - result = IRQ_HANDLED; + if (result == IRQ_NONE) + result = IRQ_HANDLED; intmask = sdhci_readl(host, SDHCI_INT_STATUS); - - /* - * If we know we'll call the driver to signal SDIO IRQ, - * disregard further indications of Card Interrupt in - * the status to avoid a needless loop. - */ - if (cardint) - intmask &= ~SDHCI_INT_CARD_INT; } while (intmask && --max_loops); out: spin_unlock(&host->lock); @@ -2523,15 +2519,33 @@ out: mmc_hostname(host->mmc), unexpected); sdhci_dumpregs(host); } - /* - * We have to delay this as it calls back into the driver. - */ - if (cardint) - mmc_signal_sdio_irq(host->mmc); return result; } +static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) +{ + struct sdhci_host *host = dev_id; + unsigned long flags; + u32 isr; + + spin_lock_irqsave(&host->lock, flags); + isr = host->thread_isr; + host->thread_isr = 0; + spin_unlock_irqrestore(&host->lock, flags); + + if (isr & SDHCI_INT_CARD_INT) { + sdio_run_irqs(host->mmc); + + spin_lock_irqsave(&host->lock, flags); + if (host->flags & SDHCI_SDIO_IRQ_ENABLED) + sdhci_enable_sdio_irq_nolock(host, true); + spin_unlock_irqrestore(&host->lock, flags); + } + + return isr ? IRQ_HANDLED : IRQ_NONE; +} + /*****************************************************************************\ * * * Suspend/resume * @@ -2601,8 +2615,9 @@ int sdhci_resume_host(struct sdhci_host *host) } if (!device_may_wakeup(mmc_dev(host->mmc))) { - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(host->mmc), host); + ret = request_threaded_irq(host->irq, sdhci_irq, + sdhci_thread_irq, IRQF_SHARED, + mmc_hostname(host->mmc), host); if (ret) return ret; } else { @@ -2681,7 +2696,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); spin_unlock_irqrestore(&host->lock, flags); - synchronize_irq(host->irq); + synchronize_hardirq(host->irq); spin_lock_irqsave(&host->lock, flags); host->runtime_suspended = true; @@ -2937,6 +2952,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) host->flags |= SDHCI_AUTO_CMD12; @@ -3226,8 +3242,8 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_init(host, 0); - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(mmc), host); + ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, + IRQF_SHARED, mmc_hostname(mmc), host); if (ret) { pr_err("%s: Failed to request IRQ %d: %d\n", mmc_hostname(mmc), host->irq, ret); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 7be12b883485..d1aa97b77dd9 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -177,6 +177,8 @@ struct sdhci_host { unsigned int ocr_avail_mmc; u32 ocr_mask; /* available voltages */ + u32 thread_isr; + wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ -- cgit v1.2.3 From 3560db8e247aa35bc6b287ec7ec51cd41abd512e Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 25 Apr 2014 12:55:51 +0100 Subject: mmc: sdhci: push card_tasklet into threaded irq handler There's no requirement to have the card tasklet separate now that we have a threaded interrupt handler, so kill this and move the called code into the threaded part of the handler. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 23 +++++++++-------------- include/linux/mmc/sdhci.h | 3 +-- 2 files changed, 10 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4a0622d52ae5..8def3919b32c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2128,15 +2128,6 @@ static const struct mmc_host_ops sdhci_ops = { * * \*****************************************************************************/ -static void sdhci_tasklet_card(unsigned long param) -{ - struct sdhci_host *host = (struct sdhci_host*)param; - - sdhci_card_event(host->mmc); - - mmc_detect_change(host->mmc, msecs_to_jiffies(200)); -} - static void sdhci_tasklet_finish(unsigned long param) { struct sdhci_host *host; @@ -2477,7 +2468,10 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); - tasklet_schedule(&host->card_tasklet); + + host->thread_isr |= intmask & (SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE); + result = IRQ_WAKE_THREAD; } if (intmask & SDHCI_INT_CMD_MASK) @@ -2534,6 +2528,11 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) host->thread_isr = 0; spin_unlock_irqrestore(&host->lock, flags); + if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { + sdhci_card_event(host->mmc); + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + } + if (isr & SDHCI_INT_CARD_INT) { sdio_run_irqs(host->mmc); @@ -3224,8 +3223,6 @@ int sdhci_add_host(struct sdhci_host *host) /* * Init tasklets. */ - tasklet_init(&host->card_tasklet, - sdhci_tasklet_card, (unsigned long)host); tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host); @@ -3290,7 +3287,6 @@ reset: free_irq(host->irq, host); #endif untasklet: - tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); return ret; @@ -3334,7 +3330,6 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) del_timer_sync(&host->timer); - tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); if (host->vmmc) { diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index d1aa97b77dd9..f1c8e14e8751 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -164,8 +164,7 @@ struct sdhci_host { dma_addr_t adma_addr; /* Mapped ADMA descr. table */ dma_addr_t align_addr; /* Mapped bounce buffer */ - struct tasklet_struct card_tasklet; /* Tasklet structures */ - struct tasklet_struct finish_tasklet; + struct tasklet_struct finish_tasklet; /* Tasklet structures */ struct timer_list timer; /* Timer for timeouts */ -- cgit v1.2.3 From b537f94ce19583de1882f539a5cc49aa99260aca Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 25 Apr 2014 12:56:01 +0100 Subject: mmc: sdhci: more efficient interrupt enable register handling Rather than wasting cycles read-modify-writing the interrupt enable registers, cache the value locally instead. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 98 +++++++++++++++++++++++------------------------ include/linux/mmc/sdhci.h | 3 ++ 2 files changed, 50 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0ecbcc4c29d2..4a98ee29d136 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -131,27 +131,6 @@ static void sdhci_dumpregs(struct sdhci_host *host) * * \*****************************************************************************/ -static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) -{ - u32 ier; - - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - ier &= ~clear; - ier |= set; - sdhci_writel(host, ier, SDHCI_INT_ENABLE); - sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); -} - -static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs) -{ - sdhci_clear_set_irqs(host, 0, irqs); -} - -static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs) -{ - sdhci_clear_set_irqs(host, irqs, 0); -} - static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) { u32 present, irqs; @@ -165,9 +144,12 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT; if (enable) - sdhci_unmask_irqs(host, irqs); + host->ier |= irqs; else - sdhci_mask_irqs(host, irqs); + host->ier &= ~irqs; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } static void sdhci_enable_card_detection(struct sdhci_host *host) @@ -183,17 +165,12 @@ static void sdhci_disable_card_detection(struct sdhci_host *host) static void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; - u32 uninitialized_var(ier); - if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) return; } - if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - if (host->ops->platform_reset_enter) host->ops->platform_reset_enter(host, mask); @@ -224,8 +201,10 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) if (host->ops->platform_reset_exit) host->ops->platform_reset_exit(host, mask); - if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) - sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); + if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) { + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + } if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) @@ -242,11 +221,14 @@ static void sdhci_init(struct sdhci_host *host, int soft) else sdhci_reset(host, SDHCI_RESET_ALL); - sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, - SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | - SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | - SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | - SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE); + host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | + SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | + SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | + SDHCI_INT_RESPONSE; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); if (soft) { /* force clock reconfiguration */ @@ -721,9 +703,12 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; if (host->flags & SDHCI_REQ_USE_DMA) - sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); + host->ier = (host->ier & ~pio_irqs) | dma_irqs; else - sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); + host->ier = (host->ier & ~dma_irqs) | pio_irqs; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) @@ -1713,9 +1698,12 @@ static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { if (!(host->flags & SDHCI_DEVICE_DEAD)) { if (enable) - sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); + host->ier |= SDHCI_INT_CARD_INT; else - sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); + host->ier &= ~SDHCI_INT_CARD_INT; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); mmiowb(); } } @@ -1857,7 +1845,6 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host; u16 ctrl; - u32 ier; int tuning_loop_counter = MAX_TUNING_LOOP; unsigned long timeout; int err = 0; @@ -1911,8 +1898,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * to make sure we don't hit a controller bug, we _only_ * enable Buffer Read Ready interrupt here. */ - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL); + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); /* * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number @@ -2047,7 +2034,8 @@ out: if (err && (host->flags & SDHCI_USING_RETUNING_TIMER)) err = 0; - sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); spin_unlock_irqrestore(&host->lock, flags); sdhci_runtime_pm_put(host); @@ -2460,10 +2448,12 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) * More testing are needed here to ensure it works * for other platforms though. */ - sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT : - SDHCI_INT_CARD_REMOVE); - sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE : - SDHCI_INT_CARD_INSERT); + host->ier &= ~(SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE); + host->ier |= present ? SDHCI_INT_CARD_REMOVE : + SDHCI_INT_CARD_INSERT; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); @@ -2592,7 +2582,9 @@ int sdhci_suspend_host(struct sdhci_host *host) } if (!device_may_wakeup(mmc_dev(host->mmc))) { - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + host->ier = 0; + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); } else { sdhci_enable_irq_wakeups(host); @@ -2691,7 +2683,9 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) } spin_lock_irqsave(&host->lock, flags); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK & ~SDHCI_INT_CARD_INT); + host->ier &= SDHCI_INT_CARD_INT; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); spin_unlock_irqrestore(&host->lock, flags); synchronize_hardirq(host->irq); @@ -3282,7 +3276,8 @@ int sdhci_add_host(struct sdhci_host *host) #ifdef SDHCI_USE_LEDS_CLASS reset: sdhci_reset(host, SDHCI_RESET_ALL); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); #endif untasklet: @@ -3324,7 +3319,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) if (!dead) sdhci_reset(host, SDHCI_RESET_ALL); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); del_timer_sync(&host->timer); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index f1c8e14e8751..9361d8ef509d 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -178,6 +178,9 @@ struct sdhci_host { u32 thread_isr; + /* cached registers */ + u32 ier; + wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ -- cgit v1.2.3 From 0718e59ae259f7c48155b4e852d8b0632d59028e Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 25 Apr 2014 12:57:18 +0100 Subject: mmc: sdhci: move FSL ESDHC reset handling quirk into esdhc code The Freescale esdhc driver is the only driver which needs the interrupt registers restored after a reset. Move this quirk to be part of the ESDHC driver implementation. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 10 +++++++++- drivers/mmc/host/sdhci-esdhc.h | 3 +-- drivers/mmc/host/sdhci.c | 5 ----- include/linux/mmc/sdhci.h | 2 -- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index b1d74fa33c5f..812c5772d900 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -876,6 +876,14 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) return esdhc_change_pinstate(host, uhs); } +static void esdhc_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +} + static struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, @@ -888,7 +896,7 @@ static struct sdhci_ops sdhci_esdhc_ops = { .get_ro = esdhc_pltfm_get_ro, .set_bus_width = esdhc_pltfm_set_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, - .reset = sdhci_reset, + .reset = esdhc_reset, }; static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a7d9f95a7b03..de69bddc3afc 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -22,8 +22,7 @@ SDHCI_QUIRK_NO_BUSY_IRQ | \ SDHCI_QUIRK_NONSTANDARD_CLOCK | \ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ - SDHCI_QUIRK_PIO_NEEDS_DELAY | \ - SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) + SDHCI_QUIRK_PIO_NEEDS_DELAY) #define ESDHC_SYSTEM_CONTROL 0x2c #define ESDHC_CLOCK_MASK 0x0000fff0 diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5e25147e92f7..074157e8e73d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -203,11 +203,6 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask) host->ops->reset(host, mask); - if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) { - sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); - sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); - } - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) host->ops->enable_dma(host); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 9361d8ef509d..02919ef99419 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -61,8 +61,6 @@ struct sdhci_host { #define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) /* Controller does not like fast PIO transfers */ #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) -/* Controller losing signal/interrupt enable states after reset */ -#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) /* Controller has to be forced to use block size of 2048 bytes */ #define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) /* Controller cannot do multi-block transfers */ -- cgit v1.2.3 From 1771059cf5f9c09e37ef6315df8acf120f2642fc Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 25 Apr 2014 12:58:55 +0100 Subject: mmc: sdhci: convert sdhci_set_clock() into a library function Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-acpi.c | 2 ++ drivers/mmc/host/sdhci-bcm-kona.c | 1 + drivers/mmc/host/sdhci-bcm2835.c | 1 + drivers/mmc/host/sdhci-cns3xxx.c | 3 +-- drivers/mmc/host/sdhci-dove.c | 1 + drivers/mmc/host/sdhci-esdhc.h | 1 - drivers/mmc/host/sdhci-of-arasan.c | 1 + drivers/mmc/host/sdhci-of-hlwd.c | 1 + drivers/mmc/host/sdhci-pci.c | 1 + drivers/mmc/host/sdhci-pltfm.c | 1 + drivers/mmc/host/sdhci-pxav2.c | 1 + drivers/mmc/host/sdhci-pxav3.c | 1 + drivers/mmc/host/sdhci-s3c.c | 19 ++++++++++++++----- drivers/mmc/host/sdhci-sirf.c | 1 + drivers/mmc/host/sdhci-spear.c | 1 + drivers/mmc/host/sdhci-tegra.c | 1 + drivers/mmc/host/sdhci.c | 17 ++++++----------- drivers/mmc/host/sdhci.h | 1 + include/linux/mmc/sdhci.h | 2 -- 19 files changed, 36 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index aca84a682551..323e2a688563 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -102,12 +102,14 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) } static const struct sdhci_ops sdhci_acpi_ops_dflt = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_acpi_enable_dma, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; static const struct sdhci_ops sdhci_acpi_ops_int = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_acpi_enable_dma, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 7b97bfab910d..e610811c09b0 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -206,6 +206,7 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host, } static struct sdhci_ops sdhci_bcm_kona_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_bcm_kona_get_max_clk, .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock, .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 289b1c80d5fc..74906d6008e1 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -131,6 +131,7 @@ static const struct sdhci_ops bcm2835_sdhci_ops = { .read_l = bcm2835_sdhci_readl, .read_w = bcm2835_sdhci_readw, .read_b = bcm2835_sdhci_readb, + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_min_clock = bcm2835_sdhci_get_min_clock, .set_bus_width = sdhci_set_bus_width, diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index 416f4a4c2e35..587d73ef33ff 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -89,8 +89,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_INVERTED_WRITE_PROTECT | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | - SDHCI_QUIRK_NONSTANDARD_CLOCK, + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, }; static int sdhci_cns3xxx_probe(struct platform_device *pdev) diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 1408cc11d881..8ef4ab52f8e0 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -86,6 +86,7 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) static const struct sdhci_ops sdhci_dove_ops = { .read_w = sdhci_dove_readw, .read_l = sdhci_dove_readl, + .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index de69bddc3afc..3497cfaf683c 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -20,7 +20,6 @@ #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ SDHCI_QUIRK_NO_BUSY_IRQ | \ - SDHCI_QUIRK_NONSTANDARD_CLOCK | \ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ SDHCI_QUIRK_PIO_NEEDS_DELAY) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index faef21740584..f0ee594f25d1 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -52,6 +52,7 @@ static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) } static struct sdhci_ops sdhci_arasan_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_timeout_clock = sdhci_arasan_get_timeout_clock, .set_bus_width = sdhci_set_bus_width, diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index fb01958cb18e..a4a1f0f2c0a0 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -58,6 +58,7 @@ static const struct sdhci_ops sdhci_hlwd_ops = { .write_l = sdhci_hlwd_writel, .write_w = sdhci_hlwd_writew, .write_b = sdhci_hlwd_writeb, + .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 87f9dd91f68c..b3a28f6b170e 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1078,6 +1078,7 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) } static const struct sdhci_ops sdhci_pci_ops = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_pci_set_bus_width, .reset = sdhci_reset, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index bfbf467b61c7..1fb89f44bd58 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -45,6 +45,7 @@ unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host) EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock); static const struct sdhci_ops sdhci_pltfm_ops = { + .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 2eee0c8b88eb..db5257bf032e 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -112,6 +112,7 @@ static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) } static const struct sdhci_ops pxav2_sdhci_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = pxav2_mmc_set_bus_width, .reset = pxav2_reset, diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 86564233ae93..8a40e079a57e 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -225,6 +225,7 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) } static const struct sdhci_ops pxav3_sdhci_ops = { + .set_clock = sdhci_set_clock, .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .get_max_clock = sdhci_pltfm_clk_get_max_clock, diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 9d710b748b9c..9e6f1c52982c 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -55,6 +55,8 @@ struct sdhci_s3c { struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; unsigned long clk_rates[MAX_BUS_CLK]; + + bool no_divider; }; /** @@ -67,6 +69,7 @@ struct sdhci_s3c { */ struct sdhci_s3c_drv_data { unsigned int sdhci_quirks; + bool no_divider; }; static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) @@ -116,7 +119,7 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, * If controller uses a non-standard clock division, find the best clock * speed possible with selected clock source and skip the division. */ - if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + if (ourhost->no_divider) { rate = clk_round_rate(clksrc, wanted); return wanted - rate; } @@ -161,8 +164,10 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) host->mmc->actual_clock = 0; /* don't bother if the clock is going off. */ - if (clock == 0) + if (clock == 0) { + sdhci_set_clock(host, clock); return; + } for (src = 0; src < MAX_BUS_CLK; src++) { delta = sdhci_s3c_consider_clock(ourhost, src, clock); @@ -214,6 +219,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) if (clock < 25 * 1000000) ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2); writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3); + + sdhci_set_clock(host, clock); } /** @@ -603,8 +610,10 @@ static int sdhci_s3c_probe(struct platform_device *pdev) /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; - if (drv_data) + if (drv_data) { host->quirks |= drv_data->sdhci_quirks; + sc->no_divider = drv_data->no_divider; + } #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -653,7 +662,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev) * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + if (sc->no_divider) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; @@ -794,7 +803,7 @@ static const struct dev_pm_ops sdhci_s3c_pmops = { #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { - .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK, + .no_divider = true, }; #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data) #else diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 5d79e10e1ba2..3b775348b470 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -28,6 +28,7 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) } static struct sdhci_ops sdhci_sirf_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_sirf_get_max_clk, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index c2a2bedc8813..8bf64ab36720 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -38,6 +38,7 @@ struct spear_sdhci { /* sdhci ops */ static const struct sdhci_ops sdhci_pltfm_ops = { + .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 7754c0319fda..a0a8b5cc3b0c 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -153,6 +153,7 @@ static const struct sdhci_ops tegra_sdhci_ops = { .read_l = tegra_sdhci_readl, .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, + .set_clock = sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, .reset = tegra_sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d9b91fc17bb0..69e58d071b33 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1112,19 +1112,13 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) return preset; } -static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div = 0; /* Initialized for compiler warning */ int real_div = div, clk_mul = 1; u16 clk = 0; unsigned long timeout; - if (host->ops->set_clock) { - host->ops->set_clock(host, clock); - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) - return; - } - host->mmc->actual_clock = 0; sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); @@ -1221,6 +1215,7 @@ clock_set: clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } +EXPORT_SYMBOL_GPL(sdhci_set_clock); static int sdhci_set_power(struct sdhci_host *host, unsigned short power) { @@ -1439,7 +1434,7 @@ 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) { - sdhci_set_clock(host, ios->clock); + host->ops->set_clock(host, ios->clock); host->clock = ios->clock; } @@ -1510,7 +1505,7 @@ 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 */ - sdhci_set_clock(host, host->clock); + host->ops->set_clock(host, host->clock); } @@ -1555,7 +1550,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } /* Re-enable SD Clock */ - sdhci_set_clock(host, host->clock); + host->ops->set_clock(host, host->clock); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -2129,7 +2124,7 @@ static void sdhci_tasklet_finish(unsigned long param) /* Some controllers need this kick or reset won't work here */ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) /* This is to force an update */ - sdhci_set_clock(host, host->clock); + host->ops->set_clock(host, host->clock); /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 7d84cb3b0e00..ac20195f667b 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -400,6 +400,7 @@ static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED); } +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); void sdhci_set_bus_width(struct sdhci_host *host, int width); void sdhci_reset(struct sdhci_host *host, u8 mask); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 02919ef99419..72a90baf111f 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -57,8 +57,6 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) /* Controller reports inverted write-protect state */ #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) -/* Controller has nonstandard clock management */ -#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) /* Controller does not like fast PIO transfers */ #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) /* Controller has to be forced to use block size of 2048 bytes */ -- cgit v1.2.3 From dc93f7b68a039fe7d918613163b1cdd21b279ec5 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 21 May 2014 10:49:19 +0800 Subject: MIPS: Fix a typo error in AUDIT_ARCH definition Missing a "|" in AUDIT_ARCH_MIPSEL64N32 macro definition. Signed-off-by: Huacai Chen Reviewed-by: Markos Chandras Cc: John Crispin Cc: Steven J. Hill Cc: Aurelien Jarno Cc: linux-mips@linux-mips.org Cc: Fuxin Zhang Cc: Zhangjin Wu Patchwork: https://patchwork.linux-mips.org/patch/6978/ Signed-off-by: Ralf Baechle --- include/uapi/linux/audit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 1b1efddb91cd..4c31a366be16 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -357,7 +357,7 @@ enum { #define AUDIT_ARCH_MIPS64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|\ __AUDIT_ARCH_CONVENTION_MIPS64_N32) #define AUDIT_ARCH_MIPSEL64 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) -#define AUDIT_ARCH_MIPSEL64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE\ +#define AUDIT_ARCH_MIPSEL64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE|\ __AUDIT_ARCH_CONVENTION_MIPS64_N32) #define AUDIT_ARCH_OPENRISC (EM_OPENRISC) #define AUDIT_ARCH_PARISC (EM_PARISC) -- cgit v1.2.3 From d975f121011a58223c7936ab483c3374a83236c3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 25 Apr 2014 12:59:31 +0100 Subject: mmc: sdhci: cache timing information locally Rather than reading back the timing information from the registers, cache it locally. This allows implementations to translate the UHS timing by overriding the set_uhs_signaling() method as required without also having to emulate the SDHCI_HOST_CONTROL2 register. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren [Ulf Hansson] Resolved conflict Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 23 ++++++++++++----------- include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0073aae0adcb..956799c75df2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1083,24 +1083,23 @@ static void sdhci_finish_command(struct sdhci_host *host) static u16 sdhci_get_preset_value(struct sdhci_host *host) { - u16 ctrl, preset = 0; + u16 preset = 0; - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - - switch (ctrl & SDHCI_CTRL_UHS_MASK) { - case SDHCI_CTRL_UHS_SDR12: + switch (host->timing) { + case MMC_TIMING_UHS_SDR12: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12); break; - case SDHCI_CTRL_UHS_SDR25: + case MMC_TIMING_UHS_SDR25: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25); break; - case SDHCI_CTRL_UHS_SDR50: + case MMC_TIMING_UHS_SDR50: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50); break; - case SDHCI_CTRL_UHS_SDR104: + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104); break; - case SDHCI_CTRL_UHS_DDR50: + case MMC_TIMING_UHS_DDR50: preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50); break; default: @@ -1538,6 +1537,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); host->ops->set_uhs_signaling(host, ios->timing); + host->timing = ios->timing; if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && ((ios->timing == MMC_TIMING_UHS_SDR12) || @@ -1842,12 +1842,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * If the Host Controller supports the HS200 mode then the * tuning function has to be executed. */ - if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) && + if (host->timing == MMC_TIMING_UHS_SDR50 && (host->flags & SDHCI_SDR50_NEEDS_TUNING || host->flags & SDHCI_SDR104_NEEDS_TUNING)) requires_tuning_nonuhs = true; - if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) || + if (host->timing == MMC_TIMING_MMC_HS200 || + host->timing == MMC_TIMING_UHS_SDR104 || requires_tuning_nonuhs) ctrl |= SDHCI_CTRL_EXEC_TUNING; else { diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 72a90baf111f..7f3efbab8732 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -172,6 +172,8 @@ struct sdhci_host { unsigned int ocr_avail_mmc; u32 ocr_mask; /* available voltages */ + unsigned timing; /* Current timing */ + u32 thread_isr; /* cached registers */ -- cgit v1.2.3 From da91a8f9c0f56d75b35bfe2e2456187ab55b3639 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 25 Apr 2014 13:00:12 +0100 Subject: mmc: sdhci: track whether preset mode is currently enabled in hardware Track whether preset mode is currently enabled in hardware, and use that when making decisions elsewhere in the code rather than reading the register and checking the bit. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 44 ++++++++++++++++++++++++++------------------ include/linux/mmc/sdhci.h | 1 + 2 files changed, 27 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index effd9e5d1d81..447eef8217c7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -205,9 +205,14 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask) host->ops->reset(host, mask); - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { - if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) - host->ops->enable_dma(host); + if (mask & SDHCI_RESET_ALL) { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if (host->ops->enable_dma) + host->ops->enable_dma(host); + } + + /* Resetting the controller clears many */ + host->preset_enabled = false; } } @@ -1126,8 +1131,7 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) return; if (host->version >= SDHCI_SPEC_300) { - if (sdhci_readw(host, SDHCI_HOST_CONTROL2) & - SDHCI_CTRL_PRESET_VAL_ENABLE) { + if (host->preset_enabled) { u16 pre_val; clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); @@ -1493,13 +1497,13 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) (ios->timing == MMC_TIMING_UHS_SDR25)) ctrl |= SDHCI_CTRL_HISPD; - ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); - if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) { + if (!host->preset_enabled) { sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* * We only need to set Driver Strength if the * preset value enable is not set. */ + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; @@ -2018,26 +2022,30 @@ out: static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) { - u16 ctrl; - /* Host Controller v3.00 defines preset value registers */ if (host->version < SDHCI_SPEC_300) return; - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - /* * We only enable or disable Preset Value if they are not already * enabled or disabled respectively. Otherwise, we bail out. */ - if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { - ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; - sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - host->flags |= SDHCI_PV_ENABLED; - } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { - ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + if (host->preset_enabled != enable) { + u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + if (enable) + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; + else + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - host->flags &= ~SDHCI_PV_ENABLED; + + if (enable) + host->flags |= SDHCI_PV_ENABLED; + else + host->flags &= ~SDHCI_PV_ENABLED; + + host->preset_enabled = enable; } } diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 7f3efbab8732..08abe9941884 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -143,6 +143,7 @@ struct sdhci_host { bool runtime_suspended; /* Host is runtime suspended */ bool bus_on; /* Bus power prevents runtime suspend */ + bool preset_enabled; /* Preset is enabled */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ -- cgit v1.2.3 From ee526d515ad12e9fee2d2dbfc7f626c0a5c7f417 Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Fri, 9 May 2014 22:16:53 +0530 Subject: mmc: omap_hsmmc: split omap-dma header file moving dmaengine consumer specific function to omap-dmaengine.h to Resolve build failure seen with sh-allmodconfig: include/linux/omap-dma.h:171:8: error: expected identifier before numeric constant make[4]: *** [drivers/mmc/host/omap_hsmmc.o] Error 1 Cc: Russell King - ARM Linux Cc: Tony Lindgren Signed-off-by: Balaji T K Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 2 +- include/linux/omap-dma.h | 19 +------------------ include/linux/omap-dmaengine.h | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 19 deletions(-) create mode 100644 include/linux/omap-dmaengine.h (limited to 'include') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index cba71d69a79c..6b7b75585926 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h index 41a13e70f41f..999f52d3d1e7 100644 --- a/include/linux/omap-dma.h +++ b/include/linux/omap-dma.h @@ -1,23 +1,6 @@ -/* - * OMAP DMA Engine support - * - * 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_OMAP_DMA_H #define __LINUX_OMAP_DMA_H - -struct dma_chan; - -#if defined(CONFIG_DMA_OMAP) || defined(CONFIG_DMA_OMAP_MODULE) -bool omap_dma_filter_fn(struct dma_chan *, void *); -#else -static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d) -{ - return false; -} -#endif +#include /* * Legacy OMAP DMA handling defines and functions diff --git a/include/linux/omap-dmaengine.h b/include/linux/omap-dmaengine.h new file mode 100644 index 000000000000..2b0b6aa01922 --- /dev/null +++ b/include/linux/omap-dmaengine.h @@ -0,0 +1,21 @@ +/* + * OMAP DMA Engine support + * + * 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_OMAP_DMAENGINE_H +#define __LINUX_OMAP_DMAENGINE_H + +struct dma_chan; + +#if defined(CONFIG_DMA_OMAP) || defined(CONFIG_DMA_OMAP_MODULE) +bool omap_dma_filter_fn(struct dma_chan *, void *); +#else +static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d) +{ + return false; +} +#endif +#endif /* __LINUX_OMAP_DMAENGINE_H */ -- cgit v1.2.3 From 73e4354444eef5251e5cdfd388ab02ef9f2e727e Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Thu, 22 May 2014 16:42:41 +0800 Subject: workqueue: declare system_highpri_wq system_highpri_wq is exported to modules via EXPORT_SYMBOL_GPL(), but it was forgotten to be declared in workqueue.h. So we add the declaration and a short description for it. tj: Minor comment tweak. Signed-off-by: Lai Jiangshan Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index d93d28b2ec73..b263b29bd98b 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -340,6 +340,9 @@ enum { * short queue flush time. Don't queue works which can run for too * long. * + * system_highpri_wq is similar to system_wq but for work items which + * require WQ_HIGHPRI. + * * system_long_wq is similar to system_wq but may host long running * works. Queue flushing might take relatively long. * @@ -358,6 +361,7 @@ enum { * 'wq_power_efficient' is disabled. See WQ_POWER_EFFICIENT for more info. */ extern struct workqueue_struct *system_wq; +extern struct workqueue_struct *system_highpri_wq; extern struct workqueue_struct *system_long_wq; extern struct workqueue_struct *system_unbound_wq; extern struct workqueue_struct *system_freezable_wq; -- cgit v1.2.3 From 79bc251f0e0aea67bc230c530f7fa57f66f9cdf3 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Thu, 22 May 2014 16:43:44 +0800 Subject: workqueue: remove unused WORK_CPU_END WORK_CPU_END is totally unused since 4e8b22bd1a37 ("workqueue: fix pool ID allocation leakage and remove BUILD_BUG_ON() in init_workqueues"). It should be removed. After it is removed, the comment "special cpu IDs" is not precise due to there is only one special CPU ID (WORK_CPU_UNBOUND) left, so we also change this comment to the description for WORK_CPU_UNBOUND. tj: Minor description and comment tweaks. Signed-off-by: Lai Jiangshan Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index b263b29bd98b..b8aee9453f22 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -56,9 +56,8 @@ enum { WORK_NR_COLORS = (1 << WORK_STRUCT_COLOR_BITS) - 1, WORK_NO_COLOR = WORK_NR_COLORS, - /* special cpu IDs */ + /* not bound to any CPU, prefer the local CPU */ WORK_CPU_UNBOUND = NR_CPUS, - WORK_CPU_END = NR_CPUS + 1, /* * Reserve 7 bits off of pwq pointer w/ debugobjects turned off. -- cgit v1.2.3 From cafebac153ae54fd0aba5d4ad28af995532c5375 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Thu, 22 May 2014 16:43:56 +0800 Subject: workqueue: remove unused work_clear_pending() In 8930caba3dbd ("workqueue: disable irq while manipulating PENDING"), setting last CPU and clearing PENDING got merged into a single operation (set_work_cpu_and_clear_pending()), which resulted that the internal routine work_clear_pending() is not used any more. tj: Minor description tweak. Signed-off-by: Lai Jiangshan Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index b8aee9453f22..a0cc2e95ed1b 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -273,13 +273,6 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } #define delayed_work_pending(w) \ work_pending(&(w)->work) -/** - * work_clear_pending - for internal use only, mark a work item as not pending - * @work: The work item in question - */ -#define work_clear_pending(work) \ - clear_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work)) - /* * Workqueue flags and constants. For details, please refer to * Documentation/workqueue.txt. -- cgit v1.2.3 From ca8a22634381537c92b5a10308652e1c38fd9edf Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Thu, 22 May 2014 10:41:08 -0400 Subject: tcp: make cwnd-limited checks measurement-based, and gentler Experience with the recent e114a710aa50 ("tcp: fix cwnd limited checking to improve congestion control") has shown that there are common cases where that commit can cause cwnd to be much larger than necessary. This leads to TSO autosizing cooking skbs that are too large, among other things. The main problems seemed to be: (1) That commit attempted to predict the future behavior of the connection by looking at the write queue (if TSO or TSQ limit sending). That prediction sometimes overestimated future outstanding packets. (2) That commit always allowed cwnd to grow to twice the number of outstanding packets (even in congestion avoidance, where this is not needed). This commit improves both of these, by: (1) Switching to a measurement-based approach where we explicitly track the largest number of packets in flight during the past window ("max_packets_out"), and remember whether we were cwnd-limited at the moment we finished sending that flight. (2) Only allowing cwnd to grow to twice the number of outstanding packets ("max_packets_out") in slow start. In congestion avoidance mode we now only allow cwnd to grow if it was fully utilized. Signed-off-by: Neal Cardwell Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/tcp.h | 6 ++++-- include/net/tcp.h | 11 ++++++++--- net/ipv4/tcp_output.c | 37 +++++++++++++++++++++++-------------- 3 files changed, 35 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index bc35e4709e8e..a0513210798f 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -197,7 +197,8 @@ struct tcp_sock { u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */ syn_data:1, /* SYN includes data */ syn_fastopen:1, /* SYN includes Fast Open option */ - syn_data_acked:1;/* data in SYN is acked by SYN-ACK */ + syn_data_acked:1,/* data in SYN is acked by SYN-ACK */ + is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */ u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ /* RTT measurement */ @@ -209,6 +210,8 @@ struct tcp_sock { u32 packets_out; /* Packets which are "in flight" */ u32 retrans_out; /* Retransmitted packets out */ + u32 max_packets_out; /* max packets_out in last window */ + u32 max_packets_seq; /* right edge of max_packets_out flight */ u16 urg_data; /* Saved octet of OOB data and control flags */ u8 ecn_flags; /* ECN status bits. */ @@ -230,7 +233,6 @@ struct tcp_sock { u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ u32 snd_cwnd_used; u32 snd_cwnd_stamp; - u32 lsnd_pending; /* packets inflight or unsent since last xmit */ u32 prior_cwnd; /* Congestion window at start of Recovery. */ u32 prr_delivered; /* Number of newly delivered packets to * receiver in Recovery. */ diff --git a/include/net/tcp.h b/include/net/tcp.h index f5d6ca4a9d28..e80abe4486cb 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -971,8 +971,9 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp) /* We follow the spirit of RFC2861 to validate cwnd but implement a more * flexible approach. The RFC suggests cwnd should not be raised unless - * it was fully used previously. But we allow cwnd to grow as long as the - * application has used half the cwnd. + * it was fully used previously. And that's exactly what we do in + * congestion avoidance mode. But in slow start we allow cwnd to grow + * as long as the application has used half the cwnd. * Example : * cwnd is 10 (IW10), but application sends 9 frames. * We allow cwnd to reach 18 when all frames are ACKed. @@ -985,7 +986,11 @@ static inline bool tcp_is_cwnd_limited(const struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); - return tp->snd_cwnd < 2 * tp->lsnd_pending; + /* If in slow start, ensure cwnd grows to twice what was ACKed. */ + if (tp->snd_cwnd <= tp->snd_ssthresh) + return tp->snd_cwnd < 2 * tp->max_packets_out; + + return tp->is_cwnd_limited; } static inline void tcp_check_probe_timer(struct sock *sk) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 3d61c52bdf79..d463c35db33d 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1402,11 +1402,19 @@ static void tcp_cwnd_application_limited(struct sock *sk) tp->snd_cwnd_stamp = tcp_time_stamp; } -static void tcp_cwnd_validate(struct sock *sk, u32 unsent_segs) +static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited) { struct tcp_sock *tp = tcp_sk(sk); - tp->lsnd_pending = tp->packets_out + unsent_segs; + /* Track the maximum number of outstanding packets in each + * window, and remember whether we were cwnd-limited then. + */ + if (!before(tp->snd_una, tp->max_packets_seq) || + tp->packets_out > tp->max_packets_out) { + tp->max_packets_out = tp->packets_out; + tp->max_packets_seq = tp->snd_nxt; + tp->is_cwnd_limited = is_cwnd_limited; + } if (tcp_is_cwnd_limited(sk)) { /* Network is feed fully. */ @@ -1660,7 +1668,8 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, * * This algorithm is from John Heffner. */ -static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) +static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, + bool *is_cwnd_limited) { struct tcp_sock *tp = tcp_sk(sk); const struct inet_connection_sock *icsk = inet_csk(sk); @@ -1724,6 +1733,9 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) if (!tp->tso_deferred) tp->tso_deferred = 1 | (jiffies << 1); + if (cong_win < send_win && cong_win < skb->len) + *is_cwnd_limited = true; + return true; send_now: @@ -1881,9 +1893,10 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - unsigned int tso_segs, sent_pkts, unsent_segs = 0; + unsigned int tso_segs, sent_pkts; int cwnd_quota; int result; + bool is_cwnd_limited = false; sent_pkts = 0; @@ -1908,6 +1921,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, cwnd_quota = tcp_cwnd_test(tp, skb); if (!cwnd_quota) { + is_cwnd_limited = true; if (push_one == 2) /* Force out a loss probe pkt. */ cwnd_quota = 1; @@ -1924,8 +1938,9 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, nonagle : TCP_NAGLE_PUSH)))) break; } else { - if (!push_one && tcp_tso_should_defer(sk, skb)) - goto compute_unsent_segs; + if (!push_one && + tcp_tso_should_defer(sk, skb, &is_cwnd_limited)) + break; } /* TCP Small Queues : @@ -1950,14 +1965,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, * there is no smp_mb__after_set_bit() yet */ smp_mb__after_clear_bit(); - if (atomic_read(&sk->sk_wmem_alloc) > limit) { - u32 unsent_bytes; - -compute_unsent_segs: - unsent_bytes = tp->write_seq - tp->snd_nxt; - unsent_segs = DIV_ROUND_UP(unsent_bytes, mss_now); + if (atomic_read(&sk->sk_wmem_alloc) > limit) break; - } } limit = mss_now; @@ -1997,7 +2006,7 @@ repair: /* Send one loss probe per tail loss episode. */ if (push_one != 2) tcp_schedule_loss_probe(sk); - tcp_cwnd_validate(sk, unsent_segs); + tcp_cwnd_validate(sk, is_cwnd_limited); return false; } return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk)); -- cgit v1.2.3 From 9edbcd2252b5ef148177c9f2c11a56469cf5db52 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 17 Apr 2014 19:48:07 +0200 Subject: PCI: Remove pcibios_add_platform_entries() Remove pcibios_add_platform_entries(). Architecture-specific attributes can be achieved by setting pdev->dev.groups. Link: https://lkml.kernel.org/r/alpine.LFD.2.11.1404141101500.1529@denkbrett Signed-off-by: Sebastian Ott Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 10 ---------- include/linux/pci.h | 1 - 2 files changed, 11 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 3db1c7ff5dd3..b7333fa5f80d 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1273,11 +1273,6 @@ static struct bin_attribute pcie_config_attr = { .write = pci_write_config, }; -int __weak pcibios_add_platform_entries(struct pci_dev *dev) -{ - return 0; -} - static ssize_t reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1393,11 +1388,6 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) pdev->rom_attr = attr; } - /* add platform-specific attributes */ - retval = pcibios_add_platform_entries(pdev); - if (retval) - goto err_rom_file; - /* add sysfs entries for various capabilities */ retval = pci_create_capabilities_sysfs(pdev); if (retval) diff --git a/include/linux/pci.h b/include/linux/pci.h index a95aac7ad37f..84182b153b21 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1572,7 +1572,6 @@ extern unsigned long pci_hotplug_io_size; extern unsigned long pci_hotplug_mem_size; /* Architecture-specific versions may override these (weak) */ -int pcibios_add_platform_entries(struct pci_dev *dev); void pcibios_disable_device(struct pci_dev *dev); void pcibios_set_master(struct pci_dev *dev); int pcibios_set_pcie_reset_state(struct pci_dev *dev, -- cgit v1.2.3 From e876f208af18b074f800656e4d1b99da75b2135f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 19 May 2014 13:59:52 -0300 Subject: net: Add a software TSO helper API Although the implementation probably needs a lot of work, this initial API allows to implement software TSO in mvneta and mv643xx_eth drivers in a not so intrusive way. Signed-off-by: Ezequiel Garcia Signed-off-by: David S. Miller --- include/net/tso.h | 20 ++++++++++++++++ net/core/Makefile | 2 +- net/core/tso.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 include/net/tso.h create mode 100644 net/core/tso.c (limited to 'include') diff --git a/include/net/tso.h b/include/net/tso.h new file mode 100644 index 000000000000..47e5444f7d15 --- /dev/null +++ b/include/net/tso.h @@ -0,0 +1,20 @@ +#ifndef _TSO_H +#define _TSO_H + +#include + +struct tso_t { + int next_frag_idx; + void *data; + size_t size; + u16 ip_id; + u32 tcp_seq; +}; + +int tso_count_descs(struct sk_buff *skb); +void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso, + int size, bool is_last); +void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size); +void tso_start(struct sk_buff *skb, struct tso_t *tso); + +#endif /* _TSO_H */ diff --git a/net/core/Makefile b/net/core/Makefile index 826b925aa453..71093d94ad2b 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ - sock_diag.o dev_ioctl.o + sock_diag.o dev_ioctl.o tso.o obj-$(CONFIG_XFRM) += flow.o obj-y += net-sysfs.o diff --git a/net/core/tso.c b/net/core/tso.c new file mode 100644 index 000000000000..097821dd3a8c --- /dev/null +++ b/net/core/tso.c @@ -0,0 +1,72 @@ +#include +#include + +/* Calculate expected number of TX descriptors */ +int tso_count_descs(struct sk_buff *skb) +{ + /* The Marvell Way */ + return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags; +} + +void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso, + int size, bool is_last) +{ + struct iphdr *iph; + struct tcphdr *tcph; + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int mac_hdr_len = skb_network_offset(skb); + + memcpy(hdr, skb->data, hdr_len); + iph = (struct iphdr *)(hdr + mac_hdr_len); + iph->id = htons(tso->ip_id); + iph->tot_len = htons(size + hdr_len - mac_hdr_len); + tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb)); + tcph->seq = htonl(tso->tcp_seq); + tso->ip_id++; + + if (!is_last) { + /* Clear all special flags for not last packet */ + tcph->psh = 0; + tcph->fin = 0; + tcph->rst = 0; + } +} + +void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size) +{ + tso->tcp_seq += size; + tso->size -= size; + tso->data += size; + + if ((tso->size == 0) && + (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx]; + + /* Move to next segment */ + tso->size = frag->size; + tso->data = page_address(frag->page.p) + frag->page_offset; + tso->next_frag_idx++; + } +} + +void tso_start(struct sk_buff *skb, struct tso_t *tso) +{ + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + + tso->ip_id = ntohs(ip_hdr(skb)->id); + tso->tcp_seq = ntohl(tcp_hdr(skb)->seq); + tso->next_frag_idx = 0; + + /* Build first data */ + tso->size = skb_headlen(skb) - hdr_len; + tso->data = skb->data + hdr_len; + if ((tso->size == 0) && + (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx]; + + /* Move to next segment */ + tso->size = frag->size; + tso->data = page_address(frag->page.p) + frag->page_offset; + tso->next_frag_idx++; + } +} -- cgit v1.2.3 From da08143b85203b581f4a6461b149186b0e9592df Mon Sep 17 00:00:00 2001 From: Michal Kubeček Date: Tue, 20 May 2014 08:29:25 +0200 Subject: vlan: more careful checksum features handling When combining real_dev's features and vlan_features, simple bitwise AND is used. This doesn't work well for checksum offloading features as if one set has NETIF_F_HW_CSUM and the other NETIF_F_IP_CSUM and/or NETIF_F_IPV6_CSUM, we end up with no checksum offloading. However, from the logical point of view (how can_checksum_protocol() works), NETIF_F_HW_CSUM contains the functionality of NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM so that the result should be IP/IPV6. Add helper function netdev_intersect_features() implementing this logic and use it in vlan_dev_fix_features(). Signed-off-by: Michal Kubecek Signed-off-by: David S. Miller --- include/linux/netdevice.h | 14 ++++++++++++++ net/8021q/vlan_dev.c | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2dea98cbbdba..f4ad247fd324 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3153,6 +3153,20 @@ const char *netdev_drivername(const struct net_device *dev); void linkwatch_run_queue(void); +static inline netdev_features_t netdev_intersect_features(netdev_features_t f1, + netdev_features_t f2) +{ + if (f1 & NETIF_F_GEN_CSUM) + f1 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + if (f2 & NETIF_F_GEN_CSUM) + f2 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + f1 &= f2; + if (f1 & NETIF_F_GEN_CSUM) + f1 &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + + return f1; +} + static inline netdev_features_t netdev_get_wanted_features( struct net_device *dev) { diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 8f025afa29fd..4181fb71ba77 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -678,9 +678,9 @@ static netdev_features_t vlan_dev_fix_features(struct net_device *dev, struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; netdev_features_t old_features = features; - features &= real_dev->vlan_features; + features = netdev_intersect_features(features, real_dev->vlan_features); features |= NETIF_F_RXCSUM; - features &= real_dev->features; + features = netdev_intersect_features(features, real_dev->features); features |= old_features & NETIF_F_SOFT_FEATURES; features |= NETIF_F_LLTX; -- cgit v1.2.3 From ea3429c77d4e34cb2983b90e49a5506fedf70b98 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 22 May 2014 09:47:50 -0700 Subject: of: mdio: remove of_phy_connect_fixed_link All in-tree drivers have been converted to use the new pair of functions: of_is_fixed_phy_link() plus of_phy_register_fixed_link(), we can now safely remove of_phy_connect_fixed_link. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/of/of_mdio.c | 38 -------------------------------------- include/linux/of_mdio.h | 10 ---------- 2 files changed, 48 deletions(-) (limited to 'include') diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 1def0bb5cb37..4c1e01ed16dc 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -245,44 +245,6 @@ struct phy_device *of_phy_connect(struct net_device *dev, } EXPORT_SYMBOL(of_phy_connect); -/** - * of_phy_connect_fixed_link - Parse fixed-link property and return a dummy phy - * @dev: pointer to net_device claiming the phy - * @hndlr: Link state callback for the network device - * @iface: PHY data interface type - * - * This function is a temporary stop-gap and will be removed soon. It is - * only to support the fs_enet, ucc_geth and gianfar Ethernet drivers. Do - * not call this function from new drivers. - */ -struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, - void (*hndlr)(struct net_device *), - phy_interface_t iface) -{ - struct device_node *net_np; - char bus_id[MII_BUS_ID_SIZE + 3]; - struct phy_device *phy; - const __be32 *phy_id; - int sz; - - if (!dev->dev.parent) - return NULL; - - net_np = dev->dev.parent->of_node; - if (!net_np) - return NULL; - - phy_id = of_get_property(net_np, "fixed-link", &sz); - if (!phy_id || sz < sizeof(*phy_id)) - return NULL; - - sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0])); - - phy = phy_connect(dev, bus_id, hndlr, iface); - return IS_ERR(phy) ? NULL : phy; -} -EXPORT_SYMBOL(of_phy_connect_fixed_link); - /** * of_phy_attach - Attach to a PHY without starting the state machine * @dev: pointer to net_device claiming the phy diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index 0aa367e316cb..d449018d0726 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -22,9 +22,6 @@ extern struct phy_device *of_phy_connect(struct net_device *dev, struct phy_device *of_phy_attach(struct net_device *dev, struct device_node *phy_np, u32 flags, phy_interface_t iface); -extern struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, - void (*hndlr)(struct net_device *), - phy_interface_t iface); extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np); @@ -59,13 +56,6 @@ static inline struct phy_device *of_phy_attach(struct net_device *dev, return NULL; } -static inline struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, - void (*hndlr)(struct net_device *), - phy_interface_t iface) -{ - return NULL; -} - static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np) { return NULL; -- cgit v1.2.3 From 16e4d93f6de7063800f3f5e68f064b0ff8fae9b7 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 19 May 2014 13:40:22 -0400 Subject: NFSD: Ignore client's source port on RDMA transports An NFS/RDMA client's source port is meaningless for RDMA transports. The transport layer typically sets the source port value on the connection to a random ephemeral port. Currently, NFS server administrators must specify the "insecure" export option to enable clients to access exports via RDMA. But this means NFS clients can access such an export via IP using an ephemeral port, which may not be desirable. This patch eliminates the need to specify the "insecure" export option to allow NFS/RDMA clients access to an export. BugLink: https://bugzilla.linux-nfs.org/show_bug.cgi?id=250 Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_xprt.h | 1 + net/sunrpc/svc_xprt.c | 2 +- net/sunrpc/svcsock.c | 9 +++++++++ net/sunrpc/xprtrdma/svc_rdma_transport.c | 7 +++++++ 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h index b05963f09ebf..0cec1b94c670 100644 --- a/include/linux/sunrpc/svc_xprt.h +++ b/include/linux/sunrpc/svc_xprt.h @@ -24,6 +24,7 @@ struct svc_xprt_ops { void (*xpo_release_rqst)(struct svc_rqst *); void (*xpo_detach)(struct svc_xprt *); void (*xpo_free)(struct svc_xprt *); + int (*xpo_secure_port)(struct svc_rqst *); }; struct svc_xprt_class { diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 06c6ff0cb911..614956f1777e 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -793,7 +793,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) clear_bit(XPT_OLD, &xprt->xpt_flags); - rqstp->rq_secure = svc_port_is_privileged(svc_addr(rqstp)); + rqstp->rq_secure = xprt->xpt_ops->xpo_secure_port(rqstp); rqstp->rq_chandle.defer = svc_defer; if (serv->sv_stats) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 43bcb4699d69..0cb34f5d58dc 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -400,6 +400,12 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd, release_sock(sock->sk); #endif } + +static int svc_sock_secure_port(struct svc_rqst *rqstp) +{ + return svc_port_is_privileged(svc_addr(rqstp)); +} + /* * INET callback when data has been received on the socket. */ @@ -678,6 +684,7 @@ static struct svc_xprt_ops svc_udp_ops = { .xpo_prep_reply_hdr = svc_udp_prep_reply_hdr, .xpo_has_wspace = svc_udp_has_wspace, .xpo_accept = svc_udp_accept, + .xpo_secure_port = svc_sock_secure_port, }; static struct svc_xprt_class svc_udp_class = { @@ -1234,6 +1241,7 @@ static struct svc_xprt_ops svc_tcp_bc_ops = { .xpo_detach = svc_bc_tcp_sock_detach, .xpo_free = svc_bc_sock_free, .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr, + .xpo_secure_port = svc_sock_secure_port, }; static struct svc_xprt_class svc_tcp_bc_class = { @@ -1272,6 +1280,7 @@ static struct svc_xprt_ops svc_tcp_ops = { .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr, .xpo_has_wspace = svc_tcp_has_wspace, .xpo_accept = svc_tcp_accept, + .xpo_secure_port = svc_sock_secure_port, }; static struct svc_xprt_class svc_tcp_class = { diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 25688fa2207f..02db8d9cc994 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -65,6 +65,7 @@ static void dto_tasklet_func(unsigned long data); static void svc_rdma_detach(struct svc_xprt *xprt); static void svc_rdma_free(struct svc_xprt *xprt); static int svc_rdma_has_wspace(struct svc_xprt *xprt); +static int svc_rdma_secure_port(struct svc_rqst *); static void rq_cq_reap(struct svcxprt_rdma *xprt); static void sq_cq_reap(struct svcxprt_rdma *xprt); @@ -82,6 +83,7 @@ static struct svc_xprt_ops svc_rdma_ops = { .xpo_prep_reply_hdr = svc_rdma_prep_reply_hdr, .xpo_has_wspace = svc_rdma_has_wspace, .xpo_accept = svc_rdma_accept, + .xpo_secure_port = svc_rdma_secure_port, }; struct svc_xprt_class svc_rdma_class = { @@ -1207,6 +1209,11 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt) return 1; } +static int svc_rdma_secure_port(struct svc_rqst *rqstp) +{ + return 1; +} + /* * Attempt to register the kvec representing the RPC memory with the * device. -- cgit v1.2.3 From ef11ce24875a8a540adc185e7bce3d7d49c8296f Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 12 May 2014 11:22:47 +1000 Subject: SUNRPC: track whether a request is coming from a loop-back interface. If an incoming NFS request is coming from the local host, then nfsd will need to perform some special handling. So detect that possibility and make the source visible in rq_local. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc.h | 1 + include/linux/sunrpc/svc_xprt.h | 1 + net/sunrpc/sunrpc.h | 13 +++++++++++++ net/sunrpc/svcsock.c | 5 +++++ 4 files changed, 20 insertions(+) (limited to 'include') diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 04e763221246..a0dbbd1e00e9 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -254,6 +254,7 @@ struct svc_rqst { u32 rq_prot; /* IP protocol */ unsigned short rq_secure : 1; /* secure port */ + unsigned short rq_local : 1; /* local request */ void * rq_argp; /* decoded arguments */ void * rq_resp; /* xdr'd results */ diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h index 0cec1b94c670..7235040a19b2 100644 --- a/include/linux/sunrpc/svc_xprt.h +++ b/include/linux/sunrpc/svc_xprt.h @@ -64,6 +64,7 @@ struct svc_xprt { #define XPT_DETACHED 10 /* detached from tempsocks list */ #define XPT_LISTENER 11 /* listening endpoint */ #define XPT_CACHE_AUTH 12 /* cache auth info */ +#define XPT_LOCAL 13 /* connection from loopback interface */ struct svc_serv *xpt_server; /* service for transport */ atomic_t xpt_reserved; /* space on outq that is rsvd */ diff --git a/net/sunrpc/sunrpc.h b/net/sunrpc/sunrpc.h index 14c9f6d1c5ff..f2b7cb540e61 100644 --- a/net/sunrpc/sunrpc.h +++ b/net/sunrpc/sunrpc.h @@ -43,6 +43,19 @@ static inline int rpc_reply_expected(struct rpc_task *task) (task->tk_msg.rpc_proc->p_decode != NULL); } +static inline int sock_is_loopback(struct sock *sk) +{ + struct dst_entry *dst; + int loopback = 0; + rcu_read_lock(); + dst = rcu_dereference(sk->sk_dst_cache); + if (dst && dst->dev && + (dst->dev->features & NETIF_F_LOOPBACK)) + loopback = 1; + rcu_read_unlock(); + return loopback; +} + int svc_send_common(struct socket *sock, struct xdr_buf *xdr, struct page *headpage, unsigned long headoffset, struct page *tailpage, unsigned long tailoffset); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 0cb34f5d58dc..f3b8eb309d01 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -874,6 +874,10 @@ static struct svc_xprt *svc_tcp_accept(struct svc_xprt *xprt) } svc_xprt_set_local(&newsvsk->sk_xprt, sin, slen); + if (sock_is_loopback(newsock->sk)) + set_bit(XPT_LOCAL, &newsvsk->sk_xprt.xpt_flags); + else + clear_bit(XPT_LOCAL, &newsvsk->sk_xprt.xpt_flags); if (serv->sv_stats) serv->sv_stats->nettcpconn++; @@ -1119,6 +1123,7 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) rqstp->rq_xprt_ctxt = NULL; rqstp->rq_prot = IPPROTO_TCP; + rqstp->rq_local = !!test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags); p = (__be32 *)rqstp->rq_arg.head[0].iov_base; calldir = p[1]; -- cgit v1.2.3 From ecc8fb11cdb37d108d4597ba0f6bdff77c6019af Mon Sep 17 00:00:00 2001 From: Amir Vadai Date: Thu, 22 May 2014 15:55:39 +0300 Subject: net/mlx4_core: Deprecate use_prio module parameter use_prio was added as part of an infrastructure for running FCoE in A0 mode. FCoE didn't get into Mellanox Upstream driver, and when it will, it won't be using A0 steering mode. Therefore we can safely deprecate this module parameter without hurting any existing user. CC: Carol Soto Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/main.c | 8 ++++---- include/linux/mlx4/device.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index a56f6012258d..08ff5dd9298f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -132,8 +132,7 @@ MODULE_PARM_DESC(log_num_vlan, "Log2 max number of VLANs per ETH port (0-7)"); static bool use_prio; module_param_named(use_prio, use_prio, bool, 0444); -MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports " - "(0/1, default 0)"); +MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports (deprecated)"); int log_mtts_per_seg = ilog2(MLX4_MTT_ENTRY_PER_SEG); module_param_named(log_mtts_per_seg, log_mtts_per_seg, int, 0444); @@ -290,7 +289,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.log_num_macs = log_num_mac; dev->caps.log_num_vlans = MLX4_LOG_NUM_VLANS; - dev->caps.log_num_prios = use_prio ? 3 : 0; for (i = 1; i <= dev->caps.num_ports; ++i) { dev->caps.port_type[i] = MLX4_PORT_TYPE_NONE; @@ -358,7 +356,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_ADDR] = (1 << dev->caps.log_num_macs) * (1 << dev->caps.log_num_vlans) * - (1 << dev->caps.log_num_prios) * dev->caps.num_ports; dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_EXCH] = MLX4_NUM_FEXCH; @@ -2775,6 +2772,9 @@ static int __init mlx4_verify_params(void) pr_warning("mlx4_core: log_num_vlan - obsolete module param, using %d\n", MLX4_LOG_NUM_VLANS); + if (use_prio != 0) + pr_warn("mlx4_core: use_prio - obsolete module param, ignored\n"); + if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 7)) { pr_warning("mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg); return -1; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index c0468e6f0442..ca38871a585c 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -449,7 +449,6 @@ struct mlx4_caps { int reserved_qps_base[MLX4_NUM_QP_REGION]; int log_num_macs; int log_num_vlans; - int log_num_prios; enum mlx4_port_type port_type[MLX4_MAX_PORTS + 1]; u8 supported_type[MLX4_MAX_PORTS + 1]; u8 suggested_type[MLX4_MAX_PORTS + 1]; -- cgit v1.2.3 From be52c9e96a6657d117bb0ec6e11438fb246af5c7 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Mon, 5 May 2014 09:59:40 -0700 Subject: openvswitch: Avoid assigning a NULL pointer to flow actions. Flow SET can accept an empty set of actions, with the intended semantics of leaving existing actions unmodified. This seems to have been brokin after OVS 1.7, as we have assigned the flow's actions pointer to NULL in this case, but we never check for the NULL pointer later on. This patch restores the intended behavior and documents it in the include/linux/openvswitch.h. Signed-off-by: Jarno Rajahalme Signed-off-by: Pravin B Shelar --- include/uapi/linux/openvswitch.h | 4 +++- net/openvswitch/datapath.c | 14 ++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 970553cbbc8e..0b979ee4bfc0 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -395,7 +395,9 @@ struct ovs_key_nd { * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying * the actions to take for packets that match the key. Always present in * notifications. Required for %OVS_FLOW_CMD_NEW requests, optional for - * %OVS_FLOW_CMD_SET requests. + * %OVS_FLOW_CMD_SET requests. An %OVS_FLOW_CMD_SET without + * %OVS_FLOW_ATTR_ACTIONS will not modify the actions. To clear the actions, + * an %OVS_FLOW_ATTR_ACTIONS without any nested attributes must be given. * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this * flow. Present in notifications if the stats would be nonzero. Ignored in * requests. diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 8867d7e2d65b..90a1e5e66287 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -810,6 +810,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) goto err_kfree; } } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) { + /* OVS_FLOW_CMD_NEW must have actions. */ error = -EINVAL; goto error; } @@ -849,8 +850,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); } else { /* We found a matching flow. */ - struct sw_flow_actions *old_acts; - /* Bail out if we're not allowed to modify an existing flow. * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL * because Generic Netlink treats the latter as a dump @@ -866,11 +865,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) if (!ovs_flow_cmp_unmasked_key(flow, &match)) goto err_unlock_ovs; - /* Update actions. */ - old_acts = ovsl_dereference(flow->sf_acts); - rcu_assign_pointer(flow->sf_acts, acts); - ovs_nla_free_flow_actions(old_acts); + /* Update actions, if present. */ + if (acts) { + struct sw_flow_actions *old_acts; + old_acts = ovsl_dereference(flow->sf_acts); + rcu_assign_pointer(flow->sf_acts, acts); + ovs_nla_free_flow_actions(old_acts); + } reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW); /* Clear stats. */ -- cgit v1.2.3 From 11d200e95f3e84c1102e4cc9863a3614fd41f3ad Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 14 Mar 2014 17:00:14 +0000 Subject: lib: add glibc style strchrnul() variant The strchrnul() variant helpfully returns a the end of the string instead of a NULL if the requested character is not found. This can simplify string parsing code since it doesn't need to expicitly check for a NULL return. If a valid string pointer is passed in, then a valid null terminated string will always come back out. Signed-off-by: Grant Likely --- include/linux/string.h | 3 +++ lib/string.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/include/linux/string.h b/include/linux/string.h index ac889c5ea11b..d36977e029af 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -52,6 +52,9 @@ extern int strncasecmp(const char *s1, const char *s2, size_t n); #ifndef __HAVE_ARCH_STRCHR extern char * strchr(const char *,int); #endif +#ifndef __HAVE_ARCH_STRCHRNUL +extern char * strchrnul(const char *,int); +#endif #ifndef __HAVE_ARCH_STRNCHR extern char * strnchr(const char *, size_t, int); #endif diff --git a/lib/string.c b/lib/string.c index 9b1f9062a202..e0c20eb362f0 100644 --- a/lib/string.c +++ b/lib/string.c @@ -301,6 +301,24 @@ char *strchr(const char *s, int c) EXPORT_SYMBOL(strchr); #endif +#ifndef __HAVE_ARCH_STRCHRNUL +/** + * strchrnul - Find and return a character in a string, or end of string + * @s: The string to be searched + * @c: The character to search for + * + * Returns pointer to first occurrence of 'c' in s. If c is not found, then + * return a pointer to the null byte at the end of s. + */ +char *strchrnul(const char *s, int c) +{ + while (*s && *s != (char)c) + s++; + return (char *)s; +} +EXPORT_SYMBOL(strchrnul); +#endif + #ifndef __HAVE_ARCH_STRRCHR /** * strrchr - Find the last occurrence of a character in a string -- cgit v1.2.3 From ad69674e73a18dc3a8da557f4059ccf9389531a5 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 20 May 2014 13:42:02 +0300 Subject: of/irq: do irq resolution in platform_get_irq_byname() The commit 9ec36cafe43bf835f8f29273597a5b0cbc8267ef "of/irq: do irq resolution in platform_get_irq" from Rob Herring - moves resolving of the interrupt resources in platform_get_irq(). But this solution isn't complete because platform_get_irq_byname() need to be modified the same way. Hence, fix it by adding interrupt resolution code at the platform_get_irq_byname() function too. Cc: Russell King Cc: Rob Herring Cc: Tony Lindgren Cc: Grant Likely Cc: Thierry Reding Signed-off-by: Grygorii Strashko Signed-off-by: Grant Likely --- drivers/base/platform.c | 7 +++++-- drivers/of/irq.c | 22 ++++++++++++++++++++++ include/linux/of_irq.h | 5 +++++ 3 files changed, 32 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 5b47210889e0..9e9227e1762d 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -131,9 +131,12 @@ EXPORT_SYMBOL_GPL(platform_get_resource_byname); */ int platform_get_irq_byname(struct platform_device *dev, const char *name) { - struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, - name); + struct resource *r; + + if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) + return of_irq_get_byname(dev->dev.of_node, name); + r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); return r ? r->start : -ENXIO; } EXPORT_SYMBOL_GPL(platform_get_irq_byname); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 5aeb89411350..3e06a699352d 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -405,6 +405,28 @@ int of_irq_get(struct device_node *dev, int index) return irq_create_of_mapping(&oirq); } +/** + * of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number + * @dev: pointer to device tree node + * @name: irq name + * + * Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain + * is not yet created, or error code in case of any other failure. + */ +int of_irq_get_byname(struct device_node *dev, const char *name) +{ + int index; + + if (unlikely(!name)) + return -EINVAL; + + index = of_property_match_string(dev, "interrupt-names", name); + if (index < 0) + return index; + + return of_irq_get(dev, index); +} + /** * of_irq_count - Count the number of IRQs a node uses * @dev: pointer to device tree node diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index 6404253d810d..bfec136a6d1e 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -45,6 +45,7 @@ extern void of_irq_init(const struct of_device_id *matches); #ifdef CONFIG_OF_IRQ extern int of_irq_count(struct device_node *dev); extern int of_irq_get(struct device_node *dev, int index); +extern int of_irq_get_byname(struct device_node *dev, const char *name); #else static inline int of_irq_count(struct device_node *dev) { @@ -54,6 +55,10 @@ static inline int of_irq_get(struct device_node *dev, int index) { return 0; } +static inline int of_irq_get_byname(struct device_node *dev, const char *name) +{ + return 0; +} #endif #if defined(CONFIG_OF) -- cgit v1.2.3 From 5c992afcf8e4f91fac05d39b86c7f7922a50145c Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Wed, 14 May 2014 17:32:59 -0700 Subject: clk: tegra: Fix xusb_hs_src clock hierarchy Currently the Tegra1x4 clock init code hard-codes the mux setting for xusb_hs_src and treats it as a fixed-factor clock. It is, however, a mux which can be parented by either xusb_ss_src/2 or pll_u_60M. Add the fixed-factor clock xusb_ss_div2 and put an entry in periph_clks[] for the xusb_hs_src mux. Signed-off-by: Andrew Bresticker Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-id.h | 1 + drivers/clk/tegra/clk-tegra-periph.c | 6 ++++++ drivers/clk/tegra/clk-tegra114.c | 15 +++++---------- drivers/clk/tegra/clk-tegra124.c | 15 +++++---------- include/dt-bindings/clock/tegra114-car.h | 3 ++- include/dt-bindings/clock/tegra124-car.h | 3 ++- 6 files changed, 21 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index c39613c519af..0011d547a9f7 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -233,6 +233,7 @@ enum clk_id { tegra_clk_xusb_hs_src, tegra_clk_xusb_ss, tegra_clk_xusb_ss_src, + tegra_clk_xusb_ss_div2, tegra_clk_max, }; diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index a4063bc25208..adf6b814b5bc 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -340,6 +340,11 @@ static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = { [0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7, }; +static const char *mux_ss_60M[] = { + "xusb_ss_div2", "pll_u_60M" +}; +#define mux_ss_60M_idx NULL + static const char *mux_d_audio_clk[] = { "pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync", "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", @@ -501,6 +506,7 @@ static struct tegra_periph_init_data periph_clks[] = { XUSB("xusb_falcon_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_falcon_src), XUSB("xusb_fs_src", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_fs_src), XUSB("xusb_ss_src", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_ss_src), + NODIV("xusb_hs_src", mux_ss_60M, CLK_SOURCE_XUSB_SS_SRC, 25, MASK(1), 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_hs_src, NULL), XUSB("xusb_dev_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_dev_src), }; diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 80431f0fb268..841f54fb8c8b 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -142,7 +142,6 @@ #define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0) #define CLK_SOURCE_CSITE 0x1d4 -#define CLK_SOURCE_XUSB_SS_SRC 0x610 #define CLK_SOURCE_EMC 0x19c /* PLLM override registers */ @@ -834,6 +833,7 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_xusb_falcon_src] = { .dt_id = TEGRA114_CLK_XUSB_FALCON_SRC, .present = true }, [tegra_clk_xusb_fs_src] = { .dt_id = TEGRA114_CLK_XUSB_FS_SRC, .present = true }, [tegra_clk_xusb_ss_src] = { .dt_id = TEGRA114_CLK_XUSB_SS_SRC, .present = true }, + [tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA114_CLK_XUSB_SS_DIV2, .present = true}, [tegra_clk_xusb_dev_src] = { .dt_id = TEGRA114_CLK_XUSB_DEV_SRC, .present = true }, [tegra_clk_xusb_dev] = { .dt_id = TEGRA114_CLK_XUSB_DEV, .present = true }, [tegra_clk_xusb_hs_src] = { .dt_id = TEGRA114_CLK_XUSB_HS_SRC, .present = true }, @@ -1182,16 +1182,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base) { struct clk *clk; - u32 val; - - /* xusb_hs_src */ - val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC); - val |= BIT(25); /* always select PLLU_60M */ - writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC); - clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0, - 1, 1); - clks[TEGRA114_CLK_XUSB_HS_SRC] = clk; + /* xusb_ss_div2 */ + clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0, + 1, 2); + clks[TEGRA114_CLK_XUSB_SS_DIV2] = clk; /* dsia mux */ clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index cc37c342c4cb..0dce8c08eb91 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -30,7 +30,6 @@ #define CLK_SOURCE_CSITE 0x1d4 #define CLK_SOURCE_EMC 0x19c -#define CLK_SOURCE_XUSB_SS_SRC 0x610 #define PLLC_BASE 0x80 #define PLLC_OUT 0x84 @@ -925,6 +924,7 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_xusb_falcon_src] = { .dt_id = TEGRA124_CLK_XUSB_FALCON_SRC, .present = true }, [tegra_clk_xusb_fs_src] = { .dt_id = TEGRA124_CLK_XUSB_FS_SRC, .present = true }, [tegra_clk_xusb_ss_src] = { .dt_id = TEGRA124_CLK_XUSB_SS_SRC, .present = true }, + [tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA124_CLK_XUSB_SS_DIV2, .present = true }, [tegra_clk_xusb_dev_src] = { .dt_id = TEGRA124_CLK_XUSB_DEV_SRC, .present = true }, [tegra_clk_xusb_dev] = { .dt_id = TEGRA124_CLK_XUSB_DEV, .present = true }, [tegra_clk_xusb_hs_src] = { .dt_id = TEGRA124_CLK_XUSB_HS_SRC, .present = true }, @@ -1105,16 +1105,11 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base) { struct clk *clk; - u32 val; - - /* xusb_hs_src */ - val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC); - val |= BIT(25); /* always select PLLU_60M */ - writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC); - clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0, - 1, 1); - clks[TEGRA124_CLK_XUSB_HS_SRC] = clk; + /* xusb_ss_div2 */ + clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0, + 1, 2); + clks[TEGRA124_CLK_XUSB_SS_DIV2] = clk; /* dsia mux */ clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, diff --git a/include/dt-bindings/clock/tegra114-car.h b/include/dt-bindings/clock/tegra114-car.h index 6d0d8d8ef31e..fc12621fb432 100644 --- a/include/dt-bindings/clock/tegra114-car.h +++ b/include/dt-bindings/clock/tegra114-car.h @@ -337,6 +337,7 @@ #define TEGRA114_CLK_CLK_OUT_3_MUX 308 #define TEGRA114_CLK_DSIA_MUX 309 #define TEGRA114_CLK_DSIB_MUX 310 -#define TEGRA114_CLK_CLK_MAX 311 +#define TEGRA114_CLK_XUSB_SS_DIV2 311 +#define TEGRA114_CLK_CLK_MAX 312 #endif /* _DT_BINDINGS_CLOCK_TEGRA114_CAR_H */ diff --git a/include/dt-bindings/clock/tegra124-car.h b/include/dt-bindings/clock/tegra124-car.h index 433528ab5161..8a4c5892890f 100644 --- a/include/dt-bindings/clock/tegra124-car.h +++ b/include/dt-bindings/clock/tegra124-car.h @@ -336,6 +336,7 @@ #define TEGRA124_CLK_DSIA_MUX 309 #define TEGRA124_CLK_DSIB_MUX 310 #define TEGRA124_CLK_SOR0_LVDS 311 -#define TEGRA124_CLK_CLK_MAX 312 +#define TEGRA124_CLK_XUSB_SS_DIV2 312 +#define TEGRA124_CLK_CLK_MAX 313 #endif /* _DT_BINDINGS_CLOCK_TEGRA124_CAR_H */ -- cgit v1.2.3 From 038b8455db9e08f295cb39cda7bec9c2e8e8674d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 23 May 2014 09:57:55 +0200 Subject: ALSA: Remove deprecated snd_card_create() Now all calls have been fixed, let's get rid of the old definition. Signed-off-by: Takashi Iwai --- include/sound/core.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index d3f5f818e0b9..eedda2cdfe57 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -282,13 +282,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid, struct module *module, int extra_size, struct snd_card **card_ret); -static inline int __deprecated -snd_card_create(int idx, const char *id, struct module *module, int extra_size, - struct snd_card **ret) -{ - return snd_card_new(NULL, idx, id, module, extra_size, ret); -} - int snd_card_disconnect(struct snd_card *card); int snd_card_free(struct snd_card *card); int snd_card_free_when_closed(struct snd_card *card); -- cgit v1.2.3 From 9e9dc7d9597bd6881b3e7ae6ae3d710319605c47 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 8 May 2014 23:16:34 +0200 Subject: mfd: stmpe: root out static GPIO and IRQ assignments The only platform using the STMPE expander now boots from device tree using all-dynamic GPIO and IRQ number assignments, so remove the mechanism to pass this from the device tree entirely. Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 18 +++++------------- drivers/mfd/stmpe.c | 6 +----- include/linux/mfd/stmpe.h | 14 -------------- 3 files changed, 6 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 2776a09bee58..628b58494294 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -23,7 +23,8 @@ enum { REG_RE, REG_FE, REG_IE }; #define CACHE_NR_REGS 3 -#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8) +/* No variant has more than 24 GPIOs */ +#define CACHE_NR_BANKS (24 / 8) struct stmpe_gpio { struct gpio_chip chip; @@ -31,8 +32,6 @@ struct stmpe_gpio { struct device *dev; struct mutex irq_lock; struct irq_domain *domain; - - int irq_base; unsigned norequest_mask; /* Caches of interrupt control registers for bus_lock */ @@ -311,13 +310,8 @@ static const struct irq_domain_ops stmpe_gpio_irq_simple_ops = { static int stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio, struct device_node *np) { - int base = 0; - - if (!np) - base = stmpe_gpio->irq_base; - stmpe_gpio->domain = irq_domain_add_simple(np, - stmpe_gpio->chip.ngpio, base, + stmpe_gpio->chip.ngpio, 0, &stmpe_gpio_irq_simple_ops, stmpe_gpio); if (!stmpe_gpio->domain) { dev_err(stmpe_gpio->dev, "failed to create irqdomain\n"); @@ -354,7 +348,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) #ifdef CONFIG_OF stmpe_gpio->chip.of_node = np; #endif - stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1; + stmpe_gpio->chip.base = -1; if (pdata) stmpe_gpio->norequest_mask = pdata->norequest_mask; @@ -362,9 +356,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) of_property_read_u32(np, "st,norequest-mask", &stmpe_gpio->norequest_mask); - if (irq >= 0) - stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0); - else + if (irq < 0) dev_info(&pdev->dev, "device configured in no-irq mode; " "irqs are not available\n"); diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 294731be1a15..3b6bfa7184ad 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -996,9 +996,6 @@ static int stmpe_irq_init(struct stmpe *stmpe, struct device_node *np) int base = 0; int num_irqs = stmpe->variant->num_irqs; - if (!np) - base = stmpe->irq_base; - stmpe->domain = irq_domain_add_simple(np, num_irqs, base, &stmpe_irq_ops, stmpe); if (!stmpe->domain) { @@ -1077,7 +1074,7 @@ static int stmpe_chip_init(struct stmpe *stmpe) static int stmpe_add_device(struct stmpe *stmpe, const struct mfd_cell *cell) { return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1, - NULL, stmpe->irq_base, stmpe->domain); + NULL, 0, stmpe->domain); } static int stmpe_devices_init(struct stmpe *stmpe) @@ -1181,7 +1178,6 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum) stmpe->dev = ci->dev; stmpe->client = ci->client; stmpe->pdata = pdata; - stmpe->irq_base = pdata->irq_base; stmpe->ci = ci; stmpe->partnum = partnum; stmpe->variant = stmpe_variant_info[partnum]; diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 980898620e57..575a86c7fcbd 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -76,7 +76,6 @@ struct stmpe_client_info; * @regs: list of addresses of registers which are at different addresses on * different variants. Indexed by one of STMPE_IDX_*. * @irq: irq number for stmpe - * @irq_base: starting IRQ number for internal IRQs * @num_gpios: number of gpios, differs for variants * @ier: cache of IER registers for bus_lock * @oldier: cache of IER registers for bus_lock @@ -96,7 +95,6 @@ struct stmpe { const u8 *regs; int irq; - int irq_base; int num_gpios; u8 ier[2]; u8 oldier[2]; @@ -137,8 +135,6 @@ struct stmpe_keypad_platform_data { /** * struct stmpe_gpio_platform_data - STMPE GPIO platform data - * @gpio_base: first gpio number assigned. A maximum of - * %STMPE_NR_GPIOS GPIOs will be allocated. * @norequest_mask: bitmask specifying which GPIOs should _not_ be * requestable due to different usage (e.g. touch, keypad) * STMPE_GPIO_NOREQ_* macros can be used here. @@ -146,7 +142,6 @@ struct stmpe_keypad_platform_data { * @remove: board specific remove callback */ struct stmpe_gpio_platform_data { - int gpio_base; unsigned norequest_mask; void (*setup)(struct stmpe *stmpe, unsigned gpio_base); void (*remove)(struct stmpe *stmpe, unsigned gpio_base); @@ -200,8 +195,6 @@ struct stmpe_ts_platform_data { * @irq_trigger: IRQ trigger to use for the interrupt to the host * @autosleep: bool to enable/disable stmpe autosleep * @autosleep_timeout: inactivity timeout in milliseconds for autosleep - * @irq_base: base IRQ number. %STMPE_NR_IRQS irqs will be used, or - * %STMPE_NR_INTERNAL_IRQS if the GPIO driver is not used. * @irq_over_gpio: true if gpio is used to get irq * @irq_gpio: gpio number over which irq will be requested (significant only if * irq_over_gpio is true) @@ -212,7 +205,6 @@ struct stmpe_ts_platform_data { struct stmpe_platform_data { int id; unsigned int blocks; - int irq_base; unsigned int irq_trigger; bool autosleep; bool irq_over_gpio; @@ -224,10 +216,4 @@ struct stmpe_platform_data { struct stmpe_ts_platform_data *ts; }; -#define STMPE_NR_INTERNAL_IRQS 9 -#define STMPE_INT_GPIO(x) (STMPE_NR_INTERNAL_IRQS + (x)) - -#define STMPE_NR_GPIOS 24 -#define STMPE_NR_IRQS STMPE_INT_GPIO(STMPE_NR_GPIOS) - #endif -- cgit v1.2.3 From 3c8a0922e0b774aa394a6925f11976ccfc852808 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 2 May 2014 11:05:21 +1000 Subject: drm/dp_helper: add defines for DP 1.2 and MST support. (v2) This just adds the defines from the DP 1.2 spec, which we will use later. fix some DP MST to 1.2 MST Signed-off-by: Dave Airlie Reviewed-by: Todd Previte Reviewed-by: Jingoo Han --- include/drm/drm_dp_helper.h | 78 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index cfcacec5b89d..c8857e6159a5 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -37,6 +37,7 @@ * eDP: Embedded DisplayPort version 1 * DPI: DisplayPort Interoperability Guideline v1.1a * 1.2: DisplayPort 1.2 + * MST: Multistream Transport - part of DP 1.2a * * 1.2 formally includes both eDP and DPI definitions. */ @@ -103,9 +104,14 @@ #define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ /* Multiple stream transport */ +#define DP_FAUX_CAP 0x020 /* 1.2 */ +# define DP_FAUX_CAP_1 (1 << 0) + #define DP_MSTM_CAP 0x021 /* 1.2 */ # define DP_MST_CAP (1 << 0) +#define DP_GUID 0x030 /* 1.2 */ + #define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ # define DP_PSR_IS_SUPPORTED 1 #define DP_PSR_CAPS 0x071 /* XXX 1.2? */ @@ -221,6 +227,16 @@ # define DP_PSR_CRC_VERIFICATION (1 << 2) # define DP_PSR_FRAME_CAPTURE (1 << 3) +#define DP_ADAPTER_CTRL 0x1a0 +# define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE (1 << 0) + +#define DP_BRANCH_DEVICE_CTRL 0x1a1 +# define DP_BRANCH_DEVICE_IRQ_HPD (1 << 0) + +#define DP_PAYLOAD_ALLOCATE_SET 0x1c0 +#define DP_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x1c1 +#define DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x1c2 + #define DP_SINK_COUNT 0x200 /* prior to 1.2 bit 7 was reserved mbz */ # define DP_GET_SINK_COUNT(x) ((((x) & 0x80) >> 1) | ((x) & 0x3f)) @@ -230,6 +246,9 @@ # define DP_REMOTE_CONTROL_COMMAND_PENDING (1 << 0) # define DP_AUTOMATED_TEST_REQUEST (1 << 1) # define DP_CP_IRQ (1 << 2) +# define DP_MCCS_IRQ (1 << 3) +# define DP_DOWN_REP_MSG_RDY (1 << 4) /* 1.2 MST */ +# define DP_UP_REQ_MSG_RDY (1 << 5) /* 1.2 MST */ # define DP_SINK_SPECIFIC_IRQ (1 << 6) #define DP_LANE0_1_STATUS 0x202 @@ -294,6 +313,13 @@ #define DP_TEST_SINK 0x270 #define DP_TEST_SINK_START (1 << 0) +#define DP_PAYLOAD_TABLE_UPDATE_STATUS 0x2c0 /* 1.2 MST */ +# define DP_PAYLOAD_TABLE_UPDATED (1 << 0) +# define DP_PAYLOAD_ACT_HANDLED (1 << 1) + +#define DP_VC_PAYLOAD_ID_SLOT_1 0x2c1 /* 1.2 MST */ +/* up to ID_SLOT_63 at 0x2ff */ + #define DP_SOURCE_OUI 0x300 #define DP_SINK_OUI 0x400 #define DP_BRANCH_OUI 0x500 @@ -303,6 +329,21 @@ # define DP_SET_POWER_D3 0x2 # define DP_SET_POWER_MASK 0x3 +#define DP_SIDEBAND_MSG_DOWN_REQ_BASE 0x1000 /* 1.2 MST */ +#define DP_SIDEBAND_MSG_UP_REP_BASE 0x1200 /* 1.2 MST */ +#define DP_SIDEBAND_MSG_DOWN_REP_BASE 0x1400 /* 1.2 MST */ +#define DP_SIDEBAND_MSG_UP_REQ_BASE 0x1600 /* 1.2 MST */ + +#define DP_SINK_COUNT_ESI 0x2002 /* 1.2 */ +/* 0-5 sink count */ +# define DP_SINK_COUNT_CP_READY (1 << 6) + +#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 0x2003 /* 1.2 */ + +#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1 0x2004 /* 1.2 */ + +#define DP_LINK_SERVICE_IRQ_VECTOR_ESI0 0x2005 /* 1.2 */ + #define DP_PSR_ERROR_STATUS 0x2006 /* XXX 1.2? */ # define DP_PSR_LINK_CRC_ERROR (1 << 0) # define DP_PSR_RFB_STORAGE_ERROR (1 << 1) @@ -319,6 +360,43 @@ # define DP_PSR_SINK_INTERNAL_ERROR 7 # define DP_PSR_SINK_STATE_MASK 0x07 +/* DP 1.2 Sideband message defines */ +/* peer device type - DP 1.2a Table 2-92 */ +#define DP_PEER_DEVICE_NONE 0x0 +#define DP_PEER_DEVICE_SOURCE_OR_SST 0x1 +#define DP_PEER_DEVICE_MST_BRANCHING 0x2 +#define DP_PEER_DEVICE_SST_SINK 0x3 +#define DP_PEER_DEVICE_DP_LEGACY_CONV 0x4 + +/* DP 1.2 MST sideband request names DP 1.2a Table 2-80 */ +#define DP_LINK_ADDRESS 0x01 +#define DP_CONNECTION_STATUS_NOTIFY 0x02 +#define DP_ENUM_PATH_RESOURCES 0x10 +#define DP_ALLOCATE_PAYLOAD 0x11 +#define DP_QUERY_PAYLOAD 0x12 +#define DP_RESOURCE_STATUS_NOTIFY 0x13 +#define DP_CLEAR_PAYLOAD_ID_TABLE 0x14 +#define DP_REMOTE_DPCD_READ 0x20 +#define DP_REMOTE_DPCD_WRITE 0x21 +#define DP_REMOTE_I2C_READ 0x22 +#define DP_REMOTE_I2C_WRITE 0x23 +#define DP_POWER_UP_PHY 0x24 +#define DP_POWER_DOWN_PHY 0x25 +#define DP_SINK_EVENT_NOTIFY 0x30 +#define DP_QUERY_STREAM_ENC_STATUS 0x38 + +/* DP 1.2 MST sideband nak reasons - table 2.84 */ +#define DP_NAK_WRITE_FAILURE 0x01 +#define DP_NAK_INVALID_READ 0x02 +#define DP_NAK_CRC_FAILURE 0x03 +#define DP_NAK_BAD_PARAM 0x04 +#define DP_NAK_DEFER 0x05 +#define DP_NAK_LINK_FAILURE 0x06 +#define DP_NAK_NO_RESOURCES 0x07 +#define DP_NAK_DPCD_FAIL 0x08 +#define DP_NAK_I2C_NAK 0x09 +#define DP_NAK_ALLOCATE_FAIL 0x0a + #define MODE_I2C_START 1 #define MODE_I2C_WRITE 2 #define MODE_I2C_READ 4 -- cgit v1.2.3 From 8b2e62cc34feaaf1cac9440a93fb18ac0b1e81bc Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 21 May 2014 10:49:19 +0800 Subject: MIPS: Fix a typo error in AUDIT_ARCH definition Missing a "|" in AUDIT_ARCH_MIPSEL64N32 macro definition. Signed-off-by: Huacai Chen Reviewed-by: Markos Chandras Cc: John Crispin Cc: Steven J. Hill Cc: Aurelien Jarno Cc: linux-mips@linux-mips.org Cc: Fuxin Zhang Cc: Zhangjin Wu Patchwork: https://patchwork.linux-mips.org/patch/6978/ Signed-off-by: Ralf Baechle --- include/uapi/linux/audit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 1b1efddb91cd..4c31a366be16 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -357,7 +357,7 @@ enum { #define AUDIT_ARCH_MIPS64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|\ __AUDIT_ARCH_CONVENTION_MIPS64_N32) #define AUDIT_ARCH_MIPSEL64 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) -#define AUDIT_ARCH_MIPSEL64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE\ +#define AUDIT_ARCH_MIPSEL64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE|\ __AUDIT_ARCH_CONVENTION_MIPS64_N32) #define AUDIT_ARCH_OPENRISC (EM_OPENRISC) #define AUDIT_ARCH_PARISC (EM_PARISC) -- cgit v1.2.3 From 1d22924e1c4e299337e86e290c02c3e3eb43b608 Mon Sep 17 00:00:00 2001 From: Anders Berg Date: Fri, 23 May 2014 11:08:35 +0200 Subject: ARM: Add platform support for LSI AXM55xx SoC The AXM55xx family consists of devices that may contain up to 16 ARM Cortex-A15 cores (in a 4x4 cluster configuration). The cores within each cluster share an L2 cache, and the clusters are connected to each other via a CCN-504 cache coherent interconnect. This machine requires CONFIG_ARM_LPAE enabled as all peripherals are located above 4GB in the memory map. Signed-off-by: Anders Berg Acked-by: Linus Walleij Signed-off-by: Arnd Bergmann --- Documentation/devicetree/bindings/arm/axxia.txt | 12 ++++ arch/arm/Kconfig | 2 + arch/arm/Makefile | 2 + arch/arm/mach-axxia/Kconfig | 16 +++++ arch/arm/mach-axxia/Makefile | 2 + arch/arm/mach-axxia/axxia.c | 28 ++++++++ arch/arm/mach-axxia/platsmp.c | 89 +++++++++++++++++++++++++ include/dt-bindings/clock/lsi,axm5516-clks.h | 36 ++++++++++ 8 files changed, 187 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/axxia.txt create mode 100644 arch/arm/mach-axxia/Kconfig create mode 100644 arch/arm/mach-axxia/Makefile create mode 100644 arch/arm/mach-axxia/axxia.c create mode 100644 arch/arm/mach-axxia/platsmp.c create mode 100644 include/dt-bindings/clock/lsi,axm5516-clks.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/arm/axxia.txt b/Documentation/devicetree/bindings/arm/axxia.txt new file mode 100644 index 000000000000..7b4ef9c07696 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/axxia.txt @@ -0,0 +1,12 @@ +Axxia AXM55xx device tree bindings + +Boards using the AXM55xx SoC need to have the following properties: + +Required root node property: + + - compatible = "lsi,axm5516" + +Boards: + + LSI AXM5516 Validation board (Amarillo) + compatible = "lsi,axm5516-amarillo", "lsi,axm5516" diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ab438cb5af55..1f8651a3a16e 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -950,6 +950,8 @@ source "arch/arm/mach-mvebu/Kconfig" source "arch/arm/mach-at91/Kconfig" +source "arch/arm/mach-axxia/Kconfig" + source "arch/arm/mach-bcm/Kconfig" source "arch/arm/mach-berlin/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 41c1931f0155..6721fab13734 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -138,10 +138,12 @@ endif textofs-$(CONFIG_ARCH_MSM7X30) := 0x00208000 textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000 textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 +textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000 # Machine directory name. This list is sorted alphanumerically # by CONFIG_* macro name. machine-$(CONFIG_ARCH_AT91) += at91 +machine-$(CONFIG_ARCH_AXXIA) += axxia machine-$(CONFIG_ARCH_BCM) += bcm machine-$(CONFIG_ARCH_BERLIN) += berlin machine-$(CONFIG_ARCH_CLPS711X) += clps711x diff --git a/arch/arm/mach-axxia/Kconfig b/arch/arm/mach-axxia/Kconfig new file mode 100644 index 000000000000..8be7e0ae1922 --- /dev/null +++ b/arch/arm/mach-axxia/Kconfig @@ -0,0 +1,16 @@ +config ARCH_AXXIA + bool "LSI Axxia platforms" if (ARCH_MULTI_V7 && ARM_LPAE) + select ARCH_DMA_ADDR_T_64BIT + select ARM_AMBA + select ARM_GIC + select ARM_TIMER_SP804 + select HAVE_ARM_ARCH_TIMER + select MFD_SYSCON + select MIGHT_HAVE_PCI + select PCI_DOMAINS if PCI + select ZONE_DMA + help + This enables support for the LSI Axxia devices. + + The LSI Axxia platforms require a Flattened Device Tree to be passed + to the kernel. diff --git a/arch/arm/mach-axxia/Makefile b/arch/arm/mach-axxia/Makefile new file mode 100644 index 000000000000..ec4f68b460c6 --- /dev/null +++ b/arch/arm/mach-axxia/Makefile @@ -0,0 +1,2 @@ +obj-y += axxia.o +obj-$(CONFIG_SMP) += platsmp.o diff --git a/arch/arm/mach-axxia/axxia.c b/arch/arm/mach-axxia/axxia.c new file mode 100644 index 000000000000..19e5a1d95397 --- /dev/null +++ b/arch/arm/mach-axxia/axxia.c @@ -0,0 +1,28 @@ +/* + * Support for the LSI Axxia SoC devices based on ARM cores. + * + * Copyright (C) 2012 LSI + * + * 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. + */ +#include +#include + +static const char *axxia_dt_match[] __initconst = { + "lsi,axm5516", + "lsi,axm5516-sim", + "lsi,axm5516-emu", + NULL +}; + +DT_MACHINE_START(AXXIA_DT, "LSI Axxia AXM55XX") + .dt_compat = axxia_dt_match, +MACHINE_END diff --git a/arch/arm/mach-axxia/platsmp.c b/arch/arm/mach-axxia/platsmp.c new file mode 100644 index 000000000000..959d4df3d2b6 --- /dev/null +++ b/arch/arm/mach-axxia/platsmp.c @@ -0,0 +1,89 @@ +/* + * linux/arch/arm/mach-axxia/platsmp.c + * + * Copyright (C) 2012 LSI Corporation + * + * 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 + +/* Syscon register offsets for releasing cores from reset */ +#define SC_CRIT_WRITE_KEY 0x1000 +#define SC_RST_CPU_HOLD 0x1010 + +/* + * Write the kernel entry point for secondary CPUs to the specified address + */ +static void write_release_addr(u32 release_phys) +{ + u32 *virt = (u32 *) phys_to_virt(release_phys); + writel_relaxed(virt_to_phys(secondary_startup), virt); + /* Make sure this store is visible to other CPUs */ + smp_wmb(); + __cpuc_flush_dcache_area(virt, sizeof(u32)); +} + +static int axxia_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + struct device_node *syscon_np; + void __iomem *syscon; + u32 tmp; + + syscon_np = of_find_compatible_node(NULL, NULL, "lsi,axxia-syscon"); + if (!syscon_np) + return -ENOENT; + + syscon = of_iomap(syscon_np, 0); + if (!syscon) + return -ENOMEM; + + tmp = readl(syscon + SC_RST_CPU_HOLD); + writel(0xab, syscon + SC_CRIT_WRITE_KEY); + tmp &= ~(1 << cpu); + writel(tmp, syscon + SC_RST_CPU_HOLD); + + return 0; +} + +static void __init axxia_smp_prepare_cpus(unsigned int max_cpus) +{ + int cpu_count = 0; + int cpu; + + /* + * Initialise the present map, which describes the set of CPUs actually + * populated at the present time. + */ + for_each_possible_cpu(cpu) { + struct device_node *np; + u32 release_phys; + + np = of_get_cpu_node(cpu, NULL); + if (!np) + continue; + if (of_property_read_u32(np, "cpu-release-addr", &release_phys)) + continue; + + if (cpu_count < max_cpus) { + set_cpu_present(cpu, true); + cpu_count++; + } + + if (release_phys != 0) + write_release_addr(release_phys); + } +} + +static struct smp_operations axxia_smp_ops __initdata = { + .smp_prepare_cpus = axxia_smp_prepare_cpus, + .smp_boot_secondary = axxia_boot_secondary, +}; +CPU_METHOD_OF_DECLARE(axxia_smp, "lsi,syscon-release", &axxia_smp_ops); diff --git a/include/dt-bindings/clock/lsi,axm5516-clks.h b/include/dt-bindings/clock/lsi,axm5516-clks.h new file mode 100644 index 000000000000..beb41ace5dd6 --- /dev/null +++ b/include/dt-bindings/clock/lsi,axm5516-clks.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014 LSI Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + */ + +#ifndef _DT_BINDINGS_CLK_AXM5516_H +#define _DT_BINDINGS_CLK_AXM5516_H + +#define AXXIA_CLK_FAB_PLL 0 +#define AXXIA_CLK_CPU_PLL 1 +#define AXXIA_CLK_SYS_PLL 2 +#define AXXIA_CLK_SM0_PLL 3 +#define AXXIA_CLK_SM1_PLL 4 +#define AXXIA_CLK_FAB_DIV 5 +#define AXXIA_CLK_SYS_DIV 6 +#define AXXIA_CLK_NRCP_DIV 7 +#define AXXIA_CLK_CPU0_DIV 8 +#define AXXIA_CLK_CPU1_DIV 9 +#define AXXIA_CLK_CPU2_DIV 10 +#define AXXIA_CLK_CPU3_DIV 11 +#define AXXIA_CLK_PER_DIV 12 +#define AXXIA_CLK_MMC_DIV 13 +#define AXXIA_CLK_FAB 14 +#define AXXIA_CLK_SYS 15 +#define AXXIA_CLK_NRCP 16 +#define AXXIA_CLK_CPU0 17 +#define AXXIA_CLK_CPU1 18 +#define AXXIA_CLK_CPU2 19 +#define AXXIA_CLK_CPU3 20 +#define AXXIA_CLK_PER 21 +#define AXXIA_CLK_MMC 22 + +#endif -- cgit v1.2.3 From ad0f614e4723db8cead439cf414108cbf975b224 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Thu, 22 May 2014 11:54:20 -0700 Subject: wait: swap EXIT_ZOMBIE(Z) and EXIT_DEAD(X) chars in TASK_STATE_TO_CHAR_STR In commit ad86622b478e ("wait: swap EXIT_ZOMBIE and EXIT_DEAD to hide EXIT_TRACE from user-space") the order of task state definitions were changed: EXIT_DEAD and EXIT_ZOMBIE were swapped. Though the charterers for the states in TASK_STATE_TO_CHAR_STR string were not updated. This patch synchronizes the string to the order of definitions. Signed-off-by: Masatake YAMATO Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 25f54c79f757..21fbdae61b9e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -220,7 +220,7 @@ print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq); #define TASK_PARKED 512 #define TASK_STATE_MAX 1024 -#define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP" +#define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWP" extern char ___assert_task_state[1 - 2*!!( sizeof(TASK_STATE_TO_CHAR_STR)-1 != ilog2(TASK_STATE_MAX)+1)]; -- cgit v1.2.3 From 08f9234ad6b0b8bc51046346eabf5b92e631e62a Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Thu, 24 Apr 2014 11:31:20 -0500 Subject: soc: qcom: Add device tree binding for GSBI Add device tree binding support for the QCOM GSBI driver. Signed-off-by: Andy Gross Signed-off-by: Kumar Gala --- .../devicetree/bindings/soc/qcom/qcom,gsbi.txt | 78 ++++++++++++++++++++++ include/dt-bindings/soc/qcom,gsbi.h | 26 ++++++++ 2 files changed, 104 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt create mode 100644 include/dt-bindings/soc/qcom,gsbi.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt new file mode 100644 index 000000000000..4ce24d425bf1 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.txt @@ -0,0 +1,78 @@ +QCOM GSBI (General Serial Bus Interface) Driver + +The GSBI controller is modeled as a node with zero or more child nodes, each +representing a serial sub-node device that is mux'd as part of the GSBI +configuration settings. The mode setting will govern the input/output mode of +the 4 GSBI IOs. + +Required properties: +- compatible: must contain "qcom,gsbi-v1.0.0" for APQ8064/IPQ8064 +- reg: Address range for GSBI registers +- clocks: required clock +- clock-names: must contain "iface" entry +- qcom,mode : indicates MUX value for configuration of the serial interface. + Please reference dt-bindings/soc/qcom,gsbi.h for valid mux values. + +Optional properties: +- qcom,crci : indicates CRCI MUX value for QUP CRCI ports. Please reference + dt-bindings/soc/qcom,gsbi.h for valid CRCI mux values. + +Required properties if child node exists: +- #address-cells: Must be 1 +- #size-cells: Must be 1 +- ranges: Must be present + +Properties for children: + +A GSBI controller node can contain 0 or more child nodes representing serial +devices. These serial devices can be a QCOM UART, I2C controller, spi +controller, or some combination of aforementioned devices. + +See the following for child node definitions: +Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt +Documentation/devicetree/bindings/spi/qcom,spi-qup.txt +Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt + +Example for APQ8064: + +#include + + gsbi4@16300000 { + compatible = "qcom,gsbi-v1.0.0"; + reg = <0x16300000 0x100>; + clocks = <&gcc GSBI4_H_CLK>; + clock-names = "iface"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + qcom,mode = ; + qcom,crci = ; + + /* child nodes go under here */ + + i2c_qup4: i2c@16380000 { + compatible = "qcom,i2c-qup-v1.1.1"; + reg = <0x16380000 0x1000>; + interrupts = <0 153 0>; + + clocks = <&gcc GSBI4_QUP_CLK>, <&gcc GSBI4_H_CLK>; + clock-names = "core", "iface"; + + clock-frequency = <200000>; + + #address-cells = <1>; + #size-cells = <0>; + + }; + + uart4: serial@16340000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x16340000 0x1000>, + <0x16300000 0x1000>; + interrupts = <0 152 0x0>; + clocks = <&gcc GSBI4_UART_CLK>, <&gcc GSBI4_H_CLK>; + clock-names = "core", "iface"; + status = "ok"; + }; + }; + diff --git a/include/dt-bindings/soc/qcom,gsbi.h b/include/dt-bindings/soc/qcom,gsbi.h new file mode 100644 index 000000000000..7ac4292333aa --- /dev/null +++ b/include/dt-bindings/soc/qcom,gsbi.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __DT_BINDINGS_QCOM_GSBI_H +#define __DT_BINDINGS_QCOM_GSBI_H + +#define GSBI_PROT_IDLE 0 +#define GSBI_PROT_I2C_UIM 1 +#define GSBI_PROT_I2C 2 +#define GSBI_PROT_SPI 3 +#define GSBI_PROT_UART_W_FC 4 +#define GSBI_PROT_UIM 5 +#define GSBI_PROT_I2C_UART 6 + +#define GSBI_CRCI_QUP 0 +#define GSBI_CRCI_UART 1 + +#endif -- cgit v1.2.3 From 43339bed7010da6e7cf797db3216a136a974a0cd Mon Sep 17 00:00:00 2001 From: Eli Billauer Date: Fri, 16 May 2014 11:26:35 +0300 Subject: devres: Add devm_get_free_pages API devm_get_free_pages() and devm_free_pages() are the managed counterparts for __get_free_pages() and free_pages(). Signed-off-by: Eli Billauer Acked-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-model/devres.txt | 2 + drivers/base/devres.c | 76 +++++++++++++++++++++++++++++++++++ include/linux/device.h | 4 ++ 3 files changed, 82 insertions(+) (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 499951873997..e1a27074caae 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -237,6 +237,8 @@ MEM devm_kzalloc() devm_kfree() devm_kmemdup() + devm_get_free_pages() + devm_free_pages() IIO devm_iio_device_alloc() diff --git a/drivers/base/devres.c b/drivers/base/devres.c index d0914cba2413..52302946770f 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -852,3 +852,79 @@ void *devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp) return p; } EXPORT_SYMBOL_GPL(devm_kmemdup); + +struct pages_devres { + unsigned long addr; + unsigned int order; +}; + +static int devm_pages_match(struct device *dev, void *res, void *p) +{ + struct pages_devres *devres = res; + struct pages_devres *target = p; + + return devres->addr == target->addr; +} + +static void devm_pages_release(struct device *dev, void *res) +{ + struct pages_devres *devres = res; + + free_pages(devres->addr, devres->order); +} + +/** + * devm_get_free_pages - Resource-managed __get_free_pages + * @dev: Device to allocate memory for + * @gfp_mask: Allocation gfp flags + * @order: Allocation size is (1 << order) pages + * + * Managed get_free_pages. Memory allocated with this function is + * automatically freed on driver detach. + * + * RETURNS: + * Address of allocated memory on success, 0 on failure. + */ + +unsigned long devm_get_free_pages(struct device *dev, + gfp_t gfp_mask, unsigned int order) +{ + struct pages_devres *devres; + unsigned long addr; + + addr = __get_free_pages(gfp_mask, order); + + if (unlikely(!addr)) + return 0; + + devres = devres_alloc(devm_pages_release, + sizeof(struct pages_devres), GFP_KERNEL); + if (unlikely(!devres)) { + free_pages(addr, order); + return 0; + } + + devres->addr = addr; + devres->order = order; + + devres_add(dev, devres); + return addr; +} +EXPORT_SYMBOL_GPL(devm_get_free_pages); + +/** + * devm_free_pages - Resource-managed free_pages + * @dev: Device this memory belongs to + * @addr: Memory to free + * + * Free memory allocated with devm_get_free_pages(). Unlike free_pages, + * there is no need to supply the @order. + */ +void devm_free_pages(struct device *dev, unsigned long addr) +{ + struct pages_devres devres = { .addr = addr }; + + WARN_ON(devres_release(dev, devm_pages_release, devm_pages_match, + &devres)); +} +EXPORT_SYMBOL_GPL(devm_free_pages); diff --git a/include/linux/device.h b/include/linux/device.h index ab871588da89..3dc69a2faa51 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -626,6 +626,10 @@ extern char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp); extern void *devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp); +extern unsigned long devm_get_free_pages(struct device *dev, + gfp_t gfp_mask, unsigned int order); +extern void devm_free_pages(struct device *dev, unsigned long addr); + void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res); void __iomem *devm_request_and_ioremap(struct device *dev, struct resource *res); -- cgit v1.2.3 From 9f8c0fe9542141fd0008d5c0f6ae365890f6da94 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 23 May 2014 16:44:10 +0100 Subject: regulator: Constify the pointer to alias name array Toughen-up checks for read-only regulator names. Signed-off-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/core.c | 7 ++++--- drivers/regulator/devres.c | 6 +++--- include/linux/mfd/core.h | 2 +- include/linux/regulator/consumer.h | 36 ++++++++++++++++++++---------------- 4 files changed, 28 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9a09f3cdbabb..ba28d29b66d2 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1597,9 +1597,10 @@ EXPORT_SYMBOL_GPL(regulator_unregister_supply_alias); * registered any aliases that were registered will be removed * before returning to the caller. */ -int regulator_bulk_register_supply_alias(struct device *dev, const char **id, +int regulator_bulk_register_supply_alias(struct device *dev, + const char *const *id, struct device *alias_dev, - const char **alias_id, + const char *const *alias_id, int num_id) { int i; @@ -1637,7 +1638,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_register_supply_alias); * aliases in one operation. */ void regulator_bulk_unregister_supply_alias(struct device *dev, - const char **id, + const char *const *id, int num_id) { int i; diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index f44818b838dc..8f785bc9e510 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -360,9 +360,9 @@ EXPORT_SYMBOL_GPL(devm_regulator_unregister_supply_alias); * will be removed before returning to the caller. */ int devm_regulator_bulk_register_supply_alias(struct device *dev, - const char **id, + const char *const *id, struct device *alias_dev, - const char **alias_id, + const char *const *alias_id, int num_id) { int i; @@ -404,7 +404,7 @@ EXPORT_SYMBOL_GPL(devm_regulator_bulk_register_supply_alias); * will ensure that the resource is freed. */ void devm_regulator_bulk_unregister_supply_alias(struct device *dev, - const char **id, + const char *const *id, int num_id) { int i; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index bdba8c61207b..f543de91ce19 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -63,7 +63,7 @@ struct mfd_cell { /* A list of regulator supplies that should be mapped to the MFD * device rather than the child device when requested */ - const char **parent_supplies; + const char * const *parent_supplies; int num_parent_supplies; }; diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index e530681bea70..10d0a53f4cd3 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -151,11 +151,13 @@ int regulator_register_supply_alias(struct device *dev, const char *id, const char *alias_id); void regulator_unregister_supply_alias(struct device *dev, const char *id); -int regulator_bulk_register_supply_alias(struct device *dev, const char **id, +int regulator_bulk_register_supply_alias(struct device *dev, + const char *const *id, struct device *alias_dev, - const char **alias_id, int num_id); + const char *const *alias_id, + int num_id); void regulator_bulk_unregister_supply_alias(struct device *dev, - const char **id, int num_id); + const char * const *id, int num_id); int devm_regulator_register_supply_alias(struct device *dev, const char *id, struct device *alias_dev, @@ -164,12 +166,12 @@ void devm_regulator_unregister_supply_alias(struct device *dev, const char *id); int devm_regulator_bulk_register_supply_alias(struct device *dev, - const char **id, + const char *const *id, struct device *alias_dev, - const char **alias_id, + const char *const *alias_id, int num_id); void devm_regulator_bulk_unregister_supply_alias(struct device *dev, - const char **id, + const char *const *id, int num_id); /* regulator output control and status */ @@ -290,17 +292,17 @@ static inline void regulator_unregister_supply_alias(struct device *dev, } static inline int regulator_bulk_register_supply_alias(struct device *dev, - const char **id, - struct device *alias_dev, - const char **alias_id, - int num_id) + const char *const *id, + struct device *alias_dev, + const char * const *alias_id, + int num_id) { return 0; } static inline void regulator_bulk_unregister_supply_alias(struct device *dev, - const char **id, - int num_id) + const char * const *id, + int num_id) { } @@ -317,15 +319,17 @@ static inline void devm_regulator_unregister_supply_alias(struct device *dev, { } -static inline int devm_regulator_bulk_register_supply_alias( - struct device *dev, const char **id, struct device *alias_dev, - const char **alias_id, int num_id) +static inline int devm_regulator_bulk_register_supply_alias(struct device *dev, + const char *const *id, + struct device *alias_dev, + const char *const *alias_id, + int num_id) { return 0; } static inline void devm_regulator_bulk_unregister_supply_alias( - struct device *dev, const char **id, int num_id) + struct device *dev, const char *const *id, int num_id) { } -- cgit v1.2.3 From d7b2545023ecfde94d3ea9c03c5480ac18da96c9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 23 May 2014 13:19:53 +0300 Subject: Bluetooth: Clearly distinguish mgmt LTK type from authenticated property On the mgmt level we have a key type parameter which currently accepts two possible values: 0x00 for unauthenticated and 0x01 for authenticated. However, in the internal struct smp_ltk representation we have an explicit "authenticated" boolean value. To make this distinction clear, add defines for the possible mgmt values and do conversion to and from the internal authenticated value. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 3 +++ net/bluetooth/mgmt.c | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 226ae03cafe7..bcffc9ae0c89 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -181,6 +181,9 @@ struct mgmt_cp_load_link_keys { } __packed; #define MGMT_LOAD_LINK_KEYS_SIZE 3 +#define MGMT_LTK_UNAUTHENTICATED 0x00 +#define MGMT_LTK_AUTHENTICATED 0x01 + struct mgmt_ltk_info { struct mgmt_addr_info addr; __u8 type; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f8ca69dd1984..5e9c21a5525f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4534,7 +4534,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, for (i = 0; i < key_count; i++) { struct mgmt_ltk_info *key = &cp->keys[i]; - u8 type, addr_type; + u8 type, addr_type, authenticated; if (key->addr.type == BDADDR_LE_PUBLIC) addr_type = ADDR_LE_DEV_PUBLIC; @@ -4546,8 +4546,13 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, else type = HCI_SMP_LTK_SLAVE; + if (key->type == MGMT_LTK_UNAUTHENTICATED) + authenticated = 0x00; + else + authenticated = 0x01; + hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type, - key->type, key->val, key->enc_size, key->ediv, + authenticated, key->val, key->enc_size, key->ediv, key->rand); } @@ -5222,6 +5227,14 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } +static u8 mgmt_ltk_type(struct smp_ltk *ltk) +{ + if (ltk->authenticated) + return MGMT_LTK_AUTHENTICATED; + + return MGMT_LTK_UNAUTHENTICATED; +} + void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) { struct mgmt_ev_new_long_term_key ev; @@ -5247,7 +5260,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) bacpy(&ev.key.addr.bdaddr, &key->bdaddr); ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type); - ev.key.type = key->authenticated; + ev.key.type = mgmt_ltk_type(key); ev.key.enc_size = key->enc_size; ev.key.ediv = key->ediv; ev.key.rand = key->rand; -- cgit v1.2.3 From ed616689a3d95eb6c9bdbb1ef74b0f50cbdf276a Mon Sep 17 00:00:00 2001 From: Sucheta Chakraborty Date: Thu, 22 May 2014 09:59:05 -0400 Subject: net-next:v4: Add support to configure SR-IOV VF minimum and maximum Tx rate through ip tool. o min_tx_rate puts lower limit on the VF bandwidth. VF is guaranteed to have a bandwidth of at least this value. max_tx_rate puts cap on the VF bandwidth. VF can have a bandwidth of up to this value. o A new handler set_vf_rate for attr IFLA_VF_RATE has been introduced which takes 4 arguments: netdev, VF number, min_tx_rate, max_tx_rate o ndo_set_vf_rate replaces ndo_set_vf_tx_rate handler. o Drivers that currently implement ndo_set_vf_tx_rate should now call ndo_set_vf_rate instead and reject attempt to set a minimum bandwidth greater than 0 for IFLA_VF_TX_RATE when IFLA_VF_RATE is not yet implemented by driver. o If user enters only one of either min_tx_rate or max_tx_rate, then, userland should read back the other value from driver and set both for IFLA_VF_RATE. Drivers that have not yet implemented IFLA_VF_RATE should always return min_tx_rate as 0 when read from ip tool. o If both IFLA_VF_TX_RATE and IFLA_VF_RATE options are specified, then IFLA_VF_RATE should override. o Idea is to have consistent display of rate values to user. o Usage example: - ./ip link set p4p1 vf 0 rate 900 ./ip link show p4p1 32: p4p1: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 00:0e:1e:08:b0:f0 brd ff:ff:ff:ff:ff:ff vf 0 MAC 3e:a0:ca:bd:ae:5a, tx rate 900 (Mbps), max_tx_rate 900Mbps vf 1 MAC f6:c6:7c:3f:3d:6c vf 2 MAC 56:32:43:98:d7:71 vf 3 MAC d6:be:c3:b5:85:ff vf 4 MAC ee:a9:9a:1e:19:14 vf 5 MAC 4a:d0:4c:07:52:18 vf 6 MAC 3a:76:44:93:62:f9 vf 7 MAC 82:e9:e7:e3:15:1a ./ip link set p4p1 vf 0 max_tx_rate 300 min_tx_rate 200 ./ip link show p4p1 32: p4p1: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 00:0e:1e:08:b0:f0 brd ff:ff:ff:ff:ff:ff vf 0 MAC 3e:a0:ca:bd:ae:5a, tx rate 300 (Mbps), max_tx_rate 300Mbps, min_tx_rate 200Mbps vf 1 MAC f6:c6:7c:3f:3d:6c vf 2 MAC 56:32:43:98:d7:71 vf 3 MAC d6:be:c3:b5:85:ff vf 4 MAC ee:a9:9a:1e:19:14 vf 5 MAC 4a:d0:4c:07:52:18 vf 6 MAC 3a:76:44:93:62:f9 vf 7 MAC 82:e9:e7:e3:15:1a ./ip link set p4p1 vf 0 max_tx_rate 600 rate 300 ./ip link show p4p1 32: p4p1: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 00:0e:1e:08:b0:f brd ff:ff:ff:ff:ff:ff vf 0 MAC 3e:a0:ca:bd:ae:5, tx rate 600 (Mbps), max_tx_rate 600Mbps, min_tx_rate 200Mbps vf 1 MAC f6:c6:7c:3f:3d:6c vf 2 MAC 56:32:43:98:d7:71 vf 3 MAC d6:be:c3:b5:85:ff vf 4 MAC ee:a9:9a:1e:19:14 vf 5 MAC 4a:d0:4c:07:52:18 vf 6 MAC 3a:76:44:93:62:f9 vf 7 MAC 82:e9:e7:e3:15:1a Signed-off-by: Sucheta Chakraborty Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c | 3 +- drivers/net/ethernet/emulex/benet/be_main.c | 21 +++++---- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 26 +++++++---- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h | 3 +- drivers/net/ethernet/intel/igb/igb_main.c | 20 ++++++--- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 13 ++++-- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h | 3 +- drivers/net/ethernet/mellanox/mlx4/cmd.c | 11 ++--- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 1 + drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h | 2 +- .../ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 1 + .../net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | 52 +++++++++++++++------- drivers/net/ethernet/sfc/siena_sriov.c | 3 +- include/linux/if_link.h | 3 +- include/linux/netdevice.h | 8 ++-- include/uapi/linux/if_link.h | 9 +++- net/core/rtnetlink.c | 38 +++++++++++++--- 20 files changed, 156 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 81cc2d9831c2..8d0479d5be8e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -2576,7 +2576,8 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, ivi->vf = vfidx; ivi->qos = 0; - ivi->tx_rate = 10000; /* always 10G. TBA take from link struct */ + ivi->max_tx_rate = 10000; /* always 10G. TBA take from link struct */ + ivi->min_tx_rate = 0; ivi->spoofchk = 1; /*always enabled */ if (vf->state == VF_ENABLED) { /* mac and vlan are in vlan_mac objects */ diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index dcc5e5c69743..4693d004a223 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1302,7 +1302,8 @@ static int be_get_vf_config(struct net_device *netdev, int vf, return -EINVAL; vi->vf = vf; - vi->tx_rate = vf_cfg->tx_rate; + vi->max_tx_rate = vf_cfg->tx_rate; + vi->min_tx_rate = 0; vi->vlan = vf_cfg->vlan_tag & VLAN_VID_MASK; vi->qos = vf_cfg->vlan_tag >> VLAN_PRIO_SHIFT; memcpy(&vi->mac, vf_cfg->mac_addr, ETH_ALEN); @@ -1342,7 +1343,8 @@ static int be_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos) return status; } -static int be_set_vf_tx_rate(struct net_device *netdev, int vf, int rate) +static int be_set_vf_tx_rate(struct net_device *netdev, int vf, + int min_tx_rate, int max_tx_rate) { struct be_adapter *adapter = netdev_priv(netdev); int status = 0; @@ -1353,18 +1355,21 @@ static int be_set_vf_tx_rate(struct net_device *netdev, int vf, int rate) if (vf >= adapter->num_vfs) return -EINVAL; - if (rate < 100 || rate > 10000) { + if (min_tx_rate) + return -EINVAL; + + if (max_tx_rate < 100 || max_tx_rate > 10000) { dev_err(&adapter->pdev->dev, - "tx rate must be between 100 and 10000 Mbps\n"); + "max tx rate must be between 100 and 10000 Mbps\n"); return -EINVAL; } - status = be_cmd_config_qos(adapter, rate / 10, vf + 1); + status = be_cmd_config_qos(adapter, max_tx_rate / 10, vf + 1); if (status) dev_err(&adapter->pdev->dev, - "tx rate %d on VF %d failed\n", rate, vf); + "max tx rate %d on VF %d failed\n", max_tx_rate, vf); else - adapter->vf_cfg[vf].tx_rate = rate; + adapter->vf_cfg[vf].tx_rate = max_tx_rate; return status; } static int be_set_vf_link_state(struct net_device *netdev, int vf, @@ -4257,7 +4262,7 @@ static const struct net_device_ops be_netdev_ops = { .ndo_vlan_rx_kill_vid = be_vlan_rem_vid, .ndo_set_vf_mac = be_set_vf_mac, .ndo_set_vf_vlan = be_set_vf_vlan, - .ndo_set_vf_tx_rate = be_set_vf_tx_rate, + .ndo_set_vf_rate = be_set_vf_tx_rate, .ndo_get_vf_config = be_get_vf_config, .ndo_set_vf_link_state = be_set_vf_link_state, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e0e5c6a867b1..96f7fabd8758 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6724,7 +6724,7 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_set_features = i40e_set_features, .ndo_set_vf_mac = i40e_ndo_set_vf_mac, .ndo_set_vf_vlan = i40e_ndo_set_vf_port_vlan, - .ndo_set_vf_tx_rate = i40e_ndo_set_vf_bw, + .ndo_set_vf_rate = i40e_ndo_set_vf_bw, .ndo_get_vf_config = i40e_ndo_get_vf_config, .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state, #ifdef CONFIG_I40E_VXLAN diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 4d219566a04d..8564b0939dc4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2205,7 +2205,8 @@ error_pvid: * * configure vf tx rate **/ -int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate) +int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, + int max_tx_rate) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; @@ -2221,6 +2222,12 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate) goto error; } + if (min_tx_rate) { + dev_err(&pf->pdev->dev, "Invalid min tx rate (%d) (greater than 0) specified for vf %d.\n", + min_tx_rate, vf_id); + return -EINVAL; + } + vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_index]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { @@ -2243,23 +2250,23 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate) break; } - if (tx_rate > speed) { - dev_err(&pf->pdev->dev, "Invalid tx rate %d specified for vf %d.", - tx_rate, vf->vf_id); + if (max_tx_rate > speed) { + dev_err(&pf->pdev->dev, "Invalid max tx rate %d specified for vf %d.", + max_tx_rate, vf->vf_id); ret = -EINVAL; goto error; } /* Tx rate credits are in values of 50Mbps, 0 is disabled*/ - ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid, tx_rate / 50, 0, - NULL); + ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid, max_tx_rate / 50, + 0, NULL); if (ret) { - dev_err(&pf->pdev->dev, "Unable to set tx rate, error code %d.\n", + dev_err(&pf->pdev->dev, "Unable to set max tx rate, error code %d.\n", ret); ret = -EIO; goto error; } - vf->tx_rate = tx_rate; + vf->tx_rate = max_tx_rate; error: return ret; } @@ -2301,7 +2308,8 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, memcpy(&ivi->mac, vf->default_lan_addr.addr, ETH_ALEN); - ivi->tx_rate = vf->tx_rate; + ivi->max_tx_rate = vf->tx_rate; + ivi->min_tx_rate = 0; ivi->vlan = le16_to_cpu(vsi->info.pvid) & I40E_VLAN_MASK; ivi->qos = (le16_to_cpu(vsi->info.pvid) & I40E_PRIORITY_MASK) >> I40E_VLAN_PRIORITY_SHIFT; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index ba3d1f8414be..5a559be4ba2c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -116,7 +116,8 @@ void i40e_vc_notify_vf_reset(struct i40e_vf *vf); int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac); int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos); -int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate); +int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, + int max_tx_rate); int i40e_ndo_get_vf_config(struct net_device *netdev, int vf_id, struct ifla_vf_info *ivi); int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index bfcda8a455f4..1075b3f8c415 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -169,7 +169,7 @@ static void igb_restore_vf_multicasts(struct igb_adapter *adapter); static int igb_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac); static int igb_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos); -static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate); +static int igb_ndo_set_vf_bw(struct net_device *, int, int, int); static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); static int igb_ndo_get_vf_config(struct net_device *netdev, int vf, @@ -2084,7 +2084,7 @@ static const struct net_device_ops igb_netdev_ops = { .ndo_vlan_rx_kill_vid = igb_vlan_rx_kill_vid, .ndo_set_vf_mac = igb_ndo_set_vf_mac, .ndo_set_vf_vlan = igb_ndo_set_vf_vlan, - .ndo_set_vf_tx_rate = igb_ndo_set_vf_bw, + .ndo_set_vf_rate = igb_ndo_set_vf_bw, .ndo_set_vf_spoofchk = igb_ndo_set_vf_spoofchk, .ndo_get_vf_config = igb_ndo_get_vf_config, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -7879,7 +7879,8 @@ static void igb_check_vf_rate_limit(struct igb_adapter *adapter) } } -static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate) +static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, + int min_tx_rate, int max_tx_rate) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -7888,15 +7889,19 @@ static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate) if (hw->mac.type != e1000_82576) return -EOPNOTSUPP; + if (min_tx_rate) + return -EINVAL; + actual_link_speed = igb_link_mbps(adapter->link_speed); if ((vf >= adapter->vfs_allocated_count) || (!(rd32(E1000_STATUS) & E1000_STATUS_LU)) || - (tx_rate < 0) || (tx_rate > actual_link_speed)) + (max_tx_rate < 0) || + (max_tx_rate > actual_link_speed)) return -EINVAL; adapter->vf_rate_link_speed = actual_link_speed; - adapter->vf_data[vf].tx_rate = (u16)tx_rate; - igb_set_vf_rate_limit(hw, vf, tx_rate, actual_link_speed); + adapter->vf_data[vf].tx_rate = (u16)max_tx_rate; + igb_set_vf_rate_limit(hw, vf, max_tx_rate, actual_link_speed); return 0; } @@ -7936,7 +7941,8 @@ static int igb_ndo_get_vf_config(struct net_device *netdev, return -EINVAL; ivi->vf = vf; memcpy(&ivi->mac, adapter->vf_data[vf].vf_mac_addresses, ETH_ALEN); - ivi->tx_rate = adapter->vf_data[vf].tx_rate; + ivi->max_tx_rate = adapter->vf_data[vf].tx_rate; + ivi->min_tx_rate = 0; ivi->vlan = adapter->vf_data[vf].pf_vlan; ivi->qos = adapter->vf_data[vf].pf_qos; ivi->spoofchk = adapter->vf_data[vf].spoofchk_enabled; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 8089ea9f2fba..a5332389620a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7926,7 +7926,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_do_ioctl = ixgbe_ioctl, .ndo_set_vf_mac = ixgbe_ndo_set_vf_mac, .ndo_set_vf_vlan = ixgbe_ndo_set_vf_vlan, - .ndo_set_vf_tx_rate = ixgbe_ndo_set_vf_bw, + .ndo_set_vf_rate = ixgbe_ndo_set_vf_bw, .ndo_set_vf_spoofchk = ixgbe_ndo_set_vf_spoofchk, .ndo_get_vf_config = ixgbe_ndo_get_vf_config, .ndo_get_stats64 = ixgbe_get_stats64, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index a01417c06620..3248e208c9dc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -1222,7 +1222,8 @@ void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter) } } -int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate) +int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate, + int max_tx_rate) { struct ixgbe_adapter *adapter = netdev_priv(netdev); int link_speed; @@ -1240,13 +1241,16 @@ int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate) if (link_speed != 10000) return -EINVAL; + if (min_tx_rate) + return -EINVAL; + /* rate limit cannot be less than 10Mbs or greater than link speed */ - if (tx_rate && ((tx_rate <= 10) || (tx_rate > link_speed))) + if (max_tx_rate && ((max_tx_rate <= 10) || (max_tx_rate > link_speed))) return -EINVAL; /* store values */ adapter->vf_rate_link_speed = link_speed; - adapter->vfinfo[vf].tx_rate = tx_rate; + adapter->vfinfo[vf].tx_rate = max_tx_rate; /* update hardware configuration */ ixgbe_set_vf_rate_limit(adapter, vf); @@ -1288,7 +1292,8 @@ int ixgbe_ndo_get_vf_config(struct net_device *netdev, return -EINVAL; ivi->vf = vf; memcpy(&ivi->mac, adapter->vfinfo[vf].vf_mac_addresses, ETH_ALEN); - ivi->tx_rate = adapter->vfinfo[vf].tx_rate; + ivi->max_tx_rate = adapter->vfinfo[vf].tx_rate; + ivi->min_tx_rate = 0; ivi->vlan = adapter->vfinfo[vf].pf_vlan; ivi->qos = adapter->vfinfo[vf].pf_qos; ivi->spoofchk = adapter->vfinfo[vf].spoofchk_enabled; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h index cea640147604..32c26d586c01 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h @@ -44,7 +44,8 @@ void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter); int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int queue, u8 *mac); int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan, u8 qos); -int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate); +int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate, + int max_tx_rate); int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); int ixgbe_ndo_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivi); diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 59c7fd406805..ca8e7cb5a8e4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -2486,11 +2486,12 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in ivf->mac[4] = ((s_info->mac >> (1*8)) & 0xff); ivf->mac[5] = ((s_info->mac) & 0xff); - ivf->vlan = s_info->default_vlan; - ivf->qos = s_info->default_qos; - ivf->tx_rate = s_info->tx_rate; - ivf->spoofchk = s_info->spoofchk; - ivf->linkstate = s_info->link_state; + ivf->vlan = s_info->default_vlan; + ivf->qos = s_info->default_qos; + ivf->max_tx_rate = s_info->tx_rate; + ivf->min_tx_rate = 0; + ivf->spoofchk = s_info->spoofchk; + ivf->linkstate = s_info->link_state; return 0; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 6e7527e2b595..41abe6070466 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1319,6 +1319,7 @@ struct qlcnic_eswitch { #define QL_STATUS_INVALID_PARAM -1 #define MAX_BW 100 /* % of link speed */ +#define MIN_BW 1 /* % of link speed */ #define MAX_VLAN_ID 4095 #define MIN_VLAN_ID 2 #define DEFAULT_MAC_LEARN 1 diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index f0a285359e66..f06ba90b4282 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -525,7 +525,7 @@ static const struct net_device_ops qlcnic_netdev_ops = { #endif #ifdef CONFIG_QLCNIC_SRIOV .ndo_set_vf_mac = qlcnic_sriov_set_vf_mac, - .ndo_set_vf_tx_rate = qlcnic_sriov_set_vf_tx_rate, + .ndo_set_vf_rate = qlcnic_sriov_set_vf_tx_rate, .ndo_get_vf_config = qlcnic_sriov_get_vf_config, .ndo_set_vf_vlan = qlcnic_sriov_set_vf_vlan, .ndo_set_vf_spoofchk = qlcnic_sriov_set_vf_spoofchk, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index 335b50f7bd3e..4677b2edccca 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h @@ -233,7 +233,7 @@ bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *, void qlcnic_sriov_pf_reset(struct qlcnic_adapter *); int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *); int qlcnic_sriov_set_vf_mac(struct net_device *, int, u8 *); -int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int); +int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int, int); int qlcnic_sriov_get_vf_config(struct net_device *, int , struct ifla_vf_info *); int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 498fa6350c8d..2bdd9deffb38 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -201,6 +201,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) sriov->vf_info[i].vp = vp; vp->vlan_mode = QLC_GUEST_VLAN_MODE; vp->max_tx_bw = MAX_BW; + vp->min_tx_bw = MIN_BW; vp->spoofchk = false; random_ether_addr(vp->mac); dev_info(&adapter->pdev->dev, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index 6d2f72f114f2..a29538b86edf 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c @@ -1848,7 +1848,8 @@ int qlcnic_sriov_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) return 0; } -int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate) +int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, + int min_tx_rate, int max_tx_rate) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_sriov *sriov = adapter->ahw->sriov; @@ -1863,35 +1864,52 @@ int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate) if (vf >= sriov->num_vfs) return -EINVAL; - if (tx_rate >= 10000 || tx_rate < 100) { + vf_info = &sriov->vf_info[vf]; + vp = vf_info->vp; + vpid = vp->handle; + + if (!min_tx_rate) + min_tx_rate = QLC_VF_MIN_TX_RATE; + + if (max_tx_rate && + (max_tx_rate >= 10000 || max_tx_rate < min_tx_rate)) { netdev_err(netdev, - "Invalid Tx rate, allowed range is [%d - %d]", - QLC_VF_MIN_TX_RATE, QLC_VF_MAX_TX_RATE); + "Invalid max Tx rate, allowed range is [%d - %d]", + min_tx_rate, QLC_VF_MAX_TX_RATE); return -EINVAL; } - if (tx_rate == 0) - tx_rate = 10000; + if (!max_tx_rate) + max_tx_rate = 10000; - vf_info = &sriov->vf_info[vf]; - vp = vf_info->vp; - vpid = vp->handle; + if (min_tx_rate && + (min_tx_rate > max_tx_rate || min_tx_rate < QLC_VF_MIN_TX_RATE)) { + netdev_err(netdev, + "Invalid min Tx rate, allowed range is [%d - %d]", + QLC_VF_MIN_TX_RATE, max_tx_rate); + return -EINVAL; + } if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { if (qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, vpid)) return -EIO; - nic_info.max_tx_bw = tx_rate / 100; + nic_info.max_tx_bw = max_tx_rate / 100; + nic_info.min_tx_bw = min_tx_rate / 100; nic_info.bit_offsets = BIT_0; if (qlcnic_sriov_pf_set_vport_info(adapter, &nic_info, vpid)) return -EIO; } - vp->max_tx_bw = tx_rate / 100; + vp->max_tx_bw = max_tx_rate / 100; + netdev_info(netdev, + "Setting Max Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n", + max_tx_rate, vp->max_tx_bw, vf); + vp->min_tx_bw = min_tx_rate / 100; netdev_info(netdev, - "Setting Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n", - tx_rate, vp->max_tx_bw, vf); + "Setting Min Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n", + min_tx_rate, vp->min_tx_bw, vf); return 0; } @@ -1990,9 +2008,13 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev, ivi->qos = vp->qos; ivi->spoofchk = vp->spoofchk; if (vp->max_tx_bw == MAX_BW) - ivi->tx_rate = 0; + ivi->max_tx_rate = 0; + else + ivi->max_tx_rate = vp->max_tx_bw * 100; + if (vp->min_tx_bw == MIN_BW) + ivi->min_tx_rate = 0; else - ivi->tx_rate = vp->max_tx_bw * 100; + ivi->min_tx_rate = vp->min_tx_bw * 100; ivi->vf = vf; return 0; diff --git a/drivers/net/ethernet/sfc/siena_sriov.c b/drivers/net/ethernet/sfc/siena_sriov.c index 9a9205e77896..43d2e64546ed 100644 --- a/drivers/net/ethernet/sfc/siena_sriov.c +++ b/drivers/net/ethernet/sfc/siena_sriov.c @@ -1633,7 +1633,8 @@ int efx_sriov_get_vf_config(struct net_device *net_dev, int vf_i, ivi->vf = vf_i; ether_addr_copy(ivi->mac, vf->addr.mac_addr); - ivi->tx_rate = 0; + ivi->max_tx_rate = 0; + ivi->min_tx_rate = 0; tci = ntohs(vf->addr.tci); ivi->vlan = tci & VLAN_VID_MASK; ivi->qos = (tci >> VLAN_PRIO_SHIFT) & 0x7; diff --git a/include/linux/if_link.h b/include/linux/if_link.h index a86784dec3d3..119130e9298b 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -10,8 +10,9 @@ struct ifla_vf_info { __u8 mac[32]; __u32 vlan; __u32 qos; - __u32 tx_rate; __u32 spoofchk; __u32 linkstate; + __u32 min_tx_rate; + __u32 max_tx_rate; }; #endif /* _LINUX_IF_LINK_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f4ad247fd324..9ec3a945caf2 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -850,7 +850,8 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * SR-IOV management functions. * int (*ndo_set_vf_mac)(struct net_device *dev, int vf, u8* mac); * int (*ndo_set_vf_vlan)(struct net_device *dev, int vf, u16 vlan, u8 qos); - * int (*ndo_set_vf_tx_rate)(struct net_device *dev, int vf, int rate); + * int (*ndo_set_vf_rate)(struct net_device *dev, int vf, int min_tx_rate, + * int max_tx_rate); * int (*ndo_set_vf_spoofchk)(struct net_device *dev, int vf, bool setting); * int (*ndo_get_vf_config)(struct net_device *dev, * int vf, struct ifla_vf_info *ivf); @@ -1044,8 +1045,9 @@ struct net_device_ops { int queue, u8 *mac); int (*ndo_set_vf_vlan)(struct net_device *dev, int queue, u16 vlan, u8 qos); - int (*ndo_set_vf_tx_rate)(struct net_device *dev, - int vf, int rate); + int (*ndo_set_vf_rate)(struct net_device *dev, + int vf, int min_tx_rate, + int max_tx_rate); int (*ndo_set_vf_spoofchk)(struct net_device *dev, int vf, bool setting); int (*ndo_get_vf_config)(struct net_device *dev, diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 9a7f7ace6649..622e7910b8cc 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -399,9 +399,10 @@ enum { IFLA_VF_UNSPEC, IFLA_VF_MAC, /* Hardware queue specific attributes */ IFLA_VF_VLAN, - IFLA_VF_TX_RATE, /* TX Bandwidth Allocation */ + IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */ IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */ + IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */ __IFLA_VF_MAX, }; @@ -423,6 +424,12 @@ struct ifla_vf_tx_rate { __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ }; +struct ifla_vf_rate { + __u32 vf; + __u32 min_tx_rate; /* Min Bandwidth in Mbps */ + __u32 max_tx_rate; /* Max Bandwidth in Mbps */ +}; + struct ifla_vf_spoofchk { __u32 vf; __u32 setting; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9837bebf93ce..d6417464dc66 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -767,8 +767,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev, size += num_vfs * (nla_total_size(sizeof(struct ifla_vf_mac)) + nla_total_size(sizeof(struct ifla_vf_vlan)) + - nla_total_size(sizeof(struct ifla_vf_tx_rate)) + - nla_total_size(sizeof(struct ifla_vf_spoofchk))); + nla_total_size(sizeof(struct ifla_vf_spoofchk)) + + nla_total_size(sizeof(struct ifla_vf_rate))); return size; } else return 0; @@ -1034,6 +1034,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, struct ifla_vf_info ivi; struct ifla_vf_mac vf_mac; struct ifla_vf_vlan vf_vlan; + struct ifla_vf_rate vf_rate; struct ifla_vf_tx_rate vf_tx_rate; struct ifla_vf_spoofchk vf_spoofchk; struct ifla_vf_link_state vf_linkstate; @@ -1054,6 +1055,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, break; vf_mac.vf = vf_vlan.vf = + vf_rate.vf = vf_tx_rate.vf = vf_spoofchk.vf = vf_linkstate.vf = ivi.vf; @@ -1061,7 +1063,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac)); vf_vlan.vlan = ivi.vlan; vf_vlan.qos = ivi.qos; - vf_tx_rate.rate = ivi.tx_rate; + vf_tx_rate.rate = ivi.max_tx_rate; + vf_rate.min_tx_rate = ivi.min_tx_rate; + vf_rate.max_tx_rate = ivi.max_tx_rate; vf_spoofchk.setting = ivi.spoofchk; vf_linkstate.link_state = ivi.linkstate; vf = nla_nest_start(skb, IFLA_VF_INFO); @@ -1071,6 +1075,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, } if (nla_put(skb, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac) || nla_put(skb, IFLA_VF_VLAN, sizeof(vf_vlan), &vf_vlan) || + nla_put(skb, IFLA_VF_RATE, sizeof(vf_rate), + &vf_rate) || nla_put(skb, IFLA_VF_TX_RATE, sizeof(vf_tx_rate), &vf_tx_rate) || nla_put(skb, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk), @@ -1177,6 +1183,8 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = { .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) }, }; static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = { @@ -1336,11 +1344,29 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr) } case IFLA_VF_TX_RATE: { struct ifla_vf_tx_rate *ivt; + struct ifla_vf_info ivf; ivt = nla_data(vf); err = -EOPNOTSUPP; - if (ops->ndo_set_vf_tx_rate) - err = ops->ndo_set_vf_tx_rate(dev, ivt->vf, - ivt->rate); + if (ops->ndo_get_vf_config) + err = ops->ndo_get_vf_config(dev, ivt->vf, + &ivf); + if (err) + break; + err = -EOPNOTSUPP; + if (ops->ndo_set_vf_rate) + err = ops->ndo_set_vf_rate(dev, ivt->vf, + ivf.min_tx_rate, + ivt->rate); + break; + } + case IFLA_VF_RATE: { + struct ifla_vf_rate *ivt; + ivt = nla_data(vf); + err = -EOPNOTSUPP; + if (ops->ndo_set_vf_rate) + err = ops->ndo_set_vf_rate(dev, ivt->vf, + ivt->min_tx_rate, + ivt->max_tx_rate); break; } case IFLA_VF_SPOOFCHK: { -- cgit v1.2.3 From edf866b3805c5651bf7d035b72dc0190cb6ff4a7 Mon Sep 17 00:00:00 2001 From: Sam Bradshaw Date: Fri, 23 May 2014 13:30:16 -0600 Subject: blk-mq: export blk_mq_tag_busy_iter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export the blk-mq in-flight tag iterator for driver consumption. This is particularly useful in exception paths or SRSI where in-flight IOs need to be cancelled and/or reissued. The NVMe driver conversion will use this. Signed-off-by: Sam Bradshaw Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- block/blk-mq-tag.c | 1 + block/blk-mq-tag.h | 1 - include/linux/blk-mq.h | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index f6dea968b710..05e2baf4fa0d 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -400,6 +400,7 @@ void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, fn(data, tag_map); kfree(tag_map); } +EXPORT_SYMBOL(blk_mq_tag_busy_iter); static unsigned int bt_unused_tags(struct blk_mq_bitmap_tags *bt) { diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h index e7ff5ceeeb97..2e5e6872d089 100644 --- a/block/blk-mq-tag.h +++ b/block/blk-mq-tag.h @@ -51,7 +51,6 @@ extern void blk_mq_free_tags(struct blk_mq_tags *tags); extern unsigned int blk_mq_get_tag(struct blk_mq_hw_ctx *hctx, unsigned int *last_tag, gfp_t gfp, bool reserved); extern void blk_mq_wait_for_tags(struct blk_mq_hw_ctx *hctx, bool reserved); extern void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, unsigned int tag, unsigned int *last_tag); -extern void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, void (*fn)(void *data, unsigned long *), void *data); extern bool blk_mq_has_free_tags(struct blk_mq_tags *tags); extern ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page); extern void blk_mq_tag_init_last_tag(struct blk_mq_tags *tags, unsigned int *last_tag); diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 4d2800567aad..f76bb18350af 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -181,6 +181,7 @@ void blk_mq_stop_hw_queues(struct request_queue *q); void blk_mq_start_hw_queues(struct request_queue *q); void blk_mq_start_stopped_hw_queues(struct request_queue *q, bool async); void blk_mq_delay_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs); +void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, void (*fn)(void *data, unsigned long *), void *data); /* * Driver command data is immediately after the request. So subtract request -- cgit v1.2.3 From b26ba202e0500eb852e89499ece1b2deaa64c3a7 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 23 May 2014 08:47:09 -0700 Subject: net: Eliminate no_check from protosw It doesn't seem like an protocols are setting anything other than the default, and allowing to arbitrarily disable checksums for a whole protocol seems dangerous. This can be done on a per socket basis. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/protocol.h | 1 - net/dccp/ipv4.c | 1 - net/ipv4/af_inet.c | 7 ------- net/ipv4/udplite.c | 1 - net/ipv6/af_inet6.c | 3 --- net/ipv6/ping.c | 1 - net/ipv6/raw.c | 1 - net/ipv6/tcp_ipv6.c | 1 - net/ipv6/udp.c | 1 - net/ipv6/udplite.c | 1 - net/l2tp/l2tp_ip.c | 1 - net/l2tp/l2tp_ip6.c | 1 - net/sctp/ipv6.c | 2 -- net/sctp/protocol.c | 2 -- 14 files changed, 24 deletions(-) (limited to 'include') diff --git a/include/net/protocol.h b/include/net/protocol.h index a7e986b08147..d6fcc1fcdb5b 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -86,7 +86,6 @@ struct inet_protosw { struct proto *prot; const struct proto_ops *ops; - char no_check; /* checksum on rcv/xmit/none? */ unsigned char flags; /* See INET_PROTOSW_* below. */ }; #define INET_PROTOSW_REUSE 0x01 /* Are ports automatically reusable? */ diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 22b5d818b200..6ca645c4b48e 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -1024,7 +1024,6 @@ static struct inet_protosw dccp_v4_protosw = { .protocol = IPPROTO_DCCP, .prot = &dccp_v4_prot, .ops = &inet_dccp_ops, - .no_check = 0, .flags = INET_PROTOSW_ICSK, }; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 279132bcadd9..0e9bb08a91e4 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -254,7 +254,6 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, struct inet_sock *inet; struct proto *answer_prot; unsigned char answer_flags; - char answer_no_check; int try_loading_module = 0; int err; @@ -312,7 +311,6 @@ lookup_protocol: sock->ops = answer->ops; answer_prot = answer->prot; - answer_no_check = answer->no_check; answer_flags = answer->flags; rcu_read_unlock(); @@ -324,7 +322,6 @@ lookup_protocol: goto out; err = 0; - sk->sk_no_check = answer_no_check; if (INET_PROTOSW_REUSE & answer_flags) sk->sk_reuse = SK_CAN_REUSE; @@ -1002,7 +999,6 @@ static struct inet_protosw inetsw_array[] = .protocol = IPPROTO_TCP, .prot = &tcp_prot, .ops = &inet_stream_ops, - .no_check = 0, .flags = INET_PROTOSW_PERMANENT | INET_PROTOSW_ICSK, }, @@ -1012,7 +1008,6 @@ static struct inet_protosw inetsw_array[] = .protocol = IPPROTO_UDP, .prot = &udp_prot, .ops = &inet_dgram_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_PERMANENT, }, @@ -1021,7 +1016,6 @@ static struct inet_protosw inetsw_array[] = .protocol = IPPROTO_ICMP, .prot = &ping_prot, .ops = &inet_dgram_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, }, @@ -1030,7 +1024,6 @@ static struct inet_protosw inetsw_array[] = .protocol = IPPROTO_IP, /* wild card */ .prot = &raw_prot, .ops = &inet_sockraw_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, } }; diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index 2c46acd4cc36..3b3efbda48e1 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -70,7 +70,6 @@ static struct inet_protosw udplite4_protosw = { .protocol = IPPROTO_UDPLITE, .prot = &udplite_prot, .ops = &inet_dgram_ops, - .no_check = 0, /* must checksum (RFC 3828) */ .flags = INET_PROTOSW_PERMANENT, }; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index dc47cc757b80..7cb4392690dd 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -106,7 +106,6 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, struct inet_protosw *answer; struct proto *answer_prot; unsigned char answer_flags; - char answer_no_check; int try_loading_module = 0; int err; @@ -162,7 +161,6 @@ lookup_protocol: sock->ops = answer->ops; answer_prot = answer->prot; - answer_no_check = answer->no_check; answer_flags = answer->flags; rcu_read_unlock(); @@ -176,7 +174,6 @@ lookup_protocol: sock_init_data(sock, sk); err = 0; - sk->sk_no_check = answer_no_check; if (INET_PROTOSW_REUSE & answer_flags) sk->sk_reuse = SK_CAN_REUSE; diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index a2a1d80dfe0c..5b7a1ed2aba9 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -51,7 +51,6 @@ static struct inet_protosw pingv6_protosw = { .protocol = IPPROTO_ICMPV6, .prot = &pingv6_prot, .ops = &inet6_dgram_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, }; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index dddfb5fa2b7a..b2dc60b0c764 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1322,7 +1322,6 @@ static struct inet_protosw rawv6_protosw = { .protocol = IPPROTO_IP, /* wild card */ .prot = &rawv6_prot, .ops = &inet6_sockraw_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_REUSE, }; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f07b2abba359..229239ad96b1 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1992,7 +1992,6 @@ static struct inet_protosw tcpv6_protosw = { .protocol = IPPROTO_TCP, .prot = &tcpv6_prot, .ops = &inet6_stream_ops, - .no_check = 0, .flags = INET_PROTOSW_PERMANENT | INET_PROTOSW_ICSK, }; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 7edf096867c4..c7ed47bde54b 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1507,7 +1507,6 @@ static struct inet_protosw udpv6_protosw = { .protocol = IPPROTO_UDP, .prot = &udpv6_prot, .ops = &inet6_dgram_ops, - .no_check = UDP_CSUM_DEFAULT, .flags = INET_PROTOSW_PERMANENT, }; diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index dfcc4be46898..9cf097e206e9 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -64,7 +64,6 @@ static struct inet_protosw udplite6_protosw = { .protocol = IPPROTO_UDPLITE, .prot = &udplitev6_prot, .ops = &inet6_dgram_ops, - .no_check = 0, .flags = INET_PROTOSW_PERMANENT, }; diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 3397fe6897c0..369a9822488c 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -606,7 +606,6 @@ static struct inet_protosw l2tp_ip_protosw = { .protocol = IPPROTO_L2TP, .prot = &l2tp_ip_prot, .ops = &l2tp_ip_ops, - .no_check = 0, }; static struct net_protocol l2tp_ip_protocol __read_mostly = { diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index e472d44a3b91..f3f98a156cee 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -755,7 +755,6 @@ static struct inet_protosw l2tp_ip6_protosw = { .protocol = IPPROTO_L2TP, .prot = &l2tp_ip6_prot, .ops = &l2tp_ip6_ops, - .no_check = 0, }; static struct inet6_protocol l2tp_ip6_protocol __read_mostly = { diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 4dc5d9e08311..1999592ba88c 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -943,7 +943,6 @@ static struct inet_protosw sctpv6_seqpacket_protosw = { .protocol = IPPROTO_SCTP, .prot = &sctpv6_prot, .ops = &inet6_seqpacket_ops, - .no_check = 0, .flags = SCTP_PROTOSW_FLAG }; static struct inet_protosw sctpv6_stream_protosw = { @@ -951,7 +950,6 @@ static struct inet_protosw sctpv6_stream_protosw = { .protocol = IPPROTO_SCTP, .prot = &sctpv6_prot, .ops = &inet6_seqpacket_ops, - .no_check = 0, .flags = SCTP_PROTOSW_FLAG, }; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index af5afca4b85a..6789d785e698 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1017,7 +1017,6 @@ static struct inet_protosw sctp_seqpacket_protosw = { .protocol = IPPROTO_SCTP, .prot = &sctp_prot, .ops = &inet_seqpacket_ops, - .no_check = 0, .flags = SCTP_PROTOSW_FLAG }; static struct inet_protosw sctp_stream_protosw = { @@ -1025,7 +1024,6 @@ static struct inet_protosw sctp_stream_protosw = { .protocol = IPPROTO_SCTP, .prot = &sctp_prot, .ops = &inet_seqpacket_ops, - .no_check = 0, .flags = SCTP_PROTOSW_FLAG }; -- cgit v1.2.3 From 28448b80456feafe07e2d05b6363b00f61f6171e Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 23 May 2014 08:47:19 -0700 Subject: net: Split sk_no_check into sk_no_check_{rx,tx} Define separate fields in the sock structure for configuring disabling checksums in both TX and RX-- sk_no_check_tx and sk_no_check_rx. The SO_NO_CHECK socket option only affects sk_no_check_tx. Also, removed UDP_CSUM_* defines since they are no longer necessary. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- drivers/scsi/iscsi_tcp.c | 2 +- include/net/sock.h | 6 ++++-- include/net/udp.h | 9 --------- net/appletalk/ddp.c | 2 +- net/core/sock.c | 4 ++-- net/decnet/af_decnet.c | 2 +- net/ipv4/udp.c | 2 +- net/ipv6/udp.c | 6 +++--- net/ipx/af_ipx.c | 2 +- net/ipx/ipx_route.c | 3 ++- net/l2tp/l2tp_core.c | 4 ++-- net/l2tp/l2tp_netlink.c | 3 +-- net/sctp/socket.c | 3 ++- 13 files changed, 21 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 11854845393b..a669f2d11c31 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -244,7 +244,7 @@ iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn) sk->sk_data_ready = tcp_sw_conn->old_data_ready; sk->sk_state_change = tcp_sw_conn->old_state_change; sk->sk_write_space = tcp_sw_conn->old_write_space; - sk->sk_no_check = 0; + sk->sk_no_check_tx = 0; write_unlock_bh(&sk->sk_callback_lock); } diff --git a/include/net/sock.h b/include/net/sock.h index 21569cf456ed..07b7fcd60d80 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -243,7 +243,8 @@ struct cg_proto; * @sk_sndbuf: size of send buffer in bytes * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings - * @sk_no_check: %SO_NO_CHECK setting, whether or not checkup packets + * @sk_no_check_tx: %SO_NO_CHECK setting, set checksum in TX packets + * @sk_no_check_rx: allow zero checksum in RX packets * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO) * @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK) * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) @@ -371,7 +372,8 @@ struct sock { struct sk_buff_head sk_write_queue; kmemcheck_bitfield_begin(flags); unsigned int sk_shutdown : 2, - sk_no_check : 2, + sk_no_check_tx : 1, + sk_no_check_rx : 1, sk_userlocks : 4, sk_protocol : 8, sk_type : 16; diff --git a/include/net/udp.h b/include/net/udp.h index a24f0f3e107f..5eb86874bcd6 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -95,15 +95,6 @@ static inline struct udp_hslot *udp_hashslot2(struct udp_table *table, return &table->hash2[hash & table->mask]; } -/* Note: this must match 'valbool' in sock_setsockopt */ -#define UDP_CSUM_NOXMIT 1 - -/* Used by SunRPC/xprt layer. */ -#define UDP_CSUM_NORCV 2 - -/* Default, as per the RFC, is to always do csums. */ -#define UDP_CSUM_DEFAULT 0 - extern struct proto udp_prot; extern atomic_long_t udp_memory_allocated; diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 786ee2f83d5f..01a1082e02b3 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1669,7 +1669,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr goto out; } - if (sk->sk_no_check == 1) + if (sk->sk_no_check_tx) ddp->deh_sum = 0; else ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp)); diff --git a/net/core/sock.c b/net/core/sock.c index 664ee4295b6f..026e01f70274 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -784,7 +784,7 @@ set_rcvbuf: break; case SO_NO_CHECK: - sk->sk_no_check = valbool; + sk->sk_no_check_tx = valbool; break; case SO_PRIORITY: @@ -1064,7 +1064,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname, break; case SO_NO_CHECK: - v.val = sk->sk_no_check; + v.val = sk->sk_no_check_tx; break; case SO_PRIORITY: diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 4c04848953bd..ae011b46c071 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -481,7 +481,7 @@ static struct sock *dn_alloc_sock(struct net *net, struct socket *sock, gfp_t gf sk->sk_backlog_rcv = dn_nsp_backlog_rcv; sk->sk_destruct = dn_destruct; - sk->sk_no_check = 1; + sk->sk_no_check_tx = 1; sk->sk_family = PF_DECnet; sk->sk_protocol = 0; sk->sk_allocation = gfp; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 590532a7bd2d..12c6175b29cd 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -785,7 +785,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) if (is_udplite) /* UDP-Lite */ csum = udplite_csum(skb); - else if (sk->sk_no_check == UDP_CSUM_NOXMIT) { /* UDP csum disabled */ + else if (sk->sk_no_check_tx) { /* UDP csum disabled */ skb->ip_summed = CHECKSUM_NONE; goto send; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index c7ed47bde54b..b8db453133aa 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -797,7 +797,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, /* If zero checksum and sk_no_check is not on for * the socket then skip it. */ - if (uh->check || sk->sk_no_check) + if (uh->check || sk->sk_no_check_rx) stack[count++] = sk; sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr, @@ -887,7 +887,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (sk != NULL) { int ret; - if (!uh->check && !sk->sk_no_check) { + if (!uh->check && !sk->sk_no_check_rx) { sock_put(sk); udp6_csum_zero_error(skb); goto csum_error; @@ -1037,7 +1037,7 @@ static int udp_v6_push_pending_frames(struct sock *sk) if (is_udplite) csum = udplite_csum_outgoing(sk, skb); - else if (sk->sk_no_check == UDP_CSUM_NOXMIT) { /* UDP csum disabled */ + else if (sk->sk_no_check_tx) { /* UDP csum disabled */ skb->ip_summed = CHECKSUM_NONE; goto send; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 41e4e93cb3aa..91729b807c7d 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1353,7 +1353,7 @@ static int ipx_create(struct net *net, struct socket *sock, int protocol, sk_refcnt_debug_inc(sk); sock_init_data(sock, sk); - sk->sk_no_check = 1; /* Checksum off by default */ + sk->sk_no_check_tx = 1; /* Checksum off by default */ sock->ops = &ipx_dgram_ops; rc = 0; out: diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c index c1f03185c5e1..67e7ad3d46b1 100644 --- a/net/ipx/ipx_route.c +++ b/net/ipx/ipx_route.c @@ -236,7 +236,8 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, } /* Apply checksum. Not allowed on 802.3 links. */ - if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023)) + if (sk->sk_no_check_tx || + intrfc->if_dlink_type == htons(IPX_FRAME_8023)) ipx->ipx_checksum = htons(0xFFFF); else ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr)); diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index ed0716a075ba..a1186105f537 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1188,7 +1188,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len l2tp_xmit_ipv6_csum(sk, skb, udp_len); else #endif - if (sk->sk_no_check == UDP_CSUM_NOXMIT) + if (sk->sk_no_check_tx) skb->ip_summed = CHECKSUM_NONE; else if ((skb_dst(skb) && skb_dst(skb)->dev) && (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { @@ -1463,7 +1463,7 @@ static int l2tp_tunnel_sock_create(struct net *net, } if (!cfg->use_udp_checksums) - sock->sk->sk_no_check = UDP_CSUM_NOXMIT; + sock->sk->sk_no_check_tx = 1; break; diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index bd7387adea9e..f3d331bdd706 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -297,8 +297,7 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla case L2TP_ENCAPTYPE_UDP: if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)) || - nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, - (sk->sk_no_check != UDP_CSUM_NOXMIT))) + nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx)) goto nla_put_failure; /* NOBREAK */ case L2TP_ENCAPTYPE_IP: diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 2af76eaba8f7..429899689408 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -6946,7 +6946,8 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, newsk->sk_type = sk->sk_type; newsk->sk_bound_dev_if = sk->sk_bound_dev_if; newsk->sk_flags = sk->sk_flags; - newsk->sk_no_check = sk->sk_no_check; + newsk->sk_no_check_tx = sk->sk_no_check_tx; + newsk->sk_no_check_rx = sk->sk_no_check_rx; newsk->sk_reuse = sk->sk_reuse; newsk->sk_shutdown = sk->sk_shutdown; -- cgit v1.2.3 From 1c19448c9ba6545b80ded18488a64a7f3d8e6998 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 23 May 2014 08:47:32 -0700 Subject: net: Make enabling of zero UDP6 csums more restrictive RFC 6935 permits zero checksums to be used in IPv6 however this is recommended only for certain tunnel protocols, it does not make checksums completely optional like they are in IPv4. This patch restricts the use of IPv6 zero checksums that was previously intoduced. no_check6_tx and no_check6_rx have been added to control the use of checksums in UDP6 RX and TX path. The normal sk_no_check_{rx,tx} settings are not used (this avoids ambiguity when dealing with a dual stack socket). A helper function has been added (udp_set_no_check6) which can be called by tunnel impelmentations to all zero checksums (send on the socket, and accept them as valid). Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/udp.h | 24 +++++++++++++++++++++++- include/uapi/linux/udp.h | 2 ++ net/ipv4/udp.c | 20 +++++++++++++++++++- net/ipv6/udp.c | 8 ++++---- 4 files changed, 48 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/udp.h b/include/linux/udp.h index 42278bbf7a88..247cfdcc4b08 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -47,7 +47,9 @@ struct udp_sock { #define udp_portaddr_node inet.sk.__sk_common.skc_portaddr_node int pending; /* Any pending frames ? */ unsigned int corkflag; /* Cork is required */ - __u16 encap_type; /* Is this an Encapsulation socket? */ + __u8 encap_type; /* Is this an Encapsulation socket? */ + unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ + no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */ /* * Following member retains the information to create a UDP header * when the socket is uncorked. @@ -76,6 +78,26 @@ static inline struct udp_sock *udp_sk(const struct sock *sk) return (struct udp_sock *)sk; } +static inline void udp_set_no_check6_tx(struct sock *sk, bool val) +{ + udp_sk(sk)->no_check6_tx = val; +} + +static inline void udp_set_no_check6_rx(struct sock *sk, bool val) +{ + udp_sk(sk)->no_check6_rx = val; +} + +static inline bool udp_get_no_check6_tx(struct sock *sk) +{ + return udp_sk(sk)->no_check6_tx; +} + +static inline bool udp_get_no_check6_rx(struct sock *sk) +{ + return udp_sk(sk)->no_check6_rx; +} + #define udp_portaddr_for_each_entry(__sk, node, list) \ hlist_nulls_for_each_entry(__sk, node, list, __sk_common.skc_portaddr_node) diff --git a/include/uapi/linux/udp.h b/include/uapi/linux/udp.h index e2bcfd75a30d..16574ea18f0c 100644 --- a/include/uapi/linux/udp.h +++ b/include/uapi/linux/udp.h @@ -29,6 +29,8 @@ struct udphdr { /* UDP socket options */ #define UDP_CORK 1 /* Never send partially complete segments */ #define UDP_ENCAP 100 /* Set the socket to accept encapsulated packets */ +#define UDP_NO_CHECK6_TX 101 /* Disable sending checksum for UDP6X */ +#define UDP_NO_CHECK6_RX 102 /* Disable accpeting checksum for UDP6 */ /* UDP encapsulation types */ #define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 12c6175b29cd..e07d52b8617a 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1968,7 +1968,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, int (*push_pending_frames)(struct sock *)) { struct udp_sock *up = udp_sk(sk); - int val; + int val, valbool; int err = 0; int is_udplite = IS_UDPLITE(sk); @@ -1978,6 +1978,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, if (get_user(val, (int __user *)optval)) return -EFAULT; + valbool = val ? 1 : 0; + switch (optname) { case UDP_CORK: if (val != 0) { @@ -2007,6 +2009,14 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, } break; + case UDP_NO_CHECK6_TX: + up->no_check6_tx = valbool; + break; + + case UDP_NO_CHECK6_RX: + up->no_check6_rx = valbool; + break; + /* * UDP-Lite's partial checksum coverage (RFC 3828). */ @@ -2089,6 +2099,14 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, val = up->encap_type; break; + case UDP_NO_CHECK6_TX: + val = up->no_check6_tx; + break; + + case UDP_NO_CHECK6_RX: + val = up->no_check6_rx; + break; + /* The following two cannot be changed on UDP sockets, the return is * always 0 (which corresponds to the full checksum coverage of UDP). */ case UDPLITE_SEND_CSCOV: diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index b8db453133aa..60325236446a 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -794,10 +794,10 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, dif = inet6_iif(skb); sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif); while (sk) { - /* If zero checksum and sk_no_check is not on for + /* If zero checksum and no_check is not on for * the socket then skip it. */ - if (uh->check || sk->sk_no_check_rx) + if (uh->check || udp_sk(sk)->no_check6_rx) stack[count++] = sk; sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr, @@ -887,7 +887,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (sk != NULL) { int ret; - if (!uh->check && !sk->sk_no_check_rx) { + if (!uh->check && !udp_sk(sk)->no_check6_rx) { sock_put(sk); udp6_csum_zero_error(skb); goto csum_error; @@ -1037,7 +1037,7 @@ static int udp_v6_push_pending_frames(struct sock *sk) if (is_udplite) csum = udplite_csum_outgoing(sk, skb); - else if (sk->sk_no_check_tx) { /* UDP csum disabled */ + else if (up->no_check6_tx) { /* UDP csum disabled */ skb->ip_summed = CHECKSUM_NONE; goto send; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ -- cgit v1.2.3 From 6b649feafe10b293f4bd5a74aca95faf625ae525 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 23 May 2014 08:47:40 -0700 Subject: l2tp: Add support for zero IPv6 checksums Added new L2TP configuration options to allow TX and RX of zero checksums in IPv6. Default is not to use them. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/uapi/linux/l2tp.h | 2 ++ net/l2tp/l2tp_core.c | 9 ++++++++- net/l2tp/l2tp_core.h | 4 +++- net/l2tp/l2tp_netlink.c | 7 +++++++ 4 files changed, 20 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/l2tp.h b/include/uapi/linux/l2tp.h index 8adb68160327..21caa2631c20 100644 --- a/include/uapi/linux/l2tp.h +++ b/include/uapi/linux/l2tp.h @@ -124,6 +124,8 @@ enum { L2TP_ATTR_STATS, /* nested */ L2TP_ATTR_IP6_SADDR, /* struct in6_addr */ L2TP_ATTR_IP6_DADDR, /* struct in6_addr */ + L2TP_ATTR_UDP_ZERO_CSUM6_TX, /* u8 */ + L2TP_ATTR_UDP_ZERO_CSUM6_RX, /* u8 */ __L2TP_ATTR_MAX, }; diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index a1186105f537..379558014b60 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1102,7 +1102,9 @@ static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb, struct ipv6_pinfo *np = inet6_sk(sk); struct udphdr *uh = udp_hdr(skb); - if (!skb_dst(skb) || !skb_dst(skb)->dev || + if (udp_get_no_check6_tx(sk)) + skb->ip_summed = CHECKSUM_NONE; + else if (!skb_dst(skb) || !skb_dst(skb)->dev || !(skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) { __wsum csum = skb_checksum(skb, 0, udp_len, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1435,6 +1437,11 @@ static int l2tp_tunnel_sock_create(struct net *net, sizeof(udp6_addr), 0); if (err < 0) goto out; + + if (cfg->udp6_zero_tx_checksums) + udp_set_no_check6_tx(sock->sk, true); + if (cfg->udp6_zero_rx_checksums) + udp_set_no_check6_rx(sock->sk, true); } else #endif { diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 3f93ccd6ba97..68aa9ffd4ae4 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -162,7 +162,9 @@ struct l2tp_tunnel_cfg { #endif u16 local_udp_port; u16 peer_udp_port; - unsigned int use_udp_checksums:1; + unsigned int use_udp_checksums:1, + udp6_zero_tx_checksums:1, + udp6_zero_rx_checksums:1; }; struct l2tp_tunnel { diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index f3d331bdd706..0ac907adb2f4 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -161,6 +161,13 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]); if (info->attrs[L2TP_ATTR_UDP_CSUM]) cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]); + +#if IS_ENABLED(CONFIG_IPV6) + if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]) + cfg.udp6_zero_tx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]); + if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]) + cfg.udp6_zero_rx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]); +#endif } if (info->attrs[L2TP_ATTR_DEBUG]) -- cgit v1.2.3 From 79c6ab509558f9871175c7e4411f857de12cf33b Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 23 May 2014 18:32:15 +0530 Subject: clk: divider: add CLK_DIVIDER_READ_ONLY flag From: Heiko Stuebner Similar to muxes which already have a read-only flag there sometimes exist dividers which should not be changed by the clock framework but whose value still should be readable. Therefore add a READ_ONLY flag similar to the mux-one to clk-divider Signed-off-by: Heiko Stuebner [changed flag bit to BIT(5) as suggested by Tomasz Figa] Signed-off-by: Thomas Abraham Acked-by: Tomasz Figa Acked-by: Max Schwarz Tested-by: Max Schwarz Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 10 +++++++++- include/linux/clk-provider.h | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index b3c83966be18..c9343f5d9918 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -361,6 +361,11 @@ const struct clk_ops clk_divider_ops = { }; EXPORT_SYMBOL_GPL(clk_divider_ops); +const struct clk_ops clk_divider_ro_ops = { + .recalc_rate = clk_divider_recalc_rate, +}; +EXPORT_SYMBOL_GPL(clk_divider_ro_ops); + static struct clk *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, @@ -386,7 +391,10 @@ static struct clk *_register_divider(struct device *dev, const char *name, } init.name = name; - init.ops = &clk_divider_ops; + if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) + init.ops = &clk_divider_ro_ops; + else + init.ops = &clk_divider_ops; init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 40809431641e..c7135dbbcd65 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -320,6 +320,8 @@ struct clk_div_table { * updated to indicate changing divider bits. * CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded * to the closest integer instead of the up one. + * CLK_DIVIDER_READ_ONLY - The divider settings are preconfigured and should + * not be changed by the clock framework. */ struct clk_divider { struct clk_hw hw; @@ -336,8 +338,10 @@ struct clk_divider { #define CLK_DIVIDER_ALLOW_ZERO BIT(2) #define CLK_DIVIDER_HIWORD_MASK BIT(3) #define CLK_DIVIDER_ROUND_CLOSEST BIT(4) +#define CLK_DIVIDER_READ_ONLY BIT(5) extern const struct clk_ops clk_divider_ops; +extern const struct clk_ops clk_divider_ro_ops; struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, -- cgit v1.2.3 From 8556ce79d5986a87fee4c29300b4efee07c0f15e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 23 May 2014 18:43:57 +0200 Subject: net: filter: remove DL macro Lets get rid of this macro. After commit 5bcfedf06f7f ("net: filter: simplify label names from jump-table"), labels have become more readable due to omission of BPF_ prefix but at the same time more generic, so that things like `git grep -n` would not find them. As a middle path, lets get rid of the DL macro as it's not strictly needed and would otherwise just hide the full name. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 3 - net/core/filter.c | 193 +++++++++++++++++++++++++------------------------ 2 files changed, 99 insertions(+), 97 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index 7977b3958e25..2b0056afd1f7 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -37,9 +37,6 @@ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ -/* Placeholder/dummy for 0 */ -#define BPF_0 0 - /* Register numbers */ enum { BPF_REG_0 = 0, diff --git a/net/core/filter.c b/net/core/filter.c index 7067cb240d3e..b3b0889fe089 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -160,95 +160,100 @@ static unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *ins static const void *jumptable[256] = { [0 ... 255] = &&default_label, /* Now overwrite non-defaults ... */ -#define DL(A, B, C) [BPF_##A|BPF_##B|BPF_##C] = &&A##_##B##_##C - DL(ALU, ADD, X), - DL(ALU, ADD, K), - DL(ALU, SUB, X), - DL(ALU, SUB, K), - DL(ALU, AND, X), - DL(ALU, AND, K), - DL(ALU, OR, X), - DL(ALU, OR, K), - DL(ALU, LSH, X), - DL(ALU, LSH, K), - DL(ALU, RSH, X), - DL(ALU, RSH, K), - DL(ALU, XOR, X), - DL(ALU, XOR, K), - DL(ALU, MUL, X), - DL(ALU, MUL, K), - DL(ALU, MOV, X), - DL(ALU, MOV, K), - DL(ALU, DIV, X), - DL(ALU, DIV, K), - DL(ALU, MOD, X), - DL(ALU, MOD, K), - DL(ALU, NEG, 0), - DL(ALU, END, TO_BE), - DL(ALU, END, TO_LE), - DL(ALU64, ADD, X), - DL(ALU64, ADD, K), - DL(ALU64, SUB, X), - DL(ALU64, SUB, K), - DL(ALU64, AND, X), - DL(ALU64, AND, K), - DL(ALU64, OR, X), - DL(ALU64, OR, K), - DL(ALU64, LSH, X), - DL(ALU64, LSH, K), - DL(ALU64, RSH, X), - DL(ALU64, RSH, K), - DL(ALU64, XOR, X), - DL(ALU64, XOR, K), - DL(ALU64, MUL, X), - DL(ALU64, MUL, K), - DL(ALU64, MOV, X), - DL(ALU64, MOV, K), - DL(ALU64, ARSH, X), - DL(ALU64, ARSH, K), - DL(ALU64, DIV, X), - DL(ALU64, DIV, K), - DL(ALU64, MOD, X), - DL(ALU64, MOD, K), - DL(ALU64, NEG, 0), - DL(JMP, CALL, 0), - DL(JMP, JA, 0), - DL(JMP, JEQ, X), - DL(JMP, JEQ, K), - DL(JMP, JNE, X), - DL(JMP, JNE, K), - DL(JMP, JGT, X), - DL(JMP, JGT, K), - DL(JMP, JGE, X), - DL(JMP, JGE, K), - DL(JMP, JSGT, X), - DL(JMP, JSGT, K), - DL(JMP, JSGE, X), - DL(JMP, JSGE, K), - DL(JMP, JSET, X), - DL(JMP, JSET, K), - DL(JMP, EXIT, 0), - DL(STX, MEM, B), - DL(STX, MEM, H), - DL(STX, MEM, W), - DL(STX, MEM, DW), - DL(STX, XADD, W), - DL(STX, XADD, DW), - DL(ST, MEM, B), - DL(ST, MEM, H), - DL(ST, MEM, W), - DL(ST, MEM, DW), - DL(LDX, MEM, B), - DL(LDX, MEM, H), - DL(LDX, MEM, W), - DL(LDX, MEM, DW), - DL(LD, ABS, W), - DL(LD, ABS, H), - DL(LD, ABS, B), - DL(LD, IND, W), - DL(LD, IND, H), - DL(LD, IND, B), -#undef DL + /* 32 bit ALU operations */ + [BPF_ALU | BPF_ADD | BPF_X] = &&ALU_ADD_X, + [BPF_ALU | BPF_ADD | BPF_K] = &&ALU_ADD_K, + [BPF_ALU | BPF_SUB | BPF_X] = &&ALU_SUB_X, + [BPF_ALU | BPF_SUB | BPF_K] = &&ALU_SUB_K, + [BPF_ALU | BPF_AND | BPF_X] = &&ALU_AND_X, + [BPF_ALU | BPF_AND | BPF_K] = &&ALU_AND_K, + [BPF_ALU | BPF_OR | BPF_X] = &&ALU_OR_X, + [BPF_ALU | BPF_OR | BPF_K] = &&ALU_OR_K, + [BPF_ALU | BPF_LSH | BPF_X] = &&ALU_LSH_X, + [BPF_ALU | BPF_LSH | BPF_K] = &&ALU_LSH_K, + [BPF_ALU | BPF_RSH | BPF_X] = &&ALU_RSH_X, + [BPF_ALU | BPF_RSH | BPF_K] = &&ALU_RSH_K, + [BPF_ALU | BPF_XOR | BPF_X] = &&ALU_XOR_X, + [BPF_ALU | BPF_XOR | BPF_K] = &&ALU_XOR_K, + [BPF_ALU | BPF_MUL | BPF_X] = &&ALU_MUL_X, + [BPF_ALU | BPF_MUL | BPF_K] = &&ALU_MUL_K, + [BPF_ALU | BPF_MOV | BPF_X] = &&ALU_MOV_X, + [BPF_ALU | BPF_MOV | BPF_K] = &&ALU_MOV_K, + [BPF_ALU | BPF_DIV | BPF_X] = &&ALU_DIV_X, + [BPF_ALU | BPF_DIV | BPF_K] = &&ALU_DIV_K, + [BPF_ALU | BPF_MOD | BPF_X] = &&ALU_MOD_X, + [BPF_ALU | BPF_MOD | BPF_K] = &&ALU_MOD_K, + [BPF_ALU | BPF_NEG] = &&ALU_NEG, + [BPF_ALU | BPF_END | BPF_TO_BE] = &&ALU_END_TO_BE, + [BPF_ALU | BPF_END | BPF_TO_LE] = &&ALU_END_TO_LE, + /* 64 bit ALU operations */ + [BPF_ALU64 | BPF_ADD | BPF_X] = &&ALU64_ADD_X, + [BPF_ALU64 | BPF_ADD | BPF_K] = &&ALU64_ADD_K, + [BPF_ALU64 | BPF_SUB | BPF_X] = &&ALU64_SUB_X, + [BPF_ALU64 | BPF_SUB | BPF_K] = &&ALU64_SUB_K, + [BPF_ALU64 | BPF_AND | BPF_X] = &&ALU64_AND_X, + [BPF_ALU64 | BPF_AND | BPF_K] = &&ALU64_AND_K, + [BPF_ALU64 | BPF_OR | BPF_X] = &&ALU64_OR_X, + [BPF_ALU64 | BPF_OR | BPF_K] = &&ALU64_OR_K, + [BPF_ALU64 | BPF_LSH | BPF_X] = &&ALU64_LSH_X, + [BPF_ALU64 | BPF_LSH | BPF_K] = &&ALU64_LSH_K, + [BPF_ALU64 | BPF_RSH | BPF_X] = &&ALU64_RSH_X, + [BPF_ALU64 | BPF_RSH | BPF_K] = &&ALU64_RSH_K, + [BPF_ALU64 | BPF_XOR | BPF_X] = &&ALU64_XOR_X, + [BPF_ALU64 | BPF_XOR | BPF_K] = &&ALU64_XOR_K, + [BPF_ALU64 | BPF_MUL | BPF_X] = &&ALU64_MUL_X, + [BPF_ALU64 | BPF_MUL | BPF_K] = &&ALU64_MUL_K, + [BPF_ALU64 | BPF_MOV | BPF_X] = &&ALU64_MOV_X, + [BPF_ALU64 | BPF_MOV | BPF_K] = &&ALU64_MOV_K, + [BPF_ALU64 | BPF_ARSH | BPF_X] = &&ALU64_ARSH_X, + [BPF_ALU64 | BPF_ARSH | BPF_K] = &&ALU64_ARSH_K, + [BPF_ALU64 | BPF_DIV | BPF_X] = &&ALU64_DIV_X, + [BPF_ALU64 | BPF_DIV | BPF_K] = &&ALU64_DIV_K, + [BPF_ALU64 | BPF_MOD | BPF_X] = &&ALU64_MOD_X, + [BPF_ALU64 | BPF_MOD | BPF_K] = &&ALU64_MOD_K, + [BPF_ALU64 | BPF_NEG] = &&ALU64_NEG, + /* Call instruction */ + [BPF_JMP | BPF_CALL] = &&JMP_CALL, + /* Jumps */ + [BPF_JMP | BPF_JA] = &&JMP_JA, + [BPF_JMP | BPF_JEQ | BPF_X] = &&JMP_JEQ_X, + [BPF_JMP | BPF_JEQ | BPF_K] = &&JMP_JEQ_K, + [BPF_JMP | BPF_JNE | BPF_X] = &&JMP_JNE_X, + [BPF_JMP | BPF_JNE | BPF_K] = &&JMP_JNE_K, + [BPF_JMP | BPF_JGT | BPF_X] = &&JMP_JGT_X, + [BPF_JMP | BPF_JGT | BPF_K] = &&JMP_JGT_K, + [BPF_JMP | BPF_JGE | BPF_X] = &&JMP_JGE_X, + [BPF_JMP | BPF_JGE | BPF_K] = &&JMP_JGE_K, + [BPF_JMP | BPF_JSGT | BPF_X] = &&JMP_JSGT_X, + [BPF_JMP | BPF_JSGT | BPF_K] = &&JMP_JSGT_K, + [BPF_JMP | BPF_JSGE | BPF_X] = &&JMP_JSGE_X, + [BPF_JMP | BPF_JSGE | BPF_K] = &&JMP_JSGE_K, + [BPF_JMP | BPF_JSET | BPF_X] = &&JMP_JSET_X, + [BPF_JMP | BPF_JSET | BPF_K] = &&JMP_JSET_K, + /* Program return */ + [BPF_JMP | BPF_EXIT] = &&JMP_EXIT, + /* Store instructions */ + [BPF_STX | BPF_MEM | BPF_B] = &&STX_MEM_B, + [BPF_STX | BPF_MEM | BPF_H] = &&STX_MEM_H, + [BPF_STX | BPF_MEM | BPF_W] = &&STX_MEM_W, + [BPF_STX | BPF_MEM | BPF_DW] = &&STX_MEM_DW, + [BPF_STX | BPF_XADD | BPF_W] = &&STX_XADD_W, + [BPF_STX | BPF_XADD | BPF_DW] = &&STX_XADD_DW, + [BPF_ST | BPF_MEM | BPF_B] = &&ST_MEM_B, + [BPF_ST | BPF_MEM | BPF_H] = &&ST_MEM_H, + [BPF_ST | BPF_MEM | BPF_W] = &&ST_MEM_W, + [BPF_ST | BPF_MEM | BPF_DW] = &&ST_MEM_DW, + /* Load instructions */ + [BPF_LDX | BPF_MEM | BPF_B] = &&LDX_MEM_B, + [BPF_LDX | BPF_MEM | BPF_H] = &&LDX_MEM_H, + [BPF_LDX | BPF_MEM | BPF_W] = &&LDX_MEM_W, + [BPF_LDX | BPF_MEM | BPF_DW] = &&LDX_MEM_DW, + [BPF_LD | BPF_ABS | BPF_W] = &&LD_ABS_W, + [BPF_LD | BPF_ABS | BPF_H] = &&LD_ABS_H, + [BPF_LD | BPF_ABS | BPF_B] = &&LD_ABS_B, + [BPF_LD | BPF_IND | BPF_W] = &&LD_IND_W, + [BPF_LD | BPF_IND | BPF_H] = &&LD_IND_H, + [BPF_LD | BPF_IND | BPF_B] = &&LD_IND_B, }; void *ptr; int off; @@ -290,10 +295,10 @@ select_insn: ALU(XOR, ^) ALU(MUL, *) #undef ALU - ALU_NEG_0: + ALU_NEG: A = (u32) -A; CONT; - ALU64_NEG_0: + ALU64_NEG: A = -A; CONT; ALU_MOV_X: @@ -382,7 +387,7 @@ select_insn: CONT; /* CALL */ - JMP_CALL_0: + JMP_CALL: /* Function call scratches BPF_R1-BPF_R5 registers, * preserves BPF_R6-BPF_R9, and stores return value * into BPF_R0. @@ -392,7 +397,7 @@ select_insn: CONT; /* JMP */ - JMP_JA_0: + JMP_JA: insn += insn->off; CONT; JMP_JEQ_X: @@ -479,7 +484,7 @@ select_insn: CONT_JMP; } CONT; - JMP_EXIT_0: + JMP_EXIT: return BPF_R0; /* STX and ST and LDX*/ -- cgit v1.2.3 From b1fcd35cf53553a0a3ef949b05106d921446abc3 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 23 May 2014 18:43:58 +0200 Subject: net: filter: let unattached filters use sock_fprog_kern The sk_unattached_filter_create() API is used by BPF filters that are not directly attached or related to sockets, and are used in team, ptp, xt_bpf, cls_bpf, etc. As such all users do their own internal managment of obtaining filter blocks and thus already have them in kernel memory and set up before calling into sk_unattached_filter_create(). As a result, due to __user annotation in sock_fprog, sparse triggers false positives (incorrect type in assignment [different address space]) when filters are set up before passing them to sk_unattached_filter_create(). Therefore, let sk_unattached_filter_create() API use sock_fprog_kern to overcome this issue. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- drivers/isdn/i4l/isdn_ppp.c | 4 ++-- drivers/net/ppp/ppp_generic.c | 4 ++-- drivers/net/team/team_mode_loadbalance.c | 10 +++++----- include/linux/filter.h | 2 +- lib/test_bpf.c | 2 +- net/core/filter.c | 2 +- net/core/ptp_classifier.c | 2 +- net/netfilter/xt_bpf.c | 5 +++-- net/sched/cls_bpf.c | 4 ++-- 9 files changed, 18 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index a5da511e3c9a..61ac63237446 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -634,7 +634,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) #ifdef CONFIG_IPPP_FILTER case PPPIOCSPASS: { - struct sock_fprog fprog; + struct sock_fprog_kern fprog; struct sock_filter *code; int err, len = get_filter(argp, &code); @@ -653,7 +653,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) } case PPPIOCSACTIVE: { - struct sock_fprog fprog; + struct sock_fprog_kern fprog; struct sock_filter *code; int err, len = get_filter(argp, &code); diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index e3923ebb693f..91d6c1272fcf 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -757,7 +757,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = get_filter(argp, &code); if (err >= 0) { - struct sock_fprog fprog = { + struct sock_fprog_kern fprog = { .len = err, .filter = code, }; @@ -778,7 +778,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = get_filter(argp, &code); if (err >= 0) { - struct sock_fprog fprog = { + struct sock_fprog_kern fprog = { .len = err, .filter = code, }; diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index dbde3412ee5e..0a6ee07bf0af 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -49,7 +49,7 @@ struct lb_port_mapping { struct lb_priv_ex { struct team *team; struct lb_port_mapping tx_hash_to_port_mapping[LB_TX_HASHTABLE_SIZE]; - struct sock_fprog *orig_fprog; + struct sock_fprog_kern *orig_fprog; struct { unsigned int refresh_interval; /* in tenths of second */ struct delayed_work refresh_dw; @@ -241,10 +241,10 @@ static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) return 0; } -static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, +static int __fprog_create(struct sock_fprog_kern **pfprog, u32 data_len, const void *data) { - struct sock_fprog *fprog; + struct sock_fprog_kern *fprog; struct sock_filter *filter = (struct sock_filter *) data; if (data_len % sizeof(struct sock_filter)) @@ -262,7 +262,7 @@ static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, return 0; } -static void __fprog_destroy(struct sock_fprog *fprog) +static void __fprog_destroy(struct sock_fprog_kern *fprog) { kfree(fprog->filter); kfree(fprog); @@ -273,7 +273,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) struct lb_priv *lb_priv = get_lb_priv(team); struct sk_filter *fp = NULL; struct sk_filter *orig_fp; - struct sock_fprog *fprog = NULL; + struct sock_fprog_kern *fprog = NULL; int err; if (ctx->data.bin_val.len) { diff --git a/include/linux/filter.h b/include/linux/filter.h index 2b0056afd1f7..625f4de9bdf2 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -188,7 +188,7 @@ int sk_convert_filter(struct sock_filter *prog, int len, struct sock_filter_int *new_prog, int *new_len); int sk_unattached_filter_create(struct sk_filter **pfp, - struct sock_fprog *fprog); + struct sock_fprog_kern *fprog); void sk_unattached_filter_destroy(struct sk_filter *fp); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 3d80adbdb559..e03991ea8cc2 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -1472,7 +1472,7 @@ static int run_one(struct sk_filter *fp, struct bpf_test *t) static __init int test_bpf(void) { struct sk_filter *fp, *fp_ext = NULL; - struct sock_fprog fprog; + struct sock_fprog_kern fprog; int err, i, err_cnt = 0; for (i = 0; i < ARRAY_SIZE(tests); i++) { diff --git a/net/core/filter.c b/net/core/filter.c index b3b0889fe089..2c2d35d9d101 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1585,7 +1585,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, * a negative errno code is returned. On success the return is zero. */ int sk_unattached_filter_create(struct sk_filter **pfp, - struct sock_fprog *fprog) + struct sock_fprog_kern *fprog) { unsigned int fsize = sk_filter_proglen(fprog); struct sk_filter *fp; diff --git a/net/core/ptp_classifier.c b/net/core/ptp_classifier.c index 37d86157b76e..d3027a73fd4b 100644 --- a/net/core/ptp_classifier.c +++ b/net/core/ptp_classifier.c @@ -133,7 +133,7 @@ void __init ptp_classifier_init(void) { 0x16, 0, 0, 0x00000000 }, { 0x06, 0, 0, 0x00000000 }, }; - struct sock_fprog ptp_prog = { + struct sock_fprog_kern ptp_prog = { .len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter, }; diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c index 12d4da8e6c77..bbffdbdaf603 100644 --- a/net/netfilter/xt_bpf.c +++ b/net/netfilter/xt_bpf.c @@ -23,10 +23,11 @@ MODULE_ALIAS("ip6t_bpf"); static int bpf_mt_check(const struct xt_mtchk_param *par) { struct xt_bpf_info *info = par->matchinfo; - struct sock_fprog program; + struct sock_fprog_kern program; program.len = info->bpf_program_num_elem; - program.filter = (struct sock_filter __user *) info->bpf_program; + program.filter = info->bpf_program; + if (sk_unattached_filter_create(&info->filter, &program)) { pr_info("bpf: check failed: parse error\n"); return -EINVAL; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 16186965af97..13f64df2c710 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -160,7 +160,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, { struct sock_filter *bpf_ops, *bpf_old; struct tcf_exts exts; - struct sock_fprog tmp; + struct sock_fprog_kern tmp; struct sk_filter *fp, *fp_old; u16 bpf_size, bpf_len; u32 classid; @@ -191,7 +191,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, memcpy(bpf_ops, nla_data(tb[TCA_BPF_OPS]), bpf_size); tmp.len = bpf_len; - tmp.filter = (struct sock_filter __user *) bpf_ops; + tmp.filter = bpf_ops; ret = sk_unattached_filter_create(&fp, &tmp); if (ret) -- cgit v1.2.3 From 49b2f4c56fbf70ca693d6df1c491f0566d516aea Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 15 Apr 2014 08:35:25 -0300 Subject: [media] exynos4-is: Remove support for non-dt platforms All platforms supported by this driver are going to get device tree support in this kernel release so remove code that would have been actually not used any more. Signed-off-by: Sylwester Nawrocki Acked-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/fimc.txt | 30 -- MAINTAINERS | 1 - drivers/media/platform/exynos4-is/Kconfig | 3 +- drivers/media/platform/exynos4-is/common.c | 2 +- drivers/media/platform/exynos4-is/fimc-core.h | 2 +- drivers/media/platform/exynos4-is/fimc-isp-video.c | 2 +- drivers/media/platform/exynos4-is/fimc-isp.h | 2 +- drivers/media/platform/exynos4-is/fimc-lite-reg.c | 2 +- drivers/media/platform/exynos4-is/fimc-lite.c | 2 +- drivers/media/platform/exynos4-is/fimc-lite.h | 2 +- drivers/media/platform/exynos4-is/fimc-reg.c | 2 +- drivers/media/platform/exynos4-is/media-dev.c | 329 ++------------------- drivers/media/platform/exynos4-is/media-dev.h | 6 +- drivers/media/platform/exynos4-is/mipi-csis.c | 43 +-- include/linux/platform_data/mipi-csis.h | 28 -- include/media/exynos-fimc.h | 161 ++++++++++ include/media/s5p_fimc.h | 182 ------------ 17 files changed, 211 insertions(+), 588 deletions(-) delete mode 100644 include/linux/platform_data/mipi-csis.h create mode 100644 include/media/exynos-fimc.h delete mode 100644 include/media/s5p_fimc.h (limited to 'include') diff --git a/Documentation/video4linux/fimc.txt b/Documentation/video4linux/fimc.txt index 7d6e160724bd..e0c6b8bc4743 100644 --- a/Documentation/video4linux/fimc.txt +++ b/Documentation/video4linux/fimc.txt @@ -140,39 +140,9 @@ You can either grep through the kernel log to find relevant information, i.e. or retrieve the information from /dev/media? with help of the media-ctl tool: # media-ctl -p -6. Platform support -=================== - -The machine code (arch/arm/plat-samsung and arch/arm/mach-*) must select -following options: - -CONFIG_S5P_DEV_FIMC0 mandatory -CONFIG_S5P_DEV_FIMC1 \ -CONFIG_S5P_DEV_FIMC2 | optional -CONFIG_S5P_DEV_FIMC3 | -CONFIG_S5P_SETUP_FIMC / -CONFIG_S5P_DEV_CSIS0 \ optional for MIPI-CSI interface -CONFIG_S5P_DEV_CSIS1 / - -Except that, relevant s5p_device_fimc? should be registered in the machine code -in addition to a "s5p-fimc-md" platform device to which the media device driver -is bound. The "s5p-fimc-md" device instance is required even if only mem-to-mem -operation is used. - -The description of sensor(s) attached to FIMC/MIPI-CSIS camera inputs should be -passed as the "s5p-fimc-md" device platform_data. The platform data structure -is defined in file include/media/s5p_fimc.h. - 7. Build ======== -This driver depends on following config options: -PLAT_S5P, -PM_RUNTIME, -I2C, -REGULATOR, -VIDEO_V4L2_SUBDEV_API, - If the driver is built as a loadable kernel module (CONFIG_VIDEO_SAMSUNG_S5P_FIMC=m) two modules are created (in addition to the core v4l2 modules): s5p-fimc.ko and optional s5p-csis.ko (MIPI-CSI receiver subdev). diff --git a/MAINTAINERS b/MAINTAINERS index 129621ed165f..6b7c633a2e98 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7654,7 +7654,6 @@ L: linux-media@vger.kernel.org Q: https://patchwork.linuxtv.org/project/linux-media/list/ S: Supported F: drivers/media/platform/exynos4-is/ -F: include/media/s5p_fimc.h SAMSUNG S3C24XX/S3C64XX SOC SERIES CAMIF DRIVER M: Sylwester Nawrocki diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index e1b2ceba00c1..5dcaa0a80540 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -3,6 +3,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on (PLAT_S5P || ARCH_EXYNOS) + depends on OF && COMMON_CLK help Say Y here to enable camera host interface devices for Samsung S5P and EXYNOS SoC series. @@ -17,7 +18,7 @@ config VIDEO_S5P_FIMC depends on I2C select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV - select MFD_SYSCON if OF + select MFD_SYSCON select VIDEO_EXYNOS4_IS_COMMON help This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c index 0ec210b4da1d..0eb34ecb8ee4 100644 --- a/drivers/media/platform/exynos4-is/common.c +++ b/drivers/media/platform/exynos4-is/common.c @@ -10,7 +10,7 @@ */ #include -#include +#include #include "common.h" /* Called with the media graph mutex held or entity->stream_count > 0. */ diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 1790fb4e32ea..6c75c6ced1f7 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #define dbg(fmt, args...) \ pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args) diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index ced46600e343..93f9cf2ebcd6 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "common.h" #include "media-dev.h" diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h index 4dc55a18d978..b99be09b49fc 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/drivers/media/platform/exynos4-is/fimc-isp.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include extern int fimc_isp_debug; diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index d0dc7ee04452..bc3ec7d25a32 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include "fimc-lite-reg.h" #include "fimc-lite.h" diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 630aef52dbb8..a97d2352f1d7 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "common.h" #include "fimc-core.h" diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index 7428b2d22b52..ea19dc7be63e 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #define FIMC_LITE_DRV_NAME "exynos-fimc-lite" #define FLITE_CLK_NAME "flite" diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c index 1db8cb4c46ef..2d77fd8f440a 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-reg.c @@ -13,7 +13,7 @@ #include #include -#include +#include #include "media-dev.h" #include "fimc-reg.h" diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 6e2d6042ade6..344718df5c62 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "media-dev.h" #include "fimc-core.h" @@ -39,10 +39,6 @@ #include "fimc-lite.h" #include "mipi-csis.h" -static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_source_info *si, - bool on); - /* Set up image sensor subdev -> FIMC capture node notifications. */ static void __setup_sensor_notification(struct fimc_md *fmd, struct v4l2_subdev *sensor, @@ -223,17 +219,10 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep, return ret; } - ret = fimc_md_set_camclk(sd, true); - if (ret < 0) - goto err_wbclk; - ret = fimc_pipeline_s_power(p, 1); if (!ret) return 0; - fimc_md_set_camclk(sd, false); - -err_wbclk: if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); @@ -259,7 +248,6 @@ static int __fimc_pipeline_close(struct exynos_media_pipeline *ep) } ret = fimc_pipeline_s_power(p, 0); - fimc_md_set_camclk(sd, false); fmd = entity_to_fimc_mdev(&sd->entity); @@ -337,75 +325,14 @@ static void fimc_md_pipelines_free(struct fimc_md *fmd) } } -/* - * Sensor subdevice helper functions - */ -static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, - struct fimc_source_info *si) -{ - struct i2c_adapter *adapter; - struct v4l2_subdev *sd = NULL; - - if (!si || !fmd) - return NULL; - /* - * If FIMC bus type is not Writeback FIFO assume it is same - * as sensor_bus_type. - */ - si->fimc_bus_type = si->sensor_bus_type; - - adapter = i2c_get_adapter(si->i2c_bus_num); - if (!adapter) { - v4l2_warn(&fmd->v4l2_dev, - "Failed to get I2C adapter %d, deferring probe\n", - si->i2c_bus_num); - return ERR_PTR(-EPROBE_DEFER); - } - sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, - si->board_info, NULL); - if (IS_ERR_OR_NULL(sd)) { - i2c_put_adapter(adapter); - v4l2_warn(&fmd->v4l2_dev, - "Failed to acquire subdev %s, deferring probe\n", - si->board_info->type); - return ERR_PTR(-EPROBE_DEFER); - } - v4l2_set_subdev_hostdata(sd, si); - sd->grp_id = GRP_ID_SENSOR; - - v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", - sd->name); - return sd; -} - -static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct i2c_adapter *adapter; - - if (!client || client->dev.of_node) - return; - - v4l2_device_unregister_subdev(sd); - - adapter = client->adapter; - i2c_unregister_device(client); - if (adapter) - i2c_put_adapter(adapter); -} - -#ifdef CONFIG_OF /* Parse port node and register as a sub-device any sensor specified there. */ static int fimc_md_parse_port_node(struct fimc_md *fmd, struct device_node *port, unsigned int index) { + struct fimc_source_info *pd = &fmd->sensor[index].pdata; struct device_node *rem, *ep, *np; - struct fimc_source_info *pd; struct v4l2_of_endpoint endpoint; - u32 val; - - pd = &fmd->sensor[index].pdata; /* Assume here a port node can have only one endpoint node. */ ep = of_get_next_child(port, NULL); @@ -425,20 +352,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, ep->full_name); return 0; } - if (!of_property_read_u32(rem, "samsung,camclk-out", &val)) - pd->clk_id = val; - - if (!of_property_read_u32(rem, "clock-frequency", &val)) - pd->clk_frequency = val; - else - pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ; - - if (pd->clk_frequency == 0) { - v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", - rem->full_name); - of_node_put(rem); - return -EINVAL; - } if (fimc_input_is_parallel(endpoint.base.port)) { if (endpoint.bus_type == V4L2_MBUS_PARALLEL) @@ -485,14 +398,26 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, } /* Register all SoC external sub-devices */ -static int fimc_md_of_sensors_register(struct fimc_md *fmd, - struct device_node *np) +static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct device_node *parent = fmd->pdev->dev.of_node; struct device_node *node, *ports; int index = 0; int ret; + /* + * Runtime resume one of the FIMC entities to make sure + * the sclk_cam clocks are not globally disabled. + */ + if (!fmd->pmf) + return -ENXIO; + + ret = pm_runtime_get_sync(fmd->pmf); + if (ret < 0) + return ret; + + fmd->num_sensors = 0; + /* Attach sensors linked to MIPI CSI-2 receivers */ for_each_available_child_of_node(parent, node) { struct device_node *port; @@ -506,14 +431,14 @@ static int fimc_md_of_sensors_register(struct fimc_md *fmd, ret = fimc_md_parse_port_node(fmd, port, index); if (ret < 0) - return ret; + goto rpm_put; index++; } /* Attach sensors listed in the parallel-ports node */ ports = of_get_child_by_name(parent, "parallel-ports"); if (!ports) - return 0; + goto rpm_put; for_each_child_of_node(ports, node) { ret = fimc_md_parse_port_node(fmd, node, index); @@ -521,8 +446,9 @@ static int fimc_md_of_sensors_register(struct fimc_md *fmd, break; index++; } - - return 0; +rpm_put: + pm_runtime_put(fmd->pmf); + return ret; } static int __of_get_csis_id(struct device_node *np) @@ -535,68 +461,10 @@ static int __of_get_csis_id(struct device_node *np) of_property_read_u32(np, "reg", ®); return reg - FIMC_INPUT_MIPI_CSI2_0; } -#else -#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS) -#define __of_get_csis_id(np) (-ENOSYS) -#endif - -static int fimc_md_register_sensor_entities(struct fimc_md *fmd) -{ - struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; - struct device_node *of_node = fmd->pdev->dev.of_node; - int num_clients = 0; - int ret, i; - - /* - * Runtime resume one of the FIMC entities to make sure - * the sclk_cam clocks are not globally disabled. - */ - if (!fmd->pmf) - return -ENXIO; - - ret = pm_runtime_get_sync(fmd->pmf); - if (ret < 0) - return ret; - - if (of_node) { - fmd->num_sensors = 0; - ret = fimc_md_of_sensors_register(fmd, of_node); - } else if (pdata) { - WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); - num_clients = min_t(u32, pdata->num_clients, - ARRAY_SIZE(fmd->sensor)); - fmd->num_sensors = num_clients; - - for (i = 0; i < num_clients; i++) { - struct fimc_sensor_info *si = &fmd->sensor[i]; - struct v4l2_subdev *sd; - - si->pdata = pdata->source_info[i]; - ret = __fimc_md_set_camclk(fmd, &si->pdata, true); - if (ret) - break; - sd = fimc_md_register_sensor(fmd, &si->pdata); - ret = __fimc_md_set_camclk(fmd, &si->pdata, false); - - if (IS_ERR(sd)) { - si->subdev = NULL; - ret = PTR_ERR(sd); - break; - } - si->subdev = sd; - if (ret) - break; - } - } - - pm_runtime_put(fmd->pmf); - return ret; -} /* * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration. */ - static int register_fimc_lite_entity(struct fimc_md *fmd, struct fimc_lite *fimc_lite) { @@ -753,35 +621,9 @@ dev_unlock: return ret; } -static int fimc_md_pdev_match(struct device *dev, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - int plat_entity = -1; - int ret; - char *p; - - if (!get_device(dev)) - return -ENODEV; - - if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) { - plat_entity = IDX_CSIS; - } else { - p = strstr(pdev->name, "fimc"); - if (p && *(p + 4) == 0) - plat_entity = IDX_FIMC; - } - - if (plat_entity >= 0) - ret = fimc_md_register_platform_entity(data, pdev, - plat_entity); - put_device(dev); - return 0; -} - /* Register FIMC, FIMC-LITE and CSIS media entities */ -#ifdef CONFIG_OF -static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, - struct device_node *parent) +static int fimc_md_register_platform_entities(struct fimc_md *fmd, + struct device_node *parent) { struct device_node *node; int ret = 0; @@ -815,9 +657,6 @@ static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, return ret; } -#else -#define fimc_md_register_of_platform_entities(fmd, node) (-ENOSYS) -#endif static void fimc_md_unregister_entities(struct fimc_md *fmd) { @@ -845,14 +684,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) v4l2_device_unregister_subdev(fmd->csis[i].sd); fmd->csis[i].sd = NULL; } - if (fmd->pdev->dev.of_node == NULL) { - for (i = 0; i < fmd->num_sensors; i++) { - if (fmd->sensor[i].subdev == NULL) - continue; - fimc_md_unregister_sensor(fmd->sensor[i].subdev); - fmd->sensor[i].subdev = NULL; - } - } if (fmd->fimc_is) v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev); @@ -1137,7 +968,7 @@ static void fimc_md_put_clocks(struct fimc_md *fmd) static int fimc_md_get_clocks(struct fimc_md *fmd) { - struct device *dev = NULL; + struct device *dev = &fmd->pdev->dev; char clk_name[32]; struct clk *clock; int i, ret = 0; @@ -1145,16 +976,12 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_CAMCLKS; i++) fmd->camclk[i].clock = ERR_PTR(-EINVAL); - if (fmd->pdev->dev.of_node) - dev = &fmd->pdev->dev; - for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); clock = clk_get(dev, clk_name); if (IS_ERR(clock)) { - dev_err(&fmd->pdev->dev, "Failed to get clock: %s\n", - clk_name); + dev_err(dev, "Failed to get clock: %s\n", clk_name); ret = PTR_ERR(clock); break; } @@ -1188,86 +1015,6 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) return ret; } -static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_source_info *si, - bool on) -{ - struct fimc_camclk_info *camclk; - int ret = 0; - - /* - * When device tree is used the sensor drivers are supposed to - * control the clock themselves. This whole function will be - * removed once S5PV210 platform is converted to the device tree. - */ - if (fmd->pdev->dev.of_node) - return 0; - - if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) - return -EINVAL; - - camclk = &fmd->camclk[si->clk_id]; - - dbg("camclk %d, f: %lu, use_count: %d, on: %d", - si->clk_id, si->clk_frequency, camclk->use_count, on); - - if (on) { - if (camclk->use_count > 0 && - camclk->frequency != si->clk_frequency) - return -EINVAL; - - if (camclk->use_count++ == 0) { - clk_set_rate(camclk->clock, si->clk_frequency); - camclk->frequency = si->clk_frequency; - ret = pm_runtime_get_sync(fmd->pmf); - if (ret < 0) - return ret; - ret = clk_prepare_enable(camclk->clock); - dbg("Enabled camclk %d: f: %lu", si->clk_id, - clk_get_rate(camclk->clock)); - } - return ret; - } - - if (WARN_ON(camclk->use_count == 0)) - return 0; - - if (--camclk->use_count == 0) { - clk_disable_unprepare(camclk->clock); - pm_runtime_put(fmd->pmf); - dbg("Disabled camclk %d", si->clk_id); - } - return ret; -} - -/** - * fimc_md_set_camclk - peripheral sensor clock setup - * @sd: sensor subdev to configure sclk_cam clock for - * @on: 1 to enable or 0 to disable the clock - * - * There are 2 separate clock outputs available in the SoC for external - * image processors. These clocks are shared between all registered FIMC - * devices to which sensors can be attached, either directly or through - * the MIPI CSI receiver. The clock is allowed here to be used by - * multiple sensors concurrently if they use same frequency. - * This function should only be called when the graph mutex is held. - */ -int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) -{ - struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); - struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); - - /* - * If there is a clock provider registered the sensors will - * handle their clock themselves, no need to control it on - * the host interface side. - */ - if (fmd->clk_provider.num_clocks > 0) - return 0; - - return __fimc_md_set_camclk(fmd, si, on); -} - static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable) { struct exynos_video_entity *ve; @@ -1426,7 +1173,6 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) return 0; } -#ifdef CONFIG_OF static int cam_clk_prepare(struct clk_hw *hw) { struct cam_clk *camclk = to_cam_clk(hw); @@ -1518,10 +1264,6 @@ err: fimc_md_unregister_clk_provider(fmd); return ret; } -#else -#define fimc_md_register_clk_provider(fmd) (0) -#define fimc_md_unregister_clk_provider(fmd) -#endif static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, @@ -1585,8 +1327,8 @@ static int fimc_md_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&fmd->slock); - fmd->pdev = pdev; INIT_LIST_HEAD(&fmd->pipelines); + fmd->pdev = pdev; strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", sizeof(fmd->media_dev.model)); @@ -1599,6 +1341,7 @@ static int fimc_md_probe(struct platform_device *pdev) strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name)); fmd->use_isp = fimc_md_is_isp_available(dev->of_node); + fmd->user_subdev_api = true; ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { @@ -1616,8 +1359,6 @@ static int fimc_md_probe(struct platform_device *pdev) if (ret) goto err_md; - fmd->user_subdev_api = (dev->of_node != NULL); - ret = fimc_md_get_pinctrl(fmd); if (ret < 0) { if (ret != EPROBE_DEFER) @@ -1630,22 +1371,16 @@ static int fimc_md_probe(struct platform_device *pdev) /* Protect the media graph while we're registering entities */ mutex_lock(&fmd->media_dev.graph_mutex); - if (dev->of_node) - ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); - else - ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, - fimc_md_pdev_match); + ret = fimc_md_register_platform_entities(fmd, dev->of_node); if (ret) { mutex_unlock(&fmd->media_dev.graph_mutex); goto err_clk; } - if (dev->platform_data || dev->of_node) { - ret = fimc_md_register_sensor_entities(fmd); - if (ret) { - mutex_unlock(&fmd->media_dev.graph_mutex); - goto err_m_ent; - } + ret = fimc_md_register_sensor_entities(fmd); + if (ret) { + mutex_unlock(&fmd->media_dev.graph_mutex); + goto err_m_ent; } mutex_unlock(&fmd->media_dev.graph_mutex); diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 58c49456b13f..03214541f149 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "fimc-core.h" #include "fimc-lite.h" @@ -94,9 +94,7 @@ struct fimc_sensor_info { }; struct cam_clk { -#ifdef CONFIG_COMMON_CLK struct clk_hw hw; -#endif struct fimc_md *fmd; }; #define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw) @@ -144,9 +142,7 @@ struct fimc_md { struct cam_clk_provider { struct clk *clks[FIMC_MAX_CAMCLKS]; -#ifdef CONFIG_COMMON_CLK struct clk_onecell_data clk_data; -#endif struct device_node *of_node; struct cam_clk camclk[FIMC_MAX_CAMCLKS]; int num_clocks; diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 3678ba59725c..ae54ef5f535d 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -22,14 +22,13 @@ #include #include #include -#include #include #include #include #include #include #include -#include +#include #include #include @@ -730,26 +729,6 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int s5pcsis_get_platform_data(struct platform_device *pdev, - struct csis_state *state) -{ - struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data; - - if (pdata == NULL) { - dev_err(&pdev->dev, "Platform data not specified\n"); - return -EINVAL; - } - - state->clk_frequency = pdata->clk_rate; - state->num_lanes = pdata->lanes; - state->hs_settle = pdata->hs_settle; - state->index = max(0, pdev->id); - state->max_num_lanes = state->index ? CSIS1_MAX_LANES : - CSIS0_MAX_LANES; - return 0; -} - -#ifdef CONFIG_OF static int s5pcsis_parse_dt(struct platform_device *pdev, struct csis_state *state) { @@ -787,9 +766,6 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, return 0; } -#else -#define s5pcsis_parse_dt(pdev, state) (-ENOSYS) -#endif static int s5pcsis_pm_resume(struct device *dev, bool runtime); static const struct of_device_id s5pcsis_of_match[]; @@ -812,19 +788,14 @@ static int s5pcsis_probe(struct platform_device *pdev) spin_lock_init(&state->slock); state->pdev = pdev; - if (dev->of_node) { - of_id = of_match_node(s5pcsis_of_match, dev->of_node); - if (WARN_ON(of_id == NULL)) - return -EINVAL; - - drv_data = of_id->data; - state->interrupt_mask = drv_data->interrupt_mask; + of_id = of_match_node(s5pcsis_of_match, dev->of_node); + if (WARN_ON(of_id == NULL)) + return -EINVAL; - ret = s5pcsis_parse_dt(pdev, state); - } else { - ret = s5pcsis_get_platform_data(pdev, state); - } + drv_data = of_id->data; + state->interrupt_mask = drv_data->interrupt_mask; + ret = s5pcsis_parse_dt(pdev, state); if (ret < 0) return ret; diff --git a/include/linux/platform_data/mipi-csis.h b/include/linux/platform_data/mipi-csis.h deleted file mode 100644 index c2fd9024717c..000000000000 --- a/include/linux/platform_data/mipi-csis.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. - * - * Samsung S5P/Exynos SoC series MIPI CSIS device support - * - * 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 __PLAT_SAMSUNG_MIPI_CSIS_H_ -#define __PLAT_SAMSUNG_MIPI_CSIS_H_ __FILE__ - -/** - * struct s5p_platform_mipi_csis - platform data for S5P MIPI-CSIS driver - * @clk_rate: bus clock frequency - * @wclk_source: CSI wrapper clock selection: 0 - bus clock, 1 - ext. SCLK_CAM - * @lanes: number of data lanes used - * @hs_settle: HS-RX settle time - */ -struct s5p_platform_mipi_csis { - unsigned long clk_rate; - u8 wclk_source; - u8 lanes; - u8 hs_settle; -}; - -#endif /* __PLAT_SAMSUNG_MIPI_CSIS_H_ */ diff --git a/include/media/exynos-fimc.h b/include/media/exynos-fimc.h new file mode 100644 index 000000000000..aa44660e2041 --- /dev/null +++ b/include/media/exynos-fimc.h @@ -0,0 +1,161 @@ +/* + * Samsung S5P/Exynos4 SoC series camera interface driver header + * + * Copyright (C) 2010 - 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * 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 S5P_FIMC_H_ +#define S5P_FIMC_H_ + +#include +#include +#include + +/* + * Enumeration of data inputs to the camera subsystem. + */ +enum fimc_input { + FIMC_INPUT_PARALLEL_0 = 1, + FIMC_INPUT_PARALLEL_1, + FIMC_INPUT_MIPI_CSI2_0 = 3, + FIMC_INPUT_MIPI_CSI2_1, + FIMC_INPUT_WRITEBACK_A = 5, + FIMC_INPUT_WRITEBACK_B, + FIMC_INPUT_WRITEBACK_ISP = 5, +}; + +/* + * Enumeration of the FIMC data bus types. + */ +enum fimc_bus_type { + /* Camera parallel bus */ + FIMC_BUS_TYPE_ITU_601 = 1, + /* Camera parallel bus with embedded synchronization */ + FIMC_BUS_TYPE_ITU_656, + /* Camera MIPI-CSI2 serial bus */ + FIMC_BUS_TYPE_MIPI_CSI2, + /* FIFO link from LCD controller (WriteBack A) */ + FIMC_BUS_TYPE_LCD_WRITEBACK_A, + /* FIFO link from LCD controller (WriteBack B) */ + FIMC_BUS_TYPE_LCD_WRITEBACK_B, + /* FIFO link from FIMC-IS */ + FIMC_BUS_TYPE_ISP_WRITEBACK = FIMC_BUS_TYPE_LCD_WRITEBACK_B, +}; + +#define fimc_input_is_parallel(x) ((x) == 1 || (x) == 2) +#define fimc_input_is_mipi_csi(x) ((x) == 3 || (x) == 4) + +/* + * The subdevices' group IDs. + */ +#define GRP_ID_SENSOR (1 << 8) +#define GRP_ID_FIMC_IS_SENSOR (1 << 9) +#define GRP_ID_WRITEBACK (1 << 10) +#define GRP_ID_CSIS (1 << 11) +#define GRP_ID_FIMC (1 << 12) +#define GRP_ID_FLITE (1 << 13) +#define GRP_ID_FIMC_IS (1 << 14) + +/** + * struct fimc_source_info - video source description required for the host + * interface configuration + * + * @fimc_bus_type: FIMC camera input type + * @sensor_bus_type: image sensor bus type, MIPI, ITU-R BT.601 etc. + * @flags: the parallel sensor bus flags defining signals polarity (V4L2_MBUS_*) + * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) + */ +struct fimc_source_info { + enum fimc_bus_type fimc_bus_type; + enum fimc_bus_type sensor_bus_type; + u16 flags; + u16 mux_id; +}; + +/* + * v4l2_device notification id. This is only for internal use in the kernel. + * Sensor subdevs should issue S5P_FIMC_TX_END_NOTIFY notification in single + * frame capture mode when there is only one VSYNC pulse issued by the sensor + * at begining of the frame transmission. + */ +#define S5P_FIMC_TX_END_NOTIFY _IO('e', 0) + +#define FIMC_MAX_PLANES 3 + +/** + * struct fimc_fmt - color format data structure + * @mbus_code: media bus pixel code, -1 if not applicable + * @name: format description + * @fourcc: fourcc code for this format, 0 if not applicable + * @color: the driver's private color format id + * @memplanes: number of physically non-contiguous data planes + * @colplanes: number of physically contiguous data planes + * @colorspace: v4l2 colorspace (V4L2_COLORSPACE_*) + * @depth: per plane driver's private 'number of bits per pixel' + * @mdataplanes: bitmask indicating meta data plane(s), (1 << plane_no) + * @flags: flags indicating which operation mode format applies to + */ +struct fimc_fmt { + enum v4l2_mbus_pixelcode mbus_code; + char *name; + u32 fourcc; + u32 color; + u16 memplanes; + u16 colplanes; + u8 colorspace; + u8 depth[FIMC_MAX_PLANES]; + u16 mdataplanes; + u16 flags; +#define FMT_FLAGS_CAM (1 << 0) +#define FMT_FLAGS_M2M_IN (1 << 1) +#define FMT_FLAGS_M2M_OUT (1 << 2) +#define FMT_FLAGS_M2M (1 << 1 | 1 << 2) +#define FMT_HAS_ALPHA (1 << 3) +#define FMT_FLAGS_COMPRESSED (1 << 4) +#define FMT_FLAGS_WRITEBACK (1 << 5) +#define FMT_FLAGS_RAW_BAYER (1 << 6) +#define FMT_FLAGS_YUV (1 << 7) +}; + +struct exynos_media_pipeline; + +/* + * Media pipeline operations to be called from within a video node, i.e. the + * last entity within the pipeline. Implemented by related media device driver. + */ +struct exynos_media_pipeline_ops { + int (*prepare)(struct exynos_media_pipeline *p, + struct media_entity *me); + int (*unprepare)(struct exynos_media_pipeline *p); + int (*open)(struct exynos_media_pipeline *p, struct media_entity *me, + bool resume); + int (*close)(struct exynos_media_pipeline *p); + int (*set_stream)(struct exynos_media_pipeline *p, bool state); +}; + +struct exynos_video_entity { + struct video_device vdev; + struct exynos_media_pipeline *pipe; +}; + +struct exynos_media_pipeline { + struct media_pipeline mp; + const struct exynos_media_pipeline_ops *ops; +}; + +static inline struct exynos_video_entity *vdev_to_exynos_video_entity( + struct video_device *vdev) +{ + return container_of(vdev, struct exynos_video_entity, vdev); +} + +#define fimc_pipeline_call(ent, op, args...) \ + (!(ent) ? -ENOENT : (((ent)->pipe->ops && (ent)->pipe->ops->op) ? \ + (ent)->pipe->ops->op(((ent)->pipe), ##args) : -ENOIOCTLCMD)) \ + +#endif /* S5P_FIMC_H_ */ diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h deleted file mode 100644 index b975c285c8a9..000000000000 --- a/include/media/s5p_fimc.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Samsung S5P/Exynos4 SoC series camera interface driver header - * - * Copyright (C) 2010 - 2013 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki - * - * 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 S5P_FIMC_H_ -#define S5P_FIMC_H_ - -#include -#include -#include - -/* - * Enumeration of data inputs to the camera subsystem. - */ -enum fimc_input { - FIMC_INPUT_PARALLEL_0 = 1, - FIMC_INPUT_PARALLEL_1, - FIMC_INPUT_MIPI_CSI2_0 = 3, - FIMC_INPUT_MIPI_CSI2_1, - FIMC_INPUT_WRITEBACK_A = 5, - FIMC_INPUT_WRITEBACK_B, - FIMC_INPUT_WRITEBACK_ISP = 5, -}; - -/* - * Enumeration of the FIMC data bus types. - */ -enum fimc_bus_type { - /* Camera parallel bus */ - FIMC_BUS_TYPE_ITU_601 = 1, - /* Camera parallel bus with embedded synchronization */ - FIMC_BUS_TYPE_ITU_656, - /* Camera MIPI-CSI2 serial bus */ - FIMC_BUS_TYPE_MIPI_CSI2, - /* FIFO link from LCD controller (WriteBack A) */ - FIMC_BUS_TYPE_LCD_WRITEBACK_A, - /* FIFO link from LCD controller (WriteBack B) */ - FIMC_BUS_TYPE_LCD_WRITEBACK_B, - /* FIFO link from FIMC-IS */ - FIMC_BUS_TYPE_ISP_WRITEBACK = FIMC_BUS_TYPE_LCD_WRITEBACK_B, -}; - -#define fimc_input_is_parallel(x) ((x) == 1 || (x) == 2) -#define fimc_input_is_mipi_csi(x) ((x) == 3 || (x) == 4) - -/* - * The subdevices' group IDs. - */ -#define GRP_ID_SENSOR (1 << 8) -#define GRP_ID_FIMC_IS_SENSOR (1 << 9) -#define GRP_ID_WRITEBACK (1 << 10) -#define GRP_ID_CSIS (1 << 11) -#define GRP_ID_FIMC (1 << 12) -#define GRP_ID_FLITE (1 << 13) -#define GRP_ID_FIMC_IS (1 << 14) - -struct i2c_board_info; - -/** - * struct fimc_source_info - video source description required for the host - * interface configuration - * - * @board_info: pointer to I2C subdevice's board info - * @clk_frequency: frequency of the clock the host interface provides to sensor - * @fimc_bus_type: FIMC camera input type - * @sensor_bus_type: image sensor bus type, MIPI, ITU-R BT.601 etc. - * @flags: the parallel sensor bus flags defining signals polarity (V4L2_MBUS_*) - * @i2c_bus_num: i2c control bus id the sensor is attached to - * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) - * @clk_id: index of the SoC peripheral clock for sensors - */ -struct fimc_source_info { - struct i2c_board_info *board_info; - unsigned long clk_frequency; - enum fimc_bus_type fimc_bus_type; - enum fimc_bus_type sensor_bus_type; - u16 flags; - u16 i2c_bus_num; - u16 mux_id; - u8 clk_id; -}; - -/** - * struct s5p_platform_fimc - camera host interface platform data - * - * @source_info: properties of an image source for the host interface setup - * @num_clients: the number of attached image sources - */ -struct s5p_platform_fimc { - struct fimc_source_info *source_info; - int num_clients; -}; - -/* - * v4l2_device notification id. This is only for internal use in the kernel. - * Sensor subdevs should issue S5P_FIMC_TX_END_NOTIFY notification in single - * frame capture mode when there is only one VSYNC pulse issued by the sensor - * at begining of the frame transmission. - */ -#define S5P_FIMC_TX_END_NOTIFY _IO('e', 0) - -#define FIMC_MAX_PLANES 3 - -/** - * struct fimc_fmt - color format data structure - * @mbus_code: media bus pixel code, -1 if not applicable - * @name: format description - * @fourcc: fourcc code for this format, 0 if not applicable - * @color: the driver's private color format id - * @memplanes: number of physically non-contiguous data planes - * @colplanes: number of physically contiguous data planes - * @colorspace: v4l2 colorspace (V4L2_COLORSPACE_*) - * @depth: per plane driver's private 'number of bits per pixel' - * @mdataplanes: bitmask indicating meta data plane(s), (1 << plane_no) - * @flags: flags indicating which operation mode format applies to - */ -struct fimc_fmt { - enum v4l2_mbus_pixelcode mbus_code; - char *name; - u32 fourcc; - u32 color; - u16 memplanes; - u16 colplanes; - u8 colorspace; - u8 depth[FIMC_MAX_PLANES]; - u16 mdataplanes; - u16 flags; -#define FMT_FLAGS_CAM (1 << 0) -#define FMT_FLAGS_M2M_IN (1 << 1) -#define FMT_FLAGS_M2M_OUT (1 << 2) -#define FMT_FLAGS_M2M (1 << 1 | 1 << 2) -#define FMT_HAS_ALPHA (1 << 3) -#define FMT_FLAGS_COMPRESSED (1 << 4) -#define FMT_FLAGS_WRITEBACK (1 << 5) -#define FMT_FLAGS_RAW_BAYER (1 << 6) -#define FMT_FLAGS_YUV (1 << 7) -}; - -struct exynos_media_pipeline; - -/* - * Media pipeline operations to be called from within a video node, i.e. the - * last entity within the pipeline. Implemented by related media device driver. - */ -struct exynos_media_pipeline_ops { - int (*prepare)(struct exynos_media_pipeline *p, - struct media_entity *me); - int (*unprepare)(struct exynos_media_pipeline *p); - int (*open)(struct exynos_media_pipeline *p, struct media_entity *me, - bool resume); - int (*close)(struct exynos_media_pipeline *p); - int (*set_stream)(struct exynos_media_pipeline *p, bool state); -}; - -struct exynos_video_entity { - struct video_device vdev; - struct exynos_media_pipeline *pipe; -}; - -struct exynos_media_pipeline { - struct media_pipeline mp; - const struct exynos_media_pipeline_ops *ops; -}; - -static inline struct exynos_video_entity *vdev_to_exynos_video_entity( - struct video_device *vdev) -{ - return container_of(vdev, struct exynos_video_entity, vdev); -} - -#define fimc_pipeline_call(ent, op, args...) \ - (!(ent) ? -ENOENT : (((ent)->pipe->ops && (ent)->pipe->ops->op) ? \ - (ent)->pipe->ops->op(((ent)->pipe), ##args) : -ENOIOCTLCMD)) \ - -#endif /* S5P_FIMC_H_ */ -- cgit v1.2.3 From 3cbe6e5bcad0b102c06b9c6029fda75630045475 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Wed, 14 May 2014 03:59:42 -0300 Subject: [media] v4l: Add source change event This event indicates that the video device has encountered a source parameter change during runtime. This can typically be a resolution change detected by a video decoder OR a format change detected by an input connector. This needs to be nofified to the userspace and the application may be expected to reallocate buffers before proceeding. The application can subscribe to events on a specific pad or input port which it is interested in. Signed-off-by: Arun Kumar K Acked-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-dqevent.xml | 33 ++++++++++++++++++++ .../DocBook/media/v4l/vidioc-subscribe-event.xml | 20 ++++++++++++ drivers/media/v4l2-core/v4l2-event.c | 36 ++++++++++++++++++++++ include/media/v4l2-event.h | 4 +++ include/uapi/linux/videodev2.h | 8 +++++ 5 files changed, 101 insertions(+) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml index 89891adb928a..820f86e8744b 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml @@ -242,6 +242,22 @@ + + struct <structname>v4l2_event_src_change</structname> + + &cs-str; + + + __u32 + changes + + A bitmask that tells what has changed. See . + + + + +
+ Changes @@ -270,6 +286,23 @@
+ + + Source Changes + + &cs-def; + + + V4L2_EVENT_SRC_CH_RESOLUTION + 0x0001 + This event gets triggered when a resolution change is + detected at an input. This can come from an input connector or + from a video decoder. + + + + +
&return-value; diff --git a/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml b/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml index 5c70b616d818..f016254377aa 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml @@ -154,6 +154,26 @@ frame interval in between them.
+ + V4L2_EVENT_SOURCE_CHANGE + 5 + + This event is triggered when a source parameter change is + detected during runtime by the video device. It can be a + runtime resolution change triggered by a video decoder or the + format change happening on an input connector. + This event requires that the id + matches the input index (when used with a video device node) + or the pad index (when used with a subdevice node) from which + you want to receive events. + + This event has a &v4l2-event-source-change; associated + with it. The changes bitfield denotes + what has changed for the subscribed pad. If multiple events + occurred before application could dequeue them, then the changes + will have the ORed value of all the events generated. + + V4L2_EVENT_PRIVATE_START 0x08000000 diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index 86dcb5483c42..8761aab99de9 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -318,3 +318,39 @@ int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, return v4l2_event_unsubscribe(fh, sub); } EXPORT_SYMBOL_GPL(v4l2_event_subdev_unsubscribe); + +static void v4l2_event_src_replace(struct v4l2_event *old, + const struct v4l2_event *new) +{ + u32 old_changes = old->u.src_change.changes; + + old->u.src_change = new->u.src_change; + old->u.src_change.changes |= old_changes; +} + +static void v4l2_event_src_merge(const struct v4l2_event *old, + struct v4l2_event *new) +{ + new->u.src_change.changes |= old->u.src_change.changes; +} + +static const struct v4l2_subscribed_event_ops v4l2_event_src_ch_ops = { + .replace = v4l2_event_src_replace, + .merge = v4l2_event_src_merge, +}; + +int v4l2_src_change_event_subscribe(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + if (sub->type == V4L2_EVENT_SOURCE_CHANGE) + return v4l2_event_subscribe(fh, sub, 0, &v4l2_event_src_ch_ops); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_src_change_event_subscribe); + +int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd, + struct v4l2_fh *fh, struct v4l2_event_subscription *sub) +{ + return v4l2_src_change_event_subscribe(fh, sub); +} +EXPORT_SYMBOL_GPL(v4l2_src_change_event_subdev_subscribe); diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index be05d019de25..1ab9045e52e3 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -132,4 +132,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, void v4l2_event_unsubscribe_all(struct v4l2_fh *fh); int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub); +int v4l2_src_change_event_subscribe(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub); +int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd, + struct v4l2_fh *fh, struct v4l2_event_subscription *sub); #endif /* V4L2_EVENT_H */ diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index db4aebd8baba..00259d2baa10 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1764,6 +1764,7 @@ struct v4l2_streamparm { #define V4L2_EVENT_EOS 2 #define V4L2_EVENT_CTRL 3 #define V4L2_EVENT_FRAME_SYNC 4 +#define V4L2_EVENT_SOURCE_CHANGE 5 #define V4L2_EVENT_PRIVATE_START 0x08000000 /* Payload for V4L2_EVENT_VSYNC */ @@ -1795,12 +1796,19 @@ struct v4l2_event_frame_sync { __u32 frame_sequence; }; +#define V4L2_EVENT_SRC_CH_RESOLUTION (1 << 0) + +struct v4l2_event_src_change { + __u32 changes; +}; + struct v4l2_event { __u32 type; union { struct v4l2_event_vsync vsync; struct v4l2_event_ctrl ctrl; struct v4l2_event_frame_sync frame_sync; + struct v4l2_event_src_change src_change; __u8 data[64]; } u; __u32 pending; -- cgit v1.2.3 From 8cd84092d35e52372da2c3c3c2afb1a719917af2 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 9 May 2014 16:43:08 +0900 Subject: PM / devfreq: Add resource-managed function for devfreq device This patch add resource-managed function for devfreq device as following functions. The devm_devfreq_add_device() manages automatically the memory of devfreq device using device resource management. - devm_devfreq_add_device() - devm_devfreq_remove_device() Signed-off-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/devfreq.h | 21 +++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index af4af7708574..8b6295d9d1f5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -544,6 +544,69 @@ int devfreq_remove_device(struct devfreq *devfreq) } EXPORT_SYMBOL(devfreq_remove_device); +static int devm_devfreq_dev_match(struct device *dev, void *res, void *data) +{ + struct devfreq **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +static void devm_devfreq_dev_release(struct device *dev, void *res) +{ + devfreq_remove_device(*(struct devfreq **)res); +} + +/** + * devm_devfreq_add_device() - Resource-managed devfreq_add_device() + * @dev: the device to add devfreq feature. + * @profile: device-specific profile to run devfreq. + * @governor_name: name of the policy to choose frequency. + * @data: private data for the governor. The devfreq framework does not + * touch this value. + * + * This function manages automatically the memory of devfreq device using device + * resource management and simplify the free operation for memory of devfreq + * device. + */ +struct devfreq *devm_devfreq_add_device(struct device *dev, + struct devfreq_dev_profile *profile, + const char *governor_name, + void *data) +{ + struct devfreq **ptr, *devfreq; + + ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + devfreq = devfreq_add_device(dev, profile, governor_name, data); + if (IS_ERR(devfreq)) { + devres_free(ptr); + return ERR_PTR(-ENOMEM); + } + + *ptr = devfreq; + devres_add(dev, ptr); + + return devfreq; +} +EXPORT_SYMBOL(devm_devfreq_add_device); + +/** + * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() + * @dev: the device to add devfreq feature. + * @devfreq: the devfreq instance to be removed + */ +void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq) +{ + WARN_ON(devres_release(dev, devm_devfreq_dev_release, + devm_devfreq_dev_match, devfreq)); +} +EXPORT_SYMBOL(devm_devfreq_remove_device); + /** * devfreq_suspend_device() - Suspend devfreq of a device. * @devfreq: the devfreq instance to be suspended diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index d48dc00232a4..023d668a2cb5 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -181,6 +181,12 @@ extern struct devfreq *devfreq_add_device(struct device *dev, const char *governor_name, void *data); extern int devfreq_remove_device(struct devfreq *devfreq); +extern struct devfreq *devm_devfreq_add_device(struct device *dev, + struct devfreq_dev_profile *profile, + const char *governor_name, + void *data); +extern void devm_devfreq_remove_device(struct device *dev, + struct devfreq *devfreq); /* Supposed to be called by PM_SLEEP/PM_RUNTIME callbacks */ extern int devfreq_suspend_device(struct devfreq *devfreq); @@ -220,7 +226,7 @@ static inline struct devfreq *devfreq_add_device(struct device *dev, const char *governor_name, void *data) { - return NULL; + return ERR_PTR(-ENOSYS); } static inline int devfreq_remove_device(struct devfreq *devfreq) @@ -228,6 +234,19 @@ static inline int devfreq_remove_device(struct devfreq *devfreq) return 0; } +static inline struct devfreq *devm_devfreq_add_device(struct device *dev, + struct devfreq_dev_profile *profile, + const char *governor_name, + void *data) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void devm_devfreq_remove_device(struct device *dev, + struct devfreq *devfreq) +{ +} + static inline int devfreq_suspend_device(struct devfreq *devfreq) { return 0; -- cgit v1.2.3 From d5b040d0cab9cae1dc1ad61a07019062235f4878 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 9 May 2014 16:43:09 +0900 Subject: PM / devfreq: Add devm_devfreq_{register,unregister}_opp_notfier function This patch add resource-managed function for devfreq opp as following functions. The devm_devfreq_register_opp_notifier() manages automatically the registration of devfreq opp using device resource management. - devm_devfreq_register_opp_notifier - devm_devfreq_unregister_opp_notifier() Signed-off-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/devfreq.h | 14 ++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'include') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 8b6295d9d1f5..9f90369dd6bd 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1169,6 +1169,54 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) return ret; } +static void devm_devfreq_opp_release(struct device *dev, void *res) +{ + devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res); +} + +/** + * devm_ devfreq_register_opp_notifier() + * - Resource-managed devfreq_register_opp_notifier() + * @dev: The devfreq user device. (parent of devfreq) + * @devfreq: The devfreq object. + */ +int devm_devfreq_register_opp_notifier(struct device *dev, + struct devfreq *devfreq) +{ + struct devfreq **ptr; + int ret; + + ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = devfreq_register_opp_notifier(dev, devfreq); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = devfreq; + devres_add(dev, ptr); + + return 0; +} +EXPORT_SYMBOL(devm_devfreq_register_opp_notifier); + +/** + * devm_devfreq_unregister_opp_notifier() + * - Resource-managed devfreq_unregister_opp_notifier() + * @dev: The devfreq user device. (parent of devfreq) + * @devfreq: The devfreq object. + */ +void devm_devfreq_unregister_opp_notifier(struct device *dev, + struct devfreq *devfreq) +{ + WARN_ON(devres_release(dev, devm_devfreq_opp_release, + devm_devfreq_dev_match, devfreq)); +} +EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); + MODULE_AUTHOR("MyungJoo Ham "); MODULE_DESCRIPTION("devfreq class support"); MODULE_LICENSE("GPL"); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 023d668a2cb5..f1863dcd83ea 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -199,6 +199,10 @@ extern int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq); extern int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq); +extern int devm_devfreq_register_opp_notifier(struct device *dev, + struct devfreq *devfreq); +extern void devm_devfreq_unregister_opp_notifier(struct device *dev, + struct devfreq *devfreq); #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) /** @@ -275,6 +279,16 @@ static inline int devfreq_unregister_opp_notifier(struct device *dev, return -EINVAL; } +static inline int devm_devfreq_register_opp_notifier(struct device *dev, + struct devfreq *devfreq) +{ + return -EINVAL; +} + +static inline void devm_devfreq_unregister_opp_notifier(struct device *dev, + struct devfreq *devfreq) +{ +} #endif /* CONFIG_PM_DEVFREQ */ #endif /* __LINUX_DEVFREQ_H__ */ -- cgit v1.2.3 From 8774bed9ce832d8d9ccb79e92800b808aa2d2ad2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 28 Apr 2014 16:53:01 -0300 Subject: [media] v4l: subdev: Move [gs]_std operation to video ops The g_std and s_std operations are video-related, move them to the video ops where they belong. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Lad, Prabhakar Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 2 +- drivers/media/i2c/adv7183.c | 4 ++-- drivers/media/i2c/adv7842.c | 4 ++-- drivers/media/i2c/bt819.c | 2 +- drivers/media/i2c/cx25840/cx25840-core.c | 4 ++-- drivers/media/i2c/ks0127.c | 6 +----- drivers/media/i2c/ml86v7667.c | 2 +- drivers/media/i2c/msp3400-driver.c | 2 +- drivers/media/i2c/saa6752hs.c | 2 +- drivers/media/i2c/saa7110.c | 2 +- drivers/media/i2c/saa7115.c | 2 +- drivers/media/i2c/saa717x.c | 2 +- drivers/media/i2c/saa7191.c | 2 +- drivers/media/i2c/soc_camera/tw9910.c | 4 ++-- drivers/media/i2c/sony-btf-mpx.c | 10 +++++----- drivers/media/i2c/tvaudio.c | 6 +++++- drivers/media/i2c/tvp514x.c | 2 +- drivers/media/i2c/tvp5150.c | 2 +- drivers/media/i2c/tw2804.c | 2 +- drivers/media/i2c/tw9903.c | 2 +- drivers/media/i2c/tw9906.c | 2 +- drivers/media/i2c/vp27smpx.c | 6 +++++- drivers/media/i2c/vpx3220.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 2 +- drivers/media/pci/cx18/cx18-av-core.c | 2 +- drivers/media/pci/cx18/cx18-fileops.c | 2 +- drivers/media/pci/cx18/cx18-gpio.c | 6 +++++- drivers/media/pci/cx18/cx18-ioctl.c | 2 +- drivers/media/pci/cx23885/cx23885-video.c | 4 ++-- drivers/media/pci/cx88/cx88-core.c | 2 +- drivers/media/pci/ivtv/ivtv-fileops.c | 2 +- drivers/media/pci/ivtv/ivtv-ioctl.c | 2 +- drivers/media/pci/saa7134/saa7134-video.c | 4 ++-- drivers/media/pci/saa7146/mxb.c | 14 +++++++------- drivers/media/pci/sta2x11/sta2x11_vip.c | 4 ++-- drivers/media/pci/zoran/zoran_device.c | 2 +- drivers/media/pci/zoran/zoran_driver.c | 2 +- drivers/media/platform/blackfin/bfin_capture.c | 4 ++-- drivers/media/platform/davinci/vpfe_capture.c | 2 +- drivers/media/platform/davinci/vpif_capture.c | 2 +- drivers/media/platform/davinci/vpif_display.c | 2 +- drivers/media/platform/fsl-viu.c | 2 +- drivers/media/platform/soc_camera/soc_camera.c | 4 ++-- drivers/media/platform/timblogiw.c | 2 +- drivers/media/platform/vino.c | 6 +++--- drivers/media/usb/au0828/au0828-video.c | 4 ++-- drivers/media/usb/cx231xx/cx231xx-417.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 6 +++--- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/pvrusb2/pvrusb2-hdw.c | 2 +- drivers/media/usb/stk1160/stk1160-v4l.c | 4 ++-- drivers/media/usb/tm6000/tm6000-cards.c | 2 +- drivers/media/usb/tm6000/tm6000-video.c | 2 +- drivers/media/usb/usbvision/usbvision-video.c | 2 +- drivers/media/v4l2-core/tuner-core.c | 6 +++++- drivers/staging/media/davinci_vpfe/vpfe_video.c | 2 +- drivers/staging/media/go7007/go7007-v4l2.c | 2 +- drivers/staging/media/go7007/s2250-board.c | 2 +- drivers/staging/media/go7007/saa7134-go7007.c | 4 ++++ include/media/v4l2-subdev.h | 6 +++--- 60 files changed, 107 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 5e638b159452..ac1cdbe251a3 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -461,6 +461,7 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd, } static const struct v4l2_subdev_video_ops adv7180_video_ops = { + .s_std = adv7180_s_std, .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, @@ -472,7 +473,6 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { - .s_std = adv7180_s_std, .s_power = adv7180_s_power, }; diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index d45e0e3a781d..df461b07b2f7 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -501,8 +501,6 @@ static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7183_core_ops = { .log_status = adv7183_log_status, - .g_std = adv7183_g_std, - .s_std = adv7183_s_std, .reset = adv7183_reset, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv7183_g_register, @@ -511,6 +509,8 @@ static const struct v4l2_subdev_core_ops adv7183_core_ops = { }; static const struct v4l2_subdev_video_ops adv7183_video_ops = { + .g_std = adv7183_g_std, + .s_std = adv7183_s_std, .s_routing = adv7183_s_routing, .querystd = adv7183_querystd, .g_input_status = adv7183_g_input_status, diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 06c25c3fa3a9..6a4d389c749c 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2873,8 +2873,6 @@ static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7842_core_ops = { .log_status = adv7842_log_status, - .g_std = adv7842_g_std, - .s_std = adv7842_s_std, .ioctl = adv7842_ioctl, .interrupt_service_routine = adv7842_isr, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -2884,6 +2882,8 @@ static const struct v4l2_subdev_core_ops adv7842_core_ops = { }; static const struct v4l2_subdev_video_ops adv7842_video_ops = { + .g_std = adv7842_g_std, + .s_std = adv7842_s_std, .s_routing = adv7842_s_routing, .querystd = adv7842_querystd, .g_input_status = adv7842_g_input_status, diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index 369cf6ff88f7..76b334a6a56d 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -387,10 +387,10 @@ static const struct v4l2_subdev_core_ops bt819_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = bt819_s_std, }; static const struct v4l2_subdev_video_ops bt819_video_ops = { + .s_std = bt819_s_std, .s_routing = bt819_s_routing, .s_stream = bt819_s_stream, .querystd = bt819_querystd, diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 2e3771d57354..e453a3ffe7d1 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5041,8 +5041,6 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = { .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = cx25840_s_std, - .g_std = cx25840_g_std, .reset = cx25840_reset, .load_fw = cx25840_load_fw, .s_io_pin_config = common_s_io_pin_config, @@ -5067,6 +5065,8 @@ static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { }; static const struct v4l2_subdev_video_ops cx25840_video_ops = { + .s_std = cx25840_s_std, + .g_std = cx25840_g_std, .s_routing = cx25840_s_video_routing, .s_mbus_fmt = cx25840_s_mbus_fmt, .s_stream = cx25840_s_stream, diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index c3e94ae82c03..25b81bc58c81 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -648,11 +648,8 @@ static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) /* ----------------------------------------------------------------------- */ -static const struct v4l2_subdev_core_ops ks0127_core_ops = { - .s_std = ks0127_s_std, -}; - static const struct v4l2_subdev_video_ops ks0127_video_ops = { + .s_std = ks0127_s_std, .s_routing = ks0127_s_routing, .s_stream = ks0127_s_stream, .querystd = ks0127_querystd, @@ -660,7 +657,6 @@ static const struct v4l2_subdev_video_ops ks0127_video_ops = { }; static const struct v4l2_subdev_ops ks0127_ops = { - .core = &ks0127_core_ops, .video = &ks0127_video_ops, }; diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index a9110d8bbbcd..2cace7313a22 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -276,6 +276,7 @@ static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = { }; static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = { + .s_std = ml86v7667_s_std, .querystd = ml86v7667_querystd, .g_input_status = ml86v7667_g_input_status, .enum_mbus_fmt = ml86v7667_enum_mbus_fmt, @@ -286,7 +287,6 @@ static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = { }; static struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = { - .s_std = ml86v7667_s_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ml86v7667_g_register, .s_register = ml86v7667_s_register, diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 8190fec68080..4d9c6bc34265 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -649,10 +649,10 @@ static const struct v4l2_subdev_core_ops msp_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = msp_s_std, }; static const struct v4l2_subdev_video_ops msp_video_ops = { + .s_std = msp_s_std, .querystd = msp_querystd, }; diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 8272c0b9c5bf..04e9e55018a5 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -643,10 +643,10 @@ static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = { static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { .init = saa6752hs_init, - .s_std = saa6752hs_s_std, }; static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { + .s_std = saa6752hs_s_std, .s_mbus_fmt = saa6752hs_s_mbus_fmt, .try_mbus_fmt = saa6752hs_try_mbus_fmt, .g_mbus_fmt = saa6752hs_g_mbus_fmt, diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index ac43e929a1d6..99689ee57d7e 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -365,10 +365,10 @@ static const struct v4l2_subdev_core_ops saa7110_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = saa7110_s_std, }; static const struct v4l2_subdev_video_ops saa7110_video_ops = { + .s_std = saa7110_s_std, .s_routing = saa7110_s_routing, .s_stream = saa7110_s_stream, .querystd = saa7110_querystd, diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index afdbcb045cee..35a44648150e 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1582,7 +1582,6 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = saa711x_s_std, .reset = saa711x_reset, .s_gpio = saa711x_s_gpio, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1601,6 +1600,7 @@ static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { }; static const struct v4l2_subdev_video_ops saa711x_video_ops = { + .s_std = saa711x_s_std, .s_routing = saa711x_s_routing, .s_crystal_freq = saa711x_s_crystal_freq, .s_mbus_fmt = saa711x_s_mbus_fmt, diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 401ca114ab99..6922a9f9a5cd 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1198,7 +1198,6 @@ static const struct v4l2_subdev_core_ops saa717x_core_ops = { .g_register = saa717x_g_register, .s_register = saa717x_s_register, #endif - .s_std = saa717x_s_std, .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, @@ -1216,6 +1215,7 @@ static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { }; static const struct v4l2_subdev_video_ops saa717x_video_ops = { + .s_std = saa717x_s_std, .s_routing = saa717x_s_video_routing, .s_mbus_fmt = saa717x_s_mbus_fmt, .s_stream = saa717x_s_stream, diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c index 606a4baf944d..8e9699268a63 100644 --- a/drivers/media/i2c/saa7191.c +++ b/drivers/media/i2c/saa7191.c @@ -573,10 +573,10 @@ static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) static const struct v4l2_subdev_core_ops saa7191_core_ops = { .g_ctrl = saa7191_g_ctrl, .s_ctrl = saa7191_s_ctrl, - .s_std = saa7191_s_std, }; static const struct v4l2_subdev_video_ops saa7191_video_ops = { + .s_std = saa7191_s_std, .s_routing = saa7191_s_routing, .querystd = saa7191_querystd, .g_input_status = saa7191_g_input_status, diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index 02a51ff57e5d..416402eb4f82 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -814,8 +814,6 @@ done: } static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { - .s_std = tw9910_s_std, - .g_std = tw9910_g_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tw9910_g_register, .s_register = tw9910_s_register, @@ -879,6 +877,8 @@ static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) } static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { + .s_std = tw9910_s_std, + .g_std = tw9910_g_std, .s_stream = tw9910_s_stream, .g_mbus_fmt = tw9910_g_fmt, .s_mbus_fmt = tw9910_s_fmt, diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 32d82320b485..1da8004f5a8e 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -327,18 +327,18 @@ static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner /* --------------------------------------------------------------------------*/ -static const struct v4l2_subdev_core_ops sony_btf_mpx_core_ops = { - .s_std = sony_btf_mpx_s_std, -}; - static const struct v4l2_subdev_tuner_ops sony_btf_mpx_tuner_ops = { .s_tuner = sony_btf_mpx_s_tuner, .g_tuner = sony_btf_mpx_g_tuner, }; +static const struct v4l2_subdev_video_ops sony_btf_mpx_video_ops = { + .s_std = sony_btf_mpx_s_std, +}; + static const struct v4l2_subdev_ops sony_btf_mpx_ops = { - .core = &sony_btf_mpx_core_ops, .tuner = &sony_btf_mpx_tuner_ops, + .video = &sony_btf_mpx_video_ops, }; /* --------------------------------------------------------------------------*/ diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index d76c53a8f027..070c152da95a 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1862,7 +1862,6 @@ static const struct v4l2_subdev_core_ops tvaudio_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = tvaudio_s_std, }; static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { @@ -1876,10 +1875,15 @@ static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { .s_routing = tvaudio_s_routing, }; +static const struct v4l2_subdev_video_ops tvaudio_video_ops = { + .s_std = tvaudio_s_std, +}; + static const struct v4l2_subdev_ops tvaudio_ops = { .core = &tvaudio_core_ops, .tuner = &tvaudio_tuner_ops, .audio = &tvaudio_audio_ops, + .video = &tvaudio_video_ops, }; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index ca001178c5bf..b9dabc9f4050 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1010,10 +1010,10 @@ static const struct v4l2_subdev_core_ops tvp514x_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = tvp514x_s_std, }; static const struct v4l2_subdev_video_ops tvp514x_video_ops = { + .s_std = tvp514x_s_std, .s_routing = tvp514x_s_routing, .querystd = tvp514x_querystd, .enum_mbus_fmt = tvp514x_enum_mbus_fmt, diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 07dee4439c2f..a9121254e37a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1063,7 +1063,6 @@ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .log_status = tvp5150_log_status, - .s_std = tvp5150_s_std, .reset = tvp5150_reset, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tvp5150_g_register, @@ -1076,6 +1075,7 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { }; static const struct v4l2_subdev_video_ops tvp5150_video_ops = { + .s_std = tvp5150_s_std, .s_routing = tvp5150_s_routing, .enum_mbus_fmt = tvp5150_enum_mbus_fmt, .s_mbus_fmt = tvp5150_mbus_fmt, diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index f58607df6193..7347480c0b0c 100644 --- a/drivers/media/i2c/tw2804.c +++ b/drivers/media/i2c/tw2804.c @@ -342,12 +342,12 @@ static const struct v4l2_ctrl_ops tw2804_ctrl_ops = { }; static const struct v4l2_subdev_video_ops tw2804_video_ops = { + .s_std = tw2804_s_std, .s_routing = tw2804_s_video_routing, }; static const struct v4l2_subdev_core_ops tw2804_core_ops = { .log_status = tw2804_log_status, - .s_std = tw2804_s_std, }; static const struct v4l2_subdev_ops tw2804_ops = { diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index 285b759a5f7f..12c7d211a4a4 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -187,10 +187,10 @@ static const struct v4l2_ctrl_ops tw9903_ctrl_ops = { static const struct v4l2_subdev_core_ops tw9903_core_ops = { .log_status = tw9903_log_status, - .s_std = tw9903_s_std, }; static const struct v4l2_subdev_video_ops tw9903_video_ops = { + .s_std = tw9903_s_std, .s_routing = tw9903_s_video_routing, }; diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index f6bef25bd9ce..2672d89265ff 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -157,10 +157,10 @@ static const struct v4l2_ctrl_ops tw9906_ctrl_ops = { static const struct v4l2_subdev_core_ops tw9906_core_ops = { .log_status = tw9906_log_status, - .s_std = tw9906_s_std, }; static const struct v4l2_subdev_video_ops tw9906_video_ops = { + .s_std = tw9906_s_std, .s_routing = tw9906_s_video_routing, }; diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index 6a3a3ff7ee6a..819ab6d12989 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -124,7 +124,6 @@ static int vp27smpx_log_status(struct v4l2_subdev *sd) static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { .log_status = vp27smpx_log_status, - .s_std = vp27smpx_s_std, }; static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { @@ -133,9 +132,14 @@ static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { .g_tuner = vp27smpx_g_tuner, }; +static const struct v4l2_subdev_video_ops vp27smpx_video_ops = { + .s_std = vp27smpx_s_std, +}; + static const struct v4l2_subdev_ops vp27smpx_ops = { .core = &vp27smpx_core_ops, .tuner = &vp27smpx_tuner_ops, + .video = &vp27smpx_video_ops, }; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index ece90df6a043..016e766e72ba 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -457,10 +457,10 @@ static const struct v4l2_subdev_core_ops vpx3220_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = vpx3220_s_std, }; static const struct v4l2_subdev_video_ops vpx3220_video_ops = { + .s_std = vpx3220_s_std, .s_routing = vpx3220_s_routing, .s_stream = vpx3220_s_stream, .querystd = vpx3220_querystd, diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index afcd53bfcf8e..da780f42b121 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -1182,7 +1182,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm) break; } id = tvnorm->v4l2_id; - bttv_call_all(btv, core, s_std, id); + bttv_call_all(btv, video, s_std, id); return 0; } diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c index c4890a430dc6..2d3afe0431a9 100644 --- a/drivers/media/pci/cx18/cx18-av-core.c +++ b/drivers/media/pci/cx18/cx18-av-core.c @@ -1263,7 +1263,6 @@ static const struct v4l2_subdev_core_ops cx18_av_general_ops = { .log_status = cx18_av_log_status, .load_fw = cx18_av_load_fw, .reset = cx18_av_reset, - .s_std = cx18_av_s_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cx18_av_g_register, .s_register = cx18_av_s_register, @@ -1283,6 +1282,7 @@ static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { }; static const struct v4l2_subdev_video_ops cx18_av_video_ops = { + .s_std = cx18_av_s_std, .s_routing = cx18_av_s_video_routing, .s_stream = cx18_av_s_stream, .s_mbus_fmt = cx18_av_s_mbus_fmt, diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c index 4bfd865a4106..76a3b4ac541e 100644 --- a/drivers/media/pci/cx18/cx18-fileops.c +++ b/drivers/media/pci/cx18/cx18-fileops.c @@ -760,7 +760,7 @@ int cx18_v4l2_close(struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); /* Switch tuner to TV */ - cx18_call_all(cx, core, s_std, cx->std); + cx18_call_all(cx, video, s_std, cx->std); /* Select correct audio input (i.e. TV tuner or Line in) */ cx18_audio_set_io(cx); if (atomic_read(&cx->ana_capturing) > 0) { diff --git a/drivers/media/pci/cx18/cx18-gpio.c b/drivers/media/pci/cx18/cx18-gpio.c index 5374aeb0cd22..38dc6b8f8254 100644 --- a/drivers/media/pci/cx18/cx18-gpio.c +++ b/drivers/media/pci/cx18/cx18-gpio.c @@ -180,7 +180,6 @@ static int gpiomux_s_audio_routing(struct v4l2_subdev *sd, static const struct v4l2_subdev_core_ops gpiomux_core_ops = { .log_status = gpiomux_log_status, - .s_std = gpiomux_s_std, }; static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = { @@ -191,10 +190,15 @@ static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = { .s_routing = gpiomux_s_audio_routing, }; +static const struct v4l2_subdev_video_ops gpiomux_video_ops = { + .s_std = gpiomux_s_std, +}; + static const struct v4l2_subdev_ops gpiomux_ops = { .core = &gpiomux_core_ops, .tuner = &gpiomux_tuner_ops, .audio = &gpiomux_audio_ops, + .video = &gpiomux_video_ops, }; /* diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index 1110bcb14e2f..fefb2cd35838 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -602,7 +602,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id std) (unsigned long long) cx->std); /* Tuner */ - cx18_call_all(cx, core, s_std, cx->std); + cx18_call_all(cx, video, s_std, cx->std); return 0; } diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 7891f34157d1..e0a59523cf3c 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -326,7 +326,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) dev->tvnorm = norm; - call_all(dev, core, s_std, norm); + call_all(dev, video, s_std, norm); return 0; } @@ -1589,7 +1589,7 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, fe = &dev->ts1.analog_fe; if (fe && fe->ops.tuner_ops.set_analog_params) { - call_all(dev, core, s_std, dev->tvnorm); + call_all(dev, video, s_std, dev->tvnorm); fe->ops.tuner_ops.set_analog_params(fe, ¶ms); } else diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index ad59dc9235ae..e061c88b697e 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -1012,7 +1012,7 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) set_tvaudio(core); // tell i2c chips - call_all(core, core, s_std, norm); + call_all(core, video, s_std, norm); /* The chroma_agc control should be inaccessible if the video format is SECAM */ v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM); diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c index 9caffd8aa995..e5ff6277ca85 100644 --- a/drivers/media/pci/ivtv/ivtv-fileops.c +++ b/drivers/media/pci/ivtv/ivtv-fileops.c @@ -894,7 +894,7 @@ int ivtv_v4l2_close(struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); /* Switch tuner to TV */ - ivtv_call_all(itv, core, s_std, itv->std); + ivtv_call_all(itv, video, s_std, itv->std); /* Select correct audio input (i.e. TV tuner or Line in) */ ivtv_audio_set_io(itv); if (itv->hw_flags & IVTV_HW_SAA711X) { diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 807b275a847e..b3667a00db3a 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -1090,7 +1090,7 @@ void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id std) itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284; /* Tuner */ - ivtv_call_all(itv, core, s_std, itv->std); + ivtv_call_all(itv, video, s_std, itv->std); } void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id std) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index f1452da0a76d..d37599980768 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -477,10 +477,10 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) saa7134_set_decoder(dev); if (card_in(dev, dev->ctl_input).tv) - saa_call_all(dev, core, s_std, dev->tvnorm->id); + saa_call_all(dev, video, s_std, dev->tvnorm->id); /* Set the correct norm for the saa6752hs. This function does nothing if there is no saa6752hs. */ - saa_call_empress(dev, core, s_std, dev->tvnorm->id); + saa_call_empress(dev, video, s_std, dev->tvnorm->id); } static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index 33abe332d175..c4c8fce8f2b4 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -357,7 +357,7 @@ static int mxb_init_done(struct saa7146_dev* dev) tea6420_route(mxb, 6); /* select video mode in saa7111a */ - saa7111a_call(mxb, core, s_std, std); + saa7111a_call(mxb, video, s_std, std); /* select tuner-output on saa7111a */ i = 0; @@ -379,8 +379,8 @@ static int mxb_init_done(struct saa7146_dev* dev) /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); saa7111a_call(mxb, core, s_gpio, 1); - saa7111a_call(mxb, core, s_std, std); - tuner_call(mxb, core, s_std, std); + saa7111a_call(mxb, video, s_std, std); + tuner_call(mxb, video, s_std, std); /* switch to tuner-channel on tea6415c */ tea6415c_call(mxb, video, s_routing, 3, 17, 0); @@ -771,9 +771,9 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); saa7111a_call(mxb, core, s_gpio, 0); - saa7111a_call(mxb, core, s_std, std); + saa7111a_call(mxb, video, s_std, std); if (mxb->cur_input == 0) - tuner_call(mxb, core, s_std, std); + tuner_call(mxb, video, s_std, std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; @@ -783,9 +783,9 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); saa7111a_call(mxb, core, s_gpio, 1); - saa7111a_call(mxb, core, s_std, std); + saa7111a_call(mxb, video, s_std, std); if (mxb->cur_input == 0) - tuner_call(mxb, core, s_std, std); + tuner_call(mxb, video, s_std, std); } return 0; } diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 7559951b2ea4..d2abd3b5c2bf 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -444,7 +444,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) int status; if (V4L2_STD_ALL == std) { - v4l2_subdev_call(vip->decoder, core, s_std, std); + v4l2_subdev_call(vip->decoder, video, s_std, std); ssleep(2); v4l2_subdev_call(vip->decoder, video, querystd, &newstd); v4l2_subdev_call(vip->decoder, video, g_input_status, &status); @@ -467,7 +467,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) vip->format = formats_50[0]; } - return v4l2_subdev_call(vip->decoder, core, s_std, std); + return v4l2_subdev_call(vip->decoder, video, s_std, std); } /** diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index 519164c572c8..bf34b93f23ee 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -1572,7 +1572,7 @@ zoran_init_hardware (struct zoran *zr) } decoder_call(zr, core, init, 0); - decoder_call(zr, core, s_std, zr->norm); + decoder_call(zr, video, s_std, zr->norm); decoder_call(zr, video, s_routing, zr->card.input[zr->input].muxsel, 0, 0); diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index e7e9840c6c35..099d5fbebb7c 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1469,7 +1469,7 @@ zoran_set_norm (struct zoran *zr, if (on) zr36057_overlay(zr, 0); - decoder_call(zr, core, s_std, norm); + decoder_call(zr, video, s_std, norm); encoder_call(zr, video, s_std_output, norm); if (on) diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 16f643c4aad6..cf3d94f733ff 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -631,7 +631,7 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std) if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; - ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, std); + ret = v4l2_subdev_call(bcap_dev->sd, video, s_std, std); if (ret < 0) return ret; @@ -1065,7 +1065,7 @@ static int bcap_probe(struct platform_device *pdev) /* now we can probe the default state */ if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) { v4l2_std_id std; - ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); + ret = v4l2_subdev_call(bcap_dev->sd, video, g_std, &std); if (ret) { v4l2_err(&bcap_dev->v4l2_dev, "Unable to get std\n"); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index ac6c8c6ac7d0..a51bda2fb637 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1217,7 +1217,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) } ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, s_std, std_id); + video, s_std, std_id); if (ret < 0) { v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); goto unlock_out; diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 0198a5a16ed4..f3d9d9cde98c 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -951,7 +951,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) vpif_config_format(ch); /* set standard in the sub device */ - ret = v4l2_subdev_call(ch->sd, core, s_std, std_id); + ret = v4l2_subdev_call(ch->sd, video, s_std, std_id); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); return ret; diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index d6110459a203..6513f5188f65 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -718,7 +718,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) return ret; } - ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, s_std, std_id); if (ret < 0) vpif_err("Failed to set standard for sub devices\n"); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index dbf0ce38a8e7..d5dc198502ef 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -964,7 +964,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) struct viu_fh *fh = priv; fh->dev->std = id; - decoder_call(fh->dev, core, s_std, id); + decoder_call(fh->dev, video, s_std, id); return 0; } diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index c8549bf3213b..7fec8cdaf095 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -314,7 +314,7 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, core, s_std, a); + return v4l2_subdev_call(sd, video, s_std, a); } static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) @@ -322,7 +322,7 @@ static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, core, g_std, a); + return v4l2_subdev_call(sd, video, g_std, a); } static int soc_camera_enum_framesizes(struct file *file, void *fh, diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index fbfdadaa0803..3cb2f3564873 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -347,7 +347,7 @@ static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std) mutex_lock(&lw->lock); if (TIMBLOGIW_HAS_DECODER(lw)) - err = v4l2_subdev_call(lw->sd_enc, core, s_std, std); + err = v4l2_subdev_call(lw->sd_enc, video, s_std, std); if (!err) fh->cur_norm = timblogiw_get_norm(std); diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index c6af974c5b45..470d35336119 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -2586,7 +2586,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) } if (data_norm == 3) data_norm = VINO_DATA_NORM_PAL; - ret = decoder_call(core, s_std, norm); + ret = decoder_call(video, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2675,7 +2675,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) } if (data_norm == 3) data_norm = VINO_DATA_NORM_PAL; - ret = decoder_call(core, s_std, norm); + ret = decoder_call(video, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2809,7 +2809,7 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs, * as it may take a while... */ norm = vino_data_norms[data_norm].std; - err = decoder_call(core, s_std, norm); + err = decoder_call(video, s_std, norm); spin_lock_irqsave(&vino_drvdata->input_lock, *flags); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index f6154546b5c0..9038194513c5 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1109,7 +1109,7 @@ static void au0828_init_tuner(struct au0828_dev *dev) /* If we've never sent the standard in tuner core, do so now. We don't do this at device probe because we don't want to incur the cost of a firmware load */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->std); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->std); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); i2c_gate_ctrl(dev, 0); } @@ -1368,7 +1368,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) have to make the au0828 bridge adjust the size of its capture buffer, which is currently hardcoded at 720x480 */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, norm); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, norm); i2c_gate_ctrl(dev, 0); diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 2f63029e7a36..30a0c69fb42f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1516,7 +1516,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) dev->ts1.height = 576; cx2341x_handler_set_50hz(&dev->mpeg_ctrl_handler, true); } - call_all(dev, core, s_std, dev->norm); + call_all(dev, video, s_std, dev->norm); /* do mode control overrides */ cx231xx_do_mode_ctrl_overrides(dev); diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 990626101718..1f8751379e24 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1009,7 +1009,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) dev->width = 720; dev->height = (dev->norm & V4L2_STD_625_50) ? 576 : 480; - call_all(dev, core, s_std, dev->norm); + call_all(dev, video, s_std, dev->norm); /* We need to reset basic properties in the decoder related to resolution (since a standard change effects things like the number @@ -1108,7 +1108,7 @@ int cx231xx_s_input(struct file *file, void *priv, unsigned int i) /* There's a tuner, so reset the standard and put it on the last known frequency (since it was probably powered down until now */ - call_all(dev, core, s_std, dev->norm); + call_all(dev, video, s_std, dev->norm); } return 0; @@ -2099,7 +2099,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) /* Set the initial input */ video_mux(dev, dev->video_input); - call_all(dev, core, s_std, dev->norm); + call_all(dev, video, s_std, dev->norm); v4l2_ctrl_handler_init(&dev->ctrl_handler, 10); v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 5); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 0f8501f39fa7..f6b49c98e2c9 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1400,7 +1400,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) &v4l2->hscale, &v4l2->vscale); em28xx_resolution_set(dev); - v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_std, v4l2->norm); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); return 0; } @@ -2440,7 +2440,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) /* set default norm */ v4l2->norm = V4L2_STD_PAL; - v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_std, v4l2->norm); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT; /* Analog specific initialization */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index ea05f678b559..9623b6218214 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -2910,7 +2910,7 @@ static void pvr2_subdev_update(struct pvr2_hdw *hdw) v4l2_std_id vs; vs = hdw->std_mask_cur; v4l2_device_call_all(&hdw->v4l2_dev, 0, - core, s_std, vs); + video, s_std, vs); pvr2_hdw_cx25840_vbi_hack(hdw); } hdw->tuner_signal_stale = !0; diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 46e8a5069b37..5461341a31cb 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -406,7 +406,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) stk1160_set_std(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); return 0; @@ -682,7 +682,7 @@ int stk1160_video_register(struct stk1160 *dev) dev->fmt = &format[0]; stk1160_set_std(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); video_set_drvdata(&dev->vdev, dev); diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c index 1ccaaddaa307..2e8c3afe4ec4 100644 --- a/drivers/media/usb/tm6000/tm6000-cards.c +++ b/drivers/media/usb/tm6000/tm6000-cards.c @@ -1120,7 +1120,7 @@ static int tm6000_init_dev(struct tm6000_core *dev) tm6000_config_tuner(dev); /* Set video standard */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ f.tuner = 0; diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index cc1aa14996ff..e6b3d5d83d43 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1071,7 +1071,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) if (rc < 0) return rc; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); return 0; } diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 5c9e3123ad2e..68bc9615660e 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -597,7 +597,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) usbvision->tvnorm_id = id; - call_all(usbvision, core, s_std, usbvision->tvnorm_id); + call_all(usbvision, video, s_std, usbvision->tvnorm_id); /* propagate the change to the decoder */ usbvision_muxsel(usbvision, usbvision->ctl_input); diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 20c09229a08e..06c18ba16fa0 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1301,7 +1301,6 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, - .s_std = tuner_s_std, .s_power = tuner_s_power, }; @@ -1315,9 +1314,14 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { .s_config = tuner_s_config, }; +static const struct v4l2_subdev_video_ops tuner_video_ops = { + .s_std = tuner_s_std, +}; + static const struct v4l2_subdev_ops tuner_ops = { .core = &tuner_core_ops, .tuner = &tuner_tuner_ops, + .video = &tuner_video_ops, }; /* diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index 7b213a7f9623..425e2c849723 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -944,7 +944,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) goto unlock_out; } ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, s_std, std_id); + video, s_std, std_id); if (ret < 0) { v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); video->stdid = V4L2_STD_UNKNOWN; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 090b3e6e852a..da7b5493e13e 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -665,7 +665,7 @@ static int go7007_s_std(struct go7007 *go) go->sensor_framerate = 30000; } - call_all(&go->v4l2_dev, core, s_std, go->std); + call_all(&go->v4l2_dev, video, s_std, go->std); set_capture_size(go, NULL, 0); return 0; } diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index 696a80756691..eaa2b0990a1b 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -474,7 +474,6 @@ static const struct v4l2_ctrl_ops s2250_ctrl_ops = { static const struct v4l2_subdev_core_ops s2250_core_ops = { .log_status = s2250_log_status, - .s_std = s2250_s_std, }; static const struct v4l2_subdev_audio_ops s2250_audio_ops = { @@ -482,6 +481,7 @@ static const struct v4l2_subdev_audio_ops s2250_audio_ops = { }; static const struct v4l2_subdev_video_ops s2250_video_ops = { + .s_std = s2250_s_std, .s_routing = s2250_s_video_routing, .s_mbus_fmt = s2250_s_mbus_fmt, }; diff --git a/drivers/staging/media/go7007/saa7134-go7007.c b/drivers/staging/media/go7007/saa7134-go7007.c index 6e2ca338cdd9..e40f7fbfc0a5 100644 --- a/drivers/staging/media/go7007/saa7134-go7007.c +++ b/drivers/staging/media/go7007/saa7134-go7007.c @@ -434,11 +434,15 @@ static const struct v4l2_subdev_core_ops saa7134_go7007_core_ops = { .g_ctrl = saa7134_go7007_g_ctrl, .s_ctrl = saa7134_go7007_s_ctrl, .queryctrl = saa7134_go7007_queryctrl, +}; + +static const struct v4l2_subdev_video_ops saa7134_go7007_video_ops = { .s_std = saa7134_go7007_s_std, }; static const struct v4l2_subdev_ops saa7134_go7007_sd_ops = { .core = &saa7134_go7007_core_ops, + .video = &saa7134_go7007_video_ops, }; /* --------------------------------------------------------------------------*/ diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 9205df13f3a5..dd5bbbe2d56a 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -159,8 +159,6 @@ struct v4l2_subdev_core_ops { int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls); int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls); int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); - int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm); - int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm); long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg); #ifdef CONFIG_COMPAT long (*compat_ioctl32)(struct v4l2_subdev *sd, unsigned int cmd, @@ -316,6 +314,8 @@ struct v4l2_mbus_frame_desc { struct v4l2_subdev_video_ops { int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config); int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags); + int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm); + int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm); int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std); @@ -693,7 +693,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, /* Call an ops of a v4l2_subdev, doing the right checks against NULL pointers. - Example: err = v4l2_subdev_call(sd, core, s_std, norm); + Example: err = v4l2_subdev_call(sd, video, s_std, norm); */ #define v4l2_subdev_call(sd, o, f, args...) \ (!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \ -- cgit v1.2.3 From 34ea4d4417bb726245fdaeb2f8951eaa0c18fc4c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 9 Mar 2014 21:42:52 -0300 Subject: [media] v4l: vb2: Add a function to discard all DONE buffers When suspending a device while a video stream is active all buffers marked as done but not dequeued yet will be kept across suspend and given back to userspace after resume. This will result in outdated buffers being dequeued. Introduce a new vb2 function to mark all done buffers as erroneous instead, to be used by drivers at resume time. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 24 ++++++++++++++++++++++++ include/media/videobuf2-core.h | 1 + 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 349e659d75fb..7c4489c42365 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1199,6 +1199,30 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) } EXPORT_SYMBOL_GPL(vb2_buffer_done); +/** + * vb2_discard_done() - discard all buffers marked as DONE + * @q: videobuf2 queue + * + * This function is intended to be used with suspend/resume operations. It + * discards all 'done' buffers as they would be too old to be requested after + * resume. + * + * Drivers must stop the hardware and synchronize with interrupt handlers and/or + * delayed works before calling this function to make sure no buffer will be + * touched by the driver and/or hardware. + */ +void vb2_discard_done(struct vb2_queue *q) +{ + struct vb2_buffer *vb; + unsigned long flags; + + spin_lock_irqsave(&q->done_lock, flags); + list_for_each_entry(vb, &q->done_list, done_entry) + vb->state = VB2_BUF_STATE_ERROR; + spin_unlock_irqrestore(&q->done_lock, flags); +} +EXPORT_SYMBOL_GPL(vb2_discard_done); + /** * __fill_vb2_buffer() - fill a vb2_buffer with information provided in a * v4l2_buffer by the userspace. The caller has already verified that struct diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index bca25dc53f9d..8fab6fa0dbfb 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -432,6 +432,7 @@ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no); void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no); void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state); +void vb2_discard_done(struct vb2_queue *q); int vb2_wait_for_all_buffers(struct vb2_queue *q); int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b); -- cgit v1.2.3 From 0349f6a5f18e929ca88c6db7def5e02f5ae0a416 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 29 Jan 2014 10:05:10 -0300 Subject: [media] v4l: Add pad-level DV timings subdev operations The dv_timings_cap and enum_dv_timings subdev operations are implemented at the device level, but apply to pads. Create new variants of those operations at the pad level. The device level variants will be removed once all drivers are ported to the pad level DT timings operations. Signed-off-by: Laurent Pinchart Acked-by: Lad, Prabhakar Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 4 ++++ include/uapi/linux/videodev2.h | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index dd5bbbe2d56a..9fab013eea86 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -517,6 +517,10 @@ struct v4l2_subdev_pad_ops { struct v4l2_subdev_selection *sel); int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid); int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid); + int (*dv_timings_cap)(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap); + int (*enum_dv_timings)(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings); #ifdef CONFIG_MEDIA_CONTROLLER int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link, struct v4l2_subdev_format *source_fmt, diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 00259d2baa10..168ff507bf75 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1106,12 +1106,15 @@ struct v4l2_dv_timings { /** struct v4l2_enum_dv_timings - DV timings enumeration * @index: enumeration index + * @pad: the pad number for which to enumerate timings (used with + * v4l-subdev nodes only) * @reserved: must be zeroed * @timings: the timings for the given index */ struct v4l2_enum_dv_timings { __u32 index; - __u32 reserved[3]; + __u32 pad; + __u32 reserved[2]; struct v4l2_dv_timings timings; }; @@ -1149,11 +1152,14 @@ struct v4l2_bt_timings_cap { /** struct v4l2_dv_timings_cap - DV timings capabilities * @type: the type of the timings (same as in struct v4l2_dv_timings) + * @pad: the pad number for which to query capabilities (used with + * v4l-subdev nodes only) * @bt: the BT656/1120 timings capabilities */ struct v4l2_dv_timings_cap { __u32 type; - __u32 reserved[3]; + __u32 pad; + __u32 reserved[2]; union { struct v4l2_bt_timings_cap bt; __u32 raw_data[32]; -- cgit v1.2.3 From c4fa146ce53cc5741016fdfadfce84f5c0bcf999 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 29 Jan 2014 10:06:13 -0300 Subject: [media] v4l: Improve readability by not wrapping ioctl number #define's Wrapping the #define's at a 80 columns boundary just obfuscates the code. Don't do that. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-subdev.h | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index 87e05159f637..db28964a697c 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -151,26 +151,19 @@ struct v4l2_subdev_selection { /* Backwards compatibility define --- to be removed */ #define v4l2_subdev_edid v4l2_edid -#define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) -#define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) -#define VIDIOC_SUBDEV_G_FRAME_INTERVAL \ - _IOWR('V', 21, struct v4l2_subdev_frame_interval) -#define VIDIOC_SUBDEV_S_FRAME_INTERVAL \ - _IOWR('V', 22, struct v4l2_subdev_frame_interval) -#define VIDIOC_SUBDEV_ENUM_MBUS_CODE \ - _IOWR('V', 2, struct v4l2_subdev_mbus_code_enum) -#define VIDIOC_SUBDEV_ENUM_FRAME_SIZE \ - _IOWR('V', 74, struct v4l2_subdev_frame_size_enum) -#define VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \ - _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) -#define VIDIOC_SUBDEV_G_CROP _IOWR('V', 59, struct v4l2_subdev_crop) -#define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) -#define VIDIOC_SUBDEV_G_SELECTION \ - _IOWR('V', 61, struct v4l2_subdev_selection) -#define VIDIOC_SUBDEV_S_SELECTION \ - _IOWR('V', 62, struct v4l2_subdev_selection) +#define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) +#define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) +#define VIDIOC_SUBDEV_G_FRAME_INTERVAL _IOWR('V', 21, struct v4l2_subdev_frame_interval) +#define VIDIOC_SUBDEV_S_FRAME_INTERVAL _IOWR('V', 22, struct v4l2_subdev_frame_interval) +#define VIDIOC_SUBDEV_ENUM_MBUS_CODE _IOWR('V', 2, struct v4l2_subdev_mbus_code_enum) +#define VIDIOC_SUBDEV_ENUM_FRAME_SIZE _IOWR('V', 74, struct v4l2_subdev_frame_size_enum) +#define VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) +#define VIDIOC_SUBDEV_G_CROP _IOWR('V', 59, struct v4l2_subdev_crop) +#define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) +#define VIDIOC_SUBDEV_G_SELECTION _IOWR('V', 61, struct v4l2_subdev_selection) +#define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection) /* These two G/S_EDID ioctls are identical to the ioctls in videodev2.h */ -#define VIDIOC_SUBDEV_G_EDID _IOWR('V', 40, struct v4l2_edid) -#define VIDIOC_SUBDEV_S_EDID _IOWR('V', 41, struct v4l2_edid) +#define VIDIOC_SUBDEV_G_EDID _IOWR('V', 40, struct v4l2_edid) +#define VIDIOC_SUBDEV_S_EDID _IOWR('V', 41, struct v4l2_edid) #endif -- cgit v1.2.3 From 9cfd65e80959836fed78704e8a127d4e10448d56 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 29 Jan 2014 10:07:13 -0300 Subject: [media] v4l: Add support for DV timings ioctls on subdev nodes Validate the pad field in the core code whenever specified. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/vidioc-dv-timings-cap.xml | 27 +++++++++++++++---- .../DocBook/media/v4l/vidioc-enum-dv-timings.xml | 30 +++++++++++++++++----- drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++++++++++++ include/uapi/linux/v4l2-subdev.h | 7 ++++- 4 files changed, 78 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml index cd7720d404ea..28a8c1e1c705 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml @@ -1,11 +1,12 @@ - ioctl VIDIOC_DV_TIMINGS_CAP + ioctl VIDIOC_DV_TIMINGS_CAP, VIDIOC_SUBDEV_DV_TIMINGS_CAP &manvol; VIDIOC_DV_TIMINGS_CAP + VIDIOC_SUBDEV_DV_TIMINGS_CAP The capabilities of the Digital Video receiver/transmitter @@ -33,7 +34,7 @@ request - VIDIOC_DV_TIMINGS_CAP + VIDIOC_DV_TIMINGS_CAP, VIDIOC_SUBDEV_DV_TIMINGS_CAP @@ -54,10 +55,19 @@ interface and may change in the future. - To query the capabilities of the DV receiver/transmitter applications can call -this ioctl and the driver will fill in the structure. Note that drivers may return + To query the capabilities of the DV receiver/transmitter applications +can call the VIDIOC_DV_TIMINGS_CAP ioctl on a video node +and the driver will fill in the structure. Note that drivers may return different values after switching the video input or output. + When implemented by the driver DV capabilities of subdevices can be +queried by calling the VIDIOC_SUBDEV_DV_TIMINGS_CAP ioctl +directly on a subdevice node. The capabilities are specific to inputs (for DV +receivers) or outputs (for DV transmitters), applications must specify the +desired pad number in the &v4l2-dv-timings-cap; pad +field. Attempts to query capabilities on a pad that doesn't support them will +return an &EINVAL;. + struct <structname>v4l2_bt_timings_cap</structname> @@ -127,7 +137,14 @@ different values after switching the video input or output. __u32 - reserved[3] + pad + Pad number as reported by the media controller API. This field + is only used when operating on a subdevice node. When operating on a + video node applications must set this field to zero. + + + __u32 + reserved[2] Reserved for future extensions. Drivers must set the array to zero. diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml index b3e17c1dfaf5..b9fdfeacdbcb 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml @@ -1,11 +1,12 @@ - ioctl VIDIOC_ENUM_DV_TIMINGS + ioctl VIDIOC_ENUM_DV_TIMINGS, VIDIOC_SUBDEV_ENUM_DV_TIMINGS &manvol; VIDIOC_ENUM_DV_TIMINGS + VIDIOC_SUBDEV_ENUM_DV_TIMINGS Enumerate supported Digital Video timings @@ -33,7 +34,7 @@ request - VIDIOC_ENUM_DV_TIMINGS + VIDIOC_ENUM_DV_TIMINGS, VIDIOC_SUBDEV_ENUM_DV_TIMINGS @@ -61,14 +62,21 @@ standards or even custom timings that are not in this list. To query the available timings, applications initialize the index field and zero the reserved array of &v4l2-enum-dv-timings; -and call the VIDIOC_ENUM_DV_TIMINGS ioctl with a pointer to this -structure. Drivers fill the rest of the structure or return an +and call the VIDIOC_ENUM_DV_TIMINGS ioctl on a video node with a +pointer to this structure. Drivers fill the rest of the structure or return an &EINVAL; when the index is out of bounds. To enumerate all supported DV timings, applications shall begin at index zero, incrementing by one until the driver returns EINVAL. Note that drivers may enumerate a different set of DV timings after switching the video input or output. + When implemented by the driver DV timings of subdevices can be queried +by calling the VIDIOC_SUBDEV_ENUM_DV_TIMINGS ioctl directly +on a subdevice node. The DV timings are specific to inputs (for DV receivers) or +outputs (for DV transmitters), applications must specify the desired pad number +in the &v4l2-enum-dv-timings; pad field. Attempts to +enumerate timings on a pad that doesn't support them will return an &EINVAL;. +
struct <structname>v4l2_enum_dv_timings</structname> @@ -82,8 +90,16 @@ application. __u32 - reserved[3] - Reserved for future extensions. Drivers must set the array to zero. + pad + Pad number as reported by the media controller API. This field + is only used when operating on a subdevice node. When operating on a + video node applications must set this field to zero. + + + __u32 + reserved[2] + Reserved for future extensions. Drivers and applications must + set the array to zero. &v4l2-dv-timings; @@ -103,7 +119,7 @@ application. EINVAL The &v4l2-enum-dv-timings; index -is out of bounds. +is out of bounds or the pad number is invalid. diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 0ed4c5be1b32..db126dbc19c9 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -366,6 +366,33 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_EDID: return v4l2_subdev_call(sd, pad, set_edid, arg); + + case VIDIOC_SUBDEV_DV_TIMINGS_CAP: { + struct v4l2_dv_timings_cap *cap = arg; + + if (cap->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, dv_timings_cap, cap); + } + + case VIDIOC_SUBDEV_ENUM_DV_TIMINGS: { + struct v4l2_enum_dv_timings *dvt = arg; + + if (dvt->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, enum_dv_timings, dvt); + } + + case VIDIOC_SUBDEV_QUERY_DV_TIMINGS: + return v4l2_subdev_call(sd, video, query_dv_timings, arg); + + case VIDIOC_SUBDEV_G_DV_TIMINGS: + return v4l2_subdev_call(sd, video, g_dv_timings, arg); + + case VIDIOC_SUBDEV_S_DV_TIMINGS: + return v4l2_subdev_call(sd, video, s_dv_timings, arg); #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index db28964a697c..a619cdd300ac 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -162,8 +162,13 @@ struct v4l2_subdev_selection { #define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) #define VIDIOC_SUBDEV_G_SELECTION _IOWR('V', 61, struct v4l2_subdev_selection) #define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection) -/* These two G/S_EDID ioctls are identical to the ioctls in videodev2.h */ +/* The following ioctls are identical to the ioctls in videodev2.h */ #define VIDIOC_SUBDEV_G_EDID _IOWR('V', 40, struct v4l2_edid) #define VIDIOC_SUBDEV_S_EDID _IOWR('V', 41, struct v4l2_edid) +#define VIDIOC_SUBDEV_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings) +#define VIDIOC_SUBDEV_G_DV_TIMINGS _IOWR('V', 88, struct v4l2_dv_timings) +#define VIDIOC_SUBDEV_ENUM_DV_TIMINGS _IOWR('V', 98, struct v4l2_enum_dv_timings) +#define VIDIOC_SUBDEV_QUERY_DV_TIMINGS _IOR('V', 99, struct v4l2_dv_timings) +#define VIDIOC_SUBDEV_DV_TIMINGS_CAP _IOWR('V', 100, struct v4l2_dv_timings_cap) #endif -- cgit v1.2.3 From e5e749dfa8606343fd7956868038bdde2e656ec1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 21 Nov 2013 11:23:45 -0300 Subject: [media] adv7604: Add missing include to linux/types.h The file is using u8 which is defined in linux/types.h. Signed-off-by: Lars-Peter Clausen Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/adv7604.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/media/adv7604.h b/include/media/adv7604.h index d262a3a922bd..c6b39372eed7 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -21,6 +21,8 @@ #ifndef _ADV7604_ #define _ADV7604_ +#include + /* Analog input muxing modes (AFE register 0x02, [2:0]) */ enum adv7604_ain_sel { ADV7604_AIN1_2_3_NC_SYNC_1_2 = 0, -- cgit v1.2.3 From d42010a1d23979db434826e6b9449ad777bee02f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 25 Nov 2013 15:45:07 -0300 Subject: [media] adv7604: Add adv7611 support This patch adds support for the Analog Devices ADV7611 HDMI receiver. The adv7611 is quite similar to the adv7604. It has only one instead of four HDMI inputs and no analog frontend though. Also some register bits have been shuffled around, but large parts of their register maps are compatible. Signed-off-by: Lars-Peter Clausen Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 719 +++++++++++++++++++++++++++++++------------- include/media/adv7604.h | 10 + 2 files changed, 528 insertions(+), 201 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index f9503d2d1ed0..1720daf0c875 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -53,6 +53,43 @@ MODULE_LICENSE("GPL"); /* ADV7604 system clock frequency */ #define ADV7604_fsc (28636360) +enum adv7604_type { + ADV7604, + ADV7611, +}; + +struct adv7604_reg_seq { + unsigned int reg; + u8 val; +}; + +struct adv7604_chip_info { + enum adv7604_type type; + + bool has_afe; + unsigned int max_port; + unsigned int num_dv_ports; + + unsigned int edid_enable_reg; + unsigned int edid_status_reg; + unsigned int lcf_reg; + + unsigned int cable_det_mask; + unsigned int tdms_lock_mask; + unsigned int fmt_change_digital_mask; + + void (*set_termination)(struct v4l2_subdev *sd, bool enable); + void (*setup_irqs)(struct v4l2_subdev *sd); + unsigned int (*read_hdmi_pixelclock)(struct v4l2_subdev *sd); + unsigned int (*read_cable_det)(struct v4l2_subdev *sd); + + /* 0 = AFE, 1 = HDMI */ + const struct adv7604_reg_seq *recommended_settings[2]; + unsigned int num_recommended_settings[2]; + + unsigned long page_mask; +}; + /* ********************************************************************** * @@ -61,6 +98,7 @@ MODULE_LICENSE("GPL"); ********************************************************************** */ struct adv7604_state { + const struct adv7604_chip_info *info; struct adv7604_platform_data pdata; struct v4l2_subdev sd; struct media_pad pad; @@ -101,6 +139,11 @@ struct adv7604_state { struct v4l2_ctrl *rgb_quantization_range_ctrl; }; +static bool adv7604_has_afe(struct adv7604_state *state) +{ + return state->info->has_afe; +} + /* Supported CEA and DMT timings */ static const struct v4l2_dv_timings adv7604_timings[] = { V4L2_DV_BT_CEA_720X480P59_94, @@ -611,6 +654,121 @@ static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) return adv_smbus_write_byte_data(state->i2c_vdp, reg, val); } +enum { + ADV7604_PAGE_IO, + ADV7604_PAGE_AVLINK, + ADV7604_PAGE_CEC, + ADV7604_PAGE_INFOFRAME, + ADV7604_PAGE_ESDP, + ADV7604_PAGE_DPP, + ADV7604_PAGE_AFE, + ADV7604_PAGE_REP, + ADV7604_PAGE_EDID, + ADV7604_PAGE_HDMI, + ADV7604_PAGE_TEST, + ADV7604_PAGE_CP, + ADV7604_PAGE_VDP, + ADV7604_PAGE_TERM, +}; + +#define ADV7604_REG(page, offset) (((page) << 8) | (offset)) +#define ADV7604_REG_SEQ_TERM 0xffff + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int adv7604_read_reg(struct v4l2_subdev *sd, unsigned int reg) +{ + struct adv7604_state *state = to_state(sd); + unsigned int page = reg >> 8; + + if (!(BIT(page) & state->info->page_mask)) + return -EINVAL; + + reg &= 0xff; + + switch (page) { + case ADV7604_PAGE_IO: + return io_read(sd, reg); + case ADV7604_PAGE_AVLINK: + return avlink_read(sd, reg); + case ADV7604_PAGE_CEC: + return cec_read(sd, reg); + case ADV7604_PAGE_INFOFRAME: + return infoframe_read(sd, reg); + case ADV7604_PAGE_ESDP: + return esdp_read(sd, reg); + case ADV7604_PAGE_DPP: + return dpp_read(sd, reg); + case ADV7604_PAGE_AFE: + return afe_read(sd, reg); + case ADV7604_PAGE_REP: + return rep_read(sd, reg); + case ADV7604_PAGE_EDID: + return edid_read(sd, reg); + case ADV7604_PAGE_HDMI: + return hdmi_read(sd, reg); + case ADV7604_PAGE_TEST: + return test_read(sd, reg); + case ADV7604_PAGE_CP: + return cp_read(sd, reg); + case ADV7604_PAGE_VDP: + return vdp_read(sd, reg); + } + + return -EINVAL; +} +#endif + +static int adv7604_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) +{ + struct adv7604_state *state = to_state(sd); + unsigned int page = reg >> 8; + + if (!(BIT(page) & state->info->page_mask)) + return -EINVAL; + + reg &= 0xff; + + switch (page) { + case ADV7604_PAGE_IO: + return io_write(sd, reg, val); + case ADV7604_PAGE_AVLINK: + return avlink_write(sd, reg, val); + case ADV7604_PAGE_CEC: + return cec_write(sd, reg, val); + case ADV7604_PAGE_INFOFRAME: + return infoframe_write(sd, reg, val); + case ADV7604_PAGE_ESDP: + return esdp_write(sd, reg, val); + case ADV7604_PAGE_DPP: + return dpp_write(sd, reg, val); + case ADV7604_PAGE_AFE: + return afe_write(sd, reg, val); + case ADV7604_PAGE_REP: + return rep_write(sd, reg, val); + case ADV7604_PAGE_EDID: + return edid_write(sd, reg, val); + case ADV7604_PAGE_HDMI: + return hdmi_write(sd, reg, val); + case ADV7604_PAGE_TEST: + return test_write(sd, reg, val); + case ADV7604_PAGE_CP: + return cp_write(sd, reg, val); + case ADV7604_PAGE_VDP: + return vdp_write(sd, reg, val); + } + + return -EINVAL; +} + +static void adv7604_write_reg_seq(struct v4l2_subdev *sd, + const struct adv7604_reg_seq *reg_seq) +{ + unsigned int i; + + for (i = 0; reg_seq[i].reg != ADV7604_REG_SEQ_TERM; i++) + adv7604_write_reg(sd, reg_seq[i].reg, reg_seq[i].val); +} + /* ----------------------------------------------------------------------- */ static inline bool is_analog_input(struct v4l2_subdev *sd) @@ -654,119 +812,61 @@ static void adv7604_inv_register(struct v4l2_subdev *sd) static int adv7604_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { - reg->size = 1; - switch (reg->reg >> 8) { - case 0: - reg->val = io_read(sd, reg->reg & 0xff); - break; - case 1: - reg->val = avlink_read(sd, reg->reg & 0xff); - break; - case 2: - reg->val = cec_read(sd, reg->reg & 0xff); - break; - case 3: - reg->val = infoframe_read(sd, reg->reg & 0xff); - break; - case 4: - reg->val = esdp_read(sd, reg->reg & 0xff); - break; - case 5: - reg->val = dpp_read(sd, reg->reg & 0xff); - break; - case 6: - reg->val = afe_read(sd, reg->reg & 0xff); - break; - case 7: - reg->val = rep_read(sd, reg->reg & 0xff); - break; - case 8: - reg->val = edid_read(sd, reg->reg & 0xff); - break; - case 9: - reg->val = hdmi_read(sd, reg->reg & 0xff); - break; - case 0xa: - reg->val = test_read(sd, reg->reg & 0xff); - break; - case 0xb: - reg->val = cp_read(sd, reg->reg & 0xff); - break; - case 0xc: - reg->val = vdp_read(sd, reg->reg & 0xff); - break; - default: + int ret; + + ret = adv7604_read_reg(sd, reg->reg); + if (ret < 0) { v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7604_inv_register(sd); - break; + return ret; } + + reg->size = 1; + reg->val = ret; + return 0; } static int adv7604_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { - u8 val = reg->val & 0xff; + int ret; - switch (reg->reg >> 8) { - case 0: - io_write(sd, reg->reg & 0xff, val); - break; - case 1: - avlink_write(sd, reg->reg & 0xff, val); - break; - case 2: - cec_write(sd, reg->reg & 0xff, val); - break; - case 3: - infoframe_write(sd, reg->reg & 0xff, val); - break; - case 4: - esdp_write(sd, reg->reg & 0xff, val); - break; - case 5: - dpp_write(sd, reg->reg & 0xff, val); - break; - case 6: - afe_write(sd, reg->reg & 0xff, val); - break; - case 7: - rep_write(sd, reg->reg & 0xff, val); - break; - case 8: - edid_write(sd, reg->reg & 0xff, val); - break; - case 9: - hdmi_write(sd, reg->reg & 0xff, val); - break; - case 0xa: - test_write(sd, reg->reg & 0xff, val); - break; - case 0xb: - cp_write(sd, reg->reg & 0xff, val); - break; - case 0xc: - vdp_write(sd, reg->reg & 0xff, val); - break; - default: + ret = adv7604_write_reg(sd, reg->reg, reg->val); + if (ret < 0) { v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7604_inv_register(sd); - break; + return ret; } + return 0; } #endif +static unsigned int adv7604_read_cable_det(struct v4l2_subdev *sd) +{ + u8 value = io_read(sd, 0x6f); + + return ((value & 0x10) >> 4) + | ((value & 0x08) >> 2) + | ((value & 0x04) << 0) + | ((value & 0x02) << 2); +} + +static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd) +{ + u8 value = io_read(sd, 0x6f); + + return value & 1; +} + static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); - u8 reg_io_6f = io_read(sd, 0x6f); + const struct adv7604_chip_info *info = state->info; return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, - ((reg_io_6f & 0x10) >> 4) | - ((reg_io_6f & 0x08) >> 2) | - (reg_io_6f & 0x04) | - ((reg_io_6f & 0x02) << 2)); + info->read_cable_det(sd)); } static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, @@ -797,9 +897,11 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s", __func__); - /* reset to default values */ - io_write(sd, 0x16, 0x43); - io_write(sd, 0x17, 0x5a); + if (adv7604_has_afe(state)) { + /* reset to default values */ + io_write(sd, 0x16, 0x43); + io_write(sd, 0x17, 0x5a); + } /* disable embedded syncs for auto graphics mode */ cp_write_and_or(sd, 0x81, 0xef, 0x00); cp_write(sd, 0x8f, 0x00); @@ -1061,6 +1163,8 @@ static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) set_rgb_quantization_range(sd); return 0; case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE: + if (!adv7604_has_afe(state)) + return -EINVAL; /* Set the analog sampling phase. This is needed to find the best sampling phase for analog video: an application or driver has to try a number of phases and analyze the picture @@ -1098,7 +1202,10 @@ static inline bool no_signal_tmds(struct v4l2_subdev *sd) static inline bool no_lock_tmds(struct v4l2_subdev *sd) { - return (io_read(sd, 0x6a) & 0xe0) != 0xe0; + struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; + + return (io_read(sd, 0x6a) & info->tdms_lock_mask) != info->tdms_lock_mask; } static inline bool is_hdmi(struct v4l2_subdev *sd) @@ -1108,6 +1215,15 @@ static inline bool is_hdmi(struct v4l2_subdev *sd) static inline bool no_lock_sspd(struct v4l2_subdev *sd) { + struct adv7604_state *state = to_state(sd); + + /* + * Chips without a AFE don't expose registers for the SSPD, so just assume + * that we have a lock. + */ + if (adv7604_has_afe(state)) + return false; + /* TODO channel 2 */ return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0); } @@ -1137,6 +1253,11 @@ static inline bool no_signal(struct v4l2_subdev *sd) static inline bool no_lock_cp(struct v4l2_subdev *sd) { + struct adv7604_state *state = to_state(sd); + + if (!adv7604_has_afe(state)) + return false; + /* CP has detected a non standard number of lines on the incoming video compared to what it is configured to receive by s_dv_timings */ return io_read(sd, 0x12) & 0x01; @@ -1205,8 +1326,11 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, return -1; } + static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) { + struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; u8 polarity; if (no_lock_stdi(sd) || no_lock_sspd(sd)) { @@ -1216,20 +1340,26 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) /* read STDI */ stdi->bl = cp_read16(sd, 0xb1, 0x3fff); - stdi->lcf = cp_read16(sd, 0xb3, 0x7ff); + stdi->lcf = cp_read16(sd, info->lcf_reg, 0x7ff); stdi->lcvs = cp_read(sd, 0xb3) >> 3; stdi->interlaced = io_read(sd, 0x12) & 0x10; - /* read SSPD */ - polarity = cp_read(sd, 0xb5); - if ((polarity & 0x03) == 0x01) { - stdi->hs_pol = polarity & 0x10 - ? (polarity & 0x08 ? '+' : '-') : 'x'; - stdi->vs_pol = polarity & 0x40 - ? (polarity & 0x20 ? '+' : '-') : 'x'; + if (adv7604_has_afe(state)) { + /* read SSPD */ + polarity = cp_read(sd, 0xb5); + if ((polarity & 0x03) == 0x01) { + stdi->hs_pol = polarity & 0x10 + ? (polarity & 0x08 ? '+' : '-') : 'x'; + stdi->vs_pol = polarity & 0x40 + ? (polarity & 0x20 ? '+' : '-') : 'x'; + } else { + stdi->hs_pol = 'x'; + stdi->vs_pol = 'x'; + } } else { - stdi->hs_pol = 'x'; - stdi->vs_pol = 'x'; + polarity = hdmi_read(sd, 0x05); + stdi->hs_pol = polarity & 0x20 ? '+' : '-'; + stdi->vs_pol = polarity & 0x10 ? '+' : '-'; } if (no_lock_stdi(sd) || no_lock_sspd(sd)) { @@ -1297,10 +1427,43 @@ static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, } } +static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd) +{ + unsigned int freq; + int a, b; + + a = hdmi_read(sd, 0x06); + b = hdmi_read(sd, 0x3b); + if (a < 0 || b < 0) + return 0; + freq = a * 1000000 + ((b & 0x30) >> 4) * 250000; + + if (is_hdmi(sd)) { + /* adjust for deep color mode */ + unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; + + freq = freq * 8 / bits_per_channel; + } + + return freq; +} + +static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) +{ + int a, b; + + a = hdmi_read(sd, 0x51); + b = hdmi_read(sd, 0x52); + if (a < 0 || b < 0) + return 0; + return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128; +} + static int adv7604_query_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; struct v4l2_bt_timings *bt = &timings->bt; struct stdi_readback stdi; @@ -1324,21 +1487,12 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; if (is_digital_input(sd)) { - uint32_t freq; - timings->type = V4L2_DV_BT_656_1120; + /* FIXME: All masks are incorrect for ADV7611 */ bt->width = hdmi_read16(sd, 0x07, 0xfff); bt->height = hdmi_read16(sd, 0x09, 0xfff); - freq = (hdmi_read(sd, 0x06) * 1000000) + - ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000; - if (is_hdmi(sd)) { - /* adjust for deep color mode */ - unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; - - freq = freq * 8 / bits_per_channel; - } - bt->pixelclock = freq; + bt->pixelclock = info->read_hdmi_pixelclock(sd); bt->hfrontporch = hdmi_read16(sd, 0x20, 0x3ff); bt->hsync = hdmi_read16(sd, 0x22, 0x3ff); bt->hbackporch = hdmi_read16(sd, 0x24, 0x3ff); @@ -1444,7 +1598,7 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, state->timings = *timings; - cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); + cp_write_and_or(sd, 0x91, 0xbf, bt->interlaced ? 0x40 : 0x00); /* Use prim_mode and vid_std when available */ err = configure_predefined_video_timings(sd, timings); @@ -1471,6 +1625,16 @@ static int adv7604_g_dv_timings(struct v4l2_subdev *sd, return 0; } +static void adv7604_set_termination(struct v4l2_subdev *sd, bool enable) +{ + hdmi_write(sd, 0x01, enable ? 0x00 : 0x78); +} + +static void adv7611_set_termination(struct v4l2_subdev *sd, bool enable) +{ + hdmi_write(sd, 0x83, enable ? 0xfe : 0xff); +} + static void enable_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); @@ -1479,7 +1643,7 @@ static void enable_input(struct v4l2_subdev *sd) io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ } else if (is_digital_input(sd)) { hdmi_write_and_or(sd, 0x00, 0xfc, state->selected_input); - hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ + state->info->set_termination(sd, true); io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */ } else { @@ -1490,67 +1654,36 @@ static void enable_input(struct v4l2_subdev *sd) static void disable_input(struct v4l2_subdev *sd) { + struct adv7604_state *state = to_state(sd); + hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio */ msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */ io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ - hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ + state->info->set_termination(sd, false); } static void select_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; if (is_analog_input(sd)) { - /* reset ADI recommended settings for HDMI: */ - /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ - hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */ - hdmi_write(sd, 0x3d, 0x00); /* DDC bus active pull-up control */ - hdmi_write(sd, 0x3e, 0x74); /* TMDS PLL optimization */ - hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */ - hdmi_write(sd, 0x57, 0x74); /* TMDS PLL optimization */ - hdmi_write(sd, 0x58, 0x63); /* TMDS PLL optimization */ - hdmi_write(sd, 0x8d, 0x18); /* equaliser */ - hdmi_write(sd, 0x8e, 0x34); /* equaliser */ - hdmi_write(sd, 0x93, 0x88); /* equaliser */ - hdmi_write(sd, 0x94, 0x2e); /* equaliser */ - hdmi_write(sd, 0x96, 0x00); /* enable automatic EQ changing */ + adv7604_write_reg_seq(sd, info->recommended_settings[0]); afe_write(sd, 0x00, 0x08); /* power up ADC */ afe_write(sd, 0x01, 0x06); /* power up Analog Front End */ afe_write(sd, 0xc8, 0x00); /* phase control */ - - /* set ADI recommended settings for digitizer */ - /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ - afe_write(sd, 0x12, 0x7b); /* ADC noise shaping filter controls */ - afe_write(sd, 0x0c, 0x1f); /* CP core gain controls */ - cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */ - cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ - cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */ } else if (is_digital_input(sd)) { hdmi_write(sd, 0x00, state->selected_input & 0x03); - /* set ADI recommended settings for HDMI: */ - /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ - hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */ - hdmi_write(sd, 0x3d, 0x10); /* DDC bus active pull-up control */ - hdmi_write(sd, 0x3e, 0x39); /* TMDS PLL optimization */ - hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */ - hdmi_write(sd, 0x57, 0xb6); /* TMDS PLL optimization */ - hdmi_write(sd, 0x58, 0x03); /* TMDS PLL optimization */ - hdmi_write(sd, 0x8d, 0x18); /* equaliser */ - hdmi_write(sd, 0x8e, 0x34); /* equaliser */ - hdmi_write(sd, 0x93, 0x8b); /* equaliser */ - hdmi_write(sd, 0x94, 0x2d); /* equaliser */ - hdmi_write(sd, 0x96, 0x01); /* enable automatic EQ changing */ - - afe_write(sd, 0x00, 0xff); /* power down ADC */ - afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ - afe_write(sd, 0xc8, 0x40); /* phase control */ - - /* reset ADI recommended settings for digitizer */ - /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ - afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */ - afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */ + adv7604_write_reg_seq(sd, info->recommended_settings[1]); + + if (adv7604_has_afe(state)) { + afe_write(sd, 0x00, 0xff); /* power down ADC */ + afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ + afe_write(sd, 0xc8, 0x40); /* phase control */ + } + cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */ cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */ @@ -1571,6 +1704,9 @@ static int adv7604_s_routing(struct v4l2_subdev *sd, if (input == state->selected_input) return 0; + if (input > state->info->max_port) + return -EINVAL; + state->selected_input = input; disable_input(sd); @@ -1610,6 +1746,8 @@ static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd, static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { + struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; const u8 irq_reg_0x43 = io_read(sd, 0x43); const u8 irq_reg_0x6b = io_read(sd, 0x6b); const u8 irq_reg_0x70 = io_read(sd, 0x70); @@ -1628,7 +1766,9 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) /* format change */ fmt_change = irq_reg_0x43 & 0x98; - fmt_change_digital = is_digital_input(sd) ? (irq_reg_0x6b & 0xc0) : 0; + fmt_change_digital = is_digital_input(sd) + ? irq_reg_0x6b & info->fmt_change_digital_mask + : 0; if (fmt_change || fmt_change_digital) { v4l2_dbg(1, debug, sd, @@ -1650,7 +1790,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) } /* tx 5v detect */ - tx_5v = io_read(sd, 0x70) & 0x1e; + tx_5v = io_read(sd, 0x70) & info->cable_det_mask; if (tx_5v) { v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v); io_write(sd, 0x71, tx_5v); @@ -1732,6 +1872,7 @@ static int get_edid_spa_location(const u8 *edid) static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; int spa_loc; int tmp = 0; int err; @@ -1745,7 +1886,7 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) /* Disable hotplug and I2C access to EDID RAM from DDC port */ state->edid.present &= ~(1 << edid->pad); v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); - rep_write_and_or(sd, 0x77, 0xf0, state->edid.present); + rep_write_and_or(sd, info->edid_enable_reg, 0xf0, state->edid.present); /* Fall back to a 16:9 aspect ratio */ state->aspect_ratio.numerator = 16; @@ -1769,7 +1910,7 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) /* Disable hotplug and I2C access to EDID RAM from DDC port */ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&tmp); - rep_write_and_or(sd, 0x77, 0xf0, 0x00); + rep_write_and_or(sd, info->edid_enable_reg, 0xf0, 0x00); spa_loc = get_edid_spa_location(edid->edid); if (spa_loc < 0) @@ -1795,8 +1936,14 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) default: return -EINVAL; } - rep_write(sd, 0x76, spa_loc & 0xff); - rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); + + if (info->type == ADV7604) { + rep_write(sd, 0x76, spa_loc & 0xff); + rep_write_and_or(sd, 0x77, 0xbf, (spa_loc & 0x100) >> 2); + } else { + /* FIXME: Where is the SPA location LSB register ? */ + rep_write_and_or(sd, 0x71, 0xfe, (spa_loc & 0x100) >> 8); + } edid->edid[spa_loc] = state->spa_port_a[0]; edid->edid[spa_loc + 1] = state->spa_port_a[1]; @@ -1815,10 +1962,10 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) /* adv7604 calculates the checksums and enables I2C access to internal EDID RAM from DDC port. */ - rep_write_and_or(sd, 0x77, 0xf0, state->edid.present); + rep_write_and_or(sd, info->edid_enable_reg, 0xf0, state->edid.present); for (i = 0; i < 1000; i++) { - if (rep_read(sd, 0x7d) & state->edid.present) + if (rep_read(sd, info->edid_status_reg) & state->edid.present) break; mdelay(1); } @@ -1881,6 +2028,7 @@ static void print_avi_infoframe(struct v4l2_subdev *sd) static int adv7604_log_status(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; struct v4l2_dv_timings timings; struct stdi_readback stdi; u8 reg_io_0x02 = io_read(sd, 0x02); @@ -1915,7 +2063,7 @@ static int adv7604_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); - edid_enabled = rep_read(sd, 0x7d); + edid_enabled = rep_read(sd, info->edid_status_reg); v4l2_info(sd, "EDID enabled port A: %s, B: %s, C: %s, D: %s\n", ((edid_enabled & 0x01) ? "Yes" : "No"), ((edid_enabled & 0x02) ? "Yes" : "No"), @@ -1925,12 +2073,12 @@ static int adv7604_log_status(struct v4l2_subdev *sd) "enabled" : "disabled"); v4l2_info(sd, "-----Signal status-----\n"); - cable_det = io_read(sd, 0x6f); + cable_det = info->read_cable_det(sd); v4l2_info(sd, "Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s\n", - ((cable_det & 0x10) ? "Yes" : "No"), - ((cable_det & 0x08) ? "Yes" : "No"), + ((cable_det & 0x01) ? "Yes" : "No"), + ((cable_det & 0x02) ? "Yes" : "No"), ((cable_det & 0x04) ? "Yes" : "No"), - ((cable_det & 0x02) ? "Yes" : "No")); + ((cable_det & 0x08) ? "Yes" : "No")); v4l2_info(sd, "TMDS signal detected: %s\n", no_signal_tmds(sd) ? "false" : "true"); v4l2_info(sd, "TMDS signal locked: %s\n", @@ -2103,6 +2251,7 @@ static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = { static int adv7604_core_init(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; struct adv7604_platform_data *pdata = &state->pdata; hdmi_write(sd, 0x48, @@ -2156,19 +2305,31 @@ static int adv7604_core_init(struct v4l2_subdev *sd) /* TODO from platform data */ afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ - afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ - io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4); + if (adv7604_has_afe(state)) { + afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ + io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4); + } /* interrupts */ - io_write(sd, 0x40, 0xc2); /* Configure INT1 */ - io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ + io_write(sd, 0x40, 0xc0 | pdata->int1_config); /* Configure INT1 */ io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */ - io_write(sd, 0x6e, 0xc1); /* Enable V_LOCKED, DE_REGEN_LCK, HDMI_MODE interrupts */ - io_write(sd, 0x73, 0x1e); /* Enable CABLE_DET_A_ST (+5v) interrupts */ + io_write(sd, 0x6e, info->fmt_change_digital_mask); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ + io_write(sd, 0x73, info->cable_det_mask); /* Enable cable detection (+5v) interrupts */ + info->setup_irqs(sd); return v4l2_ctrl_handler_setup(sd->ctrl_handler); } +static void adv7604_setup_irqs(struct v4l2_subdev *sd) +{ + io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ +} + +static void adv7611_setup_irqs(struct v4l2_subdev *sd) +{ + io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */ +} + static void adv7604_unregister_clients(struct adv7604_state *state) { if (state->i2c_avlink) @@ -2207,6 +2368,130 @@ static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); } +static const struct adv7604_reg_seq adv7604_recommended_settings_afe[] = { + /* reset ADI recommended settings for HDMI: */ + /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */ + + /* set ADI recommended settings for digitizer */ + /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ + { ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */ + { ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */ + { ADV7604_REG(ADV7604_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */ + { ADV7604_REG(ADV7604_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */ + { ADV7604_REG(ADV7604_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */ + + { ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_reg_seq adv7604_recommended_settings_hdmi[] = { + /* set ADI recommended settings for HDMI: */ + /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */ + + /* reset ADI recommended settings for digitizer */ + /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ + { ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */ + { ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */ + + { ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_reg_seq adv7611_recommended_settings_hdmi[] = { + { ADV7604_REG(ADV7604_PAGE_CP, 0x6c), 0x00 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x6f), 0x0c }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x87), 0x70 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xda }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x01 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x03), 0x98 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4c), 0x44 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x04 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x1e }, + + { ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_chip_info adv7604_chip_info[] = { + [ADV7604] = { + .type = ADV7604, + .has_afe = true, + .max_port = ADV7604_INPUT_VGA_COMP, + .num_dv_ports = 4, + .edid_enable_reg = 0x77, + .edid_status_reg = 0x7d, + .lcf_reg = 0xb3, + .tdms_lock_mask = 0xe0, + .cable_det_mask = 0x1e, + .fmt_change_digital_mask = 0xc1, + .set_termination = adv7604_set_termination, + .setup_irqs = adv7604_setup_irqs, + .read_hdmi_pixelclock = adv7604_read_hdmi_pixelclock, + .read_cable_det = adv7604_read_cable_det, + .recommended_settings = { + [0] = adv7604_recommended_settings_afe, + [1] = adv7604_recommended_settings_hdmi, + }, + .num_recommended_settings = { + [0] = ARRAY_SIZE(adv7604_recommended_settings_afe), + [1] = ARRAY_SIZE(adv7604_recommended_settings_hdmi), + }, + .page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) | + BIT(ADV7604_PAGE_CEC) | BIT(ADV7604_PAGE_INFOFRAME) | + BIT(ADV7604_PAGE_ESDP) | BIT(ADV7604_PAGE_DPP) | + BIT(ADV7604_PAGE_AFE) | BIT(ADV7604_PAGE_REP) | + BIT(ADV7604_PAGE_EDID) | BIT(ADV7604_PAGE_HDMI) | + BIT(ADV7604_PAGE_TEST) | BIT(ADV7604_PAGE_CP) | + BIT(ADV7604_PAGE_VDP), + }, + [ADV7611] = { + .type = ADV7611, + .has_afe = false, + .max_port = ADV7604_INPUT_HDMI_PORT_A, + .num_dv_ports = 1, + .edid_enable_reg = 0x74, + .edid_status_reg = 0x76, + .lcf_reg = 0xa3, + .tdms_lock_mask = 0x43, + .cable_det_mask = 0x01, + .fmt_change_digital_mask = 0x03, + .set_termination = adv7611_set_termination, + .setup_irqs = adv7611_setup_irqs, + .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, + .read_cable_det = adv7611_read_cable_det, + .recommended_settings = { + [1] = adv7611_recommended_settings_hdmi, + }, + .num_recommended_settings = { + [1] = ARRAY_SIZE(adv7611_recommended_settings_hdmi), + }, + .page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_CEC) | + BIT(ADV7604_PAGE_INFOFRAME) | BIT(ADV7604_PAGE_AFE) | + BIT(ADV7604_PAGE_REP) | BIT(ADV7604_PAGE_EDID) | + BIT(ADV7604_PAGE_HDMI) | BIT(ADV7604_PAGE_CP), + }, +}; + static int adv7604_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2216,6 +2501,7 @@ static int adv7604_probe(struct i2c_client *client, struct adv7604_platform_data *pdata = client->dev.platform_data; struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; + u16 val; int err; /* Check if the adapter supports the needed features */ @@ -2230,6 +2516,8 @@ static int adv7604_probe(struct i2c_client *client, return -ENOMEM; } + state->info = &adv7604_chip_info[id->driver_data]; + /* initialize variables */ state->restart_stdi_once = true; state->selected_input = ~0; @@ -2244,18 +2532,36 @@ static int adv7604_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7604_ops); + snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", + id->name, i2c_adapter_id(client->adapter), + client->addr); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - /* i2c access to adv7604? */ - if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) { - v4l2_info(sd, "not an adv7604 on address 0x%x\n", - client->addr << 1); - return -ENODEV; + /* + * Verify that the chip is present. On ADV7604 the RD_INFO register only + * identifies the revision, while on ADV7611 it identifies the model as + * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611. + */ + if (state->info->type == ADV7604) { + val = adv_smbus_read_byte_data_check(client, 0xfb, false); + if (val != 0x68) { + v4l2_info(sd, "not an adv7604 on address 0x%x\n", + client->addr << 1); + return -ENODEV; + } + } else { + val = (adv_smbus_read_byte_data_check(client, 0xea, false) << 8) + | (adv_smbus_read_byte_data_check(client, 0xeb, false) << 0); + if (val != 0x2051) { + v4l2_info(sd, "not an adv7611 on address 0x%x\n", + client->addr << 1); + return -ENODEV; + } } /* control handlers */ hdl = &state->hdl; - v4l2_ctrl_handler_init(hdl, 9); + v4l2_ctrl_handler_init(hdl, adv7604_has_afe(state) ? 9 : 8); v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); @@ -2268,15 +2574,17 @@ static int adv7604_probe(struct i2c_client *client, /* private controls */ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_DV_RX_POWER_PRESENT, 0, 0x0f, 0, 0); + V4L2_CID_DV_RX_POWER_PRESENT, 0, + (1 << state->info->num_dv_ports) - 1, 0, 0); state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops, V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, V4L2_DV_RGB_RANGE_AUTO); /* custom controls */ - state->analog_sampling_phase_ctrl = - v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); + if (adv7604_has_afe(state)) + state->analog_sampling_phase_ctrl = + v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); state->free_run_color_manual_ctrl = v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL); state->free_run_color_ctrl = @@ -2289,7 +2597,8 @@ static int adv7604_probe(struct i2c_client *client, } state->detect_tx_5v_ctrl->is_private = true; state->rgb_quantization_range_ctrl->is_private = true; - state->analog_sampling_phase_ctrl->is_private = true; + if (adv7604_has_afe(state)) + state->analog_sampling_phase_ctrl->is_private = true; state->free_run_color_manual_ctrl->is_private = true; state->free_run_color_ctrl->is_private = true; @@ -2298,27 +2607,34 @@ static int adv7604_probe(struct i2c_client *client, goto err_hdl; } - state->i2c_avlink = adv7604_dummy_client(sd, pdata->i2c_avlink, 0xf3); state->i2c_cec = adv7604_dummy_client(sd, pdata->i2c_cec, 0xf4); state->i2c_infoframe = adv7604_dummy_client(sd, pdata->i2c_infoframe, 0xf5); - state->i2c_esdp = adv7604_dummy_client(sd, pdata->i2c_esdp, 0xf6); - state->i2c_dpp = adv7604_dummy_client(sd, pdata->i2c_dpp, 0xf7); state->i2c_afe = adv7604_dummy_client(sd, pdata->i2c_afe, 0xf8); state->i2c_repeater = adv7604_dummy_client(sd, pdata->i2c_repeater, 0xf9); state->i2c_edid = adv7604_dummy_client(sd, pdata->i2c_edid, 0xfa); state->i2c_hdmi = adv7604_dummy_client(sd, pdata->i2c_hdmi, 0xfb); - state->i2c_test = adv7604_dummy_client(sd, pdata->i2c_test, 0xfc); state->i2c_cp = adv7604_dummy_client(sd, pdata->i2c_cp, 0xfd); - state->i2c_vdp = adv7604_dummy_client(sd, pdata->i2c_vdp, 0xfe); - if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || - !state->i2c_esdp || !state->i2c_dpp || !state->i2c_afe || + if (!state->i2c_cec || !state->i2c_infoframe || !state->i2c_afe || !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || - !state->i2c_test || !state->i2c_cp || !state->i2c_vdp) { + !state->i2c_cp) { err = -ENOMEM; - v4l2_err(sd, "failed to create all i2c clients\n"); + v4l2_err(sd, "failed to create digital i2c clients\n"); goto err_i2c; } + if (adv7604_has_afe(state)) { + state->i2c_avlink = adv7604_dummy_client(sd, pdata->i2c_avlink, 0xf3); + state->i2c_esdp = adv7604_dummy_client(sd, pdata->i2c_esdp, 0xf6); + state->i2c_dpp = adv7604_dummy_client(sd, pdata->i2c_dpp, 0xf7); + state->i2c_test = adv7604_dummy_client(sd, pdata->i2c_test, 0xfc); + state->i2c_vdp = adv7604_dummy_client(sd, pdata->i2c_vdp, 0xfe); + if (!state->i2c_avlink || !state->i2c_esdp || !state->i2c_dpp || + !state->i2c_test || !state->i2c_vdp) { + err = -ENOMEM; + v4l2_err(sd, "failed to create analog i2c clients\n"); + goto err_i2c; + } + } /* work queues */ state->work_queues = create_singlethread_workqueue(client->name); if (!state->work_queues) { @@ -2379,7 +2695,8 @@ static int adv7604_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_device_id adv7604_id[] = { - { "adv7604", 0 }, + { "adv7604", ADV7604 }, + { "adv7611", ADV7611 }, { } }; MODULE_DEVICE_TABLE(i2c, adv7604_id); diff --git a/include/media/adv7604.h b/include/media/adv7604.h index c6b39372eed7..b2500baee8ed 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -86,6 +86,13 @@ enum adv7604_drive_strength { ADV7604_DR_STR_HIGH = 3, }; +enum adv7604_int1_config { + ADV7604_INT1_CONFIG_OPEN_DRAIN, + ADV7604_INT1_CONFIG_ACTIVE_LOW, + ADV7604_INT1_CONFIG_ACTIVE_HIGH, + ADV7604_INT1_CONFIG_DISABLED, +}; + /* Platform dependent definition */ struct adv7604_platform_data { /* DIS_PWRDNB: 1 if the PWRDNB pin is unused and unconnected */ @@ -103,6 +110,9 @@ struct adv7604_platform_data { /* Select output format */ enum adv7604_op_format_sel op_format_sel; + /* Configuration of the INT1 pin */ + enum adv7604_int1_config int1_config; + /* IO register 0x02 */ unsigned alt_gamma:1; unsigned op_656_range:1; -- cgit v1.2.3 From c784b1e2ece8a591263050d1f59607547dfad8f3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 29 Jan 2014 10:08:58 -0300 Subject: [media] adv7604: Add sink pads The ADV7604 has sink pads for its HDMI and analog inputs. Report them. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 61 ++++++++++++++++++++++++++------------------- include/media/adv7604.h | 23 ++++++++--------- 2 files changed, 46 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index ca7590641581..79fb34d5ee2d 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -97,13 +97,15 @@ struct adv7604_chip_info { * ********************************************************************** */ + struct adv7604_state { const struct adv7604_chip_info *info; struct adv7604_platform_data pdata; struct v4l2_subdev sd; - struct media_pad pad; + struct media_pad pads[ADV7604_PAD_MAX]; + unsigned int source_pad; struct v4l2_ctrl_handler hdl; - enum adv7604_input_port selected_input; + enum adv7604_pad selected_input; struct v4l2_dv_timings timings; struct { u8 edid[256]; @@ -775,18 +777,18 @@ static inline bool is_analog_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); - return state->selected_input == ADV7604_INPUT_VGA_RGB || - state->selected_input == ADV7604_INPUT_VGA_COMP; + return state->selected_input == ADV7604_PAD_VGA_RGB || + state->selected_input == ADV7604_PAD_VGA_COMP; } static inline bool is_digital_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); - return state->selected_input == ADV7604_INPUT_HDMI_PORT_A || - state->selected_input == ADV7604_INPUT_HDMI_PORT_B || - state->selected_input == ADV7604_INPUT_HDMI_PORT_C || - state->selected_input == ADV7604_INPUT_HDMI_PORT_D; + return state->selected_input == ADV7604_PAD_HDMI_PORT_A || + state->selected_input == ADV7604_PAD_HDMI_PORT_B || + state->selected_input == ADV7604_PAD_HDMI_PORT_C || + state->selected_input == ADV7604_PAD_HDMI_PORT_D; } /* ----------------------------------------------------------------------- */ @@ -1066,14 +1068,14 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: - if (state->selected_input == ADV7604_INPUT_VGA_RGB) { + if (state->selected_input == ADV7604_PAD_VGA_RGB) { /* Receiving analog RGB signal * Set RGB full range (0-255) */ io_write_and_or(sd, 0x02, 0x0f, 0x10); break; } - if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + if (state->selected_input == ADV7604_PAD_VGA_COMP) { /* Receiving analog YPbPr signal * Set automode */ io_write_and_or(sd, 0x02, 0x0f, 0xf0); @@ -1106,7 +1108,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) } break; case V4L2_DV_RGB_RANGE_LIMITED: - if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + if (state->selected_input == ADV7604_PAD_VGA_COMP) { /* YCrCb limited range (16-235) */ io_write_and_or(sd, 0x02, 0x0f, 0x20); break; @@ -1117,7 +1119,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) break; case V4L2_DV_RGB_RANGE_FULL: - if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + if (state->selected_input == ADV7604_PAD_VGA_COMP) { /* YCrCb full range (0-255) */ io_write_and_or(sd, 0x02, 0x0f, 0x60); break; @@ -1806,7 +1808,7 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) struct adv7604_state *state = to_state(sd); u8 *data = NULL; - if (edid->pad > ADV7604_EDID_PORT_D) + if (edid->pad > ADV7604_PAD_HDMI_PORT_D) return -EINVAL; if (edid->blocks == 0) return -EINVAL; @@ -1821,10 +1823,10 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) edid->blocks = state->edid.blocks; switch (edid->pad) { - case ADV7604_EDID_PORT_A: - case ADV7604_EDID_PORT_B: - case ADV7604_EDID_PORT_C: - case ADV7604_EDID_PORT_D: + case ADV7604_PAD_HDMI_PORT_A: + case ADV7604_PAD_HDMI_PORT_B: + case ADV7604_PAD_HDMI_PORT_C: + case ADV7604_PAD_HDMI_PORT_D: if (state->edid.present & (1 << edid->pad)) data = state->edid.edid; break; @@ -1878,7 +1880,7 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) int err; int i; - if (edid->pad > ADV7604_EDID_PORT_D) + if (edid->pad > ADV7604_PAD_HDMI_PORT_D) return -EINVAL; if (edid->start_block != 0) return -EINVAL; @@ -1917,19 +1919,19 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ switch (edid->pad) { - case ADV7604_EDID_PORT_A: + case ADV7604_PAD_HDMI_PORT_A: state->spa_port_a[0] = edid->edid[spa_loc]; state->spa_port_a[1] = edid->edid[spa_loc + 1]; break; - case ADV7604_EDID_PORT_B: + case ADV7604_PAD_HDMI_PORT_B: rep_write(sd, 0x70, edid->edid[spa_loc]); rep_write(sd, 0x71, edid->edid[spa_loc + 1]); break; - case ADV7604_EDID_PORT_C: + case ADV7604_PAD_HDMI_PORT_C: rep_write(sd, 0x72, edid->edid[spa_loc]); rep_write(sd, 0x73, edid->edid[spa_loc + 1]); break; - case ADV7604_EDID_PORT_D: + case ADV7604_PAD_HDMI_PORT_D: rep_write(sd, 0x74, edid->edid[spa_loc]); rep_write(sd, 0x75, edid->edid[spa_loc + 1]); break; @@ -2429,7 +2431,7 @@ static const struct adv7604_chip_info adv7604_chip_info[] = { [ADV7604] = { .type = ADV7604, .has_afe = true, - .max_port = ADV7604_INPUT_VGA_COMP, + .max_port = ADV7604_PAD_VGA_COMP, .num_dv_ports = 4, .edid_enable_reg = 0x77, .edid_status_reg = 0x7d, @@ -2460,7 +2462,7 @@ static const struct adv7604_chip_info adv7604_chip_info[] = { [ADV7611] = { .type = ADV7611, .has_afe = false, - .max_port = ADV7604_INPUT_HDMI_PORT_A, + .max_port = ADV7604_PAD_HDMI_PORT_A, .num_dv_ports = 1, .edid_enable_reg = 0x74, .edid_status_reg = 0x76, @@ -2494,6 +2496,7 @@ static int adv7604_probe(struct i2c_client *client, struct adv7604_platform_data *pdata = client->dev.platform_data; struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; + unsigned int i; u16 val; int err; @@ -2639,8 +2642,14 @@ static int adv7604_probe(struct i2c_client *client, INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, adv7604_delayed_work_enable_hotplug); - state->pad.flags = MEDIA_PAD_FL_SOURCE; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + state->source_pad = state->info->num_dv_ports + + (state->info->has_afe ? 2 : 0); + for (i = 0; i < state->source_pad; ++i) + state->pads[i].flags = MEDIA_PAD_FL_SINK; + state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; + + err = media_entity_init(&sd->entity, state->source_pad + 1, + state->pads, 0); if (err) goto err_work_queues; diff --git a/include/media/adv7604.h b/include/media/adv7604.h index b2500baee8ed..6186771c0642 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -155,20 +155,19 @@ struct adv7604_platform_data { u8 i2c_vdp; }; -enum adv7604_input_port { - ADV7604_INPUT_HDMI_PORT_A, - ADV7604_INPUT_HDMI_PORT_B, - ADV7604_INPUT_HDMI_PORT_C, - ADV7604_INPUT_HDMI_PORT_D, - ADV7604_INPUT_VGA_RGB, - ADV7604_INPUT_VGA_COMP, +enum adv7604_pad { + ADV7604_PAD_HDMI_PORT_A = 0, + ADV7604_PAD_HDMI_PORT_B = 1, + ADV7604_PAD_HDMI_PORT_C = 2, + ADV7604_PAD_HDMI_PORT_D = 3, + ADV7604_PAD_VGA_RGB = 4, + ADV7604_PAD_VGA_COMP = 5, + /* The source pad is either 1 (ADV7611) or 6 (ADV7604) */ + ADV7604_PAD_SOURCE = 6, + ADV7611_PAD_SOURCE = 1, + ADV7604_PAD_MAX = 7, }; -#define ADV7604_EDID_PORT_A 0 -#define ADV7604_EDID_PORT_B 1 -#define ADV7604_EDID_PORT_C 2 -#define ADV7604_EDID_PORT_D 3 - #define V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE (V4L2_CID_DV_CLASS_BASE + 0x1000) #define V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL (V4L2_CID_DV_CLASS_BASE + 0x1001) #define V4L2_CID_ADV_RX_FREE_RUN_COLOR (V4L2_CID_DV_CLASS_BASE + 0x1002) -- cgit v1.2.3 From 539b33b05913f1de34c7e0ef3376347ffd273e8f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 26 Jan 2014 18:42:37 -0300 Subject: [media] adv7604: Make output format configurable through pad format operations Replace the dummy video format operations by pad format operations that configure the output format. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 280 ++++++++++++++++++++++++++++++++++++++++---- include/media/adv7604.h | 56 ++++----- 2 files changed, 275 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 79fb34d5ee2d..59f7bf0723fe 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -53,6 +53,28 @@ MODULE_LICENSE("GPL"); /* ADV7604 system clock frequency */ #define ADV7604_fsc (28636360) +#define ADV7604_RGB_OUT (1 << 1) + +#define ADV7604_OP_FORMAT_SEL_8BIT (0 << 0) +#define ADV7604_OP_FORMAT_SEL_10BIT (1 << 0) +#define ADV7604_OP_FORMAT_SEL_12BIT (2 << 0) + +#define ADV7604_OP_MODE_SEL_SDR_422 (0 << 5) +#define ADV7604_OP_MODE_SEL_DDR_422 (1 << 5) +#define ADV7604_OP_MODE_SEL_SDR_444 (2 << 5) +#define ADV7604_OP_MODE_SEL_DDR_444 (3 << 5) +#define ADV7604_OP_MODE_SEL_SDR_422_2X (4 << 5) +#define ADV7604_OP_MODE_SEL_ADI_CM (5 << 5) + +#define ADV7604_OP_CH_SEL_GBR (0 << 5) +#define ADV7604_OP_CH_SEL_GRB (1 << 5) +#define ADV7604_OP_CH_SEL_BGR (2 << 5) +#define ADV7604_OP_CH_SEL_RGB (3 << 5) +#define ADV7604_OP_CH_SEL_BRG (4 << 5) +#define ADV7604_OP_CH_SEL_RBG (5 << 5) + +#define ADV7604_OP_SWAP_CB_CR (1 << 0) + enum adv7604_type { ADV7604, ADV7611, @@ -63,6 +85,14 @@ struct adv7604_reg_seq { u8 val; }; +struct adv7604_format_info { + enum v4l2_mbus_pixelcode code; + u8 op_ch_sel; + bool rgb_out; + bool swap_cb_cr; + u8 op_format_sel; +}; + struct adv7604_chip_info { enum adv7604_type type; @@ -78,6 +108,9 @@ struct adv7604_chip_info { unsigned int tdms_lock_mask; unsigned int fmt_change_digital_mask; + const struct adv7604_format_info *formats; + unsigned int nformats; + void (*set_termination)(struct v4l2_subdev *sd, bool enable); void (*setup_irqs)(struct v4l2_subdev *sd); unsigned int (*read_hdmi_pixelclock)(struct v4l2_subdev *sd); @@ -101,12 +134,18 @@ struct adv7604_chip_info { struct adv7604_state { const struct adv7604_chip_info *info; struct adv7604_platform_data pdata; + struct v4l2_subdev sd; struct media_pad pads[ADV7604_PAD_MAX]; unsigned int source_pad; + struct v4l2_ctrl_handler hdl; + enum adv7604_pad selected_input; + struct v4l2_dv_timings timings; + const struct adv7604_format_info *format; + struct { u8 edid[256]; u32 present; @@ -771,6 +810,93 @@ static void adv7604_write_reg_seq(struct v4l2_subdev *sd, adv7604_write_reg(sd, reg_seq[i].reg, reg_seq[i].val); } +/* ----------------------------------------------------------------------------- + * Format helpers + */ + +static const struct adv7604_format_info adv7604_formats[] = { + { V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false, + ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV10_2X10, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_YVYU10_2X10, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_UYVY10_1X20, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_VYUY10_1X20, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_YUYV10_1X20, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_YVYU10_1X20, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +}; + +static const struct adv7604_format_info adv7611_formats[] = { + { V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false, + ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +}; + +static const struct adv7604_format_info * +adv7604_format_info(struct adv7604_state *state, enum v4l2_mbus_pixelcode code) +{ + unsigned int i; + + for (i = 0; i < state->info->nformats; ++i) { + if (state->info->formats[i].code == code) + return &state->info->formats[i]; + } + + return NULL; +} + /* ----------------------------------------------------------------------- */ static inline bool is_analog_input(struct v4l2_subdev *sd) @@ -1720,29 +1846,132 @@ static int adv7604_s_routing(struct v4l2_subdev *sd, return 0; } -static int adv7604_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) +static int adv7604_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) { - if (index) + struct adv7604_state *state = to_state(sd); + + if (code->index >= state->info->nformats) return -EINVAL; - /* Good enough for now */ - *code = V4L2_MBUS_FMT_FIXED; + + code->code = state->info->formats[code->index].code; + return 0; } -static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static void adv7604_fill_format(struct adv7604_state *state, + struct v4l2_mbus_framefmt *format) { - struct adv7604_state *state = to_state(sd); + memset(format, 0, sizeof(*format)); - fmt->width = state->timings.bt.width; - fmt->height = state->timings.bt.height; - fmt->code = V4L2_MBUS_FMT_FIXED; - fmt->field = V4L2_FIELD_NONE; - if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { - fmt->colorspace = (state->timings.bt.height <= 576) ? + format->width = state->timings.bt.width; + format->height = state->timings.bt.height; + format->field = V4L2_FIELD_NONE; + + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) + format->colorspace = (state->timings.bt.height <= 576) ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; +} + +/* + * Compute the op_ch_sel value required to obtain on the bus the component order + * corresponding to the selected format taking into account bus reordering + * applied by the board at the output of the device. + * + * The following table gives the op_ch_value from the format component order + * (expressed as op_ch_sel value in column) and the bus reordering (expressed as + * adv7604_bus_order value in row). + * + * | GBR(0) GRB(1) BGR(2) RGB(3) BRG(4) RBG(5) + * ----------+------------------------------------------------- + * RGB (NOP) | GBR GRB BGR RGB BRG RBG + * GRB (1-2) | BGR RGB GBR GRB RBG BRG + * RBG (2-3) | GRB GBR BRG RBG BGR RGB + * BGR (1-3) | RBG BRG RGB BGR GRB GBR + * BRG (ROR) | BRG RBG GRB GBR RGB BGR + * GBR (ROL) | RGB BGR RBG BRG GBR GRB + */ +static unsigned int adv7604_op_ch_sel(struct adv7604_state *state) +{ +#define _SEL(a,b,c,d,e,f) { \ + ADV7604_OP_CH_SEL_##a, ADV7604_OP_CH_SEL_##b, ADV7604_OP_CH_SEL_##c, \ + ADV7604_OP_CH_SEL_##d, ADV7604_OP_CH_SEL_##e, ADV7604_OP_CH_SEL_##f } +#define _BUS(x) [ADV7604_BUS_ORDER_##x] + + static const unsigned int op_ch_sel[6][6] = { + _BUS(RGB) /* NOP */ = _SEL(GBR, GRB, BGR, RGB, BRG, RBG), + _BUS(GRB) /* 1-2 */ = _SEL(BGR, RGB, GBR, GRB, RBG, BRG), + _BUS(RBG) /* 2-3 */ = _SEL(GRB, GBR, BRG, RBG, BGR, RGB), + _BUS(BGR) /* 1-3 */ = _SEL(RBG, BRG, RGB, BGR, GRB, GBR), + _BUS(BRG) /* ROR */ = _SEL(BRG, RBG, GRB, GBR, RGB, BGR), + _BUS(GBR) /* ROL */ = _SEL(RGB, BGR, RBG, BRG, GBR, GRB), + }; + + return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5]; +} + +static void adv7604_setup_format(struct adv7604_state *state) +{ + struct v4l2_subdev *sd = &state->sd; + + io_write_and_or(sd, 0x02, 0xfd, + state->format->rgb_out ? ADV7604_RGB_OUT : 0); + io_write(sd, 0x03, state->format->op_format_sel | + state->pdata.op_format_mode_sel); + io_write_and_or(sd, 0x04, 0x1f, adv7604_op_ch_sel(state)); + io_write_and_or(sd, 0x05, 0xfe, + state->format->swap_cb_cr ? ADV7604_OP_SWAP_CB_CR : 0); +} + +static int adv7604_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct adv7604_state *state = to_state(sd); + + if (format->pad != state->source_pad) + return -EINVAL; + + adv7604_fill_format(state, &format->format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(fh, format->pad); + format->format.code = fmt->code; + } else { + format->format.code = state->format->code; } + + return 0; +} + +static int adv7604_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct adv7604_state *state = to_state(sd); + const struct adv7604_format_info *info; + + if (format->pad != state->source_pad) + return -EINVAL; + + info = adv7604_format_info(state, format->format.code); + if (info == NULL) + info = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8); + + adv7604_fill_format(state, &format->format); + format->format.code = info->code; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(fh, format->pad); + fmt->code = format->format.code; + } else { + state->format = info; + adv7604_setup_format(state); + } + return 0; } @@ -2189,13 +2418,12 @@ static const struct v4l2_subdev_video_ops adv7604_video_ops = { .query_dv_timings = adv7604_query_dv_timings, .enum_dv_timings = adv7604_enum_dv_timings, .dv_timings_cap = adv7604_dv_timings_cap, - .enum_mbus_fmt = adv7604_enum_mbus_fmt, - .g_mbus_fmt = adv7604_g_mbus_fmt, - .try_mbus_fmt = adv7604_g_mbus_fmt, - .s_mbus_fmt = adv7604_g_mbus_fmt, }; static const struct v4l2_subdev_pad_ops adv7604_pad_ops = { + .enum_mbus_code = adv7604_enum_mbus_code, + .get_fmt = adv7604_get_format, + .set_fmt = adv7604_set_format, .get_edid = adv7604_get_edid, .set_edid = adv7604_set_edid, }; @@ -2264,14 +2492,11 @@ static int adv7604_core_init(struct v4l2_subdev *sd) io_write_and_or(sd, 0x02, 0xf0, pdata->alt_gamma << 3 | pdata->op_656_range << 2 | - pdata->rgb_out << 1 | pdata->alt_data_sat << 0); - io_write(sd, 0x03, pdata->op_format_sel); - io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5); - io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 | - pdata->insert_av_codes << 2 | - pdata->replicate_av_codes << 1 | - pdata->invert_cbcr << 0); + io_write_and_or(sd, 0x05, 0xf1, pdata->blank_data << 3 | + pdata->insert_av_codes << 2 | + pdata->replicate_av_codes << 1); + adv7604_setup_format(state); cp_write(sd, 0x69, 0x30); /* Enable CP CSC */ @@ -2439,6 +2664,8 @@ static const struct adv7604_chip_info adv7604_chip_info[] = { .tdms_lock_mask = 0xe0, .cable_det_mask = 0x1e, .fmt_change_digital_mask = 0xc1, + .formats = adv7604_formats, + .nformats = ARRAY_SIZE(adv7604_formats), .set_termination = adv7604_set_termination, .setup_irqs = adv7604_setup_irqs, .read_hdmi_pixelclock = adv7604_read_hdmi_pixelclock, @@ -2470,6 +2697,8 @@ static const struct adv7604_chip_info adv7604_chip_info[] = { .tdms_lock_mask = 0x43, .cable_det_mask = 0x01, .fmt_change_digital_mask = 0x03, + .formats = adv7611_formats, + .nformats = ARRAY_SIZE(adv7611_formats), .set_termination = adv7611_set_termination, .setup_irqs = adv7611_setup_irqs, .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, @@ -2525,6 +2754,7 @@ static int adv7604_probe(struct i2c_client *client, } state->pdata = *pdata; state->timings = cea640x480; + state->format = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8); sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7604_ops); diff --git a/include/media/adv7604.h b/include/media/adv7604.h index 6186771c0642..d8b2cb8f5dce 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -32,14 +32,18 @@ enum adv7604_ain_sel { ADV7604_AIN9_4_5_6_SYNC_2_1 = 4, }; -/* Bus rotation and reordering (IO register 0x04, [7:5]) */ -enum adv7604_op_ch_sel { - ADV7604_OP_CH_SEL_GBR = 0, - ADV7604_OP_CH_SEL_GRB = 1, - ADV7604_OP_CH_SEL_BGR = 2, - ADV7604_OP_CH_SEL_RGB = 3, - ADV7604_OP_CH_SEL_BRG = 4, - ADV7604_OP_CH_SEL_RBG = 5, +/* + * Bus rotation and reordering. This is used to specify component reordering on + * the board and describes the components order on the bus when the ADV7604 + * outputs RGB. + */ +enum adv7604_bus_order { + ADV7604_BUS_ORDER_RGB, /* No operation */ + ADV7604_BUS_ORDER_GRB, /* Swap 1-2 */ + ADV7604_BUS_ORDER_RBG, /* Swap 2-3 */ + ADV7604_BUS_ORDER_BGR, /* Swap 1-3 */ + ADV7604_BUS_ORDER_BRG, /* Rotate right */ + ADV7604_BUS_ORDER_GBR, /* Rotate left */ }; /* Input Color Space (IO register 0x02, [7:4]) */ @@ -55,29 +59,11 @@ enum adv7604_inp_color_space { ADV7604_INP_COLOR_SPACE_AUTO = 0xf, }; -/* Select output format (IO register 0x03, [7:0]) */ -enum adv7604_op_format_sel { - ADV7604_OP_FORMAT_SEL_SDR_ITU656_8 = 0x00, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_10 = 0x01, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_12_MODE0 = 0x02, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_12_MODE1 = 0x06, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_12_MODE2 = 0x0a, - ADV7604_OP_FORMAT_SEL_DDR_422_8 = 0x20, - ADV7604_OP_FORMAT_SEL_DDR_422_10 = 0x21, - ADV7604_OP_FORMAT_SEL_DDR_422_12_MODE0 = 0x22, - ADV7604_OP_FORMAT_SEL_DDR_422_12_MODE1 = 0x23, - ADV7604_OP_FORMAT_SEL_DDR_422_12_MODE2 = 0x24, - ADV7604_OP_FORMAT_SEL_SDR_444_24 = 0x40, - ADV7604_OP_FORMAT_SEL_SDR_444_30 = 0x41, - ADV7604_OP_FORMAT_SEL_SDR_444_36_MODE0 = 0x42, - ADV7604_OP_FORMAT_SEL_DDR_444_24 = 0x60, - ADV7604_OP_FORMAT_SEL_DDR_444_30 = 0x61, - ADV7604_OP_FORMAT_SEL_DDR_444_36 = 0x62, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_16 = 0x80, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_20 = 0x81, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_24_MODE0 = 0x82, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_24_MODE1 = 0x86, - ADV7604_OP_FORMAT_SEL_SDR_ITU656_24_MODE2 = 0x8a, +/* Select output format (IO register 0x03, [4:2]) */ +enum adv7604_op_format_mode_sel { + ADV7604_OP_FORMAT_MODE0 = 0x00, + ADV7604_OP_FORMAT_MODE1 = 0x04, + ADV7604_OP_FORMAT_MODE2 = 0x08, }; enum adv7604_drive_strength { @@ -105,10 +91,10 @@ struct adv7604_platform_data { enum adv7604_ain_sel ain_sel; /* Bus rotation and reordering */ - enum adv7604_op_ch_sel op_ch_sel; + enum adv7604_bus_order bus_order; - /* Select output format */ - enum adv7604_op_format_sel op_format_sel; + /* Select output format mode */ + enum adv7604_op_format_mode_sel op_format_mode_sel; /* Configuration of the INT1 pin */ enum adv7604_int1_config int1_config; @@ -116,14 +102,12 @@ struct adv7604_platform_data { /* IO register 0x02 */ unsigned alt_gamma:1; unsigned op_656_range:1; - unsigned rgb_out:1; unsigned alt_data_sat:1; /* IO register 0x05 */ unsigned blank_data:1; unsigned insert_av_codes:1; unsigned replicate_av_codes:1; - unsigned invert_cbcr:1; /* IO register 0x06 */ unsigned inv_vs_pol:1; -- cgit v1.2.3 From 29fcec60d199e2cc3ac5fccd6f2a470b104d1235 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 31 Jan 2014 08:51:18 -0300 Subject: [media] v4l: subdev: Remove deprecated video-level DV timings operations The video enum_dv_timings and dv_timings_cap operations are deprecated and unused. Remove them. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Acked-by: Sakari Ailus Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 9fab013eea86..d7465725773d 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -338,12 +338,8 @@ struct v4l2_subdev_video_ops { struct v4l2_dv_timings *timings); int (*g_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings); - int (*enum_dv_timings)(struct v4l2_subdev *sd, - struct v4l2_enum_dv_timings *timings); int (*query_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings); - int (*dv_timings_cap)(struct v4l2_subdev *sd, - struct v4l2_dv_timings_cap *cap); int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code); int (*enum_mbus_fsizes)(struct v4l2_subdev *sd, -- cgit v1.2.3 From 05cacb176712cbbd3ae0331d92fe57741ef2d2ba Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 30 Jan 2014 16:32:21 -0300 Subject: [media] adv7604: Store I2C addresses and clients in arrays This allows replacing duplicate code blocks by loops over the arrays. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 248 +++++++++++++------------------------------- include/media/adv7604.h | 30 +++--- 2 files changed, 88 insertions(+), 190 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 154790923668..fc71c171ea18 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -159,18 +159,7 @@ struct adv7604_state { bool restart_stdi_once; /* i2c clients */ - struct i2c_client *i2c_avlink; - struct i2c_client *i2c_cec; - struct i2c_client *i2c_infoframe; - struct i2c_client *i2c_esdp; - struct i2c_client *i2c_dpp; - struct i2c_client *i2c_afe; - struct i2c_client *i2c_repeater; - struct i2c_client *i2c_edid; - struct i2c_client *i2c_hdmi; - struct i2c_client *i2c_test; - struct i2c_client *i2c_cp; - struct i2c_client *i2c_vdp; + struct i2c_client *i2c_clients[ADV7604_PAGE_MAX]; /* controls */ struct v4l2_ctrl *detect_tx_5v_ctrl; @@ -377,14 +366,18 @@ static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, return -EIO; } -static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) +static s32 adv_smbus_read_byte_data(struct adv7604_state *state, + enum adv7604_page page, u8 command) { - return adv_smbus_read_byte_data_check(client, command, true); + return adv_smbus_read_byte_data_check(state->i2c_clients[page], + command, true); } -static s32 adv_smbus_write_byte_data(struct i2c_client *client, - u8 command, u8 value) +static s32 adv_smbus_write_byte_data(struct adv7604_state *state, + enum adv7604_page page, u8 command, + u8 value) { + struct i2c_client *client = state->i2c_clients[page]; union i2c_smbus_data data; int err; int i; @@ -404,9 +397,11 @@ static s32 adv_smbus_write_byte_data(struct i2c_client *client, return err; } -static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client, - u8 command, unsigned length, const u8 *values) +static s32 adv_smbus_write_i2c_block_data(struct adv7604_state *state, + enum adv7604_page page, u8 command, + unsigned length, const u8 *values) { + struct i2c_client *client = state->i2c_clients[page]; union i2c_smbus_data data; if (length > I2C_SMBUS_BLOCK_MAX) @@ -422,16 +417,16 @@ static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client, static inline int io_read(struct v4l2_subdev *sd, u8 reg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(client, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_IO, reg); } static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(client, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_IO, reg, val); } static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -443,28 +438,28 @@ static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_avlink, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg); } static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_avlink, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val); } static inline int cec_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_cec, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_CEC, reg); } static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_cec, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_CEC, reg, val); } static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -476,70 +471,71 @@ static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_infoframe, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_INFOFRAME, reg); } static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_infoframe, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_INFOFRAME, + reg, val); } static inline int esdp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_esdp, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_ESDP, reg); } static inline int esdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_esdp, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_ESDP, reg, val); } static inline int dpp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_dpp, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_DPP, reg); } static inline int dpp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_dpp, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_DPP, reg, val); } static inline int afe_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_afe, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_AFE, reg); } static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_afe, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_AFE, reg, val); } static inline int rep_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_repeater, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_REP, reg); } static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_repeater, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_REP, reg, val); } static inline int rep_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -551,20 +547,20 @@ static inline int edid_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_edid, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_EDID, reg); } static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_edid, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_EDID, reg, val); } static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val) { struct adv7604_state *state = to_state(sd); - struct i2c_client *client = state->i2c_edid; + struct i2c_client *client = state->i2c_clients[ADV7604_PAGE_EDID]; u8 msgbuf0[1] = { 0 }; u8 msgbuf1[256]; struct i2c_msg msg[2] = { @@ -597,8 +593,8 @@ static inline int edid_write_block(struct v4l2_subdev *sd, v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) - err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, - I2C_SMBUS_BLOCK_MAX, val + i); + err = adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_EDID, + i, I2C_SMBUS_BLOCK_MAX, val + i); return err; } @@ -618,7 +614,7 @@ static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_hdmi, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_HDMI, reg); } static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) @@ -630,7 +626,7 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_HDMI, reg, val); } static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -642,21 +638,21 @@ static inline int test_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_test, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_TEST, reg); } static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_test, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_TEST, reg, val); } static inline int cp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_cp, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_CP, reg); } static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) @@ -668,7 +664,7 @@ static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_cp, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_CP, reg, val); } static inline int cp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) @@ -680,32 +676,15 @@ static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_vdp, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg); } static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_vdp, reg, val); -} - -enum { - ADV7604_PAGE_IO, - ADV7604_PAGE_AVLINK, - ADV7604_PAGE_CEC, - ADV7604_PAGE_INFOFRAME, - ADV7604_PAGE_ESDP, - ADV7604_PAGE_DPP, - ADV7604_PAGE_AFE, - ADV7604_PAGE_REP, - ADV7604_PAGE_EDID, - ADV7604_PAGE_HDMI, - ADV7604_PAGE_TEST, - ADV7604_PAGE_CP, - ADV7604_PAGE_VDP, - ADV7604_PAGE_TERM, -}; + return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val); +} #define ADV7604_REG(page, offset) (((page) << 8) | (offset)) #define ADV7604_REG_SEQ_TERM 0xffff @@ -721,36 +700,7 @@ static int adv7604_read_reg(struct v4l2_subdev *sd, unsigned int reg) reg &= 0xff; - switch (page) { - case ADV7604_PAGE_IO: - return io_read(sd, reg); - case ADV7604_PAGE_AVLINK: - return avlink_read(sd, reg); - case ADV7604_PAGE_CEC: - return cec_read(sd, reg); - case ADV7604_PAGE_INFOFRAME: - return infoframe_read(sd, reg); - case ADV7604_PAGE_ESDP: - return esdp_read(sd, reg); - case ADV7604_PAGE_DPP: - return dpp_read(sd, reg); - case ADV7604_PAGE_AFE: - return afe_read(sd, reg); - case ADV7604_PAGE_REP: - return rep_read(sd, reg); - case ADV7604_PAGE_EDID: - return edid_read(sd, reg); - case ADV7604_PAGE_HDMI: - return hdmi_read(sd, reg); - case ADV7604_PAGE_TEST: - return test_read(sd, reg); - case ADV7604_PAGE_CP: - return cp_read(sd, reg); - case ADV7604_PAGE_VDP: - return vdp_read(sd, reg); - } - - return -EINVAL; + return adv_smbus_read_byte_data(state, page, reg); } #endif @@ -764,36 +714,7 @@ static int adv7604_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) reg &= 0xff; - switch (page) { - case ADV7604_PAGE_IO: - return io_write(sd, reg, val); - case ADV7604_PAGE_AVLINK: - return avlink_write(sd, reg, val); - case ADV7604_PAGE_CEC: - return cec_write(sd, reg, val); - case ADV7604_PAGE_INFOFRAME: - return infoframe_write(sd, reg, val); - case ADV7604_PAGE_ESDP: - return esdp_write(sd, reg, val); - case ADV7604_PAGE_DPP: - return dpp_write(sd, reg, val); - case ADV7604_PAGE_AFE: - return afe_write(sd, reg, val); - case ADV7604_PAGE_REP: - return rep_write(sd, reg, val); - case ADV7604_PAGE_EDID: - return edid_write(sd, reg, val); - case ADV7604_PAGE_HDMI: - return hdmi_write(sd, reg, val); - case ADV7604_PAGE_TEST: - return test_write(sd, reg, val); - case ADV7604_PAGE_CP: - return cp_write(sd, reg, val); - case ADV7604_PAGE_VDP: - return vdp_write(sd, reg, val); - } - - return -EINVAL; + return adv_smbus_write_byte_data(state, page, reg, val); } static void adv7604_write_reg_seq(struct v4l2_subdev *sd, @@ -1064,7 +985,6 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, const struct v4l2_bt_timings *bt) { struct adv7604_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); u32 width = htotal(bt); u32 height = vtotal(bt); u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; @@ -1090,7 +1010,8 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ /* IO-map reg. 0x16 and 0x17 should be written in sequence */ - if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) + if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_IO, + 0x16, 2, pll)) v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); /* active video - horizontal timing */ @@ -1141,7 +1062,8 @@ static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 off offset_buf[3] = offset_c & 0x0ff; /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf)) + if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP, + 0x77, 4, offset_buf)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); } @@ -1170,7 +1092,8 @@ static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, gain_buf[3] = ((gain_c & 0x0ff)); /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf)) + if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP, + 0x73, 4, gain_buf)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); } @@ -2571,30 +2494,12 @@ static void adv7611_setup_irqs(struct v4l2_subdev *sd) static void adv7604_unregister_clients(struct adv7604_state *state) { - if (state->i2c_avlink) - i2c_unregister_device(state->i2c_avlink); - if (state->i2c_cec) - i2c_unregister_device(state->i2c_cec); - if (state->i2c_infoframe) - i2c_unregister_device(state->i2c_infoframe); - if (state->i2c_esdp) - i2c_unregister_device(state->i2c_esdp); - if (state->i2c_dpp) - i2c_unregister_device(state->i2c_dpp); - if (state->i2c_afe) - i2c_unregister_device(state->i2c_afe); - if (state->i2c_repeater) - i2c_unregister_device(state->i2c_repeater); - if (state->i2c_edid) - i2c_unregister_device(state->i2c_edid); - if (state->i2c_hdmi) - i2c_unregister_device(state->i2c_hdmi); - if (state->i2c_test) - i2c_unregister_device(state->i2c_test); - if (state->i2c_cp) - i2c_unregister_device(state->i2c_cp); - if (state->i2c_vdp) - i2c_unregister_device(state->i2c_vdp); + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) { + if (state->i2c_clients[i]) + i2c_unregister_device(state->i2c_clients[i]); + } } static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, @@ -2761,6 +2666,7 @@ static int adv7604_probe(struct i2c_client *client, } state->info = &adv7604_chip_info[id->driver_data]; + state->i2c_clients[ADV7604_PAGE_IO] = client; /* initialize variables */ state->restart_stdi_once = true; @@ -2852,34 +2758,20 @@ static int adv7604_probe(struct i2c_client *client, goto err_hdl; } - state->i2c_cec = adv7604_dummy_client(sd, pdata->i2c_cec, 0xf4); - state->i2c_infoframe = adv7604_dummy_client(sd, pdata->i2c_infoframe, 0xf5); - state->i2c_afe = adv7604_dummy_client(sd, pdata->i2c_afe, 0xf8); - state->i2c_repeater = adv7604_dummy_client(sd, pdata->i2c_repeater, 0xf9); - state->i2c_edid = adv7604_dummy_client(sd, pdata->i2c_edid, 0xfa); - state->i2c_hdmi = adv7604_dummy_client(sd, pdata->i2c_hdmi, 0xfb); - state->i2c_cp = adv7604_dummy_client(sd, pdata->i2c_cp, 0xfd); - if (!state->i2c_cec || !state->i2c_infoframe || !state->i2c_afe || - !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || - !state->i2c_cp) { - err = -ENOMEM; - v4l2_err(sd, "failed to create digital i2c clients\n"); - goto err_i2c; - } + for (i = 1; i < ADV7604_PAGE_MAX; ++i) { + if (!(BIT(i) & state->info->page_mask)) + continue; - if (adv7604_has_afe(state)) { - state->i2c_avlink = adv7604_dummy_client(sd, pdata->i2c_avlink, 0xf3); - state->i2c_esdp = adv7604_dummy_client(sd, pdata->i2c_esdp, 0xf6); - state->i2c_dpp = adv7604_dummy_client(sd, pdata->i2c_dpp, 0xf7); - state->i2c_test = adv7604_dummy_client(sd, pdata->i2c_test, 0xfc); - state->i2c_vdp = adv7604_dummy_client(sd, pdata->i2c_vdp, 0xfe); - if (!state->i2c_avlink || !state->i2c_esdp || !state->i2c_dpp || - !state->i2c_test || !state->i2c_vdp) { + state->i2c_clients[i] = + adv7604_dummy_client(sd, pdata->i2c_addresses[i], + 0xf2 + i); + if (state->i2c_clients[i] == NULL) { err = -ENOMEM; - v4l2_err(sd, "failed to create analog i2c clients\n"); + v4l2_err(sd, "failed to create i2c client %u\n", i); goto err_i2c; } } + /* work queues */ state->work_queues = create_singlethread_workqueue(client->name); if (!state->work_queues) { diff --git a/include/media/adv7604.h b/include/media/adv7604.h index d8b2cb8f5dce..276135b7faa3 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -79,6 +79,23 @@ enum adv7604_int1_config { ADV7604_INT1_CONFIG_DISABLED, }; +enum adv7604_page { + ADV7604_PAGE_IO, + ADV7604_PAGE_AVLINK, + ADV7604_PAGE_CEC, + ADV7604_PAGE_INFOFRAME, + ADV7604_PAGE_ESDP, + ADV7604_PAGE_DPP, + ADV7604_PAGE_AFE, + ADV7604_PAGE_REP, + ADV7604_PAGE_EDID, + ADV7604_PAGE_HDMI, + ADV7604_PAGE_TEST, + ADV7604_PAGE_CP, + ADV7604_PAGE_VDP, + ADV7604_PAGE_MAX, +}; + /* Platform dependent definition */ struct adv7604_platform_data { /* DIS_PWRDNB: 1 if the PWRDNB pin is unused and unconnected */ @@ -125,18 +142,7 @@ struct adv7604_platform_data { unsigned hdmi_free_run_mode; /* i2c addresses: 0 == use default */ - u8 i2c_avlink; - u8 i2c_cec; - u8 i2c_infoframe; - u8 i2c_esdp; - u8 i2c_dpp; - u8 i2c_afe; - u8 i2c_repeater; - u8 i2c_edid; - u8 i2c_hdmi; - u8 i2c_test; - u8 i2c_cp; - u8 i2c_vdp; + u8 i2c_addresses[ADV7604_PAGE_MAX]; }; enum adv7604_pad { -- cgit v1.2.3 From 5ef54b5955acdcc63ac8ad7cf0aef3d16070773a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 31 Jan 2014 10:57:27 -0300 Subject: [media] adv7604: Specify the default input through platform data And set input routing when initializing the device. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 7 +++++++ include/media/adv7604.h | 2 ++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index b14dc7d4b7bb..342d73d3989b 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2441,6 +2441,13 @@ static int adv7604_core_init(struct v4l2_subdev *sd) disable_input(sd); + if (pdata->default_input >= 0 && + pdata->default_input < state->source_pad) { + state->selected_input = pdata->default_input; + select_input(sd); + enable_input(sd); + } + /* power */ io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */ io_write(sd, 0x0b, 0x44); /* Power down ESDP block */ diff --git a/include/media/adv7604.h b/include/media/adv7604.h index 276135b7faa3..40b4ae04ff8e 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -104,6 +104,8 @@ struct adv7604_platform_data { /* DIS_CABLE_DET_RST: 1 if the 5V pins are unused and unconnected */ unsigned disable_cable_det_rst:1; + int default_input; + /* Analog input muxing mode */ enum adv7604_ain_sel ain_sel; -- cgit v1.2.3 From 1b5ab8755ec7b6ac83bf8d09c9f908d94e36b9a4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Feb 2014 19:57:56 -0300 Subject: [media] adv7604: Add LLC polarity configuration Add an inv_llc_pol field to platform data to control the clock polarity. Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 3 ++- include/media/adv7604.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 1a2797bfd12e..1778d320272e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2467,7 +2467,8 @@ static int adv7604_core_init(struct v4l2_subdev *sd) cp_write(sd, 0x69, 0x30); /* Enable CP CSC */ /* VS, HS polarities */ - io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | pdata->inv_hs_pol << 1); + io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | + pdata->inv_hs_pol << 1 | pdata->inv_llc_pol); /* Adjust drive strength */ io_write(sd, 0x14, 0x40 | pdata->dr_str_data << 4 | diff --git a/include/media/adv7604.h b/include/media/adv7604.h index 40b4ae04ff8e..aa1c4477722d 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -131,6 +131,7 @@ struct adv7604_platform_data { /* IO register 0x06 */ unsigned inv_vs_pol:1; unsigned inv_hs_pol:1; + unsigned inv_llc_pol:1; /* IO register 0x14 */ enum adv7604_drive_strength dr_str_data; -- cgit v1.2.3 From bfed1074f213051e94648bfad0d0611a16d81366 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Thu, 22 May 2014 07:23:19 +0900 Subject: clk: exynos5250: Add missing sysmmu clocks for DISP and ISP blocks This patch adds the missing sysmmu clocks for Display and ISP blocks. Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Acked-by: Tomasz Figa Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos5250.c | 35 ++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos5250.h | 16 ++++++++++++++++ 2 files changed, 51 insertions(+) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 88488596c00b..870e18b9a687 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -28,6 +28,8 @@ #define MPLL_CON0 0x4100 #define SRC_CORE1 0x4204 #define GATE_IP_ACP 0x8800 +#define GATE_IP_ISP0 0xc800 +#define GATE_IP_ISP1 0xc804 #define CPLL_LOCK 0x10020 #define EPLL_LOCK 0x10030 #define VPLL_LOCK 0x10040 @@ -145,6 +147,8 @@ static unsigned long exynos5250_clk_regs[] __initdata = { PLL_DIV2_SEL, GATE_IP_DISP1, GATE_IP_ACP, + GATE_IP_ISP0, + GATE_IP_ISP1, }; static int exynos5250_clk_suspend(void) @@ -202,6 +206,7 @@ PNAME(mout_aclk400_p) = { "mout_aclk400_g3d_mid", "mout_gpll" }; PNAME(mout_aclk200_sub_p) = { "fin_pll", "div_aclk200" }; PNAME(mout_aclk266_sub_p) = { "fin_pll", "div_aclk266" }; PNAME(mout_aclk333_sub_p) = { "fin_pll", "div_aclk333" }; +PNAME(mout_aclk400_isp_sub_p) = { "fin_pll", "div_aclk400_isp" }; PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" }; PNAME(mout_usb3_p) = { "mout_mpll_user", "mout_cpll" }; PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m", @@ -281,6 +286,7 @@ static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { MUX(0, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1), MUX(0, "mout_aclk400_g3d_mid", mout_aclk200_p, SRC_TOP0, 20, 1), + MUX(0, "mout_aclk400_isp", mout_aclk200_p, SRC_TOP1, 24, 1), MUX(0, "mout_aclk400_g3d", mout_aclk400_p, SRC_TOP1, 28, 1), MUX(0, "mout_cpll", mout_cpll_p, SRC_TOP2, 8, 1), @@ -292,6 +298,9 @@ static struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { MUX(0, "mout_aclk200_disp1_sub", mout_aclk200_sub_p, SRC_TOP3, 4, 1), MUX(0, "mout_aclk266_gscl_sub", mout_aclk266_sub_p, SRC_TOP3, 8, 1), + MUX(0, "mout_aclk_266_isp_sub", mout_aclk266_sub_p, SRC_TOP3, 16, 1), + MUX(0, "mout_aclk_400_isp_sub", mout_aclk400_isp_sub_p, + SRC_TOP3, 20, 1), MUX(0, "mout_aclk333_sub", mout_aclk333_sub_p, SRC_TOP3, 24, 1), MUX(0, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4), @@ -364,6 +373,7 @@ static struct samsung_div_clock exynos5250_div_clks[] __initdata = { DIV(0, "div_aclk400_g3d", "mout_aclk400_g3d", DIV_TOP0, 24, 3), + DIV(0, "div_aclk400_isp", "mout_aclk400_isp", DIV_TOP1, 20, 3), DIV(0, "div_aclk66_pre", "mout_mpll_user", DIV_TOP1, 24, 3), DIV(0, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4), @@ -629,6 +639,31 @@ static struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { GATE(CLK_WDT, "wdt", "div_aclk66", GATE_IP_PERIS, 19, 0, 0), GATE(CLK_RTC, "rtc", "div_aclk66", GATE_IP_PERIS, 20, 0, 0), GATE(CLK_TMU, "tmu", "div_aclk66", GATE_IP_PERIS, 21, 0, 0), + GATE(CLK_SMMU_TV, "smmu_tv", "mout_aclk200_disp1_sub", + GATE_IP_DISP1, 2, 0, 0), + GATE(CLK_SMMU_FIMD1, "smmu_fimd1", "mout_aclk200_disp1_sub", + GATE_IP_DISP1, 8, 0, 0), + GATE(CLK_SMMU_2D, "smmu_2d", "div_aclk200", GATE_IP_ACP, 7, 0, 0), + GATE(CLK_SMMU_FIMC_ISP, "smmu_fimc_isp", "mout_aclk_266_isp_sub", + GATE_IP_ISP0, 8, 0, 0), + GATE(CLK_SMMU_FIMC_DRC, "smmu_fimc_drc", "mout_aclk_266_isp_sub", + GATE_IP_ISP0, 9, 0, 0), + GATE(CLK_SMMU_FIMC_FD, "smmu_fimc_fd", "mout_aclk_266_isp_sub", + GATE_IP_ISP0, 10, 0, 0), + GATE(CLK_SMMU_FIMC_SCC, "smmu_fimc_scc", "mout_aclk_266_isp_sub", + GATE_IP_ISP0, 11, 0, 0), + GATE(CLK_SMMU_FIMC_SCP, "smmu_fimc_scp", "mout_aclk_266_isp_sub", + GATE_IP_ISP0, 12, 0, 0), + GATE(CLK_SMMU_FIMC_MCU, "smmu_fimc_mcu", "mout_aclk_400_isp_sub", + GATE_IP_ISP0, 13, 0, 0), + GATE(CLK_SMMU_FIMC_ODC, "smmu_fimc_odc", "mout_aclk_266_isp_sub", + GATE_IP_ISP1, 4, 0, 0), + GATE(CLK_SMMU_FIMC_DIS0, "smmu_fimc_dis0", "mout_aclk_266_isp_sub", + GATE_IP_ISP1, 5, 0, 0), + GATE(CLK_SMMU_FIMC_DIS1, "smmu_fimc_dis1", "mout_aclk_266_isp_sub", + GATE_IP_ISP1, 6, 0, 0), + GATE(CLK_SMMU_FIMC_3DNR, "smmu_fimc_3dnr", "mout_aclk_266_isp_sub", + GATE_IP_ISP1, 7, 0, 0), }; static struct samsung_pll_rate_table vpll_24mhz_tbl[] __initdata = { diff --git a/include/dt-bindings/clock/exynos5250.h b/include/dt-bindings/clock/exynos5250.h index 415d447e6e29..be6e97c54f54 100644 --- a/include/dt-bindings/clock/exynos5250.h +++ b/include/dt-bindings/clock/exynos5250.h @@ -152,6 +152,22 @@ #define CLK_SMMU_MDMA0 347 #define CLK_SSS 348 #define CLK_G3D 349 +#define CLK_SMMU_TV 350 +#define CLK_SMMU_FIMD1 351 +#define CLK_SMMU_2D 352 +#define CLK_SMMU_FIMC_ISP 353 +#define CLK_SMMU_FIMC_DRC 354 +#define CLK_SMMU_FIMC_SCC 355 +#define CLK_SMMU_FIMC_SCP 356 +#define CLK_SMMU_FIMC_FD 357 +#define CLK_SMMU_FIMC_MCU 358 +#define CLK_SMMU_FIMC_ODC 359 +#define CLK_SMMU_FIMC_DIS0 360 +#define CLK_SMMU_FIMC_DIS1 361 +#define CLK_SMMU_FIMC_3DNR 362 +#define CLK_SMMU_FIMC_LITE0 363 +#define CLK_SMMU_FIMC_LITE1 364 +#define CLK_CAMIF_TOP 365 /* mux clocks */ #define CLK_MOUT_HDMI 1024 -- cgit v1.2.3 From fbebf59778600488147744cdf7d7c20d22531025 Mon Sep 17 00:00:00 2001 From: srinik Date: Thu, 15 May 2014 11:28:44 +0100 Subject: ARM: 8057/1: amba: Add Qualcomm vendor ID. This patch adds Qualcomm amba vendor Id to the list. This ID is used in mmci driver. The ID selected in same lines like 0x41 is "A" for ARM, 0x51 is "Q" for Qualcomm. As there are no physical register on Qcom SOC for amba vendor id, this is a fake ID assigned based on "Q" prefix from Qualcomm. Signed-off-by: Srinivas Kandagatla Acked-by: Linus Walleij Signed-off-by: Russell King --- include/linux/amba/bus.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 63b5eff0a80f..fdd7e1b61f60 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -47,6 +47,7 @@ struct amba_driver { enum amba_vendor { AMBA_VENDOR_ARM = 0x41, AMBA_VENDOR_ST = 0x80, + AMBA_VENDOR_QCOM = 0x51, }; extern struct bus_type amba_bustype; -- cgit v1.2.3 From 72e6ae285a1dbff553734985bedadf409d99c02d Mon Sep 17 00:00:00 2001 From: Victor Kamensky Date: Tue, 29 Apr 2014 04:20:52 +0100 Subject: ARM: 8043/1: uprobes need icache flush after xol write After instruction write into xol area, on ARM V7 architecture code need to flush dcache and icache to sync them up for given set of addresses. Having just 'flush_dcache_page(page)' call is not enough - it is possible to have stale instruction sitting in icache for given xol area slot address. Introduce arch_uprobe_ixol_copy weak function that by default calls uprobes copy_to_page function and than flush_dcache_page function and on ARM define new one that handles xol slot copy in ARM specific way flush_uprobe_xol_access function shares/reuses implementation with/of flush_ptrace_access function and takes care of writing instruction to user land address space on given variety of different cache types on ARM CPUs. Because flush_uprobe_xol_access does not have vma around flush_ptrace_access was split into two parts. First that retrieves set of condition from vma and common that receives those conditions as flags. Note ARM cache flush function need kernel address through which instruction write happened, so instead of using uprobes copy_to_page function changed code to explicitly map page and do memcpy. Note arch_uprobe_copy_ixol function, in similar way as copy_to_user_page function, has preempt_disable/preempt_enable. Signed-off-by: Victor Kamensky Acked-by: Oleg Nesterov Reviewed-by: David A. Long Signed-off-by: Russell King --- arch/arm/include/asm/cacheflush.h | 2 ++ arch/arm/kernel/uprobes.c | 20 ++++++++++++++++++++ arch/arm/mm/flush.c | 33 ++++++++++++++++++++++++++++----- include/linux/uprobes.h | 3 +++ kernel/events/uprobes.c | 25 +++++++++++++++++-------- 5 files changed, 70 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index 00af9fe435e6..fd43f7f55b70 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -487,4 +487,6 @@ int set_memory_rw(unsigned long addr, int numpages); int set_memory_x(unsigned long addr, int numpages); int set_memory_nx(unsigned long addr, int numpages); +void flush_uprobe_xol_access(struct page *page, unsigned long uaddr, + void *kaddr, unsigned long len); #endif diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c index f9bacee973bf..56adf9c1fde0 100644 --- a/arch/arm/kernel/uprobes.c +++ b/arch/arm/kernel/uprobes.c @@ -113,6 +113,26 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, return 0; } +void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, + void *src, unsigned long len) +{ + void *xol_page_kaddr = kmap_atomic(page); + void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK); + + preempt_disable(); + + /* Initialize the slot */ + memcpy(dst, src, len); + + /* flush caches (dcache/icache) */ + flush_uprobe_xol_access(page, vaddr, dst, len); + + preempt_enable(); + + kunmap_atomic(xol_page_kaddr); +} + + int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) { struct uprobe_task *utask = current->utask; diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 3387e60e4ea3..43d54f5b26b9 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -104,17 +104,20 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig #define flush_icache_alias(pfn,vaddr,len) do { } while (0) #endif +#define FLAG_PA_IS_EXEC 1 +#define FLAG_PA_CORE_IN_MM 2 + static void flush_ptrace_access_other(void *args) { __flush_icache_all(); } -static -void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, - unsigned long uaddr, void *kaddr, unsigned long len) +static inline +void __flush_ptrace_access(struct page *page, unsigned long uaddr, void *kaddr, + unsigned long len, unsigned int flags) { if (cache_is_vivt()) { - if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) { + if (flags & FLAG_PA_CORE_IN_MM) { unsigned long addr = (unsigned long)kaddr; __cpuc_coherent_kern_range(addr, addr + len); } @@ -128,7 +131,7 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, } /* VIPT non-aliasing D-cache */ - if (vma->vm_flags & VM_EXEC) { + if (flags & FLAG_PA_IS_EXEC) { unsigned long addr = (unsigned long)kaddr; if (icache_is_vipt_aliasing()) flush_icache_alias(page_to_pfn(page), uaddr, len); @@ -140,6 +143,26 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, } } +static +void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, + unsigned long uaddr, void *kaddr, unsigned long len) +{ + unsigned int flags = 0; + if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) + flags |= FLAG_PA_CORE_IN_MM; + if (vma->vm_flags & VM_EXEC) + flags |= FLAG_PA_IS_EXEC; + __flush_ptrace_access(page, uaddr, kaddr, len, flags); +} + +void flush_uprobe_xol_access(struct page *page, unsigned long uaddr, + void *kaddr, unsigned long len) +{ + unsigned int flags = FLAG_PA_CORE_IN_MM|FLAG_PA_IS_EXEC; + + __flush_ptrace_access(page, uaddr, kaddr, len, flags); +} + /* * Copy user data from/to a page which is mapped into a different * processes address space. Really, we want to allow our "user diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index edff2b97b864..c52f827ba6ce 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -32,6 +32,7 @@ struct vm_area_struct; struct mm_struct; struct inode; struct notifier_block; +struct page; #define UPROBE_HANDLER_REMOVE 1 #define UPROBE_HANDLER_MASK 1 @@ -127,6 +128,8 @@ extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned l extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); extern bool __weak arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs); +extern void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, + void *src, unsigned long len); #else /* !CONFIG_UPROBES */ struct uprobes_state { }; diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 04709b66369d..4968213c63fa 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1296,14 +1296,8 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe) if (unlikely(!xol_vaddr)) return 0; - /* Initialize the slot */ - copy_to_page(area->page, xol_vaddr, - &uprobe->arch.ixol, sizeof(uprobe->arch.ixol)); - /* - * We probably need flush_icache_user_range() but it needs vma. - * This should work on supported architectures too. - */ - flush_dcache_page(area->page); + arch_uprobe_copy_ixol(area->page, xol_vaddr, + &uprobe->arch.ixol, sizeof(uprobe->arch.ixol)); return xol_vaddr; } @@ -1346,6 +1340,21 @@ static void xol_free_insn_slot(struct task_struct *tsk) } } +void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, + void *src, unsigned long len) +{ + /* Initialize the slot */ + copy_to_page(page, vaddr, src, len); + + /* + * We probably need flush_icache_user_range() but it needs vma. + * This should work on most of architectures by default. If + * architecture needs to do something different it can define + * its own version of the function. + */ + flush_dcache_page(page); +} + /** * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs * @regs: Reflects the saved state of the task after it has hit a breakpoint -- cgit v1.2.3 From 59201052dff17d0cc8bc60bc9e89ad4924906ee8 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 23 May 2014 12:58:06 +0900 Subject: drm/ttm: fix kerneldoc of ttm_bo_create The kerneldoc header of ttm_bo_create() was referring to another (nonexisting) function and had a few obsolete or incorrect arguments. Signed-off-by: Alexandre Courbot Reviewed-by: David Herrmann Signed-off-by: Dave Airlie --- include/drm/ttm/ttm_bo_api.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index ee127ec33c60..7526c5bf5610 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -485,13 +485,12 @@ extern int ttm_bo_init(struct ttm_bo_device *bdev, void (*destroy) (struct ttm_buffer_object *)); /** - * ttm_bo_synccpu_object_init + * ttm_bo_create * * @bdev: Pointer to a ttm_bo_device struct. - * @bo: Pointer to a ttm_buffer_object to be initialized. * @size: Requested size of buffer object. * @type: Requested type of buffer object. - * @flags: Initial placement flags. + * @placement: Initial placement. * @page_alignment: Data alignment in pages. * @interruptible: If needing to sleep while waiting for GPU resources, * sleep interruptible. -- cgit v1.2.3 From 1a5f0c13d1a8808c2bdd00630818ed491e1719f5 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 23 May 2014 14:33:12 +0300 Subject: mac80211: add a single-transaction driver op to switch contexts In some cases, when the driver is already using all the channel contexts it can handle at once, we have to do an in-place switch (ie. we cannot afford using an extra context temporarily for the transaction). But some drivers may not support switching the channel context assigned to a vif on the fly (ie. without unassigning and assigning it) while others may only work if the context is changed on the fly, without unassigning it first. To allow these different scenarios, add a new driver operation that let's the driver decide how to handle an in-place switch. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 46 +++++++++++++++++++++++++ net/mac80211/driver-ops.h | 53 +++++++++++++++++++++++++++++ net/mac80211/trace.h | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 2c78997bc48d..421b6ecb4b2c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -188,6 +188,43 @@ struct ieee80211_chanctx_conf { u8 drv_priv[0] __aligned(sizeof(void *)); }; +/** + * enum ieee80211_chanctx_switch_mode - channel context switch mode + * @CHANCTX_SWMODE_REASSIGN_VIF: Both old and new contexts already + * exist (and will continue to exist), but the virtual interface + * needs to be switched from one to the other. + * @CHANCTX_SWMODE_SWAP_CONTEXTS: The old context exists but will stop + * to exist with this call, the new context doesn't exist but + * will be active after this call, the virtual interface switches + * from the old to the new (note that the driver may of course + * implement this as an on-the-fly chandef switch of the existing + * hardware context, but the mac80211 pointer for the old context + * will cease to exist and only the new one will later be used + * for changes/removal.) + */ +enum ieee80211_chanctx_switch_mode { + CHANCTX_SWMODE_REASSIGN_VIF, + CHANCTX_SWMODE_SWAP_CONTEXTS, +}; + +/** + * struct ieee80211_vif_chanctx_switch - vif chanctx switch information + * + * This is structure is used to pass information about a vif that + * needs to switch from one chanctx to another. The + * &ieee80211_chanctx_switch_mode defines how the switch should be + * done. + * + * @vif: the vif that should be switched from old_ctx to new_ctx + * @old_ctx: the old context to which the vif was assigned + * @new_ctx: the new context to which the vif must be assigned + */ +struct ieee80211_vif_chanctx_switch { + struct ieee80211_vif *vif; + struct ieee80211_chanctx_conf *old_ctx; + struct ieee80211_chanctx_conf *new_ctx; +}; + /** * enum ieee80211_bss_change - BSS change notification flags * @@ -2736,6 +2773,11 @@ enum ieee80211_roc_type { * to vif. Possible use is for hw queue remapping. * @unassign_vif_chanctx: Notifies device driver about channel context being * unbound from vif. + * @switch_vif_chanctx: switch a number of vifs from one chanctx to + * another, as specified in the list of + * @ieee80211_vif_chanctx_switch passed to the driver, according + * to the mode defined in &ieee80211_chanctx_switch_mode. + * * @start_ap: Start operation on the AP interface, this is called after all the * information in bss_conf is set and beacon can be retrieved. A channel * context is bound before this is called. Note that if the driver uses @@ -2952,6 +2994,10 @@ struct ieee80211_ops { void (*unassign_vif_chanctx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx); + int (*switch_vif_chanctx)(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode); void (*restart_complete)(struct ieee80211_hw *hw); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 696ef78b1fb7..bd782dcffcc7 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1048,6 +1048,59 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline int +drv_switch_vif_chanctx(struct ieee80211_local *local, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + int ret = 0; + int i; + + if (!local->ops->switch_vif_chanctx) + return -EOPNOTSUPP; + + for (i = 0; i < n_vifs; i++) { + struct ieee80211_chanctx *new_ctx = + container_of(vifs[i].new_ctx, + struct ieee80211_chanctx, + conf); + struct ieee80211_chanctx *old_ctx = + container_of(vifs[i].old_ctx, + struct ieee80211_chanctx, + conf); + + WARN_ON_ONCE(!old_ctx->driver_present); + WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && + new_ctx->driver_present) || + (mode == CHANCTX_SWMODE_REASSIGN_VIF && + !new_ctx->driver_present)); + } + + trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); + ret = local->ops->switch_vif_chanctx(&local->hw, + vifs, n_vifs, mode); + trace_drv_return_int(local, ret); + + if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { + for (i = 0; i < n_vifs; i++) { + struct ieee80211_chanctx *new_ctx = + container_of(vifs[i].new_ctx, + struct ieee80211_chanctx, + conf); + struct ieee80211_chanctx *old_ctx = + container_of(vifs[i].old_ctx, + struct ieee80211_chanctx, + conf); + + new_ctx->driver_present = true; + old_ctx->driver_present = false; + } + } + + return ret; +} + static inline int drv_start_ap(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 942f64b8ce0e..b22d6969cde9 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1389,6 +1389,91 @@ TRACE_EVENT(drv_change_chanctx, ) ); +#if !defined(__TRACE_VIF_ENTRY) +#define __TRACE_VIF_ENTRY +struct trace_vif_entry { + enum nl80211_iftype vif_type; + bool p2p; + char vif_name[IFNAMSIZ]; +} __packed; + +struct trace_chandef_entry { + u32 control_freq; + u32 chan_width; + u32 center_freq1; + u32 center_freq2; +} __packed; + +struct trace_switch_entry { + struct trace_vif_entry vif; + struct trace_chandef_entry old_chandef; + struct trace_chandef_entry new_chandef; +} __packed; + +#define SWITCH_ENTRY_ASSIGN(to, from) local_vifs[i].to = vifs[i].from +#endif + +TRACE_EVENT(drv_switch_vif_chanctx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, enum ieee80211_chanctx_switch_mode mode), + TP_ARGS(local, vifs, n_vifs, mode), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, n_vifs) + __field(u32, mode) + __dynamic_array(u8, vifs, + sizeof(struct trace_switch_entry) * n_vifs) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->n_vifs = n_vifs; + __entry->mode = mode; + { + struct trace_switch_entry *local_vifs = + __get_dynamic_array(vifs); + int i; + + for (i = 0; i < n_vifs; i++) { + struct ieee80211_sub_if_data *sdata; + + sdata = container_of(vifs[i].vif, + struct ieee80211_sub_if_data, + vif); + + SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type); + SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p); + strncpy(local_vifs[i].vif.vif_name, + sdata->name, + sizeof(local_vifs[i].vif.vif_name)); + SWITCH_ENTRY_ASSIGN(old_chandef.control_freq, + old_ctx->def.chan->center_freq); + SWITCH_ENTRY_ASSIGN(old_chandef.chan_width, + old_ctx->def.width); + SWITCH_ENTRY_ASSIGN(old_chandef.center_freq1, + old_ctx->def.center_freq1); + SWITCH_ENTRY_ASSIGN(old_chandef.center_freq2, + old_ctx->def.center_freq2); + SWITCH_ENTRY_ASSIGN(new_chandef.control_freq, + new_ctx->def.chan->center_freq); + SWITCH_ENTRY_ASSIGN(new_chandef.chan_width, + new_ctx->def.width); + SWITCH_ENTRY_ASSIGN(new_chandef.center_freq1, + new_ctx->def.center_freq1); + SWITCH_ENTRY_ASSIGN(new_chandef.center_freq2, + new_ctx->def.center_freq2); + } + } + ), + + TP_printk( + LOCAL_PR_FMT " n_vifs:%d mode:%d", + LOCAL_PR_ARG, __entry->n_vifs, __entry->mode + ) +); + DECLARE_EVENT_CLASS(local_sdata_chanctx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, -- cgit v1.2.3 From d25a2a16f0889de4a1cd8639896f35dc9465f6f5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 2 Apr 2014 12:47:37 +0200 Subject: iommu: Add driver for Renesas VMSA-compatible IPMMU Signed-off-by: Laurent Pinchart Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 12 + drivers/iommu/Makefile | 1 + drivers/iommu/ipmmu-vmsa.c | 1070 ++++++++++++++++++++++++++++++ include/linux/platform_data/ipmmu-vmsa.h | 24 + 4 files changed, 1107 insertions(+) create mode 100644 drivers/iommu/ipmmu-vmsa.c create mode 100644 include/linux/platform_data/ipmmu-vmsa.h (limited to 'include') diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index df56e4c74a7e..a22b537caacd 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -272,6 +272,18 @@ config SHMOBILE_IOMMU_L1SIZE default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB +config IPMMU_VMSA + bool "Renesas VMSA-compatible IPMMU" + depends on ARM_LPAE + depends on ARCH_SHMOBILE || COMPILE_TEST + select IOMMU_API + select ARM_DMA_USE_IOMMU + help + Support for the Renesas VMSA-compatible IPMMU Renesas found in the + R-Mobile APE6 and R-Car H2/M2 SoCs. + + If unsure, say N. + config SPAPR_TCE_IOMMU bool "sPAPR TCE IOMMU Support" depends on PPC_POWERNV || PPC_PSERIES diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 5d58bf16e9e3..8893bad048e0 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o obj-$(CONFIG_ARM_SMMU) += arm-smmu.o obj-$(CONFIG_DMAR_TABLE) += dmar.o obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o +obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o obj-$(CONFIG_OMAP_IOMMU) += omap-iommu2.o diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c new file mode 100644 index 000000000000..b084530babf4 --- /dev/null +++ b/drivers/iommu/ipmmu-vmsa.c @@ -0,0 +1,1070 @@ +/* + * IPMMU VMSA + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct ipmmu_vmsa_device { + struct device *dev; + void __iomem *base; + struct list_head list; + + const struct ipmmu_vmsa_platform_data *pdata; + unsigned int num_utlbs; + + struct dma_iommu_mapping *mapping; +}; + +struct ipmmu_vmsa_domain { + struct ipmmu_vmsa_device *mmu; + struct iommu_domain *io_domain; + + unsigned int context_id; + spinlock_t lock; /* Protects mappings */ + pgd_t *pgd; +}; + +static DEFINE_SPINLOCK(ipmmu_devices_lock); +static LIST_HEAD(ipmmu_devices); + +#define TLB_LOOP_TIMEOUT 100 /* 100us */ + +/* ----------------------------------------------------------------------------- + * Registers Definition + */ + +#define IM_CTX_SIZE 0x40 + +#define IMCTR 0x0000 +#define IMCTR_TRE (1 << 17) +#define IMCTR_AFE (1 << 16) +#define IMCTR_RTSEL_MASK (3 << 4) +#define IMCTR_RTSEL_SHIFT 4 +#define IMCTR_TREN (1 << 3) +#define IMCTR_INTEN (1 << 2) +#define IMCTR_FLUSH (1 << 1) +#define IMCTR_MMUEN (1 << 0) + +#define IMCAAR 0x0004 + +#define IMTTBCR 0x0008 +#define IMTTBCR_EAE (1 << 31) +#define IMTTBCR_PMB (1 << 30) +#define IMTTBCR_SH1_NON_SHAREABLE (0 << 28) +#define IMTTBCR_SH1_OUTER_SHAREABLE (2 << 28) +#define IMTTBCR_SH1_INNER_SHAREABLE (3 << 28) +#define IMTTBCR_SH1_MASK (3 << 28) +#define IMTTBCR_ORGN1_NC (0 << 26) +#define IMTTBCR_ORGN1_WB_WA (1 << 26) +#define IMTTBCR_ORGN1_WT (2 << 26) +#define IMTTBCR_ORGN1_WB (3 << 26) +#define IMTTBCR_ORGN1_MASK (3 << 26) +#define IMTTBCR_IRGN1_NC (0 << 24) +#define IMTTBCR_IRGN1_WB_WA (1 << 24) +#define IMTTBCR_IRGN1_WT (2 << 24) +#define IMTTBCR_IRGN1_WB (3 << 24) +#define IMTTBCR_IRGN1_MASK (3 << 24) +#define IMTTBCR_TSZ1_MASK (7 << 16) +#define IMTTBCR_TSZ1_SHIFT 16 +#define IMTTBCR_SH0_NON_SHAREABLE (0 << 12) +#define IMTTBCR_SH0_OUTER_SHAREABLE (2 << 12) +#define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12) +#define IMTTBCR_SH0_MASK (3 << 12) +#define IMTTBCR_ORGN0_NC (0 << 10) +#define IMTTBCR_ORGN0_WB_WA (1 << 10) +#define IMTTBCR_ORGN0_WT (2 << 10) +#define IMTTBCR_ORGN0_WB (3 << 10) +#define IMTTBCR_ORGN0_MASK (3 << 10) +#define IMTTBCR_IRGN0_NC (0 << 8) +#define IMTTBCR_IRGN0_WB_WA (1 << 8) +#define IMTTBCR_IRGN0_WT (2 << 8) +#define IMTTBCR_IRGN0_WB (3 << 8) +#define IMTTBCR_IRGN0_MASK (3 << 8) +#define IMTTBCR_SL0_LVL_2 (0 << 4) +#define IMTTBCR_SL0_LVL_1 (1 << 4) +#define IMTTBCR_TSZ0_MASK (7 << 0) +#define IMTTBCR_TSZ0_SHIFT O + +#define IMBUSCR 0x000c +#define IMBUSCR_DVM (1 << 2) +#define IMBUSCR_BUSSEL_SYS (0 << 0) +#define IMBUSCR_BUSSEL_CCI (1 << 0) +#define IMBUSCR_BUSSEL_IMCAAR (2 << 0) +#define IMBUSCR_BUSSEL_CCI_IMCAAR (3 << 0) +#define IMBUSCR_BUSSEL_MASK (3 << 0) + +#define IMTTLBR0 0x0010 +#define IMTTUBR0 0x0014 +#define IMTTLBR1 0x0018 +#define IMTTUBR1 0x001c + +#define IMSTR 0x0020 +#define IMSTR_ERRLVL_MASK (3 << 12) +#define IMSTR_ERRLVL_SHIFT 12 +#define IMSTR_ERRCODE_TLB_FORMAT (1 << 8) +#define IMSTR_ERRCODE_ACCESS_PERM (4 << 8) +#define IMSTR_ERRCODE_SECURE_ACCESS (5 << 8) +#define IMSTR_ERRCODE_MASK (7 << 8) +#define IMSTR_MHIT (1 << 4) +#define IMSTR_ABORT (1 << 2) +#define IMSTR_PF (1 << 1) +#define IMSTR_TF (1 << 0) + +#define IMMAIR0 0x0028 +#define IMMAIR1 0x002c +#define IMMAIR_ATTR_MASK 0xff +#define IMMAIR_ATTR_DEVICE 0x04 +#define IMMAIR_ATTR_NC 0x44 +#define IMMAIR_ATTR_WBRWA 0xff +#define IMMAIR_ATTR_SHIFT(n) ((n) << 3) +#define IMMAIR_ATTR_IDX_NC 0 +#define IMMAIR_ATTR_IDX_WBRWA 1 +#define IMMAIR_ATTR_IDX_DEV 2 + +#define IMEAR 0x0030 + +#define IMPCTR 0x0200 +#define IMPSTR 0x0208 +#define IMPEAR 0x020c +#define IMPMBA(n) (0x0280 + ((n) * 4)) +#define IMPMBD(n) (0x02c0 + ((n) * 4)) + +#define IMUCTR(n) (0x0300 + ((n) * 16)) +#define IMUCTR_FIXADDEN (1 << 31) +#define IMUCTR_FIXADD_MASK (0xff << 16) +#define IMUCTR_FIXADD_SHIFT 16 +#define IMUCTR_TTSEL_MMU(n) ((n) << 4) +#define IMUCTR_TTSEL_PMB (8 << 4) +#define IMUCTR_TTSEL_MASK (15 << 4) +#define IMUCTR_FLUSH (1 << 1) +#define IMUCTR_MMUEN (1 << 0) + +#define IMUASID(n) (0x0308 + ((n) * 16)) +#define IMUASID_ASID8_MASK (0xff << 8) +#define IMUASID_ASID8_SHIFT 8 +#define IMUASID_ASID0_MASK (0xff << 0) +#define IMUASID_ASID0_SHIFT 0 + +/* ----------------------------------------------------------------------------- + * Page Table Bits + */ + +/* + * VMSA states in section B3.6.3 "Control of Secure or Non-secure memory access, + * Long-descriptor format" that the NStable bit being set in a table descriptor + * will result in the NStable and NS bits of all child entries being ignored and + * considered as being set. The IPMMU seems not to comply with this, as it + * generates a secure access page fault if any of the NStable and NS bits isn't + * set when running in non-secure mode. + */ +#ifndef PMD_NSTABLE +#define PMD_NSTABLE (_AT(pmdval_t, 1) << 63) +#endif + +#define ARM_VMSA_PTE_XN (((pteval_t)3) << 53) +#define ARM_VMSA_PTE_CONT (((pteval_t)1) << 52) +#define ARM_VMSA_PTE_AF (((pteval_t)1) << 10) +#define ARM_VMSA_PTE_SH_NS (((pteval_t)0) << 8) +#define ARM_VMSA_PTE_SH_OS (((pteval_t)2) << 8) +#define ARM_VMSA_PTE_SH_IS (((pteval_t)3) << 8) +#define ARM_VMSA_PTE_NS (((pteval_t)1) << 5) +#define ARM_VMSA_PTE_PAGE (((pteval_t)3) << 0) + +/* Stage-1 PTE */ +#define ARM_VMSA_PTE_AP_UNPRIV (((pteval_t)1) << 6) +#define ARM_VMSA_PTE_AP_RDONLY (((pteval_t)2) << 6) +#define ARM_VMSA_PTE_ATTRINDX_SHIFT 2 +#define ARM_VMSA_PTE_nG (((pteval_t)1) << 11) + +/* Stage-2 PTE */ +#define ARM_VMSA_PTE_HAP_FAULT (((pteval_t)0) << 6) +#define ARM_VMSA_PTE_HAP_READ (((pteval_t)1) << 6) +#define ARM_VMSA_PTE_HAP_WRITE (((pteval_t)2) << 6) +#define ARM_VMSA_PTE_MEMATTR_OIWB (((pteval_t)0xf) << 2) +#define ARM_VMSA_PTE_MEMATTR_NC (((pteval_t)0x5) << 2) +#define ARM_VMSA_PTE_MEMATTR_DEV (((pteval_t)0x1) << 2) + +/* ----------------------------------------------------------------------------- + * Read/Write Access + */ + +static u32 ipmmu_read(struct ipmmu_vmsa_device *mmu, unsigned int offset) +{ + return ioread32(mmu->base + offset); +} + +static void ipmmu_write(struct ipmmu_vmsa_device *mmu, unsigned int offset, + u32 data) +{ + iowrite32(data, mmu->base + offset); +} + +static u32 ipmmu_ctx_read(struct ipmmu_vmsa_domain *domain, unsigned int reg) +{ + return ipmmu_read(domain->mmu, domain->context_id * IM_CTX_SIZE + reg); +} + +static void ipmmu_ctx_write(struct ipmmu_vmsa_domain *domain, unsigned int reg, + u32 data) +{ + ipmmu_write(domain->mmu, domain->context_id * IM_CTX_SIZE + reg, data); +} + +/* ----------------------------------------------------------------------------- + * TLB and microTLB Management + */ + +/* Wait for any pending TLB invalidations to complete */ +static void ipmmu_tlb_sync(struct ipmmu_vmsa_domain *domain) +{ + unsigned int count = 0; + + while (ipmmu_ctx_read(domain, IMCTR) & IMCTR_FLUSH) { + cpu_relax(); + if (++count == TLB_LOOP_TIMEOUT) { + dev_err_ratelimited(domain->mmu->dev, + "TLB sync timed out -- MMU may be deadlocked\n"); + return; + } + udelay(1); + } +} + +static void ipmmu_tlb_invalidate(struct ipmmu_vmsa_domain *domain) +{ + u32 reg; + + reg = ipmmu_ctx_read(domain, IMCTR); + reg |= IMCTR_FLUSH; + ipmmu_ctx_write(domain, IMCTR, reg); + + ipmmu_tlb_sync(domain); +} + +/* + * Enable MMU translation for the microTLB. + */ +static void ipmmu_utlb_enable(struct ipmmu_vmsa_domain *domain, + const struct ipmmu_vmsa_master *master) +{ + struct ipmmu_vmsa_device *mmu = domain->mmu; + + /* TODO: What should we set the ASID to ? */ + ipmmu_write(mmu, IMUASID(master->utlb), 0); + /* TODO: Do we need to flush the microTLB ? */ + ipmmu_write(mmu, IMUCTR(master->utlb), + IMUCTR_TTSEL_MMU(domain->context_id) | IMUCTR_FLUSH | + IMUCTR_MMUEN); +} + +/* + * Disable MMU translation for the microTLB. + */ +static void ipmmu_utlb_disable(struct ipmmu_vmsa_domain *domain, + const struct ipmmu_vmsa_master *master) +{ + struct ipmmu_vmsa_device *mmu = domain->mmu; + + ipmmu_write(mmu, IMUCTR(master->utlb), 0); +} + +static void ipmmu_flush_pgtable(struct ipmmu_vmsa_device *mmu, void *addr, + size_t size) +{ + unsigned long offset = (unsigned long)addr & ~PAGE_MASK; + + /* + * TODO: Add support for coherent walk through CCI with DVM and remove + * cache handling. + */ + dma_map_page(mmu->dev, virt_to_page(addr), offset, size, DMA_TO_DEVICE); +} + +/* ----------------------------------------------------------------------------- + * Domain/Context Management + */ + +static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) +{ + phys_addr_t ttbr; + u32 reg; + + /* + * TODO: When adding support for multiple contexts, find an unused + * context. + */ + domain->context_id = 0; + + /* TTBR0 */ + ipmmu_flush_pgtable(domain->mmu, domain->pgd, + PTRS_PER_PGD * sizeof(*domain->pgd)); + ttbr = __pa(domain->pgd); + ipmmu_ctx_write(domain, IMTTLBR0, ttbr); + ipmmu_ctx_write(domain, IMTTUBR0, ttbr >> 32); + + /* + * TTBCR + * We use long descriptors with inner-shareable WBWA tables and allocate + * the whole 32-bit VA space to TTBR0. + */ + ipmmu_ctx_write(domain, IMTTBCR, IMTTBCR_EAE | + IMTTBCR_SH0_INNER_SHAREABLE | IMTTBCR_ORGN0_WB_WA | + IMTTBCR_IRGN0_WB_WA | IMTTBCR_SL0_LVL_1); + + /* + * MAIR0 + * We need three attributes only, non-cacheable, write-back read/write + * allocate and device memory. + */ + reg = (IMMAIR_ATTR_NC << IMMAIR_ATTR_SHIFT(IMMAIR_ATTR_IDX_NC)) + | (IMMAIR_ATTR_WBRWA << IMMAIR_ATTR_SHIFT(IMMAIR_ATTR_IDX_WBRWA)) + | (IMMAIR_ATTR_DEVICE << IMMAIR_ATTR_SHIFT(IMMAIR_ATTR_IDX_DEV)); + ipmmu_ctx_write(domain, IMMAIR0, reg); + + /* IMBUSCR */ + ipmmu_ctx_write(domain, IMBUSCR, + ipmmu_ctx_read(domain, IMBUSCR) & + ~(IMBUSCR_DVM | IMBUSCR_BUSSEL_MASK)); + + /* + * IMSTR + * Clear all interrupt flags. + */ + ipmmu_ctx_write(domain, IMSTR, ipmmu_ctx_read(domain, IMSTR)); + + /* + * IMCTR + * Enable the MMU and interrupt generation. The long-descriptor + * translation table format doesn't use TEX remapping. Don't enable AF + * software management as we have no use for it. Flush the TLB as + * required when modifying the context registers. + */ + ipmmu_ctx_write(domain, IMCTR, IMCTR_INTEN | IMCTR_FLUSH | IMCTR_MMUEN); + + return 0; +} + +static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) +{ + /* + * Disable the context. Flush the TLB as required when modifying the + * context registers. + * + * TODO: Is TLB flush really needed ? + */ + ipmmu_ctx_write(domain, IMCTR, IMCTR_FLUSH); + ipmmu_tlb_sync(domain); +} + +/* ----------------------------------------------------------------------------- + * Fault Handling + */ + +static irqreturn_t ipmmu_domain_irq(struct ipmmu_vmsa_domain *domain) +{ + const u32 err_mask = IMSTR_MHIT | IMSTR_ABORT | IMSTR_PF | IMSTR_TF; + struct ipmmu_vmsa_device *mmu = domain->mmu; + u32 status; + u32 iova; + + status = ipmmu_ctx_read(domain, IMSTR); + if (!(status & err_mask)) + return IRQ_NONE; + + iova = ipmmu_ctx_read(domain, IMEAR); + + /* + * Clear the error status flags. Unlike traditional interrupt flag + * registers that must be cleared by writing 1, this status register + * seems to require 0. The error address register must be read before, + * otherwise its value will be 0. + */ + ipmmu_ctx_write(domain, IMSTR, 0); + + /* Log fatal errors. */ + if (status & IMSTR_MHIT) + dev_err_ratelimited(mmu->dev, "Multiple TLB hits @0x%08x\n", + iova); + if (status & IMSTR_ABORT) + dev_err_ratelimited(mmu->dev, "Page Table Walk Abort @0x%08x\n", + iova); + + if (!(status & (IMSTR_PF | IMSTR_TF))) + return IRQ_NONE; + + /* + * Try to handle page faults and translation faults. + * + * TODO: We need to look up the faulty device based on the I/O VA. Use + * the IOMMU device for now. + */ + if (!report_iommu_fault(domain->io_domain, mmu->dev, iova, 0)) + return IRQ_HANDLED; + + dev_err_ratelimited(mmu->dev, + "Unhandled fault: status 0x%08x iova 0x%08x\n", + status, iova); + + return IRQ_HANDLED; +} + +static irqreturn_t ipmmu_irq(int irq, void *dev) +{ + struct ipmmu_vmsa_device *mmu = dev; + struct iommu_domain *io_domain; + struct ipmmu_vmsa_domain *domain; + + if (!mmu->mapping) + return IRQ_NONE; + + io_domain = mmu->mapping->domain; + domain = io_domain->priv; + + return ipmmu_domain_irq(domain); +} + +/* ----------------------------------------------------------------------------- + * Page Table Management + */ + +static void ipmmu_free_ptes(pmd_t *pmd) +{ + pgtable_t table = pmd_pgtable(*pmd); + __free_page(table); +} + +static void ipmmu_free_pmds(pud_t *pud) +{ + pmd_t *pmd, *pmd_base = pmd_offset(pud, 0); + unsigned int i; + + pmd = pmd_base; + for (i = 0; i < PTRS_PER_PMD; ++i) { + if (pmd_none(*pmd)) + continue; + + ipmmu_free_ptes(pmd); + pmd++; + } + + pmd_free(NULL, pmd_base); +} + +static void ipmmu_free_puds(pgd_t *pgd) +{ + pud_t *pud, *pud_base = pud_offset(pgd, 0); + unsigned int i; + + pud = pud_base; + for (i = 0; i < PTRS_PER_PUD; ++i) { + if (pud_none(*pud)) + continue; + + ipmmu_free_pmds(pud); + pud++; + } + + pud_free(NULL, pud_base); +} + +static void ipmmu_free_pgtables(struct ipmmu_vmsa_domain *domain) +{ + pgd_t *pgd, *pgd_base = domain->pgd; + unsigned int i; + + /* + * Recursively free the page tables for this domain. We don't care about + * speculative TLB filling, because the TLB will be nuked next time this + * context bank is re-allocated and no devices currently map to these + * tables. + */ + pgd = pgd_base; + for (i = 0; i < PTRS_PER_PGD; ++i) { + if (pgd_none(*pgd)) + continue; + ipmmu_free_puds(pgd); + pgd++; + } + + kfree(pgd_base); +} + +/* + * We can't use the (pgd|pud|pmd|pte)_populate or the set_(pgd|pud|pmd|pte) + * functions as they would flush the CPU TLB. + */ + +static int ipmmu_alloc_init_pte(struct ipmmu_vmsa_device *mmu, pmd_t *pmd, + unsigned long addr, unsigned long end, + phys_addr_t phys, int prot) +{ + unsigned long pfn = __phys_to_pfn(phys); + pteval_t pteval = ARM_VMSA_PTE_PAGE | ARM_VMSA_PTE_NS | ARM_VMSA_PTE_AF + | ARM_VMSA_PTE_XN; + pte_t *pte, *start; + + if (pmd_none(*pmd)) { + /* Allocate a new set of tables */ + pte = (pte_t *)get_zeroed_page(GFP_ATOMIC); + if (!pte) + return -ENOMEM; + + ipmmu_flush_pgtable(mmu, pte, PAGE_SIZE); + *pmd = __pmd(__pa(pte) | PMD_NSTABLE | PMD_TYPE_TABLE); + ipmmu_flush_pgtable(mmu, pmd, sizeof(*pmd)); + + pte += pte_index(addr); + } else + pte = pte_offset_kernel(pmd, addr); + + pteval |= ARM_VMSA_PTE_AP_UNPRIV | ARM_VMSA_PTE_nG; + if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) + pteval |= ARM_VMSA_PTE_AP_RDONLY; + + if (prot & IOMMU_CACHE) + pteval |= (IMMAIR_ATTR_IDX_WBRWA << + ARM_VMSA_PTE_ATTRINDX_SHIFT); + + /* If no access, create a faulting entry to avoid TLB fills */ + if (prot & IOMMU_EXEC) + pteval &= ~ARM_VMSA_PTE_XN; + else if (!(prot & (IOMMU_READ | IOMMU_WRITE))) + pteval &= ~ARM_VMSA_PTE_PAGE; + + pteval |= ARM_VMSA_PTE_SH_IS; + start = pte; + + /* Install the page table entries. */ + do { + *pte++ = pfn_pte(pfn++, __pgprot(pteval)); + addr += PAGE_SIZE; + } while (addr != end); + + ipmmu_flush_pgtable(mmu, start, sizeof(*pte) * (pte - start)); + return 0; +} + +static int ipmmu_alloc_init_pmd(struct ipmmu_vmsa_device *mmu, pud_t *pud, + unsigned long addr, unsigned long end, + phys_addr_t phys, int prot) +{ + unsigned long next; + pmd_t *pmd; + int ret; + +#ifndef __PAGETABLE_PMD_FOLDED + if (pud_none(*pud)) { + pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC); + if (!pmd) + return -ENOMEM; + + ipmmu_flush_pgtable(mmu, pmd, PAGE_SIZE); + *pud = __pud(__pa(pmd) | PMD_NSTABLE | PMD_TYPE_TABLE); + ipmmu_flush_pgtable(mmu, pud, sizeof(*pud)); + + pmd += pmd_index(addr); + } else +#endif + pmd = pmd_offset(pud, addr); + + do { + next = pmd_addr_end(addr, end); + ret = ipmmu_alloc_init_pte(mmu, pmd, addr, end, phys, prot); + phys += next - addr; + } while (pmd++, addr = next, addr < end); + + return ret; +} + +static int ipmmu_alloc_init_pud(struct ipmmu_vmsa_device *mmu, pgd_t *pgd, + unsigned long addr, unsigned long end, + phys_addr_t phys, int prot) +{ + unsigned long next; + pud_t *pud; + int ret; + +#ifndef __PAGETABLE_PUD_FOLDED + if (pgd_none(*pgd)) { + pud = (pud_t *)get_zeroed_page(GFP_ATOMIC); + if (!pud) + return -ENOMEM; + + ipmmu_flush_pgtable(mmu, pud, PAGE_SIZE); + *pgd = __pgd(__pa(pud) | PMD_NSTABLE | PMD_TYPE_TABLE); + ipmmu_flush_pgtable(mmu, pgd, sizeof(*pgd)); + + pud += pud_index(addr); + } else +#endif + pud = pud_offset(pgd, addr); + + do { + next = pud_addr_end(addr, end); + ret = ipmmu_alloc_init_pmd(mmu, pud, addr, next, phys, prot); + phys += next - addr; + } while (pud++, addr = next, addr < end); + + return ret; +} + +static int ipmmu_handle_mapping(struct ipmmu_vmsa_domain *domain, + unsigned long iova, phys_addr_t paddr, + size_t size, int prot) +{ + struct ipmmu_vmsa_device *mmu = domain->mmu; + pgd_t *pgd = domain->pgd; + unsigned long flags; + unsigned long end; + int ret; + + if (!pgd) + return -EINVAL; + + if (size & ~PAGE_MASK) + return -EINVAL; + + if (paddr & ~((1ULL << 40) - 1)) + return -ERANGE; + + spin_lock_irqsave(&domain->lock, flags); + + pgd += pgd_index(iova); + end = iova + size; + + do { + unsigned long next = pgd_addr_end(iova, end); + + ret = ipmmu_alloc_init_pud(mmu, pgd, iova, next, paddr, prot); + if (ret) + break; + + paddr += next - iova; + iova = next; + } while (pgd++, iova != end); + + spin_unlock_irqrestore(&domain->lock, flags); + + ipmmu_tlb_invalidate(domain); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * IOMMU Operations + */ + +static const struct ipmmu_vmsa_master * +ipmmu_find_master(struct ipmmu_vmsa_device *ipmmu, struct device *dev) +{ + const struct ipmmu_vmsa_master *master = ipmmu->pdata->masters; + const char *devname = dev_name(dev); + unsigned int i; + + for (i = 0; i < ipmmu->pdata->num_masters; ++i, ++master) { + if (strcmp(master->name, devname) == 0) + return master; + } + + return NULL; +} + +static int ipmmu_domain_init(struct iommu_domain *io_domain) +{ + struct ipmmu_vmsa_domain *domain; + + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) + return -ENOMEM; + + spin_lock_init(&domain->lock); + + domain->pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL); + if (!domain->pgd) { + kfree(domain); + return -ENOMEM; + } + + io_domain->priv = domain; + domain->io_domain = io_domain; + + return 0; +} + +static void ipmmu_domain_destroy(struct iommu_domain *io_domain) +{ + struct ipmmu_vmsa_domain *domain = io_domain->priv; + + /* + * Free the domain resources. We assume that all devices have already + * been detached. + */ + ipmmu_domain_destroy_context(domain); + ipmmu_free_pgtables(domain); + kfree(domain); +} + +static int ipmmu_attach_device(struct iommu_domain *io_domain, + struct device *dev) +{ + struct ipmmu_vmsa_device *mmu = dev->archdata.iommu; + struct ipmmu_vmsa_domain *domain = io_domain->priv; + const struct ipmmu_vmsa_master *master; + unsigned long flags; + int ret = 0; + + if (!mmu) { + dev_err(dev, "Cannot attach to IPMMU\n"); + return -ENXIO; + } + + spin_lock_irqsave(&domain->lock, flags); + + if (!domain->mmu) { + /* The domain hasn't been used yet, initialize it. */ + domain->mmu = mmu; + ret = ipmmu_domain_init_context(domain); + } else if (domain->mmu != mmu) { + /* + * Something is wrong, we can't attach two devices using + * different IOMMUs to the same domain. + */ + dev_err(dev, "Can't attach IPMMU %s to domain on IPMMU %s\n", + dev_name(mmu->dev), dev_name(domain->mmu->dev)); + ret = -EINVAL; + } + + spin_unlock_irqrestore(&domain->lock, flags); + + if (ret < 0) + return ret; + + master = ipmmu_find_master(mmu, dev); + if (!master) + return -EINVAL; + + ipmmu_utlb_enable(domain, master); + + return 0; +} + +static void ipmmu_detach_device(struct iommu_domain *io_domain, + struct device *dev) +{ + struct ipmmu_vmsa_domain *domain = io_domain->priv; + const struct ipmmu_vmsa_master *master; + + master = ipmmu_find_master(domain->mmu, dev); + if (!master) + return; + + ipmmu_utlb_disable(domain, master); + + /* + * TODO: Optimize by disabling the context when no device is attached. + */ +} + +static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + struct ipmmu_vmsa_domain *domain = io_domain->priv; + + if (!domain) + return -ENODEV; + + return ipmmu_handle_mapping(domain, iova, paddr, size, prot); +} + +static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova, + size_t size) +{ + struct ipmmu_vmsa_domain *domain = io_domain->priv; + int ret; + + ret = ipmmu_handle_mapping(domain, iova, 0, size, 0); + return ret ? 0 : size; +} + +static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, + dma_addr_t iova) +{ + struct ipmmu_vmsa_domain *domain = io_domain->priv; + pgd_t pgd; + pud_t pud; + pmd_t pmd; + pte_t pte; + + /* TODO: Is locking needed ? */ + + if (!domain->pgd) + return 0; + + pgd = *(domain->pgd + pgd_index(iova)); + if (pgd_none(pgd)) + return 0; + + pud = *pud_offset(&pgd, iova); + if (pud_none(pud)) + return 0; + + pmd = *pmd_offset(&pud, iova); + if (pmd_none(pmd)) + return 0; + + pte = *(pmd_page_vaddr(pmd) + pte_index(iova)); + if (pte_none(pte)) + return 0; + + return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); +} + +static int ipmmu_add_device(struct device *dev) +{ + const struct ipmmu_vmsa_master *master = NULL; + struct ipmmu_vmsa_device *mmu; + struct iommu_group *group; + int ret; + + if (dev->archdata.iommu) { + dev_warn(dev, "IOMMU driver already assigned to device %s\n", + dev_name(dev)); + return -EINVAL; + } + + /* Find the master corresponding to the device. */ + spin_lock(&ipmmu_devices_lock); + + list_for_each_entry(mmu, &ipmmu_devices, list) { + master = ipmmu_find_master(mmu, dev); + if (master) { + /* + * TODO Take a reference to the master to protect + * against device removal. + */ + break; + } + } + + spin_unlock(&ipmmu_devices_lock); + + if (!master) + return -ENODEV; + + if (!master->utlb >= mmu->num_utlbs) + return -EINVAL; + + /* Create a device group and add the device to it. */ + 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); + + if (ret < 0) { + dev_err(dev, "Failed to add device to IPMMU group\n"); + return ret; + } + + dev->archdata.iommu = mmu; + + /* + * Create the ARM mapping, used by the ARM DMA mapping core to allocate + * VAs. This will allocate a corresponding IOMMU domain. + * + * TODO: + * - Create one mapping per context (TLB). + * - Make the mapping size configurable ? We currently use a 2GB mapping + * at a 1GB offset to ensure that NULL VAs will fault. + */ + if (!mmu->mapping) { + struct dma_iommu_mapping *mapping; + + mapping = arm_iommu_create_mapping(&platform_bus_type, + SZ_1G, SZ_2G, 0); + if (IS_ERR(mapping)) { + dev_err(mmu->dev, "failed to create ARM IOMMU mapping\n"); + return PTR_ERR(mapping); + } + + mmu->mapping = mapping; + } + + /* Attach the ARM VA mapping to the device. */ + ret = arm_iommu_attach_device(dev, mmu->mapping); + if (ret < 0) { + dev_err(dev, "Failed to attach device to VA mapping\n"); + goto error; + } + + return 0; + +error: + dev->archdata.iommu = NULL; + iommu_group_remove_device(dev); + return ret; +} + +static void ipmmu_remove_device(struct device *dev) +{ + arm_iommu_detach_device(dev); + iommu_group_remove_device(dev); + dev->archdata.iommu = NULL; +} + +static struct iommu_ops ipmmu_ops = { + .domain_init = ipmmu_domain_init, + .domain_destroy = ipmmu_domain_destroy, + .attach_dev = ipmmu_attach_device, + .detach_dev = ipmmu_detach_device, + .map = ipmmu_map, + .unmap = ipmmu_unmap, + .iova_to_phys = ipmmu_iova_to_phys, + .add_device = ipmmu_add_device, + .remove_device = ipmmu_remove_device, + .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, +}; + +/* ----------------------------------------------------------------------------- + * Probe/remove and init + */ + +static void ipmmu_device_reset(struct ipmmu_vmsa_device *mmu) +{ + unsigned int i; + + /* Disable all contexts. */ + for (i = 0; i < 4; ++i) + ipmmu_write(mmu, i * IM_CTX_SIZE + IMCTR, 0); +} + +static int ipmmu_probe(struct platform_device *pdev) +{ + struct ipmmu_vmsa_device *mmu; + struct resource *res; + int irq; + int ret; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "missing platform data\n"); + return -EINVAL; + } + + mmu = devm_kzalloc(&pdev->dev, sizeof(*mmu), GFP_KERNEL); + if (!mmu) { + dev_err(&pdev->dev, "cannot allocate device data\n"); + return -ENOMEM; + } + + mmu->dev = &pdev->dev; + mmu->pdata = pdev->dev.platform_data; + mmu->num_utlbs = 32; + + /* Map I/O memory and request IRQ. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mmu->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mmu->base)) + return PTR_ERR(mmu->base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ found\n"); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, ipmmu_irq, 0, + dev_name(&pdev->dev), mmu); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ %d\n", irq); + return irq; + } + + ipmmu_device_reset(mmu); + + /* + * We can't create the ARM mapping here as it requires the bus to have + * an IOMMU, which only happens when bus_set_iommu() is called in + * ipmmu_init() after the probe function returns. + */ + + spin_lock(&ipmmu_devices_lock); + list_add(&mmu->list, &ipmmu_devices); + spin_unlock(&ipmmu_devices_lock); + + platform_set_drvdata(pdev, mmu); + + return 0; +} + +static int ipmmu_remove(struct platform_device *pdev) +{ + struct ipmmu_vmsa_device *mmu = platform_get_drvdata(pdev); + + spin_lock(&ipmmu_devices_lock); + list_del(&mmu->list); + spin_unlock(&ipmmu_devices_lock); + + arm_iommu_release_mapping(mmu->mapping); + + ipmmu_device_reset(mmu); + + return 0; +} + +static struct platform_driver ipmmu_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ipmmu-vmsa", + }, + .probe = ipmmu_probe, + .remove = ipmmu_remove, +}; + +static int __init ipmmu_init(void) +{ + int ret; + + ret = platform_driver_register(&ipmmu_driver); + if (ret < 0) + return ret; + + if (!iommu_present(&platform_bus_type)) + bus_set_iommu(&platform_bus_type, &ipmmu_ops); + + return 0; +} + +static void __exit ipmmu_exit(void) +{ + return platform_driver_unregister(&ipmmu_driver); +} + +subsys_initcall(ipmmu_init); +module_exit(ipmmu_exit); + +MODULE_DESCRIPTION("IOMMU API for Renesas VMSA-compatible IPMMU"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/ipmmu-vmsa.h b/include/linux/platform_data/ipmmu-vmsa.h new file mode 100644 index 000000000000..5275b3ac6d37 --- /dev/null +++ b/include/linux/platform_data/ipmmu-vmsa.h @@ -0,0 +1,24 @@ +/* + * IPMMU VMSA Platform Data + * + * Copyright (C) 2014 Renesas Electronics Corporation + * + * 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; version 2 of the License. + */ + +#ifndef __IPMMU_VMSA_H__ +#define __IPMMU_VMSA_H__ + +struct ipmmu_vmsa_master { + const char *name; + unsigned int utlb; +}; + +struct ipmmu_vmsa_platform_data { + const struct ipmmu_vmsa_master *masters; + unsigned int num_masters; +}; + +#endif /* __IPMMU_VMSA_H__ */ -- cgit v1.2.3 From 594ddced821dee39a548efe46d7f834bae013505 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 25 Apr 2014 22:45:12 +0900 Subject: ALSA: fireworks: Add hwdep interface This interface is designed for mixer/control application. To use hwdep interface, the application can get information about firewire node, can lock/unlock kernel streaming and can get notification at starting/stopping kernel streaming. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 3 +- include/uapi/sound/firewire.h | 3 +- sound/firewire/Kconfig | 1 + sound/firewire/fireworks/Makefile | 2 +- sound/firewire/fireworks/fireworks.c | 5 + sound/firewire/fireworks/fireworks.h | 12 ++ sound/firewire/fireworks/fireworks_hwdep.c | 199 ++++++++++++++++++++++++++++ sound/firewire/fireworks/fireworks_midi.c | 25 +++- sound/firewire/fireworks/fireworks_pcm.c | 15 ++- sound/firewire/fireworks/fireworks_stream.c | 39 ++++++ 10 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 sound/firewire/fireworks/fireworks_hwdep.c (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 9fc6219d3848..5dfbfdfd2d12 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -94,9 +94,10 @@ enum { SNDRV_HWDEP_IFACE_HDA, /* HD-audio */ SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */ SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */ + SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */ /* Don't forget to change the following: */ - SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_FIREWORKS }; struct snd_hwdep_info { diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index 59f5961302bf..fc9afb261989 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -34,7 +34,8 @@ union snd_firewire_event { #define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa) #define SNDRV_FIREWIRE_TYPE_DICE 1 -/* Fireworks, AV/C, RME, MOTU, ... */ +#define SNDRV_FIREWIRE_TYPE_FIREWORKS 2 +/* AV/C, RME, MOTU, ... */ struct snd_firewire_get_info { unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */ diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index fb39f9cfdfbf..875af0217916 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -66,6 +66,7 @@ config SND_FIREWORKS select SND_FIREWIRE_LIB select SND_RAWMIDI select SND_PCM + select SND_HWDEP help Say Y here to include support for FireWire devices based on Echo Digital Audio Fireworks board: diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile index d7ebf834fece..0c7440826db8 100644 --- a/sound/firewire/fireworks/Makefile +++ b/sound/firewire/fireworks/Makefile @@ -1,4 +1,4 @@ snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \ fireworks_stream.o fireworks_proc.o fireworks_midi.o \ - fireworks_pcm.o fireworks.o + fireworks_pcm.o fireworks_hwdep.o fireworks.o obj-m += snd-fireworks.o diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index d7877c79e32f..f8d06f56618f 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -217,6 +217,7 @@ efw_probe(struct fw_unit *unit, efw->unit = unit; mutex_init(&efw->mutex); spin_lock_init(&efw->lock); + init_waitqueue_head(&efw->hwdep_wait); err = get_hardware_info(efw); if (err < 0) @@ -236,6 +237,10 @@ efw_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_efw_create_hwdep_device(efw); + if (err < 0) + goto error; + err = snd_efw_stream_init_duplex(efw); if (err < 0) goto error; diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 36419ca12d6f..4aaf2dce5ec8 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "../packets-buffer.h" #include "../iso-resources.h" @@ -90,6 +92,11 @@ struct snd_efw { unsigned int phys_in_grp_count; struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS]; struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS]; + + /* for uapi */ + int dev_lock_count; + bool dev_lock_changed; + wait_queue_head_t hwdep_wait; }; struct snd_efw_transaction { @@ -197,6 +204,9 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, int sampling_rate); void snd_efw_stream_stop_duplex(struct snd_efw *efw); void snd_efw_stream_update_duplex(struct snd_efw *efw); void snd_efw_stream_destroy_duplex(struct snd_efw *efw); +void snd_efw_stream_lock_changed(struct snd_efw *efw); +int snd_efw_stream_lock_try(struct snd_efw *efw); +void snd_efw_stream_lock_release(struct snd_efw *efw); void snd_efw_proc_init(struct snd_efw *efw); @@ -205,6 +215,8 @@ int snd_efw_create_midi_devices(struct snd_efw *efw); int snd_efw_create_pcm_devices(struct snd_efw *efw); int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode); +int snd_efw_create_hwdep_device(struct snd_efw *efw); + #define SND_EFW_DEV_ENTRY(vendor, model) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c new file mode 100644 index 000000000000..1cf491dc39a3 --- /dev/null +++ b/sound/firewire/fireworks/fireworks_hwdep.c @@ -0,0 +1,199 @@ +/* + * fireworks_hwdep.c - a part of driver for Fireworks based devices + * + * Copyright (c) 2013-2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +/* + * This codes have three functionalities. + * + * 1.get information about firewire node + * 2.get notification about starting/stopping stream + * 3.lock/unlock streaming + */ + +#include "fireworks.h" + +static long +hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, + loff_t *offset) +{ + struct snd_efw *efw = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&efw->lock); + + while (!efw->dev_lock_changed) { + prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&efw->lock); + schedule(); + finish_wait(&efw->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&efw->lock); + } + + memset(&event, 0, sizeof(event)); + if (efw->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = (efw->dev_lock_count > 0); + efw->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + } + + spin_unlock_irq(&efw->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int +hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) +{ + struct snd_efw *efw = hwdep->private_data; + unsigned int events; + + poll_wait(file, &efw->hwdep_wait, wait); + + spin_lock_irq(&efw->lock); + if (efw->dev_lock_changed) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&efw->lock); + + return events; +} + +static int +hwdep_get_info(struct snd_efw *efw, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(efw->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int +hwdep_lock(struct snd_efw *efw) +{ + int err; + + spin_lock_irq(&efw->lock); + + if (efw->dev_lock_count == 0) { + efw->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&efw->lock); + + return err; +} + +static int +hwdep_unlock(struct snd_efw *efw) +{ + int err; + + spin_lock_irq(&efw->lock); + + if (efw->dev_lock_count == -1) { + efw->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&efw->lock); + + return err; +} + +static int +hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_efw *efw = hwdep->private_data; + + spin_lock_irq(&efw->lock); + if (efw->dev_lock_count == -1) + efw->dev_lock_count = 0; + spin_unlock_irq(&efw->lock); + + return 0; +} + +static int +hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_efw *efw = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return hwdep_get_info(efw, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return hwdep_lock(efw); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return hwdep_unlock(efw); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int +hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define hwdep_compat_ioctl NULL +#endif + +static const struct snd_hwdep_ops hwdep_ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, +}; + +int snd_efw_create_hwdep_device(struct snd_efw *efw) +{ + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep); + if (err < 0) + goto end; + strcpy(hwdep->name, "Fireworks"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS; + hwdep->ops = hwdep_ops; + hwdep->private_data = efw; + hwdep->exclusive = true; +end: + return err; +} + diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index cbf34e99237f..4a600d235ec5 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -11,17 +11,36 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) { struct snd_efw *efw = substream->rmidi->private_data; + int err; + + err = snd_efw_stream_lock_try(efw); + if (err < 0) + goto end; atomic_inc(&efw->capture_substreams); - return snd_efw_stream_start_duplex(efw, 0); + err = snd_efw_stream_start_duplex(efw, 0); + if (err < 0) + snd_efw_stream_lock_release(efw); + +end: + return err; } static int midi_playback_open(struct snd_rawmidi_substream *substream) { struct snd_efw *efw = substream->rmidi->private_data; + int err; + + err = snd_efw_stream_lock_try(efw); + if (err < 0) + goto end; atomic_inc(&efw->playback_substreams); - return snd_efw_stream_start_duplex(efw, 0); + err = snd_efw_stream_start_duplex(efw, 0); + if (err < 0) + snd_efw_stream_lock_release(efw); +end: + return err; } static int midi_capture_close(struct snd_rawmidi_substream *substream) @@ -31,6 +50,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) atomic_dec(&efw->capture_substreams); snd_efw_stream_stop_duplex(efw); + snd_efw_stream_lock_release(efw); return 0; } @@ -41,6 +61,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) atomic_dec(&efw->playback_substreams); snd_efw_stream_stop_duplex(efw); + snd_efw_stream_lock_release(efw); return 0; } diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index 15ec4e02b84f..ed211d062202 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -199,13 +199,17 @@ static int pcm_open(struct snd_pcm_substream *substream) unsigned int clock_source; int err; - err = pcm_init_hw_params(efw, substream); + err = snd_efw_stream_lock_try(efw); if (err < 0) goto end; + err = pcm_init_hw_params(efw, substream); + if (err < 0) + goto err_locked; + err = snd_efw_command_get_clock_source(efw, &clock_source); if (err < 0) - goto end; + goto err_locked; /* * When source of clock is not internal or any PCM streams are running, @@ -216,7 +220,7 @@ static int pcm_open(struct snd_pcm_substream *substream) amdtp_stream_pcm_running(&efw->rx_stream)) { err = snd_efw_command_get_sampling_rate(efw, &sampling_rate); if (err < 0) - goto end; + goto err_locked; substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } @@ -224,10 +228,15 @@ static int pcm_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); end: return err; +err_locked: + snd_efw_stream_lock_release(efw); + return err; } static int pcm_close(struct snd_pcm_substream *substream) { + struct snd_efw *efw = substream->private_data; + snd_efw_stream_lock_release(efw); return 0; } diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 1860914a3e0f..eaab8f6bc8b6 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -331,3 +331,42 @@ void snd_efw_stream_destroy_duplex(struct snd_efw *efw) mutex_unlock(&efw->mutex); } + +void snd_efw_stream_lock_changed(struct snd_efw *efw) +{ + efw->dev_lock_changed = true; + wake_up(&efw->hwdep_wait); +} + +int snd_efw_stream_lock_try(struct snd_efw *efw) +{ + int err; + + spin_lock_irq(&efw->lock); + + /* user land lock this */ + if (efw->dev_lock_count < 0) { + err = -EBUSY; + goto end; + } + + /* this is the first time */ + if (efw->dev_lock_count++ == 0) + snd_efw_stream_lock_changed(efw); + err = 0; +end: + spin_unlock_irq(&efw->lock); + return err; +} + +void snd_efw_stream_lock_release(struct snd_efw *efw) +{ + spin_lock_irq(&efw->lock); + + if (WARN_ON(efw->dev_lock_count <= 0)) + goto end; + if (--efw->dev_lock_count == 0) + snd_efw_stream_lock_changed(efw); +end: + spin_unlock_irq(&efw->lock); +} -- cgit v1.2.3 From 555e8a8f7f149544eb7d4aa3a6420bc4c3055638 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 25 Apr 2014 22:45:13 +0900 Subject: ALSA: fireworks: Add command/response functionality into hwdep interface This commit adds two functionality for hwdep interface, adds two parameters for this driver, add a node for proc interface. To receive responses from devices, this driver already allocate own callback into initial memory space in host controller. This means no one can allocate its own callback to the address. So this driver must give a way for user applications to receive responses. This commit adds a functionality to receive responses via hwdep interface. The application can receive responses to read from this interface. To achieve this, this commit adds a buffer to queue responses. The default size of this buffer is 1024 bytes. This size can be changed to give preferrable size to 'resp_buf_size' parameter for this driver. The application should notice rest of space in this buffer because this driver don't push responses when this buffer has no space. Additionaly, this commit adds a functionality to transmit commands via hwdep interface. The application can transmit commands to write into this interface. I note that the application can transmit one command at once, but can receive as many responses as possible untill the user-buffer is full. When using these interfaces, the application must keep maximum number of sequence number in command within the number in firewire.h because this driver uses this number to distinguish the response is against the command by the application or this driver. Usually responses against commands which the application transmits are pushed into this buffer. But to enable 'resp_buf_debug' parameter for this driver, all responses are pushed into the buffer. When using this mode, I reccomend to expand the size of buffer. Finally this commit adds a new node into proc interface to output status of the buffer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/uapi/sound/firewire.h | 19 +++ sound/firewire/fireworks/fireworks.c | 21 +++ sound/firewire/fireworks/fireworks.h | 22 +-- sound/firewire/fireworks/fireworks_command.c | 8 +- sound/firewire/fireworks/fireworks_hwdep.c | 131 ++++++++++++++--- sound/firewire/fireworks/fireworks_proc.c | 18 +++ sound/firewire/fireworks/fireworks_transaction.c | 176 ++++++++++++++++++++--- 7 files changed, 347 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index fc9afb261989..bfc0e23e47a2 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -2,11 +2,13 @@ #define _UAPI_SOUND_FIREWIRE_H_INCLUDED #include +#include /* events can be read() from the hwdep device */ #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e +#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475 struct snd_firewire_event_common { unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ @@ -22,10 +24,27 @@ struct snd_firewire_event_dice_notification { unsigned int notification; /* DICE-specific bits */ }; +#define SND_EFW_TRANSACTION_USER_SEQNUM_MAX ((__u32)((__u16)~0) - 1) +/* each field should be in big endian */ +struct snd_efw_transaction { + __be32 length; + __be32 version; + __be32 seqnum; + __be32 category; + __be32 command; + __be32 status; + __be32 params[0]; +}; +struct snd_firewire_event_efw_response { + unsigned int type; + __be32 response[0]; /* some responses */ +}; + union snd_firewire_event { struct snd_firewire_event_common common; struct snd_firewire_event_lock_status lock_status; struct snd_firewire_event_dice_notification dice_notification; + struct snd_firewire_event_efw_response efw_response; }; diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index f8d06f56618f..a354d26afe9e 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -24,6 +24,8 @@ MODULE_LICENSE("GPL v2"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +unsigned int snd_efw_resp_buf_size = 1024; +bool snd_efw_resp_buf_debug = false; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "card index"); @@ -31,6 +33,11 @@ module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string"); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "enable Fireworks sound card"); +module_param_named(resp_buf_size, snd_efw_resp_buf_size, uint, 0444); +MODULE_PARM_DESC(resp_buf_size, + "response buffer size (max 4096, default 1024)"); +module_param_named(resp_buf_debug, snd_efw_resp_buf_debug, bool, 0444); +MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer"); static DEFINE_MUTEX(devices_mutex); static DECLARE_BITMAP(devices_used, SNDRV_CARDS); @@ -182,6 +189,7 @@ efw_card_free(struct snd_card *card) } mutex_destroy(&efw->mutex); + kfree(efw->resp_buf); } static int @@ -219,6 +227,17 @@ efw_probe(struct fw_unit *unit, spin_lock_init(&efw->lock); init_waitqueue_head(&efw->hwdep_wait); + /* prepare response buffer */ + snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size, + SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U); + efw->resp_buf = kzalloc(snd_efw_resp_buf_size, GFP_KERNEL); + if (efw->resp_buf == NULL) { + err = -ENOMEM; + goto error; + } + efw->pull_ptr = efw->push_ptr = efw->resp_buf; + snd_efw_transaction_add_instance(efw); + err = get_hardware_info(efw); if (err < 0) goto error; @@ -256,6 +275,7 @@ end: mutex_unlock(&devices_mutex); return err; error: + snd_efw_transaction_remove_instance(efw); mutex_unlock(&devices_mutex); snd_card_free(card); return err; @@ -274,6 +294,7 @@ 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); snd_card_free_when_closed(efw->card); diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 4aaf2dce5ec8..494195bd3296 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -49,6 +49,9 @@ */ #define SND_EFW_RESPONSE_MAXIMUM_BYTES 0x200U +extern unsigned int snd_efw_resp_buf_size; +extern bool snd_efw_resp_buf_debug; + struct snd_efw_phys_grp { u8 type; /* see enum snd_efw_grp_type */ u8 count; @@ -97,23 +100,24 @@ struct snd_efw { int dev_lock_count; bool dev_lock_changed; wait_queue_head_t hwdep_wait; -}; -struct snd_efw_transaction { - __be32 length; - __be32 version; - __be32 seqnum; - __be32 category; - __be32 command; - __be32 status; - __be32 params[0]; + /* response queue */ + u8 *resp_buf; + u8 *pull_ptr; + u8 *push_ptr; + unsigned int resp_queues; }; + +int snd_efw_transaction_cmd(struct fw_unit *unit, + const void *cmd, unsigned int size); int snd_efw_transaction_run(struct fw_unit *unit, const void *cmd, unsigned int cmd_size, void *resp, unsigned int resp_size); int snd_efw_transaction_register(void); void snd_efw_transaction_unregister(void); void snd_efw_transaction_bus_reset(struct fw_unit *unit); +void snd_efw_transaction_add_instance(struct snd_efw *efw); +void snd_efw_transaction_remove_instance(struct snd_efw *efw); struct snd_efw_hwinfo { u32 flags; diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c index d5ea7051ad0c..166f80584c2a 100644 --- a/sound/firewire/fireworks/fireworks_command.c +++ b/sound/firewire/fireworks/fireworks_command.c @@ -22,7 +22,8 @@ * Information commands. But this module don't use them. */ -#define EFW_TRANSACTION_SEQNUM_MAX ((u32)~0) +#define KERNEL_SEQNUM_MIN (SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2) +#define KERNEL_SEQNUM_MAX ((u32)~0) /* for clock source and sampling rate */ struct efc_clock { @@ -120,8 +121,9 @@ efw_transaction(struct snd_efw *efw, unsigned int category, /* to keep consistency of sequence number */ spin_lock(&efw->lock); - if (efw->seqnum + 2 >= EFW_TRANSACTION_SEQNUM_MAX) - efw->seqnum = 0; + if ((efw->seqnum < KERNEL_SEQNUM_MIN) || + (efw->seqnum >= KERNEL_SEQNUM_MAX - 2)) + efw->seqnum = KERNEL_SEQNUM_MIN; else efw->seqnum += 2; seqnum = efw->seqnum; diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c index 1cf491dc39a3..6b50a6796d22 100644 --- a/sound/firewire/fireworks/fireworks_hwdep.c +++ b/sound/firewire/fireworks/fireworks_hwdep.c @@ -7,26 +7,101 @@ */ /* - * This codes have three functionalities. + * This codes have five functionalities. * * 1.get information about firewire node * 2.get notification about starting/stopping stream * 3.lock/unlock streaming + * 4.transmit command of EFW transaction + * 5.receive response of EFW transaction + * */ #include "fireworks.h" static long -hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, +hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained, + loff_t *offset) +{ + unsigned int length, till_end, type; + struct snd_efw_transaction *t; + long count = 0; + + if (remained < sizeof(type) + sizeof(struct snd_efw_transaction)) + return -ENOSPC; + + /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */ + type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE; + if (copy_to_user(buf, &type, sizeof(type))) + return -EFAULT; + remained -= sizeof(type); + buf += sizeof(type); + + /* write into buffer as many responses as possible */ + while (efw->resp_queues > 0) { + t = (struct snd_efw_transaction *)(efw->pull_ptr); + length = be32_to_cpu(t->length) * sizeof(__be32); + + /* confirm enough space for this response */ + if (remained < length) + break; + + /* copy from ring buffer to user buffer */ + while (length > 0) { + till_end = snd_efw_resp_buf_size - + (unsigned int)(efw->pull_ptr - efw->resp_buf); + till_end = min_t(unsigned int, length, till_end); + + if (copy_to_user(buf, efw->pull_ptr, till_end)) + return -EFAULT; + + efw->pull_ptr += till_end; + if (efw->pull_ptr >= efw->resp_buf + + snd_efw_resp_buf_size) + efw->pull_ptr = efw->resp_buf; + + length -= till_end; + buf += till_end; + count += till_end; + remained -= till_end; + } + + efw->resp_queues--; + } + + return count; +} + +static long +hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count, + loff_t *offset) +{ + union snd_firewire_event event; + + memset(&event, 0, sizeof(event)); + + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = (efw->dev_lock_count > 0); + efw->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static long +hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { struct snd_efw *efw = hwdep->private_data; DEFINE_WAIT(wait); - union snd_firewire_event event; spin_lock_irq(&efw->lock); - while (!efw->dev_lock_changed) { + while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) { prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&efw->lock); schedule(); @@ -36,20 +111,43 @@ hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&efw->lock); } - memset(&event, 0, sizeof(event)); - if (efw->dev_lock_changed) { - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = (efw->dev_lock_count > 0); - efw->dev_lock_changed = false; - - count = min_t(long, count, sizeof(event.lock_status)); - } + if (efw->dev_lock_changed) + count = hwdep_read_locked(efw, buf, count, offset); + else if (efw->resp_queues > 0) + count = hwdep_read_resp_buf(efw, buf, count, offset); spin_unlock_irq(&efw->lock); - if (copy_to_user(buf, &event, count)) - return -EFAULT; + return count; +} +static long +hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count, + loff_t *offset) +{ + struct snd_efw *efw = hwdep->private_data; + u32 seqnum; + u8 *buf; + + if (count < sizeof(struct snd_efw_transaction) || + SND_EFW_RESPONSE_MAXIMUM_BYTES < count) + return -EINVAL; + + buf = memdup_user(data, count); + if (IS_ERR(buf)) + return PTR_ERR(data); + + /* check seqnum is not for kernel-land */ + seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum); + if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) { + count = -EINVAL; + goto end; + } + + if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0) + count = -EIO; +end: + kfree(buf); return count; } @@ -62,13 +160,13 @@ hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) poll_wait(file, &efw->hwdep_wait, wait); spin_lock_irq(&efw->lock); - if (efw->dev_lock_changed) + if (efw->dev_lock_changed || (efw->resp_queues > 0)) events = POLLIN | POLLRDNORM; else events = 0; spin_unlock_irq(&efw->lock); - return events; + return events | POLLOUT; } static int @@ -174,6 +272,7 @@ hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, static const struct snd_hwdep_ops hwdep_ops = { .read = hwdep_read, + .write = hwdep_write, .release = hwdep_release, .poll = hwdep_poll, .ioctl = hwdep_ioctl, diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c index 631c91f64db4..f29d4aaf56a1 100644 --- a/sound/firewire/fireworks/fireworks_proc.c +++ b/sound/firewire/fireworks/fireworks_proc.c @@ -175,6 +175,23 @@ end: kfree(meters); } +static void +proc_read_queues_state(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_efw *efw = entry->private_data; + unsigned int consumed; + + if (efw->pull_ptr > efw->push_ptr) + consumed = snd_efw_resp_buf_size - + (unsigned int)(efw->pull_ptr - efw->push_ptr); + else + consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr); + + snd_iprintf(buffer, "%d %d/%d\n", + efw->resp_queues, consumed, snd_efw_resp_buf_size); +} + static void add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name, void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b)) @@ -211,4 +228,5 @@ void snd_efw_proc_init(struct snd_efw *efw) add_node(efw, root, "clock", proc_read_clock); add_node(efw, root, "firmware", proc_read_hwinfo); add_node(efw, root, "meters", proc_read_phys_meters); + add_node(efw, root, "queues", proc_read_queues_state); } diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c index aac91d8485d5..81a65ebb5f71 100644 --- a/sound/firewire/fireworks/fireworks_transaction.c +++ b/sound/firewire/fireworks/fireworks_transaction.c @@ -38,6 +38,9 @@ #define ERROR_DELAY_MS 5 #define EFC_TIMEOUT_MS 125 +static DEFINE_SPINLOCK(instances_lock); +static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + static DEFINE_SPINLOCK(transaction_queues_lock); static LIST_HEAD(transaction_queues); @@ -57,6 +60,14 @@ struct transaction_queue { wait_queue_head_t wait; }; +int snd_efw_transaction_cmd(struct fw_unit *unit, + const void *cmd, unsigned int size) +{ + return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST, + MEMORY_SPACE_EFW_COMMAND, + (void *)cmd, size, 0); +} + int snd_efw_transaction_run(struct fw_unit *unit, const void *cmd, unsigned int cmd_size, void *resp, unsigned int resp_size) @@ -78,9 +89,7 @@ int snd_efw_transaction_run(struct fw_unit *unit, tries = 0; do { - ret = snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST, - MEMORY_SPACE_EFW_COMMAND, - (void *)cmd, cmd_size, 0); + ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size); if (ret < 0) break; @@ -107,27 +116,92 @@ int snd_efw_transaction_run(struct fw_unit *unit, } static void -efw_response(struct fw_card *card, struct fw_request *request, - int tcode, int destination, int source, - int generation, unsigned long long offset, - void *data, size_t length, void *callback_data) +copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode) { - struct fw_device *device; - struct transaction_queue *t; - unsigned long flags; - int rcode; - u32 seqnum; + size_t capacity, till_end; + struct snd_efw_transaction *t; - rcode = RCODE_TYPE_ERROR; - if (length < sizeof(struct snd_efw_transaction)) { - rcode = RCODE_DATA_ERROR; - goto end; - } else if (offset != MEMORY_SPACE_EFW_RESPONSE) { - rcode = RCODE_ADDRESS_ERROR; + spin_lock_irq(&efw->lock); + + t = (struct snd_efw_transaction *)data; + length = min_t(size_t, t->length * sizeof(t->length), length); + + if (efw->push_ptr < efw->pull_ptr) + capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr); + else + capacity = snd_efw_resp_buf_size - + (unsigned int)(efw->push_ptr - efw->pull_ptr); + + /* confirm enough space for this response */ + if (capacity < length) { + *rcode = RCODE_CONFLICT_ERROR; goto end; } - seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum); + /* copy to ring buffer */ + while (length > 0) { + till_end = snd_efw_resp_buf_size - + (unsigned int)(efw->push_ptr - efw->resp_buf); + till_end = min_t(unsigned int, length, till_end); + + memcpy(efw->push_ptr, data, till_end); + + efw->push_ptr += till_end; + if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size) + efw->push_ptr = efw->resp_buf; + + length -= till_end; + data += till_end; + } + + /* for hwdep */ + efw->resp_queues++; + wake_up(&efw->hwdep_wait); + + *rcode = RCODE_COMPLETE; +end: + spin_unlock_irq(&efw->lock); +} + +static void +handle_resp_for_user(struct fw_card *card, int generation, int source, + void *data, size_t length, int *rcode) +{ + struct fw_device *device; + struct snd_efw *efw; + unsigned int i; + + spin_lock_irq(&instances_lock); + + for (i = 0; i < SNDRV_CARDS; i++) { + efw = instances[i]; + if (efw == NULL) + continue; + device = fw_parent_device(efw->unit); + if ((device->card != card) || + (device->generation != generation)) + continue; + smp_rmb(); /* node id vs. generation */ + if (device->node_id != source) + continue; + + break; + } + if (i == SNDRV_CARDS) + goto end; + + copy_resp_to_buf(efw, data, length, rcode); +end: + spin_unlock_irq(&instances_lock); +} + +static void +handle_resp_for_kernel(struct fw_card *card, int generation, int source, + void *data, size_t length, int *rcode, u32 seqnum) +{ + struct fw_device *device; + struct transaction_queue *t; + unsigned long flags; spin_lock_irqsave(&transaction_queues_lock, flags); list_for_each_entry(t, &transaction_queues, list) { @@ -144,14 +218,76 @@ efw_response(struct fw_card *card, struct fw_request *request, t->size = min_t(unsigned int, length, t->size); memcpy(t->buf, data, t->size); wake_up(&t->wait); - rcode = RCODE_COMPLETE; + *rcode = RCODE_COMPLETE; } } spin_unlock_irqrestore(&transaction_queues_lock, flags); +} + +static void +efw_response(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + int rcode, dummy; + u32 seqnum; + + rcode = RCODE_TYPE_ERROR; + if (length < sizeof(struct snd_efw_transaction)) { + rcode = RCODE_DATA_ERROR; + goto end; + } else if (offset != MEMORY_SPACE_EFW_RESPONSE) { + rcode = RCODE_ADDRESS_ERROR; + goto end; + } + + seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum); + if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) { + handle_resp_for_kernel(card, generation, source, + data, length, &rcode, seqnum); + if (snd_efw_resp_buf_debug) + handle_resp_for_user(card, generation, source, + data, length, &dummy); + } else { + handle_resp_for_user(card, generation, source, + data, length, &rcode); + } end: fw_send_response(card, request, rcode); } +void snd_efw_transaction_add_instance(struct snd_efw *efw) +{ + unsigned int i; + + spin_lock_irq(&instances_lock); + + for (i = 0; i < SNDRV_CARDS; i++) { + if (instances[i] != NULL) + continue; + instances[i] = efw; + break; + } + + spin_unlock_irq(&instances_lock); +} + +void snd_efw_transaction_remove_instance(struct snd_efw *efw) +{ + unsigned int i; + + spin_lock_irq(&instances_lock); + + for (i = 0; i < SNDRV_CARDS; i++) { + if (instances[i] != efw) + continue; + instances[i] = NULL; + } + + spin_unlock_irq(&instances_lock); +} + void snd_efw_transaction_bus_reset(struct fw_unit *unit) { struct transaction_queue *t; -- cgit v1.2.3 From 618eabeae711c56d376daa147c6a684116d68705 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 25 Apr 2014 22:45:20 +0900 Subject: ALSA: bebob: Add hwdep interface This interface is designed for mixer/control application. By using hwdep interface, the application can get information about firewire node, can lock/unlock kernel streaming and can get notification at starting/stopping kernel streaming. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 3 +- include/uapi/sound/firewire.h | 1 + sound/firewire/Kconfig | 1 + sound/firewire/bebob/Makefile | 2 +- sound/firewire/bebob/bebob.c | 5 + sound/firewire/bebob/bebob.h | 13 +++ sound/firewire/bebob/bebob_hwdep.c | 199 ++++++++++++++++++++++++++++++++++++ sound/firewire/bebob/bebob_midi.c | 24 ++++- sound/firewire/bebob/bebob_pcm.c | 16 ++- sound/firewire/bebob/bebob_stream.c | 39 +++++++ 10 files changed, 295 insertions(+), 8 deletions(-) create mode 100644 sound/firewire/bebob/bebob_hwdep.c (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 5dfbfdfd2d12..224948342f14 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -95,9 +95,10 @@ enum { SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */ SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */ SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */ + SNDRV_HWDEP_IFACE_FW_BEBOB, /* BridgeCo BeBoB based device */ /* Don't forget to change the following: */ - SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_FIREWORKS + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB }; struct snd_hwdep_info { diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index bfc0e23e47a2..af4bd136c75d 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -54,6 +54,7 @@ union snd_firewire_event { #define SNDRV_FIREWIRE_TYPE_DICE 1 #define SNDRV_FIREWIRE_TYPE_FIREWORKS 2 +#define SNDRV_FIREWIRE_TYPE_BEBOB 3 /* AV/C, RME, MOTU, ... */ struct snd_firewire_get_info { diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index d55fd5fd16ca..21750649e14e 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -84,6 +84,7 @@ config SND_BEBOB select SND_FIREWIRE_LIB select SND_RAWMIDI select SND_PCM + select SND_HWDEP help Say Y here to include support for FireWire devices based on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware: diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 533718a5c1e5..78087772a022 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,4 +1,4 @@ snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ - bebob_pcm.o \ + bebob_pcm.o bebob_hwdep.o \ bebob.o obj-m += snd-bebob.o diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index dbd12c360046..b7d70c2e4e87 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -153,6 +153,7 @@ bebob_probe(struct fw_unit *unit, bebob->unit = unit; mutex_init(&bebob->mutex); spin_lock_init(&bebob->lock); + init_waitqueue_head(&bebob->hwdep_wait); err = name_device(bebob, entry->vendor_id); if (err < 0) @@ -175,6 +176,10 @@ bebob_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_bebob_create_hwdep_device(bebob); + if (err < 0) + goto error; + err = snd_bebob_stream_init_duplex(bebob); if (err < 0) goto error; diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index b41bb913bac5..e8a5e447ff17 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "../lib.h" #include "../fcp.h" @@ -75,6 +77,11 @@ struct snd_bebob { rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES]; int sync_input_plug; + + /* for uapi */ + int dev_lock_count; + bool dev_lock_changed; + wait_queue_head_t hwdep_wait; }; static inline int @@ -175,12 +182,18 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); void snd_bebob_stream_update_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); +void snd_bebob_stream_lock_changed(struct snd_bebob *bebob); +int snd_bebob_stream_lock_try(struct snd_bebob *bebob); +void snd_bebob_stream_lock_release(struct snd_bebob *bebob); + void snd_bebob_proc_init(struct snd_bebob *bebob); int snd_bebob_create_midi_devices(struct snd_bebob *bebob); int snd_bebob_create_pcm_devices(struct snd_bebob *bebob); +int snd_bebob_create_hwdep_device(struct snd_bebob *bebob); + #define SND_BEBOB_DEV_ENTRY(vendor, model) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c new file mode 100644 index 000000000000..ce731f4d8b4f --- /dev/null +++ b/sound/firewire/bebob/bebob_hwdep.c @@ -0,0 +1,199 @@ +/* + * bebob_hwdep.c - a part of driver for BeBoB based devices + * + * Copyright (c) 2013-2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +/* + * This codes give three functionality. + * + * 1.get firewire node infomation + * 2.get notification about starting/stopping stream + * 3.lock/unlock stream + */ + +#include "bebob.h" + +static long +hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, + loff_t *offset) +{ + struct snd_bebob *bebob = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&bebob->lock); + + while (!bebob->dev_lock_changed) { + prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&bebob->lock); + schedule(); + finish_wait(&bebob->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&bebob->lock); + } + + memset(&event, 0, sizeof(event)); + if (bebob->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = (bebob->dev_lock_count > 0); + bebob->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + } + + spin_unlock_irq(&bebob->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int +hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) +{ + struct snd_bebob *bebob = hwdep->private_data; + unsigned int events; + + poll_wait(file, &bebob->hwdep_wait, wait); + + spin_lock_irq(&bebob->lock); + if (bebob->dev_lock_changed) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&bebob->lock); + + return events; +} + +static int +hwdep_get_info(struct snd_bebob *bebob, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(bebob->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_BEBOB; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int +hwdep_lock(struct snd_bebob *bebob) +{ + int err; + + spin_lock_irq(&bebob->lock); + + if (bebob->dev_lock_count == 0) { + bebob->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&bebob->lock); + + return err; +} + +static int +hwdep_unlock(struct snd_bebob *bebob) +{ + int err; + + spin_lock_irq(&bebob->lock); + + if (bebob->dev_lock_count == -1) { + bebob->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&bebob->lock); + + return err; +} + +static int +hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_bebob *bebob = hwdep->private_data; + + spin_lock_irq(&bebob->lock); + if (bebob->dev_lock_count == -1) + bebob->dev_lock_count = 0; + spin_unlock_irq(&bebob->lock); + + return 0; +} + +static int +hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_bebob *bebob = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return hwdep_get_info(bebob, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return hwdep_lock(bebob); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return hwdep_unlock(bebob); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int +hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define hwdep_compat_ioctl NULL +#endif + +static const struct snd_hwdep_ops hwdep_ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, +}; + +int snd_bebob_create_hwdep_device(struct snd_bebob *bebob) +{ + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep); + if (err < 0) + goto end; + strcpy(hwdep->name, "BeBoB"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB; + hwdep->ops = hwdep_ops; + hwdep->private_data = bebob; + hwdep->exclusive = true; +end: + return err; +} + diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 120a61b90c59..c04cea2c19a6 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -11,17 +11,35 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; + int err; + + err = snd_bebob_stream_lock_try(bebob); + if (err < 0) + goto end; atomic_inc(&bebob->capture_substreams); - return snd_bebob_stream_start_duplex(bebob, 0); + err = snd_bebob_stream_start_duplex(bebob, 0); + if (err < 0) + snd_bebob_stream_lock_release(bebob); +end: + return err; } static int midi_playback_open(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; + int err; + + err = snd_bebob_stream_lock_try(bebob); + if (err < 0) + goto end; atomic_inc(&bebob->playback_substreams); - return snd_bebob_stream_start_duplex(bebob, 0); + err = snd_bebob_stream_start_duplex(bebob, 0); + if (err < 0) + snd_bebob_stream_lock_release(bebob); +end: + return err; } static int midi_capture_close(struct snd_rawmidi_substream *substream) @@ -31,6 +49,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) atomic_dec(&bebob->capture_substreams); snd_bebob_stream_stop_duplex(bebob); + snd_bebob_stream_lock_release(bebob); return 0; } @@ -41,6 +60,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) atomic_dec(&bebob->playback_substreams); snd_bebob_stream_stop_duplex(bebob); + snd_bebob_stream_lock_release(bebob); return 0; } diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 7474309dad87..9d868171db4e 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -159,13 +159,17 @@ pcm_open(struct snd_pcm_substream *substream) bool internal; int err; - err = pcm_init_hw_params(bebob, substream); + err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; + err = pcm_init_hw_params(bebob, substream); + if (err < 0) + goto err_locked; + err = snd_bebob_stream_check_internal_clock(bebob, &internal); if (err < 0) - goto end; + goto err_locked; /* * When source of clock is internal or any PCM stream are running, @@ -178,7 +182,7 @@ pcm_open(struct snd_pcm_substream *substream) if (err < 0) { dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); - goto end; + goto err_locked; } substream->runtime->hw.rate_min = sampling_rate; @@ -186,14 +190,18 @@ pcm_open(struct snd_pcm_substream *substream) } snd_pcm_set_sync(substream); - end: return err; +err_locked: + snd_bebob_stream_lock_release(bebob); + return err; } static int pcm_close(struct snd_pcm_substream *substream) { + struct snd_bebob *bebob = substream->private_data; + snd_bebob_stream_lock_release(bebob); return 0; } diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 46b056c8f2a8..85b4fd661787 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -912,3 +912,42 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob) end: return err; } + +void snd_bebob_stream_lock_changed(struct snd_bebob *bebob) +{ + bebob->dev_lock_changed = true; + wake_up(&bebob->hwdep_wait); +} + +int snd_bebob_stream_lock_try(struct snd_bebob *bebob) +{ + int err; + + spin_lock_irq(&bebob->lock); + + /* user land lock this */ + if (bebob->dev_lock_count < 0) { + err = -EBUSY; + goto end; + } + + /* this is the first time */ + if (bebob->dev_lock_count++ == 0) + snd_bebob_stream_lock_changed(bebob); + err = 0; +end: + spin_unlock_irq(&bebob->lock); + return err; +} + +void snd_bebob_stream_lock_release(struct snd_bebob *bebob) +{ + spin_lock_irq(&bebob->lock); + + if (WARN_ON(bebob->dev_lock_count <= 0)) + goto end; + if (--bebob->dev_lock_count == 0) + snd_bebob_stream_lock_changed(bebob); +end: + spin_unlock_irq(&bebob->lock); +} -- cgit v1.2.3 From 9b1ee0b2cb8bffdbb3003b1d5205f3ae0592c15a Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 25 Apr 2014 22:45:30 +0900 Subject: ALSA: firewire/bebob: Add a workaround for M-Audio special Firewire series In post commit, a quirk of this firmware about transactions is reported. This commit apply a workaround for this quirk. They often fail transactions due to gap_count mismatch. This state is changed by generating bus reset. The fw_schedule_bus_reset() is an exported symbol in firewire-core. But there are no header for public. This commit moves its prototype from drivers/firewire/core.h to include/linux/firewire.h. This mismatch still affects bus management before generating this bus reset. It still takes a time to call driver's probe() because transactions are still often failed. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- drivers/firewire/core.h | 1 - include/linux/firewire.h | 3 +++ sound/firewire/bebob/bebob.c | 32 ++++++++++++++++++++++++++++---- sound/firewire/bebob/bebob.h | 1 + 4 files changed, 32 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index c98764aeeec6..870044e82316 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -118,7 +118,6 @@ int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid); void fw_core_remove_card(struct fw_card *card); int fw_compute_block_crc(__be32 *block); -void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset); void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); /* -cdev */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index c3683bdf28fe..d4b7683c722d 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -367,6 +367,9 @@ static inline int fw_stream_packet_destination_id(int tag, int channel, int sy) return tag << 14 | channel << 8 | sy; } +void fw_schedule_bus_reset(struct fw_card *card, bool delayed, + bool short_reset); + struct fw_descriptor { struct list_head link; size_t length; diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index e1dd4219ea6c..31b96b7264cf 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -247,10 +247,26 @@ bebob_probe(struct fw_unit *unit, if (err < 0) goto error; - err = snd_card_register(card); - if (err < 0) { - snd_bebob_stream_destroy_duplex(bebob); - goto error; + if (!bebob->maudio_special_quirk) { + err = snd_card_register(card); + if (err < 0) { + snd_bebob_stream_destroy_duplex(bebob); + goto error; + } + } else { + /* + * This is a workaround. This bus reset seems to have an effect + * to make devices correctly handling transactions. Without + * this, the devices have gap_count mismatch. This causes much + * failure of transaction. + * + * Just after registration, user-land application receive + * signals from dbus and starts I/Os. To avoid I/Os till the + * future bus reset, registration is done in next update(). + */ + bebob->deferred_registration = true; + fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card, + false, true); } dev_set_drvdata(&unit->device, bebob); @@ -273,6 +289,14 @@ bebob_update(struct fw_unit *unit) fcp_bus_reset(bebob->unit); snd_bebob_stream_update_duplex(bebob); + + if (bebob->deferred_registration) { + if (snd_card_register(bebob->card) < 0) { + snd_bebob_stream_destroy_duplex(bebob); + snd_card_free(bebob->card); + } + bebob->deferred_registration = false; + } } static void bebob_remove(struct fw_unit *unit) diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 4a54e746c5c6..91b26b0c649a 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -109,6 +109,7 @@ struct snd_bebob { /* for M-Audio special devices */ void *maudio_special_quirk; + bool deferred_registration; }; static inline int -- cgit v1.2.3 From 45fef5b88d1f2f47ecdefae6354372d440ca5c84 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 22 May 2014 12:47:47 +0200 Subject: ACPI: add dynamic_debug support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1a699476e258 ("ACPI / hotplug / PCI: Hotplug notifications from acpi_bus_notify()") added debug messages for a few common events. These debug messages are unconditionally enabled if CONFIG_DYNAMIC_DEBUG is defined, contrary to the documented meaning, making the ACPI system spew lots of unwanted noise on any kernel with dynamic debugging. The bug was introduced by commit fbfddae69657 ("ACPI: Add acpi_handle_() interfaces"), which added the CONFIG_DYNAMIC_DEBUG dependency without respecting its meaning. Fix by adding real support for dynamic_debug. Fixes: fbfddae69657 ("ACPI: Add acpi_handle_() interfaces") Signed-off-by: Bjørn Mork Signed-off-by: Rafael J. Wysocki --- drivers/acpi/utils.c | 64 ++++++++++++++++++++++++++++++++++++++++++---------- include/linux/acpi.h | 22 ++++++++++++++++-- 2 files changed, 72 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index bba526148583..07c8c5a5ee95 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "internal.h" @@ -456,6 +457,24 @@ acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code, } EXPORT_SYMBOL(acpi_evaluate_ost); +/** + * acpi_handle_path: Return the object path of handle + * + * Caller must free the returned buffer + */ +static char *acpi_handle_path(acpi_handle handle) +{ + struct acpi_buffer buffer = { + .length = ACPI_ALLOCATE_BUFFER, + .pointer = NULL + }; + + if (in_interrupt() || + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK) + return NULL; + return buffer.pointer; +} + /** * acpi_handle_printk: Print message with ACPI prefix and object path * @@ -469,29 +488,50 @@ acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...) { struct va_format vaf; va_list args; - struct acpi_buffer buffer = { - .length = ACPI_ALLOCATE_BUFFER, - .pointer = NULL - }; const char *path; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - if (in_interrupt() || - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK) - path = ""; - else - path = buffer.pointer; - - printk("%sACPI: %s: %pV", level, path, &vaf); + path = acpi_handle_path(handle); + printk("%sACPI: %s: %pV", level, path ? path : "" , &vaf); va_end(args); - kfree(buffer.pointer); + kfree(path); } EXPORT_SYMBOL(acpi_handle_printk); +#if defined(CONFIG_DYNAMIC_DEBUG) +/** + * __acpi_handle_debug: pr_debug with ACPI prefix and object path + * + * This function is called through acpi_handle_debug macro and debug + * prints a message with ACPI prefix and object path. This function + * acquires the global namespace mutex to obtain an object path. In + * interrupt context, it shows the object path as . + */ +void +__acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + const char *path; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + path = acpi_handle_path(handle); + __dynamic_pr_debug(descriptor, "ACPI: %s: %pV", path ? path : "", &vaf); + + va_end(args); + kfree(path); +} +EXPORT_SYMBOL(__acpi_handle_debug); +#endif + /** * acpi_has_method: Check whether @handle has a method named @name * @handle: ACPI device handle diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7a8f2cd66c8b..0e2569031a6f 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -589,6 +590,14 @@ static inline __printf(3, 4) void acpi_handle_printk(const char *level, void *handle, const char *fmt, ...) {} #endif /* !CONFIG_ACPI */ +#if defined(CONFIG_ACPI) && defined(CONFIG_DYNAMIC_DEBUG) +__printf(3, 4) +void __acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, const char *fmt, ...); +#else +#define __acpi_handle_debug(descriptor, handle, fmt, ...) \ + acpi_handle_printk(KERN_DEBUG, handle, fmt, ##__VA_ARGS__); +#endif + /* * acpi_handle_: Print message with ACPI prefix and object path * @@ -610,11 +619,19 @@ acpi_handle_printk(const char *level, void *handle, const char *fmt, ...) {} #define acpi_handle_info(handle, fmt, ...) \ acpi_handle_printk(KERN_INFO, handle, fmt, ##__VA_ARGS__) -/* REVISIT: Support CONFIG_DYNAMIC_DEBUG when necessary */ -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#if defined(DEBUG) #define acpi_handle_debug(handle, fmt, ...) \ acpi_handle_printk(KERN_DEBUG, handle, fmt, ##__VA_ARGS__) #else +#if defined(CONFIG_DYNAMIC_DEBUG) +#define acpi_handle_debug(handle, fmt, ...) \ +do { \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ + __acpi_handle_debug(&descriptor, handle, pr_fmt(fmt), \ + ##__VA_ARGS__); \ +} while (0) +#else #define acpi_handle_debug(handle, fmt, ...) \ ({ \ if (0) \ @@ -622,5 +639,6 @@ acpi_handle_printk(const char *level, void *handle, const char *fmt, ...) {} 0; \ }) #endif +#endif #endif /*_LINUX_ACPI_H*/ -- cgit v1.2.3 From 50dfb69d1bb0062e2811547525c73e9a45a423e9 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 26 May 2014 14:34:36 +0300 Subject: ASoC: jack: Basic GPIO descriptor conversion This patch does basic GPIO descriptor conversion to soc-jack. Even the GPIOs are still passed and requested using legacy GPIO numbers the driver internals are converted to use GPIO descriptor API. Motivation for this is to prepare soc-jack so that it will allow registering jack GPIO pins using both GPIO descriptors and legacy GPIO numbers. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-jack.c | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168d8ff4..c6bd40f2c40b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -606,6 +606,7 @@ struct snd_soc_jack_gpio { struct snd_soc_jack *jack; struct delayed_work work; + struct gpio_desc *desc; void *data; int (*jack_status_check)(void *data); diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index b903f822d1b2..7203842fea66 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -240,7 +241,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) int enable; int report; - enable = gpio_get_value_cansleep(gpio->gpio); + enable = gpiod_get_value_cansleep(gpio->desc); if (gpio->invert) enable = !enable; @@ -314,14 +315,16 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (ret) goto undo; - ret = gpio_direction_input(gpios[i].gpio); + gpios[i].desc = gpio_to_desc(gpios[i].gpio); + + ret = gpiod_direction_input(gpios[i].desc); if (ret) goto err; INIT_DELAYED_WORK(&gpios[i].work, gpio_work); gpios[i].jack = jack; - ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio), + ret = request_any_context_irq(gpiod_to_irq(gpios[i].desc), gpio_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, @@ -331,7 +334,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, goto err; if (gpios[i].wake) { - ret = irq_set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); + ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1); if (ret != 0) dev_err(jack->codec->dev, "ASoC: " "Failed to mark GPIO %d as wake source: %d\n", @@ -339,7 +342,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, } /* Expose GPIO value over sysfs for diagnostic purposes */ - gpio_export(gpios[i].gpio, false); + gpiod_export(gpios[i].desc, false); /* Update initial jack status */ schedule_delayed_work(&gpios[i].work, @@ -372,10 +375,10 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, int i; for (i = 0; i < count; i++) { - gpio_unexport(gpios[i].gpio); - free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]); + gpiod_unexport(gpios[i].desc); + free_irq(gpiod_to_irq(gpios[i].desc), &gpios[i]); cancel_delayed_work_sync(&gpios[i].work); - gpio_free(gpios[i].gpio); + gpiod_put(gpios[i].desc); gpios[i].jack = NULL; } } -- cgit v1.2.3 From f025d3b9c64e1f7feb75a559d4a12f5f8c6a4a25 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 26 May 2014 14:34:37 +0300 Subject: ASoC: jack: Add support for GPIO descriptor defined jack pins Allow jack GPIO pins be defined also using GPIO descriptor-based interface in addition to legacy GPIO numbers. This is done by adding two new fields to struct snd_soc_jack_gpio: idx and gpiod_dev. Legacy GPIO numbers are used only when GPIO consumer device gpiod_dev is NULL and otherwise idx is the descriptor index within the GPIO consumer device. New function snd_soc_jack_add_gpiods() is added for typical cases where all GPIO descriptor jack pins belong to same GPIO consumer device. For other cases the caller must set the gpiod_dev in struct snd_soc_jack_gpio before calling snd_soc_jack_add_gpios(). Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- include/sound/soc.h | 16 +++++++++++- sound/soc/soc-jack.c | 73 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 72 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c6bd40f2c40b..61bea882a74b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -453,6 +453,9 @@ int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage); #ifdef CONFIG_GPIOLIB int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, struct snd_soc_jack_gpio *gpios); +int snd_soc_jack_add_gpiods(struct device *gpiod_dev, + struct snd_soc_jack *jack, + int count, struct snd_soc_jack_gpio *gpios); void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, struct snd_soc_jack_gpio *gpios); #else @@ -462,6 +465,13 @@ static inline int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, return 0; } +int snd_soc_jack_add_gpiods(struct device *gpiod_dev, + struct snd_soc_jack *jack, + int count, struct snd_soc_jack_gpio *gpios) +{ + return 0; +} + static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, struct snd_soc_jack_gpio *gpios) { @@ -586,7 +596,9 @@ struct snd_soc_jack_zone { /** * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection * - * @gpio: gpio number + * @gpio: legacy gpio number + * @idx: gpio descriptor index within the GPIO consumer device + * @gpiod_dev GPIO consumer device * @name: gpio name * @report: value to report when jack detected * @invert: report presence in low state @@ -598,6 +610,8 @@ struct snd_soc_jack_zone { */ struct snd_soc_jack_gpio { unsigned int gpio; + unsigned int idx; + struct device *gpiod_dev; const char *name; int report; int invert; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 7203842fea66..d0d98810af91 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -298,24 +298,41 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, int i, ret; for (i = 0; i < count; i++) { - if (!gpio_is_valid(gpios[i].gpio)) { - dev_err(jack->codec->dev, "ASoC: Invalid gpio %d\n", - gpios[i].gpio); - ret = -EINVAL; - goto undo; - } if (!gpios[i].name) { - dev_err(jack->codec->dev, "ASoC: No name for gpio %d\n", - gpios[i].gpio); + dev_err(jack->codec->dev, + "ASoC: No name for gpio at index %d\n", i); ret = -EINVAL; goto undo; } - ret = gpio_request(gpios[i].gpio, gpios[i].name); - if (ret) - goto undo; - - gpios[i].desc = gpio_to_desc(gpios[i].gpio); + if (gpios[i].gpiod_dev) { + /* GPIO descriptor */ + gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev, + gpios[i].name, + gpios[i].idx); + if (IS_ERR(gpios[i].desc)) { + ret = PTR_ERR(gpios[i].desc); + dev_err(gpios[i].gpiod_dev, + "ASoC: Cannot get gpio at index %d: %d", + i, ret); + goto undo; + } + } else { + /* legacy GPIO number */ + if (!gpio_is_valid(gpios[i].gpio)) { + dev_err(jack->codec->dev, + "ASoC: Invalid gpio %d\n", + gpios[i].gpio); + ret = -EINVAL; + goto undo; + } + + ret = gpio_request(gpios[i].gpio, gpios[i].name); + if (ret) + goto undo; + + gpios[i].desc = gpio_to_desc(gpios[i].gpio); + } ret = gpiod_direction_input(gpios[i].desc); if (ret) @@ -336,9 +353,9 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (gpios[i].wake) { ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1); if (ret != 0) - dev_err(jack->codec->dev, "ASoC: " - "Failed to mark GPIO %d as wake source: %d\n", - gpios[i].gpio, ret); + dev_err(jack->codec->dev, + "ASoC: Failed to mark GPIO at index %d as wake source: %d\n", + i, ret); } /* Expose GPIO value over sysfs for diagnostic purposes */ @@ -360,6 +377,30 @@ undo: } EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios); +/** + * snd_soc_jack_add_gpiods - Associate GPIO descriptor pins with an ASoC jack + * + * @gpiod_dev: GPIO consumer device + * @jack: ASoC jack + * @count: number of pins + * @gpios: array of gpio pins + * + * This function will request gpio, set data direction and request irq + * for each gpio in the array. + */ +int snd_soc_jack_add_gpiods(struct device *gpiod_dev, + struct snd_soc_jack *jack, + int count, struct snd_soc_jack_gpio *gpios) +{ + int i; + + for (i = 0; i < count; i++) + gpios[i].gpiod_dev = gpiod_dev; + + return snd_soc_jack_add_gpios(jack, count, gpios); +} +EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpiods); + /** * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack * -- cgit v1.2.3 From 87c1936426d13d4f0dba29430c792e6d3562f2be Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 26 May 2014 11:51:14 +0300 Subject: ASoC: omap-pcm: Move omap-pcm under include/sound Make including the omap-pcm.h outside sound/soc/omap more convenient. Signed-off-by: Jyri Sarha Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown --- include/sound/omap-pcm.h | 30 ++++++++++++++++++++++++++++++ sound/soc/davinci/davinci-mcasp.c | 2 +- sound/soc/omap/omap-dmic.c | 2 +- sound/soc/omap/omap-hdmi.c | 2 +- sound/soc/omap/omap-mcbsp.c | 2 +- sound/soc/omap/omap-mcpdm.c | 2 +- sound/soc/omap/omap-pcm.h | 30 ------------------------------ 7 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 include/sound/omap-pcm.h delete mode 100644 sound/soc/omap/omap-pcm.h (limited to 'include') diff --git a/include/sound/omap-pcm.h b/include/sound/omap-pcm.h new file mode 100644 index 000000000000..c1d2f31d71e9 --- /dev/null +++ b/include/sound/omap-pcm.h @@ -0,0 +1,30 @@ +/* + * omap-pcm.h - OMAP PCM driver + * + * Copyright (C) 2014 Texas Instruments, Inc. + * + * Author: Peter Ujfalusi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __OMAP_PCM_H__ +#define __OMAP_PCM_H__ + +#if IS_ENABLED(CONFIG_SND_OMAP_SOC) +int omap_pcm_platform_register(struct device *dev); +#else +static inline int omap_pcm_platform_register(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_SND_OMAP_SOC */ + +#endif /* __OMAP_PCM_H__ */ diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 14058dc6eaf8..9afb14629a17 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -33,10 +33,10 @@ #include #include #include +#include #include "davinci-pcm.h" #include "davinci-mcasp.h" -#include "../omap/omap-pcm.h" #define MCASP_MAX_AFIFO_DEPTH 64 diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 53da041896c4..6925d7141215 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -40,9 +40,9 @@ #include #include #include +#include #include "omap-dmic.h" -#include "omap-pcm.h" struct omap_dmic { struct device *dev; diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c index 537a1ec8ad61..eb9c39299f81 100644 --- a/sound/soc/omap/omap-hdmi.c +++ b/sound/soc/omap/omap-hdmi.c @@ -34,9 +34,9 @@ #include #include #include