From 7ad920b504a980adcab4d3f6b85695526e6fd7bb Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:05 -0700 Subject: ceph: documentation Mount options, syntax. Signed-off-by: Sage Weil diff --git a/Documentation/filesystems/ceph.txt b/Documentation/filesystems/ceph.txt new file mode 100644 index 0000000..6e03917 --- /dev/null +++ b/Documentation/filesystems/ceph.txt @@ -0,0 +1,139 @@ +Ceph Distributed File System +============================ + +Ceph is a distributed network file system designed to provide good +performance, reliability, and scalability. + +Basic features include: + + * POSIX semantics + * Seamless scaling from 1 to many thousands of nodes + * High availability and reliability. No single points of failure. + * N-way replication of data across storage nodes + * Fast recovery from node failures + * Automatic rebalancing of data on node addition/removal + * Easy deployment: most FS components are userspace daemons + +Also, + * Flexible snapshots (on any directory) + * Recursive accounting (nested files, directories, bytes) + +In contrast to cluster filesystems like GFS, OCFS2, and GPFS that rely +on symmetric access by all clients to shared block devices, Ceph +separates data and metadata management into independent server +clusters, similar to Lustre. Unlike Lustre, however, metadata and +storage nodes run entirely as user space daemons. Storage nodes +utilize btrfs to store data objects, leveraging its advanced features +(checksumming, metadata replication, etc.). File data is striped +across storage nodes in large chunks to distribute workload and +facilitate high throughputs. When storage nodes fail, data is +re-replicated in a distributed fashion by the storage nodes themselves +(with some minimal coordination from a cluster monitor), making the +system extremely efficient and scalable. + +Metadata servers effectively form a large, consistent, distributed +in-memory cache above the file namespace that is extremely scalable, +dynamically redistributes metadata in response to workload changes, +and can tolerate arbitrary (well, non-Byzantine) node failures. The +metadata server takes a somewhat unconventional approach to metadata +storage to significantly improve performance for common workloads. In +particular, inodes with only a single link are embedded in +directories, allowing entire directories of dentries and inodes to be +loaded into its cache with a single I/O operation. The contents of +extremely large directories can be fragmented and managed by +independent metadata servers, allowing scalable concurrent access. + +The system offers automatic data rebalancing/migration when scaling +from a small cluster of just a few nodes to many hundreds, without +requiring an administrator carve the data set into static volumes or +go through the tedious process of migrating data between servers. +When the file system approaches full, new nodes can be easily added +and things will "just work." + +Ceph includes flexible snapshot mechanism that allows a user to create +a snapshot on any subdirectory (and its nested contents) in the +system. Snapshot creation and deletion are as simple as 'mkdir +.snap/foo' and 'rmdir .snap/foo'. + +Ceph also provides some recursive accounting on directories for nested +files and bytes. That is, a 'getfattr -d foo' on any directory in the +system will reveal the total number of nested regular files and +subdirectories, and a summation of all nested file sizes. This makes +the identification of large disk space consumers relatively quick, as +no 'du' or similar recursive scan of the file system is required. + + +Mount Syntax +============ + +The basic mount syntax is: + + # mount -t ceph monip[:port][,monip2[:port]...]:/[subdir] mnt + +You only need to specify a single monitor, as the client will get the +full list when it connects. (However, if the monitor you specify +happens to be down, the mount won't succeed.) The port can be left +off if the monitor is using the default. So if the monitor is at +1.2.3.4, + + # mount -t ceph 1.2.3.4:/ /mnt/ceph + +is sufficient. If /sbin/mount.ceph is installed, a hostname can be +used instead of an IP address. + + + +Mount Options +============= + + ip=A.B.C.D[:N] + Specify the IP and/or port the client should bind to locally. + There is normally not much reason to do this. If the IP is not + specified, the client's IP address is determined by looking at the + address it's connection to the monitor originates from. + + wsize=X + Specify the maximum write size in bytes. By default there is no + maximu. Ceph will normally size writes based on the file stripe + size. + + rsize=X + Specify the maximum readahead. + + mount_timeout=X + Specify the timeout value for mount (in seconds), in the case + of a non-responsive Ceph file system. The default is 30 + seconds. + + rbytes + When stat() is called on a directory, set st_size to 'rbytes', + the summation of file sizes over all files nested beneath that + directory. This is the default. + + norbytes + When stat() is called on a directory, set st_size to the + number of entries in that directory. + + nocrc + Disable CRC32C calculation for data writes. If set, the OSD + must rely on TCP's error correction to detect data corruption + in the data payload. + + noasyncreaddir + Disable client's use its local cache to satisfy readdir + requests. (This does not change correctness; the client uses + cached metadata only when a lease or capability ensures it is + valid.) + + +More Information +================ + +For more information on Ceph, see the home page at + http://ceph.newdream.net/ + +The Linux kernel client source tree is available at + git://ceph.newdream.net/linux-ceph-client.git + +and the source for the full system is at + git://ceph.newdream.net/ceph.git -- cgit v0.10.2 From 0dee3c28af2fbe22ca62739a7f57da5435d35793 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:06 -0700 Subject: ceph: on-wire types These headers describe the types used to exchange messages between the Ceph client and various servers. All types are little-endian and packed. These headers are shared between the kernel and userspace, so all types are in terms of e.g. __u32. Additionally, we define a few magic values to identify the current version of the protocol(s) in use, so that discrepancies to be detected on mount. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.c b/fs/ceph/ceph_fs.c new file mode 100644 index 0000000..9371ff1 --- /dev/null +++ b/fs/ceph/ceph_fs.c @@ -0,0 +1,80 @@ +/* + * Some non-inline ceph helpers + */ +#include "types.h" + +int ceph_flags_to_mode(int flags) +{ +#ifdef O_DIRECTORY /* fixme */ + if ((flags & O_DIRECTORY) == O_DIRECTORY) + return CEPH_FILE_MODE_PIN; +#endif +#ifdef O_LAZY + if (flags & O_LAZY) + return CEPH_FILE_MODE_LAZY; +#endif + if ((flags & O_APPEND) == O_APPEND) + flags |= O_WRONLY; + + flags &= O_ACCMODE; + if ((flags & O_RDWR) == O_RDWR) + return CEPH_FILE_MODE_RDWR; + if ((flags & O_WRONLY) == O_WRONLY) + return CEPH_FILE_MODE_WR; + return CEPH_FILE_MODE_RD; +} + +int ceph_caps_for_mode(int mode) +{ + switch (mode) { + case CEPH_FILE_MODE_PIN: + return CEPH_CAP_PIN; + case CEPH_FILE_MODE_RD: + return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED | + CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE; + case CEPH_FILE_MODE_RDWR: + return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED | + CEPH_CAP_FILE_EXCL | + CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE | + CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | + CEPH_CAP_AUTH_SHARED | CEPH_CAP_AUTH_EXCL | + CEPH_CAP_XATTR_SHARED | CEPH_CAP_XATTR_EXCL; + case CEPH_FILE_MODE_WR: + return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED | + CEPH_CAP_FILE_EXCL | + CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | + CEPH_CAP_AUTH_SHARED | CEPH_CAP_AUTH_EXCL | + CEPH_CAP_XATTR_SHARED | CEPH_CAP_XATTR_EXCL; + } + return 0; +} + +/* Name hashing routines. Initial hash value */ +/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */ +#define ceph_init_name_hash() 0 + +/* partial hash update function. Assume roughly 4 bits per character */ +static unsigned long ceph_partial_name_hash(unsigned long c, + unsigned long prevhash) +{ + return (prevhash + (c << 4) + (c >> 4)) * 11; +} + +/* + * Finally: cut down the number of bits to a int value (and try to avoid + * losing bits) + */ +static unsigned long ceph_end_name_hash(unsigned long hash) +{ + return hash & 0xffffffff; +} + +/* Compute the hash for a name string. */ +unsigned int ceph_full_name_hash(const char *name, unsigned int len) +{ + unsigned long hash = ceph_init_name_hash(); + while (len--) + hash = ceph_partial_name_hash(*name++, hash); + return ceph_end_name_hash(hash); +} + diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h new file mode 100644 index 0000000..21ed51b --- /dev/null +++ b/fs/ceph/ceph_fs.h @@ -0,0 +1,629 @@ +/* + * ceph_fs.h - Ceph constants and data types to share between kernel and + * user space. + * + * Most types in this file are defined as little-endian, and are + * primarily intended to describe data structures that pass over the + * wire or that are stored on disk. + * + * LGPL2 + */ + +#ifndef _FS_CEPH_CEPH_FS_H +#define _FS_CEPH_CEPH_FS_H + +#include "msgr.h" +#include "rados.h" + +/* + * Ceph release version + */ +#define CEPH_VERSION_MAJOR 0 +#define CEPH_VERSION_MINOR 16 +#define CEPH_VERSION_PATCH 1 + +#define _CEPH_STRINGIFY(x) #x +#define CEPH_STRINGIFY(x) _CEPH_STRINGIFY(x) +#define CEPH_MAKE_VERSION(x, y, z) CEPH_STRINGIFY(x) "." CEPH_STRINGIFY(y) \ + "." CEPH_STRINGIFY(z) +#define CEPH_VERSION CEPH_MAKE_VERSION(CEPH_VERSION_MAJOR, \ + CEPH_VERSION_MINOR, CEPH_VERSION_PATCH) + +/* + * subprotocol versions. when specific messages types or high-level + * protocols change, bump the affected components. we keep rev + * internal cluster protocols separately from the public, + * client-facing protocol. + */ +#define CEPH_OSD_PROTOCOL 7 /* cluster internal */ +#define CEPH_MDS_PROTOCOL 9 /* cluster internal */ +#define CEPH_MON_PROTOCOL 4 /* cluster internal */ +#define CEPH_OSDC_PROTOCOL 20 /* server/client */ +#define CEPH_MDSC_PROTOCOL 29 /* server/client */ +#define CEPH_MONC_PROTOCOL 14 /* server/client */ + + +#define CEPH_INO_ROOT 1 + +/* arbitrary limit on max # of monitors (cluster of 3 is typical) */ +#define CEPH_MAX_MON 31 + + +unsigned int ceph_full_name_hash(const char *name, unsigned int len); + + +/* + * ceph_file_layout - describe data layout for a file/inode + */ +struct ceph_file_layout { + /* file -> object mapping */ + __le32 fl_stripe_unit; /* stripe unit, in bytes. must be multiple + of page size. */ + __le32 fl_stripe_count; /* over this many objects */ + __le32 fl_object_size; /* until objects are this big, then move to + new objects */ + __le32 fl_cas_hash; /* 0 = none; 1 = sha256 */ + + /* pg -> disk layout */ + __le32 fl_object_stripe_unit; /* for per-object parity, if any */ + + /* object -> pg layout */ + __le32 fl_pg_preferred; /* preferred primary for pg (-1 for none) */ + __le32 fl_pg_pool; /* namespace, crush ruleset, rep level */ +} __attribute__ ((packed)); + + + + +/********************************************* + * message layer + */ + +/* + * message types + */ + +/* misc */ +#define CEPH_MSG_SHUTDOWN 1 +#define CEPH_MSG_PING 2 + +/* client <-> monitor */ +#define CEPH_MSG_MON_MAP 4 +#define CEPH_MSG_MON_GET_MAP 5 +#define CEPH_MSG_CLIENT_MOUNT 10 +#define CEPH_MSG_CLIENT_MOUNT_ACK 11 +#define CEPH_MSG_STATFS 13 +#define CEPH_MSG_STATFS_REPLY 14 +#define CEPH_MSG_MON_SUBSCRIBE 15 +#define CEPH_MSG_MON_SUBSCRIBE_ACK 16 + +/* client <-> mds */ +#define CEPH_MSG_MDS_GETMAP 20 +#define CEPH_MSG_MDS_MAP 21 + +#define CEPH_MSG_CLIENT_SESSION 22 +#define CEPH_MSG_CLIENT_RECONNECT 23 + +#define CEPH_MSG_CLIENT_REQUEST 24 +#define CEPH_MSG_CLIENT_REQUEST_FORWARD 25 +#define CEPH_MSG_CLIENT_REPLY 26 +#define CEPH_MSG_CLIENT_CAPS 0x310 +#define CEPH_MSG_CLIENT_LEASE 0x311 +#define CEPH_MSG_CLIENT_SNAP 0x312 +#define CEPH_MSG_CLIENT_CAPRELEASE 0x313 + +/* osd */ +#define CEPH_MSG_OSD_GETMAP 40 +#define CEPH_MSG_OSD_MAP 41 +#define CEPH_MSG_OSD_OP 42 +#define CEPH_MSG_OSD_OPREPLY 43 + + +struct ceph_mon_statfs { + __le64 have_version; + struct ceph_fsid fsid; + __le64 tid; +} __attribute__ ((packed)); + +struct ceph_statfs { + __le64 kb, kb_used, kb_avail; + __le64 num_objects; +} __attribute__ ((packed)); + +struct ceph_mon_statfs_reply { + struct ceph_fsid fsid; + __le64 tid; + __le64 version; + struct ceph_statfs st; +} __attribute__ ((packed)); + +struct ceph_osd_getmap { + __le64 have_version; + struct ceph_fsid fsid; + __le32 start; +} __attribute__ ((packed)); + +struct ceph_mds_getmap { + __le64 have_version; + struct ceph_fsid fsid; +} __attribute__ ((packed)); + +struct ceph_client_mount { + __le64 have_version; +} __attribute__ ((packed)); + +struct ceph_mon_subscribe_item { + __le64 have; + __u8 onetime; +} __attribute__ ((packed)); + +/* + * mds states + * > 0 -> in + * <= 0 -> out + */ +#define CEPH_MDS_STATE_DNE 0 /* down, does not exist. */ +#define CEPH_MDS_STATE_STOPPED -1 /* down, once existed, but no subtrees. + empty log. */ +#define CEPH_MDS_STATE_BOOT -4 /* up, boot announcement. */ +#define CEPH_MDS_STATE_STANDBY -5 /* up, idle. waiting for assignment. */ +#define CEPH_MDS_STATE_CREATING -6 /* up, creating MDS instance. */ +#define CEPH_MDS_STATE_STARTING -7 /* up, starting previously stopped mds */ +#define CEPH_MDS_STATE_STANDBY_REPLAY -8 /* up, tailing active node's journal */ + +#define CEPH_MDS_STATE_REPLAY 8 /* up, replaying journal. */ +#define CEPH_MDS_STATE_RESOLVE 9 /* up, disambiguating distributed + operations (import, rename, etc.) */ +#define CEPH_MDS_STATE_RECONNECT 10 /* up, reconnect to clients */ +#define CEPH_MDS_STATE_REJOIN 11 /* up, rejoining distributed cache */ +#define CEPH_MDS_STATE_CLIENTREPLAY 12 /* up, replaying client operations */ +#define CEPH_MDS_STATE_ACTIVE 13 /* up, active */ +#define CEPH_MDS_STATE_STOPPING 14 /* up, but exporting metadata */ + +extern const char *ceph_mds_state_name(int s); + + +/* + * metadata lock types. + * - these are bitmasks.. we can compose them + * - they also define the lock ordering by the MDS + * - a few of these are internal to the mds + */ +#define CEPH_LOCK_DN 1 +#define CEPH_LOCK_ISNAP 2 +#define CEPH_LOCK_IVERSION 4 /* mds internal */ +#define CEPH_LOCK_IFILE 8 /* mds internal */ +#define CEPH_LOCK_IAUTH 32 +#define CEPH_LOCK_ILINK 64 +#define CEPH_LOCK_IDFT 128 /* dir frag tree */ +#define CEPH_LOCK_INEST 256 /* mds internal */ +#define CEPH_LOCK_IXATTR 512 +#define CEPH_LOCK_INO 2048 /* immutable inode bits; not a lock */ + +/* client_session ops */ +enum { + CEPH_SESSION_REQUEST_OPEN, + CEPH_SESSION_OPEN, + CEPH_SESSION_REQUEST_CLOSE, + CEPH_SESSION_CLOSE, + CEPH_SESSION_REQUEST_RENEWCAPS, + CEPH_SESSION_RENEWCAPS, + CEPH_SESSION_STALE, + CEPH_SESSION_RECALL_STATE, +}; + +extern const char *ceph_session_op_name(int op); + +struct ceph_mds_session_head { + __le32 op; + __le64 seq; + struct ceph_timespec stamp; + __le32 max_caps, max_leases; +} __attribute__ ((packed)); + +/* client_request */ +/* + * metadata ops. + * & 0x001000 -> write op + * & 0x010000 -> follow symlink (e.g. stat(), not lstat()). + & & 0x100000 -> use weird ino/path trace + */ +#define CEPH_MDS_OP_WRITE 0x001000 +enum { + CEPH_MDS_OP_LOOKUP = 0x00100, + CEPH_MDS_OP_GETATTR = 0x00101, + CEPH_MDS_OP_LOOKUPHASH = 0x00102, + CEPH_MDS_OP_LOOKUPPARENT = 0x00103, + + CEPH_MDS_OP_SETXATTR = 0x01105, + CEPH_MDS_OP_RMXATTR = 0x01106, + CEPH_MDS_OP_SETLAYOUT = 0x01107, + CEPH_MDS_OP_SETATTR = 0x01108, + + CEPH_MDS_OP_MKNOD = 0x01201, + CEPH_MDS_OP_LINK = 0x01202, + CEPH_MDS_OP_UNLINK = 0x01203, + CEPH_MDS_OP_RENAME = 0x01204, + CEPH_MDS_OP_MKDIR = 0x01220, + CEPH_MDS_OP_RMDIR = 0x01221, + CEPH_MDS_OP_SYMLINK = 0x01222, + + CEPH_MDS_OP_CREATE = 0x00301, + CEPH_MDS_OP_OPEN = 0x00302, + CEPH_MDS_OP_READDIR = 0x00305, + + CEPH_MDS_OP_LOOKUPSNAP = 0x00400, + CEPH_MDS_OP_MKSNAP = 0x01400, + CEPH_MDS_OP_RMSNAP = 0x01401, + CEPH_MDS_OP_LSSNAP = 0x00402, +}; + +extern const char *ceph_mds_op_name(int op); + + +#define CEPH_SETATTR_MODE 1 +#define CEPH_SETATTR_UID 2 +#define CEPH_SETATTR_GID 4 +#define CEPH_SETATTR_MTIME 8 +#define CEPH_SETATTR_ATIME 16 +#define CEPH_SETATTR_SIZE 32 +#define CEPH_SETATTR_CTIME 64 + +union ceph_mds_request_args { + struct { + __le32 mask; /* CEPH_CAP_* */ + } __attribute__ ((packed)) getattr; + struct { + __le32 mode; + __le32 uid; + __le32 gid; + struct ceph_timespec mtime; + struct ceph_timespec atime; + __le64 size, old_size; /* old_size needed by truncate */ + __le32 mask; /* CEPH_SETATTR_* */ + } __attribute__ ((packed)) setattr; + struct { + __le32 frag; /* which dir fragment */ + __le32 max_entries; /* how many dentries to grab */ + } __attribute__ ((packed)) readdir; + struct { + __le32 mode; + __le32 rdev; + } __attribute__ ((packed)) mknod; + struct { + __le32 mode; + } __attribute__ ((packed)) mkdir; + struct { + __le32 flags; + __le32 mode; + __le32 stripe_unit; /* layout for newly created file */ + __le32 stripe_count; /* ... */ + __le32 object_size; + __le32 file_replication; + __le32 preferred; + } __attribute__ ((packed)) open; + struct { + __le32 flags; + } __attribute__ ((packed)) setxattr; + struct { + struct ceph_file_layout layout; + } __attribute__ ((packed)) setlayout; +} __attribute__ ((packed)); + +#define CEPH_MDS_FLAG_REPLAY 1 /* this is a replayed op */ +#define CEPH_MDS_FLAG_WANT_DENTRY 2 /* want dentry in reply */ + +struct ceph_mds_request_head { + __le64 tid, oldest_client_tid; + __le32 mdsmap_epoch; /* on client */ + __le32 flags; /* CEPH_MDS_FLAG_* */ + __u8 num_retry, num_fwd; /* count retry, fwd attempts */ + __le16 num_releases; /* # include cap/lease release records */ + __le32 op; /* mds op code */ + __le32 caller_uid, caller_gid; + __le64 ino; /* use this ino for openc, mkdir, mknod, + etc. (if replaying) */ + union ceph_mds_request_args args; +} __attribute__ ((packed)); + +/* cap/lease release record */ +struct ceph_mds_request_release { + __le64 ino, cap_id; /* ino and unique cap id */ + __le32 caps, wanted; /* new issued, wanted */ + __le32 seq, issue_seq, mseq; + __le32 dname_seq; /* if releasing a dentry lease, a */ + __le32 dname_len; /* string follows. */ +} __attribute__ ((packed)); + +/* client reply */ +struct ceph_mds_reply_head { + __le64 tid; + __le32 op; + __le32 result; + __le32 mdsmap_epoch; + __u8 safe; /* true if committed to disk */ + __u8 is_dentry, is_target; /* true if dentry, target inode records + are included with reply */ +} __attribute__ ((packed)); + +/* one for each node split */ +struct ceph_frag_tree_split { + __le32 frag; /* this frag splits... */ + __le32 by; /* ...by this many bits */ +} __attribute__ ((packed)); + +struct ceph_frag_tree_head { + __le32 nsplits; /* num ceph_frag_tree_split records */ + struct ceph_frag_tree_split splits[]; +} __attribute__ ((packed)); + +/* capability issue, for bundling with mds reply */ +struct ceph_mds_reply_cap { + __le32 caps, wanted; /* caps issued, wanted */ + __le64 cap_id; + __le32 seq, mseq; + __le64 realm; /* snap realm */ + __u8 flags; /* CEPH_CAP_FLAG_* */ +} __attribute__ ((packed)); + +#define CEPH_CAP_FLAG_AUTH 1 /* cap is issued by auth mds */ + +/* inode record, for bundling with mds reply */ +struct ceph_mds_reply_inode { + __le64 ino; + __le64 snapid; + __le32 rdev; + __le64 version; /* inode version */ + __le64 xattr_version; /* version for xattr blob */ + struct ceph_mds_reply_cap cap; /* caps issued for this inode */ + struct ceph_file_layout layout; + struct ceph_timespec ctime, mtime, atime; + __le32 time_warp_seq; + __le64 size, max_size, truncate_size; + __le32 truncate_seq; + __le32 mode, uid, gid; + __le32 nlink; + __le64 files, subdirs, rbytes, rfiles, rsubdirs; /* dir stats */ + struct ceph_timespec rctime; + struct ceph_frag_tree_head fragtree; /* (must be at end of struct) */ +} __attribute__ ((packed)); +/* followed by frag array, then symlink string, then xattr blob */ + +/* reply_lease follows dname, and reply_inode */ +struct ceph_mds_reply_lease { + __le16 mask; /* lease type(s) */ + __le32 duration_ms; /* lease duration */ + __le32 seq; +} __attribute__ ((packed)); + +struct ceph_mds_reply_dirfrag { + __le32 frag; /* fragment */ + __le32 auth; /* auth mds, if this is a delegation point */ + __le32 ndist; /* number of mds' this is replicated on */ + __le32 dist[]; +} __attribute__ ((packed)); + +/* file access modes */ +#define CEPH_FILE_MODE_PIN 0 +#define CEPH_FILE_MODE_RD 1 +#define CEPH_FILE_MODE_WR 2 +#define CEPH_FILE_MODE_RDWR 3 /* RD | WR */ +#define CEPH_FILE_MODE_LAZY 4 /* lazy io */ +#define CEPH_FILE_MODE_NUM 8 /* bc these are bit fields.. mostly */ + +int ceph_flags_to_mode(int flags); + + +/* capability bits */ +#define CEPH_CAP_PIN 1 /* no specific capabilities beyond the pin */ + +/* generic cap bits */ +#define CEPH_CAP_GSHARED 1 /* client can reads */ +#define CEPH_CAP_GEXCL 2 /* client can read and update */ +#define CEPH_CAP_GCACHE 4 /* (file) client can cache reads */ +#define CEPH_CAP_GRD 8 /* (file) client can read */ +#define CEPH_CAP_GWR 16 /* (file) client can write */ +#define CEPH_CAP_GBUFFER 32 /* (file) client can buffer writes */ +#define CEPH_CAP_GWREXTEND 64 /* (file) client can extend EOF */ +#define CEPH_CAP_GLAZYIO 128 /* (file) client can perform lazy io */ + +/* per-lock shift */ +#define CEPH_CAP_SAUTH 2 +#define CEPH_CAP_SLINK 4 +#define CEPH_CAP_SXATTR 6 +#define CEPH_CAP_SFILE 8 /* goes at the end (uses >2 cap bits) */ + +#define CEPH_CAP_BITS 16 + +/* composed values */ +#define CEPH_CAP_AUTH_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SAUTH) +#define CEPH_CAP_AUTH_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SAUTH) +#define CEPH_CAP_LINK_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SLINK) +#define CEPH_CAP_LINK_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SLINK) +#define CEPH_CAP_XATTR_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SXATTR) +#define CEPH_CAP_XATTR_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SXATTR) +#define CEPH_CAP_FILE(x) (x << CEPH_CAP_SFILE) +#define CEPH_CAP_FILE_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SFILE) +#define CEPH_CAP_FILE_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SFILE) +#define CEPH_CAP_FILE_CACHE (CEPH_CAP_GCACHE << CEPH_CAP_SFILE) +#define CEPH_CAP_FILE_RD (CEPH_CAP_GRD << CEPH_CAP_SFILE) +#define CEPH_CAP_FILE_WR (CEPH_CAP_GWR << CEPH_CAP_SFILE) +#define CEPH_CAP_FILE_BUFFER (CEPH_CAP_GBUFFER << CEPH_CAP_SFILE) +#define CEPH_CAP_FILE_WREXTEND (CEPH_CAP_GWREXTEND << CEPH_CAP_SFILE) +#define CEPH_CAP_FILE_LAZYIO (CEPH_CAP_GLAZYIO << CEPH_CAP_SFILE) + +/* cap masks (for getattr) */ +#define CEPH_STAT_CAP_INODE CEPH_CAP_PIN +#define CEPH_STAT_CAP_TYPE CEPH_CAP_PIN /* mode >> 12 */ +#define CEPH_STAT_CAP_SYMLINK CEPH_CAP_PIN +#define CEPH_STAT_CAP_UID CEPH_CAP_AUTH_SHARED +#define CEPH_STAT_CAP_GID CEPH_CAP_AUTH_SHARED +#define CEPH_STAT_CAP_MODE CEPH_CAP_AUTH_SHARED +#define CEPH_STAT_CAP_NLINK CEPH_CAP_LINK_SHARED +#define CEPH_STAT_CAP_LAYOUT CEPH_CAP_FILE_SHARED +#define CEPH_STAT_CAP_MTIME CEPH_CAP_FILE_SHARED +#define CEPH_STAT_CAP_SIZE CEPH_CAP_FILE_SHARED +#define CEPH_STAT_CAP_ATIME CEPH_CAP_FILE_SHARED /* fixme */ +#define CEPH_STAT_CAP_XATTR CEPH_CAP_XATTR_SHARED +#define CEPH_STAT_CAP_INODE_ALL (CEPH_CAP_PIN | \ + CEPH_CAP_AUTH_SHARED | \ + CEPH_CAP_LINK_SHARED | \ + CEPH_CAP_FILE_SHARED | \ + CEPH_CAP_XATTR_SHARED) + +#define CEPH_CAP_ANY_SHARED (CEPH_CAP_AUTH_SHARED | \ + CEPH_CAP_LINK_SHARED | \ + CEPH_CAP_XATTR_SHARED | \ + CEPH_CAP_FILE_SHARED) +#define CEPH_CAP_ANY_RD (CEPH_CAP_ANY_SHARED | CEPH_CAP_FILE_RD | \ + CEPH_CAP_FILE_CACHE) + +#define CEPH_CAP_ANY_EXCL (CEPH_CAP_AUTH_EXCL | \ + CEPH_CAP_LINK_EXCL | \ + CEPH_CAP_XATTR_EXCL | \ + CEPH_CAP_FILE_EXCL) +#define CEPH_CAP_ANY_FILE_WR (CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | \ + CEPH_CAP_FILE_EXCL) +#define CEPH_CAP_ANY_WR (CEPH_CAP_ANY_EXCL | CEPH_CAP_ANY_FILE_WR) +#define CEPH_CAP_ANY (CEPH_CAP_ANY_RD | CEPH_CAP_ANY_EXCL | \ + CEPH_CAP_ANY_FILE_WR | CEPH_CAP_PIN) + +#define CEPH_CAP_LOCKS (CEPH_LOCK_IFILE | CEPH_LOCK_IAUTH | CEPH_LOCK_ILINK | \ + CEPH_LOCK_IXATTR) + +int ceph_caps_for_mode(int mode); + +enum { + CEPH_CAP_OP_GRANT, /* mds->client grant */ + CEPH_CAP_OP_REVOKE, /* mds->client revoke */ + CEPH_CAP_OP_TRUNC, /* mds->client trunc notify */ + CEPH_CAP_OP_EXPORT, /* mds has exported the cap */ + CEPH_CAP_OP_IMPORT, /* mds has imported the cap */ + CEPH_CAP_OP_UPDATE, /* client->mds update */ + CEPH_CAP_OP_DROP, /* client->mds drop cap bits */ + CEPH_CAP_OP_FLUSH, /* client->mds cap writeback */ + CEPH_CAP_OP_FLUSH_ACK, /* mds->client flushed */ + CEPH_CAP_OP_FLUSHSNAP, /* client->mds flush snapped metadata */ + CEPH_CAP_OP_FLUSHSNAP_ACK, /* mds->client flushed snapped metadata */ + CEPH_CAP_OP_RELEASE, /* client->mds release (clean) cap */ + CEPH_CAP_OP_RENEW, /* client->mds renewal request */ +}; + +extern const char *ceph_cap_op_name(int op); + +/* + * caps message, used for capability callbacks, acks, requests, etc. + */ +struct ceph_mds_caps { + __le32 op; /* CEPH_CAP_OP_* */ + __le64 ino, realm; + __le64 cap_id; + __le32 seq, issue_seq; + __le32 caps, wanted, dirty; /* latest issued/wanted/dirty */ + __le32 migrate_seq; + __le64 snap_follows; + __le32 snap_trace_len; + __le64 client_tid; /* for FLUSH(SNAP) -> FLUSH(SNAP)_ACK */ + + /* authlock */ + __le32 uid, gid, mode; + + /* linklock */ + __le32 nlink; + + /* xattrlock */ + __le32 xattr_len; + __le64 xattr_version; + + /* filelock */ + __le64 size, max_size, truncate_size; + __le32 truncate_seq; + struct ceph_timespec mtime, atime, ctime; + struct ceph_file_layout layout; + __le32 time_warp_seq; +} __attribute__ ((packed)); + +/* cap release msg head */ +struct ceph_mds_cap_release { + __le32 num; /* number of cap_items that follow */ +} __attribute__ ((packed)); + +struct ceph_mds_cap_item { + __le64 ino; + __le64 cap_id; + __le32 migrate_seq, seq; +} __attribute__ ((packed)); + +#define CEPH_MDS_LEASE_REVOKE 1 /* mds -> client */ +#define CEPH_MDS_LEASE_RELEASE 2 /* client -> mds */ +#define CEPH_MDS_LEASE_RENEW 3 /* client <-> mds */ +#define CEPH_MDS_LEASE_REVOKE_ACK 4 /* client -> mds */ + +extern const char *ceph_lease_op_name(int o); + +/* lease msg header */ +struct ceph_mds_lease { + __u8 action; /* CEPH_MDS_LEASE_* */ + __le16 mask; /* which lease */ + __le64 ino; + __le64 first, last; /* snap range */ + __le32 seq; + __le32 duration_ms; /* duration of renewal */ +} __attribute__ ((packed)); +/* followed by a __le32+string for dname */ + +/* client reconnect */ +struct ceph_mds_cap_reconnect { + __le64 cap_id; + __le32 wanted; + __le32 issued; + __le64 size; + struct ceph_timespec mtime, atime; + __le64 snaprealm; + __le64 pathbase; /* base ino for our path to this ino */ +} __attribute__ ((packed)); +/* followed by encoded string */ + +struct ceph_mds_snaprealm_reconnect { + __le64 ino; /* snap realm base */ + __le64 seq; /* snap seq for this snap realm */ + __le64 parent; /* parent realm */ +} __attribute__ ((packed)); + +/* + * snaps + */ +enum { + CEPH_SNAP_OP_UPDATE, /* CREATE or DESTROY */ + CEPH_SNAP_OP_CREATE, + CEPH_SNAP_OP_DESTROY, + CEPH_SNAP_OP_SPLIT, +}; + +extern const char *ceph_snap_op_name(int o); + +/* snap msg header */ +struct ceph_mds_snap_head { + __le32 op; /* CEPH_SNAP_OP_* */ + __le64 split; /* ino to split off, if any */ + __le32 num_split_inos; /* # inos belonging to new child realm */ + __le32 num_split_realms; /* # child realms udner new child realm */ + __le32 trace_len; /* size of snap trace blob */ +} __attribute__ ((packed)); +/* followed by split ino list, then split realms, then the trace blob */ + +/* + * encode info about a snaprealm, as viewed by a client + */ +struct ceph_mds_snap_realm { + __le64 ino; /* ino */ + __le64 created; /* snap: when created */ + __le64 parent; /* ino: parent realm */ + __le64 parent_since; /* snap: same parent since */ + __le64 seq; /* snap: version */ + __le32 num_snaps; + __le32 num_prior_parent_snaps; +} __attribute__ ((packed)); +/* followed by my snap list, then prior parent snap list */ + +#endif diff --git a/fs/ceph/ceph_strings.c b/fs/ceph/ceph_strings.c new file mode 100644 index 0000000..90d19d9 --- /dev/null +++ b/fs/ceph/ceph_strings.c @@ -0,0 +1,163 @@ +/* + * Ceph string constants + */ +#include "types.h" + +const char *ceph_osd_op_name(int op) +{ + switch (op) { + case CEPH_OSD_OP_READ: return "read"; + case CEPH_OSD_OP_STAT: return "stat"; + + case CEPH_OSD_OP_MASKTRUNC: return "masktrunc"; + + case CEPH_OSD_OP_WRITE: return "write"; + case CEPH_OSD_OP_DELETE: return "delete"; + case CEPH_OSD_OP_TRUNCATE: return "truncate"; + case CEPH_OSD_OP_ZERO: return "zero"; + case CEPH_OSD_OP_WRITEFULL: return "writefull"; + + case CEPH_OSD_OP_APPEND: return "append"; + case CEPH_OSD_OP_STARTSYNC: return "startsync"; + case CEPH_OSD_OP_SETTRUNC: return "settrunc"; + case CEPH_OSD_OP_TRIMTRUNC: return "trimtrunc"; + + case CEPH_OSD_OP_TMAPUP: return "tmapup"; + case CEPH_OSD_OP_TMAPGET: return "tmapget"; + case CEPH_OSD_OP_TMAPPUT: return "tmapput"; + + case CEPH_OSD_OP_GETXATTR: return "getxattr"; + case CEPH_OSD_OP_GETXATTRS: return "getxattrs"; + case CEPH_OSD_OP_SETXATTR: return "setxattr"; + case CEPH_OSD_OP_SETXATTRS: return "setxattrs"; + case CEPH_OSD_OP_RESETXATTRS: return "resetxattrs"; + case CEPH_OSD_OP_RMXATTR: return "rmxattr"; + + case CEPH_OSD_OP_PULL: return "pull"; + case CEPH_OSD_OP_PUSH: return "push"; + case CEPH_OSD_OP_BALANCEREADS: return "balance-reads"; + case CEPH_OSD_OP_UNBALANCEREADS: return "unbalance-reads"; + case CEPH_OSD_OP_SCRUB: return "scrub"; + + case CEPH_OSD_OP_WRLOCK: return "wrlock"; + case CEPH_OSD_OP_WRUNLOCK: return "wrunlock"; + case CEPH_OSD_OP_RDLOCK: return "rdlock"; + case CEPH_OSD_OP_RDUNLOCK: return "rdunlock"; + case CEPH_OSD_OP_UPLOCK: return "uplock"; + case CEPH_OSD_OP_DNLOCK: return "dnlock"; + + case CEPH_OSD_OP_CALL: return "call"; + + case CEPH_OSD_OP_PGLS: return "pgls"; + } + return "???"; +} + +const char *ceph_mds_state_name(int s) +{ + switch (s) { + /* down and out */ + case CEPH_MDS_STATE_DNE: return "down:dne"; + case CEPH_MDS_STATE_STOPPED: return "down:stopped"; + /* up and out */ + case CEPH_MDS_STATE_BOOT: return "up:boot"; + case CEPH_MDS_STATE_STANDBY: return "up:standby"; + case CEPH_MDS_STATE_STANDBY_REPLAY: return "up:standby-replay"; + case CEPH_MDS_STATE_CREATING: return "up:creating"; + case CEPH_MDS_STATE_STARTING: return "up:starting"; + /* up and in */ + case CEPH_MDS_STATE_REPLAY: return "up:replay"; + case CEPH_MDS_STATE_RESOLVE: return "up:resolve"; + case CEPH_MDS_STATE_RECONNECT: return "up:reconnect"; + case CEPH_MDS_STATE_REJOIN: return "up:rejoin"; + case CEPH_MDS_STATE_CLIENTREPLAY: return "up:clientreplay"; + case CEPH_MDS_STATE_ACTIVE: return "up:active"; + case CEPH_MDS_STATE_STOPPING: return "up:stopping"; + } + return "???"; +} + +const char *ceph_session_op_name(int op) +{ + switch (op) { + case CEPH_SESSION_REQUEST_OPEN: return "request_open"; + case CEPH_SESSION_OPEN: return "open"; + case CEPH_SESSION_REQUEST_CLOSE: return "request_close"; + case CEPH_SESSION_CLOSE: return "close"; + case CEPH_SESSION_REQUEST_RENEWCAPS: return "request_renewcaps"; + case CEPH_SESSION_RENEWCAPS: return "renewcaps"; + case CEPH_SESSION_STALE: return "stale"; + case CEPH_SESSION_RECALL_STATE: return "recall_state"; + } + return "???"; +} + +const char *ceph_mds_op_name(int op) +{ + switch (op) { + case CEPH_MDS_OP_LOOKUP: return "lookup"; + case CEPH_MDS_OP_LOOKUPHASH: return "lookuphash"; + case CEPH_MDS_OP_LOOKUPPARENT: return "lookupparent"; + case CEPH_MDS_OP_GETATTR: return "getattr"; + case CEPH_MDS_OP_SETXATTR: return "setxattr"; + case CEPH_MDS_OP_SETATTR: return "setattr"; + case CEPH_MDS_OP_RMXATTR: return "rmxattr"; + case CEPH_MDS_OP_READDIR: return "readdir"; + case CEPH_MDS_OP_MKNOD: return "mknod"; + case CEPH_MDS_OP_LINK: return "link"; + case CEPH_MDS_OP_UNLINK: return "unlink"; + case CEPH_MDS_OP_RENAME: return "rename"; + case CEPH_MDS_OP_MKDIR: return "mkdir"; + case CEPH_MDS_OP_RMDIR: return "rmdir"; + case CEPH_MDS_OP_SYMLINK: return "symlink"; + case CEPH_MDS_OP_CREATE: return "create"; + case CEPH_MDS_OP_OPEN: return "open"; + case CEPH_MDS_OP_LOOKUPSNAP: return "lookupsnap"; + case CEPH_MDS_OP_LSSNAP: return "lssnap"; + case CEPH_MDS_OP_MKSNAP: return "mksnap"; + case CEPH_MDS_OP_RMSNAP: return "rmsnap"; + } + return "???"; +} + +const char *ceph_cap_op_name(int op) +{ + switch (op) { + case CEPH_CAP_OP_GRANT: return "grant"; + case CEPH_CAP_OP_REVOKE: return "revoke"; + case CEPH_CAP_OP_TRUNC: return "trunc"; + case CEPH_CAP_OP_EXPORT: return "export"; + case CEPH_CAP_OP_IMPORT: return "import"; + case CEPH_CAP_OP_UPDATE: return "update"; + case CEPH_CAP_OP_DROP: return "drop"; + case CEPH_CAP_OP_FLUSH: return "flush"; + case CEPH_CAP_OP_FLUSH_ACK: return "flush_ack"; + case CEPH_CAP_OP_FLUSHSNAP: return "flushsnap"; + case CEPH_CAP_OP_FLUSHSNAP_ACK: return "flushsnap_ack"; + case CEPH_CAP_OP_RELEASE: return "release"; + case CEPH_CAP_OP_RENEW: return "renew"; + } + return "???"; +} + +const char *ceph_lease_op_name(int o) +{ + switch (o) { + case CEPH_MDS_LEASE_REVOKE: return "revoke"; + case CEPH_MDS_LEASE_RELEASE: return "release"; + case CEPH_MDS_LEASE_RENEW: return "renew"; + case CEPH_MDS_LEASE_REVOKE_ACK: return "revoke_ack"; + } + return "???"; +} + +const char *ceph_snap_op_name(int o) +{ + switch (o) { + case CEPH_SNAP_OP_UPDATE: return "update"; + case CEPH_SNAP_OP_CREATE: return "create"; + case CEPH_SNAP_OP_DESTROY: return "destroy"; + case CEPH_SNAP_OP_SPLIT: return "split"; + } + return "???"; +} diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h new file mode 100644 index 0000000..73921ae --- /dev/null +++ b/fs/ceph/msgr.h @@ -0,0 +1,157 @@ +#ifndef __MSGR_H +#define __MSGR_H + +/* + * Data types for message passing layer used by Ceph. + */ + +#define CEPH_MON_PORT 6789 /* default monitor port */ + +/* + * client-side processes will try to bind to ports in this + * range, simply for the benefit of tools like nmap or wireshark + * that would like to identify the protocol. + */ +#define CEPH_PORT_FIRST 6789 +#define CEPH_PORT_START 6800 /* non-monitors start here */ +#define CEPH_PORT_LAST 6900 + +/* + * tcp connection banner. include a protocol version. and adjust + * whenever the wire protocol changes. try to keep this string length + * constant. + */ +#define CEPH_BANNER "ceph v021" +#define CEPH_BANNER_MAX_LEN 30 + + +/* + * Rollover-safe type and comparator for 32-bit sequence numbers. + * Comparator returns -1, 0, or 1. + */ +typedef __u32 ceph_seq_t; + +static inline __s32 ceph_seq_cmp(__u32 a, __u32 b) +{ + return (__s32)a - (__s32)b; +} + + +/* + * entity_name -- logical name for a process participating in the + * network, e.g. 'mds0' or 'osd3'. + */ +struct ceph_entity_name { + __u8 type; /* CEPH_ENTITY_TYPE_* */ + __le64 num; +} __attribute__ ((packed)); + +#define CEPH_ENTITY_TYPE_MON 1 +#define CEPH_ENTITY_TYPE_MDS 2 +#define CEPH_ENTITY_TYPE_OSD 3 +#define CEPH_ENTITY_TYPE_CLIENT 4 +#define CEPH_ENTITY_TYPE_ADMIN 5 + +/* + * entity_addr -- network address + */ +struct ceph_entity_addr { + __le32 erank; /* entity's rank in process */ + __le32 nonce; /* unique id for process (e.g. pid) */ + struct sockaddr_storage in_addr; +} __attribute__ ((packed)); + +static inline bool ceph_entity_addr_is_local(const struct ceph_entity_addr *a, + const struct ceph_entity_addr *b) +{ + return a->nonce == b->nonce && + memcmp(&a->in_addr, &b->in_addr, sizeof(a->in_addr)) == 0; +} + +static inline bool ceph_entity_addr_equal(const struct ceph_entity_addr *a, + const struct ceph_entity_addr *b) +{ + return memcmp(a, b, sizeof(*a)) == 0; +} + +struct ceph_entity_inst { + struct ceph_entity_name name; + struct ceph_entity_addr addr; +} __attribute__ ((packed)); + + +/* used by message exchange protocol */ +#define CEPH_MSGR_TAG_READY 1 /* server->client: ready for messages */ +#define CEPH_MSGR_TAG_RESETSESSION 2 /* server->client: reset, try again */ +#define CEPH_MSGR_TAG_WAIT 3 /* server->client: wait for racing + incoming connection */ +#define CEPH_MSGR_TAG_RETRY_SESSION 4 /* server->client + cseq: try again + with higher cseq */ +#define CEPH_MSGR_TAG_RETRY_GLOBAL 5 /* server->client + gseq: try again + with higher gseq */ +#define CEPH_MSGR_TAG_CLOSE 6 /* closing pipe */ +#define CEPH_MSGR_TAG_MSG 7 /* message */ +#define CEPH_MSGR_TAG_ACK 8 /* message ack */ +#define CEPH_MSGR_TAG_KEEPALIVE 9 /* just a keepalive byte! */ +#define CEPH_MSGR_TAG_BADPROTOVER 10 /* bad protocol version */ + + +/* + * connection negotiation + */ +struct ceph_msg_connect { + __le32 host_type; /* CEPH_ENTITY_TYPE_* */ + __le32 global_seq; /* count connections initiated by this host */ + __le32 connect_seq; /* count connections initiated in this session */ + __le32 protocol_version; + __u8 flags; /* CEPH_MSG_CONNECT_* */ +} __attribute__ ((packed)); + +struct ceph_msg_connect_reply { + __u8 tag; + __le32 global_seq; + __le32 connect_seq; + __le32 protocol_version; + __u8 flags; +} __attribute__ ((packed)); + +#define CEPH_MSG_CONNECT_LOSSY 1 /* messages i send may be safely dropped */ + + +/* + * message header + */ +struct ceph_msg_header { + __le64 seq; /* message seq# for this session */ + __le16 type; /* message type */ + __le16 priority; /* priority. higher value == higher priority */ + + __le32 front_len; /* bytes in main payload */ + __le32 middle_len;/* bytes in middle payload */ + __le32 data_len; /* bytes of data payload */ + __le16 data_off; /* sender: include full offset; + receiver: mask against ~PAGE_MASK */ + + struct ceph_entity_inst src, orig_src; + __le32 dst_erank; + __le32 crc; /* header crc32c */ +} __attribute__ ((packed)); + +#define CEPH_MSG_PRIO_LOW 64 +#define CEPH_MSG_PRIO_DEFAULT 127 +#define CEPH_MSG_PRIO_HIGH 196 +#define CEPH_MSG_PRIO_HIGHEST 255 + +/* + * follows data payload + */ +struct ceph_msg_footer { + __le32 front_crc, middle_crc, data_crc; + __u8 flags; +} __attribute__ ((packed)); + +#define CEPH_MSG_FOOTER_COMPLETE (1<<0) /* msg wasn't aborted */ +#define CEPH_MSG_FOOTER_NOCRC (1<<1) /* no data crc */ + + +#endif diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h new file mode 100644 index 0000000..a48cf4a --- /dev/null +++ b/fs/ceph/rados.h @@ -0,0 +1,372 @@ +#ifndef __RADOS_H +#define __RADOS_H + +/* + * Data types for the Ceph distributed object storage layer RADOS + * (Reliable Autonomic Distributed Object Store). + */ + +#include "msgr.h" + +/* + * fs id + */ +struct ceph_fsid { + unsigned char fsid[16]; +}; + +static inline int ceph_fsid_compare(const struct ceph_fsid *a, + const struct ceph_fsid *b) +{ + return memcmp(a, b, sizeof(*a)); +} + +/* + * ino, object, etc. + */ +typedef __le64 ceph_snapid_t; +#define CEPH_SNAPDIR ((__u64)(-1)) /* reserved for hidden .snap dir */ +#define CEPH_NOSNAP ((__u64)(-2)) /* "head", "live" revision */ +#define CEPH_MAXSNAP ((__u64)(-3)) /* largest valid snapid */ + +struct ceph_timespec { + __le32 tv_sec; + __le32 tv_nsec; +} __attribute__ ((packed)); + + +/* + * object layout - how objects are mapped into PGs + */ +#define CEPH_OBJECT_LAYOUT_HASH 1 +#define CEPH_OBJECT_LAYOUT_LINEAR 2 +#define CEPH_OBJECT_LAYOUT_HASHINO 3 + +/* + * pg layout -- how PGs are mapped onto (sets of) OSDs + */ +#define CEPH_PG_LAYOUT_CRUSH 0 +#define CEPH_PG_LAYOUT_HASH 1 +#define CEPH_PG_LAYOUT_LINEAR 2 +#define CEPH_PG_LAYOUT_HYBRID 3 + + +/* + * placement group. + * we encode this into one __le64. + */ +union ceph_pg { + __u64 pg64; + struct { + __s16 preferred; /* preferred primary osd */ + __u16 ps; /* placement seed */ + __u32 pool; /* object pool */ + } __attribute__ ((packed)) pg; +} __attribute__ ((packed)); + +/* + * pg_pool is a set of pgs storing a pool of objects + * + * pg_num -- base number of pseudorandomly placed pgs + * + * pgp_num -- effective number when calculating pg placement. this + * is used for pg_num increases. new pgs result in data being "split" + * into new pgs. for this to proceed smoothly, new pgs are intiially + * colocated with their parents; that is, pgp_num doesn't increase + * until the new pgs have successfully split. only _then_ are the new + * pgs placed independently. + * + * lpg_num -- localized pg count (per device). replicas are randomly + * selected. + * + * lpgp_num -- as above. + */ +#define CEPH_PG_TYPE_REP 1 +#define CEPH_PG_TYPE_RAID4 2 +struct ceph_pg_pool { + __u8 type; /* CEPH_PG_TYPE_* */ + __u8 size; /* number of osds in each pg */ + __u8 crush_ruleset; /* crush placement rule */ + __le32 pg_num, pgp_num; /* number of pg's */ + __le32 lpg_num, lpgp_num; /* number of localized pg's */ + __le32 last_change; /* most recent epoch changed */ + __le64 snap_seq; /* seq for per-pool snapshot */ + __le32 snap_epoch; /* epoch of last snap */ + __le32 num_snaps; + __le32 num_removed_snap_intervals; +} __attribute__ ((packed)); + +/* + * stable_mod func is used to control number of placement groups. + * similar to straight-up modulo, but produces a stable mapping as b + * increases over time. b is the number of bins, and bmask is the + * containing power of 2 minus 1. + * + * b <= bmask and bmask=(2**n)-1 + * e.g., b=12 -> bmask=15, b=123 -> bmask=127 + */ +static inline int ceph_stable_mod(int x, int b, int bmask) +{ + if ((x & bmask) < b) + return x & bmask; + else + return x & (bmask >> 1); +} + +/* + * object layout - how a given object should be stored. + */ +struct ceph_object_layout { + __le64 ol_pgid; /* raw pg, with _full_ ps precision. */ + __le32 ol_stripe_unit; /* for per-object parity, if any */ +} __attribute__ ((packed)); + +/* + * compound epoch+version, used by storage layer to serialize mutations + */ +struct ceph_eversion { + __le32 epoch; + __le64 version; +} __attribute__ ((packed)); + +/* + * osd map bits + */ + +/* status bits */ +#define CEPH_OSD_EXISTS 1 +#define CEPH_OSD_UP 2 + +/* osd weights. fixed point value: 0x10000 == 1.0 ("in"), 0 == "out" */ +#define CEPH_OSD_IN 0x10000 +#define CEPH_OSD_OUT 0 + + +/* + * osd map flag bits + */ +#define CEPH_OSDMAP_NEARFULL (1<<0) /* sync writes (near ENOSPC) */ +#define CEPH_OSDMAP_FULL (1<<1) /* no data writes (ENOSPC) */ +#define CEPH_OSDMAP_PAUSERD (1<<2) /* pause all reads */ +#define CEPH_OSDMAP_PAUSEWR (1<<3) /* pause all writes */ +#define CEPH_OSDMAP_PAUSEREC (1<<4) /* pause recovery */ + +/* + * osd ops + */ +#define CEPH_OSD_OP_MODE 0xf000 +#define CEPH_OSD_OP_MODE_RD 0x1000 +#define CEPH_OSD_OP_MODE_WR 0x2000 +#define CEPH_OSD_OP_MODE_RMW 0x3000 +#define CEPH_OSD_OP_MODE_SUB 0x4000 +#define CEPH_OSD_OP_MODE_EXEC 0x8000 + +#define CEPH_OSD_OP_TYPE 0x0f00 +#define CEPH_OSD_OP_TYPE_LOCK 0x0100 +#define CEPH_OSD_OP_TYPE_DATA 0x0200 +#define CEPH_OSD_OP_TYPE_ATTR 0x0300 +#define CEPH_OSD_OP_TYPE_EXEC 0x0400 +#define CEPH_OSD_OP_TYPE_PG 0x0500 + +enum { + /** data **/ + /* read */ + CEPH_OSD_OP_READ = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 1, + CEPH_OSD_OP_STAT = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 2, + + /* fancy read */ + CEPH_OSD_OP_MASKTRUNC = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 4, + + /* write */ + CEPH_OSD_OP_WRITE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 1, + CEPH_OSD_OP_WRITEFULL = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 2, + CEPH_OSD_OP_TRUNCATE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 3, + CEPH_OSD_OP_ZERO = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 4, + CEPH_OSD_OP_DELETE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 5, + + /* fancy write */ + CEPH_OSD_OP_APPEND = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 6, + CEPH_OSD_OP_STARTSYNC = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 7, + CEPH_OSD_OP_SETTRUNC = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 8, + CEPH_OSD_OP_TRIMTRUNC = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 9, + + CEPH_OSD_OP_TMAPUP = CEPH_OSD_OP_MODE_RMW | CEPH_OSD_OP_TYPE_DATA | 10, + CEPH_OSD_OP_TMAPPUT = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 11, + CEPH_OSD_OP_TMAPGET = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 12, + + CEPH_OSD_OP_CREATE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 13, + + /** attrs **/ + /* read */ + CEPH_OSD_OP_GETXATTR = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_ATTR | 1, + CEPH_OSD_OP_GETXATTRS = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_ATTR | 2, + + /* write */ + CEPH_OSD_OP_SETXATTR = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_ATTR | 1, + CEPH_OSD_OP_SETXATTRS = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_ATTR | 2, + CEPH_OSD_OP_RESETXATTRS = CEPH_OSD_OP_MODE_WR|CEPH_OSD_OP_TYPE_ATTR | 3, + CEPH_OSD_OP_RMXATTR = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_ATTR | 4, + + /** subop **/ + CEPH_OSD_OP_PULL = CEPH_OSD_OP_MODE_SUB | 1, + CEPH_OSD_OP_PUSH = CEPH_OSD_OP_MODE_SUB | 2, + CEPH_OSD_OP_BALANCEREADS = CEPH_OSD_OP_MODE_SUB | 3, + CEPH_OSD_OP_UNBALANCEREADS = CEPH_OSD_OP_MODE_SUB | 4, + CEPH_OSD_OP_SCRUB = CEPH_OSD_OP_MODE_SUB | 5, + + /** lock **/ + CEPH_OSD_OP_WRLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 1, + CEPH_OSD_OP_WRUNLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 2, + CEPH_OSD_OP_RDLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 3, + CEPH_OSD_OP_RDUNLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 4, + CEPH_OSD_OP_UPLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 5, + CEPH_OSD_OP_DNLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 6, + + /** exec **/ + CEPH_OSD_OP_CALL = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_EXEC | 1, + + /** pg **/ + CEPH_OSD_OP_PGLS = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_PG | 1, +}; + +static inline int ceph_osd_op_type_lock(int op) +{ + return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_LOCK; +} +static inline int ceph_osd_op_type_data(int op) +{ + return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_DATA; +} +static inline int ceph_osd_op_type_attr(int op) +{ + return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_ATTR; +} +static inline int ceph_osd_op_type_exec(int op) +{ + return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_EXEC; +} +static inline int ceph_osd_op_type_pg(int op) +{ + return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_PG; +} + +static inline int ceph_osd_op_mode_subop(int op) +{ + return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_SUB; +} +static inline int ceph_osd_op_mode_read(int op) +{ + return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_RD; +} +static inline int ceph_osd_op_mode_modify(int op) +{ + return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_WR; +} + +#define CEPH_OSD_TMAP_HDR 'h' +#define CEPH_OSD_TMAP_SET 's' +#define CEPH_OSD_TMAP_RM 'r' + +extern const char *ceph_osd_op_name(int op); + + +/* + * osd op flags + * + * An op may be READ, WRITE, or READ|WRITE. + */ +enum { + CEPH_OSD_FLAG_ACK = 1, /* want (or is) "ack" ack */ + CEPH_OSD_FLAG_ONNVRAM = 2, /* want (or is) "onnvram" ack */ + CEPH_OSD_FLAG_ONDISK = 4, /* want (or is) "ondisk" ack */ + CEPH_OSD_FLAG_RETRY = 8, /* resend attempt */ + CEPH_OSD_FLAG_READ = 16, /* op may read */ + CEPH_OSD_FLAG_WRITE = 32, /* op may write */ + CEPH_OSD_FLAG_ORDERSNAP = 64, /* EOLDSNAP if snapc is out of order */ + CEPH_OSD_FLAG_PEERSTAT = 128, /* msg includes osd_peer_stat */ + CEPH_OSD_FLAG_BALANCE_READS = 256, + CEPH_OSD_FLAG_PARALLELEXEC = 512, /* execute op in parallel */ + CEPH_OSD_FLAG_PGOP = 1024, /* pg op, no object */ +}; + +enum { + CEPH_OSD_OP_FLAG_EXCL = 1, /* EXCL object create */ +}; + +#define EOLDSNAPC ERESTART /* ORDERSNAP flag set; writer has old snapc*/ +#define EBLACKLISTED ESHUTDOWN /* blacklisted */ + +/* + * an individual object operation. each may be accompanied by some data + * payload + */ +struct ceph_osd_op { + __le16 op; /* CEPH_OSD_OP_* */ + __le32 flags; /* CEPH_OSD_FLAG_* */ + union { + struct { + __le64 offset, length; + } __attribute__ ((packed)) extent; + struct { + __le32 name_len; + __le32 value_len; + } __attribute__ ((packed)) xattr; + struct { + __le64 truncate_size; + __le32 truncate_seq; + } __attribute__ ((packed)) trunc; + struct { + __u8 class_len; + __u8 method_len; + __u8 argc; + __le32 indata_len; + } __attribute__ ((packed)) cls; + struct { + __le64 cookie, count; + } __attribute__ ((packed)) pgls; + }; + __le32 payload_len; +} __attribute__ ((packed)); + +/* + * osd request message header. each request may include multiple + * ceph_osd_op object operations. + */ +struct ceph_osd_request_head { + __le64 tid; /* transaction id */ + __le32 client_inc; /* client incarnation */ + struct ceph_object_layout layout; /* pgid */ + __le32 osdmap_epoch; /* client's osdmap epoch */ + + __le32 flags; + + struct ceph_timespec mtime; /* for mutations only */ + struct ceph_eversion reassert_version; /* if we are replaying op */ + + __le32 object_len; /* length of object name */ + + __le64 snapid; /* snapid to read */ + __le64 snap_seq; /* writer's snap context */ + __le32 num_snaps; + + __le16 num_ops; + struct ceph_osd_op ops[]; /* followed by ops[], obj, ticket, snaps */ +} __attribute__ ((packed)); + +struct ceph_osd_reply_head { + __le64 tid; /* transaction id */ + __le32 client_inc; /* client incarnation */ + __le32 flags; + struct ceph_object_layout layout; + __le32 osdmap_epoch; + struct ceph_eversion reassert_version; /* for replaying uncommitted */ + + __le32 result; /* result code */ + + __le32 object_len; /* length of object name */ + __le32 num_ops; + struct ceph_osd_op ops[0]; /* ops[], object */ +} __attribute__ ((packed)); + + +#endif -- cgit v0.10.2 From de57606c23afded22202825b3db8a5d61859f198 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:07 -0700 Subject: ceph: client types We first define constants, types, and prototypes for the kernel client proper. A few subsystems are defined separately later: the MDS, OSD, and monitor clients, and the messaging layer. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_debug.h b/fs/ceph/ceph_debug.h new file mode 100644 index 0000000..1818c23 --- /dev/null +++ b/fs/ceph/ceph_debug.h @@ -0,0 +1,37 @@ +#ifndef _FS_CEPH_DEBUG_H +#define _FS_CEPH_DEBUG_H + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#ifdef CONFIG_CEPH_FS_PRETTYDEBUG + +/* + * wrap pr_debug to include a filename:lineno prefix on each line. + * this incurs some overhead (kernel size and execution time) due to + * the extra function call at each call site. + */ + +# if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +extern const char *ceph_file_part(const char *s, int len); +# define dout(fmt, ...) \ + pr_debug(" %12.12s:%-4d : " fmt, \ + ceph_file_part(__FILE__, sizeof(__FILE__)), \ + __LINE__, ##__VA_ARGS__) +# else +/* faux printk call just to see any compiler warnings. */ +# define dout(fmt, ...) do { \ + if (0) \ + printk(KERN_DEBUG fmt, ##__VA_ARGS__); \ + } while (0) +# endif + +#else + +/* + * or, just wrap pr_debug + */ +# define dout(fmt, ...) pr_debug(" " fmt, ##__VA_ARGS__) + +#endif + +#endif diff --git a/fs/ceph/ceph_frag.c b/fs/ceph/ceph_frag.c new file mode 100644 index 0000000..ab6cf35 --- /dev/null +++ b/fs/ceph/ceph_frag.c @@ -0,0 +1,21 @@ +/* + * Ceph 'frag' type + */ +#include "types.h" + +int ceph_frag_compare(__u32 a, __u32 b) +{ + unsigned va = ceph_frag_value(a); + unsigned vb = ceph_frag_value(b); + if (va < vb) + return -1; + if (va > vb) + return 1; + va = ceph_frag_bits(a); + vb = ceph_frag_bits(b); + if (va < vb) + return -1; + if (va > vb) + return 1; + return 0; +} diff --git a/fs/ceph/ceph_frag.h b/fs/ceph/ceph_frag.h new file mode 100644 index 0000000..793f50c --- /dev/null +++ b/fs/ceph/ceph_frag.h @@ -0,0 +1,109 @@ +#ifndef _FS_CEPH_FRAG_H +#define _FS_CEPH_FRAG_H + +/* + * "Frags" are a way to describe a subset of a 32-bit number space, + * using a mask and a value to match against that mask. Any given frag + * (subset of the number space) can be partitioned into 2^n sub-frags. + * + * Frags are encoded into a 32-bit word: + * 8 upper bits = "bits" + * 24 lower bits = "value" + * (We could go to 5+27 bits, but who cares.) + * + * We use the _most_ significant bits of the 24 bit value. This makes + * values logically sort. + * + * Unfortunately, because the "bits" field is still in the high bits, we + * can't sort encoded frags numerically. However, it does allow you + * to feed encoded frags as values into frag_contains_value. + */ +static inline __u32 ceph_frag_make(__u32 b, __u32 v) +{ + return (b << 24) | + (v & (0xffffffu << (24-b)) & 0xffffffu); +} +static inline __u32 ceph_frag_bits(__u32 f) +{ + return f >> 24; +} +static inline __u32 ceph_frag_value(__u32 f) +{ + return f & 0xffffffu; +} +static inline __u32 ceph_frag_mask(__u32 f) +{ + return (0xffffffu << (24-ceph_frag_bits(f))) & 0xffffffu; +} +static inline __u32 ceph_frag_mask_shift(__u32 f) +{ + return 24 - ceph_frag_bits(f); +} + +static inline int ceph_frag_contains_value(__u32 f, __u32 v) +{ + return (v & ceph_frag_mask(f)) == ceph_frag_value(f); +} +static inline int ceph_frag_contains_frag(__u32 f, __u32 sub) +{ + /* is sub as specific as us, and contained by us? */ + return ceph_frag_bits(sub) >= ceph_frag_bits(f) && + (ceph_frag_value(sub) & ceph_frag_mask(f)) == ceph_frag_value(f); +} + +static inline __u32 ceph_frag_parent(__u32 f) +{ + return ceph_frag_make(ceph_frag_bits(f) - 1, + ceph_frag_value(f) & (ceph_frag_mask(f) << 1)); +} +static inline int ceph_frag_is_left_child(__u32 f) +{ + return ceph_frag_bits(f) > 0 && + (ceph_frag_value(f) & (0x1000000 >> ceph_frag_bits(f))) == 0; +} +static inline int ceph_frag_is_right_child(__u32 f) +{ + return ceph_frag_bits(f) > 0 && + (ceph_frag_value(f) & (0x1000000 >> ceph_frag_bits(f))) == 1; +} +static inline __u32 ceph_frag_sibling(__u32 f) +{ + return ceph_frag_make(ceph_frag_bits(f), + ceph_frag_value(f) ^ (0x1000000 >> ceph_frag_bits(f))); +} +static inline __u32 ceph_frag_left_child(__u32 f) +{ + return ceph_frag_make(ceph_frag_bits(f)+1, ceph_frag_value(f)); +} +static inline __u32 ceph_frag_right_child(__u32 f) +{ + return ceph_frag_make(ceph_frag_bits(f)+1, + ceph_frag_value(f) | (0x1000000 >> (1+ceph_frag_bits(f)))); +} +static inline __u32 ceph_frag_make_child(__u32 f, int by, int i) +{ + int newbits = ceph_frag_bits(f) + by; + return ceph_frag_make(newbits, + ceph_frag_value(f) | (i << (24 - newbits))); +} +static inline int ceph_frag_is_leftmost(__u32 f) +{ + return ceph_frag_value(f) == 0; +} +static inline int ceph_frag_is_rightmost(__u32 f) +{ + return ceph_frag_value(f) == ceph_frag_mask(f); +} +static inline __u32 ceph_frag_next(__u32 f) +{ + return ceph_frag_make(ceph_frag_bits(f), + ceph_frag_value(f) + (0x1000000 >> ceph_frag_bits(f))); +} + +/* + * comparator to sort frags logically, as when traversing the + * number space in ascending order... + */ +int ceph_frag_compare(__u32 a, __u32 b); + +#endif diff --git a/fs/ceph/ceph_ver.h b/fs/ceph/ceph_ver.h new file mode 100644 index 0000000..66c3727 --- /dev/null +++ b/fs/ceph/ceph_ver.h @@ -0,0 +1,6 @@ +#ifndef __CEPH_VERSION_H +#define __CEPH_VERSION_H + +#define CEPH_GIT_VER 335cd8f952b457095ea2a66aee3db50efb63c91d + +#endif diff --git a/fs/ceph/super.h b/fs/ceph/super.h new file mode 100644 index 0000000..cfd39ef --- /dev/null +++ b/fs/ceph/super.h @@ -0,0 +1,890 @@ +#ifndef _FS_CEPH_SUPER_H +#define _FS_CEPH_SUPER_H + +#include "ceph_debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "messenger.h" +#include "msgpool.h" +#include "mon_client.h" +#include "mds_client.h" +#include "osd_client.h" +#include "ceph_fs.h" + +/* f_type in struct statfs */ +#define CEPH_SUPER_MAGIC 0x00c36400 + +/* large granularity for statfs utilization stats to facilitate + * large volume sizes on 32-bit machines. */ +#define CEPH_BLOCK_SHIFT 20 /* 1 MB */ +#define CEPH_BLOCK (1 << CEPH_BLOCK_SHIFT) + +/* + * mount options + */ +#define CEPH_OPT_FSID (1<<0) +#define CEPH_OPT_NOSHARE (1<<1) /* don't share client with other sbs */ +#define CEPH_OPT_MYIP (1<<2) /* specified my ip */ +#define CEPH_OPT_DIRSTAT (1<<4) /* funky `cat dirname` for stats */ +#define CEPH_OPT_RBYTES (1<<5) /* dir st_bytes = rbytes */ +#define CEPH_OPT_NOCRC (1<<6) /* no data crc on writes */ +#define CEPH_OPT_NOASYNCREADDIR (1<<7) /* no dcache readdir */ + +#define CEPH_OPT_DEFAULT (CEPH_OPT_RBYTES) + +#define ceph_set_opt(client, opt) \ + (client)->mount_args.flags |= CEPH_OPT_##opt; +#define ceph_test_opt(client, opt) \ + (!!((client)->mount_args.flags & CEPH_OPT_##opt)) + + +#define CEPH_MAX_MON_MOUNT_ADDR 5 + +struct ceph_mount_args { + int sb_flags; + int flags; + int mount_timeout; + int caps_wanted_delay_min, caps_wanted_delay_max; + struct ceph_fsid fsid; + struct ceph_entity_addr my_addr; + int wsize; + int rsize; /* max readahead */ + int max_readdir; /* max readdir size */ + int osd_timeout; + char *snapdir_name; /* default ".snap" */ + char *secret; + int cap_release_safety; +}; + +/* + * defaults + */ +#define CEPH_MOUNT_TIMEOUT_DEFAULT 60 +#define CEPH_MOUNT_RSIZE_DEFAULT (128*1024) /* readahead */ + +#define CEPH_MSG_MAX_FRONT_LEN (16*1024*1024) +#define CEPH_MSG_MAX_DATA_LEN (16*1024*1024) + +#define CEPH_SNAPDIRNAME_DEFAULT ".snap" + +/* + * Delay telling the MDS we no longer want caps, in case we reopen + * the file. Delay a minimum amount of time, even if we send a cap + * message for some other reason. Otherwise, take the oppotunity to + * update the mds to avoid sending another message later. + */ +#define CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT 5 /* cap release delay */ +#define CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT 60 /* cap release delay */ + + +/* mount state */ +enum { + CEPH_MOUNT_MOUNTING, + CEPH_MOUNT_MOUNTED, + CEPH_MOUNT_UNMOUNTING, + CEPH_MOUNT_UNMOUNTED, + CEPH_MOUNT_SHUTDOWN, +}; + +/* + * subtract jiffies + */ +static inline unsigned long time_sub(unsigned long a, unsigned long b) +{ + BUG_ON(time_after(b, a)); + return (long)a - (long)b; +} + +/* + * per-filesystem client state + * + * possibly shared by multiple mount points, if they are + * mounting the same ceph filesystem/cluster. + */ +struct ceph_client { + __s64 whoami; /* my client number */ + struct dentry *debugfs_monmap; + struct dentry *debugfs_mdsmap, *debugfs_osdmap; + struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps; + + struct mutex mount_mutex; /* serialize mount attempts */ + struct ceph_mount_args mount_args; + struct ceph_fsid fsid; + + struct super_block *sb; + + unsigned long mount_state; + wait_queue_head_t mount_wq; + + int mount_err; + void *signed_ticket; /* our keys to the kingdom */ + int signed_ticket_len; + + struct ceph_messenger *msgr; /* messenger instance */ + struct ceph_mon_client monc; + struct ceph_mds_client mdsc; + struct ceph_osd_client osdc; + + /* writeback */ + mempool_t *wb_pagevec_pool; + struct workqueue_struct *wb_wq; + struct workqueue_struct *pg_inv_wq; + struct workqueue_struct *trunc_wq; + + struct backing_dev_info backing_dev_info; +}; + +static inline struct ceph_client *ceph_client(struct super_block *sb) +{ + return sb->s_fs_info; +} + + +/* + * File i/o capability. This tracks shared state with the metadata + * server that allows us to cache or writeback attributes or to read + * and write data. For any given inode, we should have one or more + * capabilities, one issued by each metadata server, and our + * cumulative access is the OR of all issued capabilities. + * + * Each cap is referenced by the inode's i_caps rbtree and by per-mds + * session capability lists. + */ +struct ceph_cap { + struct ceph_inode_info *ci; + struct rb_node ci_node; /* per-ci cap tree */ + struct ceph_mds_session *session; + struct list_head session_caps; /* per-session caplist */ + int mds; + u64 cap_id; /* unique cap id (mds provided) */ + int issued; /* latest, from the mds */ + int implemented; /* implemented superset of issued (for revocation) */ + int mds_wanted; + u32 seq, issue_seq, mseq, gen; + unsigned long last_used; + struct list_head caps_item; +}; + +#define CHECK_CAPS_NODELAY 1 /* do not delay any further */ +#define CHECK_CAPS_AUTHONLY 2 /* only check auth cap */ +#define CHECK_CAPS_FLUSH 4 /* flush any dirty caps */ + +/* + * Snapped cap state that is pending flush to mds. When a snapshot occurs, + * we first complete any in-process sync writes and writeback any dirty + * data before flushing the snapped state (tracked here) back to the MDS. + */ +struct ceph_cap_snap { + atomic_t nref; + struct ceph_inode_info *ci; + struct list_head ci_item, flushing_item; + + u64 follows, flush_tid; + int issued, dirty; + struct ceph_snap_context *context; + + mode_t mode; + uid_t uid; + gid_t gid; + + void *xattr_blob; + int xattr_len; + u64 xattr_version; + + u64 size; + struct timespec mtime, atime, ctime; + u64 time_warp_seq; + int writing; /* a sync write is still in progress */ + int dirty_pages; /* dirty pages awaiting writeback */ +}; + +static inline void ceph_put_cap_snap(struct ceph_cap_snap *capsnap) +{ + if (atomic_dec_and_test(&capsnap->nref)) + kfree(capsnap); +} + +/* + * The frag tree describes how a directory is fragmented, potentially across + * multiple metadata servers. It is also used to indicate points where + * metadata authority is delegated, and whether/where metadata is replicated. + * + * A _leaf_ frag will be present in the i_fragtree IFF there is + * delegation info. That is, if mds >= 0 || ndist > 0. + */ +#define CEPH_MAX_DIRFRAG_REP 4 + +struct ceph_inode_frag { + struct rb_node node; + + /* fragtree state */ + u32 frag; + int split_by; /* i.e. 2^(split_by) children */ + + /* delegation and replication info */ + int mds; /* -1 if same authority as parent */ + int ndist; /* >0 if replicated */ + int dist[CEPH_MAX_DIRFRAG_REP]; +}; + +/* + * We cache inode xattrs as an encoded blob until they are first used, + * at which point we parse them into an rbtree. + */ +struct ceph_inode_xattr { + struct rb_node node; + + const char *name; + int name_len; + const char *val; + int val_len; + int dirty; + + int should_free_name; + int should_free_val; +}; + +struct ceph_inode_xattrs_info { + /* + * (still encoded) xattr blob. we avoid the overhead of parsing + * this until someone actually calls getxattr, etc. + * + * blob->vec.iov_len == 4 implies there are no xattrs; blob == + * NULL means we don't know. + */ + struct ceph_buffer *blob, *prealloc_blob; + + struct rb_root index; + bool dirty; + int count; + int names_size; + int vals_size; + u64 version, index_version; +}; + +/* + * Ceph inode. + */ +#define CEPH_I_COMPLETE 1 /* we have complete directory cached */ +#define CEPH_I_NODELAY 4 /* do not delay cap release */ +#define CEPH_I_FLUSH 8 /* do not delay flush of dirty metadata */ + +struct ceph_inode_info { + struct ceph_vino i_vino; /* ceph ino + snap */ + + u64 i_version; + u32 i_time_warp_seq; + + unsigned i_ceph_flags; + unsigned long i_release_count; + + struct ceph_file_layout i_layout; + char *i_symlink; + + /* for dirs */ + struct timespec i_rctime; + u64 i_rbytes, i_rfiles, i_rsubdirs; + u64 i_files, i_subdirs; + u64 i_max_offset; /* largest readdir offset, set with I_COMPLETE */ + + struct rb_root i_fragtree; + struct mutex i_fragtree_mutex; + + struct ceph_inode_xattrs_info i_xattrs; + + /* capabilities. protected _both_ by i_lock and cap->session's + * s_mutex. */ + struct rb_root i_caps; /* cap list */ + struct ceph_cap *i_auth_cap; /* authoritative cap, if any */ + unsigned i_dirty_caps, i_flushing_caps; /* mask of dirtied fields */ + struct list_head i_dirty_item, i_flushing_item; + u64 i_cap_flush_seq; + /* we need to track cap writeback on a per-cap-bit basis, to allow + * overlapping, pipelined cap flushes to the mds. we can probably + * reduce the tid to 8 bits if we're concerned about inode size. */ + u16 i_cap_flush_last_tid, i_cap_flush_tid[CEPH_CAP_BITS]; + wait_queue_head_t i_cap_wq; /* threads waiting on a capability */ + unsigned long i_hold_caps_min; /* jiffies */ + unsigned long i_hold_caps_max; /* jiffies */ + struct list_head i_cap_delay_list; /* for delayed cap release to mds */ + int i_cap_exporting_mds; /* to handle cap migration between */ + unsigned i_cap_exporting_mseq; /* mds's. */ + unsigned i_cap_exporting_issued; + struct ceph_cap_reservation i_cap_migration_resv; + struct list_head i_cap_snaps; /* snapped state pending flush to mds */ + struct ceph_snap_context *i_head_snapc; /* set if wr_buffer_head > 0 */ + unsigned i_snap_caps; /* cap bits for snapped files */ + + int i_nr_by_mode[CEPH_FILE_MODE_NUM]; /* open file counts */ + + u32 i_truncate_seq; /* last truncate to smaller size */ + u64 i_truncate_size; /* and the size we last truncated down to */ + int i_truncate_pending; /* still need to call vmtruncate */ + + u64 i_max_size; /* max file size authorized by mds */ + u64 i_reported_size; /* (max_)size reported to or requested of mds */ + u64 i_wanted_max_size; /* offset we'd like to write too */ + u64 i_requested_max_size; /* max_size we've requested */ + + /* held references to caps */ + int i_pin_ref; + int i_rd_ref, i_rdcache_ref, i_wr_ref; + int i_wrbuffer_ref, i_wrbuffer_ref_head; + u32 i_shared_gen; /* increment each time we get FILE_SHARED */ + u32 i_rdcache_gen; /* we increment this each time we get + FILE_CACHE. If it's non-zero, we + _may_ have cached pages. */ + u32 i_rdcache_revoking; /* RDCACHE gen to async invalidate, if any */ + + struct list_head i_unsafe_writes; /* uncommitted sync writes */ + struct list_head i_unsafe_dirops; /* uncommitted mds dir ops */ + spinlock_t i_unsafe_lock; + + struct ceph_snap_realm *i_snap_realm; /* snap realm (if caps) */ + int i_snap_realm_counter; /* snap realm (if caps) */ + struct list_head i_snap_realm_item; + struct list_head i_snap_flush_item; + + struct work_struct i_wb_work; /* writeback work */ + struct work_struct i_pg_inv_work; /* page invalidation work */ + + struct work_struct i_vmtruncate_work; + + struct inode vfs_inode; /* at end */ +}; + +static inline struct ceph_inode_info *ceph_inode(struct inode *inode) +{ + return list_entry(inode, struct ceph_inode_info, vfs_inode); +} + +static inline void ceph_i_clear(struct inode *inode, unsigned mask) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + + spin_lock(&inode->i_lock); + ci->i_ceph_flags &= ~mask; + spin_unlock(&inode->i_lock); +} + +static inline void ceph_i_set(struct inode *inode, unsigned mask) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + + spin_lock(&inode->i_lock); + ci->i_ceph_flags |= mask; + spin_unlock(&inode->i_lock); +} + +static inline bool ceph_i_test(struct inode *inode, unsigned mask) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + bool r; + + smp_mb(); + r = (ci->i_ceph_flags & mask) == mask; + return r; +} + + +/* find a specific frag @f */ +extern struct ceph_inode_frag *__ceph_find_frag(struct ceph_inode_info *ci, + u32 f); + +/* + * choose fragment for value @v. copy frag content to pfrag, if leaf + * exists + */ +extern u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v, + struct ceph_inode_frag *pfrag, + int *found); + +/* + * Ceph dentry state + */ +struct ceph_dentry_info { + struct ceph_mds_session *lease_session; + u32 lease_gen, lease_shared_gen; + u32 lease_seq; + unsigned long lease_renew_after, lease_renew_from; + struct list_head lru; + struct dentry *dentry; + u64 time; + u64 offset; +}; + +static inline struct ceph_dentry_info *ceph_dentry(struct dentry *dentry) +{ + return (struct ceph_dentry_info *)dentry->d_fsdata; +} + +static inline loff_t ceph_make_fpos(unsigned frag, unsigned off) +{ + return ((loff_t)frag << 32) | (loff_t)off; +} + +/* + * ino_t is <64 bits on many architectures, blech. + * + * don't include snap in ino hash, at least for now. + */ +static inline ino_t ceph_vino_to_ino(struct ceph_vino vino) +{ + ino_t ino = (ino_t)vino.ino; /* ^ (vino.snap << 20); */ +#if BITS_PER_LONG == 32 + ino ^= vino.ino >> (sizeof(u64)-sizeof(ino_t)) * 8; + if (!ino) + ino = 1; +#endif + return ino; +} + +static inline int ceph_set_ino_cb(struct inode *inode, void *data) +{ + ceph_inode(inode)->i_vino = *(struct ceph_vino *)data; + inode->i_ino = ceph_vino_to_ino(*(struct ceph_vino *)data); + return 0; +} + +static inline struct ceph_vino ceph_vino(struct inode *inode) +{ + return ceph_inode(inode)->i_vino; +} + +/* for printf-style formatting */ +#define ceph_vinop(i) ceph_inode(i)->i_vino.ino, ceph_inode(i)->i_vino.snap + +static inline u64 ceph_ino(struct inode *inode) +{ + return ceph_inode(inode)->i_vino.ino; +} +static inline u64 ceph_snap(struct inode *inode) +{ + return ceph_inode(inode)->i_vino.snap; +} + +static inline int ceph_ino_compare(struct inode *inode, void *data) +{ + struct ceph_vino *pvino = (struct ceph_vino *)data; + struct ceph_inode_info *ci = ceph_inode(inode); + return ci->i_vino.ino == pvino->ino && + ci->i_vino.snap == pvino->snap; +} + +static inline struct inode *ceph_find_inode(struct super_block *sb, + struct ceph_vino vino) +{ + ino_t t = ceph_vino_to_ino(vino); + return ilookup5(sb, t, ceph_ino_compare, &vino); +} + + +/* + * caps helpers + */ +static inline bool __ceph_is_any_real_caps(struct ceph_inode_info *ci) +{ + return !RB_EMPTY_ROOT(&ci->i_caps); +} + +extern int __ceph_caps_issued(struct ceph_inode_info *ci, int *implemented); +extern int __ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, int t); +extern int __ceph_caps_issued_other(struct ceph_inode_info *ci, + struct ceph_cap *cap); + +static inline int ceph_caps_issued(struct ceph_inode_info *ci) +{ + int issued; + spin_lock(&ci->vfs_inode.i_lock); + issued = __ceph_caps_issued(ci, NULL); + spin_unlock(&ci->vfs_inode.i_lock); + return issued; +} + +static inline int ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, + int touch) +{ + int r; + spin_lock(&ci->vfs_inode.i_lock); + r = __ceph_caps_issued_mask(ci, mask, touch); + spin_unlock(&ci->vfs_inode.i_lock); + return r; +} + +static inline int __ceph_caps_dirty(struct ceph_inode_info *ci) +{ + return ci->i_dirty_caps | ci->i_flushing_caps; +} +extern int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask); + +extern int ceph_caps_revoking(struct ceph_inode_info *ci, int mask); +extern int __ceph_caps_used(struct ceph_inode_info *ci); + +extern int __ceph_caps_file_wanted(struct ceph_inode_info *ci); + +/* + * wanted, by virtue of open file modes AND cap refs (buffered/cached data) + */ +static inline int __ceph_caps_wanted(struct ceph_inode_info *ci) +{ + int w = __ceph_caps_file_wanted(ci) | __ceph_caps_used(ci); + if (w & CEPH_CAP_FILE_BUFFER) + w |= CEPH_CAP_FILE_EXCL; /* we want EXCL if dirty data */ + return w; +} + +/* what the mds thinks we want */ +extern int __ceph_caps_mds_wanted(struct ceph_inode_info *ci); + +extern void ceph_caps_init(void); +extern void ceph_caps_finalize(void); +extern int ceph_reserve_caps(struct ceph_cap_reservation *ctx, int need); +extern int ceph_unreserve_caps(struct ceph_cap_reservation *ctx); +extern void ceph_reservation_status(struct ceph_client *client, + int *total, int *avail, int *used, + int *reserved); + +static inline struct ceph_client *ceph_inode_to_client(struct inode *inode) +{ + return (struct ceph_client *)inode->i_sb->s_fs_info; +} + +static inline struct ceph_client *ceph_sb_to_client(struct super_block *sb) +{ + return (struct ceph_client *)sb->s_fs_info; +} + +static inline int ceph_queue_writeback(struct inode *inode) +{ + return queue_work(ceph_inode_to_client(inode)->wb_wq, + &ceph_inode(inode)->i_wb_work); +} + +static inline int ceph_queue_page_invalidation(struct inode *inode) +{ + return queue_work(ceph_inode_to_client(inode)->pg_inv_wq, + &ceph_inode(inode)->i_pg_inv_work); +} + + +/* + * we keep buffered readdir results attached to file->private_data + */ +struct ceph_file_info { + int fmode; /* initialized on open */ + + /* readdir: position within the dir */ + u32 frag; + struct ceph_mds_request *last_readdir; + int at_end; + + /* readdir: position within a frag */ + unsigned offset; /* offset of last chunk, adjusted for . and .. */ + u64 next_offset; /* offset of next chunk (last_name's + 1) */ + char *last_name; /* last entry in previous chunk */ + struct dentry *dentry; /* next dentry (for dcache readdir) */ + unsigned long dir_release_count; + + /* used for -o dirstat read() on directory thing */ + char *dir_info; + int dir_info_len; +}; + + + +/* + * snapshots + */ + +/* + * A "snap context" is the set of existing snapshots when we + * write data. It is used by the OSD to guide its COW behavior. + * + * The ceph_snap_context is refcounted, and attached to each dirty + * page, indicating which context the dirty data belonged when it was + * dirtied. + */ +struct ceph_snap_context { + atomic_t nref; + u64 seq; + int num_snaps; + u64 snaps[]; +}; + +static inline struct ceph_snap_context * +ceph_get_snap_context(struct ceph_snap_context *sc) +{ + /* + printk("get_snap_context %p %d -> %d\n", sc, atomic_read(&sc->nref), + atomic_read(&sc->nref)+1); + */ + if (sc) + atomic_inc(&sc->nref); + return sc; +} + +static inline void ceph_put_snap_context(struct ceph_snap_context *sc) +{ + if (!sc) + return; + /* + printk("put_snap_context %p %d -> %d\n", sc, atomic_read(&sc->nref), + atomic_read(&sc->nref)-1); + */ + if (atomic_dec_and_test(&sc->nref)) { + /*printk(" deleting snap_context %p\n", sc);*/ + kfree(sc); + } +} + +/* + * A "snap realm" describes a subset of the file hierarchy sharing + * the same set of snapshots that apply to it. The realms themselves + * are organized into a hierarchy, such that children inherit (some of) + * the snapshots of their parents. + * + * All inodes within the realm that have capabilities are linked into a + * per-realm list. + */ +struct ceph_snap_realm { + u64 ino; + atomic_t nref; + u64 created, seq; + u64 parent_ino; + u64 parent_since; /* snapid when our current parent became so */ + + u64 *prior_parent_snaps; /* snaps inherited from any parents we */ + int num_prior_parent_snaps; /* had prior to parent_since */ + u64 *snaps; /* snaps specific to this realm */ + int num_snaps; + + struct ceph_snap_realm *parent; + struct list_head children; /* list of child realms */ + struct list_head child_item; + + struct list_head empty_item; /* if i have ref==0 */ + + /* the current set of snaps for this realm */ + struct ceph_snap_context *cached_context; + + struct list_head inodes_with_caps; + spinlock_t inodes_with_caps_lock; +}; + + + +/* + * calculate the number of pages a given length and offset map onto, + * if we align the data. + */ +static inline int calc_pages_for(u64 off, u64 len) +{ + return ((off+len+PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT) - + (off >> PAGE_CACHE_SHIFT); +} + + + +/* snap.c */ +struct ceph_snap_realm *ceph_lookup_snap_realm(struct ceph_mds_client *mdsc, + u64 ino); +extern void ceph_get_snap_realm(struct ceph_mds_client *mdsc, + struct ceph_snap_realm *realm); +extern void ceph_put_snap_realm(struct ceph_mds_client *mdsc, + struct ceph_snap_realm *realm); +extern int ceph_update_snap_trace(struct ceph_mds_client *m, + void *p, void *e, bool deletion); +extern void ceph_handle_snap(struct ceph_mds_client *mdsc, + struct ceph_msg *msg); +extern void ceph_queue_cap_snap(struct ceph_inode_info *ci, + struct ceph_snap_context *snapc); +extern int __ceph_finish_cap_snap(struct ceph_inode_info *ci, + struct ceph_cap_snap *capsnap); +extern void ceph_cleanup_empty_realms(struct ceph_mds_client *mdsc); + +/* + * a cap_snap is "pending" if it is still awaiting an in-progress + * sync write (that may/may not still update size, mtime, etc.). + */ +static inline bool __ceph_have_pending_cap_snap(struct ceph_inode_info *ci) +{ + return !list_empty(&ci->i_cap_snaps) && + list_entry(ci->i_cap_snaps.prev, struct ceph_cap_snap, + ci_item)->writing; +} + + +/* super.c */ +extern struct kmem_cache *ceph_inode_cachep; +extern struct kmem_cache *ceph_cap_cachep; +extern struct kmem_cache *ceph_dentry_cachep; +extern struct kmem_cache *ceph_file_cachep; + +extern const char *ceph_msg_type_name(int type); + +#define FSID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \ + "%02x%02x%02x%02x%02x%02x" +#define PR_FSID(f) (f)->fsid[0], (f)->fsid[1], (f)->fsid[2], (f)->fsid[3], \ + (f)->fsid[4], (f)->fsid[5], (f)->fsid[6], (f)->fsid[7], \ + (f)->fsid[8], (f)->fsid[9], (f)->fsid[10], (f)->fsid[11], \ + (f)->fsid[12], (f)->fsid[13], (f)->fsid[14], (f)->fsid[15] + +/* inode.c */ +extern const struct inode_operations ceph_file_iops; + +extern struct inode *ceph_alloc_inode(struct super_block *sb); +extern void ceph_destroy_inode(struct inode *inode); + +extern struct inode *ceph_get_inode(struct super_block *sb, + struct ceph_vino vino); +extern struct inode *ceph_get_snapdir(struct inode *parent); +extern int ceph_fill_file_size(struct inode *inode, int issued, + u32 truncate_seq, u64 truncate_size, u64 size); +extern void ceph_fill_file_time(struct inode *inode, int issued, + u64 time_warp_seq, struct timespec *ctime, + struct timespec *mtime, struct timespec *atime); +extern int ceph_fill_trace(struct super_block *sb, + struct ceph_mds_request *req, + struct ceph_mds_session *session); +extern int ceph_readdir_prepopulate(struct ceph_mds_request *req, + struct ceph_mds_session *session); + +extern int ceph_inode_holds_cap(struct inode *inode, int mask); + +extern int ceph_inode_set_size(struct inode *inode, loff_t size); +extern void ceph_inode_writeback(struct work_struct *work); +extern void ceph_vmtruncate_work(struct work_struct *work); +extern void __ceph_do_pending_vmtruncate(struct inode *inode); +extern void __ceph_queue_vmtruncate(struct inode *inode); + +extern int ceph_do_getattr(struct inode *inode, int mask); +extern int ceph_permission(struct inode *inode, int mask); +extern int ceph_setattr(struct dentry *dentry, struct iattr *attr); +extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat); + +/* xattr.c */ +extern int ceph_setxattr(struct dentry *, const char *, const void *, + size_t, int); +extern ssize_t ceph_getxattr(struct dentry *, const char *, void *, size_t); +extern ssize_t ceph_listxattr(struct dentry *, char *, size_t); +extern int ceph_removexattr(struct dentry *, const char *); +extern void __ceph_build_xattrs_blob(struct ceph_inode_info *ci); +extern void __ceph_destroy_xattrs(struct ceph_inode_info *ci); + +/* caps.c */ +extern const char *ceph_cap_string(int c); +extern void ceph_handle_caps(struct ceph_mds_session *session, + struct ceph_msg *msg); +extern int ceph_add_cap(struct inode *inode, + struct ceph_mds_session *session, u64 cap_id, + int fmode, unsigned issued, unsigned wanted, + unsigned cap, unsigned seq, u64 realmino, int flags, + struct ceph_cap_reservation *caps_reservation); +extern void __ceph_remove_cap(struct ceph_cap *cap, + struct ceph_cap_reservation *ctx); +static inline void ceph_remove_cap(struct ceph_cap *cap) +{ + struct inode *inode = &cap->ci->vfs_inode; + spin_lock(&inode->i_lock); + __ceph_remove_cap(cap, NULL); + spin_unlock(&inode->i_lock); +} + +extern void ceph_queue_caps_release(struct inode *inode); +extern int ceph_write_inode(struct inode *inode, int unused); +extern int ceph_fsync(struct file *file, struct dentry *dentry, int datasync); +extern void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session); +extern int ceph_get_cap_mds(struct inode *inode); +extern void ceph_get_cap_refs(struct ceph_inode_info *ci, int caps); +extern void ceph_put_cap_refs(struct ceph_inode_info *ci, int had); +extern void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr, + struct ceph_snap_context *snapc); +extern void __ceph_flush_snaps(struct ceph_inode_info *ci, + struct ceph_mds_session **psession); +extern void ceph_check_caps(struct ceph_inode_info *ci, int flags, + struct ceph_mds_session *session); +extern void ceph_check_delayed_caps(struct ceph_mds_client *mdsc, + int flushdirty); + +extern int ceph_encode_inode_release(void **p, struct inode *inode, + int mds, int drop, int unless, int force); +extern int ceph_encode_dentry_release(void **p, struct dentry *dn, + int mds, int drop, int unless); + +extern int ceph_get_caps(struct ceph_inode_info *ci, int need, int want, + int *got, loff_t endoff); + +/* for counting open files by mode */ +static inline void __ceph_get_fmode(struct ceph_inode_info *ci, int mode) +{ + ci->i_nr_by_mode[mode]++; +} +extern void ceph_put_fmode(struct ceph_inode_info *ci, int mode); + +/* addr.c */ +extern const struct address_space_operations ceph_aops; +extern int ceph_mmap(struct file *file, struct vm_area_struct *vma); + +/* file.c */ +extern const struct file_operations ceph_file_fops; +extern const struct address_space_operations ceph_aops; +extern int ceph_open(struct inode *inode, struct file *file); +extern struct dentry *ceph_lookup_open(struct inode *dir, struct dentry *dentry, + struct nameidata *nd, int mode, + int locked_dir); +extern int ceph_release(struct inode *inode, struct file *filp); +extern void ceph_release_page_vector(struct page **pages, int num_pages); + +/* dir.c */ +extern const struct file_operations ceph_dir_fops; +extern const struct inode_operations ceph_dir_iops; +extern struct dentry_operations ceph_dentry_ops, ceph_snap_dentry_ops, + ceph_snapdir_dentry_ops; + +extern int ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry); +extern struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, + struct dentry *dentry, int err); + +extern void ceph_dentry_lru_add(struct dentry *dn); +extern void ceph_dentry_lru_touch(struct dentry *dn); +extern void ceph_dentry_lru_del(struct dentry *dn); + +/* + * our d_ops vary depending on whether the inode is live, + * snapshotted (read-only), or a virtual ".snap" directory. + */ +int ceph_init_dentry(struct dentry *dentry); + + +/* ioctl.c */ +extern long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg); + +/* export.c */ +extern const struct export_operations ceph_export_ops; + +/* debugfs.c */ +extern int ceph_debugfs_init(void); +extern void ceph_debugfs_cleanup(void); +extern int ceph_debugfs_client_init(struct ceph_client *client); +extern void ceph_debugfs_client_cleanup(struct ceph_client *client); + +static inline struct inode *get_dentry_parent_inode(struct dentry *dentry) +{ + if (dentry && dentry->d_parent) + return dentry->d_parent->d_inode; + + return NULL; +} + +#endif /* _FS_CEPH_SUPER_H */ diff --git a/fs/ceph/types.h b/fs/ceph/types.h new file mode 100644 index 0000000..8a51456 --- /dev/null +++ b/fs/ceph/types.h @@ -0,0 +1,28 @@ +#ifndef _FS_CEPH_TYPES_H +#define _FS_CEPH_TYPES_H + +/* needed before including ceph_fs.h */ +#include +#include +#include +#include + +#include "ceph_fs.h" +#include "ceph_frag.h" + +/* + * Identify inodes by both their ino AND snapshot id (a u64). + */ +struct ceph_vino { + u64 ino; + u64 snap; +}; + + +/* context for the caps reservation mechanism */ +struct ceph_cap_reservation { + int count; +}; + + +#endif -- cgit v0.10.2 From c30dbb9cc7fc75ab1d0ee6fb084ba4684f7a665d Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:07 -0700 Subject: ceph: ref counted buffer struct ceph_buffer is a simple ref-counted buffer. We transparently choose between kmalloc for small buffers and vmalloc for large ones. This is currently used only for allocating memory for xattr data. Signed-off-by: Sage Weil diff --git a/fs/ceph/buffer.c b/fs/ceph/buffer.c new file mode 100644 index 0000000..cf9aacc --- /dev/null +++ b/fs/ceph/buffer.c @@ -0,0 +1,34 @@ + +#include "ceph_debug.h" +#include "buffer.h" + +struct ceph_buffer *ceph_buffer_new(gfp_t gfp) +{ + struct ceph_buffer *b; + + b = kmalloc(sizeof(*b), gfp); + if (!b) + return NULL; + atomic_set(&b->nref, 1); + b->vec.iov_base = NULL; + b->vec.iov_len = 0; + b->alloc_len = 0; + return b; +} + +int ceph_buffer_alloc(struct ceph_buffer *b, int len, gfp_t gfp) +{ + b->vec.iov_base = kmalloc(len, gfp | __GFP_NOWARN); + if (b->vec.iov_base) { + b->is_vmalloc = false; + } else { + b->vec.iov_base = __vmalloc(len, gfp, PAGE_KERNEL); + b->is_vmalloc = true; + } + if (!b->vec.iov_base) + return -ENOMEM; + b->alloc_len = len; + b->vec.iov_len = len; + return 0; +} + diff --git a/fs/ceph/buffer.h b/fs/ceph/buffer.h new file mode 100644 index 0000000..16b1930 --- /dev/null +++ b/fs/ceph/buffer.h @@ -0,0 +1,55 @@ +#ifndef __FS_CEPH_BUFFER_H +#define __FS_CEPH_BUFFER_H + +#include +#include +#include +#include + +/* + * a simple reference counted buffer. + * + * use kmalloc for small sizes (<= one page), vmalloc for larger + * sizes. + */ +struct ceph_buffer { + atomic_t nref; + struct kvec vec; + size_t alloc_len; + bool is_vmalloc; +}; + +struct ceph_buffer *ceph_buffer_new(gfp_t gfp); +int ceph_buffer_alloc(struct ceph_buffer *b, int len, gfp_t gfp); + +static inline struct ceph_buffer *ceph_buffer_get(struct ceph_buffer *b) +{ + atomic_inc(&b->nref); + return b; +} + +static inline void ceph_buffer_put(struct ceph_buffer *b) +{ + if (b && atomic_dec_and_test(&b->nref)) { + if (b->vec.iov_base) { + if (b->is_vmalloc) + vfree(b->vec.iov_base); + else + kfree(b->vec.iov_base); + } + kfree(b); + } +} + +static inline struct ceph_buffer *ceph_buffer_new_alloc(int len, gfp_t gfp) +{ + struct ceph_buffer *b = ceph_buffer_new(gfp); + + if (b && ceph_buffer_alloc(b, len, gfp) < 0) { + ceph_buffer_put(b); + b = NULL; + } + return b; +} + +#endif -- cgit v0.10.2 From 16725b9d2a2e3d0fd2b0034482e2eb0a2d78050f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:07 -0700 Subject: ceph: super.c Mount option parsing, client setup and teardown, and a few odds and ends (e.g., statfs). Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c new file mode 100644 index 0000000..0723fb6 --- /dev/null +++ b/fs/ceph/super.c @@ -0,0 +1,936 @@ + +#include "ceph_debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ceph_ver.h" +#include "decode.h" +#include "super.h" +#include "mon_client.h" + +/* + * Ceph superblock operations + * + * Handle the basics of mounting, unmounting. + */ + + +/* + * find filename portion of a path (/foo/bar/baz -> baz) + */ +const char *ceph_file_part(const char *s, int len) +{ + const char *e = s + len; + + while (e != s && *(e-1) != '/') + e--; + return e; +} + + +/* + * super ops + */ +static void ceph_put_super(struct super_block *s) +{ + struct ceph_client *cl = ceph_client(s); + + dout("put_super\n"); + ceph_mdsc_close_sessions(&cl->mdsc); + return; +} + +static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct ceph_client *client = ceph_inode_to_client(dentry->d_inode); + struct ceph_monmap *monmap = client->monc.monmap; + struct ceph_statfs st; + u64 fsid; + int err; + + dout("statfs\n"); + err = ceph_monc_do_statfs(&client->monc, &st); + if (err < 0) + return err; + + /* fill in kstatfs */ + buf->f_type = CEPH_SUPER_MAGIC; /* ?? */ + + /* + * express utilization in terms of large blocks to avoid + * overflow on 32-bit machines. + */ + buf->f_bsize = 1 << CEPH_BLOCK_SHIFT; + buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10); + buf->f_bfree = (le64_to_cpu(st.kb) - le64_to_cpu(st.kb_used)) >> + (CEPH_BLOCK_SHIFT-10); + buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10); + + buf->f_files = le64_to_cpu(st.num_objects); + buf->f_ffree = -1; + buf->f_namelen = PATH_MAX; + buf->f_frsize = PAGE_CACHE_SIZE; + + /* leave fsid little-endian, regardless of host endianness */ + fsid = *(u64 *)(&monmap->fsid) ^ *((u64 *)&monmap->fsid + 1); + buf->f_fsid.val[0] = fsid & 0xffffffff; + buf->f_fsid.val[1] = fsid >> 32; + + return 0; +} + + +static int ceph_syncfs(struct super_block *sb, int wait) +{ + dout("sync_fs %d\n", wait); + ceph_osdc_sync(&ceph_client(sb)->osdc); + ceph_mdsc_sync(&ceph_client(sb)->mdsc); + return 0; +} + + +/** + * ceph_show_options - Show mount options in /proc/mounts + * @m: seq_file to write to + * @mnt: mount descriptor + */ +static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct ceph_client *client = ceph_sb_to_client(mnt->mnt_sb); + struct ceph_mount_args *args = &client->mount_args; + + if (args->flags & CEPH_OPT_FSID) + seq_printf(m, ",fsidmajor=%llu,fsidminor%llu", + le64_to_cpu(*(__le64 *)&args->fsid.fsid[0]), + le64_to_cpu(*(__le64 *)&args->fsid.fsid[8])); + if (args->flags & CEPH_OPT_NOSHARE) + seq_puts(m, ",noshare"); + if (args->flags & CEPH_OPT_DIRSTAT) + seq_puts(m, ",dirstat"); + if ((args->flags & CEPH_OPT_RBYTES) == 0) + seq_puts(m, ",norbytes"); + if (args->flags & CEPH_OPT_NOCRC) + seq_puts(m, ",nocrc"); + if (args->flags & CEPH_OPT_NOASYNCREADDIR) + seq_puts(m, ",noasyncreaddir"); + if (strcmp(args->snapdir_name, CEPH_SNAPDIRNAME_DEFAULT)) + seq_printf(m, ",snapdirname=%s", args->snapdir_name); + if (args->secret) + seq_puts(m, ",secret="); + return 0; +} + +/* + * caches + */ +struct kmem_cache *ceph_inode_cachep; +struct kmem_cache *ceph_cap_cachep; +struct kmem_cache *ceph_dentry_cachep; +struct kmem_cache *ceph_file_cachep; + +static void ceph_inode_init_once(void *foo) +{ + struct ceph_inode_info *ci = foo; + inode_init_once(&ci->vfs_inode); +} + +static int __init init_caches(void) +{ + ceph_inode_cachep = kmem_cache_create("ceph_inode_info", + sizeof(struct ceph_inode_info), + __alignof__(struct ceph_inode_info), + (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), + ceph_inode_init_once); + if (ceph_inode_cachep == NULL) + return -ENOMEM; + + ceph_cap_cachep = KMEM_CACHE(ceph_cap, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); + if (ceph_cap_cachep == NULL) + goto bad_cap; + + ceph_dentry_cachep = KMEM_CACHE(ceph_dentry_info, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); + if (ceph_dentry_cachep == NULL) + goto bad_dentry; + + ceph_file_cachep = KMEM_CACHE(ceph_file_info, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); + if (ceph_file_cachep == NULL) + goto bad_file; + + return 0; + +bad_file: + kmem_cache_destroy(ceph_dentry_cachep); +bad_dentry: + kmem_cache_destroy(ceph_cap_cachep); +bad_cap: + kmem_cache_destroy(ceph_inode_cachep); + return -ENOMEM; +} + +static void destroy_caches(void) +{ + kmem_cache_destroy(ceph_inode_cachep); + kmem_cache_destroy(ceph_cap_cachep); + kmem_cache_destroy(ceph_dentry_cachep); + kmem_cache_destroy(ceph_file_cachep); +} + + +/* + * ceph_umount_begin - initiate forced umount. Tear down down the + * mount, skipping steps that may hang while waiting for server(s). + */ +static void ceph_umount_begin(struct super_block *sb) +{ + struct ceph_client *client = ceph_sb_to_client(sb); + + dout("ceph_umount_begin - starting forced umount\n"); + if (!client) + return; + client->mount_state = CEPH_MOUNT_SHUTDOWN; + return; +} + +static const struct super_operations ceph_super_ops = { + .alloc_inode = ceph_alloc_inode, + .destroy_inode = ceph_destroy_inode, + .write_inode = ceph_write_inode, + .sync_fs = ceph_syncfs, + .put_super = ceph_put_super, + .show_options = ceph_show_options, + .statfs = ceph_statfs, + .umount_begin = ceph_umount_begin, +}; + + +const char *ceph_msg_type_name(int type) +{ + switch (type) { + case CEPH_MSG_SHUTDOWN: return "shutdown"; + case CEPH_MSG_PING: return "ping"; + case CEPH_MSG_MON_MAP: return "mon_map"; + case CEPH_MSG_MON_GET_MAP: return "mon_get_map"; + case CEPH_MSG_MON_SUBSCRIBE: return "mon_subscribe"; + case CEPH_MSG_MON_SUBSCRIBE_ACK: return "mon_subscribe_ack"; + case CEPH_MSG_CLIENT_MOUNT: return "client_mount"; + case CEPH_MSG_CLIENT_MOUNT_ACK: return "client_mount_ack"; + case CEPH_MSG_STATFS: return "statfs"; + case CEPH_MSG_STATFS_REPLY: return "statfs_reply"; + case CEPH_MSG_MDS_GETMAP: return "mds_getmap"; + case CEPH_MSG_MDS_MAP: return "mds_map"; + case CEPH_MSG_CLIENT_SESSION: return "client_session"; + case CEPH_MSG_CLIENT_RECONNECT: return "client_reconnect"; + case CEPH_MSG_CLIENT_REQUEST: return "client_request"; + case CEPH_MSG_CLIENT_REQUEST_FORWARD: return "client_request_forward"; + case CEPH_MSG_CLIENT_REPLY: return "client_reply"; + case CEPH_MSG_CLIENT_CAPS: return "client_caps"; + case CEPH_MSG_CLIENT_CAPRELEASE: return "client_cap_release"; + case CEPH_MSG_CLIENT_SNAP: return "client_snap"; + case CEPH_MSG_CLIENT_LEASE: return "client_lease"; + case CEPH_MSG_OSD_GETMAP: return "osd_getmap"; + case CEPH_MSG_OSD_MAP: return "osd_map"; + case CEPH_MSG_OSD_OP: return "osd_op"; + case CEPH_MSG_OSD_OPREPLY: return "osd_opreply"; + default: return "unknown"; + } +} + + +/* + * mount options + */ +enum { + Opt_fsidmajor, + Opt_fsidminor, + Opt_monport, + Opt_wsize, + Opt_rsize, + Opt_osdtimeout, + Opt_mount_timeout, + Opt_caps_wanted_delay_min, + Opt_caps_wanted_delay_max, + Opt_readdir_max_entries, + /* int args above */ + Opt_snapdirname, + Opt_secret, + /* string args above */ + Opt_ip, + Opt_noshare, + Opt_dirstat, + Opt_nodirstat, + Opt_rbytes, + Opt_norbytes, + Opt_nocrc, + Opt_noasyncreaddir, +}; + +static match_table_t arg_tokens = { + {Opt_fsidmajor, "fsidmajor=%ld"}, + {Opt_fsidminor, "fsidminor=%ld"}, + {Opt_monport, "monport=%d"}, + {Opt_wsize, "wsize=%d"}, + {Opt_rsize, "rsize=%d"}, + {Opt_osdtimeout, "osdtimeout=%d"}, + {Opt_mount_timeout, "mount_timeout=%d"}, + {Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"}, + {Opt_caps_wanted_delay_max, "caps_wanted_delay_max=%d"}, + {Opt_readdir_max_entries, "readdir_max_entries=%d"}, + /* int args above */ + {Opt_snapdirname, "snapdirname=%s"}, + {Opt_secret, "secret=%s"}, + /* string args above */ + {Opt_ip, "ip=%s"}, + {Opt_noshare, "noshare"}, + {Opt_dirstat, "dirstat"}, + {Opt_nodirstat, "nodirstat"}, + {Opt_rbytes, "rbytes"}, + {Opt_norbytes, "norbytes"}, + {Opt_nocrc, "nocrc"}, + {Opt_noasyncreaddir, "noasyncreaddir"}, + {-1, NULL} +}; + + +static int parse_mount_args(struct ceph_client *client, + int flags, char *options, const char *dev_name, + const char **path) +{ + struct ceph_mount_args *args = &client->mount_args; + const char *c; + int err; + substring_t argstr[MAX_OPT_ARGS]; + int num_mon; + struct ceph_entity_addr mon_addr[CEPH_MAX_MON_MOUNT_ADDR]; + int i; + + dout("parse_mount_args dev_name '%s'\n", dev_name); + memset(args, 0, sizeof(*args)); + + /* start with defaults */ + args->sb_flags = flags; + args->flags = CEPH_OPT_DEFAULT; + args->osd_timeout = 5; /* seconds */ + args->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; /* seconds */ + args->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT; + args->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT; + args->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL); + args->cap_release_safety = CEPH_CAPS_PER_RELEASE * 4; + args->max_readdir = 1024; + + /* ip1[:port1][,ip2[:port2]...]:/subdir/in/fs */ + if (!dev_name) + return -EINVAL; + *path = strstr(dev_name, ":/"); + if (*path == NULL) { + pr_err("device name is missing path (no :/ in %s)\n", + dev_name); + return -EINVAL; + } + + /* get mon ip(s) */ + err = ceph_parse_ips(dev_name, *path, mon_addr, + CEPH_MAX_MON_MOUNT_ADDR, &num_mon); + if (err < 0) + return err; + + /* build initial monmap */ + client->monc.monmap = kzalloc(sizeof(*client->monc.monmap) + + num_mon*sizeof(client->monc.monmap->mon_inst[0]), + GFP_KERNEL); + if (!client->monc.monmap) + return -ENOMEM; + for (i = 0; i < num_mon; i++) { + client->monc.monmap->mon_inst[i].addr = mon_addr[i]; + client->monc.monmap->mon_inst[i].addr.erank = 0; + client->monc.monmap->mon_inst[i].addr.nonce = 0; + client->monc.monmap->mon_inst[i].name.type = + CEPH_ENTITY_TYPE_MON; + client->monc.monmap->mon_inst[i].name.num = cpu_to_le64(i); + } + client->monc.monmap->num_mon = num_mon; + memset(&args->my_addr.in_addr, 0, sizeof(args->my_addr.in_addr)); + + /* path on server */ + *path += 2; + dout("server path '%s'\n", *path); + + /* parse mount options */ + while ((c = strsep(&options, ",")) != NULL) { + int token, intval, ret; + if (!*c) + continue; + token = match_token((char *)c, arg_tokens, argstr); + if (token < 0) { + pr_err("bad mount option at '%s'\n", c); + return -EINVAL; + + } + if (token < Opt_ip) { + ret = match_int(&argstr[0], &intval); + if (ret < 0) { + pr_err("bad mount option arg (not int) " + "at '%s'\n", c); + continue; + } + dout("got token %d intval %d\n", token, intval); + } + switch (token) { + case Opt_fsidmajor: + *(__le64 *)&args->fsid.fsid[0] = cpu_to_le64(intval); + break; + case Opt_fsidminor: + *(__le64 *)&args->fsid.fsid[8] = cpu_to_le64(intval); + break; + case Opt_ip: + err = ceph_parse_ips(argstr[0].from, + argstr[0].to, + &args->my_addr, + 1, NULL); + if (err < 0) + return err; + args->flags |= CEPH_OPT_MYIP; + break; + + case Opt_snapdirname: + kfree(args->snapdir_name); + args->snapdir_name = kstrndup(argstr[0].from, + argstr[0].to-argstr[0].from, + GFP_KERNEL); + break; + case Opt_secret: + args->secret = kstrndup(argstr[0].from, + argstr[0].to-argstr[0].from, + GFP_KERNEL); + break; + + /* misc */ + case Opt_wsize: + args->wsize = intval; + break; + case Opt_rsize: + args->rsize = intval; + break; + case Opt_osdtimeout: + args->osd_timeout = intval; + break; + case Opt_mount_timeout: + args->mount_timeout = intval; + break; + case Opt_caps_wanted_delay_min: + args->caps_wanted_delay_min = intval; + break; + case Opt_caps_wanted_delay_max: + args->caps_wanted_delay_max = intval; + break; + case Opt_readdir_max_entries: + args->max_readdir = intval; + break; + + case Opt_noshare: + args->flags |= CEPH_OPT_NOSHARE; + break; + + case Opt_dirstat: + args->flags |= CEPH_OPT_DIRSTAT; + break; + case Opt_nodirstat: + args->flags &= ~CEPH_OPT_DIRSTAT; + break; + case Opt_rbytes: + args->flags |= CEPH_OPT_RBYTES; + break; + case Opt_norbytes: + args->flags &= ~CEPH_OPT_RBYTES; + break; + case Opt_nocrc: + args->flags |= CEPH_OPT_NOCRC; + break; + case Opt_noasyncreaddir: + args->flags |= CEPH_OPT_NOASYNCREADDIR; + break; + + default: + BUG_ON(token); + } + } + + return 0; +} + +static void release_mount_args(struct ceph_mount_args *args) +{ + kfree(args->snapdir_name); + args->snapdir_name = NULL; + kfree(args->secret); + args->secret = NULL; +} + +/* + * create a fresh client instance + */ +static struct ceph_client *ceph_create_client(void) +{ + struct ceph_client *client; + int err = -ENOMEM; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (client == NULL) + return ERR_PTR(-ENOMEM); + + mutex_init(&client->mount_mutex); + + init_waitqueue_head(&client->mount_wq); + + client->sb = NULL; + client->mount_state = CEPH_MOUNT_MOUNTING; + client->whoami = -1; + + client->msgr = NULL; + + client->mount_err = 0; + client->signed_ticket = NULL; + client->signed_ticket_len = 0; + + err = -ENOMEM; + client->wb_wq = create_workqueue("ceph-writeback"); + if (client->wb_wq == NULL) + goto fail; + client->pg_inv_wq = create_singlethread_workqueue("ceph-pg-invalid"); + if (client->pg_inv_wq == NULL) + goto fail_wb_wq; + client->trunc_wq = create_singlethread_workqueue("ceph-trunc"); + if (client->trunc_wq == NULL) + goto fail_pg_inv_wq; + + /* subsystems */ + err = ceph_monc_init(&client->monc, client); + if (err < 0) + goto fail_trunc_wq; + err = ceph_osdc_init(&client->osdc, client); + if (err < 0) + goto fail_monc; + ceph_mdsc_init(&client->mdsc, client); + return client; + +fail_monc: + ceph_monc_stop(&client->monc); +fail_trunc_wq: + destroy_workqueue(client->trunc_wq); +fail_pg_inv_wq: + destroy_workqueue(client->pg_inv_wq); +fail_wb_wq: + destroy_workqueue(client->wb_wq); +fail: + kfree(client); + return ERR_PTR(err); +} + +static void ceph_destroy_client(struct ceph_client *client) +{ + dout("destroy_client %p\n", client); + + /* unmount */ + ceph_mdsc_stop(&client->mdsc); + ceph_monc_stop(&client->monc); + ceph_osdc_stop(&client->osdc); + + kfree(client->signed_ticket); + + ceph_debugfs_client_cleanup(client); + destroy_workqueue(client->wb_wq); + destroy_workqueue(client->pg_inv_wq); + destroy_workqueue(client->trunc_wq); + + if (client->msgr) + ceph_messenger_destroy(client->msgr); + if (client->wb_pagevec_pool) + mempool_destroy(client->wb_pagevec_pool); + + release_mount_args(&client->mount_args); + + kfree(client); + dout("destroy_client %p done\n", client); +} + +/* + * true if we have the mon map (and have thus joined the cluster) + */ +static int have_mon_map(struct ceph_client *client) +{ + return client->monc.monmap && client->monc.monmap->epoch; +} + +/* + * Bootstrap mount by opening the root directory. Note the mount + * @started time from caller, and time out if this takes too long. + */ +static struct dentry *open_root_dentry(struct ceph_client *client, + const char *path, + unsigned long started) +{ + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req = NULL; + int err; + struct dentry *root; + + /* open dir */ + dout("open_root_inode opening '%s'\n", path); + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_GETATTR, USE_ANY_MDS); + if (IS_ERR(req)) + return ERR_PTR(PTR_ERR(req)); + req->r_path1 = kstrdup(path, GFP_NOFS); + req->r_ino1.ino = CEPH_INO_ROOT; + req->r_ino1.snap = CEPH_NOSNAP; + req->r_started = started; + req->r_timeout = client->mount_args.mount_timeout * HZ; + req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE); + req->r_num_caps = 2; + err = ceph_mdsc_do_request(mdsc, NULL, req); + if (err == 0) { + dout("open_root_inode success\n"); + if (ceph_ino(req->r_target_inode) == CEPH_INO_ROOT && + client->sb->s_root == NULL) + root = d_alloc_root(req->r_target_inode); + else + root = d_obtain_alias(req->r_target_inode); + req->r_target_inode = NULL; + dout("open_root_inode success, root dentry is %p\n", root); + } else { + root = ERR_PTR(err); + } + ceph_mdsc_put_request(req); + return root; +} + +/* + * mount: join the ceph cluster, and open root directory. + */ +static int ceph_mount(struct ceph_client *client, struct vfsmount *mnt, + const char *path) +{ + struct ceph_entity_addr *myaddr = NULL; + int err; + unsigned long timeout = client->mount_args.mount_timeout * HZ; + unsigned long started = jiffies; /* note the start time */ + struct dentry *root; + + dout("mount start\n"); + mutex_lock(&client->mount_mutex); + + /* initialize the messenger */ + if (client->msgr == NULL) { + if (ceph_test_opt(client, MYIP)) + myaddr = &client->mount_args.my_addr; + client->msgr = ceph_messenger_create(myaddr); + if (IS_ERR(client->msgr)) { + err = PTR_ERR(client->msgr); + client->msgr = NULL; + goto out; + } + client->msgr->nocrc = ceph_test_opt(client, NOCRC); + } + + /* send mount request, and wait for mon, mds, and osd maps */ + err = ceph_monc_request_mount(&client->monc); + if (err < 0) + goto out; + + while (!have_mon_map(client) && !client->mount_err) { + err = -EIO; + if (timeout && time_after_eq(jiffies, started + timeout)) + goto out; + + /* wait */ + dout("mount waiting for mount\n"); + err = wait_event_interruptible_timeout(client->mount_wq, + client->mount_err || have_mon_map(client), + timeout); + if (err == -EINTR || err == -ERESTARTSYS) + goto out; + if (client->mount_err) { + err = client->mount_err; + goto out; + } + } + + dout("mount opening root\n"); + root = open_root_dentry(client, "", started); + if (IS_ERR(root)) { + err = PTR_ERR(root); + goto out; + } + if (client->sb->s_root) + dput(root); + else + client->sb->s_root = root; + + if (path[0] == 0) { + dget(root); + } else { + dout("mount opening base mountpoint\n"); + root = open_root_dentry(client, path, started); + if (IS_ERR(root)) { + err = PTR_ERR(root); + dput(client->sb->s_root); + client->sb->s_root = NULL; + goto out; + } + } + + mnt->mnt_root = root; + mnt->mnt_sb = client->sb; + + client->mount_state = CEPH_MOUNT_MOUNTED; + dout("mount success\n"); + err = 0; + +out: + mutex_unlock(&client->mount_mutex); + return err; +} + +static int ceph_set_super(struct super_block *s, void *data) +{ + struct ceph_client *client = data; + int ret; + + dout("set_super %p data %p\n", s, data); + + s->s_flags = client->mount_args.sb_flags; + s->s_maxbytes = 1ULL << 40; /* temp value until we get mdsmap */ + + s->s_fs_info = client; + client->sb = s; + + s->s_op = &ceph_super_ops; + s->s_export_op = &ceph_export_ops; + + s->s_time_gran = 1000; /* 1000 ns == 1 us */ + + ret = set_anon_super(s, NULL); /* what is that second arg for? */ + if (ret != 0) + goto fail; + + return ret; + +fail: + s->s_fs_info = NULL; + client->sb = NULL; + return ret; +} + +/* + * share superblock if same fs AND options + */ +static int ceph_compare_super(struct super_block *sb, void *data) +{ + struct ceph_client *new = data; + struct ceph_mount_args *args = &new->mount_args; + struct ceph_client *other = ceph_sb_to_client(sb); + int i; + + dout("ceph_compare_super %p\n", sb); + if (args->flags & CEPH_OPT_FSID) { + if (ceph_fsid_compare(&args->fsid, &other->fsid)) { + dout("fsid doesn't match\n"); + return 0; + } + } else { + /* do we share (a) monitor? */ + for (i = 0; i < new->monc.monmap->num_mon; i++) + if (ceph_monmap_contains(other->monc.monmap, + &new->monc.monmap->mon_inst[i].addr)) + break; + if (i == new->monc.monmap->num_mon) { + dout("mon ip not part of monmap\n"); + return 0; + } + dout("mon ip matches existing sb %p\n", sb); + } + if (args->sb_flags != other->mount_args.sb_flags) { + dout("flags differ\n"); + return 0; + } + return 1; +} + +/* + * construct our own bdi so we can control readahead, etc. + */ +static int ceph_init_bdi(struct super_block *sb, struct ceph_client *client) +{ + int err; + + err = bdi_init(&client->backing_dev_info); + if (err < 0) + return err; + + /* set ra_pages based on rsize mount option? */ + if (client->mount_args.rsize >= PAGE_CACHE_SIZE) + client->backing_dev_info.ra_pages = + (client->mount_args.rsize + PAGE_CACHE_SIZE - 1) + >> PAGE_SHIFT; + + err = bdi_register_dev(&client->backing_dev_info, sb->s_dev); + return err; +} + +static int ceph_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) +{ + struct super_block *sb; + struct ceph_client *client; + int err; + int (*compare_super)(struct super_block *, void *) = ceph_compare_super; + const char *path; + + dout("ceph_get_sb\n"); + + /* create client (which we may/may not use) */ + client = ceph_create_client(); + if (IS_ERR(client)) + return PTR_ERR(client); + + err = parse_mount_args(client, flags, data, dev_name, &path); + if (err < 0) + goto out; + + if (client->mount_args.flags & CEPH_OPT_NOSHARE) + compare_super = NULL; + sb = sget(fs_type, compare_super, ceph_set_super, client); + if (IS_ERR(sb)) { + err = PTR_ERR(sb); + goto out; + } + + if (ceph_client(sb) != client) { + ceph_destroy_client(client); + client = ceph_client(sb); + dout("get_sb got existing client %p\n", client); + } else { + dout("get_sb using new client %p\n", client); + + /* set up mempools */ + err = -ENOMEM; + client->wb_pagevec_pool = mempool_create_kmalloc_pool(10, + client->mount_args.wsize >> PAGE_CACHE_SHIFT); + if (!client->wb_pagevec_pool) + goto out_splat; + + err = ceph_init_bdi(sb, client); + if (err < 0) + goto out_splat; + } + + err = ceph_mount(client, mnt, path); + if (err < 0) + goto out_splat; + dout("root %p inode %p ino %llx.%llx\n", mnt->mnt_root, + mnt->mnt_root->d_inode, ceph_vinop(mnt->mnt_root->d_inode)); + return 0; + +out_splat: + ceph_mdsc_close_sessions(&client->mdsc); + up_write(&sb->s_umount); + deactivate_super(sb); + goto out_final; + +out: + ceph_destroy_client(client); +out_final: + dout("ceph_get_sb fail %d\n", err); + return err; +} + +static void ceph_kill_sb(struct super_block *s) +{ + struct ceph_client *client = ceph_sb_to_client(s); + dout("kill_sb %p\n", s); + ceph_mdsc_pre_umount(&client->mdsc); + bdi_unregister(&client->backing_dev_info); + kill_anon_super(s); /* will call put_super after sb is r/o */ + bdi_destroy(&client->backing_dev_info); + ceph_destroy_client(client); +} + +static struct file_system_type ceph_fs_type = { + .owner = THIS_MODULE, + .name = "ceph", + .get_sb = ceph_get_sb, + .kill_sb = ceph_kill_sb, + .fs_flags = FS_RENAME_DOES_D_MOVE, +}; + +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) + +static int __init init_ceph(void) +{ + int ret = 0; + + ret = ceph_debugfs_init(); + if (ret < 0) + goto out; + + ret = ceph_msgr_init(); + if (ret < 0) + goto out_debugfs; + + ret = init_caches(); + if (ret) + goto out_msgr; + + ceph_caps_init(); + + ret = register_filesystem(&ceph_fs_type); + if (ret) + goto out_icache; + + pr_info("loaded (%s)\n", STRINGIFY(CEPH_GIT_VER)); + return 0; + +out_icache: + destroy_caches(); +out_msgr: + ceph_msgr_exit(); +out_debugfs: + ceph_debugfs_cleanup(); +out: + return ret; +} + +static void __exit exit_ceph(void) +{ + dout("exit_ceph\n"); + unregister_filesystem(&ceph_fs_type); + ceph_caps_finalize(); + destroy_caches(); + ceph_msgr_exit(); + ceph_debugfs_cleanup(); +} + +module_init(init_ceph); +module_exit(exit_ceph); + +MODULE_AUTHOR("Sage Weil "); +MODULE_AUTHOR("Yehuda Sadeh "); +MODULE_AUTHOR("Patience Warnick "); +MODULE_DESCRIPTION("Ceph filesystem for Linux"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 355da1eb7a1f91c276b991764e951bbcd8047599 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:08 -0700 Subject: ceph: inode operations Inode cache and inode operations. We also include routines to incorporate metadata structures returned by the MDS into the client cache, and some helpers to deal with file capabilities and metadata leases. The bulk of that work is done by fill_inode() and fill_trace(). Signed-off-by: Sage Weil diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c new file mode 100644 index 0000000..6097af7 --- /dev/null +++ b/fs/ceph/inode.c @@ -0,0 +1,1620 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "super.h" +#include "decode.h" + +/* + * Ceph inode operations + * + * Implement basic inode helpers (get, alloc) and inode ops (getattr, + * setattr, etc.), xattr helpers, and helpers for assimilating + * metadata returned by the MDS into our cache. + * + * Also define helpers for doing asynchronous writeback, invalidation, + * and truncation for the benefit of those who can't afford to block + * (typically because they are in the message handler path). + */ + +static const struct inode_operations ceph_symlink_iops; + +static void ceph_inode_invalidate_pages(struct work_struct *work); + +/* + * find or create an inode, given the ceph ino number + */ +struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino) +{ + struct inode *inode; + ino_t t = ceph_vino_to_ino(vino); + + inode = iget5_locked(sb, t, ceph_ino_compare, ceph_set_ino_cb, &vino); + if (inode == NULL) + return ERR_PTR(-ENOMEM); + if (inode->i_state & I_NEW) { + dout("get_inode created new inode %p %llx.%llx ino %llx\n", + inode, ceph_vinop(inode), (u64)inode->i_ino); + unlock_new_inode(inode); + } + + dout("get_inode on %lu=%llx.%llx got %p\n", inode->i_ino, vino.ino, + vino.snap, inode); + return inode; +} + +/* + * get/constuct snapdir inode for a given directory + */ +struct inode *ceph_get_snapdir(struct inode *parent) +{ + struct ceph_vino vino = { + .ino = ceph_ino(parent), + .snap = CEPH_SNAPDIR, + }; + struct inode *inode = ceph_get_inode(parent->i_sb, vino); + + BUG_ON(!S_ISDIR(parent->i_mode)); + if (IS_ERR(inode)) + return ERR_PTR(PTR_ERR(inode)); + inode->i_mode = parent->i_mode; + inode->i_uid = parent->i_uid; + inode->i_gid = parent->i_gid; + inode->i_op = &ceph_dir_iops; + inode->i_fop = &ceph_dir_fops; + ceph_inode(inode)->i_snap_caps = CEPH_CAP_PIN; /* so we can open */ + return inode; +} + +const struct inode_operations ceph_file_iops = { + .permission = ceph_permission, + .setattr = ceph_setattr, + .getattr = ceph_getattr, + .setxattr = ceph_setxattr, + .getxattr = ceph_getxattr, + .listxattr = ceph_listxattr, + .removexattr = ceph_removexattr, +}; + + +/* + * We use a 'frag tree' to keep track of the MDS's directory fragments + * for a given inode (usually there is just a single fragment). We + * need to know when a child frag is delegated to a new MDS, or when + * it is flagged as replicated, so we can direct our requests + * accordingly. + */ + +/* + * find/create a frag in the tree + */ +static struct ceph_inode_frag *__get_or_create_frag(struct ceph_inode_info *ci, + u32 f) +{ + struct rb_node **p; + struct rb_node *parent = NULL; + struct ceph_inode_frag *frag; + int c; + + p = &ci->i_fragtree.rb_node; + while (*p) { + parent = *p; + frag = rb_entry(parent, struct ceph_inode_frag, node); + c = ceph_frag_compare(f, frag->frag); + if (c < 0) + p = &(*p)->rb_left; + else if (c > 0) + p = &(*p)->rb_right; + else + return frag; + } + + frag = kmalloc(sizeof(*frag), GFP_NOFS); + if (!frag) { + pr_err("__get_or_create_frag ENOMEM on %p %llx.%llx " + "frag %x\n", &ci->vfs_inode, + ceph_vinop(&ci->vfs_inode), f); + return ERR_PTR(-ENOMEM); + } + frag->frag = f; + frag->split_by = 0; + frag->mds = -1; + frag->ndist = 0; + + rb_link_node(&frag->node, parent, p); + rb_insert_color(&frag->node, &ci->i_fragtree); + + dout("get_or_create_frag added %llx.%llx frag %x\n", + ceph_vinop(&ci->vfs_inode), f); + return frag; +} + +/* + * find a specific frag @f + */ +struct ceph_inode_frag *__ceph_find_frag(struct ceph_inode_info *ci, u32 f) +{ + struct rb_node *n = ci->i_fragtree.rb_node; + + while (n) { + struct ceph_inode_frag *frag = + rb_entry(n, struct ceph_inode_frag, node); + int c = ceph_frag_compare(f, frag->frag); + if (c < 0) + n = n->rb_left; + else if (c > 0) + n = n->rb_right; + else + return frag; + } + return NULL; +} + +/* + * Choose frag containing the given value @v. If @pfrag is + * specified, copy the frag delegation info to the caller if + * it is present. + */ +u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v, + struct ceph_inode_frag *pfrag, + int *found) +{ + u32 t = ceph_frag_make(0, 0); + struct ceph_inode_frag *frag; + unsigned nway, i; + u32 n; + + if (found) + *found = 0; + + mutex_lock(&ci->i_fragtree_mutex); + while (1) { + WARN_ON(!ceph_frag_contains_value(t, v)); + frag = __ceph_find_frag(ci, t); + if (!frag) + break; /* t is a leaf */ + if (frag->split_by == 0) { + if (pfrag) + memcpy(pfrag, frag, sizeof(*pfrag)); + if (found) + *found = 1; + break; + } + + /* choose child */ + nway = 1 << frag->split_by; + dout("choose_frag(%x) %x splits by %d (%d ways)\n", v, t, + frag->split_by, nway); + for (i = 0; i < nway; i++) { + n = ceph_frag_make_child(t, frag->split_by, i); + if (ceph_frag_contains_value(n, v)) { + t = n; + break; + } + } + BUG_ON(i == nway); + } + dout("choose_frag(%x) = %x\n", v, t); + + mutex_unlock(&ci->i_fragtree_mutex); + return t; +} + +/* + * Process dirfrag (delegation) info from the mds. Include leaf + * fragment in tree ONLY if ndist > 0. Otherwise, only + * branches/splits are included in i_fragtree) + */ +static int ceph_fill_dirfrag(struct inode *inode, + struct ceph_mds_reply_dirfrag *dirinfo) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_inode_frag *frag; + u32 id = le32_to_cpu(dirinfo->frag); + int mds = le32_to_cpu(dirinfo->auth); + int ndist = le32_to_cpu(dirinfo->ndist); + int i; + int err = 0; + + mutex_lock(&ci->i_fragtree_mutex); + if (ndist == 0) { + /* no delegation info needed. */ + frag = __ceph_find_frag(ci, id); + if (!frag) + goto out; + if (frag->split_by == 0) { + /* tree leaf, remove */ + dout("fill_dirfrag removed %llx.%llx frag %x" + " (no ref)\n", ceph_vinop(inode), id); + rb_erase(&frag->node, &ci->i_fragtree); + kfree(frag); + } else { + /* tree branch, keep and clear */ + dout("fill_dirfrag cleared %llx.%llx frag %x" + " referral\n", ceph_vinop(inode), id); + frag->mds = -1; + frag->ndist = 0; + } + goto out; + } + + + /* find/add this frag to store mds delegation info */ + frag = __get_or_create_frag(ci, id); + if (IS_ERR(frag)) { + /* this is not the end of the world; we can continue + with bad/inaccurate delegation info */ + pr_err("fill_dirfrag ENOMEM on mds ref %llx.%llx fg %x\n", + ceph_vinop(inode), le32_to_cpu(dirinfo->frag)); + err = -ENOMEM; + goto out; + } + + frag->mds = mds; + frag->ndist = min_t(u32, ndist, CEPH_MAX_DIRFRAG_REP); + for (i = 0; i < frag->ndist; i++) + frag->dist[i] = le32_to_cpu(dirinfo->dist[i]); + dout("fill_dirfrag %llx.%llx frag %x ndist=%d\n", + ceph_vinop(inode), frag->frag, frag->ndist); + +out: + mutex_unlock(&ci->i_fragtree_mutex); + return err; +} + + +/* + * initialize a newly allocated inode. + */ +struct inode *ceph_alloc_inode(struct super_block *sb) +{ + struct ceph_inode_info *ci; + int i; + + ci = kmem_cache_alloc(ceph_inode_cachep, GFP_NOFS); + if (!ci) + return NULL; + + dout("alloc_inode %p\n", &ci->vfs_inode); + + ci->i_version = 0; + ci->i_time_warp_seq = 0; + ci->i_ceph_flags = 0; + ci->i_release_count = 0; + ci->i_symlink = NULL; + + ci->i_fragtree = RB_ROOT; + mutex_init(&ci->i_fragtree_mutex); + + ci->i_xattrs.blob = NULL; + ci->i_xattrs.prealloc_blob = NULL; + ci->i_xattrs.dirty = false; + ci->i_xattrs.index = RB_ROOT; + ci->i_xattrs.count = 0; + ci->i_xattrs.names_size = 0; + ci->i_xattrs.vals_size = 0; + ci->i_xattrs.version = 0; + ci->i_xattrs.index_version = 0; + + ci->i_caps = RB_ROOT; + ci->i_auth_cap = NULL; + ci->i_dirty_caps = 0; + ci->i_flushing_caps = 0; + INIT_LIST_HEAD(&ci->i_dirty_item); + INIT_LIST_HEAD(&ci->i_flushing_item); + ci->i_cap_flush_seq = 0; + ci->i_cap_flush_last_tid = 0; + memset(&ci->i_cap_flush_tid, 0, sizeof(ci->i_cap_flush_tid)); + init_waitqueue_head(&ci->i_cap_wq); + ci->i_hold_caps_min = 0; + ci->i_hold_caps_max = 0; + INIT_LIST_HEAD(&ci->i_cap_delay_list); + ci->i_cap_exporting_mds = 0; + ci->i_cap_exporting_mseq = 0; + ci->i_cap_exporting_issued = 0; + INIT_LIST_HEAD(&ci->i_cap_snaps); + ci->i_head_snapc = NULL; + ci->i_snap_caps = 0; + + for (i = 0; i < CEPH_FILE_MODE_NUM; i++) + ci->i_nr_by_mode[i] = 0; + + ci->i_truncate_seq = 0; + ci->i_truncate_size = 0; + ci->i_truncate_pending = 0; + + ci->i_max_size = 0; + ci->i_reported_size = 0; + ci->i_wanted_max_size = 0; + ci->i_requested_max_size = 0; + + ci->i_pin_ref = 0; + ci->i_rd_ref = 0; + ci->i_rdcache_ref = 0; + ci->i_wr_ref = 0; + ci->i_wrbuffer_ref = 0; + ci->i_wrbuffer_ref_head = 0; + ci->i_shared_gen = 0; + ci->i_rdcache_gen = 0; + ci->i_rdcache_revoking = 0; + + INIT_LIST_HEAD(&ci->i_unsafe_writes); + INIT_LIST_HEAD(&ci->i_unsafe_dirops); + spin_lock_init(&ci->i_unsafe_lock); + + ci->i_snap_realm = NULL; + INIT_LIST_HEAD(&ci->i_snap_realm_item); + INIT_LIST_HEAD(&ci->i_snap_flush_item); + + INIT_WORK(&ci->i_wb_work, ceph_inode_writeback); + INIT_WORK(&ci->i_pg_inv_work, ceph_inode_invalidate_pages); + + INIT_WORK(&ci->i_vmtruncate_work, ceph_vmtruncate_work); + + return &ci->vfs_inode; +} + +void ceph_destroy_inode(struct inode *inode) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_inode_frag *frag; + struct rb_node *n; + + dout("destroy_inode %p ino %llx.%llx\n", inode, ceph_vinop(inode)); + + ceph_queue_caps_release(inode); + + kfree(ci->i_symlink); + while ((n = rb_first(&ci->i_fragtree)) != NULL) { + frag = rb_entry(n, struct ceph_inode_frag, node); + rb_erase(n, &ci->i_fragtree); + kfree(frag); + } + + __ceph_destroy_xattrs(ci); + ceph_buffer_put(ci->i_xattrs.blob); + ceph_buffer_put(ci->i_xattrs.prealloc_blob); + + kmem_cache_free(ceph_inode_cachep, ci); +} + + +/* + * Helpers to fill in size, ctime, mtime, and atime. We have to be + * careful because either the client or MDS may have more up to date + * info, depending on which capabilities are held, and whether + * time_warp_seq or truncate_seq have increased. (Ordinarily, mtime + * and size are monotonically increasing, except when utimes() or + * truncate() increments the corresponding _seq values.) + */ +int ceph_fill_file_size(struct inode *inode, int issued, + u32 truncate_seq, u64 truncate_size, u64 size) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int queue_trunc = 0; + + if (ceph_seq_cmp(truncate_seq, ci->i_truncate_seq) > 0 || + (truncate_seq == ci->i_truncate_seq && size > inode->i_size)) { + dout("size %lld -> %llu\n", inode->i_size, size); + inode->i_size = size; + inode->i_blocks = (size + (1<<9) - 1) >> 9; + ci->i_reported_size = size; + if (truncate_seq != ci->i_truncate_seq) { + dout("truncate_seq %u -> %u\n", + ci->i_truncate_seq, truncate_seq); + ci->i_truncate_seq = truncate_seq; + if (issued & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_RD| + CEPH_CAP_FILE_WR|CEPH_CAP_FILE_BUFFER| + CEPH_CAP_FILE_EXCL)) { + ci->i_truncate_pending++; + queue_trunc = 1; + } + } + } + if (ceph_seq_cmp(truncate_seq, ci->i_truncate_seq) >= 0 && + ci->i_truncate_size != truncate_size) { + dout("truncate_size %lld -> %llu\n", ci->i_truncate_size, + truncate_size); + ci->i_truncate_size = truncate_size; + } + return queue_trunc; +} + +void ceph_fill_file_time(struct inode *inode, int issued, + u64 time_warp_seq, struct timespec *ctime, + struct timespec *mtime, struct timespec *atime) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int warn = 0; + + if (issued & (CEPH_CAP_FILE_EXCL| + CEPH_CAP_FILE_WR| + CEPH_CAP_FILE_BUFFER)) { + if (timespec_compare(ctime, &inode->i_ctime) > 0) { + dout("ctime %ld.%09ld -> %ld.%09ld inc w/ cap\n", + inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec, + ctime->tv_sec, ctime->tv_nsec); + inode->i_ctime = *ctime; + } + if (ceph_seq_cmp(time_warp_seq, ci->i_time_warp_seq) > 0) { + /* the MDS did a utimes() */ + dout("mtime %ld.%09ld -> %ld.%09ld " + "tw %d -> %d\n", + inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec, + mtime->tv_sec, mtime->tv_nsec, + ci->i_time_warp_seq, (int)time_warp_seq); + + inode->i_mtime = *mtime; + inode->i_atime = *atime; + ci->i_time_warp_seq = time_warp_seq; + } else if (time_warp_seq == ci->i_time_warp_seq) { + /* nobody did utimes(); take the max */ + if (timespec_compare(mtime, &inode->i_mtime) > 0) { + dout("mtime %ld.%09ld -> %ld.%09ld inc\n", + inode->i_mtime.tv_sec, + inode->i_mtime.tv_nsec, + mtime->tv_sec, mtime->tv_nsec); + inode->i_mtime = *mtime; + } + if (timespec_compare(atime, &inode->i_atime) > 0) { + dout("atime %ld.%09ld -> %ld.%09ld inc\n", + inode->i_atime.tv_sec, + inode->i_atime.tv_nsec, + atime->tv_sec, atime->tv_nsec); + inode->i_atime = *atime; + } + } else if (issued & CEPH_CAP_FILE_EXCL) { + /* we did a utimes(); ignore mds values */ + } else { + warn = 1; + } + } else { + /* we have no write caps; whatever the MDS says is true */ + if (ceph_seq_cmp(time_warp_seq, ci->i_time_warp_seq) >= 0) { + inode->i_ctime = *ctime; + inode->i_mtime = *mtime; + inode->i_atime = *atime; + ci->i_time_warp_seq = time_warp_seq; + } else { + warn = 1; + } + } + if (warn) /* time_warp_seq shouldn't go backwards */ + dout("%p mds time_warp_seq %llu < %u\n", + inode, time_warp_seq, ci->i_time_warp_seq); +} + +/* + * Populate an inode based on info from mds. May be called on new or + * existing inodes. + */ +static int fill_inode(struct inode *inode, + struct ceph_mds_reply_info_in *iinfo, + struct ceph_mds_reply_dirfrag *dirinfo, + struct ceph_mds_session *session, + unsigned long ttl_from, int cap_fmode, + struct ceph_cap_reservation *caps_reservation) +{ + struct ceph_mds_reply_inode *info = iinfo->in; + struct ceph_inode_info *ci = ceph_inode(inode); + int i; + int issued, implemented; + struct timespec mtime, atime, ctime; + u32 nsplits; + struct ceph_buffer *xattr_blob = NULL; + int err = 0; + int queue_trunc = 0; + + dout("fill_inode %p ino %llx.%llx v %llu had %llu\n", + inode, ceph_vinop(inode), le64_to_cpu(info->version), + ci->i_version); + + /* + * prealloc xattr data, if it looks like we'll need it. only + * if len > 4 (meaning there are actually xattrs; the first 4 + * bytes are the xattr count). + */ + if (iinfo->xattr_len > 4) { + xattr_blob = ceph_buffer_new_alloc(iinfo->xattr_len, GFP_NOFS); + if (!xattr_blob) + pr_err("fill_inode ENOMEM xattr blob %d bytes\n", + iinfo->xattr_len); + } + + spin_lock(&inode->i_lock); + + /* + * provided version will be odd if inode value is projected, + * even if stable. skip the update if we have a newer info + * (e.g., due to inode info racing form multiple MDSs), or if + * we are getting projected (unstable) inode info. + */ + if (le64_to_cpu(info->version) > 0 && + (ci->i_version & ~1) > le64_to_cpu(info->version)) + goto no_change; + + issued = __ceph_caps_issued(ci, &implemented); + issued |= implemented | __ceph_caps_dirty(ci); + + /* update inode */ + ci->i_version = le64_to_cpu(info->version); + inode->i_version++; + inode->i_rdev = le32_to_cpu(info->rdev); + + if ((issued & CEPH_CAP_AUTH_EXCL) == 0) { + inode->i_mode = le32_to_cpu(info->mode); + inode->i_uid = le32_to_cpu(info->uid); + inode->i_gid = le32_to_cpu(info->gid); + dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, + inode->i_uid, inode->i_gid); + } + + if ((issued & CEPH_CAP_LINK_EXCL) == 0) + inode->i_nlink = le32_to_cpu(info->nlink); + + /* be careful with mtime, atime, size */ + ceph_decode_timespec(&atime, &info->atime); + ceph_decode_timespec(&mtime, &info->mtime); + ceph_decode_timespec(&ctime, &info->ctime); + queue_trunc = ceph_fill_file_size(inode, issued, + le32_to_cpu(info->truncate_seq), + le64_to_cpu(info->truncate_size), + S_ISDIR(inode->i_mode) ? + ci->i_rbytes : + le64_to_cpu(info->size)); + ceph_fill_file_time(inode, issued, + le32_to_cpu(info->time_warp_seq), + &ctime, &mtime, &atime); + + ci->i_max_size = le64_to_cpu(info->max_size); + ci->i_layout = info->layout; + inode->i_blkbits = fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1; + + /* xattrs */ + /* note that if i_xattrs.len <= 4, i_xattrs.data will still be NULL. */ + if ((issued & CEPH_CAP_XATTR_EXCL) == 0 && + le64_to_cpu(info->xattr_version) > ci->i_xattrs.version) { + if (ci->i_xattrs.blob) + ceph_buffer_put(ci->i_xattrs.blob); + ci->i_xattrs.blob = xattr_blob; + if (xattr_blob) + memcpy(ci->i_xattrs.blob->vec.iov_base, + iinfo->xattr_data, iinfo->xattr_len); + ci->i_xattrs.version = le64_to_cpu(info->xattr_version); + } + + inode->i_mapping->a_ops = &ceph_aops; + inode->i_mapping->backing_dev_info = + &ceph_client(inode->i_sb)->backing_dev_info; + + switch (inode->i_mode & S_IFMT) { + case S_IFIFO: + case S_IFBLK: + case S_IFCHR: + case S_IFSOCK: + init_special_inode(inode, inode->i_mode, inode->i_rdev); + inode->i_op = &ceph_file_iops; + break; + case S_IFREG: + inode->i_op = &ceph_file_iops; + inode->i_fop = &ceph_file_fops; + break; + case S_IFLNK: + inode->i_op = &ceph_symlink_iops; + if (!ci->i_symlink) { + int symlen = iinfo->symlink_len; + char *sym; + + BUG_ON(symlen != inode->i_size); + spin_unlock(&inode->i_lock); + + err = -ENOMEM; + sym = kmalloc(symlen+1, GFP_NOFS); + if (!sym) + goto out; + memcpy(sym, iinfo->symlink, symlen); + sym[symlen] = 0; + + spin_lock(&inode->i_lock); + if (!ci->i_symlink) + ci->i_symlink = sym; + else + kfree(sym); /* lost a race */ + } + break; + case S_IFDIR: + inode->i_op = &ceph_dir_iops; + inode->i_fop = &ceph_dir_fops; + + ci->i_files = le64_to_cpu(info->files); + ci->i_subdirs = le64_to_cpu(info->subdirs); + ci->i_rbytes = le64_to_cpu(info->rbytes); + ci->i_rfiles = le64_to_cpu(info->rfiles); + ci->i_rsubdirs = le64_to_cpu(info->rsubdirs); + ceph_decode_timespec(&ci->i_rctime, &info->rctime); + + /* set dir completion flag? */ + if (ci->i_files == 0 && ci->i_subdirs == 0 && + ceph_snap(inode) == CEPH_NOSNAP && + (le32_to_cpu(info->cap.caps) & CEPH_CAP_FILE_SHARED)) { + dout(" marking %p complete (empty)\n", inode); + ci->i_ceph_flags |= CEPH_I_COMPLETE; + ci->i_max_offset = 2; + } + + /* it may be better to set st_size in getattr instead? */ + if (ceph_test_opt(ceph_client(inode->i_sb), RBYTES)) + inode->i_size = ci->i_rbytes; + break; + default: + pr_err("fill_inode %llx.%llx BAD mode 0%o\n", + ceph_vinop(inode), inode->i_mode); + } + +no_change: + spin_unlock(&inode->i_lock); + + /* queue truncate if we saw i_size decrease */ + if (queue_trunc) + if (queue_work(ceph_client(inode->i_sb)->trunc_wq, + &ci->i_vmtruncate_work)) + igrab(inode); + + /* populate frag tree */ + /* FIXME: move me up, if/when version reflects fragtree changes */ + nsplits = le32_to_cpu(info->fragtree.nsplits); + mutex_lock(&ci->i_fragtree_mutex); + for (i = 0; i < nsplits; i++) { + u32 id = le32_to_cpu(info->fragtree.splits[i].frag); + struct ceph_inode_frag *frag = __get_or_create_frag(ci, id); + + if (IS_ERR(frag)) + continue; + frag->split_by = le32_to_cpu(info->fragtree.splits[i].by); + dout(" frag %x split by %d\n", frag->frag, frag->split_by); + } + mutex_unlock(&ci->i_fragtree_mutex); + + /* were we issued a capability? */ + if (info->cap.caps) { + if (ceph_snap(inode) == CEPH_NOSNAP) { + ceph_add_cap(inode, session, + le64_to_cpu(info->cap.cap_id), + cap_fmode, + le32_to_cpu(info->cap.caps), + le32_to_cpu(info->cap.wanted), + le32_to_cpu(info->cap.seq), + le32_to_cpu(info->cap.mseq), + le64_to_cpu(info->cap.realm), + info->cap.flags, + caps_reservation); + } else { + spin_lock(&inode->i_lock); + dout(" %p got snap_caps %s\n", inode, + ceph_cap_string(le32_to_cpu(info->cap.caps))); + ci->i_snap_caps |= le32_to_cpu(info->cap.caps); + if (cap_fmode >= 0) + __ceph_get_fmode(ci, cap_fmode); + spin_unlock(&inode->i_lock); + } + } + + /* update delegation info? */ + if (dirinfo) + ceph_fill_dirfrag(inode, dirinfo); + + err = 0; + +out: + ceph_buffer_put(xattr_blob); + return err; +} + +/* + * caller should hold session s_mutex. + */ +static void update_dentry_lease(struct dentry *dentry, + struct ceph_mds_reply_lease *lease, + struct ceph_mds_session *session, + unsigned long from_time) +{ + struct ceph_dentry_info *di = ceph_dentry(dentry); + long unsigned duration = le32_to_cpu(lease->duration_ms); + long unsigned ttl = from_time + (duration * HZ) / 1000; + long unsigned half_ttl = from_time + (duration * HZ / 2) / 1000; + struct inode *dir; + + /* only track leases on regular dentries */ + if (dentry->d_op != &ceph_dentry_ops) + return; + + spin_lock(&dentry->d_lock); + dout("update_dentry_lease %p mask %d duration %lu ms ttl %lu\n", + dentry, le16_to_cpu(lease->mask), duration, ttl); + + /* make lease_rdcache_gen match directory */ + dir = dentry->d_parent->d_inode; + di->lease_shared_gen = ceph_inode(dir)->i_shared_gen; + + if (lease->mask == 0) + goto out_unlock; + + if (di->lease_gen == session->s_cap_gen && + time_before(ttl, dentry->d_time)) + goto out_unlock; /* we already have a newer lease. */ + + if (di->lease_session && di->lease_session != session) + goto out_unlock; + + ceph_dentry_lru_touch(dentry); + + if (!di->lease_session) + di->lease_session = ceph_get_mds_session(session); + di->lease_gen = session->s_cap_gen; + di->lease_seq = le32_to_cpu(lease->seq); + di->lease_renew_after = half_ttl; + di->lease_renew_from = 0; + dentry->d_time = ttl; +out_unlock: + spin_unlock(&dentry->d_lock); + return; +} + +/* + * splice a dentry to an inode. + * caller must hold directory i_mutex for this to be safe. + * + * we will only rehash the resulting dentry if @prehash is + * true; @prehash will be set to false (for the benefit of + * the caller) if we fail. + */ +static struct dentry *splice_dentry(struct dentry *dn, struct inode *in, + bool *prehash) +{ + struct dentry *realdn; + + /* dn must be unhashed */ + if (!d_unhashed(dn)) + d_drop(dn); + realdn = d_materialise_unique(dn, in); + if (IS_ERR(realdn)) { + pr_err("splice_dentry error %p inode %p ino %llx.%llx\n", + dn, in, ceph_vinop(in)); + if (prehash) + *prehash = false; /* don't rehash on error */ + dn = realdn; /* note realdn contains the error */ + goto out; + } else if (realdn) { + dout("dn %p (%d) spliced with %p (%d) " + "inode %p ino %llx.%llx\n", + dn, atomic_read(&dn->d_count), + realdn, atomic_read(&realdn->d_count), + realdn->d_inode, ceph_vinop(realdn->d_inode)); + dput(dn); + dn = realdn; + } else { + BUG_ON(!ceph_dentry(dn)); + + dout("dn %p attached to %p ino %llx.%llx\n", + dn, dn->d_inode, ceph_vinop(dn->d_inode)); + } + if ((!prehash || *prehash) && d_unhashed(dn)) + d_rehash(dn); +out: + return dn; +} + +/* + * Incorporate results into the local cache. This is either just + * one inode, or a directory, dentry, and possibly linked-to inode (e.g., + * after a lookup). + * + * A reply may contain + * a directory inode along with a dentry. + * and/or a target inode + * + * Called with snap_rwsem (read). + */ +int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, + struct ceph_mds_session *session) +{ + struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; + struct inode *in = NULL; + struct ceph_mds_reply_inode *ininfo; + struct ceph_vino vino; + int i = 0; + int err = 0; + + dout("fill_trace %p is_dentry %d is_target %d\n", req, + rinfo->head->is_dentry, rinfo->head->is_target); + +#if 0 + /* + * Debugging hook: + * + * If we resend completed ops to a recovering mds, we get no + * trace. Since that is very rare, pretend this is the case + * to ensure the 'no trace' handlers in the callers behave. + * + * Fill in inodes unconditionally to avoid breaking cap + * invariants. + */ + if (rinfo->head->op & CEPH_MDS_OP_WRITE) { + pr_info("fill_trace faking empty trace on %lld %s\n", + req->r_tid, ceph_mds_op_name(rinfo->head->op)); + if (rinfo->head->is_dentry) { + rinfo->head->is_dentry = 0; + err = fill_inode(req->r_locked_dir, + &rinfo->diri, rinfo->dirfrag, + session, req->r_request_started, -1); + } + if (rinfo->head->is_target) { + rinfo->head->is_target = 0; + ininfo = rinfo->targeti.in; + vino.ino = le64_to_cpu(ininfo->ino); + vino.snap = le64_to_cpu(ininfo->snapid); + in = ceph_get_inode(sb, vino); + err = fill_inode(in, &rinfo->targeti, NULL, + session, req->r_request_started, + req->r_fmode); + iput(in); + } + } +#endif + + if (!rinfo->head->is_target && !rinfo->head->is_dentry) { + dout("fill_trace reply is empty!\n"); + if (rinfo->head->result == 0 && req->r_locked_dir) { + struct ceph_inode_info *ci = + ceph_inode(req->r_locked_dir); + dout(" clearing %p complete (empty trace)\n", + req->r_locked_dir); + ci->i_ceph_flags &= ~CEPH_I_COMPLETE; + ci->i_release_count++; + } + return 0; + } + + if (rinfo->head->is_dentry) { + /* + * lookup link rename : null -> possibly existing inode + * mknod symlink mkdir : null -> new inode + * unlink : linked -> null + */ + struct inode *dir = req->r_locked_dir; + struct dentry *dn = req->r_dentry; + bool have_dir_cap, have_lease; + + BUG_ON(!dn); + BUG_ON(!dir); + BUG_ON(dn->d_parent->d_inode != dir); + BUG_ON(ceph_ino(dir) != + le64_to_cpu(rinfo->diri.in->ino)); + BUG_ON(ceph_snap(dir) != + le64_to_cpu(rinfo->diri.in->snapid)); + + err = fill_inode(dir, &rinfo->diri, rinfo->dirfrag, + session, req->r_request_started, -1, + &req->r_caps_reservation); + if (err < 0) + return err; + + /* do we have a lease on the whole dir? */ + have_dir_cap = + (le32_to_cpu(rinfo->diri.in->cap.caps) & + CEPH_CAP_FILE_SHARED); + + /* do we have a dn lease? */ + have_lease = have_dir_cap || + (le16_to_cpu(rinfo->dlease->mask) & + CEPH_LOCK_DN); + + if (!have_lease) + dout("fill_trace no dentry lease or dir cap\n"); + + /* rename? */ + if (req->r_old_dentry && req->r_op == CEPH_MDS_OP_RENAME) { + dout(" src %p '%.*s' dst %p '%.*s'\n", + req->r_old_dentry, + req->r_old_dentry->d_name.len, + req->r_old_dentry->d_name.name, + dn, dn->d_name.len, dn->d_name.name); + dout("fill_trace doing d_move %p -> %p\n", + req->r_old_dentry, dn); + d_move(req->r_old_dentry, dn); + dout(" src %p '%.*s' dst %p '%.*s'\n", + req->r_old_dentry, + req->r_old_dentry->d_name.len, + req->r_old_dentry->d_name.name, + dn, dn->d_name.len, dn->d_name.name); + /* take overwritten dentry's readdir offset */ + ceph_dentry(req->r_old_dentry)->offset = + ceph_dentry(dn)->offset; + dn = req->r_old_dentry; /* use old_dentry */ + in = dn->d_inode; + } + + /* null dentry? */ + if (!rinfo->head->is_target) { + dout("fill_trace null dentry\n"); + if (dn->d_inode) { + dout("d_delete %p\n", dn); + d_delete(dn); + } else { + dout("d_instantiate %p NULL\n", dn); + d_instantiate(dn, NULL); + if (have_lease && d_unhashed(dn)) + d_rehash(dn); + update_dentry_lease(dn, rinfo->dlease, + session, + req->r_request_started); + } + goto done; + } + + /* attach proper inode */ + ininfo = rinfo->targeti.in; + vino.ino = le64_to_cpu(ininfo->ino); + vino.snap = le64_to_cpu(ininfo->snapid); + if (!dn->d_inode) { + in = ceph_get_inode(sb, vino); + if (IS_ERR(in)) { + pr_err("fill_trace bad get_inode " + "%llx.%llx\n", vino.ino, vino.snap); + err = PTR_ERR(in); + d_delete(dn); + goto done; + } + dn = splice_dentry(dn, in, &have_lease); + if (IS_ERR(dn)) { + err = PTR_ERR(dn); + goto done; + } + req->r_dentry = dn; /* may have spliced */ + igrab(in); + } else if (ceph_ino(in) == vino.ino && + ceph_snap(in) == vino.snap) { + igrab(in); + } else { + dout(" %p links to %p %llx.%llx, not %llx.%llx\n", + dn, in, ceph_ino(in), ceph_snap(in), + vino.ino, vino.snap); + have_lease = false; + in = NULL; + } + + if (have_lease) + update_dentry_lease(dn, rinfo->dlease, session, + req->r_request_started); + dout(" final dn %p\n", dn); + i++; + } else if (req->r_op == CEPH_MDS_OP_LOOKUPSNAP || + req->r_op == CEPH_MDS_OP_MKSNAP) { + struct dentry *dn = req->r_dentry; + + /* fill out a snapdir LOOKUPSNAP dentry */ + BUG_ON(!dn); + BUG_ON(!req->r_locked_dir); + BUG_ON(ceph_snap(req->r_locked_dir) != CEPH_SNAPDIR); + ininfo = rinfo->targeti.in; + vino.ino = le64_to_cpu(ininfo->ino); + vino.snap = le64_to_cpu(ininfo->snapid); + in = ceph_get_inode(sb, vino); + if (IS_ERR(in)) { + pr_err("fill_inode get_inode badness %llx.%llx\n", + vino.ino, vino.snap); + err = PTR_ERR(in); + d_delete(dn); + goto done; + } + dout(" linking snapped dir %p to dn %p\n", in, dn); + dn = splice_dentry(dn, in, NULL); + if (IS_ERR(dn)) { + err = PTR_ERR(dn); + goto done; + } + req->r_dentry = dn; /* may have spliced */ + igrab(in); + rinfo->head->is_dentry = 1; /* fool notrace handlers */ + } + + if (rinfo->head->is_target) { + vino.ino = le64_to_cpu(rinfo->targeti.in->ino); + vino.snap = le64_to_cpu(rinfo->targeti.in->snapid); + + if (in == NULL || ceph_ino(in) != vino.ino || + ceph_snap(in) != vino.snap) { + in = ceph_get_inode(sb, vino); + if (IS_ERR(in)) { + err = PTR_ERR(in); + goto done; + } + } + req->r_target_inode = in; + + err = fill_inode(in, + &rinfo->targeti, NULL, + session, req->r_request_started, + (le32_to_cpu(rinfo->head->result) == 0) ? + req->r_fmode : -1, + &req->r_caps_reservation); + if (err < 0) { + pr_err("fill_inode badness %p %llx.%llx\n", + in, ceph_vinop(in)); + goto done; + } + } + +done: + dout("fill_trace done err=%d\n", err); + return err; +} + +/* + * Prepopulate our cache with readdir results, leases, etc. + */ +int ceph_readdir_prepopulate(struct ceph_mds_request *req, + struct ceph_mds_session *session) +{ + struct dentry *parent = req->r_dentry; + struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; + struct qstr dname; + struct dentry *dn; + struct inode *in; + int err = 0, i; + struct inode *snapdir = NULL; + struct ceph_mds_request_head *rhead = req->r_request->front.iov_base; + u64 frag = le32_to_cpu(rhead->args.readdir.frag); + struct ceph_dentry_info *di; + + if (le32_to_cpu(rinfo->head->op) == CEPH_MDS_OP_LSSNAP) { + snapdir = ceph_get_snapdir(parent->d_inode); + parent = d_find_alias(snapdir); + dout("readdir_prepopulate %d items under SNAPDIR dn %p\n", + rinfo->dir_nr, parent); + } else { + dout("readdir_prepopulate %d items under dn %p\n", + rinfo->dir_nr, parent); + if (rinfo->dir_dir) + ceph_fill_dirfrag(parent->d_inode, rinfo->dir_dir); + } + + for (i = 0; i < rinfo->dir_nr; i++) { + struct ceph_vino vino; + + dname.name = rinfo->dir_dname[i]; + dname.len = rinfo->dir_dname_len[i]; + dname.hash = full_name_hash(dname.name, dname.len); + + vino.ino = le64_to_cpu(rinfo->dir_in[i].in->ino); + vino.snap = le64_to_cpu(rinfo->dir_in[i].in->snapid); + +retry_lookup: + dn = d_lookup(parent, &dname); + dout("d_lookup on parent=%p name=%.*s got %p\n", + parent, dname.len, dname.name, dn); + + if (!dn) { + dn = d_alloc(parent, &dname); + dout("d_alloc %p '%.*s' = %p\n", parent, + dname.len, dname.name, dn); + if (dn == NULL) { + dout("d_alloc badness\n"); + err = -ENOMEM; + goto out; + } + err = ceph_init_dentry(dn); + if (err < 0) + goto out; + } else if (dn->d_inode && + (ceph_ino(dn->d_inode) != vino.ino || + ceph_snap(dn->d_inode) != vino.snap)) { + dout(" dn %p points to wrong inode %p\n", + dn, dn->d_inode); + d_delete(dn); + dput(dn); + goto retry_lookup; + } else { + /* reorder parent's d_subdirs */ + spin_lock(&dcache_lock); + spin_lock(&dn->d_lock); + list_move(&dn->d_u.d_child, &parent->d_subdirs); + spin_unlock(&dn->d_lock); + spin_unlock(&dcache_lock); + } + + di = dn->d_fsdata; + di->offset = ceph_make_fpos(frag, i + req->r_readdir_offset); + + /* inode */ + if (dn->d_inode) { + in = dn->d_inode; + } else { + in = ceph_get_inode(parent->d_sb, vino); + if (in == NULL) { + dout("new_inode badness\n"); + d_delete(dn); + dput(dn); + err = -ENOMEM; + goto out; + } + dn = splice_dentry(dn, in, NULL); + } + + if (fill_inode(in, &rinfo->dir_in[i], NULL, session, + req->r_request_started, -1, + &req->r_caps_reservation) < 0) { + pr_err("fill_inode badness on %p\n", in); + dput(dn); + continue; + } + update_dentry_lease(dn, rinfo->dir_dlease[i], + req->r_session, req->r_request_started); + dput(dn); + } + req->r_did_prepopulate = true; + +out: + if (snapdir) { + iput(snapdir); + dput(parent); + } + dout("readdir_prepopulate done\n"); + return err; +} + +int ceph_inode_set_size(struct inode *inode, loff_t size) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int ret = 0; + + spin_lock(&inode->i_lock); + dout("set_size %p %llu -> %llu\n", inode, inode->i_size, size); + inode->i_size = size; + inode->i_blocks = (size + (1 << 9) - 1) >> 9; + + /* tell the MDS if we are approaching max_size */ + if ((size << 1) >= ci->i_max_size && + (ci->i_reported_size << 1) < ci->i_max_size) + ret = 1; + + spin_unlock(&inode->i_lock); + return ret; +} + +/* + * Write back inode data in a worker thread. (This can't be done + * in the message handler context.) + */ +void ceph_inode_writeback(struct work_struct *work) +{ + struct ceph_inode_info *ci = container_of(work, struct ceph_inode_info, + i_wb_work); + struct inode *inode = &ci->vfs_inode; + + dout("writeback %p\n", inode); + filemap_fdatawrite(&inode->i_data); + iput(inode); +} + +/* + * Invalidate inode pages in a worker thread. (This can't be done + * in the message handler context.) + */ +static void ceph_inode_invalidate_pages(struct work_struct *work) +{ + struct ceph_inode_info *ci = container_of(work, struct ceph_inode_info, + i_pg_inv_work); + struct inode *inode = &ci->vfs_inode; + u32 orig_gen; + int check = 0; + + spin_lock(&inode->i_lock); + dout("invalidate_pages %p gen %d revoking %d\n", inode, + ci->i_rdcache_gen, ci->i_rdcache_revoking); + if (ci->i_rdcache_gen == 0 || + ci->i_rdcache_revoking != ci->i_rdcache_gen) { + BUG_ON(ci->i_rdcache_revoking > ci->i_rdcache_gen); + /* nevermind! */ + ci->i_rdcache_revoking = 0; + spin_unlock(&inode->i_lock); + goto out; + } + orig_gen = ci->i_rdcache_gen; + spin_unlock(&inode->i_lock); + + truncate_inode_pages(&inode->i_data, 0); + + spin_lock(&inode->i_lock); + if (orig_gen == ci->i_rdcache_gen) { + dout("invalidate_pages %p gen %d successful\n", inode, + ci->i_rdcache_gen); + ci->i_rdcache_gen = 0; + ci->i_rdcache_revoking = 0; + check = 1; + } else { + dout("invalidate_pages %p gen %d raced, gen now %d\n", + inode, orig_gen, ci->i_rdcache_gen); + } + spin_unlock(&inode->i_lock); + + if (check) + ceph_check_caps(ci, 0, NULL); +out: + iput(inode); +} + + +/* + * called by trunc_wq; take i_mutex ourselves + * + * We also truncate in a separate thread as well. + */ +void ceph_vmtruncate_work(struct work_struct *work) +{ + struct ceph_inode_info *ci = container_of(work, struct ceph_inode_info, + i_vmtruncate_work); + struct inode *inode = &ci->vfs_inode; + + dout("vmtruncate_work %p\n", inode); + mutex_lock(&inode->i_mutex); + __ceph_do_pending_vmtruncate(inode); + mutex_unlock(&inode->i_mutex); + iput(inode); +} + +/* + * called with i_mutex held. + * + * Make sure any pending truncation is applied before doing anything + * that may depend on it. + */ +void __ceph_do_pending_vmtruncate(struct inode *inode) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + u64 to; + int wrbuffer_refs, wake = 0; + +retry: + spin_lock(&inode->i_lock); + if (ci->i_truncate_pending == 0) { + dout("__do_pending_vmtruncate %p none pending\n", inode); + spin_unlock(&inode->i_lock); + return; + } + + /* + * make sure any dirty snapped pages are flushed before we + * possibly truncate them.. so write AND block! + */ + if (ci->i_wrbuffer_ref_head < ci->i_wrbuffer_ref) { + dout("__do_pending_vmtruncate %p flushing snaps first\n", + inode); + spin_unlock(&inode->i_lock); + filemap_write_and_wait_range(&inode->i_data, 0, + inode->i_sb->s_maxbytes); + goto retry; + } + + to = ci->i_truncate_size; + wrbuffer_refs = ci->i_wrbuffer_ref; + dout("__do_pending_vmtruncate %p (%d) to %lld\n", inode, + ci->i_truncate_pending, to); + spin_unlock(&inode->i_lock); + + truncate_inode_pages(inode->i_mapping, to); + + spin_lock(&inode->i_lock); + ci->i_truncate_pending--; + if (ci->i_truncate_pending == 0) + wake = 1; + spin_unlock(&inode->i_lock); + + if (wrbuffer_refs == 0) + ceph_check_caps(ci, CHECK_CAPS_AUTHONLY, NULL); + if (wake) + wake_up(&ci->i_cap_wq); +} + + +/* + * symlinks + */ +static void *ceph_sym_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct ceph_inode_info *ci = ceph_inode(dentry->d_inode); + nd_set_link(nd, ci->i_symlink); + return NULL; +} + +static const struct inode_operations ceph_symlink_iops = { + .readlink = generic_readlink, + .follow_link = ceph_sym_follow_link, +}; + +/* + * setattr + */ +int ceph_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct inode *parent_inode = dentry->d_parent->d_inode; + const unsigned int ia_valid = attr->ia_valid; + struct ceph_mds_request *req; + struct ceph_mds_client *mdsc = &ceph_client(dentry->d_sb)->mdsc; + int issued; + int release = 0, dirtied = 0; + int mask = 0; + int err = 0; + int queue_trunc = 0; + + if (ceph_snap(inode) != CEPH_NOSNAP) + return -EROFS; + + __ceph_do_pending_vmtruncate(inode); + + err = inode_change_ok(inode, attr); + if (err != 0) + return err; + + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETATTR, + USE_AUTH_MDS); + if (IS_ERR(req)) + return PTR_ERR(req); + + spin_lock(&inode->i_lock); + issued = __ceph_caps_issued(ci, NULL); + dout("setattr %p issued %s\n", inode, ceph_cap_string(issued)); + + if (ia_valid & ATTR_UID) { + dout("setattr %p uid %d -> %d\n", inode, + inode->i_uid, attr->ia_uid); + if (issued & CEPH_CAP_AUTH_EXCL) { + inode->i_uid = attr->ia_uid; + dirtied |= CEPH_CAP_AUTH_EXCL; + } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 || + attr->ia_uid != inode->i_uid) { + req->r_args.setattr.uid = cpu_to_le32(attr->ia_uid); + mask |= CEPH_SETATTR_UID; + release |= CEPH_CAP_AUTH_SHARED; + } + } + if (ia_valid & ATTR_GID) { + dout("setattr %p gid %d -> %d\n", inode, + inode->i_gid, attr->ia_gid); + if (issued & CEPH_CAP_AUTH_EXCL) { + inode->i_gid = attr->ia_gid; + dirtied |= CEPH_CAP_AUTH_EXCL; + } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 || + attr->ia_gid != inode->i_gid) { + req->r_args.setattr.gid = cpu_to_le32(attr->ia_gid); + mask |= CEPH_SETATTR_GID; + release |= CEPH_CAP_AUTH_SHARED; + } + } + if (ia_valid & ATTR_MODE) { + dout("setattr %p mode 0%o -> 0%o\n", inode, inode->i_mode, + attr->ia_mode); + if (issued & CEPH_CAP_AUTH_EXCL) { + inode->i_mode = attr->ia_mode; + dirtied |= CEPH_CAP_AUTH_EXCL; + } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 || + attr->ia_mode != inode->i_mode) { + req->r_args.setattr.mode = cpu_to_le32(attr->ia_mode); + mask |= CEPH_SETATTR_MODE; + release |= CEPH_CAP_AUTH_SHARED; + } + } + + if (ia_valid & ATTR_ATIME) { + dout("setattr %p atime %ld.%ld -> %ld.%ld\n", inode, + inode->i_atime.tv_sec, inode->i_atime.tv_nsec, + attr->ia_atime.tv_sec, attr->ia_atime.tv_nsec); + if (issued & CEPH_CAP_FILE_EXCL) { + ci->i_time_warp_seq++; + inode->i_atime = attr->ia_atime; + dirtied |= CEPH_CAP_FILE_EXCL; + } else if ((issued & CEPH_CAP_FILE_WR) && + timespec_compare(&inode->i_atime, + &attr->ia_atime) < 0) { + inode->i_atime = attr->ia_atime; + dirtied |= CEPH_CAP_FILE_WR; + } else if ((issued & CEPH_CAP_FILE_SHARED) == 0 || + !timespec_equal(&inode->i_atime, &attr->ia_atime)) { + ceph_encode_timespec(&req->r_args.setattr.atime, + &attr->ia_atime); + mask |= CEPH_SETATTR_ATIME; + release |= CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_RD | + CEPH_CAP_FILE_WR; + } + } + if (ia_valid & ATTR_MTIME) { + dout("setattr %p mtime %ld.%ld -> %ld.%ld\n", inode, + inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec, + attr->ia_mtime.tv_sec, attr->ia_mtime.tv_nsec); + if (issued & CEPH_CAP_FILE_EXCL) { + ci->i_time_warp_seq++; + inode->i_mtime = attr->ia_mtime; + dirtied |= CEPH_CAP_FILE_EXCL; + } else if ((issued & CEPH_CAP_FILE_WR) && + timespec_compare(&inode->i_mtime, + &attr->ia_mtime) < 0) { + inode->i_mtime = attr->ia_mtime; + dirtied |= CEPH_CAP_FILE_WR; + } else if ((issued & CEPH_CAP_FILE_SHARED) == 0 || + !timespec_equal(&inode->i_mtime, &attr->ia_mtime)) { + ceph_encode_timespec(&req->r_args.setattr.mtime, + &attr->ia_mtime); + mask |= CEPH_SETATTR_MTIME; + release |= CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_RD | + CEPH_CAP_FILE_WR; + } + } + if (ia_valid & ATTR_SIZE) { + dout("setattr %p size %lld -> %lld\n", inode, + inode->i_size, attr->ia_size); + if (attr->ia_size > inode->i_sb->s_maxbytes) { + err = -EINVAL; + goto out; + } + if ((issued & CEPH_CAP_FILE_EXCL) && + attr->ia_size > inode->i_size) { + inode->i_size = attr->ia_size; + if (attr->ia_size < inode->i_size) { + ci->i_truncate_size = attr->ia_size; + ci->i_truncate_pending++; + queue_trunc = 1; + } + inode->i_blocks = + (attr->ia_size + (1 << 9) - 1) >> 9; + inode->i_ctime = attr->ia_ctime; + ci->i_reported_size = attr->ia_size; + dirtied |= CEPH_CAP_FILE_EXCL; + } else if ((issued & CEPH_CAP_FILE_SHARED) == 0 || + attr->ia_size != inode->i_size) { + req->r_args.setattr.size = cpu_to_le64(attr->ia_size); + req->r_args.setattr.old_size = + cpu_to_le64(inode->i_size); + mask |= CEPH_SETATTR_SIZE; + release |= CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_RD | + CEPH_CAP_FILE_WR; + } + } + + /* these do nothing */ + if (ia_valid & ATTR_CTIME) { + bool only = (ia_valid & (ATTR_SIZE|ATTR_MTIME|ATTR_ATIME| + ATTR_MODE|ATTR_UID|ATTR_GID)) == 0; + dout("setattr %p ctime %ld.%ld -> %ld.%ld (%s)\n", inode, + inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec, + attr->ia_ctime.tv_sec, attr->ia_ctime.tv_nsec, + only ? "ctime only" : "ignored"); + inode->i_ctime = attr->ia_ctime; + if (only) { + /* + * if kernel wants to dirty ctime but nothing else, + * we need to choose a cap to dirty under, or do + * a almost-no-op setattr + */ + if (issued & CEPH_CAP_AUTH_EXCL) + dirtied |= CEPH_CAP_AUTH_EXCL; + else if (issued & CEPH_CAP_FILE_EXCL) + dirtied |= CEPH_CAP_FILE_EXCL; + else if (issued & CEPH_CAP_XATTR_EXCL) + dirtied |= CEPH_CAP_XATTR_EXCL; + else + mask |= CEPH_SETATTR_CTIME; + } + } + if (ia_valid & ATTR_FILE) + dout("setattr %p ATTR_FILE ... hrm!\n", inode); + + if (dirtied) { + __ceph_mark_dirty_caps(ci, dirtied); + inode->i_ctime = CURRENT_TIME; + } + + release &= issued; + spin_unlock(&inode->i_lock); + + if (queue_trunc) + __ceph_do_pending_vmtruncate(inode); + + if (mask) { + req->r_inode = igrab(inode); + req->r_inode_drop = release; + req->r_args.setattr.mask = cpu_to_le32(mask); + req->r_num_caps = 1; + err = ceph_mdsc_do_request(mdsc, parent_inode, req); + } + dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err, + ceph_cap_string(dirtied), mask); + + ceph_mdsc_put_request(req); + __ceph_do_pending_vmtruncate(inode); + return err; +out: + spin_unlock(&inode->i_lock); + ceph_mdsc_put_request(req); + return err; +} + +/* + * Verify that we have a lease on the given mask. If not, + * do a getattr against an mds. + */ +int ceph_do_getattr(struct inode *inode, int mask) +{ + struct ceph_client *client = ceph_sb_to_client(inode->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + int err; + + if (ceph_snap(inode) == CEPH_SNAPDIR) { + dout("do_getattr inode %p SNAPDIR\n", inode); + return 0; + } + + dout("do_getattr inode %p mask %s\n", inode, ceph_cap_string(mask)); + if (ceph_caps_issued_mask(ceph_inode(inode), mask, 1)) + return 0; + + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_GETATTR, USE_ANY_MDS); + if (IS_ERR(req)) + return PTR_ERR(req); + req->r_inode = igrab(inode); + req->r_num_caps = 1; + req->r_args.getattr.mask = cpu_to_le32(mask); + err = ceph_mdsc_do_request(mdsc, NULL, req); + ceph_mdsc_put_request(req); + dout("do_getattr result=%d\n", err); + return err; +} + + +/* + * Check inode permissions. We verify we have a valid value for + * the AUTH cap, then call the generic handler. + */ +int ceph_permission(struct inode *inode, int mask) +{ + int err = ceph_do_getattr(inode, CEPH_CAP_AUTH_SHARED); + + if (!err) + err = generic_permission(inode, mask, NULL); + return err; +} + +/* + * Get all attributes. Hopefully somedata we'll have a statlite() + * and can limit the fields we require to be accurate. + */ +int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + int err; + + err = ceph_do_getattr(inode, CEPH_STAT_CAP_INODE_ALL); + if (!err) { + generic_fillattr(inode, stat); + stat->ino = inode->i_ino; + if (ceph_snap(inode) != CEPH_NOSNAP) + stat->dev = ceph_snap(inode); + else + stat->dev = 0; + if (S_ISDIR(inode->i_mode)) + stat->blksize = 65536; + } + return err; +} diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c new file mode 100644 index 0000000..8eaac04 --- /dev/null +++ b/fs/ceph/xattr.c @@ -0,0 +1,833 @@ +#include "ceph_debug.h" +#include "super.h" +#include "decode.h" + +#include + +static bool ceph_is_valid_xattr(const char *name) +{ + return !strncmp(name, XATTR_SECURITY_PREFIX, + XATTR_SECURITY_PREFIX_LEN) || + !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) || + !strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); +} + +/* + * These define virtual xattrs exposing the recursive directory + * statistics and layout metadata. + */ +struct ceph_vxattr_cb { + bool readonly; + char *name; + size_t (*getxattr_cb)(struct ceph_inode_info *ci, char *val, + size_t size); +}; + +/* directories */ + +static size_t ceph_vxattrcb_entries(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, "%lld", ci->i_files + ci->i_subdirs); +} + +static size_t ceph_vxattrcb_files(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, "%lld", ci->i_files); +} + +static size_t ceph_vxattrcb_subdirs(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, "%lld", ci->i_subdirs); +} + +static size_t ceph_vxattrcb_rentries(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, "%lld", ci->i_rfiles + ci->i_rsubdirs); +} + +static size_t ceph_vxattrcb_rfiles(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, "%lld", ci->i_rfiles); +} + +static size_t ceph_vxattrcb_rsubdirs(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, "%lld", ci->i_rsubdirs); +} + +static size_t ceph_vxattrcb_rbytes(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, "%lld", ci->i_rbytes); +} + +static size_t ceph_vxattrcb_rctime(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, "%ld.%ld", (long)ci->i_rctime.tv_sec, + (long)ci->i_rctime.tv_nsec); +} + +static struct ceph_vxattr_cb ceph_dir_vxattrs[] = { + { true, "user.ceph.dir.entries", ceph_vxattrcb_entries}, + { true, "user.ceph.dir.files", ceph_vxattrcb_files}, + { true, "user.ceph.dir.subdirs", ceph_vxattrcb_subdirs}, + { true, "user.ceph.dir.rentries", ceph_vxattrcb_rentries}, + { true, "user.ceph.dir.rfiles", ceph_vxattrcb_rfiles}, + { true, "user.ceph.dir.rsubdirs", ceph_vxattrcb_rsubdirs}, + { true, "user.ceph.dir.rbytes", ceph_vxattrcb_rbytes}, + { true, "user.ceph.dir.rctime", ceph_vxattrcb_rctime}, + { true, NULL, NULL } +}; + +/* files */ + +static size_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val, + size_t size) +{ + return snprintf(val, size, + "chunk_bytes=%lld\nstripe_count=%lld\nobject_size=%lld\n", + (unsigned long long)ceph_file_layout_su(ci->i_layout), + (unsigned long long)ceph_file_layout_stripe_count(ci->i_layout), + (unsigned long long)ceph_file_layout_object_size(ci->i_layout)); +} + +static struct ceph_vxattr_cb ceph_file_vxattrs[] = { + { true, "user.ceph.layout", ceph_vxattrcb_layout}, + { NULL, NULL } +}; + +static struct ceph_vxattr_cb *ceph_inode_vxattrs(struct inode *inode) +{ + if (S_ISDIR(inode->i_mode)) + return ceph_dir_vxattrs; + else if (S_ISREG(inode->i_mode)) + return ceph_file_vxattrs; + return NULL; +} + +static struct ceph_vxattr_cb *ceph_match_vxattr(struct ceph_vxattr_cb *vxattr, + const char *name) +{ + do { + if (strcmp(vxattr->name, name) == 0) + return vxattr; + vxattr++; + } while (vxattr->name); + return NULL; +} + +static int __set_xattr(struct ceph_inode_info *ci, + const char *name, int name_len, + const char *val, int val_len, + int dirty, + int should_free_name, int should_free_val, + struct ceph_inode_xattr **newxattr) +{ + struct rb_node **p; + struct rb_node *parent = NULL; + struct ceph_inode_xattr *xattr = NULL; + int c; + int new = 0; + + p = &ci->i_xattrs.index.rb_node; + while (*p) { + parent = *p; + xattr = rb_entry(parent, struct ceph_inode_xattr, node); + c = strncmp(name, xattr->name, min(name_len, xattr->name_len)); + if (c < 0) + p = &(*p)->rb_left; + else if (c > 0) + p = &(*p)->rb_right; + else { + if (name_len == xattr->name_len) + break; + else if (name_len < xattr->name_len) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + xattr = NULL; + } + + if (!xattr) { + new = 1; + xattr = *newxattr; + xattr->name = name; + xattr->name_len = name_len; + xattr->should_free_name = should_free_name; + + ci->i_xattrs.count++; + dout("__set_xattr count=%d\n", ci->i_xattrs.count); + } else { + kfree(*newxattr); + *newxattr = NULL; + if (xattr->should_free_val) + kfree((void *)xattr->val); + + if (should_free_name) { + kfree((void *)name); + name = xattr->name; + } + ci->i_xattrs.names_size -= xattr->name_len; + ci->i_xattrs.vals_size -= xattr->val_len; + } + if (!xattr) { + pr_err("__set_xattr ENOMEM on %p %llx.%llx xattr %s=%s\n", + &ci->vfs_inode, ceph_vinop(&ci->vfs_inode), name, + xattr->val); + return -ENOMEM; + } + ci->i_xattrs.names_size += name_len; + ci->i_xattrs.vals_size += val_len; + if (val) + xattr->val = val; + else + xattr->val = ""; + + xattr->val_len = val_len; + xattr->dirty = dirty; + xattr->should_free_val = (val && should_free_val); + + if (new) { + rb_link_node(&xattr->node, parent, p); + rb_insert_color(&xattr->node, &ci->i_xattrs.index); + dout("__set_xattr_val p=%p\n", p); + } + + dout("__set_xattr_val added %llx.%llx xattr %p %s=%.*s\n", + ceph_vinop(&ci->vfs_inode), xattr, name, val_len, val); + + return 0; +} + +static struct ceph_inode_xattr *__get_xattr(struct ceph_inode_info *ci, + const char *name) +{ + struct rb_node **p; + struct rb_node *parent = NULL; + struct ceph_inode_xattr *xattr = NULL; + int c; + + p = &ci->i_xattrs.index.rb_node; + while (*p) { + parent = *p; + xattr = rb_entry(parent, struct ceph_inode_xattr, node); + c = strncmp(name, xattr->name, xattr->name_len); + if (c < 0) + p = &(*p)->rb_left; + else if (c > 0) + p = &(*p)->rb_right; + else { + dout("__get_xattr %s: found %.*s\n", name, + xattr->val_len, xattr->val); + return xattr; + } + } + + dout("__get_xattr %s: not found\n", name); + + return NULL; +} + +static void __free_xattr(struct ceph_inode_xattr *xattr) +{ + BUG_ON(!xattr); + + if (xattr->should_free_name) + kfree((void *)xattr->name); + if (xattr->should_free_val) + kfree((void *)xattr->val); + + kfree(xattr); +} + +static int __remove_xattr(struct ceph_inode_info *ci, + struct ceph_inode_xattr *xattr) +{ + if (!xattr) + return -EOPNOTSUPP; + + rb_erase(&xattr->node, &ci->i_xattrs.index); + + if (xattr->should_free_name) + kfree((void *)xattr->name); + if (xattr->should_free_val) + kfree((void *)xattr->val); + + ci->i_xattrs.names_size -= xattr->name_len; + ci->i_xattrs.vals_size -= xattr->val_len; + ci->i_xattrs.count--; + kfree(xattr); + + return 0; +} + +static int __remove_xattr_by_name(struct ceph_inode_info *ci, + const char *name) +{ + struct rb_node **p; + struct ceph_inode_xattr *xattr; + int err; + + p = &ci->i_xattrs.index.rb_node; + xattr = __get_xattr(ci, name); + err = __remove_xattr(ci, xattr); + return err; +} + +static char *__copy_xattr_names(struct ceph_inode_info *ci, + char *dest) +{ + struct rb_node *p; + struct ceph_inode_xattr *xattr = NULL; + + p = rb_first(&ci->i_xattrs.index); + dout("__copy_xattr_names count=%d\n", ci->i_xattrs.count); + + while (p) { + xattr = rb_entry(p, struct ceph_inode_xattr, node); + memcpy(dest, xattr->name, xattr->name_len); + dest[xattr->name_len] = '\0'; + + dout("dest=%s %p (%s) (%d/%d)\n", dest, xattr, xattr->name, + xattr->name_len, ci->i_xattrs.names_size); + + dest += xattr->name_len + 1; + p = rb_next(p); + } + + return dest; +} + +void __ceph_destroy_xattrs(struct ceph_inode_info *ci) +{ + struct rb_node *p, *tmp; + struct ceph_inode_xattr *xattr = NULL; + + p = rb_first(&ci->i_xattrs.index); + + dout("__ceph_destroy_xattrs p=%p\n", p); + + while (p) { + xattr = rb_entry(p, struct ceph_inode_xattr, node); + tmp = p; + p = rb_next(tmp); + dout("__ceph_destroy_xattrs next p=%p (%.*s)\n", p, + xattr->name_len, xattr->name); + rb_erase(tmp, &ci->i_xattrs.index); + + __free_xattr(xattr); + } + + ci->i_xattrs.names_size = 0; + ci->i_xattrs.vals_size = 0; + ci->i_xattrs.index_version = 0; + ci->i_xattrs.count = 0; + ci->i_xattrs.index = RB_ROOT; +} + +static int __build_xattrs(struct inode *inode) +{ + u32 namelen; + u32 numattr = 0; + void *p, *end; + u32 len; + const char *name, *val; + struct ceph_inode_info *ci = ceph_inode(inode); + int xattr_version; + struct ceph_inode_xattr **xattrs = NULL; + int err; + int i; + + dout("__build_xattrs() len=%d\n", + ci->i_xattrs.blob ? (int)ci->i_xattrs.blob->vec.iov_len : 0); + + if (ci->i_xattrs.index_version >= ci->i_xattrs.version) + return 0; /* already built */ + + __ceph_destroy_xattrs(ci); + +start: + /* updated internal xattr rb tree */ + if (ci->i_xattrs.blob && ci->i_xattrs.blob->vec.iov_len > 4) { + p = ci->i_xattrs.blob->vec.iov_base; + end = p + ci->i_xattrs.blob->vec.iov_len; + ceph_decode_32_safe(&p, end, numattr, bad); + xattr_version = ci->i_xattrs.version; + spin_unlock(&inode->i_lock); + + xattrs = kcalloc(numattr, sizeof(struct ceph_xattr *), + GFP_NOFS); + err = -ENOMEM; + if (!xattrs) + goto bad_lock; + memset(xattrs, 0, numattr*sizeof(struct ceph_xattr *)); + for (i = 0; i < numattr; i++) { + xattrs[i] = kmalloc(sizeof(struct ceph_inode_xattr), + GFP_NOFS); + if (!xattrs[i]) + goto bad_lock; + } + + spin_lock(&inode->i_lock); + if (ci->i_xattrs.version != xattr_version) { + /* lost a race, retry */ + for (i = 0; i < numattr; i++) + kfree(xattrs[i]); + kfree(xattrs); + goto start; + } + err = -EIO; + while (numattr--) { + ceph_decode_32_safe(&p, end, len, bad); + namelen = len; + name = p; + p += len; + ceph_decode_32_safe(&p, end, len, bad); + val = p; + p += len; + + err = __set_xattr(ci, name, namelen, val, len, + 0, 0, 0, &xattrs[numattr]); + + if (err < 0) + goto bad; + } + kfree(xattrs); + } + ci->i_xattrs.index_version = ci->i_xattrs.version; + ci->i_xattrs.dirty = false; + + return err; +bad_lock: + spin_lock(&inode->i_lock); +bad: + if (xattrs) { + for (i = 0; i < numattr; i++) + kfree(xattrs[i]); + kfree(xattrs); + } + ci->i_xattrs.names_size = 0; + return err; +} + +static int __get_required_blob_size(struct ceph_inode_info *ci, int name_size, + int val_size) +{ + /* + * 4 bytes for the length, and additional 4 bytes per each xattr name, + * 4 bytes per each value + */ + int size = 4 + ci->i_xattrs.count*(4 + 4) + + ci->i_xattrs.names_size + + ci->i_xattrs.vals_size; + dout("__get_required_blob_size c=%d names.size=%d vals.size=%d\n", + ci->i_xattrs.count, ci->i_xattrs.names_size, + ci->i_xattrs.vals_size); + + if (name_size) + size += 4 + 4 + name_size + val_size; + + return size; +} + +/* + * If there are dirty xattrs, reencode xattrs into the prealloc_blob + * and swap into place. + */ +void __ceph_build_xattrs_blob(struct ceph_inode_info *ci) +{ + struct rb_node *p; + struct ceph_inode_xattr *xattr = NULL; + void *dest; + + dout("__build_xattrs_blob %p\n", &ci->vfs_inode); + if (ci->i_xattrs.dirty) { + int need = __get_required_blob_size(ci, 0, 0); + + BUG_ON(need > ci->i_xattrs.prealloc_blob->alloc_len); + + p = rb_first(&ci->i_xattrs.index); + dest = ci->i_xattrs.prealloc_blob->vec.iov_base; + + ceph_encode_32(&dest, ci->i_xattrs.count); + while (p) { + xattr = rb_entry(p, struct ceph_inode_xattr, node); + + ceph_encode_32(&dest, xattr->name_len); + memcpy(dest, xattr->name, xattr->name_len); + dest += xattr->name_len; + ceph_encode_32(&dest, xattr->val_len); + memcpy(dest, xattr->val, xattr->val_len); + dest += xattr->val_len; + + p = rb_next(p); + } + + /* adjust buffer len; it may be larger than we need */ + ci->i_xattrs.prealloc_blob->vec.iov_len = + dest - ci->i_xattrs.prealloc_blob->vec.iov_base; + + ceph_buffer_put(ci->i_xattrs.blob); + ci->i_xattrs.blob = ci->i_xattrs.prealloc_blob; + ci->i_xattrs.prealloc_blob = NULL; + ci->i_xattrs.dirty = false; + } +} + +ssize_t ceph_getxattr(struct dentry *dentry, const char *name, void *value, + size_t size) +{ + struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_vxattr_cb *vxattrs = ceph_inode_vxattrs(inode); + int err; + struct ceph_inode_xattr *xattr; + struct ceph_vxattr_cb *vxattr = NULL; + + if (!ceph_is_valid_xattr(name)) + return -ENODATA; + + /* let's see if a virtual xattr was requested */ + if (vxattrs) + vxattr = ceph_match_vxattr(vxattrs, name); + + spin_lock(&inode->i_lock); + dout("getxattr %p ver=%lld index_ver=%lld\n", inode, + ci->i_xattrs.version, ci->i_xattrs.index_version); + + if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 1) && + (ci->i_xattrs.index_version >= ci->i_xattrs.version)) { + goto get_xattr; + } else { + spin_unlock(&inode->i_lock); + /* get xattrs from mds (if we don't already have them) */ + err = ceph_do_getattr(inode, CEPH_STAT_CAP_XATTR); + if (err) + return err; + } + + spin_lock(&inode->i_lock); + + if (vxattr && vxattr->readonly) { + err = vxattr->getxattr_cb(ci, value, size); + goto out; + } + + err = __build_xattrs(inode); + if (err < 0) + goto out; + +get_xattr: + err = -ENODATA; /* == ENOATTR */ + xattr = __get_xattr(ci, name); + if (!xattr) { + if (vxattr) + err = vxattr->getxattr_cb(ci, value, size); + goto out; + } + + err = -ERANGE; + if (size && size < xattr->val_len) + goto out; + + err = xattr->val_len; + if (size == 0) + goto out; + + memcpy(value, xattr->val, xattr->val_len); + +out: + spin_unlock(&inode->i_lock); + return err; +} + +ssize_t ceph_listxattr(struct dentry *dentry, char *names, size_t size) +{ + struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_vxattr_cb *vxattrs = ceph_inode_vxattrs(inode); + u32 vir_namelen = 0; + u32 namelen; + int err; + u32 len; + int i; + + spin_lock(&inode->i_lock); + dout("listxattr %p ver=%lld index_ver=%lld\n", inode, + ci->i_xattrs.version, ci->i_xattrs.index_version); + + if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 1) && + (ci->i_xattrs.index_version > ci->i_xattrs.version)) { + goto list_xattr; + } else { + spin_unlock(&inode->i_lock); + err = ceph_do_getattr(inode, CEPH_STAT_CAP_XATTR); + if (err) + return err; + } + + spin_lock(&inode->i_lock); + + err = __build_xattrs(inode); + if (err < 0) + goto out; + +list_xattr: + vir_namelen = 0; + /* include virtual dir xattrs */ + if (vxattrs) + for (i = 0; vxattrs[i].name; i++) + vir_namelen += strlen(vxattrs[i].name) + 1; + /* adding 1 byte per each variable due to the null termination */ + namelen = vir_namelen + ci->i_xattrs.names_size + ci->i_xattrs.count; + err = -ERANGE; + if (size && namelen > size) + goto out; + + err = namelen; + if (size == 0) + goto out; + + names = __copy_xattr_names(ci, names); + + /* virtual xattr names, too */ + if (vxattrs) + for (i = 0; vxattrs[i].name; i++) { + len = sprintf(names, "%s", vxattrs[i].name); + names += len + 1; + } + +out: + spin_unlock(&inode->i_lock); + return err; +} + +static int ceph_sync_setxattr(struct dentry *dentry, const char *name, + const char *value, size_t size, int flags) +{ + struct ceph_client *client = ceph_client(dentry->d_sb); + struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct inode *parent_inode = dentry->d_parent->d_inode; + struct ceph_mds_request *req; + struct ceph_mds_client *mdsc = &client->mdsc; + int err; + int i, nr_pages; + struct page **pages = NULL; + void *kaddr; + + /* copy value into some pages */ + nr_pages = calc_pages_for(0, size); + if (nr_pages) { + pages = kmalloc(sizeof(pages[0])*nr_pages, GFP_NOFS); + if (!pages) + return -ENOMEM; + err = -ENOMEM; + for (i = 0; i < nr_pages; i++) { + pages[i] = alloc_page(GFP_NOFS); + if (!pages[i]) { + nr_pages = i; + goto out; + } + kaddr = kmap(pages[i]); + memcpy(kaddr, value + i*PAGE_CACHE_SIZE, + min(PAGE_CACHE_SIZE, size-i*PAGE_CACHE_SIZE)); + } + } + + dout("setxattr value=%.*s\n", (int)size, value); + + /* do request */ + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETXATTR, + USE_AUTH_MDS); + if (IS_ERR(req)) + return PTR_ERR(req); + req->r_inode = igrab(inode); + req->r_inode_drop = CEPH_CAP_XATTR_SHARED; + req->r_num_caps = 1; + req->r_args.setxattr.flags = cpu_to_le32(flags); + req->r_path2 = kstrdup(name, GFP_NOFS); + + req->r_pages = pages; + req->r_num_pages = nr_pages; + req->r_data_len = size; + + dout("xattr.ver (before): %lld\n", ci->i_xattrs.version); + err = ceph_mdsc_do_request(mdsc, parent_inode, req); + ceph_mdsc_put_request(req); + dout("xattr.ver (after): %lld\n", ci->i_xattrs.version); + +out: + if (pages) { + for (i = 0; i < nr_pages; i++) + __free_page(pages[i]); + kfree(pages); + } + return err; +} + +int ceph_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_vxattr_cb *vxattrs = ceph_inode_vxattrs(inode); + int err; + int name_len = strlen(name); + int val_len = size; + char *newname = NULL; + char *newval = NULL; + struct ceph_inode_xattr *xattr = NULL; + int issued; + int required_blob_size; + + if (ceph_snap(inode) != CEPH_NOSNAP) + return -EROFS; + + if (!ceph_is_valid_xattr(name)) + return -EOPNOTSUPP; + + if (vxattrs) { + struct ceph_vxattr_cb *vxattr = + ceph_match_vxattr(vxattrs, name); + if (vxattr && vxattr->readonly) + return -EOPNOTSUPP; + } + + /* preallocate memory for xattr name, value, index node */ + err = -ENOMEM; + newname = kmalloc(name_len + 1, GFP_NOFS); + if (!newname) + goto out; + memcpy(newname, name, name_len + 1); + + if (val_len) { + newval = kmalloc(val_len + 1, GFP_NOFS); + if (!newval) + goto out; + memcpy(newval, value, val_len); + newval[val_len] = '\0'; + } + + xattr = kmalloc(sizeof(struct ceph_inode_xattr), GFP_NOFS); + if (!xattr) + goto out; + + spin_lock(&inode->i_lock); +retry: + issued = __ceph_caps_issued(ci, NULL); + if (!(issued & CEPH_CAP_XATTR_EXCL)) + goto do_sync; + __build_xattrs(inode); + + required_blob_size = __get_required_blob_size(ci, name_len, val_len); + + if (!ci->i_xattrs.prealloc_blob || + required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) { + struct ceph_buffer *blob = NULL; + + spin_unlock(&inode->i_lock); + dout(" preaallocating new blob size=%d\n", required_blob_size); + blob = ceph_buffer_new_alloc(required_blob_size, GFP_NOFS); + if (!blob) + goto out; + spin_lock(&inode->i_lock); + ceph_buffer_put(ci->i_xattrs.prealloc_blob); + ci->i_xattrs.prealloc_blob = blob; + goto retry; + } + + dout("setxattr %p issued %s\n", inode, ceph_cap_string(issued)); + err = __set_xattr(ci, newname, name_len, newval, + val_len, 1, 1, 1, &xattr); + __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL); + ci->i_xattrs.dirty = true; + inode->i_ctime = CURRENT_TIME; + spin_unlock(&inode->i_lock); + + return err; + +do_sync: + spin_unlock(&inode->i_lock); + err = ceph_sync_setxattr(dentry, name, value, size, flags); +out: + kfree(newname); + kfree(newval); + kfree(xattr); + return err; +} + +static int ceph_send_removexattr(struct dentry *dentry, const char *name) +{ + struct ceph_client *client = ceph_client(dentry->d_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct inode *inode = dentry->d_inode; + struct inode *parent_inode = dentry->d_parent->d_inode; + struct ceph_mds_request *req; + int err; + + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_RMXATTR, + USE_AUTH_MDS); + if (IS_ERR(req)) + return PTR_ERR(req); + req->r_inode = igrab(inode); + req->r_inode_drop = CEPH_CAP_XATTR_SHARED; + req->r_num_caps = 1; + req->r_path2 = kstrdup(name, GFP_NOFS); + + err = ceph_mdsc_do_request(mdsc, parent_inode, req); + ceph_mdsc_put_request(req); + return err; +} + +int ceph_removexattr(struct dentry *dentry, const char *name) +{ + struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_vxattr_cb *vxattrs = ceph_inode_vxattrs(inode); + int issued; + int err; + + if (ceph_snap(inode) != CEPH_NOSNAP) + return -EROFS; + + if (!ceph_is_valid_xattr(name)) + return -EOPNOTSUPP; + + if (vxattrs) { + struct ceph_vxattr_cb *vxattr = + ceph_match_vxattr(vxattrs, name); + if (vxattr && vxattr->readonly) + return -EOPNOTSUPP; + } + + spin_lock(&inode->i_lock); + __build_xattrs(inode); + issued = __ceph_caps_issued(ci, NULL); + dout("removexattr %p issued %s\n", inode, ceph_cap_string(issued)); + + if (!(issued & CEPH_CAP_XATTR_EXCL)) + goto do_sync; + + err = __remove_xattr_by_name(ceph_inode(inode), name); + __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL); + ci->i_xattrs.dirty = true; + inode->i_ctime = CURRENT_TIME; + + spin_unlock(&inode->i_lock); + + return err; +do_sync: + spin_unlock(&inode->i_lock); + err = ceph_send_removexattr(dentry, name); + return err; +} + -- cgit v0.10.2 From 2817b000b02c5f0c05af67c01fb2684e1381d6ef Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:08 -0700 Subject: ceph: directory operations Directory operations, including lookup, are defined here. We take advantage of lookup intents when possible. For the most part, we just need to build the proper requests for the metadata server(s) and pass things off to the mds_client. The results of most operations are normally incorporated into the client's cache when the reply is parsed by ceph_fill_trace(). However, if the MDS replies without a trace (e.g., when retrying an update after an MDS failure recovery), some operation-specific cleanup may be needed. We can validate cached dentries in two ways. A per-dentry lease may be issued by the MDS, or a per-directory cap may be issued that acts as a lease on the entire directory. In the latter case, a 'gen' value is used to determine which dentries belong to the currently leased directory contents. We normally prepopulate the dcache and icache with readdir results. This makes subsequent lookups and getattrs avoid any server interaction. It also lets us satisfy readdir operation by peeking at the dcache IFF we hold the per-directory cap/lease, previously performed a readdir, and haven't dropped any of the resulting dentries. Signed-off-by: Sage Weil diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c new file mode 100644 index 0000000..7bb8db5 --- /dev/null +++ b/fs/ceph/dir.c @@ -0,0 +1,1212 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include + +#include "super.h" + +/* + * Directory operations: readdir, lookup, create, link, unlink, + * rename, etc. + */ + +/* + * Ceph MDS operations are specified in terms of a base ino and + * relative path. Thus, the client can specify an operation on a + * specific inode (e.g., a getattr due to fstat(2)), or as a path + * relative to, say, the root directory. + * + * Normally, we limit ourselves to strict inode ops (no path component) + * or dentry operations (a single path component relative to an ino). The + * exception to this is open_root_dentry(), which will open the mount + * point by name. + */ + +const struct inode_operations ceph_dir_iops; +const struct file_operations ceph_dir_fops; +struct dentry_operations ceph_dentry_ops; + +/* + * Initialize ceph dentry state. + */ +int ceph_init_dentry(struct dentry *dentry) +{ + struct ceph_dentry_info *di; + + if (dentry->d_fsdata) + return 0; + + if (ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP) + dentry->d_op = &ceph_dentry_ops; + else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR) + dentry->d_op = &ceph_snapdir_dentry_ops; + else + dentry->d_op = &ceph_snap_dentry_ops; + + di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS); + if (!di) + return -ENOMEM; /* oh well */ + + spin_lock(&dentry->d_lock); + if (dentry->d_fsdata) /* lost a race */ + goto out_unlock; + di->dentry = dentry; + di->lease_session = NULL; + dentry->d_fsdata = di; + dentry->d_time = jiffies; + ceph_dentry_lru_add(dentry); +out_unlock: + spin_unlock(&dentry->d_lock); + return 0; +} + + + +/* + * for readdir, we encode the directory frag and offset within that + * frag into f_pos. + */ +static unsigned fpos_frag(loff_t p) +{ + return p >> 32; +} +static unsigned fpos_off(loff_t p) +{ + return p & 0xffffffff; +} + +/* + * When possible, we try to satisfy a readdir by peeking at the + * dcache. We make this work by carefully ordering dentries on + * d_u.d_child when we initially get results back from the MDS, and + * falling back to a "normal" sync readdir if any dentries in the dir + * are dropped. + * + * I_COMPLETE tells indicates we have all dentries in the dir. It is + * defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by + * the MDS if/when the directory is modified). + */ +static int __dcache_readdir(struct file *filp, + void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct ceph_file_info *fi = filp->private_data; + struct dentry *parent = filp->f_dentry; + struct inode *dir = parent->d_inode; + struct list_head *p; + struct dentry *dentry, *last; + struct ceph_dentry_info *di; + int err = 0; + + /* claim ref on last dentry we returned */ + last = fi->dentry; + fi->dentry = NULL; + + dout("__dcache_readdir %p at %llu (last %p)\n", dir, filp->f_pos, + last); + + spin_lock(&dcache_lock); + + /* start at beginning? */ + if (filp->f_pos == 2 || (last && + filp->f_pos < ceph_dentry(last)->offset)) { + if (list_empty(&parent->d_subdirs)) + goto out_unlock; + p = parent->d_subdirs.prev; + dout(" initial p %p/%p\n", p->prev, p->next); + } else { + p = last->d_u.d_child.prev; + } + +more: + dentry = list_entry(p, struct dentry, d_u.d_child); + di = ceph_dentry(dentry); + while (1) { + dout(" p %p/%p d_subdirs %p/%p\n", p->prev, p->next, + parent->d_subdirs.prev, parent->d_subdirs.next); + if (p == &parent->d_subdirs) { + fi->at_end = 1; + goto out_unlock; + } + if (!d_unhashed(dentry) && dentry->d_inode && + filp->f_pos <= di->offset) + break; + dout(" skipping %p %.*s at %llu (%llu)%s%s\n", dentry, + dentry->d_name.len, dentry->d_name.name, di->offset, + filp->f_pos, d_unhashed(dentry) ? " unhashed" : "", + !dentry->d_inode ? " null" : ""); + p = p->prev; + dentry = list_entry(p, struct dentry, d_u.d_child); + di = ceph_dentry(dentry); + } + + atomic_inc(&dentry->d_count); + spin_unlock(&dcache_lock); + spin_unlock(&inode->i_lock); + + dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos, + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); + filp->f_pos = di->offset; + err = filldir(dirent, dentry->d_name.name, + dentry->d_name.len, di->offset, + dentry->d_inode->i_ino, + dentry->d_inode->i_mode >> 12); + + if (last) { + if (err < 0) { + /* remember our position */ + fi->dentry = last; + fi->next_offset = di->offset; + } else { + dput(last); + } + last = NULL; + } + + spin_lock(&inode->i_lock); + spin_lock(&dcache_lock); + + if (err < 0) + goto out_unlock; + + last = dentry; + + p = p->prev; + filp->f_pos++; + + /* make sure a dentry wasn't dropped while we didn't have dcache_lock */ + if ((ceph_inode(dir)->i_ceph_flags & CEPH_I_COMPLETE)) + goto more; + dout(" lost I_COMPLETE on %p; falling back to mds\n", dir); + err = -EAGAIN; + +out_unlock: + spin_unlock(&dcache_lock); + + if (last) { + spin_unlock(&inode->i_lock); + dput(last); + spin_lock(&inode->i_lock); + } + + return err; +} + +/* + * make note of the last dentry we read, so we can + * continue at the same lexicographical point, + * regardless of what dir changes take place on the + * server. + */ +static int note_last_dentry(struct ceph_file_info *fi, const char *name, + int len) +{ + kfree(fi->last_name); + fi->last_name = kmalloc(len+1, GFP_NOFS); + if (!fi->last_name) + return -ENOMEM; + memcpy(fi->last_name, name, len); + fi->last_name[len] = 0; + dout("note_last_dentry '%s'\n", fi->last_name); + return 0; +} + +static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct ceph_file_info *fi = filp->private_data; + struct inode *inode = filp->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_client *client = ceph_inode_to_client(inode); + struct ceph_mds_client *mdsc = &client->mdsc; + unsigned frag = fpos_frag(filp->f_pos); + int off = fpos_off(filp->f_pos); + int err; + u32 ftype; + struct ceph_mds_reply_info_parsed *rinfo; + const int max_entries = client->mount_args.max_readdir; + + dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off); + if (fi->at_end) + return 0; + + /* always start with . and .. */ + if (filp->f_pos == 0) { + /* note dir version at start of readdir so we can tell + * if any dentries get dropped */ + fi->dir_release_count = ci->i_release_count; + + dout("readdir off 0 -> '.'\n"); + if (filldir(dirent, ".", 1, ceph_make_fpos(0, 0), + inode->i_ino, inode->i_mode >> 12) < 0) + return 0; + filp->f_pos = 1; + off = 1; + } + if (filp->f_pos == 1) { + dout("readdir off 1 -> '..'\n"); + if (filldir(dirent, "..", 2, ceph_make_fpos(0, 1), + filp->f_dentry->d_parent->d_inode->i_ino, + inode->i_mode >> 12) < 0) + return 0; + filp->f_pos = 2; + off = 2; + } + + /* can we use the dcache? */ + spin_lock(&inode->i_lock); + if ((filp->f_pos == 2 || fi->dentry) && + !ceph_test_opt(client, NOASYNCREADDIR) && + (ci->i_ceph_flags & CEPH_I_COMPLETE) && + __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) { + err = __dcache_readdir(filp, dirent, filldir); + if (err != -EAGAIN) { + spin_unlock(&inode->i_lock); + return err; + } + } + spin_unlock(&inode->i_lock); + if (fi->dentry) { + err = note_last_dentry(fi, fi->dentry->d_name.name, + fi->dentry->d_name.len); + if (err) + return err; + dput(fi->dentry); + fi->dentry = NULL; + } + + /* proceed with a normal readdir */ + +more: + /* do we have the correct frag content buffered? */ + if (fi->frag != frag || fi->last_readdir == NULL) { + struct ceph_mds_request *req; + int op = ceph_snap(inode) == CEPH_SNAPDIR ? + CEPH_MDS_OP_LSSNAP : CEPH_MDS_OP_READDIR; + + /* discard old result, if any */ + if (fi->last_readdir) + ceph_mdsc_put_request(fi->last_readdir); + + /* requery frag tree, as the frag topology may have changed */ + frag = ceph_choose_frag(ceph_inode(inode), frag, NULL, NULL); + + dout("readdir fetching %llx.%llx frag %x offset '%s'\n", + ceph_vinop(inode), frag, fi->last_name); + req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); + if (IS_ERR(req)) + return PTR_ERR(req); + req->r_inode = igrab(inode); + req->r_dentry = dget(filp->f_dentry); + /* hints to request -> mds selection code */ + req->r_direct_mode = USE_AUTH_MDS; + req->r_direct_hash = ceph_frag_value(frag); + req->r_direct_is_hash = true; + req->r_path2 = kstrdup(fi->last_name, GFP_NOFS); + req->r_readdir_offset = fi->next_offset; + req->r_args.readdir.frag = cpu_to_le32(frag); + req->r_args.readdir.max_entries = cpu_to_le32(max_entries); + req->r_num_caps = max_entries; + err = ceph_mdsc_do_request(mdsc, NULL, req); + if (err < 0) { + ceph_mdsc_put_request(req); + return err; + } + dout("readdir got and parsed readdir result=%d" + " on frag %x, end=%d, complete=%d\n", err, frag, + (int)req->r_reply_info.dir_end, + (int)req->r_reply_info.dir_complete); + + if (!req->r_did_prepopulate) { + dout("readdir !did_prepopulate"); + fi->dir_release_count--; /* preclude I_COMPLETE */ + } + + /* note next offset and last dentry name */ + fi->offset = fi->next_offset; + fi->last_readdir = req; + + if (req->r_reply_info.dir_end) { + kfree(fi->last_name); + fi->last_name = NULL; + fi->next_offset = 0; + } else { + rinfo = &req->r_reply_info; + err = note_last_dentry(fi, + rinfo->dir_dname[rinfo->dir_nr-1], + rinfo->dir_dname_len[rinfo->dir_nr-1]); + if (err) + return err; + fi->next_offset += rinfo->dir_nr; + } + } + + rinfo = &fi->last_readdir->r_reply_info; + dout("readdir frag %x num %d off %d chunkoff %d\n", frag, + rinfo->dir_nr, off, fi->offset); + while (off - fi->offset >= 0 && off - fi->offset < rinfo->dir_nr) { + u64 pos = ceph_make_fpos(frag, off); + struct ceph_mds_reply_inode *in = + rinfo->dir_in[off - fi->offset].in; + dout("readdir off %d (%d/%d) -> %lld '%.*s' %p\n", + off, off - fi->offset, rinfo->dir_nr, pos, + rinfo->dir_dname_len[off - fi->offset], + rinfo->dir_dname[off - fi->offset], in); + BUG_ON(!in); + ftype = le32_to_cpu(in->mode) >> 12; + if (filldir(dirent, + rinfo->dir_dname[off - fi->offset], + rinfo->dir_dname_len[off - fi->offset], + pos, + le64_to_cpu(in->ino), + ftype) < 0) { + dout("filldir stopping us...\n"); + return 0; + } + off++; + filp->f_pos = pos + 1; + } + + if (fi->last_name) { + ceph_mdsc_put_request(fi->last_readdir); + fi->last_readdir = NULL; + goto more; + } + + /* more frags? */ + if (!ceph_frag_is_rightmost(frag)) { + frag = ceph_frag_next(frag); + off = 0; + filp->f_pos = ceph_make_fpos(frag, off); + dout("readdir next frag is %x\n", frag); + goto more; + } + fi->at_end = 1; + + /* + * if dir_release_count still matches the dir, no dentries + * were released during the whole readdir, and we should have + * the complete dir contents in our cache. + */ + spin_lock(&inode->i_lock); + if (ci->i_release_count == fi->dir_release_count) { + dout(" marking %p complete\n", inode); + ci->i_ceph_flags |= CEPH_I_COMPLETE; + ci->i_max_offset = filp->f_pos; + } + spin_unlock(&inode->i_lock); + + dout("readdir %p filp %p done.\n", inode, filp); + return 0; +} + +static void reset_readdir(struct ceph_file_info *fi) +{ + if (fi->last_readdir) { + ceph_mdsc_put_request(fi->last_readdir); + fi->last_readdir = NULL; + } + kfree(fi->last_name); + fi->next_offset = 2; /* compensate for . and .. */ + if (fi->dentry) { + dput(fi->dentry); + fi->dentry = NULL; + } + fi->at_end = 0; +} + +static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin) +{ + struct ceph_file_info *fi = file->private_data; + struct inode *inode = file->f_mapping->host; + loff_t old_offset = offset; + loff_t retval; + + mutex_lock(&inode->i_mutex); + switch (origin) { + case SEEK_END: + offset += inode->i_size + 2; /* FIXME */ + break; + case SEEK_CUR: + offset += file->f_pos; + } + retval = -EINVAL; + if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) { + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_version = 0; + fi->at_end = 0; + } + retval = offset; + + /* + * discard buffered readdir content on seekdir(0), or + * seek to new frag, or seek prior to current chunk. + */ + if (offset == 0 || + fpos_frag(offset) != fpos_frag(old_offset) || + fpos_off(offset) < fi->offset) { + dout("dir_llseek dropping %p content\n", file); + reset_readdir(fi); + } + + /* bump dir_release_count if we did a forward seek */ + if (offset > old_offset) + fi->dir_release_count--; + } + mutex_unlock(&inode->i_mutex); + return retval; +} + +/* + * Process result of a lookup/open request. + * + * Mainly, make sure we return the final req->r_dentry (if it already + * existed) in place of the original VFS-provided dentry when they + * differ. + * + * Gracefully handle the case where the MDS replies with -ENOENT and + * no trace (which it may do, at its discretion, e.g., if it doesn't + * care to issue a lease on the negative dentry). + */ +struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, + struct dentry *dentry, int err) +{ + struct ceph_client *client = ceph_client(dentry->d_sb); + struct inode *parent = dentry->d_parent->d_inode; + + /* .snap dir? */ + if (err == -ENOENT && + ceph_vino(parent).ino != CEPH_INO_ROOT && /* no .snap in root dir */ + strcmp(dentry->d_name.name, client->mount_args.snapdir_name) == 0) { + struct inode *inode = ceph_get_snapdir(parent); + dout("ENOENT on snapdir %p '%.*s', linking to snapdir %p\n", + dentry, dentry->d_name.len, dentry->d_name.name, inode); + d_add(dentry, inode); + err = 0; + } + + if (err == -ENOENT) { + /* no trace? */ + err = 0; + if (!req->r_reply_info.head->is_dentry) { + dout("ENOENT and no trace, dentry %p inode %p\n", + dentry, dentry->d_inode); + if (dentry->d_inode) { + d_drop(dentry); + err = -ENOENT; + } else { + d_add(dentry, NULL); + } + } + } + if (err) + dentry = ERR_PTR(err); + else if (dentry != req->r_dentry) + dentry = dget(req->r_dentry); /* we got spliced */ + else + dentry = NULL; + return dentry; +} + +/* + * Look up a single dir entry. If there is a lookup intent, inform + * the MDS so that it gets our 'caps wanted' value in a single op. + */ +static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct ceph_client *client = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + int op; + int err; + + dout("lookup %p dentry %p '%.*s'\n", + dir, dentry, dentry->d_name.len, dentry->d_name.name); + + if (dentry->d_name.len > NAME_MAX) + return ERR_PTR(-ENAMETOOLONG); + + err = ceph_init_dentry(dentry); + if (err < 0) + return ERR_PTR(err); + + /* open (but not create!) intent? */ + if (nd && + (nd->flags & LOOKUP_OPEN) && + (nd->flags & LOOKUP_CONTINUE) == 0 && /* only open last component */ + !(nd->intent.open.flags & O_CREAT)) { + int mode = nd->intent.open.create_mode & ~current->fs->umask; + return ceph_lookup_open(dir, dentry, nd, mode, 1); + } + + /* can we conclude ENOENT locally? */ + if (dentry->d_inode == NULL) { + struct ceph_inode_info *ci = ceph_inode(dir); + struct ceph_dentry_info *di = ceph_dentry(dentry); + + spin_lock(&dir->i_lock); + dout(" dir %p flags are %d\n", dir, ci->i_ceph_flags); + if (strncmp(dentry->d_name.name, + client->mount_args.snapdir_name, + dentry->d_name.len) && + (ci->i_ceph_flags & CEPH_I_COMPLETE) && + (__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) { + di->offset = ci->i_max_offset++; + spin_unlock(&dir->i_lock); + dout(" dir %p complete, -ENOENT\n", dir); + d_add(dentry, NULL); + di->lease_shared_gen = ci->i_shared_gen; + return NULL; + } + spin_unlock(&dir->i_lock); + } + + op = ceph_snap(dir) == CEPH_SNAPDIR ? + CEPH_MDS_OP_LOOKUPSNAP : CEPH_MDS_OP_LOOKUP; + req = ceph_mdsc_create_request(mdsc, op, USE_ANY_MDS); + if (IS_ERR(req)) + return ERR_PTR(PTR_ERR(req)); + req->r_dentry = dget(dentry); + req->r_num_caps = 2; + /* we only need inode linkage */ + req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE); + req->r_locked_dir = dir; + err = ceph_mdsc_do_request(mdsc, NULL, req); + dentry = ceph_finish_lookup(req, dentry, err); + ceph_mdsc_put_request(req); /* will dput(dentry) */ + dout("lookup result=%p\n", dentry); + return dentry; +} + +/* + * If we do a create but get no trace back from the MDS, follow up with + * a lookup (the VFS expects us to link up the provided dentry). + */ +int ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry) +{ + struct dentry *result = ceph_lookup(dir, dentry, NULL); + + if (result && !IS_ERR(result)) { + /* + * We created the item, then did a lookup, and found + * it was already linked to another inode we already + * had in our cache (and thus got spliced). Link our + * dentry to that inode, but don't hash it, just in + * case the VFS wants to dereference it. + */ + BUG_ON(!result->d_inode); + d_instantiate(dentry, result->d_inode); + return 0; + } + return PTR_ERR(result); +} + +static int ceph_mknod(struct inode *dir, struct dentry *dentry, + int mode, dev_t rdev) +{ + struct ceph_client *client = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + int err; + + if (ceph_snap(dir) != CEPH_NOSNAP) + return -EROFS; + + dout("mknod in dir %p dentry %p mode 0%o rdev %d\n", + dir, dentry, mode, rdev); + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS); + if (IS_ERR(req)) { + d_drop(dentry); + return PTR_ERR(req); + } + req->r_dentry = dget(dentry); + req->r_num_caps = 2; + req->r_locked_dir = dir; + req->r_args.mknod.mode = cpu_to_le32(mode); + req->r_args.mknod.rdev = cpu_to_le32(rdev); + req->r_dentry_drop = CEPH_CAP_FILE_SHARED; + req->r_dentry_unless = CEPH_CAP_FILE_EXCL; + err = ceph_mdsc_do_request(mdsc, dir, req); + if (!err && !req->r_reply_info.head->is_dentry) + err = ceph_handle_notrace_create(dir, dentry); + ceph_mdsc_put_request(req); + if (err) + d_drop(dentry); + return err; +} + +static int ceph_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + dout("create in dir %p dentry %p name '%.*s'\n", + dir, dentry, dentry->d_name.len, dentry->d_name.name); + + if (ceph_snap(dir) != CEPH_NOSNAP) + return -EROFS; + + if (nd) { + BUG_ON((nd->flags & LOOKUP_OPEN) == 0); + dentry = ceph_lookup_open(dir, dentry, nd, mode, 0); + /* hrm, what should i do here if we get aliased? */ + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + return 0; + } + + /* fall back to mknod */ + return ceph_mknod(dir, dentry, (mode & ~S_IFMT) | S_IFREG, 0); +} + +static int ceph_symlink(struct inode *dir, struct dentry *dentry, + const char *dest) +{ + struct ceph_client *client = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + int err; + + if (ceph_snap(dir) != CEPH_NOSNAP) + return -EROFS; + + dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest); + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS); + if (IS_ERR(req)) { + d_drop(dentry); + return PTR_ERR(req); + } + req->r_dentry = dget(dentry); + req->r_num_caps = 2; + req->r_path2 = kstrdup(dest, GFP_NOFS); + req->r_locked_dir = dir; + req->r_dentry_drop = CEPH_CAP_FILE_SHARED; + req->r_dentry_unless = CEPH_CAP_FILE_EXCL; + err = ceph_mdsc_do_request(mdsc, dir, req); + if (!err && !req->r_reply_info.head->is_dentry) + err = ceph_handle_notrace_create(dir, dentry); + ceph_mdsc_put_request(req); + if (err) + d_drop(dentry); + return err; +} + +static int ceph_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct ceph_client *client = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + int err = -EROFS; + int op; + + if (ceph_snap(dir) == CEPH_SNAPDIR) { + /* mkdir .snap/foo is a MKSNAP */ + op = CEPH_MDS_OP_MKSNAP; + dout("mksnap dir %p snap '%.*s' dn %p\n", dir, + dentry->d_name.len, dentry->d_name.name, dentry); + } else if (ceph_snap(dir) == CEPH_NOSNAP) { + dout("mkdir dir %p dn %p mode 0%o\n", dir, dentry, mode); + op = CEPH_MDS_OP_MKDIR; + } else { + goto out; + } + req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto out; + } + + req->r_dentry = dget(dentry); + req->r_num_caps = 2; + req->r_locked_dir = dir; + req->r_args.mkdir.mode = cpu_to_le32(mode); + req->r_dentry_drop = CEPH_CAP_FILE_SHARED; + req->r_dentry_unless = CEPH_CAP_FILE_EXCL; + err = ceph_mdsc_do_request(mdsc, dir, req); + if (!err && !req->r_reply_info.head->is_dentry) + err = ceph_handle_notrace_create(dir, dentry); + ceph_mdsc_put_request(req); +out: + if (err < 0) + d_drop(dentry); + return err; +} + +static int ceph_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct ceph_client *client = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + int err; + + if (ceph_snap(dir) != CEPH_NOSNAP) + return -EROFS; + + dout("link in dir %p old_dentry %p dentry %p\n", dir, + old_dentry, dentry); + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS); + if (IS_ERR(req)) { + d_drop(dentry); + return PTR_ERR(req); + } + req->r_dentry = dget(dentry); + req->r_num_caps = 2; + req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */ + req->r_locked_dir = dir; + req->r_dentry_drop = CEPH_CAP_FILE_SHARED; + req->r_dentry_unless = CEPH_CAP_FILE_EXCL; + err = ceph_mdsc_do_request(mdsc, dir, req); + if (err) + d_drop(dentry); + else if (!req->r_reply_info.head->is_dentry) + d_instantiate(dentry, igrab(old_dentry->d_inode)); + ceph_mdsc_put_request(req); + return err; +} + +/* + * For a soon-to-be unlinked file, drop the AUTH_RDCACHE caps. If it + * looks like the link count will hit 0, drop any other caps (other + * than PIN) we don't specifically want (due to the file still being + * open). + */ +static int drop_caps_for_unlink(struct inode *inode) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int drop = CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL; + + spin_lock(&inode->i_lock); + if (inode->i_nlink == 1) { + drop |= ~(__ceph_caps_wanted(ci) | CEPH_CAP_PIN); + ci->i_ceph_flags |= CEPH_I_NODELAY; + } + spin_unlock(&inode->i_lock); + return drop; +} + +/* + * rmdir and unlink are differ only by the metadata op code + */ +static int ceph_unlink(struct inode *dir, struct dentry *dentry) +{ + struct ceph_client *client = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct inode *inode = dentry->d_inode; + struct ceph_mds_request *req; + int err = -EROFS; + int op; + + if (ceph_snap(dir) == CEPH_SNAPDIR) { + /* rmdir .snap/foo is RMSNAP */ + dout("rmsnap dir %p '%.*s' dn %p\n", dir, dentry->d_name.len, + dentry->d_name.name, dentry); + op = CEPH_MDS_OP_RMSNAP; + } else if (ceph_snap(dir) == CEPH_NOSNAP) { + dout("unlink/rmdir dir %p dn %p inode %p\n", + dir, dentry, inode); + op = ((dentry->d_inode->i_mode & S_IFMT) == S_IFDIR) ? + CEPH_MDS_OP_RMDIR : CEPH_MDS_OP_UNLINK; + } else + goto out; + req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto out; + } + req->r_dentry = dget(dentry); + req->r_num_caps = 2; + req->r_locked_dir = dir; + req->r_dentry_drop = CEPH_CAP_FILE_SHARED; + req->r_dentry_unless = CEPH_CAP_FILE_EXCL; + req->r_inode_drop = drop_caps_for_unlink(inode); + err = ceph_mdsc_do_request(mdsc, dir, req); + if (!err && !req->r_reply_info.head->is_dentry) + d_delete(dentry); + ceph_mdsc_put_request(req); +out: + return err; +} + +static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct ceph_client *client = ceph_sb_to_client(old_dir->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + int err; + + if (ceph_snap(old_dir) != ceph_snap(new_dir)) + return -EXDEV; + if (ceph_snap(old_dir) != CEPH_NOSNAP || + ceph_snap(new_dir) != CEPH_NOSNAP) + return -EROFS; + dout("rename dir %p dentry %p to dir %p dentry %p\n", + old_dir, old_dentry, new_dir, new_dentry); + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_RENAME, USE_AUTH_MDS); + if (IS_ERR(req)) + return PTR_ERR(req); + req->r_dentry = dget(new_dentry); + req->r_num_caps = 2; + req->r_old_dentry = dget(old_dentry); + req->r_locked_dir = new_dir; + req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED; + req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL; + req->r_dentry_drop = CEPH_CAP_FILE_SHARED; + req->r_dentry_unless = CEPH_CAP_FILE_EXCL; + /* release LINK_RDCACHE on source inode (mds will lock it) */ + req->r_old_inode_drop = CEPH_CAP_LINK_SHARED; + if (new_dentry->d_inode) + req->r_inode_drop = drop_caps_for_unlink(new_dentry->d_inode); + err = ceph_mdsc_do_request(mdsc, old_dir, req); + if (!err && !req->r_reply_info.head->is_dentry) { + /* + * Normally d_move() is done by fill_trace (called by + * do_request, above). If there is no trace, we need + * to do it here. + */ + d_move(old_dentry, new_dentry); + } + ceph_mdsc_put_request(req); + return err; +} + + +/* + * Check if dentry lease is valid. If not, delete the lease. Try to + * renew if the least is more than half up. + */ +static int dentry_lease_is_valid(struct dentry *dentry) +{ + struct ceph_dentry_info *di; + struct ceph_mds_session *s; + int valid = 0; + u32 gen; + unsigned long ttl; + struct ceph_mds_session *session = NULL; + struct inode *dir = NULL; + u32 seq = 0; + + spin_lock(&dentry->d_lock); + di = ceph_dentry(dentry); + if (di && di->lease_session) { + s = di->lease_session; + spin_lock(&s->s_cap_lock); + gen = s->s_cap_gen; + ttl = s->s_cap_ttl; + spin_unlock(&s->s_cap_lock); + + if (di->lease_gen == gen && + time_before(jiffies, dentry->d_time) && + time_before(jiffies, ttl)) { + valid = 1; + if (di->lease_renew_after && + time_after(jiffies, di->lease_renew_after)) { + /* we should renew */ + dir = dentry->d_parent->d_inode; + session = ceph_get_mds_session(s); + seq = di->lease_seq; + di->lease_renew_after = 0; + di->lease_renew_from = jiffies; + } + } else { + __ceph_mdsc_drop_dentry_lease(dentry); + } + } + spin_unlock(&dentry->d_lock); + + if (session) { + ceph_mdsc_lease_send_msg(session, dir, dentry, + CEPH_MDS_LEASE_RENEW, seq); + ceph_put_mds_session(session); + } + dout("dentry_lease_is_valid - dentry %p = %d\n", dentry, valid); + return valid; +} + +/* + * Check if directory-wide content lease/cap is valid. + */ +static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry) +{ + struct ceph_inode_info *ci = ceph_inode(dir); + struct ceph_dentry_info *di = ceph_dentry(dentry); + int valid = 0; + + spin_lock(&dir->i_lock); + if (ci->i_shared_gen == di->lease_shared_gen) + valid = __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1); + spin_unlock(&dir->i_lock); + dout("dir_lease_is_valid dir %p v%u dentry %p v%u = %d\n", + dir, (unsigned)ci->i_shared_gen, dentry, + (unsigned)di->lease_shared_gen, valid); + return valid; +} + +/* + * Check if cached dentry can be trusted. + */ +static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + struct inode *dir = dentry->d_parent->d_inode; + + dout("d_revalidate %p '%.*s' inode %p\n", dentry, + dentry->d_name.len, dentry->d_name.name, dentry->d_inode); + + /* always trust cached snapped dentries, snapdir dentry */ + if (ceph_snap(dir) != CEPH_NOSNAP) { + dout("d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry, + dentry->d_name.len, dentry->d_name.name, dentry->d_inode); + goto out_touch; + } + if (dentry->d_inode && ceph_snap(dentry->d_inode) == CEPH_SNAPDIR) + goto out_touch; + + if (dentry_lease_is_valid(dentry) || + dir_lease_is_valid(dir, dentry)) + goto out_touch; + + dout("d_revalidate %p invalid\n", dentry); + d_drop(dentry); + return 0; +out_touch: + ceph_dentry_lru_touch(dentry); + return 1; +} + +/* + * When a dentry is released, clear the dir I_COMPLETE if it was part + * of the current dir gen. + */ +static void ceph_dentry_release(struct dentry *dentry) +{ + struct ceph_dentry_info *di = ceph_dentry(dentry); + struct inode *parent_inode = dentry->d_parent->d_inode; + + if (parent_inode) { + struct ceph_inode_info *ci = ceph_inode(parent_inode); + + spin_lock(&parent_inode->i_lock); + if (ci->i_shared_gen == di->lease_shared_gen) { + dout(" clearing %p complete (d_release)\n", + parent_inode); + ci->i_ceph_flags &= ~CEPH_I_COMPLETE; + ci->i_release_count++; + } + spin_unlock(&parent_inode->i_lock); + } + if (di) { + ceph_dentry_lru_del(dentry); + if (di->lease_session) + ceph_put_mds_session(di->lease_session); + kmem_cache_free(ceph_dentry_cachep, di); + dentry->d_fsdata = NULL; + } +} + +static int ceph_snapdir_d_revalidate(struct dentry *dentry, + struct nameidata *nd) +{ + /* + * Eventually, we'll want to revalidate snapped metadata + * too... probably... + */ + return 1; +} + + + +/* + * read() on a dir. This weird interface hack only works if mounted + * with '-o dirstat'. + */ +static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, + loff_t *ppos) +{ + struct ceph_file_info *cf = file->private_data; + struct inode *inode = file->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + int left; + + if (!ceph_test_opt(ceph_client(inode->i_sb), DIRSTAT)) + return -EISDIR; + + if (!cf->dir_info) { + cf->dir_info = kmalloc(1024, GFP_NOFS); + if (!cf->dir_info) + return -ENOMEM; + cf->dir_info_len = + sprintf(cf->dir_info, + "entries: %20lld\n" + " files: %20lld\n" + " subdirs: %20lld\n" + "rentries: %20lld\n" + " rfiles: %20lld\n" + " rsubdirs: %20lld\n" + "rbytes: %20lld\n" + "rctime: %10ld.%09ld\n", + ci->i_files + ci->i_subdirs, + ci->i_files, + ci->i_subdirs, + ci->i_rfiles + ci->i_rsubdirs, + ci->i_rfiles, + ci->i_rsubdirs, + ci->i_rbytes, + (long)ci->i_rctime.tv_sec, + (long)ci->i_rctime.tv_nsec); + } + + if (*ppos >= cf->dir_info_len) + return 0; + size = min_t(unsigned, size, cf->dir_info_len-*ppos); + left = copy_to_user(buf, cf->dir_info + *ppos, size); + if (left == size) + return -EFAULT; + *ppos += (size - left); + return size - left; +} + +/* + * an fsync() on a dir will wait for any uncommitted directory + * operations to commit. + */ +static int ceph_dir_fsync(struct file *file, struct dentry *dentry, + int datasync) +{ + struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct list_head *head = &ci->i_unsafe_dirops; + struct ceph_mds_request *req; + u64 last_tid; + int ret = 0; + + dout("dir_fsync %p\n", inode); + spin_lock(&ci->i_unsafe_lock); + if (list_empty(head)) + goto out; + + req = list_entry(head->prev, + struct ceph_mds_request, r_unsafe_dir_item); + last_tid = req->r_tid; + + do { + ceph_mdsc_get_request(req); + spin_unlock(&ci->i_unsafe_lock); + dout("dir_fsync %p wait on tid %llu (until %llu)\n", + inode, req->r_tid, last_tid); + if (req->r_timeout) { + ret = wait_for_completion_timeout( + &req->r_safe_completion, req->r_timeout); + if (ret > 0) + ret = 0; + else if (ret == 0) + ret = -EIO; /* timed out */ + } else { + wait_for_completion(&req->r_safe_completion); + } + spin_lock(&ci->i_unsafe_lock); + ceph_mdsc_put_request(req); + + if (ret || list_empty(head)) + break; + req = list_entry(head->next, + struct ceph_mds_request, r_unsafe_dir_item); + } while (req->r_tid < last_tid); +out: + spin_unlock(&ci->i_unsafe_lock); + return ret; +} + +/* + * We maintain a private dentry LRU. + * + * FIXME: this needs to be changed to a per-mds lru to be useful. + */ +void ceph_dentry_lru_add(struct dentry *dn) +{ + struct ceph_dentry_info *di = ceph_dentry(dn); + struct ceph_mds_client *mdsc; + dout("dentry_lru_add %p %p\t%.*s\n", + di, dn, dn->d_name.len, dn->d_name.name); + + if (di) { + mdsc = &ceph_client(dn->d_sb)->mdsc; + spin_lock(&mdsc->dentry_lru_lock); + list_add_tail(&di->lru, &mdsc->dentry_lru); + mdsc->num_dentry++; + spin_unlock(&mdsc->dentry_lru_lock); + } +} + +void ceph_dentry_lru_touch(struct dentry *dn) +{ + struct ceph_dentry_info *di = ceph_dentry(dn); + struct ceph_mds_client *mdsc; + dout("dentry_lru_touch %p %p\t%.*s\n", + di, dn, dn->d_name.len, dn->d_name.name); + + if (di) { + mdsc = &ceph_client(dn->d_sb)->mdsc; + spin_lock(&mdsc->dentry_lru_lock); + list_move_tail(&di->lru, &mdsc->dentry_lru); + spin_unlock(&mdsc->dentry_lru_lock); + } +} + +void ceph_dentry_lru_del(struct dentry *dn) +{ + struct ceph_dentry_info *di = ceph_dentry(dn); + struct ceph_mds_client *mdsc; + + dout("dentry_lru_del %p %p\t%.*s\n", + di, dn, dn->d_name.len, dn->d_name.name); + if (di) { + mdsc = &ceph_client(dn->d_sb)->mdsc; + spin_lock(&mdsc->dentry_lru_lock); + list_del_init(&di->lru); + mdsc->num_dentry--; + spin_unlock(&mdsc->dentry_lru_lock); + } +} + +const struct file_operations ceph_dir_fops = { + .read = ceph_read_dir, + .readdir = ceph_readdir, + .llseek = ceph_dir_llseek, + .open = ceph_open, + .release = ceph_release, + .unlocked_ioctl = ceph_ioctl, + .fsync = ceph_dir_fsync, +}; + +const struct inode_operations ceph_dir_iops = { + .lookup = ceph_lookup, + .permission = ceph_permission, + .getattr = ceph_getattr, + .setattr = ceph_setattr, + .setxattr = ceph_setxattr, + .getxattr = ceph_getxattr, + .listxattr = ceph_listxattr, + .removexattr = ceph_removexattr, + .mknod = ceph_mknod, + .symlink = ceph_symlink, + .mkdir = ceph_mkdir, + .link = ceph_link, + .unlink = ceph_unlink, + .rmdir = ceph_unlink, + .rename = ceph_rename, + .create = ceph_create, +}; + +struct dentry_operations ceph_dentry_ops = { + .d_revalidate = ceph_d_revalidate, + .d_release = ceph_dentry_release, +}; + +struct dentry_operations ceph_snapdir_dentry_ops = { + .d_revalidate = ceph_snapdir_d_revalidate, +}; + +struct dentry_operations ceph_snap_dentry_ops = { +}; -- cgit v0.10.2 From 124e68e74099090e28da5518f73fda878e7e8232 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:08 -0700 Subject: ceph: file operations File open and close operations, and read and write methods that ensure we have obtained the proper capabilities from the MDS cluster before performing IO on a file. We take references on held capabilities for the duration of the read/write to avoid prematurely releasing them back to the MDS. We implement two main paths for read and write: one that is buffered (and uses generic_aio_{read,write}), and one that is fully synchronous and blocking (operating either on a __user pointer or, if O_DIRECT, directly on user pages). Signed-off-by: Sage Weil diff --git a/fs/ceph/file.c b/fs/ceph/file.c new file mode 100644 index 0000000..1bd57c8 --- /dev/null +++ b/fs/ceph/file.c @@ -0,0 +1,904 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include + +#include "super.h" +#include "mds_client.h" + +/* + * Ceph file operations + * + * Implement basic open/close functionality, and implement + * read/write. + * + * We implement three modes of file I/O: + * - buffered uses the generic_file_aio_{read,write} helpers + * + * - synchronous is used when there is multi-client read/write + * sharing, avoids the page cache, and synchronously waits for an + * ack from the OSD. + * + * - direct io takes the variant of the sync path that references + * user pages directly. + * + * fsync() flushes and waits on dirty pages, but just queues metadata + * for writeback: since the MDS can recover size and mtime there is no + * need to wait for MDS acknowledgement. + */ + + +/* + * Prepare an open request. Preallocate ceph_cap to avoid an + * inopportune ENOMEM later. + */ +static struct ceph_mds_request * +prepare_open_request(struct super_block *sb, int flags, int create_mode) +{ + struct ceph_client *client = ceph_sb_to_client(sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + int want_auth = USE_ANY_MDS; + int op = (flags & O_CREAT) ? CEPH_MDS_OP_CREATE : CEPH_MDS_OP_OPEN; + + if (flags & (O_WRONLY|O_RDWR|O_CREAT|O_TRUNC)) + want_auth = USE_AUTH_MDS; + + req = ceph_mdsc_create_request(mdsc, op, want_auth); + if (IS_ERR(req)) + goto out; + req->r_fmode = ceph_flags_to_mode(flags); + req->r_args.open.flags = cpu_to_le32(flags); + req->r_args.open.mode = cpu_to_le32(create_mode); + req->r_args.open.preferred = -1; +out: + return req; +} + +/* + * initialize private struct file data. + * if we fail, clean up by dropping fmode reference on the ceph_inode + */ +static int ceph_init_file(struct inode *inode, struct file *file, int fmode) +{ + struct ceph_file_info *cf; + int ret = 0; + + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + case S_IFDIR: + dout("init_file %p %p 0%o (regular)\n", inode, file, + inode->i_mode); + cf = kmem_cache_alloc(ceph_file_cachep, GFP_NOFS | __GFP_ZERO); + if (cf == NULL) { + ceph_put_fmode(ceph_inode(inode), fmode); /* clean up */ + return -ENOMEM; + } + cf->fmode = fmode; + cf->next_offset = 2; + file->private_data = cf; + BUG_ON(inode->i_fop->release != ceph_release); + break; + + case S_IFLNK: + dout("init_file %p %p 0%o (symlink)\n", inode, file, + inode->i_mode); + ceph_put_fmode(ceph_inode(inode), fmode); /* clean up */ + break; + + default: + dout("init_file %p %p 0%o (special)\n", inode, file, + inode->i_mode); + /* + * we need to drop the open ref now, since we don't + * have .release set to ceph_release. + */ + ceph_put_fmode(ceph_inode(inode), fmode); /* clean up */ + BUG_ON(inode->i_fop->release == ceph_release); + + /* call the proper open fop */ + ret = inode->i_fop->open(inode, file); + } + return ret; +} + +/* + * If the filp already has private_data, that means the file was + * already opened by intent during lookup, and we do nothing. + * + * If we already have the requisite capabilities, we can satisfy + * the open request locally (no need to request new caps from the + * MDS). We do, however, need to inform the MDS (asynchronously) + * if our wanted caps set expands. + */ +int ceph_open(struct inode *inode, struct file *file) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_client *client = ceph_sb_to_client(inode->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_request *req; + struct ceph_file_info *cf = file->private_data; + struct inode *parent_inode = file->f_dentry->d_parent->d_inode; + int err; + int flags, fmode, wanted; + + if (cf) { + dout("open file %p is already opened\n", file); + return 0; + } + + /* filter out O_CREAT|O_EXCL; vfs did that already. yuck. */ + flags = file->f_flags & ~(O_CREAT|O_EXCL); + if (S_ISDIR(inode->i_mode)) + flags = O_DIRECTORY; /* mds likes to know */ + + dout("open inode %p ino %llx.%llx file %p flags %d (%d)\n", inode, + ceph_vinop(inode), file, flags, file->f_flags); + fmode = ceph_flags_to_mode(flags); + wanted = ceph_caps_for_mode(fmode); + + /* snapped files are read-only */ + if (ceph_snap(inode) != CEPH_NOSNAP && (file->f_mode & FMODE_WRITE)) + return -EROFS; + + /* trivially open snapdir */ + if (ceph_snap(inode) == CEPH_SNAPDIR) { + spin_lock(&inode->i_lock); + __ceph_get_fmode(ci, fmode); + spin_unlock(&inode->i_lock); + return ceph_init_file(inode, file, fmode); + } + + /* + * No need to block if we have any caps. Update wanted set + * asynchronously. + */ + spin_lock(&inode->i_lock); + if (__ceph_is_any_real_caps(ci)) { + int mds_wanted = __ceph_caps_mds_wanted(ci); + int issued = __ceph_caps_issued(ci, NULL); + + dout("open %p fmode %d want %s issued %s using existing\n", + inode, fmode, ceph_cap_string(wanted), + ceph_cap_string(issued)); + __ceph_get_fmode(ci, fmode); + spin_unlock(&inode->i_lock); + + /* adjust wanted? */ + if ((issued & wanted) != wanted && + (mds_wanted & wanted) != wanted && + ceph_snap(inode) != CEPH_SNAPDIR) + ceph_check_caps(ci, 0, NULL); + + return ceph_init_file(inode, file, fmode); + } else if (ceph_snap(inode) != CEPH_NOSNAP && + (ci->i_snap_caps & wanted) == wanted) { + __ceph_get_fmode(ci, fmode); + spin_unlock(&inode->i_lock); + return ceph_init_file(inode, file, fmode); + } + spin_unlock(&inode->i_lock); + + dout("open fmode %d wants %s\n", fmode, ceph_cap_string(wanted)); + req = prepare_open_request(inode->i_sb, flags, 0); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto out; + } + req->r_inode = igrab(inode); + req->r_num_caps = 1; + err = ceph_mdsc_do_request(mdsc, parent_inode, req); + if (!err) + err = ceph_init_file(inode, file, req->r_fmode); + ceph_mdsc_put_request(req); + dout("open result=%d on %llx.%llx\n", err, ceph_vinop(inode)); +out: + return err; +} + + +/* + * Do a lookup + open with a single request. + * + * If this succeeds, but some subsequent check in the vfs + * may_open() fails, the struct *file gets cleaned up (i.e. + * ceph_release gets called). So fear not! + */ +/* + * flags + * path_lookup_open -> LOOKUP_OPEN + * path_lookup_create -> LOOKUP_OPEN|LOOKUP_CREATE + */ +struct dentry *ceph_lookup_open(struct inode *dir, struct dentry *dentry, + struct nameidata *nd, int mode, + int locked_dir) +{ + struct ceph_client *client = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = &client->mdsc; + struct file *file = nd->intent.open.file; + struct inode *parent_inode = get_dentry_parent_inode(file->f_dentry); + struct ceph_mds_request *req; + int err; + int flags = nd->intent.open.flags - 1; /* silly vfs! */ + + dout("ceph_lookup_open dentry %p '%.*s' flags %d mode 0%o\n", + dentry, dentry->d_name.len, dentry->d_name.name, flags, mode); + + /* do the open */ + req = prepare_open_request(dir->i_sb, flags, mode); + if (IS_ERR(req)) + return ERR_PTR(PTR_ERR(req)); + req->r_dentry = dget(dentry); + req->r_num_caps = 2; + if (flags & O_CREAT) { + req->r_dentry_drop = CEPH_CAP_FILE_SHARED; + req->r_dentry_unless = CEPH_CAP_FILE_EXCL; + } + req->r_locked_dir = dir; /* caller holds dir->i_mutex */ + err = ceph_mdsc_do_request(mdsc, parent_inode, req); + dentry = ceph_finish_lookup(req, dentry, err); + if (!err && (flags & O_CREAT) && !req->r_reply_info.head->is_dentry) + err = ceph_handle_notrace_create(dir, dentry); + if (!err) + err = ceph_init_file(req->r_dentry->d_inode, file, + req->r_fmode); + ceph_mdsc_put_request(req); + dout("ceph_lookup_open result=%p\n", dentry); + return dentry; +} + +int ceph_release(struct inode *inode, struct file *file) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_file_info *cf = file->private_data; + + dout("release inode %p file %p\n", inode, file); + ceph_put_fmode(ci, cf->fmode); + if (cf->last_readdir) + ceph_mdsc_put_request(cf->last_readdir); + kfree(cf->last_name); + kfree(cf->dir_info); + dput(cf->dentry); + kmem_cache_free(ceph_file_cachep, cf); + return 0; +} + +/* + * build a vector of user pages + */ +static struct page **get_direct_page_vector(const char __user *data, + int num_pages, + loff_t off, size_t len) +{ + struct page **pages; + int rc; + + pages = kmalloc(sizeof(*pages) * num_pages, GFP_NOFS); + if (!pages) + return ERR_PTR(-ENOMEM); + + down_read(¤t->mm->mmap_sem); + rc = get_user_pages(current, current->mm, (unsigned long)data, + num_pages, 0, 0, pages, NULL); + up_read(¤t->mm->mmap_sem); + if (rc < 0) + goto fail; + return pages; + +fail: + kfree(pages); + return ERR_PTR(rc); +} + +static void put_page_vector(struct page **pages, int num_pages) +{ + int i; + + for (i = 0; i < num_pages; i++) + put_page(pages[i]); + kfree(pages); +} + +void ceph_release_page_vector(struct page **pages, int num_pages) +{ + int i; + + for (i = 0; i < num_pages; i++) + __free_pages(pages[i], 0); + kfree(pages); +} + +/* + * allocate a vector new pages + */ +static struct page **alloc_page_vector(int num_pages) +{ + struct page **pages; + int i; + + pages = kmalloc(sizeof(*pages) * num_pages, GFP_NOFS); + if (!pages) + return ERR_PTR(-ENOMEM); + for (i = 0; i < num_pages; i++) { + pages[i] = alloc_page(GFP_NOFS); + if (pages[i] == NULL) { + ceph_release_page_vector(pages, i); + return ERR_PTR(-ENOMEM); + } + } + return pages; +} + +/* + * copy user data into a page vector + */ +static int copy_user_to_page_vector(struct page **pages, + const char __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, PAGE_CACHE_SIZE-po, left); + bad = copy_from_user(page_address(pages[i]) + po, data, l); + if (bad == l) + return -EFAULT; + data += l - bad; + left -= l - bad; + if (po) { + po += l - bad; + if (po == PAGE_CACHE_SIZE) + po = 0; + } + } + return len; +} + +/* + * copy user data from a page vector into a user pointer + */ +static int copy_page_vector_to_user(struct page **pages, char __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; +} + +/* + * Zero an extent within a page vector. Offset is relative to the + * start of the first page. + */ +static void zero_page_vector_range(int off, int len, struct page **pages) +{ + int i = off >> PAGE_CACHE_SHIFT; + + dout("zero_page_vector_page %u~%u\n", off, len); + BUG_ON(len < PAGE_CACHE_SIZE); + + /* leading partial page? */ + if (off & ~PAGE_CACHE_MASK) { + dout("zeroing %d %p head from %d\n", i, pages[i], + (int)(off & ~PAGE_CACHE_MASK)); + zero_user_segment(pages[i], off & ~PAGE_CACHE_MASK, + PAGE_CACHE_SIZE); + off += PAGE_CACHE_SIZE; + off &= PAGE_CACHE_MASK; + i++; + } + while (len >= PAGE_CACHE_SIZE) { + dout("zeroing %d %p\n", i, pages[i]); + zero_user_segment(pages[i], 0, PAGE_CACHE_SIZE); + off += PAGE_CACHE_SIZE; + len -= PAGE_CACHE_SIZE; + i++; + } + /* trailing partial page? */ + if (len) { + dout("zeroing %d %p tail to %d\n", i, pages[i], (int)len); + zero_user_segment(pages[i], 0, len); + } +} + + +/* + * Read a range of bytes striped over one or more objects. Iterate over + * objects we stripe over. (That's not atomic, but good enough for now.) + * + * If we get a short result from the OSD, check against i_size; we need to + * only return a short read to the caller if we hit EOF. + */ +static int striped_read(struct inode *inode, + u64 off, u64 len, + struct page **pages, int num_pages) +{ + struct ceph_client *client = ceph_inode_to_client(inode); + struct ceph_inode_info *ci = ceph_inode(inode); + u64 pos, this_len; + int page_off = off & ~PAGE_CACHE_SIZE; /* first byte's offset in page */ + int left, pages_left; + int read; + struct page **page_pos; + int ret; + bool hit_stripe, was_short; + + /* + * we may need to do multiple reads. not atomic, unfortunately. + */ + pos = off; + left = len; + page_pos = pages; + pages_left = num_pages; + read = 0; + +more: + this_len = left; + ret = ceph_osdc_readpages(&client->osdc, ceph_vino(inode), + &ci->i_layout, pos, &this_len, + ci->i_truncate_seq, + ci->i_truncate_size, + page_pos, pages_left); + hit_stripe = this_len < left; + was_short = ret >= 0 && ret < this_len; + if (ret == -ENOENT) + ret = 0; + dout("striped_read %llu~%u (read %u) got %d%s%s\n", pos, left, read, + ret, hit_stripe ? " HITSTRIPE" : "", was_short ? " SHORT" : ""); + + if (ret > 0) { + int didpages = + ((pos & ~PAGE_CACHE_MASK) + ret) >> PAGE_CACHE_SHIFT; + + if (read < pos - off) { + dout(" zero gap %llu to %llu\n", off + read, pos); + zero_page_vector_range(page_off + read, + pos - off - read, pages); + } + pos += ret; + read = pos - off; + left -= ret; + page_pos += didpages; + pages_left -= didpages; + + /* hit stripe? */ + if (left && hit_stripe) + goto more; + } + + if (was_short) { + /* was original extent fully inside i_size? */ + if (pos + left <= inode->i_size) { + dout("zero tail\n"); + zero_page_vector_range(page_off + read, len - read, + pages); + goto out; + } + + /* check i_size */ + ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE); + if (ret < 0) + goto out; + + /* hit EOF? */ + if (pos >= inode->i_size) + goto out; + + goto more; + } + +out: + if (ret >= 0) + ret = read; + dout("striped_read returns %d\n", ret); + return ret; +} + +/* + * Completely synchronous read and write methods. Direct from __user + * buffer to osd, or directly to user pages (if O_DIRECT). + * + * If the read spans object boundary, just do multiple reads. + */ +static ssize_t ceph_sync_read(struct file *file, char __user *data, + unsigned len, loff_t *poff) +{ + struct inode *inode = file->f_dentry->d_inode; + struct page **pages; + u64 off = *poff; + int num_pages = calc_pages_for(off, len); + int ret; + + dout("sync_read on file %p %llu~%u %s\n", file, off, len, + (file->f_flags & O_DIRECT) ? "O_DIRECT" : ""); + + if (file->f_flags & O_DIRECT) { + pages = get_direct_page_vector(data, num_pages, off, len); + + /* + * flush any page cache pages in this range. this + * will make concurrent normal and O_DIRECT io slow, + * but it will at least behave sensibly when they are + * in sequence. + */ + filemap_write_and_wait(inode->i_mapping); + } else { + pages = alloc_page_vector(num_pages); + } + if (IS_ERR(pages)) + return PTR_ERR(pages); + + ret = striped_read(inode, off, len, pages, num_pages); + + if (ret >= 0 && (file->f_flags & O_DIRECT) == 0) + ret = copy_page_vector_to_user(pages, data, off, ret); + if (ret >= 0) + *poff = off + ret; + + if (file->f_flags & O_DIRECT) + put_page_vector(pages, num_pages); + else + ceph_release_page_vector(pages, num_pages); + dout("sync_read result %d\n", ret); + return ret; +} + +/* + * Write commit callback, called if we requested both an ACK and + * ONDISK commit reply from the OSD. + */ +static void sync_write_commit(struct ceph_osd_request *req, + struct ceph_msg *msg) +{ + struct ceph_inode_info *ci = ceph_inode(req->r_inode); + + dout("sync_write_commit %p tid %llu\n", req, req->r_tid); + spin_lock(&ci->i_unsafe_lock); + list_del_init(&req->r_unsafe_item); + spin_unlock(&ci->i_unsafe_lock); + ceph_put_cap_refs(ci, CEPH_CAP_FILE_WR); +} + +/* + * Synchronous write, straight from __user pointer or user pages (if + * O_DIRECT). + * + * If write spans object boundary, just do multiple writes. (For a + * correct atomic write, we should e.g. take write locks on all + * objects, rollback on failure, etc.) + */ +static ssize_t ceph_sync_write(struct file *file, const char __user *data, + size_t left, loff_t *offset) +{ + struct inode *inode = file->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_client *client = ceph_inode_to_client(inode); + struct ceph_osd_request *req; + struct page **pages; + int num_pages; + long long unsigned pos; + u64 len; + int written = 0; + int flags; + int do_sync = 0; + int check_caps = 0; + int ret; + struct timespec mtime = CURRENT_TIME; + + if (ceph_snap(file->f_dentry->d_inode) != CEPH_NOSNAP) + return -EROFS; + + dout("sync_write on file %p %lld~%u %s\n", file, *offset, + (unsigned)left, (file->f_flags & O_DIRECT) ? "O_DIRECT" : ""); + + if (file->f_flags & O_APPEND) + pos = i_size_read(inode); + else + pos = *offset; + + flags = CEPH_OSD_FLAG_ORDERSNAP | + CEPH_OSD_FLAG_ONDISK | + CEPH_OSD_FLAG_WRITE; + if ((file->f_flags & (O_SYNC|O_DIRECT)) == 0) + flags |= CEPH_OSD_FLAG_ACK; + else + do_sync = 1; + + /* + * we may need to do multiple writes here if we span an object + * boundary. this isn't atomic, unfortunately. :( + */ +more: + len = left; + req = ceph_osdc_new_request(&client->osdc, &ci->i_layout, + ceph_vino(inode), pos, &len, + CEPH_OSD_OP_WRITE, flags, + ci->i_snap_realm->cached_context, + do_sync, + ci->i_truncate_seq, ci->i_truncate_size, + &mtime, false, 2); + if (IS_ERR(req)) + return PTR_ERR(req); + + num_pages = calc_pages_for(pos, len); + + if (file->f_flags & O_DIRECT) { + pages = get_direct_page_vector(data, num_pages, pos, len); + if (IS_ERR(pages)) { + ret = PTR_ERR(pages); + goto out; + } + + /* + * throw out any page cache pages in this range. this + * may block. + */ + truncate_inode_pages_range(inode->i_mapping, pos, pos+len); + } else { + pages = alloc_page_vector(num_pages); + if (IS_ERR(pages)) { + ret = PTR_ERR(pages); + goto out; + } + ret = copy_user_to_page_vector(pages, data, pos, len); + if (ret < 0) { + ceph_release_page_vector(pages, num_pages); + goto out; + } + + if ((file->f_flags & O_SYNC) == 0) { + /* get a second commit callback */ + req->r_safe_callback = sync_write_commit; + req->r_own_pages = 1; + } + } + req->r_pages = pages; + req->r_num_pages = num_pages; + req->r_inode = inode; + + ret = ceph_osdc_start_request(&client->osdc, req, false); + if (!ret) { + if (req->r_safe_callback) { + /* + * Add to inode unsafe list only after we + * start_request so that a tid has been assigned. + */ + spin_lock(&ci->i_unsafe_lock); + list_add(&ci->i_unsafe_writes, &req->r_unsafe_item); + spin_unlock(&ci->i_unsafe_lock); + ceph_get_cap_refs(ci, CEPH_CAP_FILE_WR); + } + ret = ceph_osdc_wait_request(&client->osdc, req); + } + + if (file->f_flags & O_DIRECT) + put_page_vector(pages, num_pages); + else if (file->f_flags & O_SYNC) + ceph_release_page_vector(pages, num_pages); + +out: + ceph_osdc_put_request(req); + if (ret == 0) { + pos += len; + written += len; + left -= len; + if (left) + goto more; + + ret = written; + *offset = pos; + if (pos > i_size_read(inode)) + check_caps = ceph_inode_set_size(inode, pos); + if (check_caps) + ceph_check_caps(ceph_inode(inode), CHECK_CAPS_AUTHONLY, + NULL); + } + return ret; +} + +/* + * Wrap generic_file_aio_read with checks for cap bits on the inode. + * Atomically grab references, so that those bits are not released + * back to the MDS mid-read. + * + * Hmm, the sync read case isn't actually async... should it be? + */ +static ssize_t ceph_aio_read(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct file *filp = iocb->ki_filp; + loff_t *ppos = &iocb->ki_pos; + size_t len = iov->iov_len; + struct inode *inode = filp->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + ssize_t ret; + int got = 0; + + dout("aio_read %p %llx.%llx %llu~%u trying to get caps on %p\n", + inode, ceph_vinop(inode), pos, (unsigned)len, inode); + __ceph_do_pending_vmtruncate(inode); + ret = ceph_get_caps(ci, CEPH_CAP_FILE_RD, CEPH_CAP_FILE_CACHE, + &got, -1); + if (ret < 0) + goto out; + 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)); + + if ((got & CEPH_CAP_FILE_CACHE) == 0 || + (iocb->ki_filp->f_flags & O_DIRECT) || + (inode->i_sb->s_flags & MS_SYNCHRONOUS)) + /* hmm, this isn't really async... */ + ret = ceph_sync_read(filp, iov->iov_base, len, ppos); + else + 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); + return ret; +} + +/* + * Take cap references to avoid releasing caps to MDS mid-write. + * + * If we are synchronous, and write with an old snap context, the OSD + * may return EOLDSNAPC. In that case, retry the write.. _after_ + * dropping our cap refs and allowing the pending snap to logically + * complete _before_ this write occurs. + * + * If we are near ENOSPC, write synchronously. + */ +static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_osd_client *osdc = &ceph_client(inode->i_sb)->osdc; + loff_t endoff = pos + iov->iov_len; + int got = 0; + int ret; + + if (ceph_snap(inode) != CEPH_NOSNAP) + return -EROFS; + +retry_snap: + if (ceph_osdmap_flag(osdc->osdmap, CEPH_OSDMAP_FULL)) + return -ENOSPC; + __ceph_do_pending_vmtruncate(inode); + dout("aio_write %p %llx.%llx %llu~%u getting caps. i_size %llu\n", + inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len, + inode->i_size); + ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, CEPH_CAP_FILE_BUFFER, + &got, endoff); + if (ret < 0) + goto out; + + dout("aio_write %p %llx.%llx %llu~%u got cap refs on %s\n", + inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len, + ceph_cap_string(got)); + + if ((got & CEPH_CAP_FILE_BUFFER) == 0 || + (iocb->ki_filp->f_flags & O_DIRECT) || + (inode->i_sb->s_flags & MS_SYNCHRONOUS)) { + ret = ceph_sync_write(file, iov->iov_base, iov->iov_len, + &iocb->ki_pos); + } else { + ret = generic_file_aio_write(iocb, iov, nr_segs, pos); + + if ((ret >= 0 || ret == -EIOCBQUEUED) && + ((file->f_flags & O_SYNC) || IS_SYNC(file->f_mapping->host) + || ceph_osdmap_flag(osdc->osdmap, CEPH_OSDMAP_NEARFULL))) + ret = vfs_fsync_range(file, file->f_path.dentry, + pos, pos + ret - 1, 1); + } + if (ret >= 0) { + spin_lock(&inode->i_lock); + __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR); + spin_unlock(&inode->i_lock); + } + +out: + dout("aio_write %p %llx.%llx %llu~%u dropping cap refs on %s\n", + inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len, + ceph_cap_string(got)); + ceph_put_cap_refs(ci, got); + + if (ret == -EOLDSNAPC) { + dout("aio_write %p %llx.%llx %llu~%u got EOLDSNAPC, retrying\n", + inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len); + goto retry_snap; + } + + return ret; +} + +/* + * llseek. be sure to verify file size on SEEK_END. + */ +static loff_t ceph_llseek(struct file *file, loff_t offset, int origin) +{ + struct inode *inode = file->f_mapping->host; + int ret; + + mutex_lock(&inode->i_mutex); + __ceph_do_pending_vmtruncate(inode); + switch (origin) { + case SEEK_END: + ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE); + if (ret < 0) { + offset = ret; + goto out; + } + offset += inode->i_size; + break; + case SEEK_CUR: + /* + * Here we special-case the lseek(fd, 0, SEEK_CUR) + * position-querying operation. Avoid rewriting the "same" + * f_pos value back to the file because a concurrent read(), + * write() or lseek() might have altered it + */ + if (offset == 0) { + offset = file->f_pos; + goto out; + } + offset += file->f_pos; + break; + } + + if (offset < 0 || offset > inode->i_sb->s_maxbytes) { + offset = -EINVAL; + goto out; + } + + /* Special lock needed here? */ + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_version = 0; + } + +out: + mutex_unlock(&inode->i_mutex); + return offset; +} + +const struct file_operations ceph_file_fops = { + .open = ceph_open, + .release = ceph_release, + .llseek = ceph_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = ceph_aio_read, + .aio_write = ceph_aio_write, + .mmap = ceph_mmap, + .fsync = ceph_fsync, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, + .unlocked_ioctl = ceph_ioctl, + .compat_ioctl = ceph_ioctl, +}; + -- cgit v0.10.2 From 1d3576fd10f0d7a104204267b81cf84a07028dad Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:09 -0700 Subject: ceph: address space operations The ceph address space methods are concerned primarily with managing the dirty page accounting in the inode, which (among other things) must keep track of which snapshot context each page was dirtied in, and ensure that dirty data is written out to the OSDs in snapshort order. A writepage() on a page that is not currently writeable due to snapshot writeback ordering constraints is ignored (it was presumably called from kswapd). Signed-off-by: Sage Weil diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c new file mode 100644 index 0000000..c7d673f --- /dev/null +++ b/fs/ceph/addr.c @@ -0,0 +1,1115 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include +#include /* generic_writepages */ +#include +#include + +#include "super.h" +#include "osd_client.h" + +/* + * Ceph address space ops. + * + * There are a few funny things going on here. + * + * The page->private field is used to reference a struct + * ceph_snap_context for _every_ dirty page. This indicates which + * snapshot the page was logically dirtied in, and thus which snap + * context needs to be associated with the osd write during writeback. + * + * Similarly, struct ceph_inode_info maintains a set of counters to + * count dirty pages on the inode. In the absense of snapshots, + * i_wrbuffer_ref == i_wrbuffer_ref_head == the dirty page count. + * + * When a snapshot is taken (that is, when the client receives + * notification that a snapshot was taken), each inode with caps and + * with dirty pages (dirty pages implies there is a cap) gets a new + * ceph_cap_snap in the i_cap_snaps list (which is sorted in ascending + * order, new snaps go to the tail). The i_wrbuffer_ref_head count is + * moved to capsnap->dirty. (Unless a sync write is currently in + * progress. In that case, the capsnap is said to be "pending", new + * writes cannot start, and the capsnap isn't "finalized" until the + * write completes (or fails) and a final size/mtime for the inode for + * that snap can be settled upon.) i_wrbuffer_ref_head is reset to 0. + * + * On writeback, we must submit writes to the osd IN SNAP ORDER. So, + * we look for the first capsnap in i_cap_snaps and write out pages in + * that snap context _only_. Then we move on to the next capsnap, + * eventually reaching the "live" or "head" context (i.e., pages that + * are not yet snapped) and are writing the most recently dirtied + * pages. + * + * Invalidate and so forth must take care to ensure the dirty page + * accounting is preserved. + */ + + +/* + * Dirty a page. Optimistically adjust accounting, on the assumption + * that we won't race with invalidate. If we do, readjust. + */ +static int ceph_set_page_dirty(struct page *page) +{ + struct address_space *mapping = page->mapping; + struct inode *inode; + struct ceph_inode_info *ci; + int undo = 0; + struct ceph_snap_context *snapc; + + if (unlikely(!mapping)) + return !TestSetPageDirty(page); + + if (TestSetPageDirty(page)) { + dout("%p set_page_dirty %p idx %lu -- already dirty\n", + mapping->host, page, page->index); + return 0; + } + + inode = mapping->host; + ci = ceph_inode(inode); + + /* + * Note that we're grabbing a snapc ref here without holding + * any locks! + */ + snapc = ceph_get_snap_context(ci->i_snap_realm->cached_context); + + /* dirty the head */ + spin_lock(&inode->i_lock); + if (ci->i_wrbuffer_ref_head == 0) + ci->i_head_snapc = ceph_get_snap_context(snapc); + ++ci->i_wrbuffer_ref_head; + if (ci->i_wrbuffer_ref == 0) + igrab(inode); + ++ci->i_wrbuffer_ref; + dout("%p set_page_dirty %p idx %lu head %d/%d -> %d/%d " + "snapc %p seq %lld (%d snaps)\n", + mapping->host, page, page->index, + ci->i_wrbuffer_ref-1, ci->i_wrbuffer_ref_head-1, + ci->i_wrbuffer_ref, ci->i_wrbuffer_ref_head, + snapc, snapc->seq, snapc->num_snaps); + spin_unlock(&inode->i_lock); + + /* now adjust page */ + spin_lock_irq(&mapping->tree_lock); + if (page->mapping) { /* Race with truncate? */ + WARN_ON_ONCE(!PageUptodate(page)); + + if (mapping_cap_account_dirty(mapping)) { + __inc_zone_page_state(page, NR_FILE_DIRTY); + __inc_bdi_stat(mapping->backing_dev_info, + BDI_RECLAIMABLE); + task_io_account_write(PAGE_CACHE_SIZE); + } + radix_tree_tag_set(&mapping->page_tree, + page_index(page), PAGECACHE_TAG_DIRTY); + + /* + * Reference snap context in page->private. Also set + * PagePrivate so that we get invalidatepage callback. + */ + page->private = (unsigned long)snapc; + SetPagePrivate(page); + } else { + dout("ANON set_page_dirty %p (raced truncate?)\n", page); + undo = 1; + } + + spin_unlock_irq(&mapping->tree_lock); + + if (undo) + /* whoops, we failed to dirty the page */ + ceph_put_wrbuffer_cap_refs(ci, 1, snapc); + + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); + + BUG_ON(!PageDirty(page)); + return 1; +} + +/* + * If we are truncating the full page (i.e. offset == 0), adjust the + * dirty page counters appropriately. Only called if there is private + * data on the page. + */ +static void ceph_invalidatepage(struct page *page, unsigned long offset) +{ + struct inode *inode = page->mapping->host; + struct ceph_inode_info *ci; + struct ceph_snap_context *snapc = (void *)page->private; + + BUG_ON(!PageLocked(page)); + BUG_ON(!page->private); + BUG_ON(!PagePrivate(page)); + BUG_ON(!page->mapping); + + /* + * We can get non-dirty pages here due to races between + * set_page_dirty and truncate_complete_page; just spit out a + * warning, in case we end up with accounting problems later. + */ + if (!PageDirty(page)) + pr_err("%p invalidatepage %p page not dirty\n", inode, page); + + if (offset == 0) + ClearPageChecked(page); + + ci = ceph_inode(inode); + if (offset == 0) { + dout("%p invalidatepage %p idx %lu full dirty page %lu\n", + inode, page, page->index, offset); + ceph_put_wrbuffer_cap_refs(ci, 1, snapc); + ceph_put_snap_context(snapc); + page->private = 0; + ClearPagePrivate(page); + } else { + dout("%p invalidatepage %p idx %lu partial dirty page\n", + inode, page, page->index); + } +} + +/* just a sanity check */ +static int ceph_releasepage(struct page *page, gfp_t g) +{ + struct inode *inode = page->mapping ? page->mapping->host : NULL; + dout("%p releasepage %p idx %lu\n", inode, page, page->index); + WARN_ON(PageDirty(page)); + WARN_ON(page->private); + WARN_ON(PagePrivate(page)); + return 0; +} + +/* + * read a single page, without unlocking it. + */ +static int readpage_nounlock(struct file *filp, struct page *page) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_osd_client *osdc = &ceph_inode_to_client(inode)->osdc; + int err = 0; + u64 len = PAGE_CACHE_SIZE; + + dout("readpage inode %p file %p page %p index %lu\n", + inode, filp, page, page->index); + err = ceph_osdc_readpages(osdc, ceph_vino(inode), &ci->i_layout, + page->index << PAGE_CACHE_SHIFT, &len, + ci->i_truncate_seq, ci->i_truncate_size, + &page, 1); + if (err == -ENOENT) + err = 0; + if (err < 0) { + SetPageError(page); + goto out; + } else if (err < PAGE_CACHE_SIZE) { + /* zero fill remainder of page */ + zero_user_segment(page, err, PAGE_CACHE_SIZE); + } + SetPageUptodate(page); + +out: + return err < 0 ? err : 0; +} + +static int ceph_readpage(struct file *filp, struct page *page) +{ + int r = readpage_nounlock(filp, page); + unlock_page(page); + return r; +} + +/* + * Build a vector of contiguous pages from the provided page list. + */ +static struct page **page_vector_from_list(struct list_head *page_list, + unsigned *nr_pages) +{ + struct page **pages; + struct page *page; + int next_index, contig_pages = 0; + + /* build page vector */ + pages = kmalloc(sizeof(*pages) * *nr_pages, GFP_NOFS); + if (!pages) + return ERR_PTR(-ENOMEM); + + BUG_ON(list_empty(page_list)); + next_index = list_entry(page_list->prev, struct page, lru)->index; + list_for_each_entry_reverse(page, page_list, lru) { + if (page->index == next_index) { + dout("readpages page %d %p\n", contig_pages, page); + pages[contig_pages] = page; + contig_pages++; + next_index++; + } else { + break; + } + } + *nr_pages = contig_pages; + return pages; +} + +/* + * Read multiple pages. Leave pages we don't read + unlock in page_list; + * the caller (VM) cleans them up. + */ +static int ceph_readpages(struct file *file, struct address_space *mapping, + struct list_head *page_list, unsigned nr_pages) +{ + struct inode *inode = file->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_osd_client *osdc = &ceph_inode_to_client(inode)->osdc; + int rc = 0; + struct page **pages; + struct pagevec pvec; + loff_t offset; + u64 len; + + dout("readpages %p file %p nr_pages %d\n", + inode, file, nr_pages); + + pages = page_vector_from_list(page_list, &nr_pages); + if (IS_ERR(pages)) + return PTR_ERR(pages); + + /* guess read extent */ + offset = pages[0]->index << PAGE_CACHE_SHIFT; + len = nr_pages << PAGE_CACHE_SHIFT; + rc = ceph_osdc_readpages(osdc, ceph_vino(inode), &ci->i_layout, + offset, &len, + ci->i_truncate_seq, ci->i_truncate_size, + pages, nr_pages); + if (rc == -ENOENT) + rc = 0; + if (rc < 0) + goto out; + + /* set uptodate and add to lru in pagevec-sized chunks */ + pagevec_init(&pvec, 0); + for (; !list_empty(page_list) && len > 0; + rc -= PAGE_CACHE_SIZE, len -= PAGE_CACHE_SIZE) { + struct page *page = + list_entry(page_list->prev, struct page, lru); + + list_del(&page->lru); + + if (rc < (int)PAGE_CACHE_SIZE) { + /* zero (remainder of) page */ + int s = rc < 0 ? 0 : rc; + zero_user_segment(page, s, PAGE_CACHE_SIZE); + } + + if (add_to_page_cache(page, mapping, page->index, GFP_NOFS)) { + page_cache_release(page); + dout("readpages %p add_to_page_cache failed %p\n", + inode, page); + continue; + } + dout("readpages %p adding %p idx %lu\n", inode, page, + page->index); + flush_dcache_page(page); + SetPageUptodate(page); + unlock_page(page); + if (pagevec_add(&pvec, page) == 0) + pagevec_lru_add_file(&pvec); /* add to lru */ + } + pagevec_lru_add_file(&pvec); + rc = 0; + +out: + kfree(pages); + return rc; +} + +/* + * Get ref for the oldest snapc for an inode with dirty data... that is, the + * only snap context we are allowed to write back. + * + * Caller holds i_lock. + */ +static struct ceph_snap_context *__get_oldest_context(struct inode *inode, + u64 *snap_size) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_snap_context *snapc = NULL; + struct ceph_cap_snap *capsnap = NULL; + + list_for_each_entry(capsnap, &ci->i_cap_snaps, ci_item) { + dout(" cap_snap %p snapc %p has %d dirty pages\n", capsnap, + capsnap->context, capsnap->dirty_pages); + if (capsnap->dirty_pages) { + snapc = ceph_get_snap_context(capsnap->context); + if (snap_size) + *snap_size = capsnap->size; + break; + } + } + if (!snapc && ci->i_snap_realm) { + snapc = ceph_get_snap_context(ci->i_snap_realm->cached_context); + dout(" head snapc %p has %d dirty pages\n", + snapc, ci->i_wrbuffer_ref_head); + } + return snapc; +} + +static struct ceph_snap_context *get_oldest_context(struct inode *inode, + u64 *snap_size) +{ + struct ceph_snap_context *snapc = NULL; + + spin_lock(&inode->i_lock); + snapc = __get_oldest_context(inode, snap_size); + spin_unlock(&inode->i_lock); + return snapc; +} + +/* + * Write a single page, but leave the page locked. + * + * If we get a write error, set the page error bit, but still adjust the + * dirty page accounting (i.e., page is no longer dirty). + */ +static int writepage_nounlock(struct page *page, struct writeback_control *wbc) +{ + struct inode *inode; + struct ceph_inode_info *ci; + struct ceph_osd_client *osdc; + loff_t page_off = page->index << PAGE_CACHE_SHIFT; + int len = PAGE_CACHE_SIZE; + loff_t i_size; + int err = 0; + struct ceph_snap_context *snapc; + u64 snap_size = 0; + + dout("writepage %p idx %lu\n", page, page->index); + + if (!page->mapping || !page->mapping->host) { + dout("writepage %p - no mapping\n", page); + return -EFAULT; + } + inode = page->mapping->host; + ci = ceph_inode(inode); + osdc = &ceph_inode_to_client(inode)->osdc; + + /* verify this is a writeable snap context */ + snapc = (void *)page->private; + if (snapc == NULL) { + dout("writepage %p page %p not dirty?\n", inode, page); + goto out; + } + if (snapc != get_oldest_context(inode, &snap_size)) { + dout("writepage %p page %p snapc %p not writeable - noop\n", + inode, page, (void *)page->private); + /* we should only noop if called by kswapd */ + WARN_ON((current->flags & PF_MEMALLOC) == 0); + goto out; + } + + /* is this a partial page at end of file? */ + if (snap_size) + i_size = snap_size; + else + i_size = i_size_read(inode); + if (i_size < page_off + len) + len = i_size - page_off; + + dout("writepage %p page %p index %lu on %llu~%u\n", + inode, page, page->index, page_off, len); + + set_page_writeback(page); + err = ceph_osdc_writepages(osdc, ceph_vino(inode), + &ci->i_layout, snapc, + page_off, len, + ci->i_truncate_seq, ci->i_truncate_size, + &inode->i_mtime, + &page, 1, 0, 0, true); + if (err < 0) { + dout("writepage setting page/mapping error %d %p\n", err, page); + SetPageError(page); + mapping_set_error(&inode->i_data, err); + if (wbc) + wbc->pages_skipped++; + } else { + dout("writepage cleaned page %p\n", page); + err = 0; /* vfs expects us to return 0 */ + } + page->private = 0; + ClearPagePrivate(page); + end_page_writeback(page); + ceph_put_wrbuffer_cap_refs(ci, 1, snapc); + ceph_put_snap_context(snapc); +out: + return err; +} + +static int ceph_writepage(struct page *page, struct writeback_control *wbc) +{ + int err = writepage_nounlock(page, wbc); + unlock_page(page); + return err; +} + + +/* + * lame release_pages helper. release_pages() isn't exported to + * modules. + */ +static void ceph_release_pages(struct page **pages, int num) +{ + struct pagevec pvec; + int i; + + pagevec_init(&pvec, 0); + for (i = 0; i < num; i++) { + if (pagevec_add(&pvec, pages[i]) == 0) + pagevec_release(&pvec); + } + pagevec_release(&pvec); +} + + +/* + * async writeback completion handler. + * + * If we get an error, set the mapping error bit, but not the individual + * page error bits. + */ +static void writepages_finish(struct ceph_osd_request *req, + struct ceph_msg *msg) +{ + struct inode *inode = req->r_inode; + struct ceph_osd_reply_head *replyhead; + struct ceph_osd_op *op; + struct ceph_inode_info *ci = ceph_inode(inode); + unsigned wrote; + loff_t offset = req->r_pages[0]->index << PAGE_CACHE_SHIFT; + struct page *page; + int i; + struct ceph_snap_context *snapc = req->r_snapc; + struct address_space *mapping = inode->i_mapping; + struct writeback_control *wbc = req->r_wbc; + __s32 rc = -EIO; + u64 bytes = 0; + + /* parse reply */ + replyhead = msg->front.iov_base; + WARN_ON(le32_to_cpu(replyhead->num_ops) == 0); + op = (void *)(replyhead + 1); + rc = le32_to_cpu(replyhead->result); + bytes = le64_to_cpu(op->extent.length); + + if (rc >= 0) { + wrote = (bytes + (offset & ~PAGE_CACHE_MASK) + ~PAGE_CACHE_MASK) + >> PAGE_CACHE_SHIFT; + WARN_ON(wrote != req->r_num_pages); + } else { + wrote = 0; + mapping_set_error(mapping, rc); + } + dout("writepages_finish %p rc %d bytes %llu wrote %d (pages)\n", + inode, rc, bytes, wrote); + + /* clean all pages */ + for (i = 0; i < req->r_num_pages; i++) { + page = req->r_pages[i]; + BUG_ON(!page); + WARN_ON(!PageUptodate(page)); + + if (i >= wrote) { + dout("inode %p skipping page %p\n", inode, page); + wbc->pages_skipped++; + } + page->private = 0; + ClearPagePrivate(page); + ceph_put_snap_context(snapc); + dout("unlocking %d %p\n", i, page); + end_page_writeback(page); + unlock_page(page); + } + dout("%p wrote+cleaned %d pages\n", inode, wrote); + ceph_put_wrbuffer_cap_refs(ci, req->r_num_pages, snapc); + + ceph_release_pages(req->r_pages, req->r_num_pages); + if (req->r_pages_from_pool) + mempool_free(req->r_pages, + ceph_client(inode->i_sb)->wb_pagevec_pool); + else + kfree(req->r_pages); + ceph_osdc_put_request(req); +} + +/* + * allocate a page vec, either directly, or if necessary, via a the + * mempool. we avoid the mempool if we can because req->r_num_pages + * may be less than the maximum write size. + */ +static void alloc_page_vec(struct ceph_client *client, + struct ceph_osd_request *req) +{ + req->r_pages = kmalloc(sizeof(struct page *) * req->r_num_pages, + GFP_NOFS); + if (!req->r_pages) { + req->r_pages = mempool_alloc(client->wb_pagevec_pool, GFP_NOFS); + req->r_pages_from_pool = 1; + WARN_ON(!req->r_pages); + } +} + +/* + * initiate async writeback + */ +static int ceph_writepages_start(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + struct backing_dev_info *bdi = mapping->backing_dev_info; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_client *client = ceph_inode_to_client(inode); + pgoff_t index, start, end; + int range_whole = 0; + int should_loop = 1; + pgoff_t max_pages = 0, max_pages_ever = 0; + struct ceph_snap_context *snapc = NULL, *last_snapc = NULL; + struct pagevec pvec; + int done = 0; + int rc = 0; + unsigned wsize = 1 << inode->i_blkbits; + struct ceph_osd_request *req = NULL; + int do_sync; + u64 snap_size = 0; + + /* + * Include a 'sync' in the OSD request if this is a data + * integrity write (e.g., O_SYNC write or fsync()), or if our + * cap is being revoked. + */ + do_sync = wbc->sync_mode == WB_SYNC_ALL; + if (ceph_caps_revoking(ci, CEPH_CAP_FILE_BUFFER)) + do_sync = 1; + dout("writepages_start %p dosync=%d (mode=%s)\n", + inode, do_sync, + wbc->sync_mode == WB_SYNC_NONE ? "NONE" : + (wbc->sync_mode == WB_SYNC_ALL ? "ALL" : "HOLD")); + + client = ceph_inode_to_client(inode); + if (client->mount_state == CEPH_MOUNT_SHUTDOWN) { + pr_warning("writepage_start %p on forced umount\n", inode); + return -EIO; /* we're in a forced umount, don't write! */ + } + if (client->mount_args.wsize && client->mount_args.wsize < wsize) + wsize = client->mount_args.wsize; + if (wsize < PAGE_CACHE_SIZE) + wsize = PAGE_CACHE_SIZE; + max_pages_ever = wsize >> PAGE_CACHE_SHIFT; + + pagevec_init(&pvec, 0); + + /* ?? */ + if (wbc->nonblocking && bdi_write_congested(bdi)) { + dout(" writepages congested\n"); + wbc->encountered_congestion = 1; + goto out_final; + } + + /* where to start/end? */ + if (wbc->range_cyclic) { + start = mapping->writeback_index; /* Start from prev offset */ + end = -1; + dout(" cyclic, start at %lu\n", start); + } else { + start = wbc->range_start >> PAGE_CACHE_SHIFT; + end = wbc->range_end >> PAGE_CACHE_SHIFT; + if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) + range_whole = 1; + should_loop = 0; + dout(" not cyclic, %lu to %lu\n", start, end); + } + index = start; + +retry: + /* find oldest snap context with dirty data */ + ceph_put_snap_context(snapc); + snapc = get_oldest_context(inode, &snap_size); + if (!snapc) { + /* hmm, why does writepages get called when there + is no dirty data? */ + dout(" no snap context with dirty data?\n"); + goto out; + } + dout(" oldest snapc is %p seq %lld (%d snaps)\n", + snapc, snapc->seq, snapc->num_snaps); + if (last_snapc && snapc != last_snapc) { + /* if we switched to a newer snapc, restart our scan at the + * start of the original file range. */ + dout(" snapc differs from last pass, restarting at %lu\n", + index); + index = start; + } + last_snapc = snapc; + + while (!done && index <= end) { + unsigned i; + int first; + pgoff_t next; + int pvec_pages, locked_pages; + struct page *page; + int want; + u64 offset, len; + struct ceph_osd_request_head *reqhead; + struct ceph_osd_op *op; + + next = 0; + locked_pages = 0; + max_pages = max_pages_ever; + +get_more_pages: + first = -1; + want = min(end - index, + min((pgoff_t)PAGEVEC_SIZE, + max_pages - (pgoff_t)locked_pages) - 1) + + 1; + pvec_pages = pagevec_lookup_tag(&pvec, mapping, &index, + PAGECACHE_TAG_DIRTY, + want); + dout("pagevec_lookup_tag got %d\n", pvec_pages); + if (!pvec_pages && !locked_pages) + break; + for (i = 0; i < pvec_pages && locked_pages < max_pages; i++) { + page = pvec.pages[i]; + dout("? %p idx %lu\n", page, page->index); + if (locked_pages == 0) + lock_page(page); /* first page */ + else if (!trylock_page(page)) + break; + + /* only dirty pages, or our accounting breaks */ + if (unlikely(!PageDirty(page)) || + unlikely(page->mapping != mapping)) { + dout("!dirty or !mapping %p\n", page); + unlock_page(page); + break; + } + if (!wbc->range_cyclic && page->index > end) { + dout("end of range %p\n", page); + done = 1; + unlock_page(page); + break; + } + if (next && (page->index != next)) { + dout("not consecutive %p\n", page); + unlock_page(page); + break; + } + if (wbc->sync_mode != WB_SYNC_NONE) { + dout("waiting on writeback %p\n", page); + wait_on_page_writeback(page); + } + if ((snap_size && page_offset(page) > snap_size) || + (!snap_size && + page_offset(page) > i_size_read(inode))) { + dout("%p page eof %llu\n", page, snap_size ? + snap_size : i_size_read(inode)); + done = 1; + unlock_page(page); + break; + } + if (PageWriteback(page)) { + dout("%p under writeback\n", page); + unlock_page(page); + break; + } + + /* only if matching snap context */ + if (snapc != (void *)page->private) { + dout("page snapc %p != oldest %p\n", + (void *)page->private, snapc); + unlock_page(page); + if (!locked_pages) + continue; /* keep looking for snap */ + break; + } + + if (!clear_page_dirty_for_io(page)) { + dout("%p !clear_page_dirty_for_io\n", page); + unlock_page(page); + break; + } + + /* ok */ + if (locked_pages == 0) { + /* prepare async write request */ + offset = page->index << PAGE_CACHE_SHIFT; + len = wsize; + req = ceph_osdc_new_request(&client->osdc, + &ci->i_layout, + ceph_vino(inode), + offset, &len, + CEPH_OSD_OP_WRITE, + CEPH_OSD_FLAG_WRITE | + CEPH_OSD_FLAG_ONDISK, + snapc, do_sync, + ci->i_truncate_seq, + ci->i_truncate_size, + &inode->i_mtime, true, 1); + max_pages = req->r_num_pages; + + alloc_page_vec(client, req); + req->r_callback = writepages_finish; + req->r_inode = inode; + req->r_wbc = wbc; + } + + /* note position of first page in pvec */ + if (first < 0) + first = i; + dout("%p will write page %p idx %lu\n", + inode, page, page->index); + set_page_writeback(page); + req->r_pages[locked_pages] = page; + locked_pages++; + next = page->index + 1; + } + + /* did we get anything? */ + if (!locked_pages) + goto release_pvec_pages; + if (i) { + int j; + BUG_ON(!locked_pages || first < 0); + + if (pvec_pages && i == pvec_pages && + locked_pages < max_pages) { + dout("reached end pvec, trying for more\n"); + pagevec_reinit(&pvec); + goto get_more_pages; + } + + /* shift unused pages over in the pvec... we + * will need to release them below. */ + for (j = i; j < pvec_pages; j++) { + dout(" pvec leftover page %p\n", + pvec.pages[j]); + pvec.pages[j-i+first] = pvec.pages[j]; + } + pvec.nr -= i-first; + } + + /* submit the write */ + offset = req->r_pages[0]->index << PAGE_CACHE_SHIFT; + len = min((snap_size ? snap_size : i_size_read(inode)) - offset, + (u64)locked_pages << PAGE_CACHE_SHIFT); + dout("writepages got %d pages at %llu~%llu\n", + locked_pages, offset, len); + + /* revise final length, page count */ + req->r_num_pages = locked_pages; + reqhead = req->r_request->front.iov_base; + op = (void *)(reqhead + 1); + op->extent.length = cpu_to_le64(len); + op->payload_len = cpu_to_le32(len); + req->r_request->hdr.data_len = cpu_to_le32(len); + + ceph_osdc_start_request(&client->osdc, req, true); + req = NULL; + + /* continue? */ + index = next; + wbc->nr_to_write -= locked_pages; + if (wbc->nr_to_write <= 0) + done = 1; + +release_pvec_pages: + dout("pagevec_release on %d pages (%p)\n", (int)pvec.nr, + pvec.nr ? pvec.pages[0] : NULL); + pagevec_release(&pvec); + + if (locked_pages && !done) + goto retry; + } + + if (should_loop && !done) { + /* more to do; loop back to beginning of file */ + dout("writepages looping back to beginning of file\n"); + should_loop = 0; + index = 0; + goto retry; + } + + if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) + mapping->writeback_index = index; + +out: + if (req) + ceph_osdc_put_request(req); + if (rc > 0) + rc = 0; /* vfs expects us to return 0 */ + ceph_put_snap_context(snapc); + dout("writepages done, rc = %d\n", rc); +out_final: + return rc; +} + + + +/* + * See if a given @snapc is either writeable, or already written. + */ +static int context_is_writeable_or_written(struct inode *inode, + struct ceph_snap_context *snapc) +{ + struct ceph_snap_context *oldest = get_oldest_context(inode, NULL); + return !oldest || snapc->seq <= oldest->seq; +} + +/* + * We are only allowed to write into/dirty the page if the page is + * clean, or already dirty within the same snap context. + */ +static int ceph_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + struct inode *inode = file->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + struct page *page; + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + loff_t page_off = pos & PAGE_CACHE_MASK; + int pos_in_page = pos & ~PAGE_CACHE_MASK; + int end_in_page = pos_in_page + len; + loff_t i_size; + struct ceph_snap_context *snapc; + int r; + + /* get a page*/ +retry: + page = grab_cache_page_write_begin(mapping, index, 0); + if (!page) + return -ENOMEM; + *pagep = page; + + dout("write_begin file %p inode %p page %p %d~%d\n", file, + inode, page, (int)pos, (int)len); + +retry_locked: + /* writepages currently holds page lock, but if we change that later, */ + wait_on_page_writeback(page); + + /* check snap context */ + BUG_ON(!ci->i_snap_realm); + down_read(&mdsc->snap_rwsem); + BUG_ON(!ci->i_snap_realm->cached_context); + if (page->private && + (void *)page->private != ci->i_snap_realm->cached_context) { + /* + * this page is already dirty in another (older) snap + * context! is it writeable now? + */ + snapc = get_oldest_context(inode, NULL); + up_read(&mdsc->snap_rwsem); + + if (snapc != (void *)page->private) { + dout(" page %p snapc %p not current or oldest\n", + page, (void *)page->private); + /* + * queue for writeback, and wait for snapc to + * be writeable or written + */ + snapc = ceph_get_snap_context((void *)page->private); + unlock_page(page); + if (ceph_queue_writeback(inode)) + igrab(inode); + wait_event_interruptible(ci->i_cap_wq, + context_is_writeable_or_written(inode, snapc)); + ceph_put_snap_context(snapc); + goto retry; + } + + /* yay, writeable, do it now (without dropping page lock) */ + dout(" page %p snapc %p not current, but oldest\n", + page, snapc); + if (!clear_page_dirty_for_io(page)) + goto retry_locked; + r = writepage_nounlock(page, NULL); + if (r < 0) + goto fail_nosnap; + goto retry_locked; + } + + if (PageUptodate(page)) { + dout(" page %p already uptodate\n", page); + return 0; + } + + /* full page? */ + if (pos_in_page == 0 && len == PAGE_CACHE_SIZE) + return 0; + + /* past end of file? */ + i_size = inode->i_size; /* caller holds i_mutex */ + + if (i_size + len > inode->i_sb->s_maxbytes) { + /* file is too big */ + r = -EINVAL; + goto fail; + } + + if (page_off >= i_size || + (pos_in_page == 0 && (pos+len) >= i_size && + end_in_page - pos_in_page != PAGE_CACHE_SIZE)) { + dout(" zeroing %p 0 - %d and %d - %d\n", + page, pos_in_page, end_in_page, (int)PAGE_CACHE_SIZE); + zero_user_segments(page, + 0, pos_in_page, + end_in_page, PAGE_CACHE_SIZE); + return 0; + } + + /* we need to read it. */ + up_read(&mdsc->snap_rwsem); + r = readpage_nounlock(file, page); + if (r < 0) + goto fail_nosnap; + goto retry_locked; + +fail: + up_read(&mdsc->snap_rwsem); +fail_nosnap: + unlock_page(page); + return r; +} + +/* + * we don't do anything in here that simple_write_end doesn't do + * except adjust dirty page accounting and drop read lock on + * mdsc->snap_rwsem. + */ +static int ceph_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + struct inode *inode = file->f_dentry->d_inode; + struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + unsigned from = pos & (PAGE_CACHE_SIZE - 1); + int check_cap = 0; + + dout("write_end file %p inode %p page %p %d~%d (%d)\n", file, + inode, page, (int)pos, (int)copied, (int)len); + + /* zero the stale part of the page if we did a short copy */ + if (copied < len) + zero_user_segment(page, from+copied, len); + + /* did file size increase? */ + /* (no need for i_size_read(); we caller holds i_mutex */ + if (pos+copied > inode->i_size) + check_cap = ceph_inode_set_size(inode, pos+copied); + + if (!PageUptodate(page)) + SetPageUptodate(page); + + set_page_dirty(page); + + unlock_page(page); + up_read(&mdsc->snap_rwsem); + page_cache_release(page); + + if (check_cap) + ceph_check_caps(ceph_inode(inode), CHECK_CAPS_AUTHONLY, NULL); + + return copied; +} + +/* + * we set .direct_IO to indicate direct io is supported, but since we + * intercept O_DIRECT reads and writes early, this function should + * 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) +{ + WARN_ON(1); + return -EINVAL; +} + +const struct address_space_operations ceph_aops = { + .readpage = ceph_readpage, + .readpages = ceph_readpages, + .writepage = ceph_writepage, + .writepages = ceph_writepages_start, + .write_begin = ceph_write_begin, + .write_end = ceph_write_end, + .set_page_dirty = ceph_set_page_dirty, + .invalidatepage = ceph_invalidatepage, + .releasepage = ceph_releasepage, + .direct_IO = ceph_direct_io, +}; + + +/* + * vm ops + */ + +/* + * Reuse write_begin here for simplicity. + */ +static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct inode *inode = vma->vm_file->f_dentry->d_inode; + struct page *page = vmf->page; + struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + loff_t off = page->index << PAGE_CACHE_SHIFT; + loff_t size, len; + struct page *locked_page = NULL; + void *fsdata = NULL; + int ret; + + size = i_size_read(inode); + if (off + PAGE_CACHE_SIZE <= size) + len = PAGE_CACHE_SIZE; + else + len = size & ~PAGE_CACHE_MASK; + + dout("page_mkwrite %p %llu~%llu page %p idx %lu\n", inode, + off, len, page, page->index); + ret = ceph_write_begin(vma->vm_file, inode->i_mapping, off, len, 0, + &locked_page, &fsdata); + WARN_ON(page != locked_page); + if (!ret) { + /* + * doing the following, instead of calling + * ceph_write_end. Note that we keep the + * page locked + */ + set_page_dirty(page); + up_read(&mdsc->snap_rwsem); + page_cache_release(page); + ret = VM_FAULT_LOCKED; + } else { + ret = VM_FAULT_SIGBUS; + } + dout("page_mkwrite %p %llu~%llu = %d\n", inode, off, len, ret); + return ret; +} + +static struct vm_operations_struct ceph_vmops = { + .fault = filemap_fault, + .page_mkwrite = ceph_page_mkwrite, +}; + +int ceph_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct address_space *mapping = file->f_mapping; + + if (!mapping->a_ops->readpage) + return -ENOEXEC; + file_accessed(file); + vma->vm_ops = &ceph_vmops; + vma->vm_flags |= VM_CAN_NONLINEAR; + return 0; +} -- cgit v0.10.2 From 2f2dc053404febedc9c273452d9d518fb31fde72 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:09 -0700 Subject: ceph: MDS client The MDS (metadata server) client is responsible for submitting requests to the MDS cluster and parsing the response. We decide which MDS to submit each request to based on cached information about the current partition of the directory hierarchy across the cluster. A stateful session is opened with each MDS before we submit requests to it, and a mutex is used to control the ordering of messages within each session. An MDS request may generate two responses. The first indicates the operation was a success and returns any result. A second reply is sent when the operation commits to disk. Note that locking on the MDS ensures that the results of updates are visible only to the updating client before the operation commits. Requests are linked to the containing directory so that an fsync will wait for them to commit. If an MDS fails and/or recovers, we resubmit requests as needed. We also reconnect existing capabilities to a recovering MDS to reestablish that shared session state. Old dentry leases are invalidated. Signed-off-by: Sage Weil diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c new file mode 100644 index 0000000..de8ba4a --- /dev/null +++ b/fs/ceph/mds_client.c @@ -0,0 +1,2912 @@ +#include "ceph_debug.h" + +#include +#include + +#include "mds_client.h" +#include "mon_client.h" +#include "super.h" +#include "messenger.h" +#include "decode.h" + +/* + * A cluster of MDS (metadata server) daemons is responsible for + * managing the file system namespace (the directory hierarchy and + * inodes) and for coordinating shared access to storage. Metadata is + * partitioning hierarchically across a number of servers, and that + * partition varies over time as the cluster adjusts the distribution + * in order to balance load. + * + * The MDS client is primarily responsible to managing synchronous + * metadata requests for operations like open, unlink, and so forth. + * If there is a MDS failure, we find out about it when we (possibly + * request and) receive a new MDS map, and can resubmit affected + * requests. + * + * For the most part, though, we take advantage of a lossless + * communications channel to the MDS, and do not need to worry about + * timing out or resubmitting requests. + * + * We maintain a stateful "session" with each MDS we interact with. + * Within each session, we sent periodic heartbeat messages to ensure + * any capabilities or leases we have been issues remain valid. If + * the session times out and goes stale, our leases and capabilities + * are no longer valid. + */ + +static void __wake_requests(struct ceph_mds_client *mdsc, + struct list_head *head); + +const static struct ceph_connection_operations mds_con_ops; + + +/* + * mds reply parsing + */ + +/* + * parse individual inode info + */ +static int parse_reply_info_in(void **p, void *end, + struct ceph_mds_reply_info_in *info) +{ + int err = -EIO; + + info->in = *p; + *p += sizeof(struct ceph_mds_reply_inode) + + sizeof(*info->in->fragtree.splits) * + le32_to_cpu(info->in->fragtree.nsplits); + + ceph_decode_32_safe(p, end, info->symlink_len, bad); + ceph_decode_need(p, end, info->symlink_len, bad); + info->symlink = *p; + *p += info->symlink_len; + + ceph_decode_32_safe(p, end, info->xattr_len, bad); + ceph_decode_need(p, end, info->xattr_len, bad); + info->xattr_data = *p; + *p += info->xattr_len; + return 0; +bad: + return err; +} + +/* + * parse a normal reply, which may contain a (dir+)dentry and/or a + * target inode. + */ +static int parse_reply_info_trace(void **p, void *end, + struct ceph_mds_reply_info_parsed *info) +{ + int err; + + if (info->head->is_dentry) { + err = parse_reply_info_in(p, end, &info->diri); + if (err < 0) + goto out_bad; + + if (unlikely(*p + sizeof(*info->dirfrag) > end)) + goto bad; + info->dirfrag = *p; + *p += sizeof(*info->dirfrag) + + sizeof(u32)*le32_to_cpu(info->dirfrag->ndist); + if (unlikely(*p > end)) + goto bad; + + ceph_decode_32_safe(p, end, info->dname_len, bad); + ceph_decode_need(p, end, info->dname_len, bad); + info->dname = *p; + *p += info->dname_len; + info->dlease = *p; + *p += sizeof(*info->dlease); + } + + if (info->head->is_target) { + err = parse_reply_info_in(p, end, &info->targeti); + if (err < 0) + goto out_bad; + } + + if (unlikely(*p != end)) + goto bad; + return 0; + +bad: + err = -EIO; +out_bad: + pr_err("problem parsing mds trace %d\n", err); + return err; +} + +/* + * parse readdir results + */ +static int parse_reply_info_dir(void **p, void *end, + struct ceph_mds_reply_info_parsed *info) +{ + u32 num, i = 0; + int err; + + info->dir_dir = *p; + if (*p + sizeof(*info->dir_dir) > end) + goto bad; + *p += sizeof(*info->dir_dir) + + sizeof(u32)*le32_to_cpu(info->dir_dir->ndist); + if (*p > end) + goto bad; + + ceph_decode_need(p, end, sizeof(num) + 2, bad); + ceph_decode_32(p, num); + ceph_decode_8(p, info->dir_end); + ceph_decode_8(p, info->dir_complete); + if (num == 0) + goto done; + + /* alloc large array */ + info->dir_nr = num; + info->dir_in = kcalloc(num, sizeof(*info->dir_in) + + sizeof(*info->dir_dname) + + sizeof(*info->dir_dname_len) + + sizeof(*info->dir_dlease), + GFP_NOFS); + if (info->dir_in == NULL) { + err = -ENOMEM; + goto out_bad; + } + info->dir_dname = (void *)(info->dir_in + num); + info->dir_dname_len = (void *)(info->dir_dname + num); + info->dir_dlease = (void *)(info->dir_dname_len + num); + + while (num) { + /* dentry */ + ceph_decode_need(p, end, sizeof(u32)*2, bad); + ceph_decode_32(p, info->dir_dname_len[i]); + ceph_decode_need(p, end, info->dir_dname_len[i], bad); + info->dir_dname[i] = *p; + *p += info->dir_dname_len[i]; + dout("parsed dir dname '%.*s'\n", info->dir_dname_len[i], + info->dir_dname[i]); + info->dir_dlease[i] = *p; + *p += sizeof(struct ceph_mds_reply_lease); + + /* inode */ + err = parse_reply_info_in(p, end, &info->dir_in[i]); + if (err < 0) + goto out_bad; + i++; + num--; + } + +done: + if (*p != end) + goto bad; + return 0; + +bad: + err = -EIO; +out_bad: + pr_err("problem parsing dir contents %d\n", err); + return err; +} + +/* + * parse entire mds reply + */ +static int parse_reply_info(struct ceph_msg *msg, + struct ceph_mds_reply_info_parsed *info) +{ + void *p, *end; + u32 len; + int err; + + info->head = msg->front.iov_base; + p = msg->front.iov_base + sizeof(struct ceph_mds_reply_head); + end = p + msg->front.iov_len - sizeof(struct ceph_mds_reply_head); + + /* trace */ + ceph_decode_32_safe(&p, end, len, bad); + if (len > 0) { + err = parse_reply_info_trace(&p, p+len, info); + if (err < 0) + goto out_bad; + } + + /* dir content */ + ceph_decode_32_safe(&p, end, len, bad); + if (len > 0) { + err = parse_reply_info_dir(&p, p+len, info); + if (err < 0) + goto out_bad; + } + + /* snap blob */ + ceph_decode_32_safe(&p, end, len, bad); + info->snapblob_len = len; + info->snapblob = p; + p += len; + + if (p != end) + goto bad; + return 0; + +bad: + err = -EIO; +out_bad: + pr_err("mds parse_reply err %d\n", err); + return err; +} + +static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info) +{ + kfree(info->dir_in); +} + + +/* + * sessions + */ +static const char *session_state_name(int s) +{ + switch (s) { + case CEPH_MDS_SESSION_NEW: return "new"; + case CEPH_MDS_SESSION_OPENING: return "opening"; + case CEPH_MDS_SESSION_OPEN: return "open"; + case CEPH_MDS_SESSION_HUNG: return "hung"; + case CEPH_MDS_SESSION_CLOSING: return "closing"; + case CEPH_MDS_SESSION_RECONNECTING: return "reconnecting"; + default: return "???"; + } +} + +static struct ceph_mds_session *get_session(struct ceph_mds_session *s) +{ + if (atomic_inc_not_zero(&s->s_ref)) { + dout("mdsc get_session %p %d -> %d\n", s, + atomic_read(&s->s_ref)-1, atomic_read(&s->s_ref)); + return s; + } else { + dout("mdsc get_session %p 0 -- FAIL", s); + return NULL; + } +} + +void ceph_put_mds_session(struct ceph_mds_session *s) +{ + dout("mdsc put_session %p %d -> %d\n", s, + atomic_read(&s->s_ref), atomic_read(&s->s_ref)-1); + if (atomic_dec_and_test(&s->s_ref)) { + ceph_con_shutdown(&s->s_con); + kfree(s); + } +} + +/* + * called under mdsc->mutex + */ +struct ceph_mds_session *__ceph_lookup_mds_session(struct ceph_mds_client *mdsc, + int mds) +{ + struct ceph_mds_session *session; + + if (mds >= mdsc->max_sessions || mdsc->sessions[mds] == NULL) + return NULL; + session = mdsc->sessions[mds]; + dout("lookup_mds_session %p %d\n", session, + atomic_read(&session->s_ref)); + get_session(session); + return session; +} + +static bool __have_session(struct ceph_mds_client *mdsc, int mds) +{ + if (mds >= mdsc->max_sessions) + return false; + return mdsc->sessions[mds]; +} + +/* + * create+register a new session for given mds. + * called under mdsc->mutex. + */ +static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, + int mds) +{ + struct ceph_mds_session *s; + + s = kzalloc(sizeof(*s), GFP_NOFS); + s->s_mdsc = mdsc; + s->s_mds = mds; + s->s_state = CEPH_MDS_SESSION_NEW; + s->s_ttl = 0; + s->s_seq = 0; + mutex_init(&s->s_mutex); + + ceph_con_init(mdsc->client->msgr, &s->s_con); + s->s_con.private = s; + s->s_con.ops = &mds_con_ops; + s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; + s->s_con.peer_name.num = cpu_to_le64(mds); + ceph_con_open(&s->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); + + spin_lock_init(&s->s_cap_lock); + s->s_cap_gen = 0; + s->s_cap_ttl = 0; + s->s_renew_requested = 0; + s->s_renew_seq = 0; + INIT_LIST_HEAD(&s->s_caps); + s->s_nr_caps = 0; + atomic_set(&s->s_ref, 1); + INIT_LIST_HEAD(&s->s_waiting); + INIT_LIST_HEAD(&s->s_unsafe); + s->s_num_cap_releases = 0; + INIT_LIST_HEAD(&s->s_cap_releases); + INIT_LIST_HEAD(&s->s_cap_releases_done); + INIT_LIST_HEAD(&s->s_cap_flushing); + INIT_LIST_HEAD(&s->s_cap_snaps_flushing); + + dout("register_session mds%d\n", mds); + if (mds >= mdsc->max_sessions) { + int newmax = 1 << get_count_order(mds+1); + struct ceph_mds_session **sa; + + dout("register_session realloc to %d\n", newmax); + sa = kcalloc(newmax, sizeof(void *), GFP_NOFS); + if (sa == NULL) + return ERR_PTR(-ENOMEM); + if (mdsc->sessions) { + memcpy(sa, mdsc->sessions, + mdsc->max_sessions * sizeof(void *)); + kfree(mdsc->sessions); + } + mdsc->sessions = sa; + mdsc->max_sessions = newmax; + } + mdsc->sessions[mds] = s; + atomic_inc(&s->s_ref); /* one ref to sessions[], one to caller */ + return s; +} + +/* + * called under mdsc->mutex + */ +static void unregister_session(struct ceph_mds_client *mdsc, int mds) +{ + dout("unregister_session mds%d %p\n", mds, mdsc->sessions[mds]); + ceph_put_mds_session(mdsc->sessions[mds]); + mdsc->sessions[mds] = NULL; +} + +/* + * drop session refs in request. + * + * should be last request ref, or hold mdsc->mutex + */ +static void put_request_session(struct ceph_mds_request *req) +{ + if (req->r_session) { + ceph_put_mds_session(req->r_session); + req->r_session = NULL; + } +} + +void ceph_mdsc_put_request(struct ceph_mds_request *req) +{ + dout("mdsc put_request %p %d -> %d\n", req, + atomic_read(&req->r_ref), atomic_read(&req->r_ref)-1); + if (atomic_dec_and_test(&req->r_ref)) { + if (req->r_request) + ceph_msg_put(req->r_request); + if (req->r_reply) { + ceph_msg_put(req->r_reply); + destroy_reply_info(&req->r_reply_info); + } + if (req->r_inode) { + ceph_put_cap_refs(ceph_inode(req->r_inode), + CEPH_CAP_PIN); + iput(req->r_inode); + } + if (req->r_locked_dir) + ceph_put_cap_refs(ceph_inode(req->r_locked_dir), + CEPH_CAP_PIN); + if (req->r_target_inode) + iput(req->r_target_inode); + if (req->r_dentry) + dput(req->r_dentry); + if (req->r_old_dentry) { + ceph_put_cap_refs( + ceph_inode(req->r_old_dentry->d_parent->d_inode), + CEPH_CAP_PIN); + dput(req->r_old_dentry); + } + kfree(req->r_path1); + kfree(req->r_path2); + put_request_session(req); + ceph_unreserve_caps(&req->r_caps_reservation); + kfree(req); + } +} + +/* + * lookup session, bump ref if found. + * + * called under mdsc->mutex. + */ +static struct ceph_mds_request *__lookup_request(struct ceph_mds_client *mdsc, + u64 tid) +{ + struct ceph_mds_request *req; + req = radix_tree_lookup(&mdsc->request_tree, tid); + if (req) + ceph_mdsc_get_request(req); + return req; +} + +/* + * Register an in-flight request, and assign a tid. Link to directory + * are modifying (if any). + * + * Called under mdsc->mutex. + */ +static void __register_request(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req, + struct inode *dir) +{ + req->r_tid = ++mdsc->last_tid; + if (req->r_num_caps) + ceph_reserve_caps(&req->r_caps_reservation, req->r_num_caps); + dout("__register_request %p tid %lld\n", req, req->r_tid); + ceph_mdsc_get_request(req); + radix_tree_insert(&mdsc->request_tree, req->r_tid, (void *)req); + + if (dir) { + struct ceph_inode_info *ci = ceph_inode(dir); + + spin_lock(&ci->i_unsafe_lock); + req->r_unsafe_dir = dir; + list_add_tail(&req->r_unsafe_dir_item, &ci->i_unsafe_dirops); + spin_unlock(&ci->i_unsafe_lock); + } +} + +static void __unregister_request(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req) +{ + dout("__unregister_request %p tid %lld\n", req, req->r_tid); + radix_tree_delete(&mdsc->request_tree, req->r_tid); + ceph_mdsc_put_request(req); + + if (req->r_unsafe_dir) { + struct ceph_inode_info *ci = ceph_inode(req->r_unsafe_dir); + + spin_lock(&ci->i_unsafe_lock); + list_del_init(&req->r_unsafe_dir_item); + spin_unlock(&ci->i_unsafe_lock); + } +} + +/* + * Choose mds to send request to next. If there is a hint set in the + * request (e.g., due to a prior forward hint from the mds), use that. + * Otherwise, consult frag tree and/or caps to identify the + * appropriate mds. If all else fails, choose randomly. + * + * Called under mdsc->mutex. + */ +static int __choose_mds(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req) +{ + struct inode *inode; + struct ceph_inode_info *ci; + struct ceph_cap *cap; + int mode = req->r_direct_mode; + int mds = -1; + u32 hash = req->r_direct_hash; + bool is_hash = req->r_direct_is_hash; + + /* + * is there a specific mds we should try? ignore hint if we have + * no session and the mds is not up (active or recovering). + */ + if (req->r_resend_mds >= 0 && + (__have_session(mdsc, req->r_resend_mds) || + ceph_mdsmap_get_state(mdsc->mdsmap, req->r_resend_mds) > 0)) { + dout("choose_mds using resend_mds mds%d\n", + req->r_resend_mds); + return req->r_resend_mds; + } + + if (mode == USE_RANDOM_MDS) + goto random; + + inode = NULL; + if (req->r_inode) { + inode = req->r_inode; + } else if (req->r_dentry) { + if (req->r_dentry->d_inode) { + inode = req->r_dentry->d_inode; + } else { + inode = req->r_dentry->d_parent->d_inode; + hash = req->r_dentry->d_name.hash; + is_hash = true; + } + } + dout("__choose_mds %p is_hash=%d (%d) mode %d\n", inode, (int)is_hash, + (int)hash, mode); + if (!inode) + goto random; + ci = ceph_inode(inode); + + if (is_hash && S_ISDIR(inode->i_mode)) { + struct ceph_inode_frag frag; + int found; + + ceph_choose_frag(ci, hash, &frag, &found); + if (found) { + if (mode == USE_ANY_MDS && frag.ndist > 0) { + u8 r; + + /* choose a random replica */ + get_random_bytes(&r, 1); + r %= frag.ndist; + mds = frag.dist[r]; + dout("choose_mds %p %llx.%llx " + "frag %u mds%d (%d/%d)\n", + inode, ceph_vinop(inode), + frag.frag, frag.mds, + (int)r, frag.ndist); + return mds; + } + + /* since this file/dir wasn't known to be + * replicated, then we want to look for the + * authoritative mds. */ + mode = USE_AUTH_MDS; + if (frag.mds >= 0) { + /* choose auth mds */ + mds = frag.mds; + dout("choose_mds %p %llx.%llx " + "frag %u mds%d (auth)\n", + inode, ceph_vinop(inode), frag.frag, mds); + return mds; + } + } + } + + spin_lock(&inode->i_lock); + cap = NULL; + if (mode == USE_AUTH_MDS) + cap = ci->i_auth_cap; + if (!cap && !RB_EMPTY_ROOT(&ci->i_caps)) + cap = rb_entry(rb_first(&ci->i_caps), struct ceph_cap, ci_node); + if (!cap) { + spin_unlock(&inode->i_lock); + goto random; + } + mds = cap->session->s_mds; + dout("choose_mds %p %llx.%llx mds%d (%scap %p)\n", + inode, ceph_vinop(inode), mds, + cap == ci->i_auth_cap ? "auth " : "", cap); + spin_unlock(&inode->i_lock); + return mds; + +random: + mds = ceph_mdsmap_get_random_mds(mdsc->mdsmap); + dout("choose_mds chose random mds%d\n", mds); + return mds; +} + + +/* + * session messages + */ +static struct ceph_msg *create_session_msg(u32 op, u64 seq) +{ + struct ceph_msg *msg; + struct ceph_mds_session_head *h; + + msg = ceph_msg_new(CEPH_MSG_CLIENT_SESSION, sizeof(*h), 0, 0, NULL); + if (IS_ERR(msg)) { + pr_err("create_session_msg ENOMEM creating msg\n"); + return ERR_PTR(PTR_ERR(msg)); + } + h = msg->front.iov_base; + h->op = cpu_to_le32(op); + h->seq = cpu_to_le64(seq); + return msg; +} + +/* + * send session open request. + * + * called under mdsc->mutex + */ +static int __open_session(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_msg *msg; + int mstate; + int mds = session->s_mds; + int err = 0; + + /* wait for mds to go active? */ + mstate = ceph_mdsmap_get_state(mdsc->mdsmap, mds); + dout("open_session to mds%d (%s)\n", mds, + ceph_mds_state_name(mstate)); + session->s_state = CEPH_MDS_SESSION_OPENING; + session->s_renew_requested = jiffies; + + /* send connect message */ + msg = create_session_msg(CEPH_SESSION_REQUEST_OPEN, session->s_seq); + if (IS_ERR(msg)) { + err = PTR_ERR(msg); + goto out; + } + ceph_con_send(&session->s_con, msg); + +out: + return 0; +} + +/* + * session caps + */ + +/* + * Free preallocated cap messages assigned to this session + */ +static void cleanup_cap_releases(struct ceph_mds_session *session) +{ + struct ceph_msg *msg; + + spin_lock(&session->s_cap_lock); + while (!list_empty(&session->s_cap_releases)) { + msg = list_first_entry(&session->s_cap_releases, + struct ceph_msg, list_head); + list_del_init(&msg->list_head); + ceph_msg_put(msg); + } + while (!list_empty(&session->s_cap_releases_done)) { + msg = list_first_entry(&session->s_cap_releases_done, + struct ceph_msg, list_head); + list_del_init(&msg->list_head); + ceph_msg_put(msg); + } + spin_unlock(&session->s_cap_lock); +} + +/* + * Helper to safely iterate over all caps associated with a session. + * + * caller must hold session s_mutex + */ +static int iterate_session_caps(struct ceph_mds_session *session, + int (*cb)(struct inode *, struct ceph_cap *, + void *), void *arg) +{ + struct ceph_cap *cap, *ncap; + struct inode *inode; + int ret; + + dout("iterate_session_caps %p mds%d\n", session, session->s_mds); + spin_lock(&session->s_cap_lock); + list_for_each_entry_safe(cap, ncap, &session->s_caps, session_caps) { + inode = igrab(&cap->ci->vfs_inode); + if (!inode) + continue; + spin_unlock(&session->s_cap_lock); + ret = cb(inode, cap, arg); + iput(inode); + if (ret < 0) + return ret; + spin_lock(&session->s_cap_lock); + } + spin_unlock(&session->s_cap_lock); + + return 0; +} + +static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, + void *arg) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + dout("removing cap %p, ci is %p, inode is %p\n", + cap, ci, &ci->vfs_inode); + ceph_remove_cap(cap); + return 0; +} + +/* + * caller must hold session s_mutex + */ +static void remove_session_caps(struct ceph_mds_session *session) +{ + dout("remove_session_caps on %p\n", session); + iterate_session_caps(session, remove_session_caps_cb, NULL); + BUG_ON(session->s_nr_caps > 0); + cleanup_cap_releases(session); +} + +/* + * wake up any threads waiting on this session's caps. if the cap is + * old (didn't get renewed on the client reconnect), remove it now. + * + * caller must hold s_mutex. + */ +static int wake_up_session_cb(struct inode *inode, struct ceph_cap *cap, + void *arg) +{ + struct ceph_mds_session *session = arg; + + spin_lock(&inode->i_lock); + if (cap->gen != session->s_cap_gen) { + pr_err("failed reconnect %p %llx.%llx cap %p " + "(gen %d < session %d)\n", inode, ceph_vinop(inode), + cap, cap->gen, session->s_cap_gen); + __ceph_remove_cap(cap, NULL); + } + wake_up(&ceph_inode(inode)->i_cap_wq); + spin_unlock(&inode->i_lock); + return 0; +} + +static void wake_up_session_caps(struct ceph_mds_session *session) +{ + dout("wake_up_session_caps %p mds%d\n", session, session->s_mds); + iterate_session_caps(session, wake_up_session_cb, session); +} + +/* + * Send periodic message to MDS renewing all currently held caps. The + * ack will reset the expiration for all caps from this session. + * + * caller holds s_mutex + */ +static int send_renew_caps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_msg *msg; + int state; + + if (time_after_eq(jiffies, session->s_cap_ttl) && + time_after_eq(session->s_cap_ttl, session->s_renew_requested)) + pr_info("mds%d caps stale\n", session->s_mds); + + /* do not try to renew caps until a recovering mds has reconnected + * with its clients. */ + state = ceph_mdsmap_get_state(mdsc->mdsmap, session->s_mds); + if (state < CEPH_MDS_STATE_RECONNECT) { + dout("send_renew_caps ignoring mds%d (%s)\n", + session->s_mds, ceph_mds_state_name(state)); + return 0; + } + + dout("send_renew_caps to mds%d (%s)\n", session->s_mds, + ceph_mds_state_name(state)); + session->s_renew_requested = jiffies; + msg = create_session_msg(CEPH_SESSION_REQUEST_RENEWCAPS, + ++session->s_renew_seq); + if (IS_ERR(msg)) + return PTR_ERR(msg); + ceph_con_send(&session->s_con, msg); + return 0; +} + +/* + * Note new cap ttl, and any transition from stale -> not stale (fresh?). + */ +static void renewed_caps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session, int is_renew) +{ + int was_stale; + int wake = 0; + + spin_lock(&session->s_cap_lock); + was_stale = is_renew && (session->s_cap_ttl == 0 || + time_after_eq(jiffies, session->s_cap_ttl)); + + session->s_cap_ttl = session->s_renew_requested + + mdsc->mdsmap->m_session_timeout*HZ; + + if (was_stale) { + if (time_before(jiffies, session->s_cap_ttl)) { + pr_info("mds%d caps renewed\n", session->s_mds); + wake = 1; + } else { + pr_info("mds%d caps still stale\n", session->s_mds); + } + } + dout("renewed_caps mds%d ttl now %lu, was %s, now %s\n", + session->s_mds, session->s_cap_ttl, was_stale ? "stale" : "fresh", + time_before(jiffies, session->s_cap_ttl) ? "stale" : "fresh"); + spin_unlock(&session->s_cap_lock); + + if (wake) + wake_up_session_caps(session); +} + +/* + * send a session close request + */ +static int request_close_session(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_msg *msg; + int err = 0; + + dout("request_close_session mds%d state %s seq %lld\n", + session->s_mds, session_state_name(session->s_state), + session->s_seq); + msg = create_session_msg(CEPH_SESSION_REQUEST_CLOSE, session->s_seq); + if (IS_ERR(msg)) + err = PTR_ERR(msg); + else + ceph_con_send(&session->s_con, msg); + return err; +} + +/* + * Called with s_mutex held. + */ +static int __close_session(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + if (session->s_state >= CEPH_MDS_SESSION_CLOSING) + return 0; + session->s_state = CEPH_MDS_SESSION_CLOSING; + return request_close_session(mdsc, session); +} + +/* + * Trim old(er) caps. + * + * Because we can't cache an inode without one or more caps, we do + * this indirectly: if a cap is unused, we prune its aliases, at which + * point the inode will hopefully get dropped to. + * + * Yes, this is a bit sloppy. Our only real goal here is to respond to + * memory pressure from the MDS, though, so it needn't be perfect. + */ +static int trim_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg) +{ + struct ceph_mds_session *session = arg; + struct ceph_inode_info *ci = ceph_inode(inode); + int used, oissued, mine; + + if (session->s_trim_caps <= 0) + return -1; + + spin_lock(&inode->i_lock); + mine = cap->issued | cap->implemented; + used = __ceph_caps_used(ci); + oissued = __ceph_caps_issued_other(ci, cap); + + dout("trim_caps_cb %p cap %p mine %s oissued %s used %s\n", + inode, cap, ceph_cap_string(mine), ceph_cap_string(oissued), + ceph_cap_string(used)); + if (ci->i_dirty_caps) + goto out; /* dirty caps */ + if ((used & ~oissued) & mine) + goto out; /* we need these caps */ + + session->s_trim_caps--; + if (oissued) { + /* we aren't the only cap.. just remove us */ + __ceph_remove_cap(cap, NULL); + } else { + /* try to drop referring dentries */ + spin_unlock(&inode->i_lock); + d_prune_aliases(inode); + dout("trim_caps_cb %p cap %p pruned, count now %d\n", + inode, cap, atomic_read(&inode->i_count)); + return 0; + } + +out: + spin_unlock(&inode->i_lock); + return 0; +} + +/* + * Trim session cap count down to some max number. + */ +static int trim_caps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session, + int max_caps) +{ + int trim_caps = session->s_nr_caps - max_caps; + + dout("trim_caps mds%d start: %d / %d, trim %d\n", + session->s_mds, session->s_nr_caps, max_caps, trim_caps); + if (trim_caps > 0) { + session->s_trim_caps = trim_caps; + iterate_session_caps(session, trim_caps_cb, session); + dout("trim_caps mds%d done: %d / %d, trimmed %d\n", + session->s_mds, session->s_nr_caps, max_caps, + trim_caps - session->s_trim_caps); + } + return 0; +} + +/* + * Allocate cap_release messages. If there is a partially full message + * in the queue, try to allocate enough to cover it's remainder, so that + * we can send it immediately. + * + * Called under s_mutex. + */ +static int add_cap_releases(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session, + int extra) +{ + struct ceph_msg *msg; + struct ceph_mds_cap_release *head; + int err = -ENOMEM; + + if (extra < 0) + extra = mdsc->client->mount_args.cap_release_safety; + + spin_lock(&session->s_cap_lock); + + if (!list_empty(&session->s_cap_releases)) { + msg = list_first_entry(&session->s_cap_releases, + struct ceph_msg, + list_head); + head = msg->front.iov_base; + extra += CEPH_CAPS_PER_RELEASE - le32_to_cpu(head->num); + } + + while (session->s_num_cap_releases < session->s_nr_caps + extra) { + spin_unlock(&session->s_cap_lock); + msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPRELEASE, PAGE_CACHE_SIZE, + 0, 0, NULL); + if (!msg) + goto out_unlocked; + dout("add_cap_releases %p msg %p now %d\n", session, msg, + (int)msg->front.iov_len); + head = msg->front.iov_base; + head->num = cpu_to_le32(0); + msg->front.iov_len = sizeof(*head); + spin_lock(&session->s_cap_lock); + list_add(&msg->list_head, &session->s_cap_releases); + session->s_num_cap_releases += CEPH_CAPS_PER_RELEASE; + } + + if (!list_empty(&session->s_cap_releases)) { + msg = list_first_entry(&session->s_cap_releases, + struct ceph_msg, + list_head); + head = msg->front.iov_base; + if (head->num) { + dout(" queueing non-full %p (%d)\n", msg, + le32_to_cpu(head->num)); + list_move_tail(&msg->list_head, + &session->s_cap_releases_done); + session->s_num_cap_releases -= + CEPH_CAPS_PER_RELEASE - le32_to_cpu(head->num); + } + } + err = 0; + spin_unlock(&session->s_cap_lock); +out_unlocked: + return err; +} + +/* + * flush all dirty inode data to disk. + * + * returns true if we've flushed through want_flush_seq + */ +static int check_cap_flush(struct ceph_mds_client *mdsc, u64 want_flush_seq) +{ + int mds, ret = 1; + + dout("check_cap_flush want %lld\n", want_flush_seq); + mutex_lock(&mdsc->mutex); + for (mds = 0; ret && mds < mdsc->max_sessions; mds++) { + struct ceph_mds_session *session = mdsc->sessions[mds]; + + if (!session) + continue; + get_session(session); + mutex_unlock(&mdsc->mutex); + + mutex_lock(&session->s_mutex); + if (!list_empty(&session->s_cap_flushing)) { + struct ceph_inode_info *ci = + list_entry(session->s_cap_flushing.next, + struct ceph_inode_info, + i_flushing_item); + struct inode *inode = &ci->vfs_inode; + + spin_lock(&inode->i_lock); + if (ci->i_cap_flush_seq <= want_flush_seq) { + dout("check_cap_flush still flushing %p " + "seq %lld <= %lld to mds%d\n", inode, + ci->i_cap_flush_seq, want_flush_seq, + session->s_mds); + ret = 0; + } + spin_unlock(&inode->i_lock); + } + mutex_unlock(&session->s_mutex); + ceph_put_mds_session(session); + + if (!ret) + return ret; + mutex_lock(&mdsc->mutex); + } + + mutex_unlock(&mdsc->mutex); + dout("check_cap_flush ok, flushed thru %lld\n", want_flush_seq); + return ret; +} + +/* + * called under s_mutex + */ +static void send_cap_releases(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_msg *msg; + + dout("send_cap_releases mds%d\n", session->s_mds); + while (1) { + spin_lock(&session->s_cap_lock); + if (list_empty(&session->s_cap_releases_done)) + break; + msg = list_first_entry(&session->s_cap_releases_done, + struct ceph_msg, list_head); + list_del_init(&msg->list_head); + spin_unlock(&session->s_cap_lock); + msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); + dout("send_cap_releases mds%d %p\n", session->s_mds, msg); + ceph_con_send(&session->s_con, msg); + } + spin_unlock(&session->s_cap_lock); +} + +/* + * requests + */ + +/* + * Create an mds request. + */ +struct ceph_mds_request * +ceph_mdsc_create_request(struct ceph_mds_client *mdsc, int op, int mode) +{ + struct ceph_mds_request *req = kzalloc(sizeof(*req), GFP_NOFS); + + if (!req) + return ERR_PTR(-ENOMEM); + + req->r_started = jiffies; + req->r_resend_mds = -1; + INIT_LIST_HEAD(&req->r_unsafe_dir_item); + req->r_fmode = -1; + atomic_set(&req->r_ref, 1); /* one for request_tree, one for caller */ + INIT_LIST_HEAD(&req->r_wait); + init_completion(&req->r_completion); + init_completion(&req->r_safe_completion); + INIT_LIST_HEAD(&req->r_unsafe_item); + + req->r_op = op; + req->r_direct_mode = mode; + return req; +} + +/* + * return oldest (lowest) tid in request tree, 0 if none. + * + * called under mdsc->mutex. + */ +static u64 __get_oldest_tid(struct ceph_mds_client *mdsc) +{ + struct ceph_mds_request *first; + if (radix_tree_gang_lookup(&mdsc->request_tree, + (void **)&first, 0, 1) <= 0) + return 0; + return first->r_tid; +} + +/* + * Build a dentry's path. Allocate on heap; caller must kfree. Based + * on build_path_from_dentry in fs/cifs/dir.c. + * + * If @stop_on_nosnap, generate path relative to the first non-snapped + * inode. + * + * Encode hidden .snap dirs as a double /, i.e. + * foo/.snap/bar -> foo//bar + */ +char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, + int stop_on_nosnap) +{ + struct dentry *temp; + char *path; + int len, pos; + + if (dentry == NULL) + return ERR_PTR(-EINVAL); + +retry: + len = 0; + for (temp = dentry; !IS_ROOT(temp);) { + struct inode *inode = temp->d_inode; + if (inode && ceph_snap(inode) == CEPH_SNAPDIR) + len++; /* slash only */ + else if (stop_on_nosnap && inode && + ceph_snap(inode) == CEPH_NOSNAP) + break; + else + len += 1 + temp->d_name.len; + temp = temp->d_parent; + if (temp == NULL) { + pr_err("build_path_dentry corrupt dentry %p\n", dentry); + return ERR_PTR(-EINVAL); + } + } + if (len) + len--; /* no leading '/' */ + + path = kmalloc(len+1, GFP_NOFS); + if (path == NULL) + return ERR_PTR(-ENOMEM); + pos = len; + path[pos] = 0; /* trailing null */ + for (temp = dentry; !IS_ROOT(temp) && pos != 0; ) { + struct inode *inode = temp->d_inode; + + if (inode && ceph_snap(inode) == CEPH_SNAPDIR) { + dout("build_path_dentry path+%d: %p SNAPDIR\n", + pos, temp); + } else if (stop_on_nosnap && inode && + ceph_snap(inode) == CEPH_NOSNAP) { + break; + } else { + pos -= temp->d_name.len; + if (pos < 0) + break; + strncpy(path + pos, temp->d_name.name, + temp->d_name.len); + dout("build_path_dentry path+%d: %p '%.*s'\n", + pos, temp, temp->d_name.len, path + pos); + } + if (pos) + path[--pos] = '/'; + temp = temp->d_parent; + if (temp == NULL) { + pr_err("build_path_dentry corrupt dentry\n"); + kfree(path); + return ERR_PTR(-EINVAL); + } + } + if (pos != 0) { + pr_err("build_path_dentry did not end path lookup where " + "expected, namelen is %d, pos is %d\n", len, pos); + /* presumably this is only possible if racing with a + rename of one of the parent directories (we can not + lock the dentries above us to prevent this, but + retrying should be harmless) */ + kfree(path); + goto retry; + } + + *base = ceph_ino(temp->d_inode); + *plen = len; + dout("build_path_dentry on %p %d built %llx '%.*s'\n", + dentry, atomic_read(&dentry->d_count), *base, len, path); + return path; +} + +static int build_dentry_path(struct dentry *dentry, + const char **ppath, int *ppathlen, u64 *pino, + int *pfreepath) +{ + char *path; + + if (ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP) { + *pino = ceph_ino(dentry->d_parent->d_inode); + *ppath = dentry->d_name.name; + *ppathlen = dentry->d_name.len; + return 0; + } + path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1); + if (IS_ERR(path)) + return PTR_ERR(path); + *ppath = path; + *pfreepath = 1; + return 0; +} + +static int build_inode_path(struct inode *inode, + const char **ppath, int *ppathlen, u64 *pino, + int *pfreepath) +{ + struct dentry *dentry; + char *path; + + if (ceph_snap(inode) == CEPH_NOSNAP) { + *pino = ceph_ino(inode); + *ppathlen = 0; + return 0; + } + dentry = d_find_alias(inode); + path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1); + dput(dentry); + if (IS_ERR(path)) + return PTR_ERR(path); + *ppath = path; + *pfreepath = 1; + return 0; +} + +/* + * request arguments may be specified via an inode *, a dentry *, or + * an explicit ino+path. + */ +static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, + const char *rpath, u64 rino, + const char **ppath, int *pathlen, + u64 *ino, int *freepath) +{ + int r = 0; + + if (rinode) { + r = build_inode_path(rinode, ppath, pathlen, ino, freepath); + dout(" inode %p %llx.%llx\n", rinode, ceph_ino(rinode), + ceph_snap(rinode)); + } else if (rdentry) { + r = build_dentry_path(rdentry, ppath, pathlen, ino, freepath); + dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen, + *ppath); + } else if (rpath) { + *ino = rino; + *ppath = rpath; + *pathlen = strlen(rpath); + dout(" path %.*s\n", *pathlen, rpath); + } + + return r; +} + +/* + * called under mdsc->mutex + */ +static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req, + int mds) +{ + struct ceph_msg *msg; + struct ceph_mds_request_head *head; + const char *path1 = NULL; + const char *path2 = NULL; + u64 ino1 = 0, ino2 = 0; + int pathlen1 = 0, pathlen2 = 0; + int freepath1 = 0, freepath2 = 0; + int len; + u16 releases; + void *p, *end; + int ret; + + ret = set_request_path_attr(req->r_inode, req->r_dentry, + req->r_path1, req->r_ino1.ino, + &path1, &pathlen1, &ino1, &freepath1); + if (ret < 0) { + msg = ERR_PTR(ret); + goto out; + } + + ret = set_request_path_attr(NULL, req->r_old_dentry, + req->r_path2, req->r_ino2.ino, + &path2, &pathlen2, &ino2, &freepath2); + if (ret < 0) { + msg = ERR_PTR(ret); + goto out_free1; + } + + len = sizeof(*head) + + pathlen1 + pathlen2 + 2*(sizeof(u32) + sizeof(u64)); + + /* calculate (max) length for cap releases */ + len += sizeof(struct ceph_mds_request_release) * + (!!req->r_inode_drop + !!req->r_dentry_drop + + !!req->r_old_inode_drop + !!req->r_old_dentry_drop); + if (req->r_dentry_drop) + len += req->r_dentry->d_name.len; + if (req->r_old_dentry_drop) + len += req->r_old_dentry->d_name.len; + + msg = ceph_msg_new(CEPH_MSG_CLIENT_REQUEST, len, 0, 0, NULL); + if (IS_ERR(msg)) + goto out_free2; + + head = msg->front.iov_base; + p = msg->front.iov_base + sizeof(*head); + end = msg->front.iov_base + msg->front.iov_len; + + head->mdsmap_epoch = cpu_to_le32(mdsc->mdsmap->m_epoch); + head->op = cpu_to_le32(req->r_op); + head->caller_uid = cpu_to_le32(current_fsuid()); + head->caller_gid = cpu_to_le32(current_fsgid()); + head->args = req->r_args; + + ceph_encode_filepath(&p, end, ino1, path1); + ceph_encode_filepath(&p, end, ino2, path2); + + /* cap releases */ + releases = 0; + if (req->r_inode_drop) + releases += ceph_encode_inode_release(&p, + req->r_inode ? req->r_inode : req->r_dentry->d_inode, + mds, req->r_inode_drop, req->r_inode_unless, 0); + if (req->r_dentry_drop) + releases += ceph_encode_dentry_release(&p, req->r_dentry, + mds, req->r_dentry_drop, req->r_dentry_unless); + if (req->r_old_dentry_drop) + releases += ceph_encode_dentry_release(&p, req->r_old_dentry, + mds, req->r_old_dentry_drop, req->r_old_dentry_unless); + if (req->r_old_inode_drop) + releases += ceph_encode_inode_release(&p, + req->r_old_dentry->d_inode, + mds, req->r_old_inode_drop, req->r_old_inode_unless, 0); + head->num_releases = cpu_to_le16(releases); + + BUG_ON(p > end); + msg->front.iov_len = p - msg->front.iov_base; + msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); + + msg->pages = req->r_pages; + msg->nr_pages = req->r_num_pages; + msg->hdr.data_len = cpu_to_le32(req->r_data_len); + msg->hdr.data_off = cpu_to_le16(0); + +out_free2: + if (freepath2) + kfree((char *)path2); +out_free1: + if (freepath1) + kfree((char *)path1); +out: + return msg; +} + +/* + * called under mdsc->mutex if error, under no mutex if + * success. + */ +static void complete_request(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req) +{ + if (req->r_callback) + req->r_callback(mdsc, req); + else + complete(&req->r_completion); +} + +/* + * called under mdsc->mutex + */ +static int __prepare_send_request(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req, + int mds) +{ + struct ceph_mds_request_head *rhead; + struct ceph_msg *msg; + int flags = 0; + + req->r_mds = mds; + req->r_attempts++; + dout("prepare_send_request %p tid %lld %s (attempt %d)\n", req, + req->r_tid, ceph_mds_op_name(req->r_op), req->r_attempts); + + if (req->r_request) { + ceph_msg_put(req->r_request); + req->r_request = NULL; + } + msg = create_request_message(mdsc, req, mds); + if (IS_ERR(msg)) { + req->r_reply = ERR_PTR(PTR_ERR(msg)); + complete_request(mdsc, req); + return -PTR_ERR(msg); + } + req->r_request = msg; + + rhead = msg->front.iov_base; + rhead->tid = cpu_to_le64(req->r_tid); + rhead->oldest_client_tid = cpu_to_le64(__get_oldest_tid(mdsc)); + if (req->r_got_unsafe) + flags |= CEPH_MDS_FLAG_REPLAY; + if (req->r_locked_dir) + flags |= CEPH_MDS_FLAG_WANT_DENTRY; + rhead->flags = cpu_to_le32(flags); + rhead->num_fwd = req->r_num_fwd; + rhead->num_retry = req->r_attempts - 1; + + dout(" r_locked_dir = %p\n", req->r_locked_dir); + + if (req->r_target_inode && req->r_got_unsafe) + rhead->ino = cpu_to_le64(ceph_ino(req->r_target_inode)); + else + rhead->ino = 0; + return 0; +} + +/* + * send request, or put it on the appropriate wait list. + */ +static int __do_request(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req) +{ + struct ceph_mds_session *session = NULL; + int mds = -1; + int err = -EAGAIN; + + if (req->r_reply) + goto out; + + if (req->r_timeout && + time_after_eq(jiffies, req->r_started + req->r_timeout)) { + dout("do_request timed out\n"); + err = -EIO; + goto finish; + } + + mds = __choose_mds(mdsc, req); + if (mds < 0 || + ceph_mdsmap_get_state(mdsc->mdsmap, mds) < CEPH_MDS_STATE_ACTIVE) { + dout("do_request no mds or not active, waiting for map\n"); + list_add(&req->r_wait, &mdsc->waiting_for_map); + goto out; + } + + /* get, open session */ + session = __ceph_lookup_mds_session(mdsc, mds); + if (!session) + session = register_session(mdsc, mds); + dout("do_request mds%d session %p state %s\n", mds, session, + session_state_name(session->s_state)); + if (session->s_state != CEPH_MDS_SESSION_OPEN && + session->s_state != CEPH_MDS_SESSION_HUNG) { + if (session->s_state == CEPH_MDS_SESSION_NEW || + session->s_state == CEPH_MDS_SESSION_CLOSING) + __open_session(mdsc, session); + list_add(&req->r_wait, &session->s_waiting); + goto out_session; + } + + /* send request */ + req->r_session = get_session(session); + req->r_resend_mds = -1; /* forget any previous mds hint */ + + if (req->r_request_started == 0) /* note request start time */ + req->r_request_started = jiffies; + + err = __prepare_send_request(mdsc, req, mds); + if (!err) { + ceph_msg_get(req->r_request); + ceph_con_send(&session->s_con, req->r_request); + } + +out_session: + ceph_put_mds_session(session); +out: + return err; + +finish: + req->r_reply = ERR_PTR(err); + complete_request(mdsc, req); + goto out; +} + +/* + * called under mdsc->mutex + */ +static void __wake_requests(struct ceph_mds_client *mdsc, + struct list_head *head) +{ + struct ceph_mds_request *req, *nreq; + + list_for_each_entry_safe(req, nreq, head, r_wait) { + list_del_init(&req->r_wait); + __do_request(mdsc, req); + } +} + +/* + * Wake up threads with requests pending for @mds, so that they can + * resubmit their requests to a possibly different mds. If @all is set, + * wake up if their requests has been forwarded to @mds, too. + */ +static void kick_requests(struct ceph_mds_client *mdsc, int mds, int all) +{ + struct ceph_mds_request *reqs[10]; + u64 nexttid = 0; + int i, got; + + dout("kick_requests mds%d\n", mds); + while (nexttid <= mdsc->last_tid) { + got = radix_tree_gang_lookup(&mdsc->request_tree, + (void **)&reqs, nexttid, 10); + if (got == 0) + break; + nexttid = reqs[got-1]->r_tid + 1; + for (i = 0; i < got; i++) { + if (reqs[i]->r_got_unsafe) + continue; + if (reqs[i]->r_session && + reqs[i]->r_session->s_mds == mds) { + dout(" kicking tid %llu\n", reqs[i]->r_tid); + put_request_session(reqs[i]); + __do_request(mdsc, reqs[i]); + } + } + } +} + +void ceph_mdsc_submit_request(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req) +{ + dout("submit_request on %p\n", req); + mutex_lock(&mdsc->mutex); + __register_request(mdsc, req, NULL); + __do_request(mdsc, req); + mutex_unlock(&mdsc->mutex); +} + +/* + * Synchrously perform an mds request. Take care of all of the + * session setup, forwarding, retry details. + */ +int ceph_mdsc_do_request(struct ceph_mds_client *mdsc, + struct inode *dir, + struct ceph_mds_request *req) +{ + int err; + + dout("do_request on %p\n", req); + + /* take CAP_PIN refs for r_inode, r_locked_dir, r_old_dentry */ + if (req->r_inode) + ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN); + if (req->r_locked_dir) + ceph_get_cap_refs(ceph_inode(req->r_locked_dir), CEPH_CAP_PIN); + if (req->r_old_dentry) + ceph_get_cap_refs( + ceph_inode(req->r_old_dentry->d_parent->d_inode), + CEPH_CAP_PIN); + + /* issue */ + mutex_lock(&mdsc->mutex); + __register_request(mdsc, req, dir); + __do_request(mdsc, req); + + /* wait */ + if (!req->r_reply) { + mutex_unlock(&mdsc->mutex); + if (req->r_timeout) { + err = wait_for_completion_timeout(&req->r_completion, + req->r_timeout); + if (err > 0) + err = 0; + else if (err == 0) + req->r_reply = ERR_PTR(-EIO); + } else { + wait_for_completion(&req->r_completion); + } + mutex_lock(&mdsc->mutex); + } + + if (IS_ERR(req->r_reply)) { + err = PTR_ERR(req->r_reply); + req->r_reply = NULL; + + /* clean up */ + __unregister_request(mdsc, req); + if (!list_empty(&req->r_unsafe_item)) + list_del_init(&req->r_unsafe_item); + complete(&req->r_safe_completion); + } else if (req->r_err) { + err = req->r_err; + } else { + err = le32_to_cpu(req->r_reply_info.head->result); + } + mutex_unlock(&mdsc->mutex); + + dout("do_request %p done, result %d\n", req, err); + return err; +} + +/* + * Handle mds reply. + * + * We take the session mutex and parse and process the reply immediately. + * This preserves the logical ordering of replies, capabilities, etc., sent + * by the MDS as they are applied to our local cache. + */ +static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) +{ + struct ceph_mds_client *mdsc = session->s_mdsc; + struct ceph_mds_request *req; + struct ceph_mds_reply_head *head = msg->front.iov_base; + struct ceph_mds_reply_info_parsed *rinfo; /* parsed reply info */ + u64 tid; + int err, result; + int mds; + + if (msg->hdr.src.name.type != CEPH_ENTITY_TYPE_MDS) + return; + if (msg->front.iov_len < sizeof(*head)) { + pr_err("mdsc_handle_reply got corrupt (short) reply\n"); + return; + } + + /* get request, session */ + tid = le64_to_cpu(head->tid); + mutex_lock(&mdsc->mutex); + req = __lookup_request(mdsc, tid); + if (!req) { + dout("handle_reply on unknown tid %llu\n", tid); + mutex_unlock(&mdsc->mutex); + return; + } + dout("handle_reply %p\n", req); + mds = le64_to_cpu(msg->hdr.src.name.num); + + /* correct session? */ + if (!req->r_session && req->r_session != session) { + pr_err("mdsc_handle_reply got %llu on session mds%d" + " not mds%d\n", tid, session->s_mds, + req->r_session ? req->r_session->s_mds : -1); + mutex_unlock(&mdsc->mutex); + goto out; + } + + /* dup? */ + if ((req->r_got_unsafe && !head->safe) || + (req->r_got_safe && head->safe)) { + pr_warning("got a dup %s reply on %llu from mds%d\n", + head->safe ? "safe" : "unsafe", tid, mds); + mutex_unlock(&mdsc->mutex); + goto out; + } + + result = le32_to_cpu(head->result); + + /* + * Tolerate 2 consecutive ESTALEs from the same mds. + * FIXME: we should be looking at the cap migrate_seq. + */ + if (result == -ESTALE) { + req->r_direct_mode = USE_AUTH_MDS; + req->r_num_stale++; + if (req->r_num_stale <= 2) { + __do_request(mdsc, req); + mutex_unlock(&mdsc->mutex); + goto out; + } + } else { + req->r_num_stale = 0; + } + + if (head->safe) { + req->r_got_safe = true; + __unregister_request(mdsc, req); + complete(&req->r_safe_completion); + + if (req->r_got_unsafe) { + /* + * We already handled the unsafe response, now do the + * cleanup. No need to examine the response; the MDS + * doesn't include any result info in the safe + * response. And even if it did, there is nothing + * useful we could do with a revised return value. + */ + dout("got safe reply %llu, mds%d\n", tid, mds); + list_del_init(&req->r_unsafe_item); + + /* last unsafe request during umount? */ + if (mdsc->stopping && !__get_oldest_tid(mdsc)) + complete(&mdsc->safe_umount_waiters); + mutex_unlock(&mdsc->mutex); + goto out; + } + } + + BUG_ON(req->r_reply); + + if (!head->safe) { + req->r_got_unsafe = true; + list_add_tail(&req->r_unsafe_item, &req->r_session->s_unsafe); + } + + dout("handle_reply tid %lld result %d\n", tid, result); + rinfo = &req->r_reply_info; + err = parse_reply_info(msg, rinfo); + mutex_unlock(&mdsc->mutex); + + mutex_lock(&session->s_mutex); + if (err < 0) { + pr_err("mdsc_handle_reply got corrupt reply mds%d\n", mds); + goto out_err; + } + + /* snap trace */ + if (rinfo->snapblob_len) { + down_write(&mdsc->snap_rwsem); + ceph_update_snap_trace(mdsc, rinfo->snapblob, + rinfo->snapblob + rinfo->snapblob_len, + le32_to_cpu(head->op) == CEPH_MDS_OP_RMSNAP); + downgrade_write(&mdsc->snap_rwsem); + } else { + down_read(&mdsc->snap_rwsem); + } + + /* insert trace into our cache */ + err = ceph_fill_trace(mdsc->client->sb, req, req->r_session); + if (err == 0) { + if (result == 0 && rinfo->dir_nr) + ceph_readdir_prepopulate(req, req->r_session); + ceph_unreserve_caps(&req->r_caps_reservation); + } + + up_read(&mdsc->snap_rwsem); +out_err: + if (err) { + req->r_err = err; + } else { + req->r_reply = msg; + ceph_msg_get(msg); + } + + add_cap_releases(mdsc, req->r_session, -1); + mutex_unlock(&session->s_mutex); + + /* kick calling process */ + complete_request(mdsc, req); +out: + ceph_mdsc_put_request(req); + return; +} + + + +/* + * handle mds notification that our request has been forwarded. + */ +static void handle_forward(struct ceph_mds_client *mdsc, struct ceph_msg *msg) +{ + struct ceph_mds_request *req; + u64 tid; + u32 next_mds; + u32 fwd_seq; + u8 must_resend; + int err = -EINVAL; + void *p = msg->front.iov_base; + void *end = p + msg->front.iov_len; + int from_mds, state; + + if (msg->hdr.src.name.type != CEPH_ENTITY_TYPE_MDS) + goto bad; + from_mds = le64_to_cpu(msg->hdr.src.name.num); + + ceph_decode_need(&p, end, sizeof(u64)+2*sizeof(u32), bad); + ceph_decode_64(&p, tid); + ceph_decode_32(&p, next_mds); + ceph_decode_32(&p, fwd_seq); + ceph_decode_8(&p, must_resend); + + WARN_ON(must_resend); /* shouldn't happen. */ + + mutex_lock(&mdsc->mutex); + req = __lookup_request(mdsc, tid); + if (!req) { + dout("forward %llu dne\n", tid); + goto out; /* dup reply? */ + } + + state = mdsc->sessions[next_mds]->s_state; + if (fwd_seq <= req->r_num_fwd) { + dout("forward %llu to mds%d - old seq %d <= %d\n", + tid, next_mds, req->r_num_fwd, fwd_seq); + } else { + /* resend. forward race not possible; mds would drop */ + dout("forward %llu to mds%d (we resend)\n", tid, next_mds); + req->r_num_fwd = fwd_seq; + req->r_resend_mds = next_mds; + put_request_session(req); + __do_request(mdsc, req); + } + ceph_mdsc_put_request(req); +out: + mutex_unlock(&mdsc->mutex); + return; + +bad: + pr_err("mdsc_handle_forward decode error err=%d\n", err); +} + +/* + * handle a mds session control message + */ +static void handle_session(struct ceph_mds_session *session, + struct ceph_msg *msg) +{ + struct ceph_mds_client *mdsc = session->s_mdsc; + u32 op; + u64 seq; + int mds; + struct ceph_mds_session_head *h = msg->front.iov_base; + int wake = 0; + + if (msg->hdr.src.name.type != CEPH_ENTITY_TYPE_MDS) + return; + mds = le64_to_cpu(msg->hdr.src.name.num); + + /* decode */ + if (msg->front.iov_len != sizeof(*h)) + goto bad; + op = le32_to_cpu(h->op); + seq = le64_to_cpu(h->seq); + + mutex_lock(&mdsc->mutex); + /* FIXME: this ttl calculation is generous */ + session->s_ttl = jiffies + HZ*mdsc->mdsmap->m_session_autoclose; + mutex_unlock(&mdsc->mutex); + + mutex_lock(&session->s_mutex); + + dout("handle_session mds%d %s %p state %s seq %llu\n", + mds, ceph_session_op_name(op), session, + session_state_name(session->s_state), seq); + + if (session->s_state == CEPH_MDS_SESSION_HUNG) { + session->s_state = CEPH_MDS_SESSION_OPEN; + pr_info("mds%d came back\n", session->s_mds); + } + + switch (op) { + case CEPH_SESSION_OPEN: + session->s_state = CEPH_MDS_SESSION_OPEN; + renewed_caps(mdsc, session, 0); + wake = 1; + if (mdsc->stopping) + __close_session(mdsc, session); + break; + + case CEPH_SESSION_RENEWCAPS: + if (session->s_renew_seq == seq) + renewed_caps(mdsc, session, 1); + break; + + case CEPH_SESSION_CLOSE: + unregister_session(mdsc, mds); + remove_session_caps(session); + wake = 1; /* for good measure */ + complete(&mdsc->session_close_waiters); + kick_requests(mdsc, mds, 0); /* cur only */ + break; + + case CEPH_SESSION_STALE: + pr_info("mds%d caps went stale, renewing\n", + session->s_mds); + spin_lock(&session->s_cap_lock); + session->s_cap_gen++; + session->s_cap_ttl = 0; + spin_unlock(&session->s_cap_lock); + send_renew_caps(mdsc, session); + break; + + case CEPH_SESSION_RECALL_STATE: + trim_caps(mdsc, session, le32_to_cpu(h->max_caps)); + break; + + default: + pr_err("mdsc_handle_session bad op %d mds%d\n", op, mds); + WARN_ON(1); + } + + mutex_unlock(&session->s_mutex); + if (wake) { + mutex_lock(&mdsc->mutex); + __wake_requests(mdsc, &session->s_waiting); + mutex_unlock(&mdsc->mutex); + } + return; + +bad: + pr_err("mdsc_handle_session corrupt message mds%d len %d\n", mds, + (int)msg->front.iov_len); + return; +} + + +/* + * called under session->mutex. + */ +static void replay_unsafe_requests(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_mds_request *req, *nreq; + int err; + + dout("replay_unsafe_requests mds%d\n", session->s_mds); + + mutex_lock(&mdsc->mutex); + list_for_each_entry_safe(req, nreq, &session->s_unsafe, r_unsafe_item) { + err = __prepare_send_request(mdsc, req, session->s_mds); + if (!err) { + ceph_msg_get(req->r_request); + ceph_con_send(&session->s_con, req->r_request); + } + } + mutex_unlock(&mdsc->mutex); +} + +/* + * Encode information about a cap for a reconnect with the MDS. + */ +struct encode_caps_data { + void **pp; + void *end; + int *num_caps; +}; + +static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, + void *arg) +{ + struct ceph_mds_cap_reconnect *rec; + struct ceph_inode_info *ci; + struct encode_caps_data *data = (struct encode_caps_data *)arg; + void *p = *(data->pp); + void *end = data->end; + char *path; + int pathlen, err; + u64 pathbase; + struct dentry *dentry; + + ci = cap->ci; + + dout(" adding %p ino %llx.%llx cap %p %lld %s\n", + inode, ceph_vinop(inode), cap, cap->cap_id, + ceph_cap_string(cap->issued)); + ceph_decode_need(&p, end, sizeof(u64), needmore); + ceph_encode_64(&p, ceph_ino(inode)); + + dentry = d_find_alias(inode); + if (dentry) { + path = ceph_mdsc_build_path(dentry, &pathlen, &pathbase, 0); + if (IS_ERR(path)) { + err = PTR_ERR(path); + BUG_ON(err); + } + } else { + path = NULL; + pathlen = 0; + } + ceph_decode_need(&p, end, pathlen+4, needmore); + ceph_encode_string(&p, end, path, pathlen); + + ceph_decode_need(&p, end, sizeof(*rec), needmore); + rec = p; + p += sizeof(*rec); + BUG_ON(p > end); + spin_lock(&inode->i_lock); + cap->seq = 0; /* reset cap seq */ + cap->issue_seq = 0; /* and issue_seq */ + rec->cap_id = cpu_to_le64(cap->cap_id); + rec->pathbase = cpu_to_le64(pathbase); + rec->wanted = cpu_to_le32(__ceph_caps_wanted(ci)); + rec->issued = cpu_to_le32(cap->issued); + rec->size = cpu_to_le64(inode->i_size); + ceph_encode_timespec(&rec->mtime, &inode->i_mtime); + ceph_encode_timespec(&rec->atime, &inode->i_atime); + rec->snaprealm = cpu_to_le64(ci->i_snap_realm->ino); + spin_unlock(&inode->i_lock); + + kfree(path); + dput(dentry); + (*data->num_caps)++; + *(data->pp) = p; + return 0; +needmore: + return -ENOSPC; +} + + +/* + * If an MDS fails and recovers, clients need to reconnect in order to + * reestablish shared state. This includes all caps issued through + * this session _and_ the snap_realm hierarchy. Because it's not + * clear which snap realms the mds cares about, we send everything we + * know about.. that ensures we'll then get any new info the + * recovering MDS might have. + * + * This is a relatively heavyweight operation, but it's rare. + * + * called with mdsc->mutex held. + */ +static void send_mds_reconnect(struct ceph_mds_client *mdsc, int mds) +{ + struct ceph_mds_session *session; + struct ceph_msg *reply; + int newlen, len = 4 + 1; + void *p, *end; + int err; + int num_caps, num_realms = 0; + int got; + u64 next_snap_ino = 0; + __le32 *pnum_caps, *pnum_realms; + struct encode_caps_data iter_args; + + pr_info("reconnect to recovering mds%d\n", mds); + + /* find session */ + session = __ceph_lookup_mds_session(mdsc, mds); + mutex_unlock(&mdsc->mutex); /* drop lock for duration */ + + if (session) { + mutex_lock(&session->s_mutex); + + session->s_state = CEPH_MDS_SESSION_RECONNECTING; + session->s_seq = 0; + + ceph_con_open(&session->s_con, + ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); + + /* replay unsafe requests */ + replay_unsafe_requests(mdsc, session); + + /* estimate needed space */ + len += session->s_nr_caps * + (100+sizeof(struct ceph_mds_cap_reconnect)); + pr_info("estimating i need %d bytes for %d caps\n", + len, session->s_nr_caps); + } else { + dout("no session for mds%d, will send short reconnect\n", + mds); + } + + down_read(&mdsc->snap_rwsem); + +retry: + /* build reply */ + reply = ceph_msg_new(CEPH_MSG_CLIENT_RECONNECT, len, 0, 0, NULL); + if (IS_ERR(reply)) { + err = PTR_ERR(reply); + pr_err("send_mds_reconnect ENOMEM on %d for mds%d\n", + len, mds); + goto out; + } + p = reply->front.iov_base; + end = p + len; + + if (!session) { + ceph_encode_8(&p, 1); /* session was closed */ + ceph_encode_32(&p, 0); + goto send; + } + dout("session %p state %s\n", session, + session_state_name(session->s_state)); + + /* traverse this session's caps */ + ceph_encode_8(&p, 0); + pnum_caps = p; + ceph_encode_32(&p, session->s_nr_caps); + num_caps = 0; + + iter_args.pp = &p; + iter_args.end = end; + iter_args.num_caps = &num_caps; + err = iterate_session_caps(session, encode_caps_cb, &iter_args); + if (err == -ENOSPC) + goto needmore; + if (err < 0) + goto out; + *pnum_caps = cpu_to_le32(num_caps); + + /* + * snaprealms. we provide mds with the ino, seq (version), and + * parent for all of our realms. If the mds has any newer info, + * it will tell us. + */ + next_snap_ino = 0; + /* save some space for the snaprealm count */ + pnum_realms = p; + ceph_decode_need(&p, end, sizeof(*pnum_realms), needmore); + p += sizeof(*pnum_realms); + num_realms = 0; + while (1) { + struct ceph_snap_realm *realm; + struct ceph_mds_snaprealm_reconnect *sr_rec; + got = radix_tree_gang_lookup(&mdsc->snap_realms, + (void **)&realm, next_snap_ino, 1); + if (!got) + break; + + dout(" adding snap realm %llx seq %lld parent %llx\n", + realm->ino, realm->seq, realm->parent_ino); + ceph_decode_need(&p, end, sizeof(*sr_rec), needmore); + sr_rec = p; + sr_rec->ino = cpu_to_le64(realm->ino); + sr_rec->seq = cpu_to_le64(realm->seq); + sr_rec->parent = cpu_to_le64(realm->parent_ino); + p += sizeof(*sr_rec); + num_realms++; + next_snap_ino = realm->ino + 1; + } + *pnum_realms = cpu_to_le32(num_realms); + +send: + reply->front.iov_len = p - reply->front.iov_base; + reply->hdr.front_len = cpu_to_le32(reply->front.iov_len); + dout("final len was %u (guessed %d)\n", + (unsigned)reply->front.iov_len, len); + ceph_con_send(&session->s_con, reply); + + if (session) { + session->s_state = CEPH_MDS_SESSION_OPEN; + __wake_requests(mdsc, &session->s_waiting); + } + +out: + up_read(&mdsc->snap_rwsem); + if (session) { + mutex_unlock(&session->s_mutex); + ceph_put_mds_session(session); + } + mutex_lock(&mdsc->mutex); + return; + +needmore: + /* + * we need a larger buffer. this doesn't very accurately + * factor in snap realms, but it's safe. + */ + num_caps += num_realms; + newlen = len * ((100 * (session->s_nr_caps+3)) / (num_caps + 1)) / 100; + pr_info("i guessed %d, and did %d of %d caps, retrying with %d\n", + len, num_caps, session->s_nr_caps, newlen); + len = newlen; + ceph_msg_put(reply); + goto retry; +} + + +/* + * compare old and new mdsmaps, kicking requests + * and closing out old connections as necessary + * + * called under mdsc->mutex. + */ +static void check_new_map(struct ceph_mds_client *mdsc, + struct ceph_mdsmap *newmap, + struct ceph_mdsmap *oldmap) +{ + int i; + int oldstate, newstate; + struct ceph_mds_session *s; + + dout("check_new_map new %u old %u\n", + newmap->m_epoch, oldmap->m_epoch); + + for (i = 0; i < oldmap->m_max_mds && i < mdsc->max_sessions; i++) { + if (mdsc->sessions[i] == NULL) + continue; + s = mdsc->sessions[i]; + oldstate = ceph_mdsmap_get_state(oldmap, i); + newstate = ceph_mdsmap_get_state(newmap, i); + + dout("check_new_map mds%d state %s -> %s (session %s)\n", + i, ceph_mds_state_name(oldstate), + ceph_mds_state_name(newstate), + session_state_name(s->s_state)); + + if (memcmp(ceph_mdsmap_get_addr(oldmap, i), + ceph_mdsmap_get_addr(newmap, i), + sizeof(struct ceph_entity_addr))) { + if (s->s_state == CEPH_MDS_SESSION_OPENING) { + /* the session never opened, just close it + * out now */ + __wake_requests(mdsc, &s->s_waiting); + unregister_session(mdsc, i); + } else { + /* just close it */ + mutex_unlock(&mdsc->mutex); + mutex_lock(&s->s_mutex); + mutex_lock(&mdsc->mutex); + ceph_con_close(&s->s_con); + mutex_unlock(&s->s_mutex); + s->s_state = CEPH_MDS_SESSION_RESTARTING; + } + + /* kick any requests waiting on the recovering mds */ + kick_requests(mdsc, i, 1); + } else if (oldstate == newstate) { + continue; /* nothing new with this mds */ + } + + /* + * send reconnect? + */ + if (s->s_state == CEPH_MDS_SESSION_RESTARTING && + newstate >= CEPH_MDS_STATE_RECONNECT) + send_mds_reconnect(mdsc, i); + + /* + * kick requests on any mds that has gone active. + * + * kick requests on cur or forwarder: we may have sent + * the request to mds1, mds1 told us it forwarded it + * to mds2, but then we learn mds1 failed and can't be + * sure it successfully forwarded our request before + * it died. + */ + if (oldstate < CEPH_MDS_STATE_ACTIVE && + newstate >= CEPH_MDS_STATE_ACTIVE) { + kick_requests(mdsc, i, 1); + ceph_kick_flushing_caps(mdsc, s); + } + } +} + + + +/* + * leases + */ + +/* + * caller must hold session s_mutex, dentry->d_lock + */ +void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry) +{ + struct ceph_dentry_info *di = ceph_dentry(dentry); + + ceph_put_mds_session(di->lease_session); + di->lease_session = NULL; +} + +static void handle_lease(struct ceph_mds_client *mdsc, struct ceph_msg *msg) +{ + struct super_block *sb = mdsc->client->sb; + struct inode *inode; + struct ceph_mds_session *session; + struct ceph_inode_info *ci; + struct dentry *parent, *dentry; + struct ceph_dentry_info *di; + int mds; + struct ceph_mds_lease *h = msg->front.iov_base; + struct ceph_vino vino; + int mask; + struct qstr dname; + int release = 0; + + if (msg->hdr.src.name.type != CEPH_ENTITY_TYPE_MDS) + return; + mds = le64_to_cpu(msg->hdr.src.name.num); + dout("handle_lease from mds%d\n", mds); + + /* decode */ + if (msg->front.iov_len < sizeof(*h) + sizeof(u32)) + goto bad; + vino.ino = le64_to_cpu(h->ino); + vino.snap = CEPH_NOSNAP; + mask = le16_to_cpu(h->mask); + dname.name = (void *)h + sizeof(*h) + sizeof(u32); + dname.len = msg->front.iov_len - sizeof(*h) - sizeof(u32); + if (dname.len != get_unaligned_le32(h+1)) + goto bad; + + /* find session */ + mutex_lock(&mdsc->mutex); + session = __ceph_lookup_mds_session(mdsc, mds); + mutex_unlock(&mdsc->mutex); + if (!session) { + pr_err("handle_lease got lease but no session mds%d\n", mds); + return; + } + + mutex_lock(&session->s_mutex); + session->s_seq++; + + /* lookup inode */ + inode = ceph_find_inode(sb, vino); + dout("handle_lease '%s', mask %d, ino %llx %p\n", + ceph_lease_op_name(h->action), mask, vino.ino, inode); + if (inode == NULL) { + dout("handle_lease no inode %llx\n", vino.ino); + goto release; + } + ci = ceph_inode(inode); + + /* dentry */ + parent = d_find_alias(inode); + if (!parent) { + dout("no parent dentry on inode %p\n", inode); + WARN_ON(1); + goto release; /* hrm... */ + } + dname.hash = full_name_hash(dname.name, dname.len); + dentry = d_lookup(parent, &dname); + dput(parent); + if (!dentry) + goto release; + + spin_lock(&dentry->d_lock); + di = ceph_dentry(dentry); + switch (h->action) { + case CEPH_MDS_LEASE_REVOKE: + if (di && di->lease_session == session) { + h->seq = cpu_to_le32(di->lease_seq); + __ceph_mdsc_drop_dentry_lease(dentry); + } + release = 1; + break; + + case CEPH_MDS_LEASE_RENEW: + if (di && di->lease_session == session && + di->lease_gen == session->s_cap_gen && + di->lease_renew_from && + di->lease_renew_after == 0) { + unsigned long duration = + le32_to_cpu(h->duration_ms) * HZ / 1000; + + di->lease_seq = le32_to_cpu(h->seq); + dentry->d_time = di->lease_renew_from + duration; + di->lease_renew_after = di->lease_renew_from + + (duration >> 1); + di->lease_renew_from = 0; + } + break; + } + spin_unlock(&dentry->d_lock); + dput(dentry); + + if (!release) + goto out; + +release: + /* let's just reuse the same message */ + h->action = CEPH_MDS_LEASE_REVOKE_ACK; + ceph_msg_get(msg); + ceph_con_send(&session->s_con, msg); + +out: + iput(inode); + mutex_unlock(&session->s_mutex); + ceph_put_mds_session(session); + return; + +bad: + pr_err("corrupt lease message\n"); +} + +void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session, + struct inode *inode, + struct dentry *dentry, char action, + u32 seq) +{ + struct ceph_msg *msg; + struct ceph_mds_lease *lease; + int len = sizeof(*lease) + sizeof(u32); + int dnamelen = 0; + + dout("lease_send_msg inode %p dentry %p %s to mds%d\n", + inode, dentry, ceph_lease_op_name(action), session->s_mds); + dnamelen = dentry->d_name.len; + len += dnamelen; + + msg = ceph_msg_new(CEPH_MSG_CLIENT_LEASE, len, 0, 0, NULL); + if (IS_ERR(msg)) + return; + lease = msg->front.iov_base; + lease->action = action; + lease->mask = cpu_to_le16(CEPH_LOCK_DN); + lease->ino = cpu_to_le64(ceph_vino(inode).ino); + lease->first = lease->last = cpu_to_le64(ceph_vino(inode).snap); + lease->seq = cpu_to_le32(seq); + put_unaligned_le32(dnamelen, lease + 1); + memcpy((void *)(lease + 1) + 4, dentry->d_name.name, dnamelen); + + /* + * if this is a preemptive lease RELEASE, no need to + * flush request stream, since the actual request will + * soon follow. + */ + msg->more_to_follow = (action == CEPH_MDS_LEASE_RELEASE); + + ceph_con_send(&session->s_con, msg); +} + +/* + * Preemptively release a lease we expect to invalidate anyway. + * Pass @inode always, @dentry is optional. + */ +void ceph_mdsc_lease_release(struct ceph_mds_client *mdsc, struct inode *inode, + struct dentry *dentry, int mask) +{ + struct ceph_dentry_info *di; + struct ceph_mds_session *session; + u32 seq; + + BUG_ON(inode == NULL); + BUG_ON(dentry == NULL); + BUG_ON(mask != CEPH_LOCK_DN); + + /* is dentry lease valid? */ + spin_lock(&dentry->d_lock); + di = ceph_dentry(dentry); + if (!di || !di->lease_session || + di->lease_session->s_mds < 0 || + di->lease_gen != di->lease_session->s_cap_gen || + !time_before(jiffies, dentry->d_time)) { + dout("lease_release inode %p dentry %p -- " + "no lease on %d\n", + inode, dentry, mask); + spin_unlock(&dentry->d_lock); + return; + } + + /* we do have a lease on this dentry; note mds and seq */ + session = ceph_get_mds_session(di->lease_session); + seq = di->lease_seq; + __ceph_mdsc_drop_dentry_lease(dentry); + spin_unlock(&dentry->d_lock); + + dout("lease_release inode %p dentry %p mask %d to mds%d\n", + inode, dentry, mask, session->s_mds); + ceph_mdsc_lease_send_msg(session, inode, dentry, + CEPH_MDS_LEASE_RELEASE, seq); + ceph_put_mds_session(session); +} + +/* + * drop all leases (and dentry refs) in preparation for umount + */ +static void drop_leases(struct ceph_mds_client *mdsc) +{ + int i; + + dout("drop_leases\n"); + mutex_lock(&mdsc->mutex); + for (i = 0; i < mdsc->max_sessions; i++) { + struct ceph_mds_session *s = __ceph_lookup_mds_session(mdsc, i); + if (!s) + continue; + mutex_unlock(&mdsc->mutex); + mutex_lock(&s->s_mutex); + mutex_unlock(&s->s_mutex); + ceph_put_mds_session(s); + mutex_lock(&mdsc->mutex); + } + mutex_unlock(&mdsc->mutex); +} + + + +/* + * delayed work -- periodically trim expired leases, renew caps with mds + */ +static void schedule_delayed(struct ceph_mds_client *mdsc) +{ + int delay = 5; + unsigned hz = round_jiffies_relative(HZ * delay); + schedule_delayed_work(&mdsc->delayed_work, hz); +} + +static void delayed_work(struct work_struct *work) +{ + int i; + struct ceph_mds_client *mdsc = + container_of(work, struct ceph_mds_client, delayed_work.work); + int renew_interval; + int renew_caps; + + dout("mdsc delayed_work\n"); + ceph_check_delayed_caps(mdsc, 0); + + mutex_lock(&mdsc->mutex); + renew_interval = mdsc->mdsmap->m_session_timeout >> 2; + renew_caps = time_after_eq(jiffies, HZ*renew_interval + + mdsc->last_renew_caps); + if (renew_caps) + mdsc->last_renew_caps = jiffies; + + for (i = 0; i < mdsc->max_sessions; i++) { + struct ceph_mds_session *s = __ceph_lookup_mds_session(mdsc, i); + if (s == NULL) + continue; + if (s->s_state == CEPH_MDS_SESSION_CLOSING) { + dout("resending session close request for mds%d\n", + s->s_mds); + request_close_session(mdsc, s); + ceph_put_mds_session(s); + continue; + } + if (s->s_ttl && time_after(jiffies, s->s_ttl)) { + if (s->s_state == CEPH_MDS_SESSION_OPEN) { + s->s_state = CEPH_MDS_SESSION_HUNG; + pr_info("mds%d hung\n", s->s_mds); + } + } + if (s->s_state < CEPH_MDS_SESSION_OPEN) { + /* this mds is failed or recovering, just wait */ + ceph_put_mds_session(s); + continue; + } + mutex_unlock(&mdsc->mutex); + + mutex_lock(&s->s_mutex); + if (renew_caps) + send_renew_caps(mdsc, s); + else + ceph_con_keepalive(&s->s_con); + add_cap_releases(mdsc, s, -1); + send_cap_releases(mdsc, s); + mutex_unlock(&s->s_mutex); + ceph_put_mds_session(s); + + mutex_lock(&mdsc->mutex); + } + mutex_unlock(&mdsc->mutex); + + schedule_delayed(mdsc); +} + + +void ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client) +{ + mdsc->client = client; + mutex_init(&mdsc->mutex); + mdsc->mdsmap = kzalloc(sizeof(*mdsc->mdsmap), GFP_NOFS); + init_completion(&mdsc->safe_umount_waiters); + init_completion(&mdsc->session_close_waiters); + INIT_LIST_HEAD(&mdsc->waiting_for_map); + mdsc->sessions = NULL; + mdsc->max_sessions = 0; + mdsc->stopping = 0; + init_rwsem(&mdsc->snap_rwsem); + INIT_RADIX_TREE(&mdsc->snap_realms, GFP_NOFS); + INIT_LIST_HEAD(&mdsc->snap_empty); + spin_lock_init(&mdsc->snap_empty_lock); + mdsc->last_tid = 0; + INIT_RADIX_TREE(&mdsc->request_tree, GFP_NOFS); + INIT_DELAYED_WORK(&mdsc->delayed_work, delayed_work); + mdsc->last_renew_caps = jiffies; + INIT_LIST_HEAD(&mdsc->cap_delay_list); + spin_lock_init(&mdsc->cap_delay_lock); + INIT_LIST_HEAD(&mdsc->snap_flush_list); + spin_lock_init(&mdsc->snap_flush_lock); + mdsc->cap_flush_seq = 0; + INIT_LIST_HEAD(&mdsc->cap_dirty); + mdsc->num_cap_flushing = 0; + spin_lock_init(&mdsc->cap_dirty_lock); + init_waitqueue_head(&mdsc->cap_flushing_wq); + spin_lock_init(&mdsc->dentry_lru_lock); + INIT_LIST_HEAD(&mdsc->dentry_lru); +} + +/* + * Wait for safe replies on open mds requests. If we time out, drop + * all requests from the tree to avoid dangling dentry refs. + */ +static void wait_requests(struct ceph_mds_client *mdsc) +{ + struct ceph_mds_request *req; + struct ceph_client *client = mdsc->client; + + mutex_lock(&mdsc->mutex); + if (__get_oldest_tid(mdsc)) { + mutex_unlock(&mdsc->mutex); + dout("wait_requests waiting for requests\n"); + wait_for_completion_timeout(&mdsc->safe_umount_waiters, + client->mount_args.mount_timeout * HZ); + mutex_lock(&mdsc->mutex); + + /* tear down remaining requests */ + while (radix_tree_gang_lookup(&mdsc->request_tree, + (void **)&req, 0, 1)) { + dout("wait_requests timed out on tid %llu\n", + req->r_tid); + radix_tree_delete(&mdsc->request_tree, req->r_tid); + ceph_mdsc_put_request(req); + } + } + mutex_unlock(&mdsc->mutex); + dout("wait_requests done\n"); +} + +/* + * called before mount is ro, and before dentries are torn down. + * (hmm, does this still race with new lookups?) + */ +void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc) +{ + dout("pre_umount\n"); + mdsc->stopping = 1; + + drop_leases(mdsc); + ceph_check_delayed_caps(mdsc, 1); + wait_requests(mdsc); +} + +/* + * wait for all write mds requests to flush. + */ +static void wait_unsafe_requests(struct ceph_mds_client *mdsc, u64 want_tid) +{ + struct ceph_mds_request *req; + u64 next_tid = 0; + int got; + + mutex_lock(&mdsc->mutex); + dout("wait_unsafe_requests want %lld\n", want_tid); + while (1) { + got = radix_tree_gang_lookup(&mdsc->request_tree, (void **)&req, + next_tid, 1); + if (!got) + break; + if (req->r_tid > want_tid) + break; + + next_tid = req->r_tid + 1; + if ((req->r_op & CEPH_MDS_OP_WRITE) == 0) + continue; /* not a write op */ + + ceph_mdsc_get_request(req); + mutex_unlock(&mdsc->mutex); + dout("wait_unsafe_requests wait on %llu (want %llu)\n", + req->r_tid, want_tid); + wait_for_completion(&req->r_safe_completion); + mutex_lock(&mdsc->mutex); + ceph_mdsc_put_request(req); + } + mutex_unlock(&mdsc->mutex); + dout("wait_unsafe_requests done\n"); +} + +void ceph_mdsc_sync(struct ceph_mds_client *mdsc) +{ + u64 want_tid, want_flush; + + dout("sync\n"); + mutex_lock(&mdsc->mutex); + want_tid = mdsc->last_tid; + want_flush = mdsc->cap_flush_seq; + mutex_unlock(&mdsc->mutex); + dout("sync want tid %lld flush_seq %lld\n", want_tid, want_flush); + + ceph_check_delayed_caps(mdsc, 1); + + wait_unsafe_requests(mdsc, want_tid); + wait_event(mdsc->cap_flushing_wq, check_cap_flush(mdsc, want_flush)); +} + + +/* + * called after sb is ro. + */ +void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc) +{ + struct ceph_mds_session *session; + int i; + int n; + struct ceph_client *client = mdsc->client; + unsigned long started, timeout = client->mount_args.mount_timeout * HZ; + + dout("close_sessions\n"); + + mutex_lock(&mdsc->mutex); + + /* close sessions */ + started = jiffies; + while (time_before(jiffies, started + timeout)) { + dout("closing sessions\n"); + n = 0; + for (i = 0; i < mdsc->max_sessions; i++) { + session = __ceph_lookup_mds_session(mdsc, i); + if (!session) + continue; + mutex_unlock(&mdsc->mutex); + mutex_lock(&session->s_mutex); + __close_session(mdsc, session); + mutex_unlock(&session->s_mutex); + ceph_put_mds_session(session); + mutex_lock(&mdsc->mutex); + n++; + } + if (n == 0) + break; + + if (client->mount_state == CEPH_MOUNT_SHUTDOWN) + break; + + dout("waiting for sessions to close\n"); + mutex_unlock(&mdsc->mutex); + wait_for_completion_timeout(&mdsc->session_close_waiters, + timeout); + mutex_lock(&mdsc->mutex); + } + + /* tear down remaining sessions */ + for (i = 0; i < mdsc->max_sessions; i++) { + if (mdsc->sessions[i]) { + session = get_session(mdsc->sessions[i]); + unregister_session(mdsc, i); + mutex_unlock(&mdsc->mutex); + mutex_lock(&session->s_mutex); + remove_session_caps(session); + mutex_unlock(&session->s_mutex); + ceph_put_mds_session(session); + mutex_lock(&mdsc->mutex); + } + } + + WARN_ON(!list_empty(&mdsc->cap_delay_list)); + + mutex_unlock(&mdsc->mutex); + + ceph_cleanup_empty_realms(mdsc); + + cancel_delayed_work_sync(&mdsc->delayed_work); /* cancel timer */ + + dout("stopped\n"); +} + +void ceph_mdsc_stop(struct ceph_mds_client *mdsc) +{ + dout("stop\n"); + cancel_delayed_work_sync(&mdsc->delayed_work); /* cancel timer */ + if (mdsc->mdsmap) + ceph_mdsmap_destroy(mdsc->mdsmap); + kfree(mdsc->sessions); +} + + +/* + * handle mds map update. + */ +void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc, struct ceph_msg *msg) +{ + u32 epoch; + u32 maplen; + void *p = msg->front.iov_base; + void *end = p + msg->front.iov_len; + struct ceph_mdsmap *newmap, *oldmap; + struct ceph_fsid fsid; + int err = -EINVAL; + + ceph_decode_need(&p, end, sizeof(fsid)+2*sizeof(u32), bad); + ceph_decode_copy(&p, &fsid, sizeof(fsid)); + if (ceph_fsid_compare(&fsid, &mdsc->client->monc.monmap->fsid)) { + pr_err("got mdsmap with wrong fsid\n"); + return; + } + ceph_decode_32(&p, epoch); + ceph_decode_32(&p, maplen); + dout("handle_map epoch %u len %d\n", epoch, (int)maplen); + + /* do we need it? */ + ceph_monc_got_mdsmap(&mdsc->client->monc, epoch); + mutex_lock(&mdsc->mutex); + if (mdsc->mdsmap && epoch <= mdsc->mdsmap->m_epoch) { + dout("handle_map epoch %u <= our %u\n", + epoch, mdsc->mdsmap->m_epoch); + mutex_unlock(&mdsc->mutex); + return; + } + + newmap = ceph_mdsmap_decode(&p, end); + if (IS_ERR(newmap)) { + err = PTR_ERR(newmap); + goto bad_unlock; + } + + /* swap into place */ + if (mdsc->mdsmap) { + oldmap = mdsc->mdsmap; + mdsc->mdsmap = newmap; + check_new_map(mdsc, newmap, oldmap); + ceph_mdsmap_destroy(oldmap); + } else { + mdsc->mdsmap = newmap; /* first mds map */ + } + mdsc->client->sb->s_maxbytes = mdsc->mdsmap->m_max_file_size; + + __wake_requests(mdsc, &mdsc->waiting_for_map); + + mutex_unlock(&mdsc->mutex); + schedule_delayed(mdsc); + return; + +bad_unlock: + mutex_unlock(&mdsc->mutex); +bad: + pr_err("error decoding mdsmap %d\n", err); + return; +} + +static struct ceph_connection *con_get(struct ceph_connection *con) +{ + struct ceph_mds_session *s = con->private; + + if (get_session(s)) { + dout("mdsc con_get %p %d -> %d\n", s, + atomic_read(&s->s_ref) - 1, atomic_read(&s->s_ref)); + return con; + } + dout("mdsc con_get %p FAIL\n", s); + return NULL; +} + +static void con_put(struct ceph_connection *con) +{ + struct ceph_mds_session *s = con->private; + + dout("mdsc con_put %p %d -> %d\n", s, atomic_read(&s->s_ref), + atomic_read(&s->s_ref) - 1); + ceph_put_mds_session(s); +} + +/* + * if the client is unresponsive for long enough, the mds will kill + * the session entirely. + */ +static void peer_reset(struct ceph_connection *con) +{ + struct ceph_mds_session *s = con->private; + + pr_err("mds%d gave us the boot. IMPLEMENT RECONNECT.\n", + s->s_mds); +} + +static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) +{ + struct ceph_mds_session *s = con->private; + struct ceph_mds_client *mdsc = s->s_mdsc; + int type = le16_to_cpu(msg->hdr.type); + + switch (type) { + case CEPH_MSG_MDS_MAP: + ceph_mdsc_handle_map(mdsc, msg); + break; + case CEPH_MSG_CLIENT_SESSION: + handle_session(s, msg); + break; + case CEPH_MSG_CLIENT_REPLY: + handle_reply(s, msg); + break; + case CEPH_MSG_CLIENT_REQUEST_FORWARD: + handle_forward(mdsc, msg); + break; + case CEPH_MSG_CLIENT_CAPS: + ceph_handle_caps(s, msg); + break; + case CEPH_MSG_CLIENT_SNAP: + ceph_handle_snap(mdsc, msg); + break; + case CEPH_MSG_CLIENT_LEASE: + handle_lease(mdsc, msg); + break; + + default: + pr_err("received unknown message type %d %s\n", type, + ceph_msg_type_name(type)); + } + ceph_msg_put(msg); +} + +const static struct ceph_connection_operations mds_con_ops = { + .get = con_get, + .put = con_put, + .dispatch = dispatch, + .peer_reset = peer_reset, + .alloc_msg = ceph_alloc_msg, + .alloc_middle = ceph_alloc_middle, +}; + + + + +/* eof */ diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h new file mode 100644 index 0000000..f566e9c --- /dev/null +++ b/fs/ceph/mds_client.h @@ -0,0 +1,321 @@ +#ifndef _FS_CEPH_MDS_CLIENT_H +#define _FS_CEPH_MDS_CLIENT_H + +#include +#include +#include +#include +#include + +#include "types.h" +#include "messenger.h" +#include "mdsmap.h" + +/* + * Some lock dependencies: + * + * session->s_mutex + * mdsc->mutex + * + * mdsc->snap_rwsem + * + * inode->i_lock + * mdsc->snap_flush_lock + * mdsc->cap_delay_lock + * + */ + +struct ceph_client; +struct ceph_cap; + +/* + * parsed info about a single inode. pointers are into the encoded + * on-wire structures within the mds reply message payload. + */ +struct ceph_mds_reply_info_in { + struct ceph_mds_reply_inode *in; + u32 symlink_len; + char *symlink; + u32 xattr_len; + char *xattr_data; +}; + +/* + * parsed info about an mds reply, including information about the + * target inode and/or its parent directory and dentry, and directory + * contents (for readdir results). + */ +struct ceph_mds_reply_info_parsed { + struct ceph_mds_reply_head *head; + + struct ceph_mds_reply_info_in diri, targeti; + struct ceph_mds_reply_dirfrag *dirfrag; + char *dname; + u32 dname_len; + struct ceph_mds_reply_lease *dlease; + + struct ceph_mds_reply_dirfrag *dir_dir; + int dir_nr; + char **dir_dname; + u32 *dir_dname_len; + struct ceph_mds_reply_lease **dir_dlease; + struct ceph_mds_reply_info_in *dir_in; + u8 dir_complete, dir_end; + + /* encoded blob describing snapshot contexts for certain + operations (e.g., open) */ + void *snapblob; + int snapblob_len; +}; + + +/* + * cap releases are batched and sent to the MDS en masse. + */ +#define CEPH_CAPS_PER_RELEASE ((PAGE_CACHE_SIZE - \ + sizeof(struct ceph_mds_cap_release)) / \ + sizeof(struct ceph_mds_cap_item)) + + +/* + * state associated with each MDS<->client session + */ +enum { + CEPH_MDS_SESSION_NEW = 1, + CEPH_MDS_SESSION_OPENING = 2, + CEPH_MDS_SESSION_OPEN = 3, + CEPH_MDS_SESSION_HUNG = 4, + CEPH_MDS_SESSION_CLOSING = 5, + CEPH_MDS_SESSION_RESTARTING = 6, + CEPH_MDS_SESSION_RECONNECTING = 7, +}; + +struct ceph_mds_session { + struct ceph_mds_client *s_mdsc; + int s_mds; + int s_state; + unsigned long s_ttl; /* time until mds kills us */ + u64 s_seq; /* incoming msg seq # */ + struct mutex s_mutex; /* serialize session messages */ + + struct ceph_connection s_con; + + /* protected by s_cap_lock */ + spinlock_t s_cap_lock; + u32 s_cap_gen; /* inc each time we get mds stale msg */ + unsigned long s_cap_ttl; /* when session caps expire */ + struct list_head s_caps; /* all caps issued by this session */ + int s_nr_caps, s_trim_caps; + int s_num_cap_releases; + struct list_head s_cap_releases; /* waiting cap_release messages */ + struct list_head s_cap_releases_done; /* ready to send */ + + /* protected by mutex */ + struct list_head s_cap_flushing; /* inodes w/ flushing caps */ + struct list_head s_cap_snaps_flushing; + unsigned long s_renew_requested; /* last time we sent a renew req */ + u64 s_renew_seq; + + atomic_t s_ref; + struct list_head s_waiting; /* waiting requests */ + struct list_head s_unsafe; /* unsafe requests */ +}; + +/* + * modes of choosing which MDS to send a request to + */ +enum { + USE_ANY_MDS, + USE_RANDOM_MDS, + USE_AUTH_MDS, /* prefer authoritative mds for this metadata item */ +}; + +struct ceph_mds_request; +struct ceph_mds_client; + +/* + * request completion callback + */ +typedef void (*ceph_mds_request_callback_t) (struct ceph_mds_client *mdsc, + struct ceph_mds_request *req); + +/* + * an in-flight mds request + */ +struct ceph_mds_request { + u64 r_tid; /* transaction id */ + + int r_op; /* mds op code */ + int r_mds; + + /* operation on what? */ + struct inode *r_inode; /* arg1 */ + struct dentry *r_dentry; /* arg1 */ + struct dentry *r_old_dentry; /* arg2: rename from or link from */ + char *r_path1, *r_path2; + struct ceph_vino r_ino1, r_ino2; + + struct inode *r_locked_dir; /* dir (if any) i_mutex locked by vfs */ + struct inode *r_target_inode; /* resulting inode */ + + union ceph_mds_request_args r_args; + int r_fmode; /* file mode, if expecting cap */ + + /* for choosing which mds to send this request to */ + int r_direct_mode; + u32 r_direct_hash; /* choose dir frag based on this dentry hash */ + bool r_direct_is_hash; /* true if r_direct_hash is valid */ + + /* data payload is used for xattr ops */ + struct page **r_pages; + int r_num_pages; + int r_data_len; + + /* what caps shall we drop? */ + int r_inode_drop, r_inode_unless; + int r_dentry_drop, r_dentry_unless; + int r_old_dentry_drop, r_old_dentry_unless; + struct inode *r_old_inode; + int r_old_inode_drop, r_old_inode_unless; + + struct ceph_msg *r_request; /* original request */ + struct ceph_msg *r_reply; + struct ceph_mds_reply_info_parsed r_reply_info; + int r_err; + + unsigned long r_timeout; /* optional. jiffies */ + unsigned long r_started; /* start time to measure timeout against */ + unsigned long r_request_started; /* start time for mds request only, + used to measure lease durations */ + + /* link unsafe requests to parent directory, for fsync */ + struct inode *r_unsafe_dir; + struct list_head r_unsafe_dir_item; + + struct ceph_mds_session *r_session; + + int r_attempts; /* resend attempts */ + int r_num_fwd; /* number of forward attempts */ + int r_num_stale; + int r_resend_mds; /* mds to resend to next, if any*/ + + atomic_t r_ref; + struct list_head r_wait; + struct completion r_completion; + struct completion r_safe_completion; + ceph_mds_request_callback_t r_callback; + struct list_head r_unsafe_item; /* per-session unsafe list item */ + bool r_got_unsafe, r_got_safe; + + bool r_did_prepopulate; + u32 r_readdir_offset; + + struct ceph_cap_reservation r_caps_reservation; + int r_num_caps; +}; + +/* + * mds client state + */ +struct ceph_mds_client { + struct ceph_client *client; + struct mutex mutex; /* all nested structures */ + + struct ceph_mdsmap *mdsmap; + struct completion safe_umount_waiters, session_close_waiters; + struct list_head waiting_for_map; + + struct ceph_mds_session **sessions; /* NULL for mds if no session */ + int max_sessions; /* len of s_mds_sessions */ + int stopping; /* true if shutting down */ + + /* + * snap_rwsem will cover cap linkage into snaprealms, and + * realm snap contexts. (later, we can do per-realm snap + * contexts locks..) the empty list contains realms with no + * references (implying they contain no inodes with caps) that + * should be destroyed. + */ + struct rw_semaphore snap_rwsem; + struct radix_tree_root snap_realms; + struct list_head snap_empty; + spinlock_t snap_empty_lock; /* protect snap_empty */ + + u64 last_tid; /* most recent mds request */ + struct radix_tree_root request_tree; /* pending mds requests */ + struct delayed_work delayed_work; /* delayed work */ + unsigned long last_renew_caps; /* last time we renewed our caps */ + struct list_head cap_delay_list; /* caps with delayed release */ + spinlock_t cap_delay_lock; /* protects cap_delay_list */ + struct list_head snap_flush_list; /* cap_snaps ready to flush */ + spinlock_t snap_flush_lock; + + u64 cap_flush_seq; + struct list_head cap_dirty; /* inodes with dirty caps */ + int num_cap_flushing; /* # caps we are flushing */ + spinlock_t cap_dirty_lock; /* protects above items */ + wait_queue_head_t cap_flushing_wq; + + struct dentry *debugfs_file; + + spinlock_t dentry_lru_lock; + struct list_head dentry_lru; + int num_dentry; +}; + +extern const char *ceph_mds_op_name(int op); + +extern struct ceph_mds_session * +__ceph_lookup_mds_session(struct ceph_mds_client *, int mds); + +static inline struct ceph_mds_session * +ceph_get_mds_session(struct ceph_mds_session *s) +{ + atomic_inc(&s->s_ref); + return s; +} + +extern void ceph_put_mds_session(struct ceph_mds_session *s); + +extern int ceph_send_msg_mds(struct ceph_mds_client *mdsc, + struct ceph_msg *msg, int mds); + +extern void ceph_mdsc_init(struct ceph_mds_client *mdsc, + struct ceph_client *client); +extern void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc); +extern void ceph_mdsc_stop(struct ceph_mds_client *mdsc); + +extern void ceph_mdsc_sync(struct ceph_mds_client *mdsc); + +extern void ceph_mdsc_lease_release(struct ceph_mds_client *mdsc, + struct inode *inode, + struct dentry *dn, int mask); + +extern struct ceph_mds_request * +ceph_mdsc_create_request(struct ceph_mds_client *mdsc, int op, int mode); +extern void ceph_mdsc_submit_request(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req); +extern int ceph_mdsc_do_request(struct ceph_mds_client *mdsc, + struct inode *dir, + struct ceph_mds_request *req); +static inline void ceph_mdsc_get_request(struct ceph_mds_request *req) +{ + atomic_inc(&req->r_ref); +} +extern void ceph_mdsc_put_request(struct ceph_mds_request *req); + +extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc); + +extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, + int stop_on_nosnap); + +extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry); +extern void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session, + struct inode *inode, + struct dentry *dentry, char action, + u32 seq); + +extern void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc, + struct ceph_msg *msg); + +#endif diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c new file mode 100644 index 0000000..15913cbe --- /dev/null +++ b/fs/ceph/mdsmap.c @@ -0,0 +1,166 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include +#include + +#include "mdsmap.h" +#include "messenger.h" +#include "decode.h" + +#include "super.h" + + +/* + * choose a random mds that is "up" (i.e. has a state > 0), or -1. + */ +int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m) +{ + int n = 0; + int i; + char r; + + /* count */ + for (i = 0; i < m->m_max_mds; i++) + if (m->m_info[i].state > 0) + n++; + if (n == 0) + return -1; + + /* pick */ + get_random_bytes(&r, 1); + n = r % n; + i = 0; + for (i = 0; n > 0; i++, n--) + while (m->m_info[i].state <= 0) + i++; + + return i; +} + +/* + * Decode an MDS map + * + * Ignore any fields we don't care about (there are quite a few of + * them). + */ +struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) +{ + struct ceph_mdsmap *m; + int i, j, n; + int err = -EINVAL; + u16 version; + + m = kzalloc(sizeof(*m), GFP_NOFS); + if (m == NULL) + return ERR_PTR(-ENOMEM); + + ceph_decode_16_safe(p, end, version, bad); + + ceph_decode_need(p, end, 8*sizeof(u32) + sizeof(u64), bad); + ceph_decode_32(p, m->m_epoch); + ceph_decode_32(p, m->m_client_epoch); + ceph_decode_32(p, m->m_last_failure); + ceph_decode_32(p, m->m_root); + ceph_decode_32(p, m->m_session_timeout); + ceph_decode_32(p, m->m_session_autoclose); + ceph_decode_64(p, m->m_max_file_size); + ceph_decode_32(p, m->m_max_mds); + + m->m_info = kcalloc(m->m_max_mds, sizeof(*m->m_info), GFP_NOFS); + if (m->m_info == NULL) + goto badmem; + + /* pick out active nodes from mds_info (state > 0) */ + ceph_decode_32(p, n); + for (i = 0; i < n; i++) { + u32 namelen; + s32 mds, inc, state; + u64 state_seq; + u8 infoversion; + struct ceph_entity_addr addr; + u32 num_export_targets; + void *pexport_targets = NULL; + + ceph_decode_need(p, end, sizeof(addr) + 1 + sizeof(u32), bad); + *p += sizeof(addr); /* skip addr key */ + ceph_decode_8(p, infoversion); + ceph_decode_32(p, namelen); /* skip mds name */ + *p += namelen; + + ceph_decode_need(p, end, + 5*sizeof(u32) + sizeof(u64) + + sizeof(addr) + sizeof(struct ceph_timespec), + bad); + ceph_decode_32(p, mds); + ceph_decode_32(p, inc); + ceph_decode_32(p, state); + ceph_decode_64(p, state_seq); + ceph_decode_copy(p, &addr, sizeof(addr)); + *p += sizeof(struct ceph_timespec); + *p += sizeof(u32); + ceph_decode_32_safe(p, end, namelen, bad); + *p += sizeof(namelen); + if (infoversion >= 2) { + ceph_decode_32_safe(p, end, num_export_targets, bad); + pexport_targets = *p; + *p += sizeof(num_export_targets * sizeof(u32)); + } else { + num_export_targets = 0; + } + + dout("mdsmap_decode %d/%d mds%d.%d %s %s\n", + i+1, n, mds, inc, pr_addr(&addr.in_addr), + ceph_mds_state_name(state)); + if (mds >= 0 && mds < m->m_max_mds && state > 0) { + m->m_info[mds].state = state; + m->m_info[mds].addr = addr; + m->m_info[mds].num_export_targets = num_export_targets; + if (num_export_targets) { + m->m_info[mds].export_targets = + kcalloc(num_export_targets, sizeof(u32), + GFP_NOFS); + for (j = 0; j < num_export_targets; j++) + ceph_decode_32(&pexport_targets, + m->m_info[mds].export_targets[j]); + } else { + m->m_info[mds].export_targets = NULL; + } + } + } + + /* pg_pools */ + ceph_decode_32_safe(p, end, n, bad); + m->m_num_data_pg_pools = n; + m->m_data_pg_pools = kcalloc(n, sizeof(u32), GFP_NOFS); + if (!m->m_data_pg_pools) + goto badmem; + ceph_decode_need(p, end, sizeof(u32)*(n+1), bad); + for (i = 0; i < n; i++) + ceph_decode_32(p, m->m_data_pg_pools[i]); + ceph_decode_32(p, m->m_cas_pg_pool); + + /* ok, we don't care about the rest. */ + dout("mdsmap_decode success epoch %u\n", m->m_epoch); + return m; + +badmem: + err = -ENOMEM; +bad: + pr_err("corrupt mdsmap\n"); + ceph_mdsmap_destroy(m); + return ERR_PTR(-EINVAL); +} + +void ceph_mdsmap_destroy(struct ceph_mdsmap *m) +{ + int i; + + for (i = 0; i < m->m_max_mds; i++) + kfree(m->m_info[i].export_targets); + kfree(m->m_info); + kfree(m->m_data_pg_pools); + kfree(m); +} diff --git a/fs/ceph/mdsmap.h b/fs/ceph/mdsmap.h new file mode 100644 index 0000000..d317308 --- /dev/null +++ b/fs/ceph/mdsmap.h @@ -0,0 +1,53 @@ +#ifndef _FS_CEPH_MDSMAP_H +#define _FS_CEPH_MDSMAP_H + +#include "types.h" + +/* + * mds map - describe servers in the mds cluster. + * + * we limit fields to those the client actually xcares about + */ +struct ceph_mds_info { + struct ceph_entity_addr addr; + s32 state; + int num_export_targets; + u32 *export_targets; +}; + +struct ceph_mdsmap { + u32 m_epoch, m_client_epoch, m_last_failure; + u32 m_root; + u32 m_session_timeout; /* seconds */ + u32 m_session_autoclose; /* seconds */ + u64 m_max_file_size; + u32 m_max_mds; /* size of m_addr, m_state arrays */ + struct ceph_mds_info *m_info; + + /* which object pools file data can be stored in */ + int m_num_data_pg_pools; + u32 *m_data_pg_pools; + u32 m_cas_pg_pool; +}; + +static inline struct ceph_entity_addr * +ceph_mdsmap_get_addr(struct ceph_mdsmap *m, int w) +{ + if (w >= m->m_max_mds) + return NULL; + return &m->m_info[w].addr; +} + +static inline int ceph_mdsmap_get_state(struct ceph_mdsmap *m, int w) +{ + BUG_ON(w < 0); + if (w >= m->m_max_mds) + return CEPH_MDS_STATE_DNE; + return m->m_info[w].state; +} + +extern int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m); +extern struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end); +extern void ceph_mdsmap_destroy(struct ceph_mdsmap *m); + +#endif -- cgit v0.10.2 From f24e9980eb860d8600cbe5ef3d2fd9295320d229 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:10 -0700 Subject: ceph: OSD client The OSD client is responsible for reading and writing data from/to the object storage pool. This includes determining where objects are stored in the cluster, and ensuring that requests are retried or redirected in the event of a node failure or data migration. If an OSD does not respond before a timeout expires, keepalive messages are sent across the lossless, ordered communications channel to ensure that any break in the TCP is discovered. If the session does reset, a reconnection is attempted and affected requests are resent (by the message transport layer). Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c new file mode 100644 index 0000000..978593a --- /dev/null +++ b/fs/ceph/osd_client.c @@ -0,0 +1,1294 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include +#include +#include + +#include "super.h" +#include "osd_client.h" +#include "messenger.h" +#include "decode.h" + +const static struct ceph_connection_operations osd_con_ops; + +static void kick_requests(struct ceph_osd_client *osdc, struct ceph_osd *osd); + +/* + * Implement client access to distributed object storage cluster. + * + * All data objects are stored within a cluster/cloud of OSDs, or + * "object storage devices." (Note that Ceph OSDs have _nothing_ to + * do with the T10 OSD extensions to SCSI.) Ceph OSDs are simply + * remote daemons serving up and coordinating consistent and safe + * access to storage. + * + * Cluster membership and the mapping of data objects onto storage devices + * are described by the osd map. + * + * We keep track of pending OSD requests (read, write), resubmit + * requests to different OSDs when the cluster topology/data layout + * change, or retry the affected requests when the communications + * channel with an OSD is reset. + */ + +/* + * calculate the mapping of a file extent onto an object, and fill out the + * request accordingly. shorten extent as necessary if it crosses an + * object boundary. + * + * fill osd op in request message. + */ +static void calc_layout(struct ceph_osd_client *osdc, + struct ceph_vino vino, struct ceph_file_layout *layout, + u64 off, u64 *plen, + struct ceph_osd_request *req) +{ + struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; + struct ceph_osd_op *op = (void *)(reqhead + 1); + u64 orig_len = *plen; + u64 objoff, objlen; /* extent in object */ + u64 bno; + + reqhead->snapid = cpu_to_le64(vino.snap); + + /* object extent? */ + ceph_calc_file_object_mapping(layout, off, plen, &bno, + &objoff, &objlen); + if (*plen < orig_len) + dout(" skipping last %llu, final file extent %llu~%llu\n", + orig_len - *plen, off, *plen); + + sprintf(req->r_oid, "%llx.%08llx", vino.ino, bno); + req->r_oid_len = strlen(req->r_oid); + + op->extent.offset = cpu_to_le64(objoff); + op->extent.length = cpu_to_le64(objlen); + req->r_num_pages = calc_pages_for(off, *plen); + + dout("calc_layout %s (%d) %llu~%llu (%d pages)\n", + req->r_oid, req->r_oid_len, objoff, objlen, req->r_num_pages); +} + + +/* + * requests + */ +void ceph_osdc_put_request(struct ceph_osd_request *req) +{ + dout("osdc put_request %p %d -> %d\n", req, atomic_read(&req->r_ref), + atomic_read(&req->r_ref)-1); + BUG_ON(atomic_read(&req->r_ref) <= 0); + if (atomic_dec_and_test(&req->r_ref)) { + if (req->r_request) + ceph_msg_put(req->r_request); + if (req->r_reply) + ceph_msg_put(req->r_reply); + if (req->r_own_pages) + ceph_release_page_vector(req->r_pages, + req->r_num_pages); + ceph_put_snap_context(req->r_snapc); + if (req->r_mempool) + mempool_free(req, req->r_osdc->req_mempool); + else + kfree(req); + } +} + +/* + * build new request AND message, calculate layout, and adjust file + * extent as needed. + * + * if the file was recently truncated, we include information about its + * old and new size so that the object can be updated appropriately. (we + * avoid synchronously deleting truncated objects because it's slow.) + * + * if @do_sync, include a 'startsync' command so that the osd will flush + * data quickly. + */ +struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, + struct ceph_file_layout *layout, + struct ceph_vino vino, + u64 off, u64 *plen, + int opcode, int flags, + struct ceph_snap_context *snapc, + int do_sync, + u32 truncate_seq, + u64 truncate_size, + struct timespec *mtime, + bool use_mempool, int num_reply) +{ + struct ceph_osd_request *req; + struct ceph_msg *msg; + struct ceph_osd_request_head *head; + struct ceph_osd_op *op; + void *p; + int do_trunc = truncate_seq && (off + *plen > truncate_size); + int num_op = 1 + do_sync + do_trunc; + size_t msg_size = sizeof(*head) + num_op*sizeof(*op); + int err, i; + u64 prevofs; + + if (use_mempool) { + req = mempool_alloc(osdc->req_mempool, GFP_NOFS); + memset(req, 0, sizeof(*req)); + } else { + req = kzalloc(sizeof(*req), GFP_NOFS); + } + if (req == NULL) + return ERR_PTR(-ENOMEM); + + err = ceph_msgpool_resv(&osdc->msgpool_op_reply, num_reply); + if (err) { + ceph_osdc_put_request(req); + return ERR_PTR(-ENOMEM); + } + + req->r_osdc = osdc; + req->r_mempool = use_mempool; + atomic_set(&req->r_ref, 1); + init_completion(&req->r_completion); + init_completion(&req->r_safe_completion); + INIT_LIST_HEAD(&req->r_unsafe_item); + req->r_flags = flags; + + WARN_ON((flags & (CEPH_OSD_FLAG_READ|CEPH_OSD_FLAG_WRITE)) == 0); + + /* create message; allow space for oid */ + msg_size += 40; + if (snapc) + msg_size += sizeof(u64) * snapc->num_snaps; + if (use_mempool) + msg = ceph_msgpool_get(&osdc->msgpool_op); + else + msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, 0, 0, NULL); + if (IS_ERR(msg)) { + ceph_msgpool_resv(&osdc->msgpool_op_reply, num_reply); + ceph_osdc_put_request(req); + return ERR_PTR(PTR_ERR(msg)); + } + msg->hdr.type = cpu_to_le16(CEPH_MSG_OSD_OP); + memset(msg->front.iov_base, 0, msg->front.iov_len); + head = msg->front.iov_base; + op = (void *)(head + 1); + p = (void *)(op + num_op); + + req->r_request = msg; + req->r_snapc = ceph_get_snap_context(snapc); + + head->client_inc = cpu_to_le32(1); /* always, for now. */ + head->flags = cpu_to_le32(flags); + if (flags & CEPH_OSD_FLAG_WRITE) + ceph_encode_timespec(&head->mtime, mtime); + head->num_ops = cpu_to_le16(num_op); + op->op = cpu_to_le16(opcode); + + /* calculate max write size */ + calc_layout(osdc, vino, layout, off, plen, req); + req->r_file_layout = *layout; /* keep a copy */ + + if (flags & CEPH_OSD_FLAG_WRITE) { + req->r_request->hdr.data_off = cpu_to_le16(off); + req->r_request->hdr.data_len = cpu_to_le32(*plen); + op->payload_len = cpu_to_le32(*plen); + } + + /* fill in oid */ + head->object_len = cpu_to_le32(req->r_oid_len); + memcpy(p, req->r_oid, req->r_oid_len); + p += req->r_oid_len; + + /* additional ops */ + if (do_trunc) { + op++; + op->op = cpu_to_le16(opcode == CEPH_OSD_OP_READ ? + CEPH_OSD_OP_MASKTRUNC : CEPH_OSD_OP_SETTRUNC); + op->trunc.truncate_seq = cpu_to_le32(truncate_seq); + prevofs = le64_to_cpu((op-1)->extent.offset); + op->trunc.truncate_size = cpu_to_le64(truncate_size - + (off-prevofs)); + } + if (do_sync) { + op++; + op->op = cpu_to_le16(CEPH_OSD_OP_STARTSYNC); + } + if (snapc) { + head->snap_seq = cpu_to_le64(snapc->seq); + head->num_snaps = cpu_to_le32(snapc->num_snaps); + for (i = 0; i < snapc->num_snaps; i++) { + put_unaligned_le64(snapc->snaps[i], p); + p += sizeof(u64); + } + } + + BUG_ON(p > msg->front.iov_base + msg->front.iov_len); + return req; +} + +/* + * We keep osd requests in an rbtree, sorted by ->r_tid. + */ +static void __insert_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *new) +{ + struct rb_node **p = &osdc->requests.rb_node; + struct rb_node *parent = NULL; + struct ceph_osd_request *req = NULL; + + while (*p) { + parent = *p; + req = rb_entry(parent, struct ceph_osd_request, r_node); + if (new->r_tid < req->r_tid) + p = &(*p)->rb_left; + else if (new->r_tid > req->r_tid) + p = &(*p)->rb_right; + else + BUG(); + } + + rb_link_node(&new->r_node, parent, p); + rb_insert_color(&new->r_node, &osdc->requests); +} + +static struct ceph_osd_request *__lookup_request(struct ceph_osd_client *osdc, + u64 tid) +{ + struct ceph_osd_request *req; + struct rb_node *n = osdc->requests.rb_node; + + while (n) { + req = rb_entry(n, struct ceph_osd_request, r_node); + if (tid < req->r_tid) + n = n->rb_left; + else if (tid > req->r_tid) + n = n->rb_right; + else + return req; + } + return NULL; +} + +static struct ceph_osd_request * +__lookup_request_ge(struct ceph_osd_client *osdc, + u64 tid) +{ + struct ceph_osd_request *req; + struct rb_node *n = osdc->requests.rb_node; + + while (n) { + req = rb_entry(n, struct ceph_osd_request, r_node); + if (tid < req->r_tid) { + if (!n->rb_left) + return req; + n = n->rb_left; + } else if (tid > req->r_tid) { + n = n->rb_right; + } else { + return req; + } + } + return NULL; +} + + +/* + * The messaging layer will reconnect to the osd as needed. If the + * session has dropped, the OSD will have dropped the session state, + * and we'll get notified by the messaging layer. If that happens, we + * need to resubmit all requests for that osd. + */ +static void osd_reset(struct ceph_connection *con) +{ + struct ceph_osd *osd = con->private; + struct ceph_osd_client *osdc; + + if (!osd) + return; + dout("osd_reset osd%d\n", osd->o_osd); + osdc = osd->o_osdc; + osd->o_incarnation++; + down_read(&osdc->map_sem); + kick_requests(osdc, osd); + up_read(&osdc->map_sem); +} + +/* + * Track open sessions with osds. + */ +static struct ceph_osd *create_osd(struct ceph_osd_client *osdc) +{ + struct ceph_osd *osd; + + osd = kzalloc(sizeof(*osd), GFP_NOFS); + if (!osd) + return NULL; + + atomic_set(&osd->o_ref, 1); + osd->o_osdc = osdc; + INIT_LIST_HEAD(&osd->o_requests); + osd->o_incarnation = 1; + + ceph_con_init(osdc->client->msgr, &osd->o_con); + osd->o_con.private = osd; + osd->o_con.ops = &osd_con_ops; + osd->o_con.peer_name.type = CEPH_ENTITY_TYPE_OSD; + return osd; +} + +static struct ceph_osd *get_osd(struct ceph_osd *osd) +{ + if (atomic_inc_not_zero(&osd->o_ref)) { + dout("get_osd %p %d -> %d\n", osd, atomic_read(&osd->o_ref)-1, + atomic_read(&osd->o_ref)); + return osd; + } else { + dout("get_osd %p FAIL\n", osd); + return NULL; + } +} + +static void put_osd(struct ceph_osd *osd) +{ + dout("put_osd %p %d -> %d\n", osd, atomic_read(&osd->o_ref), + atomic_read(&osd->o_ref) - 1); + if (atomic_dec_and_test(&osd->o_ref)) { + ceph_con_shutdown(&osd->o_con); + kfree(osd); + } +} + +/* + * remove an osd from our map + */ +static void remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) +{ + dout("remove_osd %p\n", osd); + BUG_ON(!list_empty(&osd->o_requests)); + rb_erase(&osd->o_node, &osdc->osds); + ceph_con_close(&osd->o_con); + put_osd(osd); +} + +/* + * reset osd connect + */ +static int reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) +{ + int ret = 0; + + dout("reset_osd %p osd%d\n", osd, osd->o_osd); + if (list_empty(&osd->o_requests)) { + remove_osd(osdc, osd); + } else { + ceph_con_close(&osd->o_con); + ceph_con_open(&osd->o_con, &osdc->osdmap->osd_addr[osd->o_osd]); + osd->o_incarnation++; + } + return ret; +} + +static void __insert_osd(struct ceph_osd_client *osdc, struct ceph_osd *new) +{ + struct rb_node **p = &osdc->osds.rb_node; + struct rb_node *parent = NULL; + struct ceph_osd *osd = NULL; + + while (*p) { + parent = *p; + osd = rb_entry(parent, struct ceph_osd, o_node); + if (new->o_osd < osd->o_osd) + p = &(*p)->rb_left; + else if (new->o_osd > osd->o_osd) + p = &(*p)->rb_right; + else + BUG(); + } + + rb_link_node(&new->o_node, parent, p); + rb_insert_color(&new->o_node, &osdc->osds); +} + +static struct ceph_osd *__lookup_osd(struct ceph_osd_client *osdc, int o) +{ + struct ceph_osd *osd; + struct rb_node *n = osdc->osds.rb_node; + + while (n) { + osd = rb_entry(n, struct ceph_osd, o_node); + if (o < osd->o_osd) + n = n->rb_left; + else if (o > osd->o_osd) + n = n->rb_right; + else + return osd; + } + return NULL; +} + + +/* + * Register request, assign tid. If this is the first request, set up + * the timeout event. + */ +static void register_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req) +{ + struct ceph_osd_request_head *head = req->r_request->front.iov_base; + + mutex_lock(&osdc->request_mutex); + req->r_tid = ++osdc->last_tid; + head->tid = cpu_to_le64(req->r_tid); + + dout("register_request %p tid %lld\n", req, req->r_tid); + __insert_request(osdc, req); + ceph_osdc_get_request(req); + osdc->num_requests++; + + req->r_timeout_stamp = + jiffies + osdc->client->mount_args.osd_timeout*HZ; + + if (osdc->num_requests == 1) { + osdc->timeout_tid = req->r_tid; + dout(" timeout on tid %llu at %lu\n", req->r_tid, + req->r_timeout_stamp); + schedule_delayed_work(&osdc->timeout_work, + round_jiffies_relative(req->r_timeout_stamp - jiffies)); + } + mutex_unlock(&osdc->request_mutex); +} + +/* + * called under osdc->request_mutex + */ +static void __unregister_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req) +{ + dout("__unregister_request %p tid %lld\n", req, req->r_tid); + rb_erase(&req->r_node, &osdc->requests); + osdc->num_requests--; + + list_del_init(&req->r_osd_item); + if (list_empty(&req->r_osd->o_requests)) + remove_osd(osdc, req->r_osd); + req->r_osd = NULL; + + ceph_osdc_put_request(req); + + if (req->r_tid == osdc->timeout_tid) { + if (osdc->num_requests == 0) { + dout("no requests, canceling timeout\n"); + osdc->timeout_tid = 0; + cancel_delayed_work(&osdc->timeout_work); + } else { + req = rb_entry(rb_first(&osdc->requests), + struct ceph_osd_request, r_node); + osdc->timeout_tid = req->r_tid; + dout("rescheduled timeout on tid %llu at %lu\n", + req->r_tid, req->r_timeout_stamp); + schedule_delayed_work(&osdc->timeout_work, + round_jiffies_relative(req->r_timeout_stamp - + jiffies)); + } + } +} + +/* + * Cancel a previously queued request message + */ +static void __cancel_request(struct ceph_osd_request *req) +{ + if (req->r_sent) { + ceph_con_revoke(&req->r_osd->o_con, req->r_request); + req->r_sent = 0; + } +} + +/* + * Pick an osd (the first 'up' osd in the pg), allocate the osd struct + * (as needed), and set the request r_osd appropriately. If there is + * no up osd, set r_osd to NULL. + * + * Return 0 if unchanged, 1 if changed, or negative on error. + * + * Caller should hold map_sem for read and request_mutex. + */ +static int __map_osds(struct ceph_osd_client *osdc, + struct ceph_osd_request *req) +{ + struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; + union ceph_pg pgid; + int o = -1; + int err; + struct ceph_osd *newosd = NULL; + + dout("map_osds %p tid %lld\n", req, req->r_tid); + err = ceph_calc_object_layout(&reqhead->layout, req->r_oid, + &req->r_file_layout, osdc->osdmap); + if (err) + return err; + pgid.pg64 = le64_to_cpu(reqhead->layout.ol_pgid); + o = ceph_calc_pg_primary(osdc->osdmap, pgid); + + if ((req->r_osd && req->r_osd->o_osd == o && + req->r_sent >= req->r_osd->o_incarnation) || + (req->r_osd == NULL && o == -1)) + return 0; /* no change */ + + dout("map_osds tid %llu pgid %llx pool %d osd%d (was osd%d)\n", + req->r_tid, pgid.pg64, pgid.pg.pool, o, + req->r_osd ? req->r_osd->o_osd : -1); + + if (req->r_osd) { + __cancel_request(req); + list_del_init(&req->r_osd_item); + if (list_empty(&req->r_osd->o_requests)) { + /* try to re-use r_osd if possible */ + newosd = get_osd(req->r_osd); + remove_osd(osdc, newosd); + } + req->r_osd = NULL; + } + + req->r_osd = __lookup_osd(osdc, o); + if (!req->r_osd && o >= 0) { + if (newosd) { + req->r_osd = newosd; + newosd = NULL; + } else { + err = -ENOMEM; + req->r_osd = create_osd(osdc); + if (!req->r_osd) + goto out; + } + + dout("map_osds osd %p is osd%d\n", req->r_osd, o); + req->r_osd->o_osd = o; + req->r_osd->o_con.peer_name.num = cpu_to_le64(o); + __insert_osd(osdc, req->r_osd); + + ceph_con_open(&req->r_osd->o_con, &osdc->osdmap->osd_addr[o]); + } + + if (req->r_osd) + list_add(&req->r_osd_item, &req->r_osd->o_requests); + err = 1; /* osd changed */ + +out: + if (newosd) + put_osd(newosd); + return err; +} + +/* + * caller should hold map_sem (for read) and request_mutex + */ +static int __send_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req) +{ + struct ceph_osd_request_head *reqhead; + int err; + + err = __map_osds(osdc, req); + if (err < 0) + return err; + if (req->r_osd == NULL) { + dout("send_request %p no up osds in pg\n", req); + ceph_monc_request_next_osdmap(&osdc->client->monc); + return 0; + } + + dout("send_request %p tid %llu to osd%d flags %d\n", + req, req->r_tid, req->r_osd->o_osd, req->r_flags); + + reqhead = req->r_request->front.iov_base; + reqhead->osdmap_epoch = cpu_to_le32(osdc->osdmap->epoch); + reqhead->flags |= cpu_to_le32(req->r_flags); /* e.g., RETRY */ + reqhead->reassert_version = req->r_reassert_version; + + req->r_timeout_stamp = jiffies+osdc->client->mount_args.osd_timeout*HZ; + + ceph_msg_get(req->r_request); /* send consumes a ref */ + ceph_con_send(&req->r_osd->o_con, req->r_request); + req->r_sent = req->r_osd->o_incarnation; + return 0; +} + +/* + * Timeout callback, called every N seconds when 1 or more osd + * requests has been active for more than N seconds. When this + * happens, we ping all OSDs with requests who have timed out to + * ensure any communications channel reset is detected. Reset the + * request timeouts another N seconds in the future as we go. + * Reschedule the timeout event another N seconds in future (unless + * there are no open requests). + */ +static void handle_timeout(struct work_struct *work) +{ + struct ceph_osd_client *osdc = + container_of(work, struct ceph_osd_client, timeout_work.work); + struct ceph_osd_request *req; + struct ceph_osd *osd; + unsigned long timeout = osdc->client->mount_args.osd_timeout * HZ; + unsigned long next_timeout = timeout + jiffies; + struct rb_node *p; + + dout("timeout\n"); + down_read(&osdc->map_sem); + + ceph_monc_request_next_osdmap(&osdc->client->monc); + + mutex_lock(&osdc->request_mutex); + for (p = rb_first(&osdc->requests); p; p = rb_next(p)) { + req = rb_entry(p, struct ceph_osd_request, r_node); + + if (req->r_resend) { + int err; + + dout("osdc resending prev failed %lld\n", req->r_tid); + err = __send_request(osdc, req); + if (err) + dout("osdc failed again on %lld\n", req->r_tid); + else + req->r_resend = false; + continue; + } + } + for (p = rb_first(&osdc->osds); p; p = rb_next(p)) { + osd = rb_entry(p, struct ceph_osd, o_node); + if (list_empty(&osd->o_requests)) + continue; + req = list_first_entry(&osd->o_requests, + struct ceph_osd_request, r_osd_item); + if (time_before(jiffies, req->r_timeout_stamp)) + continue; + + dout(" tid %llu (at least) timed out on osd%d\n", + req->r_tid, osd->o_osd); + req->r_timeout_stamp = next_timeout; + ceph_con_keepalive(&osd->o_con); + } + + if (osdc->timeout_tid) + schedule_delayed_work(&osdc->timeout_work, + round_jiffies_relative(timeout)); + + mutex_unlock(&osdc->request_mutex); + + up_read(&osdc->map_sem); +} + +/* + * handle osd op reply. either call the callback if it is specified, + * or do the completion to wake up the waiting thread. + */ +static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg) +{ + struct ceph_osd_reply_head *rhead = msg->front.iov_base; + struct ceph_osd_request *req; + u64 tid; + int numops, object_len, flags; + + if (msg->front.iov_len < sizeof(*rhead)) + goto bad; + tid = le64_to_cpu(rhead->tid); + numops = le32_to_cpu(rhead->num_ops); + object_len = le32_to_cpu(rhead->object_len); + if (msg->front.iov_len != sizeof(*rhead) + object_len + + numops * sizeof(struct ceph_osd_op)) + goto bad; + dout("handle_reply %p tid %llu\n", msg, tid); + + /* lookup */ + mutex_lock(&osdc->request_mutex); + req = __lookup_request(osdc, tid); + if (req == NULL) { + dout("handle_reply tid %llu dne\n", tid); + mutex_unlock(&osdc->request_mutex); + return; + } + ceph_osdc_get_request(req); + flags = le32_to_cpu(rhead->flags); + + if (req->r_reply) { + /* + * once we see the message has been received, we don't + * need a ref (which is only needed for revoking + * pages) + */ + ceph_msg_put(req->r_reply); + req->r_reply = NULL; + } + + if (!req->r_got_reply) { + unsigned bytes; + + req->r_result = le32_to_cpu(rhead->result); + bytes = le32_to_cpu(msg->hdr.data_len); + dout("handle_reply result %d bytes %d\n", req->r_result, + bytes); + if (req->r_result == 0) + req->r_result = bytes; + + /* in case this is a write and we need to replay, */ + req->r_reassert_version = rhead->reassert_version; + + req->r_got_reply = 1; + } else if ((flags & CEPH_OSD_FLAG_ONDISK) == 0) { + dout("handle_reply tid %llu dup ack\n", tid); + goto done; + } + + dout("handle_reply tid %llu flags %d\n", tid, flags); + + /* either this is a read, or we got the safe response */ + if ((flags & CEPH_OSD_FLAG_ONDISK) || + ((flags & CEPH_OSD_FLAG_WRITE) == 0)) + __unregister_request(osdc, req); + + mutex_unlock(&osdc->request_mutex); + + if (req->r_callback) + req->r_callback(req, msg); + else + complete(&req->r_completion); + + if (flags & CEPH_OSD_FLAG_ONDISK) { + if (req->r_safe_callback) + req->r_safe_callback(req, msg); + complete(&req->r_safe_completion); /* fsync waiter */ + } + +done: + ceph_osdc_put_request(req); + return; + +bad: + pr_err("corrupt osd_op_reply got %d %d expected %d\n", + (int)msg->front.iov_len, le32_to_cpu(msg->hdr.front_len), + (int)sizeof(*rhead)); +} + + +/* + * Resubmit osd requests whose osd or osd address has changed. Request + * a new osd map if osds are down, or we are otherwise unable to determine + * how to direct a request. + * + * Close connections to down osds. + * + * If @who is specified, resubmit requests for that specific osd. + * + * Caller should hold map_sem for read and request_mutex. + */ +static void kick_requests(struct ceph_osd_client *osdc, + struct ceph_osd *kickosd) +{ + struct ceph_osd_request *req; + struct rb_node *p, *n; + int needmap = 0; + int err; + + dout("kick_requests osd%d\n", kickosd ? kickosd->o_osd : -1); + mutex_lock(&osdc->request_mutex); + if (!kickosd) { + for (p = rb_first(&osdc->osds); p; p = n) { + struct ceph_osd *osd = + rb_entry(p, struct ceph_osd, o_node); + + n = rb_next(p); + if (!ceph_osd_is_up(osdc->osdmap, osd->o_osd) || + !ceph_entity_addr_equal(&osd->o_con.peer_addr, + ceph_osd_addr(osdc->osdmap, + osd->o_osd))) + reset_osd(osdc, osd); + } + } + + for (p = rb_first(&osdc->requests); p; p = rb_next(p)) { + req = rb_entry(p, struct ceph_osd_request, r_node); + + if (req->r_resend) { + dout(" r_resend set on tid %llu\n", req->r_tid); + goto kick; + } + if (req->r_osd && kickosd == req->r_osd) + goto kick; + + err = __map_osds(osdc, req); + if (err == 0) + continue; /* no change */ + if (err < 0) { + /* + * FIXME: really, we should set the request + * error and fail if this isn't a 'nofail' + * request, but that's a fair bit more + * complicated to do. So retry! + */ + dout(" setting r_resend on %llu\n", req->r_tid); + req->r_resend = true; + continue; + } + if (req->r_osd == NULL) { + dout("tid %llu maps to no valid osd\n", req->r_tid); + needmap++; /* request a newer map */ + continue; + } + +kick: + dout("kicking tid %llu osd%d\n", req->r_tid, req->r_osd->o_osd); + req->r_flags |= CEPH_OSD_FLAG_RETRY; + err = __send_request(osdc, req); + if (err) { + dout(" setting r_resend on %llu\n", req->r_tid); + req->r_resend = true; + } + } + mutex_unlock(&osdc->request_mutex); + + if (needmap) { + dout("%d requests for down osds, need new map\n", needmap); + ceph_monc_request_next_osdmap(&osdc->client->monc); + } +} + +/* + * Process updated osd map. + * + * The message contains any number of incremental and full maps, normally + * indicating some sort of topology change in the cluster. Kick requests + * off to different OSDs as needed. + */ +void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) +{ + void *p, *end, *next; + u32 nr_maps, maplen; + u32 epoch; + struct ceph_osdmap *newmap = NULL, *oldmap; + int err; + struct ceph_fsid fsid; + + dout("handle_map have %u\n", osdc->osdmap ? osdc->osdmap->epoch : 0); + p = msg->front.iov_base; + end = p + msg->front.iov_len; + + /* verify fsid */ + ceph_decode_need(&p, end, sizeof(fsid), bad); + ceph_decode_copy(&p, &fsid, sizeof(fsid)); + if (ceph_fsid_compare(&fsid, &osdc->client->monc.monmap->fsid)) { + pr_err("got osdmap with wrong fsid, ignoring\n"); + return; + } + + down_write(&osdc->map_sem); + + /* incremental maps */ + ceph_decode_32_safe(&p, end, nr_maps, bad); + dout(" %d inc maps\n", nr_maps); + while (nr_maps > 0) { + ceph_decode_need(&p, end, 2*sizeof(u32), bad); + ceph_decode_32(&p, epoch); + ceph_decode_32(&p, maplen); + ceph_decode_need(&p, end, maplen, bad); + next = p + maplen; + if (osdc->osdmap && osdc->osdmap->epoch+1 == epoch) { + dout("applying incremental map %u len %d\n", + epoch, maplen); + newmap = osdmap_apply_incremental(&p, next, + osdc->osdmap, + osdc->client->msgr); + if (IS_ERR(newmap)) { + err = PTR_ERR(newmap); + goto bad; + } + if (newmap != osdc->osdmap) { + ceph_osdmap_destroy(osdc->osdmap); + osdc->osdmap = newmap; + } + } else { + dout("ignoring incremental map %u len %d\n", + epoch, maplen); + } + p = next; + nr_maps--; + } + if (newmap) + goto done; + + /* full maps */ + ceph_decode_32_safe(&p, end, nr_maps, bad); + dout(" %d full maps\n", nr_maps); + while (nr_maps) { + ceph_decode_need(&p, end, 2*sizeof(u32), bad); + ceph_decode_32(&p, epoch); + ceph_decode_32(&p, maplen); + ceph_decode_need(&p, end, maplen, bad); + if (nr_maps > 1) { + dout("skipping non-latest full map %u len %d\n", + epoch, maplen); + } else if (osdc->osdmap && osdc->osdmap->epoch >= epoch) { + dout("skipping full map %u len %d, " + "older than our %u\n", epoch, maplen, + osdc->osdmap->epoch); + } else { + dout("taking full map %u len %d\n", epoch, maplen); + newmap = osdmap_decode(&p, p+maplen); + if (IS_ERR(newmap)) { + err = PTR_ERR(newmap); + goto bad; + } + oldmap = osdc->osdmap; + osdc->osdmap = newmap; + if (oldmap) + ceph_osdmap_destroy(oldmap); + } + p += maplen; + nr_maps--; + } + +done: + downgrade_write(&osdc->map_sem); + ceph_monc_got_osdmap(&osdc->client->monc, osdc->osdmap->epoch); + if (newmap) + kick_requests(osdc, NULL); + up_read(&osdc->map_sem); + return; + +bad: + pr_err("osdc handle_map corrupt msg\n"); + up_write(&osdc->map_sem); + return; +} + + +/* + * A read request prepares specific pages that data is to be read into. + * When a message is being read off the wire, we call prepare_pages to + * find those pages. + * 0 = success, -1 failure. + */ +static int prepare_pages(struct ceph_connection *con, struct ceph_msg *m, + int want) +{ + struct ceph_osd *osd = con->private; + struct ceph_osd_client *osdc; + struct ceph_osd_reply_head *rhead = m->front.iov_base; + struct ceph_osd_request *req; + u64 tid; + int ret = -1; + int type = le16_to_cpu(m->hdr.type); + + if (!osd) + return -1; + osdc = osd->o_osdc; + + dout("prepare_pages on msg %p want %d\n", m, want); + if (unlikely(type != CEPH_MSG_OSD_OPREPLY)) + return -1; /* hmm! */ + + tid = le64_to_cpu(rhead->tid); + mutex_lock(&osdc->request_mutex); + req = __lookup_request(osdc, tid); + if (!req) { + dout("prepare_pages unknown tid %llu\n", tid); + goto out; + } + dout("prepare_pages tid %llu has %d pages, want %d\n", + tid, req->r_num_pages, want); + if (likely(req->r_num_pages >= want && !req->r_prepared_pages)) { + m->pages = req->r_pages; + m->nr_pages = req->r_num_pages; + req->r_reply = m; /* only for duration of read over socket */ + ceph_msg_get(m); + req->r_prepared_pages = 1; + ret = 0; /* success */ + } +out: + mutex_unlock(&osdc->request_mutex); + return ret; +} + +/* + * Register request, send initial attempt. + */ +int ceph_osdc_start_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req, + bool nofail) +{ + int rc; + + req->r_request->pages = req->r_pages; + req->r_request->nr_pages = req->r_num_pages; + + register_request(osdc, req); + + down_read(&osdc->map_sem); + mutex_lock(&osdc->request_mutex); + rc = __send_request(osdc, req); + if (rc) { + if (nofail) { + dout("osdc_start_request failed send, marking %lld\n", + req->r_tid); + req->r_resend = true; + rc = 0; + } else { + __unregister_request(osdc, req); + } + } + mutex_unlock(&osdc->request_mutex); + up_read(&osdc->map_sem); + return rc; +} + +/* + * wait for a request to complete + */ +int ceph_osdc_wait_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req) +{ + int rc; + + rc = wait_for_completion_interruptible(&req->r_completion); + if (rc < 0) { + mutex_lock(&osdc->request_mutex); + __cancel_request(req); + mutex_unlock(&osdc->request_mutex); + dout("wait_request tid %llu timed out\n", req->r_tid); + return rc; + } + + dout("wait_request tid %llu result %d\n", req->r_tid, req->r_result); + return req->r_result; +} + +/* + * sync - wait for all in-flight requests to flush. avoid starvation. + */ +void ceph_osdc_sync(struct ceph_osd_client *osdc) +{ + struct ceph_osd_request *req; + u64 last_tid, next_tid = 0; + + mutex_lock(&osdc->request_mutex); + last_tid = osdc->last_tid; + while (1) { + req = __lookup_request_ge(osdc, next_tid); + if (!req) + break; + if (req->r_tid > last_tid) + break; + + next_tid = req->r_tid + 1; + if ((req->r_flags & CEPH_OSD_FLAG_WRITE) == 0) + continue; + + ceph_osdc_get_request(req); + mutex_unlock(&osdc->request_mutex); + dout("sync waiting on tid %llu (last is %llu)\n", + req->r_tid, last_tid); + wait_for_completion(&req->r_safe_completion); + mutex_lock(&osdc->request_mutex); + ceph_osdc_put_request(req); + } + mutex_unlock(&osdc->request_mutex); + dout("sync done (thru tid %llu)\n", last_tid); +} + +/* + * init, shutdown + */ +int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client) +{ + int err; + + dout("init\n"); + osdc->client = client; + osdc->osdmap = NULL; + init_rwsem(&osdc->map_sem); + init_completion(&osdc->map_waiters); + osdc->last_requested_map = 0; + mutex_init(&osdc->request_mutex); + osdc->timeout_tid = 0; + osdc->last_tid = 0; + osdc->osds = RB_ROOT; + osdc->requests = RB_ROOT; + osdc->num_requests = 0; + INIT_DELAYED_WORK(&osdc->timeout_work, handle_timeout); + + osdc->req_mempool = mempool_create_kmalloc_pool(10, + sizeof(struct ceph_osd_request)); + if (!osdc->req_mempool) + return -ENOMEM; + + err = ceph_msgpool_init(&osdc->msgpool_op, 4096, 10, true); + if (err < 0) + return -ENOMEM; + err = ceph_msgpool_init(&osdc->msgpool_op_reply, 512, 0, false); + if (err < 0) + return -ENOMEM; + + return 0; +} + +void ceph_osdc_stop(struct ceph_osd_client *osdc) +{ + cancel_delayed_work_sync(&osdc->timeout_work); + if (osdc->osdmap) { + ceph_osdmap_destroy(osdc->osdmap); + osdc->osdmap = NULL; + } + mempool_destroy(osdc->req_mempool); + ceph_msgpool_destroy(&osdc->msgpool_op); + ceph_msgpool_destroy(&osdc->msgpool_op_reply); +} + +/* + * Read some contiguous pages. If we cross a stripe boundary, shorten + * *plen. Return number of bytes read, or error. + */ +int ceph_osdc_readpages(struct ceph_osd_client *osdc, + struct ceph_vino vino, struct ceph_file_layout *layout, + u64 off, u64 *plen, + u32 truncate_seq, u64 truncate_size, + struct page **pages, int num_pages) +{ + struct ceph_osd_request *req; + int rc = 0; + + dout("readpages on ino %llx.%llx on %llu~%llu\n", vino.ino, + vino.snap, off, *plen); + req = ceph_osdc_new_request(osdc, layout, vino, off, plen, + CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, + NULL, 0, truncate_seq, truncate_size, NULL, + false, 1); + if (IS_ERR(req)) + return PTR_ERR(req); + + /* it may be a short read due to an object boundary */ + req->r_pages = pages; + num_pages = calc_pages_for(off, *plen); + req->r_num_pages = num_pages; + + dout("readpages final extent is %llu~%llu (%d pages)\n", + off, *plen, req->r_num_pages); + + rc = ceph_osdc_start_request(osdc, req, false); + if (!rc) + rc = ceph_osdc_wait_request(osdc, req); + + ceph_osdc_put_request(req); + dout("readpages result %d\n", rc); + return rc; +} + +/* + * do a synchronous write on N pages + */ +int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, + struct ceph_file_layout *layout, + struct ceph_snap_context *snapc, + u64 off, u64 len, + u32 truncate_seq, u64 truncate_size, + struct timespec *mtime, + struct page **pages, int num_pages, + int flags, int do_sync, bool nofail) +{ + struct ceph_osd_request *req; + int rc = 0; + + BUG_ON(vino.snap != CEPH_NOSNAP); + req = ceph_osdc_new_request(osdc, layout, vino, off, &len, + CEPH_OSD_OP_WRITE, + flags | CEPH_OSD_FLAG_ONDISK | + CEPH_OSD_FLAG_WRITE, + snapc, do_sync, + truncate_seq, truncate_size, mtime, + nofail, 1); + if (IS_ERR(req)) + return PTR_ERR(req); + + /* it may be a short write due to an object boundary */ + req->r_pages = pages; + req->r_num_pages = calc_pages_for(off, len); + dout("writepages %llu~%llu (%d pages)\n", off, len, + req->r_num_pages); + + rc = ceph_osdc_start_request(osdc, req, nofail); + if (!rc) + rc = ceph_osdc_wait_request(osdc, req); + + ceph_osdc_put_request(req); + if (rc == 0) + rc = len; + dout("writepages result %d\n", rc); + return rc; +} + +/* + * handle incoming message + */ +static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) +{ + struct ceph_osd *osd = con->private; + struct ceph_osd_client *osdc = osd->o_osdc; + int type = le16_to_cpu(msg->hdr.type); + + if (!osd) + return; + + switch (type) { + case CEPH_MSG_OSD_MAP: + ceph_osdc_handle_map(osdc, msg); + break; + case CEPH_MSG_OSD_OPREPLY: + handle_reply(osdc, msg); + break; + + default: + pr_err("received unknown message type %d %s\n", type, + ceph_msg_type_name(type)); + } + ceph_msg_put(msg); +} + +static struct ceph_msg *alloc_msg(struct ceph_connection *con, + struct ceph_msg_header *hdr) +{ + struct ceph_osd *osd = con->private; + struct ceph_osd_client *osdc = osd->o_osdc; + int type = le16_to_cpu(hdr->type); + + switch (type) { + case CEPH_MSG_OSD_OPREPLY: + return ceph_msgpool_get(&osdc->msgpool_op_reply); + } + return ceph_alloc_msg(con, hdr); +} + +/* + * Wrappers to refcount containing ceph_osd struct + */ +static struct ceph_connection *get_osd_con(struct ceph_connection *con) +{ + struct ceph_osd *osd = con->private; + if (get_osd(osd)) + return con; + return NULL; +} + +static void put_osd_con(struct ceph_connection *con) +{ + struct ceph_osd *osd = con->private; + put_osd(osd); +} + +const static struct ceph_connection_operations osd_con_ops = { + .get = get_osd_con, + .put = put_osd_con, + .dispatch = dispatch, + .alloc_msg = alloc_msg, + .peer_reset = osd_reset, + .alloc_middle = ceph_alloc_middle, + .prepare_pages = prepare_pages, +}; diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h new file mode 100644 index 0000000..9a4addf --- /dev/null +++ b/fs/ceph/osd_client.h @@ -0,0 +1,144 @@ +#ifndef _FS_CEPH_OSD_CLIENT_H +#define _FS_CEPH_OSD_CLIENT_H + +#include +#include +#include + +#include "types.h" +#include "osdmap.h" +#include "messenger.h" + +struct ceph_msg; +struct ceph_snap_context; +struct ceph_osd_request; +struct ceph_osd_client; + +/* + * completion callback for async writepages + */ +typedef void (*ceph_osdc_callback_t)(struct ceph_osd_request *, + struct ceph_msg *); + +/* a given osd we're communicating with */ +struct ceph_osd { + atomic_t o_ref; + struct ceph_osd_client *o_osdc; + int o_osd; + int o_incarnation; + struct rb_node o_node; + struct ceph_connection o_con; + struct list_head o_requests; +}; + +/* an in-flight request */ +struct ceph_osd_request { + u64 r_tid; /* unique for this client */ + struct rb_node r_node; + struct list_head r_osd_item; + struct ceph_osd *r_osd; + + struct ceph_msg *r_request, *r_reply; + int r_result; + int r_flags; /* any additional flags for the osd */ + u32 r_sent; /* >0 if r_request is sending/sent */ + int r_prepared_pages, r_got_reply; + + struct ceph_osd_client *r_osdc; + atomic_t r_ref; + bool r_mempool; + struct completion r_completion, r_safe_completion; + ceph_osdc_callback_t r_callback, r_safe_callback; + struct ceph_eversion r_reassert_version; + struct list_head r_unsafe_item; + + struct inode *r_inode; /* for use by callbacks */ + struct writeback_control *r_wbc; /* ditto */ + + char r_oid[40]; /* object name */ + int r_oid_len; + unsigned long r_timeout_stamp; + bool r_resend; /* msg send failed, needs retry */ + + struct ceph_file_layout r_file_layout; + struct ceph_snap_context *r_snapc; /* snap context for writes */ + unsigned r_num_pages; /* size of page array (follows) */ + struct page **r_pages; /* pages for data payload */ + int r_pages_from_pool; + int r_own_pages; /* if true, i own page list */ +}; + +struct ceph_osd_client { + struct ceph_client *client; + + struct ceph_osdmap *osdmap; /* current map */ + struct rw_semaphore map_sem; + struct completion map_waiters; + u64 last_requested_map; + + struct mutex request_mutex; + struct rb_root osds; /* osds */ + u64 timeout_tid; /* tid of timeout triggering rq */ + u64 last_tid; /* tid of last request */ + struct rb_root requests; /* pending requests */ + int num_requests; + struct delayed_work timeout_work; + struct dentry *debugfs_file; + + mempool_t *req_mempool; + + struct ceph_msgpool msgpool_op; + struct ceph_msgpool msgpool_op_reply; +}; + +extern int ceph_osdc_init(struct ceph_osd_client *osdc, + struct ceph_client *client); +extern void ceph_osdc_stop(struct ceph_osd_client *osdc); + +extern void ceph_osdc_handle_reply(struct ceph_osd_client *osdc, + struct ceph_msg *msg); +extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, + struct ceph_msg *msg); + +extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, + struct ceph_file_layout *layout, + struct ceph_vino vino, + u64 offset, u64 *len, int op, int flags, + struct ceph_snap_context *snapc, + int do_sync, u32 truncate_seq, + u64 truncate_size, + struct timespec *mtime, + bool use_mempool, int num_reply); + +static inline void ceph_osdc_get_request(struct ceph_osd_request *req) +{ + atomic_inc(&req->r_ref); +} +extern void ceph_osdc_put_request(struct ceph_osd_request *req); + +extern int ceph_osdc_start_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req, + bool nofail); +extern int ceph_osdc_wait_request(struct ceph_osd_client *osdc, + struct ceph_osd_request *req); +extern void ceph_osdc_sync(struct ceph_osd_client *osdc); + +extern int ceph_osdc_readpages(struct ceph_osd_client *osdc, + struct ceph_vino vino, + struct ceph_file_layout *layout, + u64 off, u64 *plen, + u32 truncate_seq, u64 truncate_size, + struct page **pages, int nr_pages); + +extern int ceph_osdc_writepages(struct ceph_osd_client *osdc, + struct ceph_vino vino, + struct ceph_file_layout *layout, + struct ceph_snap_context *sc, + u64 off, u64 len, + u32 truncate_seq, u64 truncate_size, + struct timespec *mtime, + struct page **pages, int nr_pages, + int flags, int do_sync, bool nofail); + +#endif + diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c new file mode 100644 index 0000000..e38fe63 --- /dev/null +++ b/fs/ceph/osdmap.c @@ -0,0 +1,875 @@ + +#include + +#include "super.h" +#include "osdmap.h" +#include "crush/hash.h" +#include "crush/mapper.h" +#include "decode.h" +#include "ceph_debug.h" + +char *ceph_osdmap_state_str(char *str, int len, int state) +{ + int flag = 0; + + if (!len) + goto done; + + *str = '\0'; + if (state) { + if (state & CEPH_OSD_EXISTS) { + snprintf(str, len, "exists"); + flag = 1; + } + if (state & CEPH_OSD_UP) { + snprintf(str, len, "%s%s%s", str, (flag ? ", " : ""), + "up"); + flag = 1; + } + } else { + snprintf(str, len, "doesn't exist"); + } +done: + return str; +} + +/* maps */ + +static int calc_bits_of(unsigned t) +{ + int b = 0; + while (t) { + t = t >> 1; + b++; + } + return b; +} + +/* + * the foo_mask is the smallest value 2^n-1 that is >= foo. + */ +static void calc_pg_masks(struct ceph_pg_pool_info *pi) +{ + pi->pg_num_mask = (1 << calc_bits_of(le32_to_cpu(pi->v.pg_num)-1)) - 1; + pi->pgp_num_mask = + (1 << calc_bits_of(le32_to_cpu(pi->v.pgp_num)-1)) - 1; + pi->lpg_num_mask = + (1 << calc_bits_of(le32_to_cpu(pi->v.lpg_num)-1)) - 1; + pi->lpgp_num_mask = + (1 << calc_bits_of(le32_to_cpu(pi->v.lpgp_num)-1)) - 1; +} + +/* + * decode crush map + */ +static int crush_decode_uniform_bucket(void **p, void *end, + struct crush_bucket_uniform *b) +{ + dout("crush_decode_uniform_bucket %p to %p\n", *p, end); + ceph_decode_need(p, end, (1+b->h.size) * sizeof(u32), bad); + ceph_decode_32(p, b->item_weight); + return 0; +bad: + return -EINVAL; +} + +static int crush_decode_list_bucket(void **p, void *end, + struct crush_bucket_list *b) +{ + int j; + dout("crush_decode_list_bucket %p to %p\n", *p, end); + b->item_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS); + if (b->item_weights == NULL) + return -ENOMEM; + b->sum_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS); + if (b->sum_weights == NULL) + return -ENOMEM; + ceph_decode_need(p, end, 2 * b->h.size * sizeof(u32), bad); + for (j = 0; j < b->h.size; j++) { + ceph_decode_32(p, b->item_weights[j]); + ceph_decode_32(p, b->sum_weights[j]); + } + return 0; +bad: + return -EINVAL; +} + +static int crush_decode_tree_bucket(void **p, void *end, + struct crush_bucket_tree *b) +{ + int j; + dout("crush_decode_tree_bucket %p to %p\n", *p, end); + ceph_decode_32_safe(p, end, b->num_nodes, bad); + b->node_weights = kcalloc(b->num_nodes, sizeof(u32), GFP_NOFS); + if (b->node_weights == NULL) + return -ENOMEM; + ceph_decode_need(p, end, b->num_nodes * sizeof(u32), bad); + for (j = 0; j < b->num_nodes; j++) + ceph_decode_32(p, b->node_weights[j]); + return 0; +bad: + return -EINVAL; +} + +static int crush_decode_straw_bucket(void **p, void *end, + struct crush_bucket_straw *b) +{ + int j; + dout("crush_decode_straw_bucket %p to %p\n", *p, end); + b->item_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS); + if (b->item_weights == NULL) + return -ENOMEM; + b->straws = kcalloc(b->h.size, sizeof(u32), GFP_NOFS); + if (b->straws == NULL) + return -ENOMEM; + ceph_decode_need(p, end, 2 * b->h.size * sizeof(u32), bad); + for (j = 0; j < b->h.size; j++) { + ceph_decode_32(p, b->item_weights[j]); + ceph_decode_32(p, b->straws[j]); + } + return 0; +bad: + return -EINVAL; +} + +static struct crush_map *crush_decode(void *pbyval, void *end) +{ + struct crush_map *c; + int err = -EINVAL; + int i, j; + void **p = &pbyval; + void *start = pbyval; + u32 magic; + + dout("crush_decode %p to %p len %d\n", *p, end, (int)(end - *p)); + + c = kzalloc(sizeof(*c), GFP_NOFS); + if (c == NULL) + return ERR_PTR(-ENOMEM); + + ceph_decode_need(p, end, 4*sizeof(u32), bad); + ceph_decode_32(p, magic); + if (magic != CRUSH_MAGIC) { + pr_err("crush_decode magic %x != current %x\n", + (unsigned)magic, (unsigned)CRUSH_MAGIC); + goto bad; + } + ceph_decode_32(p, c->max_buckets); + ceph_decode_32(p, c->max_rules); + ceph_decode_32(p, c->max_devices); + + c->device_parents = kcalloc(c->max_devices, sizeof(u32), GFP_NOFS); + if (c->device_parents == NULL) + goto badmem; + c->bucket_parents = kcalloc(c->max_buckets, sizeof(u32), GFP_NOFS); + if (c->bucket_parents == NULL) + goto badmem; + + c->buckets = kcalloc(c->max_buckets, sizeof(*c->buckets), GFP_NOFS); + if (c->buckets == NULL) + goto badmem; + c->rules = kcalloc(c->max_rules, sizeof(*c->rules), GFP_NOFS); + if (c->rules == NULL) + goto badmem; + + /* buckets */ + for (i = 0; i < c->max_buckets; i++) { + int size = 0; + u32 alg; + struct crush_bucket *b; + + ceph_decode_32_safe(p, end, alg, bad); + if (alg == 0) { + c->buckets[i] = NULL; + continue; + } + dout("crush_decode bucket %d off %x %p to %p\n", + i, (int)(*p-start), *p, end); + + switch (alg) { + case CRUSH_BUCKET_UNIFORM: + size = sizeof(struct crush_bucket_uniform); + break; + case CRUSH_BUCKET_LIST: + size = sizeof(struct crush_bucket_list); + break; + case CRUSH_BUCKET_TREE: + size = sizeof(struct crush_bucket_tree); + break; + case CRUSH_BUCKET_STRAW: + size = sizeof(struct crush_bucket_straw); + break; + default: + goto bad; + } + BUG_ON(size == 0); + b = c->buckets[i] = kzalloc(size, GFP_NOFS); + if (b == NULL) + goto badmem; + + ceph_decode_need(p, end, 4*sizeof(u32), bad); + ceph_decode_32(p, b->id); + ceph_decode_16(p, b->type); + ceph_decode_16(p, b->alg); + ceph_decode_32(p, b->weight); + ceph_decode_32(p, b->size); + + dout("crush_decode bucket size %d off %x %p to %p\n", + b->size, (int)(*p-start), *p, end); + + b->items = kcalloc(b->size, sizeof(__s32), GFP_NOFS); + if (b->items == NULL) + goto badmem; + b->perm = kcalloc(b->size, sizeof(u32), GFP_NOFS); + if (b->perm == NULL) + goto badmem; + b->perm_n = 0; + + ceph_decode_need(p, end, b->size*sizeof(u32), bad); + for (j = 0; j < b->size; j++) + ceph_decode_32(p, b->items[j]); + + switch (b->alg) { + case CRUSH_BUCKET_UNIFORM: + err = crush_decode_uniform_bucket(p, end, + (struct crush_bucket_uniform *)b); + if (err < 0) + goto bad; + break; + case CRUSH_BUCKET_LIST: + err = crush_decode_list_bucket(p, end, + (struct crush_bucket_list *)b); + if (err < 0) + goto bad; + break; + case CRUSH_BUCKET_TREE: + err = crush_decode_tree_bucket(p, end, + (struct crush_bucket_tree *)b); + if (err < 0) + goto bad; + break; + case CRUSH_BUCKET_STRAW: + err = crush_decode_straw_bucket(p, end, + (struct crush_bucket_straw *)b); + if (err < 0) + goto bad; + break; + } + } + + /* rules */ + dout("rule vec is %p\n", c->rules); + for (i = 0; i < c->max_rules; i++) { + u32 yes; + struct crush_rule *r; + + ceph_decode_32_safe(p, end, yes, bad); + if (!yes) { + dout("crush_decode NO rule %d off %x %p to %p\n", + i, (int)(*p-start), *p, end); + c->rules[i] = NULL; + continue; + } + + dout("crush_decode rule %d off %x %p to %p\n", + i, (int)(*p-start), *p, end); + + /* len */ + ceph_decode_32_safe(p, end, yes, bad); +#if BITS_PER_LONG == 32 + if (yes > ULONG_MAX / sizeof(struct crush_rule_step)) + goto bad; +#endif + r = c->rules[i] = kmalloc(sizeof(*r) + + yes*sizeof(struct crush_rule_step), + GFP_NOFS); + if (r == NULL) + goto badmem; + dout(" rule %d is at %p\n", i, r); + r->len = yes; + ceph_decode_copy_safe(p, end, &r->mask, 4, bad); /* 4 u8's */ + ceph_decode_need(p, end, r->len*3*sizeof(u32), bad); + for (j = 0; j < r->len; j++) { + ceph_decode_32(p, r->steps[j].op); + ceph_decode_32(p, r->steps[j].arg1); + ceph_decode_32(p, r->steps[j].arg2); + } + } + + /* ignore trailing name maps. */ + + dout("crush_decode success\n"); + return c; + +badmem: + err = -ENOMEM; +bad: + dout("crush_decode fail %d\n", err); + crush_destroy(c); + return ERR_PTR(err); +} + + +/* + * osd map + */ +void ceph_osdmap_destroy(struct ceph_osdmap *map) +{ + dout("osdmap_destroy %p\n", map); + if (map->crush) + crush_destroy(map->crush); + while (!RB_EMPTY_ROOT(&map->pg_temp)) + rb_erase(rb_first(&map->pg_temp), &map->pg_temp); + kfree(map->osd_state); + kfree(map->osd_weight); + kfree(map->pg_pool); + kfree(map->osd_addr); + kfree(map); +} + +/* + * adjust max osd value. reallocate arrays. + */ +static int osdmap_set_max_osd(struct ceph_osdmap *map, int max) +{ + u8 *state; + struct ceph_entity_addr *addr; + u32 *weight; + + state = kcalloc(max, sizeof(*state), GFP_NOFS); + addr = kcalloc(max, sizeof(*addr), GFP_NOFS); + weight = kcalloc(max, sizeof(*weight), GFP_NOFS); + if (state == NULL || addr == NULL || weight == NULL) { + kfree(state); + kfree(addr); + kfree(weight); + return -ENOMEM; + } + + /* copy old? */ + if (map->osd_state) { + memcpy(state, map->osd_state, map->max_osd*sizeof(*state)); + memcpy(addr, map->osd_addr, map->max_osd*sizeof(*addr)); + memcpy(weight, map->osd_weight, map->max_osd*sizeof(*weight)); + kfree(map->osd_state); + kfree(map->osd_addr); + kfree(map->osd_weight); + } + + map->osd_state = state; + map->osd_weight = weight; + map->osd_addr = addr; + map->max_osd = max; + return 0; +} + +/* + * Insert a new pg_temp mapping + */ +static void __insert_pg_mapping(struct ceph_pg_mapping *new, + struct rb_root *root) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct ceph_pg_mapping *pg = NULL; + + while (*p) { + parent = *p; + pg = rb_entry(parent, struct ceph_pg_mapping, node); + if (new->pgid < pg->pgid) + p = &(*p)->rb_left; + else if (new->pgid > pg->pgid) + p = &(*p)->rb_right; + else + BUG(); + } + + rb_link_node(&new->node, parent, p); + rb_insert_color(&new->node, root); +} + +/* + * decode a full map. + */ +struct ceph_osdmap *osdmap_decode(void **p, void *end) +{ + struct ceph_osdmap *map; + u16 version; + u32 len, max, i; + int err = -EINVAL; + void *start = *p; + + dout("osdmap_decode %p to %p len %d\n", *p, end, (int)(end - *p)); + + map = kzalloc(sizeof(*map), GFP_NOFS); + if (map == NULL) + return ERR_PTR(-ENOMEM); + map->pg_temp = RB_ROOT; + + ceph_decode_16_safe(p, end, version, bad); + + ceph_decode_need(p, end, 2*sizeof(u64)+6*sizeof(u32), bad); + ceph_decode_copy(p, &map->fsid, sizeof(map->fsid)); + ceph_decode_32(p, map->epoch); + ceph_decode_copy(p, &map->created, sizeof(map->created)); + ceph_decode_copy(p, &map->modified, sizeof(map->modified)); + + ceph_decode_32(p, map->num_pools); + map->pg_pool = kcalloc(map->num_pools, sizeof(*map->pg_pool), + GFP_NOFS); + if (!map->pg_pool) { + err = -ENOMEM; + goto bad; + } + ceph_decode_32_safe(p, end, max, bad); + while (max--) { + ceph_decode_need(p, end, 4+sizeof(map->pg_pool->v), bad); + ceph_decode_32(p, i); + if (i >= map->num_pools) + goto bad; + ceph_decode_copy(p, &map->pg_pool[i].v, + sizeof(map->pg_pool->v)); + calc_pg_masks(&map->pg_pool[i]); + p += le32_to_cpu(map->pg_pool[i].v.num_snaps) * sizeof(u64); + p += le32_to_cpu(map->pg_pool[i].v.num_removed_snap_intervals) + * sizeof(u64) * 2; + } + + ceph_decode_32_safe(p, end, map->flags, bad); + + ceph_decode_32(p, max); + + /* (re)alloc osd arrays */ + err = osdmap_set_max_osd(map, max); + if (err < 0) + goto bad; + dout("osdmap_decode max_osd = %d\n", map->max_osd); + + /* osds */ + err = -EINVAL; + ceph_decode_need(p, end, 3*sizeof(u32) + + map->max_osd*(1 + sizeof(*map->osd_weight) + + sizeof(*map->osd_addr)), bad); + *p += 4; /* skip length field (should match max) */ + ceph_decode_copy(p, map->osd_state, map->max_osd); + + *p += 4; /* skip length field (should match max) */ + for (i = 0; i < map->max_osd; i++) + ceph_decode_32(p, map->osd_weight[i]); + + *p += 4; /* skip length field (should match max) */ + ceph_decode_copy(p, map->osd_addr, map->max_osd*sizeof(*map->osd_addr)); + + /* pg_temp */ + ceph_decode_32_safe(p, end, len, bad); + for (i = 0; i < len; i++) { + int n, j; + u64 pgid; + struct ceph_pg_mapping *pg; + + ceph_decode_need(p, end, sizeof(u32) + sizeof(u64), bad); + ceph_decode_64(p, pgid); + ceph_decode_32(p, n); + ceph_decode_need(p, end, n * sizeof(u32), bad); + pg = kmalloc(sizeof(*pg) + n*sizeof(u32), GFP_NOFS); + if (!pg) { + err = -ENOMEM; + goto bad; + } + pg->pgid = pgid; + pg->len = n; + for (j = 0; j < n; j++) + ceph_decode_32(p, pg->osds[j]); + + __insert_pg_mapping(pg, &map->pg_temp); + dout(" added pg_temp %llx len %d\n", pgid, len); + } + + /* crush */ + ceph_decode_32_safe(p, end, len, bad); + dout("osdmap_decode crush len %d from off 0x%x\n", len, + (int)(*p - start)); + ceph_decode_need(p, end, len, bad); + map->crush = crush_decode(*p, end); + *p += len; + if (IS_ERR(map->crush)) { + err = PTR_ERR(map->crush); + map->crush = NULL; + goto bad; + } + + /* ignore the rest of the map */ + *p = end; + + dout("osdmap_decode done %p %p\n", *p, end); + return map; + +bad: + dout("osdmap_decode fail\n"); + ceph_osdmap_destroy(map); + return ERR_PTR(err); +} + +/* + * decode and apply an incremental map update. + */ +struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, + struct ceph_osdmap *map, + struct ceph_messenger *msgr) +{ + struct ceph_osdmap *newmap = map; + struct crush_map *newcrush = NULL; + struct ceph_fsid fsid; + u32 epoch = 0; + struct ceph_timespec modified; + u32 len, pool; + __s32 new_flags, max; + void *start = *p; + int err = -EINVAL; + u16 version; + struct rb_node *rbp; + + ceph_decode_16_safe(p, end, version, bad); + + ceph_decode_need(p, end, sizeof(fsid)+sizeof(modified)+2*sizeof(u32), + bad); + ceph_decode_copy(p, &fsid, sizeof(fsid)); + ceph_decode_32(p, epoch); + BUG_ON(epoch != map->epoch+1); + ceph_decode_copy(p, &modified, sizeof(modified)); + ceph_decode_32(p, new_flags); + + /* full map? */ + ceph_decode_32_safe(p, end, len, bad); + if (len > 0) { + dout("apply_incremental full map len %d, %p to %p\n", + len, *p, end); + newmap = osdmap_decode(p, min(*p+len, end)); + return newmap; /* error or not */ + } + + /* new crush? */ + ceph_decode_32_safe(p, end, len, bad); + if (len > 0) { + dout("apply_incremental new crush map len %d, %p to %p\n", + len, *p, end); + newcrush = crush_decode(*p, min(*p+len, end)); + if (IS_ERR(newcrush)) + return ERR_PTR(PTR_ERR(newcrush)); + } + + /* new flags? */ + if (new_flags >= 0) + map->flags = new_flags; + + ceph_decode_need(p, end, 5*sizeof(u32), bad); + + /* new max? */ + ceph_decode_32(p, max); + if (max >= 0) { + err = osdmap_set_max_osd(map, max); + if (err < 0) + goto bad; + } + + map->epoch++; + map->modified = map->modified; + if (newcrush) { + if (map->crush) + crush_destroy(map->crush); + map->crush = newcrush; + newcrush = NULL; + } + + /* new_pool */ + ceph_decode_32_safe(p, end, len, bad); + while (len--) { + ceph_decode_32_safe(p, end, pool, bad); + if (pool >= map->num_pools) { + void *pg_pool = kcalloc(pool + 1, + sizeof(*map->pg_pool), + GFP_NOFS); + if (!pg_pool) { + err = -ENOMEM; + goto bad; + } + memcpy(pg_pool, map->pg_pool, + map->num_pools * sizeof(*map->pg_pool)); + kfree(map->pg_pool); + map->pg_pool = pg_pool; + map->num_pools = pool+1; + } + ceph_decode_copy(p, &map->pg_pool[pool].v, + sizeof(map->pg_pool->v)); + calc_pg_masks(&map->pg_pool[pool]); + } + + /* old_pool (ignore) */ + ceph_decode_32_safe(p, end, len, bad); + *p += len * sizeof(u32); + + /* new_up */ + err = -EINVAL; + ceph_decode_32_safe(p, end, len, bad); + while (len--) { + u32 osd; + struct ceph_entity_addr addr; + ceph_decode_32_safe(p, end, osd, bad); + ceph_decode_copy_safe(p, end, &addr, sizeof(addr), bad); + pr_info("osd%d up\n", osd); + BUG_ON(osd >= map->max_osd); + map->osd_state[osd] |= CEPH_OSD_UP; + map->osd_addr[osd] = addr; + } + + /* new_down */ + ceph_decode_32_safe(p, end, len, bad); + while (len--) { + u32 osd; + ceph_decode_32_safe(p, end, osd, bad); + (*p)++; /* clean flag */ + pr_info("ceph osd%d down\n", osd); + if (osd < map->max_osd) + map->osd_state[osd] &= ~CEPH_OSD_UP; + } + + /* new_weight */ + ceph_decode_32_safe(p, end, len, bad); + while (len--) { + u32 osd, off; + ceph_decode_need(p, end, sizeof(u32)*2, bad); + ceph_decode_32(p, osd); + ceph_decode_32(p, off); + pr_info("osd%d weight 0x%x %s\n", osd, off, + off == CEPH_OSD_IN ? "(in)" : + (off == CEPH_OSD_OUT ? "(out)" : "")); + if (osd < map->max_osd) + map->osd_weight[osd] = off; + } + + /* new_pg_temp */ + rbp = rb_first(&map->pg_temp); + ceph_decode_32_safe(p, end, len, bad); + while (len--) { + struct ceph_pg_mapping *pg; + int j; + u64 pgid; + u32 pglen; + ceph_decode_need(p, end, sizeof(u64) + sizeof(u32), bad); + ceph_decode_64(p, pgid); + ceph_decode_32(p, pglen); + + /* remove any? */ + while (rbp && rb_entry(rbp, struct ceph_pg_mapping, + node)->pgid <= pgid) { + struct rb_node *cur = rbp; + rbp = rb_next(rbp); + dout(" removed pg_temp %llx\n", + rb_entry(cur, struct ceph_pg_mapping, node)->pgid); + rb_erase(cur, &map->pg_temp); + } + + if (pglen) { + /* insert */ + ceph_decode_need(p, end, pglen*sizeof(u32), bad); + pg = kmalloc(sizeof(*pg) + sizeof(u32)*pglen, GFP_NOFS); + if (!pg) { + err = -ENOMEM; + goto bad; + } + pg->pgid = pgid; + pg->len = pglen; + for (j = 0; j < len; j++) + ceph_decode_32(p, pg->osds[j]); + __insert_pg_mapping(pg, &map->pg_temp); + dout(" added pg_temp %llx len %d\n", pgid, pglen); + } + } + while (rbp) { + struct rb_node *cur = rbp; + rbp = rb_next(rbp); + dout(" removed pg_temp %llx\n", + rb_entry(cur, struct ceph_pg_mapping, node)->pgid); + rb_erase(cur, &map->pg_temp); + } + + /* ignore the rest */ + *p = end; + return map; + +bad: + pr_err("corrupt inc osdmap epoch %d off %d (%p of %p-%p)\n", + epoch, (int)(*p - start), *p, start, end); + if (newcrush) + crush_destroy(newcrush); + return ERR_PTR(err); +} + + + + +/* + * calculate file layout from given offset, length. + * fill in correct oid, logical length, and object extent + * offset, length. + * + * for now, we write only a single su, until we can + * pass a stride back to the caller. + */ +void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, + u64 off, u64 *plen, + u64 *bno, + u64 *oxoff, u64 *oxlen) +{ + u32 osize = le32_to_cpu(layout->fl_object_size); + u32 su = le32_to_cpu(layout->fl_stripe_unit); + u32 sc = le32_to_cpu(layout->fl_stripe_count); + u32 bl, stripeno, stripepos, objsetno; + u32 su_per_object; + u64 t; + + dout("mapping %llu~%llu osize %u fl_su %u\n", off, *plen, + osize, su); + su_per_object = osize / le32_to_cpu(layout->fl_stripe_unit); + dout("osize %u / su %u = su_per_object %u\n", osize, su, + su_per_object); + + BUG_ON((su & ~PAGE_MASK) != 0); + /* bl = *off / su; */ + t = off; + do_div(t, su); + bl = t; + dout("off %llu / su %u = bl %u\n", off, su, bl); + + stripeno = bl / sc; + stripepos = bl % sc; + objsetno = stripeno / su_per_object; + + *bno = objsetno * sc + stripepos; + dout("objset %u * sc %u = bno %u\n", objsetno, sc, (unsigned)*bno); + /* *oxoff = *off / layout->fl_stripe_unit; */ + t = off; + *oxoff = do_div(t, su); + *oxlen = min_t(u64, *plen, su - *oxoff); + *plen = *oxlen; + + dout(" obj extent %llu~%llu\n", *oxoff, *oxlen); +} + +/* + * calculate an object layout (i.e. pgid) from an oid, + * file_layout, and osdmap + */ +int ceph_calc_object_layout(struct ceph_object_layout *ol, + const char *oid, + struct ceph_file_layout *fl, + struct ceph_osdmap *osdmap) +{ + unsigned num, num_mask; + union ceph_pg pgid; + s32 preferred = (s32)le32_to_cpu(fl->fl_pg_preferred); + int poolid = le32_to_cpu(fl->fl_pg_pool); + struct ceph_pg_pool_info *pool; + + if (poolid >= osdmap->num_pools) + return -EIO; + pool = &osdmap->pg_pool[poolid]; + + if (preferred >= 0) { + num = le32_to_cpu(pool->v.lpg_num); + num_mask = pool->lpg_num_mask; + } else { + num = le32_to_cpu(pool->v.pg_num); + num_mask = pool->pg_num_mask; + } + + pgid.pg64 = 0; /* start with it zeroed out */ + pgid.pg.ps = ceph_full_name_hash(oid, strlen(oid)); + pgid.pg.preferred = preferred; + pgid.pg.pool = le32_to_cpu(fl->fl_pg_pool); + if (preferred >= 0) + dout("calc_object_layout '%s' pgid %d.%xp%d (%llx)\n", oid, + pgid.pg.pool, pgid.pg.ps, (int)preferred, pgid.pg64); + else + dout("calc_object_layout '%s' pgid %d.%x (%llx)\n", oid, + pgid.pg.pool, pgid.pg.ps, pgid.pg64); + + ol->ol_pgid = cpu_to_le64(pgid.pg64); + ol->ol_stripe_unit = fl->fl_object_stripe_unit; + + return 0; +} + +/* + * Calculate raw osd vector for the given pgid. Return pointer to osd + * array, or NULL on failure. + */ +static int *calc_pg_raw(struct ceph_osdmap *osdmap, union ceph_pg pgid, + int *osds, int *num) +{ + struct rb_node *n = osdmap->pg_temp.rb_node; + struct ceph_pg_mapping *pg; + struct ceph_pg_pool_info *pool; + int ruleno; + unsigned pps; /* placement ps */ + + /* pg_temp? */ + while (n) { + pg = rb_entry(n, struct ceph_pg_mapping, node); + if (pgid.pg64 < pg->pgid) + n = n->rb_left; + else if (pgid.pg64 > pg->pgid) + n = n->rb_right; + else { + *num = pg->len; + return pg->osds; + } + } + + /* crush */ + if (pgid.pg.pool >= osdmap->num_pools) + return NULL; + pool = &osdmap->pg_pool[pgid.pg.pool]; + ruleno = crush_find_rule(osdmap->crush, pool->v.crush_ruleset, + pool->v.type, pool->v.size); + if (ruleno < 0) { + pr_err("no crush rule pool %d type %d size %d\n", + pgid.pg.pool, pool->v.type, pool->v.size); + return NULL; + } + + if (pgid.pg.preferred >= 0) + pps = ceph_stable_mod(pgid.pg.ps, + le32_to_cpu(pool->v.lpgp_num), + pool->lpgp_num_mask); + else + pps = ceph_stable_mod(pgid.pg.ps, + le32_to_cpu(pool->v.pgp_num), + pool->pgp_num_mask); + pps += pgid.pg.pool; + *num = crush_do_rule(osdmap->crush, ruleno, pps, osds, + min_t(int, pool->v.size, *num), + pgid.pg.preferred, osdmap->osd_weight); + return osds; +} + +/* + * Return primary osd for given pgid, or -1 if none. + */ +int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, union ceph_pg pgid) +{ + int rawosds[10], *osds; + int i, num = ARRAY_SIZE(rawosds); + + osds = calc_pg_raw(osdmap, pgid, rawosds, &num); + if (!osds) + return -1; + + /* primary is first up osd */ + for (i = 0; i < num; i++) + if (ceph_osd_is_up(osdmap, osds[i])) { + return osds[i]; + break; + } + return -1; +} diff --git a/fs/ceph/osdmap.h b/fs/ceph/osdmap.h new file mode 100644 index 0000000..07127c6 --- /dev/null +++ b/fs/ceph/osdmap.h @@ -0,0 +1,123 @@ +#ifndef _FS_CEPH_OSDMAP_H +#define _FS_CEPH_OSDMAP_H + +#include +#include "types.h" +#include "ceph_fs.h" +#include "crush/crush.h" + +/* + * The osd map describes the current membership of the osd cluster and + * specifies the mapping of objects to placement groups and placement + * groups to (sets of) osds. That is, it completely specifies the + * (desired) distribution of all data objects in the system at some + * point in time. + * + * Each map version is identified by an epoch, which increases monotonically. + * + * The map can be updated either via an incremental map (diff) describing + * the change between two successive epochs, or as a fully encoded map. + */ +struct ceph_pg_pool_info { + struct ceph_pg_pool v; + int pg_num_mask, pgp_num_mask, lpg_num_mask, lpgp_num_mask; +}; + +struct ceph_pg_mapping { + struct rb_node node; + u64 pgid; + int len; + int osds[]; +}; + +struct ceph_osdmap { + struct ceph_fsid fsid; + u32 epoch; + u32 mkfs_epoch; + struct ceph_timespec created, modified; + + u32 flags; /* CEPH_OSDMAP_* */ + + u32 max_osd; /* size of osd_state, _offload, _addr arrays */ + u8 *osd_state; /* CEPH_OSD_* */ + u32 *osd_weight; /* 0 = failed, 0x10000 = 100% normal */ + struct ceph_entity_addr *osd_addr; + + struct rb_root pg_temp; + + u32 num_pools; + struct ceph_pg_pool_info *pg_pool; + + /* the CRUSH map specifies the mapping of placement groups to + * the list of osds that store+replicate them. */ + struct crush_map *crush; +}; + +/* + * file layout helpers + */ +#define ceph_file_layout_su(l) ((__s32)le32_to_cpu((l).fl_stripe_unit)) +#define ceph_file_layout_stripe_count(l) \ + ((__s32)le32_to_cpu((l).fl_stripe_count)) +#define ceph_file_layout_object_size(l) ((__s32)le32_to_cpu((l).fl_object_size)) +#define ceph_file_layout_cas_hash(l) ((__s32)le32_to_cpu((l).fl_cas_hash)) +#define ceph_file_layout_object_su(l) \ + ((__s32)le32_to_cpu((l).fl_object_stripe_unit)) +#define ceph_file_layout_pg_preferred(l) \ + ((__s32)le32_to_cpu((l).fl_pg_preferred)) +#define ceph_file_layout_pg_pool(l) \ + ((__s32)le32_to_cpu((l).fl_pg_pool)) + +static inline unsigned ceph_file_layout_stripe_width(struct ceph_file_layout *l) +{ + return le32_to_cpu(l->fl_stripe_unit) * + le32_to_cpu(l->fl_stripe_count); +} + +/* "period" == bytes before i start on a new set of objects */ +static inline unsigned ceph_file_layout_period(struct ceph_file_layout *l) +{ + return le32_to_cpu(l->fl_object_size) * + le32_to_cpu(l->fl_stripe_count); +} + + +static inline int ceph_osd_is_up(struct ceph_osdmap *map, int osd) +{ + return (osd < map->max_osd) && (map->osd_state[osd] & CEPH_OSD_UP); +} + +static inline bool ceph_osdmap_flag(struct ceph_osdmap *map, int flag) +{ + return map && (map->flags & flag); +} + +extern char *ceph_osdmap_state_str(char *str, int len, int state); + +static inline struct ceph_entity_addr *ceph_osd_addr(struct ceph_osdmap *map, + int osd) +{ + if (osd >= map->max_osd) + return NULL; + return &map->osd_addr[osd]; +} + +extern struct ceph_osdmap *osdmap_decode(void **p, void *end); +extern struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, + struct ceph_osdmap *map, + struct ceph_messenger *msgr); +extern void ceph_osdmap_destroy(struct ceph_osdmap *map); + +/* calculate mapping of a file extent to an object */ +extern void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, + u64 off, u64 *plen, + u64 *bno, u64 *oxoff, u64 *oxlen); + +/* calculate mapping of object to a placement group */ +extern int ceph_calc_object_layout(struct ceph_object_layout *ol, + const char *oid, + struct ceph_file_layout *fl, + struct ceph_osdmap *osdmap); +extern int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, union ceph_pg pgid); + +#endif -- cgit v0.10.2 From 5ecc0a0f8128b1876e8614638deaed49cc8b174c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:11 -0700 Subject: ceph: CRUSH mapping algorithm CRUSH is a pseudorandom data distribution function designed to map inputs onto a dynamic hierarchy of devices, while minimizing the extent to which inputs are remapped when the devices are added or removed. It includes some features that are specifically useful for storage, most notably the ability to map each input onto a set of N devices that are separated across administrator-defined failure domains. CRUSH is used to distribute data across the cluster of Ceph storage nodes. More information about CRUSH can be found in this paper: http://www.ssrc.ucsc.edu/Papers/weil-sc06.pdf Signed-off-by: Sage Weil diff --git a/fs/ceph/crush/crush.c b/fs/ceph/crush/crush.c new file mode 100644 index 0000000..13755cd --- /dev/null +++ b/fs/ceph/crush/crush.c @@ -0,0 +1,140 @@ + +#ifdef __KERNEL__ +# include +#else +# include +# include +# define kfree(x) do { if (x) free(x); } while (0) +# define BUG_ON(x) assert(!(x)) +#endif + +#include "crush.h" + +/** + * crush_get_bucket_item_weight - Get weight of an item in given bucket + * @b: bucket pointer + * @p: item index in bucket + */ +int crush_get_bucket_item_weight(struct crush_bucket *b, int p) +{ + if (p >= b->size) + return 0; + + switch (b->alg) { + case CRUSH_BUCKET_UNIFORM: + return ((struct crush_bucket_uniform *)b)->item_weight; + case CRUSH_BUCKET_LIST: + return ((struct crush_bucket_list *)b)->item_weights[p]; + case CRUSH_BUCKET_TREE: + if (p & 1) + return ((struct crush_bucket_tree *)b)->node_weights[p]; + return 0; + case CRUSH_BUCKET_STRAW: + return ((struct crush_bucket_straw *)b)->item_weights[p]; + } + return 0; +} + +/** + * crush_calc_parents - Calculate parent vectors for the given crush map. + * @map: crush_map pointer + */ +void crush_calc_parents(struct crush_map *map) +{ + int i, b, c; + + for (b = 0; b < map->max_buckets; b++) { + if (map->buckets[b] == NULL) + continue; + for (i = 0; i < map->buckets[b]->size; i++) { + c = map->buckets[b]->items[i]; + BUG_ON(c >= map->max_devices || + c < -map->max_buckets); + if (c >= 0) + map->device_parents[c] = map->buckets[b]->id; + else + map->bucket_parents[-1-c] = map->buckets[b]->id; + } + } +} + +void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b) +{ + kfree(b->h.perm); + kfree(b->h.items); + kfree(b); +} + +void crush_destroy_bucket_list(struct crush_bucket_list *b) +{ + kfree(b->item_weights); + kfree(b->sum_weights); + kfree(b->h.perm); + kfree(b->h.items); + kfree(b); +} + +void crush_destroy_bucket_tree(struct crush_bucket_tree *b) +{ + kfree(b->node_weights); + kfree(b); +} + +void crush_destroy_bucket_straw(struct crush_bucket_straw *b) +{ + kfree(b->straws); + kfree(b->item_weights); + kfree(b->h.perm); + kfree(b->h.items); + kfree(b); +} + +void crush_destroy_bucket(struct crush_bucket *b) +{ + switch (b->alg) { + case CRUSH_BUCKET_UNIFORM: + crush_destroy_bucket_uniform((struct crush_bucket_uniform *)b); + break; + case CRUSH_BUCKET_LIST: + crush_destroy_bucket_list((struct crush_bucket_list *)b); + break; + case CRUSH_BUCKET_TREE: + crush_destroy_bucket_tree((struct crush_bucket_tree *)b); + break; + case CRUSH_BUCKET_STRAW: + crush_destroy_bucket_straw((struct crush_bucket_straw *)b); + break; + } +} + +/** + * crush_destroy - Destroy a crush_map + * @map: crush_map pointer + */ +void crush_destroy(struct crush_map *map) +{ + int b; + + /* buckets */ + if (map->buckets) { + for (b = 0; b < map->max_buckets; b++) { + if (map->buckets[b] == NULL) + continue; + crush_destroy_bucket(map->buckets[b]); + } + kfree(map->buckets); + } + + /* rules */ + if (map->rules) { + for (b = 0; b < map->max_rules; b++) + kfree(map->rules[b]); + kfree(map->rules); + } + + kfree(map->bucket_parents); + kfree(map->device_parents); + kfree(map); +} + + diff --git a/fs/ceph/crush/crush.h b/fs/ceph/crush/crush.h new file mode 100644 index 0000000..9ac7e09 --- /dev/null +++ b/fs/ceph/crush/crush.h @@ -0,0 +1,188 @@ +#ifndef _CRUSH_CRUSH_H +#define _CRUSH_CRUSH_H + +#include + +/* + * CRUSH is a pseudo-random data distribution algorithm that + * efficiently distributes input values (typically, data objects) + * across a heterogeneous, structured storage cluster. + * + * The algorithm was originally described in detail in this paper + * (although the algorithm has evolved somewhat since then): + * + * http://www.ssrc.ucsc.edu/Papers/weil-sc06.pdf + * + * LGPL2 + */ + + +#define CRUSH_MAGIC 0x00010000ul /* for detecting algorithm revisions */ + + +#define CRUSH_MAX_DEPTH 10 /* max crush hierarchy depth */ +#define CRUSH_MAX_SET 10 /* max size of a mapping result */ + + +/* + * CRUSH uses user-defined "rules" to describe how inputs should be + * mapped to devices. A rule consists of sequence of steps to perform + * to generate the set of output devices. + */ +struct crush_rule_step { + __u32 op; + __s32 arg1; + __s32 arg2; +}; + +/* step op codes */ +enum { + CRUSH_RULE_NOOP = 0, + CRUSH_RULE_TAKE = 1, /* arg1 = value to start with */ + CRUSH_RULE_CHOOSE_FIRSTN = 2, /* arg1 = num items to pick */ + /* arg2 = type */ + CRUSH_RULE_CHOOSE_INDEP = 3, /* same */ + CRUSH_RULE_EMIT = 4, /* no args */ + CRUSH_RULE_CHOOSE_LEAF_FIRSTN = 6, + CRUSH_RULE_CHOOSE_LEAF_INDEP = 7, +}; + +/* + * for specifying choose num (arg1) relative to the max parameter + * passed to do_rule + */ +#define CRUSH_CHOOSE_N 0 +#define CRUSH_CHOOSE_N_MINUS(x) (-(x)) + +/* + * The rule mask is used to describe what the rule is intended for. + * Given a ruleset and size of output set, we search through the + * rule list for a matching rule_mask. + */ +struct crush_rule_mask { + __u8 ruleset; + __u8 type; + __u8 min_size; + __u8 max_size; +}; + +struct crush_rule { + __u32 len; + struct crush_rule_mask mask; + struct crush_rule_step steps[0]; +}; + +#define crush_rule_size(len) (sizeof(struct crush_rule) + \ + (len)*sizeof(struct crush_rule_step)) + + + +/* + * A bucket is a named container of other items (either devices or + * other buckets). Items within a bucket are chosen using one of a + * few different algorithms. The table summarizes how the speed of + * each option measures up against mapping stability when items are + * added or removed. + * + * Bucket Alg Speed Additions Removals + * ------------------------------------------------ + * uniform O(1) poor poor + * list O(n) optimal poor + * tree O(log n) good good + * straw O(n) optimal optimal + */ +enum { + CRUSH_BUCKET_UNIFORM = 1, + CRUSH_BUCKET_LIST = 2, + CRUSH_BUCKET_TREE = 3, + CRUSH_BUCKET_STRAW = 4 +}; +static inline const char *crush_bucket_alg_name(int alg) +{ + switch (alg) { + case CRUSH_BUCKET_UNIFORM: return "uniform"; + case CRUSH_BUCKET_LIST: return "list"; + case CRUSH_BUCKET_TREE: return "tree"; + case CRUSH_BUCKET_STRAW: return "straw"; + default: return "unknown"; + } +} + +struct crush_bucket { + __s32 id; /* this'll be negative */ + __u16 type; /* non-zero; type=0 is reserved for devices */ + __u16 alg; /* one of CRUSH_BUCKET_* */ + __u32 weight; /* 16-bit fixed point */ + __u32 size; /* num items */ + __s32 *items; + + /* + * cached random permutation: used for uniform bucket and for + * the linear search fallback for the other bucket types. + */ + __u32 perm_x; /* @x for which *perm is defined */ + __u32 perm_n; /* num elements of *perm that are permuted/defined */ + __u32 *perm; +}; + +struct crush_bucket_uniform { + struct crush_bucket h; + __u32 item_weight; /* 16-bit fixed point; all items equally weighted */ +}; + +struct crush_bucket_list { + struct crush_bucket h; + __u32 *item_weights; /* 16-bit fixed point */ + __u32 *sum_weights; /* 16-bit fixed point. element i is sum + of weights 0..i, inclusive */ +}; + +struct crush_bucket_tree { + struct crush_bucket h; /* note: h.size is _tree_ size, not number of + actual items */ + __u8 num_nodes; + __u32 *node_weights; +}; + +struct crush_bucket_straw { + struct crush_bucket h; + __u32 *item_weights; /* 16-bit fixed point */ + __u32 *straws; /* 16-bit fixed point */ +}; + + + +/* + * CRUSH map includes all buckets, rules, etc. + */ +struct crush_map { + struct crush_bucket **buckets; + struct crush_rule **rules; + + /* + * Parent pointers to identify the parent bucket a device or + * bucket in the hierarchy. If an item appears more than + * once, this is the _last_ time it appeared (where buckets + * are processed in bucket id order, from -1 on down to + * -max_buckets. + */ + __u32 *bucket_parents; + __u32 *device_parents; + + __s32 max_buckets; + __u32 max_rules; + __s32 max_devices; +}; + + +/* crush.c */ +extern int crush_get_bucket_item_weight(struct crush_bucket *b, int pos); +extern void crush_calc_parents(struct crush_map *map); +extern void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b); +extern void crush_destroy_bucket_list(struct crush_bucket_list *b); +extern void crush_destroy_bucket_tree(struct crush_bucket_tree *b); +extern void crush_destroy_bucket_straw(struct crush_bucket_straw *b); +extern void crush_destroy_bucket(struct crush_bucket *b); +extern void crush_destroy(struct crush_map *map); + +#endif diff --git a/fs/ceph/crush/hash.h b/fs/ceph/crush/hash.h new file mode 100644 index 0000000..42f3312 --- /dev/null +++ b/fs/ceph/crush/hash.h @@ -0,0 +1,90 @@ +#ifndef _CRUSH_HASH_H +#define _CRUSH_HASH_H + +/* + * Robert Jenkins' function for mixing 32-bit values + * http://burtleburtle.net/bob/hash/evahash.html + * a, b = random bits, c = input and output + */ +#define crush_hashmix(a, b, c) do { \ + a = a-b; a = a-c; a = a^(c>>13); \ + b = b-c; b = b-a; b = b^(a<<8); \ + c = c-a; c = c-b; c = c^(b>>13); \ + a = a-b; a = a-c; a = a^(c>>12); \ + b = b-c; b = b-a; b = b^(a<<16); \ + c = c-a; c = c-b; c = c^(b>>5); \ + a = a-b; a = a-c; a = a^(c>>3); \ + b = b-c; b = b-a; b = b^(a<<10); \ + c = c-a; c = c-b; c = c^(b>>15); \ + } while (0) + +#define crush_hash_seed 1315423911 + +static inline __u32 crush_hash32(__u32 a) +{ + __u32 hash = crush_hash_seed ^ a; + __u32 b = a; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(b, x, hash); + crush_hashmix(y, a, hash); + return hash; +} + +static inline __u32 crush_hash32_2(__u32 a, __u32 b) +{ + __u32 hash = crush_hash_seed ^ a ^ b; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(a, b, hash); + crush_hashmix(x, a, hash); + crush_hashmix(b, y, hash); + return hash; +} + +static inline __u32 crush_hash32_3(__u32 a, __u32 b, __u32 c) +{ + __u32 hash = crush_hash_seed ^ a ^ b ^ c; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(a, b, hash); + crush_hashmix(c, x, hash); + crush_hashmix(y, a, hash); + crush_hashmix(b, x, hash); + crush_hashmix(y, c, hash); + return hash; +} + +static inline __u32 crush_hash32_4(__u32 a, __u32 b, __u32 c, + __u32 d) +{ + __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(a, b, hash); + crush_hashmix(c, d, hash); + crush_hashmix(a, x, hash); + crush_hashmix(y, b, hash); + crush_hashmix(c, x, hash); + crush_hashmix(y, d, hash); + return hash; +} + +static inline __u32 crush_hash32_5(__u32 a, __u32 b, __u32 c, + __u32 d, __u32 e) +{ + __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d ^ e; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(a, b, hash); + crush_hashmix(c, d, hash); + crush_hashmix(e, x, hash); + crush_hashmix(y, a, hash); + crush_hashmix(b, x, hash); + crush_hashmix(y, c, hash); + crush_hashmix(d, x, hash); + crush_hashmix(y, e, hash); + return hash; +} + +#endif diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c new file mode 100644 index 0000000..0f0730c --- /dev/null +++ b/fs/ceph/crush/mapper.c @@ -0,0 +1,589 @@ + +#ifdef __KERNEL__ +# include +# include +# include +# include +# ifndef dprintk +# define dprintk(args...) +# endif +#else +# include +# include +# include +# include +# define BUG_ON(x) assert(!(x)) +# define dprintk(args...) /* printf(args) */ +# define kmalloc(x, f) malloc(x) +# define kfree(x) free(x) +#endif + +#include "crush.h" +#include "hash.h" + +/* + * Implement the core CRUSH mapping algorithm. + */ + +/** + * crush_find_rule - find a crush_rule id for a given ruleset, type, and size. + * @map: the crush_map + * @ruleset: the storage ruleset id (user defined) + * @type: storage ruleset type (user defined) + * @size: output set size + */ +int crush_find_rule(struct crush_map *map, int ruleset, int type, int size) +{ + int i; + + for (i = 0; i < map->max_rules; i++) { + if (map->rules[i] && + map->rules[i]->mask.ruleset == ruleset && + map->rules[i]->mask.type == type && + map->rules[i]->mask.min_size <= size && + map->rules[i]->mask.max_size >= size) + return i; + } + return -1; +} + + +/* + * bucket choose methods + * + * For each bucket algorithm, we have a "choose" method that, given a + * crush input @x and replica position (usually, position in output set) @r, + * will produce an item in the bucket. + */ + +/* + * Choose based on a random permutation of the bucket. + * + * We used to use some prime number arithmetic to do this, but it + * wasn't very random, and had some other bad behaviors. Instead, we + * calculate an actual random permutation of the bucket members. + * Since this is expensive, we optimize for the r=0 case, which + * captures the vast majority of calls. + */ +static int bucket_perm_choose(struct crush_bucket *bucket, + int x, int r) +{ + unsigned pr = r % bucket->size; + unsigned i, s; + + /* start a new permutation if @x has changed */ + if (bucket->perm_x != x || bucket->perm_n == 0) { + dprintk("bucket %d new x=%d\n", bucket->id, x); + bucket->perm_x = x; + + /* optimize common r=0 case */ + if (pr == 0) { + s = crush_hash32_3(x, bucket->id, 0) % + bucket->size; + bucket->perm[0] = s; + bucket->perm_n = 0xffff; /* magic value, see below */ + goto out; + } + + for (i = 0; i < bucket->size; i++) + bucket->perm[i] = i; + bucket->perm_n = 0; + } else if (bucket->perm_n == 0xffff) { + /* clean up after the r=0 case above */ + for (i = 1; i < bucket->size; i++) + bucket->perm[i] = i; + bucket->perm[bucket->perm[0]] = 0; + bucket->perm_n = 1; + } + + /* calculate permutation up to pr */ + for (i = 0; i < bucket->perm_n; i++) + dprintk(" perm_choose have %d: %d\n", i, bucket->perm[i]); + while (bucket->perm_n <= pr) { + unsigned p = bucket->perm_n; + /* no point in swapping the final entry */ + if (p < bucket->size - 1) { + i = crush_hash32_3(x, bucket->id, p) % + (bucket->size - p); + if (i) { + unsigned t = bucket->perm[p + i]; + bucket->perm[p + i] = bucket->perm[p]; + bucket->perm[p] = t; + } + dprintk(" perm_choose swap %d with %d\n", p, p+i); + } + bucket->perm_n++; + } + for (i = 0; i < bucket->size; i++) + dprintk(" perm_choose %d: %d\n", i, bucket->perm[i]); + + s = bucket->perm[pr]; +out: + dprintk(" perm_choose %d sz=%d x=%d r=%d (%d) s=%d\n", bucket->id, + bucket->size, x, r, pr, s); + return bucket->items[s]; +} + +/* uniform */ +static int bucket_uniform_choose(struct crush_bucket_uniform *bucket, + int x, int r) +{ + return bucket_perm_choose(&bucket->h, x, r); +} + +/* list */ +static int bucket_list_choose(struct crush_bucket_list *bucket, + int x, int r) +{ + int i; + + for (i = bucket->h.size-1; i >= 0; i--) { + __u64 w = crush_hash32_4(x, bucket->h.items[i], r, + bucket->h.id); + w &= 0xffff; + dprintk("list_choose i=%d x=%d r=%d item %d weight %x " + "sw %x rand %llx", + i, x, r, bucket->h.items[i], bucket->item_weights[i], + bucket->sum_weights[i], w); + w *= bucket->sum_weights[i]; + w = w >> 16; + /*dprintk(" scaled %llx\n", w);*/ + if (w < bucket->item_weights[i]) + return bucket->h.items[i]; + } + + BUG_ON(1); + return 0; +} + + +/* (binary) tree */ +static int height(int n) +{ + int h = 0; + while ((n & 1) == 0) { + h++; + n = n >> 1; + } + return h; +} + +static int left(int x) +{ + int h = height(x); + return x - (1 << (h-1)); +} + +static int right(int x) +{ + int h = height(x); + return x + (1 << (h-1)); +} + +static int terminal(int x) +{ + return x & 1; +} + +static int bucket_tree_choose(struct crush_bucket_tree *bucket, + int x, int r) +{ + int n, l; + __u32 w; + __u64 t; + + /* start at root */ + n = bucket->num_nodes >> 1; + + while (!terminal(n)) { + /* pick point in [0, w) */ + w = bucket->node_weights[n]; + t = (__u64)crush_hash32_4(x, n, r, bucket->h.id) * (__u64)w; + t = t >> 32; + + /* descend to the left or right? */ + l = left(n); + if (t < bucket->node_weights[l]) + n = l; + else + n = right(n); + } + + return bucket->h.items[n >> 1]; +} + + +/* straw */ + +static int bucket_straw_choose(struct crush_bucket_straw *bucket, + int x, int r) +{ + int i; + int high = 0; + __u64 high_draw = 0; + __u64 draw; + + for (i = 0; i < bucket->h.size; i++) { + draw = crush_hash32_3(x, bucket->h.items[i], r); + draw &= 0xffff; + draw *= bucket->straws[i]; + if (i == 0 || draw > high_draw) { + high = i; + high_draw = draw; + } + } + return bucket->h.items[high]; +} + +static int crush_bucket_choose(struct crush_bucket *in, int x, int r) +{ + dprintk("choose %d x=%d r=%d\n", in->id, x, r); + switch (in->alg) { + case CRUSH_BUCKET_UNIFORM: + return bucket_uniform_choose((struct crush_bucket_uniform *)in, + x, r); + case CRUSH_BUCKET_LIST: + return bucket_list_choose((struct crush_bucket_list *)in, + x, r); + case CRUSH_BUCKET_TREE: + return bucket_tree_choose((struct crush_bucket_tree *)in, + x, r); + case CRUSH_BUCKET_STRAW: + return bucket_straw_choose((struct crush_bucket_straw *)in, + x, r); + default: + BUG_ON(1); +/* return in->items[0] */; + } +} + +/* + * true if device is marked "out" (failed, fully offloaded) + * of the cluster + */ +static int is_out(struct crush_map *map, __u32 *weight, int item, int x) +{ + if (weight[item] >= 0x1000) + return 0; + if (weight[item] == 0) + return 1; + if ((crush_hash32_2(x, item) & 0xffff) < weight[item]) + return 0; + return 1; +} + +/** + * crush_choose - choose numrep distinct items of given type + * @map: the crush_map + * @bucket: the bucket we are choose an item from + * @x: crush input value + * @numrep: the number of items to choose + * @type: the type of item to choose + * @out: pointer to output vector + * @outpos: our position in that vector + * @firstn: true if choosing "first n" items, false if choosing "indep" + * @recurse_to_leaf: true if we want one device under each item of given type + * @out2: second output vector for leaf items (if @recurse_to_leaf) + */ +static int crush_choose(struct crush_map *map, + struct crush_bucket *bucket, + __u32 *weight, + int x, int numrep, int type, + int *out, int outpos, + int firstn, int recurse_to_leaf, + int *out2) +{ + int rep; + int ftotal, flocal; + int retry_descent, retry_bucket, skip_rep; + struct crush_bucket *in = bucket; + int r; + int i; + int item; + int itemtype; + int collide, reject; + const int orig_tries = 5; /* attempts before we fall back to search */ + dprintk("choose bucket %d x %d outpos %d\n", bucket->id, x, outpos); + + for (rep = outpos; rep < numrep; rep++) { + /* keep trying until we get a non-out, non-colliding item */ + ftotal = 0; + skip_rep = 0; + do { + retry_descent = 0; + in = bucket; /* initial bucket */ + + /* choose through intervening buckets */ + flocal = 0; + do { + retry_bucket = 0; + r = rep; + if (in->alg == CRUSH_BUCKET_UNIFORM) { + /* be careful */ + if (firstn || numrep >= in->size) + /* r' = r + f_total */ + r += ftotal; + else if (in->size % numrep == 0) + /* r'=r+(n+1)*f_local */ + r += (numrep+1) * + (flocal+ftotal); + else + /* r' = r + n*f_local */ + r += numrep * (flocal+ftotal); + } else { + if (firstn) + /* r' = r + f_total */ + r += ftotal; + else + /* r' = r + n*f_local */ + r += numrep * (flocal+ftotal); + } + + /* bucket choose */ + if (flocal >= (in->size>>1) && + flocal > orig_tries) + item = bucket_perm_choose(in, x, r); + else + item = crush_bucket_choose(in, x, r); + BUG_ON(item >= map->max_devices); + + /* desired type? */ + if (item < 0) + itemtype = map->buckets[-1-item]->type; + else + itemtype = 0; + dprintk(" item %d type %d\n", item, itemtype); + + /* keep going? */ + if (itemtype != type) { + BUG_ON(item >= 0 || + (-1-item) >= map->max_buckets); + in = map->buckets[-1-item]; + continue; + } + + /* collision? */ + collide = 0; + for (i = 0; i < outpos; i++) { + if (out[i] == item) { + collide = 1; + break; + } + } + + if (recurse_to_leaf && + item < 0 && + crush_choose(map, map->buckets[-1-item], + weight, + x, outpos+1, 0, + out2, outpos, + firstn, 0, NULL) <= outpos) { + reject = 1; + } else { + /* out? */ + if (itemtype == 0) + reject = is_out(map, weight, + item, x); + else + reject = 0; + } + + if (reject || collide) { + ftotal++; + flocal++; + + if (collide && flocal < 3) + /* retry locally a few times */ + retry_bucket = 1; + else if (flocal < in->size + orig_tries) + /* exhaustive bucket search */ + retry_bucket = 1; + else if (ftotal < 20) + /* then retry descent */ + retry_descent = 1; + else + /* else give up */ + skip_rep = 1; + dprintk(" reject %d collide %d " + "ftotal %d flocal %d\n", + reject, collide, ftotal, + flocal); + } + } while (retry_bucket); + } while (retry_descent); + + if (skip_rep) { + dprintk("skip rep\n"); + continue; + } + + dprintk("choose got %d\n", item); + out[outpos] = item; + outpos++; + } + + dprintk("choose returns %d\n", outpos); + return outpos; +} + + +/** + * crush_do_rule - calculate a mapping with the given input and rule + * @map: the crush_map + * @ruleno: the rule id + * @x: hash input + * @result: pointer to result vector + * @result_max: maximum result size + * @force: force initial replica choice; -1 for none + */ +int crush_do_rule(struct crush_map *map, + int ruleno, int x, int *result, int result_max, + int force, __u32 *weight) +{ + int result_len; + int force_context[CRUSH_MAX_DEPTH]; + int force_pos = -1; + int a[CRUSH_MAX_SET]; + int b[CRUSH_MAX_SET]; + int c[CRUSH_MAX_SET]; + int recurse_to_leaf; + int *w; + int wsize = 0; + int *o; + int osize; + int *tmp; + struct crush_rule *rule; + int step; + int i, j; + int numrep; + int firstn; + int rc = -1; + + BUG_ON(ruleno >= map->max_rules); + + rule = map->rules[ruleno]; + result_len = 0; + w = a; + o = b; + + /* + * determine hierarchical context of force, if any. note + * that this may or may not correspond to the specific types + * referenced by the crush rule. + */ + if (force >= 0) { + if (force >= map->max_devices || + map->device_parents[force] == 0) { + /*dprintk("CRUSH: forcefed device dne\n");*/ + rc = -1; /* force fed device dne */ + goto out; + } + if (!is_out(map, weight, force, x)) { + while (1) { + force_context[++force_pos] = force; + if (force >= 0) + force = map->device_parents[force]; + else + force = map->bucket_parents[-1-force]; + if (force == 0) + break; + } + } + } + + for (step = 0; step < rule->len; step++) { + firstn = 0; + switch (rule->steps[step].op) { + case CRUSH_RULE_TAKE: + w[0] = rule->steps[step].arg1; + if (force_pos >= 0) { + BUG_ON(force_context[force_pos] != w[0]); + force_pos--; + } + wsize = 1; + break; + + case CRUSH_RULE_CHOOSE_LEAF_FIRSTN: + case CRUSH_RULE_CHOOSE_FIRSTN: + firstn = 1; + case CRUSH_RULE_CHOOSE_LEAF_INDEP: + case CRUSH_RULE_CHOOSE_INDEP: + BUG_ON(wsize == 0); + + recurse_to_leaf = + rule->steps[step].op == + CRUSH_RULE_CHOOSE_LEAF_FIRSTN || + rule->steps[step].op == + CRUSH_RULE_CHOOSE_LEAF_INDEP; + + /* reset output */ + osize = 0; + + for (i = 0; i < wsize; i++) { + /* + * see CRUSH_N, CRUSH_N_MINUS macros. + * basically, numrep <= 0 means relative to + * the provided result_max + */ + numrep = rule->steps[step].arg1; + if (numrep <= 0) { + numrep += result_max; + if (numrep <= 0) + continue; + } + j = 0; + if (osize == 0 && force_pos >= 0) { + /* skip any intermediate types */ + while (force_pos && + force_context[force_pos] < 0 && + rule->steps[step].arg2 != + map->buckets[-1 - + force_context[force_pos]]->type) + force_pos--; + o[osize] = force_context[force_pos]; + if (recurse_to_leaf) + c[osize] = force_context[0]; + j++; + force_pos--; + } + osize += crush_choose(map, + map->buckets[-1-w[i]], + weight, + x, numrep, + rule->steps[step].arg2, + o+osize, j, + firstn, + recurse_to_leaf, c+osize); + } + + if (recurse_to_leaf) + /* copy final _leaf_ values to output set */ + memcpy(o, c, osize*sizeof(*o)); + + /* swap t and w arrays */ + tmp = o; + o = w; + w = tmp; + wsize = osize; + break; + + + case CRUSH_RULE_EMIT: + for (i = 0; i < wsize && result_len < result_max; i++) { + result[result_len] = w[i]; + result_len++; + } + wsize = 0; + break; + + default: + BUG_ON(1); + } + } + rc = result_len; + +out: + return rc; +} + + diff --git a/fs/ceph/crush/mapper.h b/fs/ceph/crush/mapper.h new file mode 100644 index 0000000..98e9004 --- /dev/null +++ b/fs/ceph/crush/mapper.h @@ -0,0 +1,20 @@ +#ifndef _CRUSH_MAPPER_H +#define _CRUSH_MAPPER_H + +/* + * CRUSH functions for find rules and then mapping an input to an + * output set. + * + * LGPL2 + */ + +#include "crush.h" + +extern int crush_find_rule(struct crush_map *map, int pool, int type, int size); +extern int crush_do_rule(struct crush_map *map, + int ruleno, + int x, int *result, int result_max, + int forcefeed, /* -1 for none */ + __u32 *weights); + +#endif -- cgit v0.10.2 From ba75bb98cfb93b62c54af25bf67ff90857264bbe Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:11 -0700 Subject: ceph: monitor client The monitor cluster is responsible for managing cluster membership and state. The monitor client handles what minimal interaction the Ceph client has with it: checking for updated versions of the MDS and OSD maps, getting statfs() information, and unmounting. Signed-off-by: Sage Weil diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c new file mode 100644 index 0000000..b0c95ce --- /dev/null +++ b/fs/ceph/mon_client.c @@ -0,0 +1,694 @@ +#include "ceph_debug.h" + +#include +#include +#include + +#include "mon_client.h" +#include "super.h" +#include "decode.h" + +/* + * Interact with Ceph monitor cluster. Handle requests for new map + * versions, and periodically resend as needed. Also implement + * statfs() and umount(). + * + * A small cluster of Ceph "monitors" are responsible for managing critical + * cluster configuration and state information. An odd number (e.g., 3, 5) + * of cmon daemons use a modified version of the Paxos part-time parliament + * algorithm to manage the MDS map (mds cluster membership), OSD map, and + * list of clients who have mounted the file system. + * + * We maintain an open, active session with a monitor at all times in order to + * receive timely MDSMap updates. We periodically send a keepalive byte on the + * TCP socket to ensure we detect a failure. If the connection does break, we + * randomly hunt for a new monitor. Once the connection is reestablished, we + * resend any outstanding requests. + */ + +const static struct ceph_connection_operations mon_con_ops; + +/* + * Decode a monmap blob (e.g., during mount). + */ +struct ceph_monmap *ceph_monmap_decode(void *p, void *end) +{ + struct ceph_monmap *m = NULL; + int i, err = -EINVAL; + struct ceph_fsid fsid; + u32 epoch, num_mon; + u16 version; + + dout("monmap_decode %p %p len %d\n", p, end, (int)(end-p)); + + ceph_decode_16_safe(&p, end, version, bad); + + ceph_decode_need(&p, end, sizeof(fsid) + 2*sizeof(u32), bad); + ceph_decode_copy(&p, &fsid, sizeof(fsid)); + ceph_decode_32(&p, epoch); + + ceph_decode_32(&p, num_mon); + ceph_decode_need(&p, end, num_mon*sizeof(m->mon_inst[0]), bad); + + if (num_mon >= CEPH_MAX_MON) + goto bad; + m = kmalloc(sizeof(*m) + sizeof(m->mon_inst[0])*num_mon, GFP_NOFS); + if (m == NULL) + return ERR_PTR(-ENOMEM); + m->fsid = fsid; + m->epoch = epoch; + m->num_mon = num_mon; + ceph_decode_copy(&p, m->mon_inst, num_mon*sizeof(m->mon_inst[0])); + + if (p != end) + goto bad; + + dout("monmap_decode epoch %d, num_mon %d\n", m->epoch, + m->num_mon); + for (i = 0; i < m->num_mon; i++) + dout("monmap_decode mon%d is %s\n", i, + pr_addr(&m->mon_inst[i].addr.in_addr)); + return m; + +bad: + dout("monmap_decode failed with %d\n", err); + kfree(m); + return ERR_PTR(err); +} + +/* + * return true if *addr is included in the monmap. + */ +int ceph_monmap_contains(struct ceph_monmap *m, struct ceph_entity_addr *addr) +{ + int i; + + for (i = 0; i < m->num_mon; i++) + if (ceph_entity_addr_equal(addr, &m->mon_inst[i].addr)) + return 1; + return 0; +} + +/* + * Close monitor session, if any. + */ +static void __close_session(struct ceph_mon_client *monc) +{ + if (monc->con) { + dout("__close_session closing mon%d\n", monc->cur_mon); + ceph_con_close(monc->con); + monc->cur_mon = -1; + } +} + +/* + * Open a session with a (new) monitor. + */ +static int __open_session(struct ceph_mon_client *monc) +{ + char r; + + if (monc->cur_mon < 0) { + get_random_bytes(&r, 1); + monc->cur_mon = r % monc->monmap->num_mon; + dout("open_session num=%d r=%d -> mon%d\n", + monc->monmap->num_mon, r, monc->cur_mon); + monc->sub_sent = 0; + monc->sub_renew_after = jiffies; /* i.e., expired */ + monc->want_next_osdmap = !!monc->want_next_osdmap; + + dout("open_session mon%d opening\n", monc->cur_mon); + monc->con->peer_name.type = CEPH_ENTITY_TYPE_MON; + monc->con->peer_name.num = cpu_to_le64(monc->cur_mon); + ceph_con_open(monc->con, + &monc->monmap->mon_inst[monc->cur_mon].addr); + } else { + dout("open_session mon%d already open\n", monc->cur_mon); + } + return 0; +} + +static bool __sub_expired(struct ceph_mon_client *monc) +{ + return time_after_eq(jiffies, monc->sub_renew_after); +} + +/* + * Reschedule delayed work timer. + */ +static void __schedule_delayed(struct ceph_mon_client *monc) +{ + unsigned delay; + + if (monc->cur_mon < 0 || monc->want_mount || __sub_expired(monc)) + delay = 10 * HZ; + else + delay = 20 * HZ; + dout("__schedule_delayed after %u\n", delay); + schedule_delayed_work(&monc->delayed_work, delay); +} + +/* + * Send subscribe request for mdsmap and/or osdmap. + */ +static void __send_subscribe(struct ceph_mon_client *monc) +{ + dout("__send_subscribe sub_sent=%u exp=%u want_osd=%d\n", + (unsigned)monc->sub_sent, __sub_expired(monc), + monc->want_next_osdmap); + if ((__sub_expired(monc) && !monc->sub_sent) || + monc->want_next_osdmap == 1) { + struct ceph_msg *msg; + struct ceph_mon_subscribe_item *i; + void *p, *end; + + msg = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE, 64, 0, 0, NULL); + if (!msg) + return; + + p = msg->front.iov_base; + end = p + msg->front.iov_len; + + dout("__send_subscribe to 'mdsmap' %u+\n", + (unsigned)monc->have_mdsmap); + if (monc->want_next_osdmap) { + dout("__send_subscribe to 'osdmap' %u\n", + (unsigned)monc->have_osdmap); + ceph_encode_32(&p, 2); + ceph_encode_string(&p, end, "osdmap", 6); + i = p; + i->have = cpu_to_le64(monc->have_osdmap); + i->onetime = 1; + p += sizeof(*i); + monc->want_next_osdmap = 2; /* requested */ + } else { + ceph_encode_32(&p, 1); + } + ceph_encode_string(&p, end, "mdsmap", 6); + i = p; + i->have = cpu_to_le64(monc->have_mdsmap); + i->onetime = 0; + p += sizeof(*i); + + msg->front.iov_len = p - msg->front.iov_base; + msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); + ceph_con_send(monc->con, msg); + + monc->sub_sent = jiffies | 1; /* never 0 */ + } +} + +static void handle_subscribe_ack(struct ceph_mon_client *monc, + struct ceph_msg *msg) +{ + unsigned seconds; + void *p = msg->front.iov_base; + void *end = p + msg->front.iov_len; + + ceph_decode_32_safe(&p, end, seconds, bad); + mutex_lock(&monc->mutex); + if (monc->hunting) { + pr_info("mon%d %s session established\n", + monc->cur_mon, pr_addr(&monc->con->peer_addr.in_addr)); + monc->hunting = false; + } + dout("handle_subscribe_ack after %d seconds\n", seconds); + monc->sub_renew_after = monc->sub_sent + seconds*HZ - 1; + monc->sub_sent = 0; + mutex_unlock(&monc->mutex); + return; +bad: + pr_err("got corrupt subscribe-ack msg\n"); +} + +/* + * Keep track of which maps we have + */ +int ceph_monc_got_mdsmap(struct ceph_mon_client *monc, u32 got) +{ + mutex_lock(&monc->mutex); + monc->have_mdsmap = got; + mutex_unlock(&monc->mutex); + return 0; +} + +int ceph_monc_got_osdmap(struct ceph_mon_client *monc, u32 got) +{ + mutex_lock(&monc->mutex); + monc->have_osdmap = got; + monc->want_next_osdmap = 0; + mutex_unlock(&monc->mutex); + return 0; +} + +/* + * Register interest in the next osdmap + */ +void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc) +{ + dout("request_next_osdmap have %u\n", monc->have_osdmap); + mutex_lock(&monc->mutex); + if (!monc->want_next_osdmap) + monc->want_next_osdmap = 1; + if (monc->want_next_osdmap < 2) + __send_subscribe(monc); + mutex_unlock(&monc->mutex); +} + + +/* + * mount + */ +static void __request_mount(struct ceph_mon_client *monc) +{ + struct ceph_msg *msg; + struct ceph_client_mount *h; + int err; + + dout("__request_mount\n"); + err = __open_session(monc); + if (err) + return; + msg = ceph_msg_new(CEPH_MSG_CLIENT_MOUNT, sizeof(*h), 0, 0, NULL); + if (IS_ERR(msg)) + return; + h = msg->front.iov_base; + h->have_version = 0; + ceph_con_send(monc->con, msg); +} + +int ceph_monc_request_mount(struct ceph_mon_client *monc) +{ + if (!monc->con) { + monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); + if (!monc->con) + return -ENOMEM; + ceph_con_init(monc->client->msgr, monc->con); + monc->con->private = monc; + monc->con->ops = &mon_con_ops; + } + + mutex_lock(&monc->mutex); + __request_mount(monc); + __schedule_delayed(monc); + mutex_unlock(&monc->mutex); + return 0; +} + +/* + * The monitor responds with mount ack indicate mount success. The + * included client ticket allows the client to talk to MDSs and OSDs. + */ +static void handle_mount_ack(struct ceph_mon_client *monc, struct ceph_msg *msg) +{ + struct ceph_client *client = monc->client; + struct ceph_monmap *monmap = NULL, *old = monc->monmap; + void *p, *end; + s32 result; + u32 len; + s64 cnum; + int err = -EINVAL; + + if (client->whoami >= 0) { + dout("handle_mount_ack - already mounted\n"); + return; + } + + mutex_lock(&monc->mutex); + + dout("handle_mount_ack\n"); + p = msg->front.iov_base; + end = p + msg->front.iov_len; + + ceph_decode_64_safe(&p, end, cnum, bad); + ceph_decode_32_safe(&p, end, result, bad); + ceph_decode_32_safe(&p, end, len, bad); + if (result) { + pr_err("mount denied: %.*s (%d)\n", len, (char *)p, + result); + err = result; + goto out; + } + p += len; + + ceph_decode_32_safe(&p, end, len, bad); + ceph_decode_need(&p, end, len, bad); + monmap = ceph_monmap_decode(p, p + len); + if (IS_ERR(monmap)) { + pr_err("problem decoding monmap, %d\n", + (int)PTR_ERR(monmap)); + err = -EINVAL; + goto out; + } + p += len; + + client->monc.monmap = monmap; + kfree(old); + + client->signed_ticket = NULL; + client->signed_ticket_len = 0; + + monc->want_mount = false; + + client->whoami = cnum; + client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; + client->msgr->inst.name.num = cpu_to_le64(cnum); + pr_info("client%lld fsid " FSID_FORMAT "\n", + client->whoami, PR_FSID(&client->monc.monmap->fsid)); + + ceph_debugfs_client_init(client); + __send_subscribe(monc); + + err = 0; + goto out; + +bad: + pr_err("error decoding mount_ack message\n"); +out: + client->mount_err = err; + mutex_unlock(&monc->mutex); + wake_up(&client->mount_wq); +} + + + + +/* + * statfs + */ +static void handle_statfs_reply(struct ceph_mon_client *monc, + struct ceph_msg *msg) +{ + struct ceph_mon_statfs_request *req; + struct ceph_mon_statfs_reply *reply = msg->front.iov_base; + u64 tid; + + if (msg->front.iov_len != sizeof(*reply)) + goto bad; + tid = le64_to_cpu(reply->tid); + dout("handle_statfs_reply %p tid %llu\n", msg, tid); + + mutex_lock(&monc->mutex); + req = radix_tree_lookup(&monc->statfs_request_tree, tid); + if (req) { + *req->buf = reply->st; + req->result = 0; + } + mutex_unlock(&monc->mutex); + if (req) + complete(&req->completion); + return; + +bad: + pr_err("corrupt statfs reply, no tid\n"); +} + +/* + * (re)send a statfs request + */ +static int send_statfs(struct ceph_mon_client *monc, + struct ceph_mon_statfs_request *req) +{ + struct ceph_msg *msg; + struct ceph_mon_statfs *h; + int err; + + dout("send_statfs tid %llu\n", req->tid); + err = __open_session(monc); + if (err) + return err; + msg = ceph_msg_new(CEPH_MSG_STATFS, sizeof(*h), 0, 0, NULL); + if (IS_ERR(msg)) + return PTR_ERR(msg); + req->request = msg; + h = msg->front.iov_base; + h->have_version = 0; + h->fsid = monc->monmap->fsid; + h->tid = cpu_to_le64(req->tid); + ceph_con_send(monc->con, msg); + return 0; +} + +/* + * Do a synchronous statfs(). + */ +int ceph_monc_do_statfs(struct ceph_mon_client *monc, struct ceph_statfs *buf) +{ + struct ceph_mon_statfs_request req; + int err; + + req.buf = buf; + init_completion(&req.completion); + + /* allocate memory for reply */ + err = ceph_msgpool_resv(&monc->msgpool_statfs_reply, 1); + if (err) + return err; + + /* register request */ + mutex_lock(&monc->mutex); + req.tid = ++monc->last_tid; + req.last_attempt = jiffies; + req.delay = BASE_DELAY_INTERVAL; + if (radix_tree_insert(&monc->statfs_request_tree, req.tid, &req) < 0) { + mutex_unlock(&monc->mutex); + pr_err("ENOMEM in do_statfs\n"); + return -ENOMEM; + } + monc->num_statfs_requests++; + mutex_unlock(&monc->mutex); + + /* send request and wait */ + err = send_statfs(monc, &req); + if (!err) + err = wait_for_completion_interruptible(&req.completion); + + mutex_lock(&monc->mutex); + radix_tree_delete(&monc->statfs_request_tree, req.tid); + monc->num_statfs_requests--; + ceph_msgpool_resv(&monc->msgpool_statfs_reply, -1); + mutex_unlock(&monc->mutex); + + if (!err) + err = req.result; + return err; +} + +/* + * Resend pending statfs requests. + */ +static void __resend_statfs(struct ceph_mon_client *monc) +{ + u64 next_tid = 0; + int got; + int did = 0; + struct ceph_mon_statfs_request *req; + + while (1) { + got = radix_tree_gang_lookup(&monc->statfs_request_tree, + (void **)&req, + next_tid, 1); + if (got == 0) + break; + did++; + next_tid = req->tid + 1; + + send_statfs(monc, req); + } +} + +/* + * Delayed work. If we haven't mounted yet, retry. Otherwise, + * renew/retry subscription as needed (in case it is timing out, or we + * got an ENOMEM). And keep the monitor connection alive. + */ +static void delayed_work(struct work_struct *work) +{ + struct ceph_mon_client *monc = + container_of(work, struct ceph_mon_client, delayed_work.work); + + dout("monc delayed_work\n"); + mutex_lock(&monc->mutex); + if (monc->want_mount) { + __request_mount(monc); + } else { + if (__sub_expired(monc)) { + __close_session(monc); + __open_session(monc); /* continue hunting */ + } else { + ceph_con_keepalive(monc->con); + } + } + __send_subscribe(monc); + __schedule_delayed(monc); + mutex_unlock(&monc->mutex); +} + +int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) +{ + int err = 0; + + dout("init\n"); + memset(monc, 0, sizeof(*monc)); + monc->client = cl; + monc->monmap = NULL; + mutex_init(&monc->mutex); + + monc->con = NULL; + + /* msg pools */ + err = ceph_msgpool_init(&monc->msgpool_mount_ack, 4096, 1, false); + if (err < 0) + goto out; + err = ceph_msgpool_init(&monc->msgpool_subscribe_ack, 8, 1, false); + if (err < 0) + goto out; + err = ceph_msgpool_init(&monc->msgpool_statfs_reply, + sizeof(struct ceph_mon_statfs_reply), 0, false); + if (err < 0) + goto out; + + monc->cur_mon = -1; + monc->hunting = false; /* not really */ + monc->sub_renew_after = jiffies; + monc->sub_sent = 0; + + INIT_DELAYED_WORK(&monc->delayed_work, delayed_work); + INIT_RADIX_TREE(&monc->statfs_request_tree, GFP_NOFS); + monc->num_statfs_requests = 0; + monc->last_tid = 0; + + monc->have_mdsmap = 0; + monc->have_osdmap = 0; + monc->want_next_osdmap = 1; + monc->want_mount = true; +out: + return err; +} + +void ceph_monc_stop(struct ceph_mon_client *monc) +{ + dout("stop\n"); + cancel_delayed_work_sync(&monc->delayed_work); + + mutex_lock(&monc->mutex); + __close_session(monc); + if (monc->con) { + monc->con->private = NULL; + monc->con->ops->put(monc->con); + monc->con = NULL; + } + mutex_unlock(&monc->mutex); + + ceph_msgpool_destroy(&monc->msgpool_mount_ack); + ceph_msgpool_destroy(&monc->msgpool_subscribe_ack); + ceph_msgpool_destroy(&monc->msgpool_statfs_reply); + + kfree(monc->monmap); +} + + +/* + * handle incoming message + */ +static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) +{ + struct ceph_mon_client *monc = con->private; + int type = le16_to_cpu(msg->hdr.type); + + if (!monc) + return; + + switch (type) { + case CEPH_MSG_CLIENT_MOUNT_ACK: + handle_mount_ack(monc, msg); + break; + + case CEPH_MSG_MON_SUBSCRIBE_ACK: + handle_subscribe_ack(monc, msg); + break; + + case CEPH_MSG_STATFS_REPLY: + handle_statfs_reply(monc, msg); + break; + + case CEPH_MSG_MDS_MAP: + ceph_mdsc_handle_map(&monc->client->mdsc, msg); + break; + + case CEPH_MSG_OSD_MAP: + ceph_osdc_handle_map(&monc->client->osdc, msg); + break; + + default: + pr_err("received unknown message type %d %s\n", type, + ceph_msg_type_name(type)); + } + ceph_msg_put(msg); +} + +/* + * Allocate memory for incoming message + */ +static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, + struct ceph_msg_header *hdr) +{ + struct ceph_mon_client *monc = con->private; + int type = le16_to_cpu(hdr->type); + + switch (type) { + case CEPH_MSG_CLIENT_MOUNT_ACK: + return ceph_msgpool_get(&monc->msgpool_mount_ack); + case CEPH_MSG_MON_SUBSCRIBE_ACK: + return ceph_msgpool_get(&monc->msgpool_subscribe_ack); + case CEPH_MSG_STATFS_REPLY: + return ceph_msgpool_get(&monc->msgpool_statfs_reply); + } + return ceph_alloc_msg(con, hdr); +} + +/* + * If the monitor connection resets, pick a new monitor and resubmit + * any pending requests. + */ +static void mon_fault(struct ceph_connection *con) +{ + struct ceph_mon_client *monc = con->private; + + if (!monc) + return; + + dout("mon_fault\n"); + mutex_lock(&monc->mutex); + if (!con->private) + goto out; + + if (monc->con && !monc->hunting) + pr_info("mon%d %s session lost, " + "hunting for new mon\n", monc->cur_mon, + pr_addr(&monc->con->peer_addr.in_addr)); + + __close_session(monc); + if (!monc->hunting) { + /* start hunting */ + monc->hunting = true; + if (__open_session(monc) == 0) { + __send_subscribe(monc); + __resend_statfs(monc); + } + } else { + /* already hunting, let's wait a bit */ + __schedule_delayed(monc); + } +out: + mutex_unlock(&monc->mutex); +} + +const static struct ceph_connection_operations mon_con_ops = { + .get = ceph_con_get, + .put = ceph_con_put, + .dispatch = dispatch, + .fault = mon_fault, + .alloc_msg = mon_alloc_msg, + .alloc_middle = ceph_alloc_middle, +}; diff --git a/fs/ceph/mon_client.h b/fs/ceph/mon_client.h new file mode 100644 index 0000000..5258c56 --- /dev/null +++ b/fs/ceph/mon_client.h @@ -0,0 +1,109 @@ +#ifndef _FS_CEPH_MON_CLIENT_H +#define _FS_CEPH_MON_CLIENT_H + +#include +#include + +#include "messenger.h" +#include "msgpool.h" + +struct ceph_client; +struct ceph_mount_args; + +/* + * The monitor map enumerates the set of all monitors. + */ +struct ceph_monmap { + struct ceph_fsid fsid; + u32 epoch; + u32 num_mon; + struct ceph_entity_inst mon_inst[0]; +}; + +struct ceph_mon_client; +struct ceph_mon_statfs_request; + + +/* + * Generic mechanism for resending monitor requests. + */ +typedef void (*ceph_monc_request_func_t)(struct ceph_mon_client *monc, + int newmon); + +/* a pending monitor request */ +struct ceph_mon_request { + struct ceph_mon_client *monc; + struct delayed_work delayed_work; + unsigned long delay; + ceph_monc_request_func_t do_request; +}; + +/* + * statfs() is done a bit differently because we need to get data back + * to the caller + */ +struct ceph_mon_statfs_request { + u64 tid; + int result; + struct ceph_statfs *buf; + struct completion completion; + unsigned long last_attempt, delay; /* jiffies */ + struct ceph_msg *request; /* original request */ +}; + +struct ceph_mon_client { + struct ceph_client *client; + struct ceph_monmap *monmap; + + struct mutex mutex; + struct delayed_work delayed_work; + + bool hunting; + int cur_mon; /* last monitor i contacted */ + unsigned long sub_sent, sub_renew_after; + struct ceph_connection *con; + + /* msg pools */ + struct ceph_msgpool msgpool_mount_ack; + struct ceph_msgpool msgpool_subscribe_ack; + struct ceph_msgpool msgpool_statfs_reply; + + /* pending statfs requests */ + struct radix_tree_root statfs_request_tree; + int num_statfs_requests; + u64 last_tid; + + /* mds/osd map or mount requests */ + bool want_mount; + int want_next_osdmap; /* 1 = want, 2 = want+asked */ + u32 have_osdmap, have_mdsmap; + + struct dentry *debugfs_file; +}; + +extern struct ceph_monmap *ceph_monmap_decode(void *p, void *end); +extern int ceph_monmap_contains(struct ceph_monmap *m, + struct ceph_entity_addr *addr); + +extern int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl); +extern void ceph_monc_stop(struct ceph_mon_client *monc); + +/* + * The model here is to indicate that we need a new map of at least + * epoch @want, and also call in when we receive a map. We will + * periodically rerequest the map from the monitor cluster until we + * get what we want. + */ +extern int ceph_monc_got_mdsmap(struct ceph_mon_client *monc, u32 have); +extern int ceph_monc_got_osdmap(struct ceph_mon_client *monc, u32 have); + +extern void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc); + +extern int ceph_monc_request_mount(struct ceph_mon_client *monc); + +extern int ceph_monc_do_statfs(struct ceph_mon_client *monc, + struct ceph_statfs *buf); + + + +#endif -- cgit v0.10.2 From a8599bd821d084d04a3290fffae1071624ec00ea Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:12 -0700 Subject: ceph: capability management The Ceph metadata servers control client access to inode metadata and file data by issuing capabilities, granting clients permission to read and/or write both inode field and file data to OSDs (storage nodes). Each capability consists of a set of bits indicating which operations are allowed. If the client holds a *_SHARED cap, the client has a coherent value that can be safely read from the cached inode. In the case of a *_EXCL (exclusive) or FILE_WR capabilities, the client is allowed to change inode attributes (e.g., file size, mtime), note its dirty state in the ceph_cap, and asynchronously flush that metadata change to the MDS. In the event of a conflicting operation (perhaps by another client), the MDS will revoke the conflicting client capabilities. In order for a client to cache an inode, it must hold a capability with at least one MDS server. When inodes are released, release notifications are batched and periodically sent en masse to the MDS cluster to release server state. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c new file mode 100644 index 0000000..5c7d0e9 --- /dev/null +++ b/fs/ceph/caps.c @@ -0,0 +1,2830 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include +#include + +#include "super.h" +#include "decode.h" +#include "messenger.h" + +/* + * Capability management + * + * The Ceph metadata servers control client access to inode metadata + * and file data by issuing capabilities, granting clients permission + * to read and/or write both inode field and file data to OSDs + * (storage nodes). Each capability consists of a set of bits + * indicating which operations are allowed. + * + * If the client holds a *_SHARED cap, the client has a coherent value + * that can be safely read from the cached inode. + * + * In the case of a *_EXCL (exclusive) or FILE_WR capabilities, the + * client is allowed to change inode attributes (e.g., file size, + * mtime), note its dirty state in the ceph_cap, and asynchronously + * flush that metadata change to the MDS. + * + * In the event of a conflicting operation (perhaps by another + * client), the MDS will revoke the conflicting client capabilities. + * + * In order for a client to cache an inode, it must hold a capability + * with at least one MDS server. When inodes are released, release + * notifications are batched and periodically sent en masse to the MDS + * cluster to release server state. + */ + + +/* + * Generate readable cap strings for debugging output. + */ +#define MAX_CAP_STR 20 +static char cap_str[MAX_CAP_STR][40]; +static DEFINE_SPINLOCK(cap_str_lock); +static int last_cap_str; + +static char *gcap_string(char *s, int c) +{ + if (c & CEPH_CAP_GSHARED) + *s++ = 's'; + if (c & CEPH_CAP_GEXCL) + *s++ = 'x'; + if (c & CEPH_CAP_GCACHE) + *s++ = 'c'; + if (c & CEPH_CAP_GRD) + *s++ = 'r'; + if (c & CEPH_CAP_GWR) + *s++ = 'w'; + if (c & CEPH_CAP_GBUFFER) + *s++ = 'b'; + if (c & CEPH_CAP_GLAZYIO) + *s++ = 'l'; + return s; +} + +const char *ceph_cap_string(int caps) +{ + int i; + char *s; + int c; + + spin_lock(&cap_str_lock); + i = last_cap_str++; + if (last_cap_str == MAX_CAP_STR) + last_cap_str = 0; + spin_unlock(&cap_str_lock); + + s = cap_str[i]; + + if (caps & CEPH_CAP_PIN) + *s++ = 'p'; + + c = (caps >> CEPH_CAP_SAUTH) & 3; + if (c) { + *s++ = 'A'; + s = gcap_string(s, c); + } + + c = (caps >> CEPH_CAP_SLINK) & 3; + if (c) { + *s++ = 'L'; + s = gcap_string(s, c); + } + + c = (caps >> CEPH_CAP_SXATTR) & 3; + if (c) { + *s++ = 'X'; + s = gcap_string(s, c); + } + + c = caps >> CEPH_CAP_SFILE; + if (c) { + *s++ = 'F'; + s = gcap_string(s, c); + } + + if (s == cap_str[i]) + *s++ = '-'; + *s = 0; + return cap_str[i]; +} + +/* + * Cap reservations + * + * Maintain a global pool of preallocated struct ceph_caps, referenced + * by struct ceph_caps_reservations. This ensures that we preallocate + * memory needed to successfully process an MDS response. (If an MDS + * sends us cap information and we fail to process it, we will have + * problems due to the client and MDS being out of sync.) + * + * Reservations are 'owned' by a ceph_cap_reservation context. + */ +static spinlock_t caps_list_lock; +static struct list_head caps_list; /* unused (reserved or unreserved) */ +static int caps_total_count; /* total caps allocated */ +static int caps_use_count; /* in use */ +static int caps_reserve_count; /* unused, reserved */ +static int caps_avail_count; /* unused, unreserved */ + +void __init ceph_caps_init(void) +{ + INIT_LIST_HEAD(&caps_list); + spin_lock_init(&caps_list_lock); +} + +void ceph_caps_finalize(void) +{ + struct ceph_cap *cap; + + spin_lock(&caps_list_lock); + while (!list_empty(&caps_list)) { + cap = list_first_entry(&caps_list, struct ceph_cap, caps_item); + list_del(&cap->caps_item); + kmem_cache_free(ceph_cap_cachep, cap); + } + caps_total_count = 0; + caps_avail_count = 0; + caps_use_count = 0; + caps_reserve_count = 0; + spin_unlock(&caps_list_lock); +} + +int ceph_reserve_caps(struct ceph_cap_reservation *ctx, int need) +{ + int i; + struct ceph_cap *cap; + int have; + int alloc = 0; + LIST_HEAD(newcaps); + int ret = 0; + + dout("reserve caps ctx=%p need=%d\n", ctx, need); + + /* first reserve any caps that are already allocated */ + spin_lock(&caps_list_lock); + if (caps_avail_count >= need) + have = need; + else + have = caps_avail_count; + caps_avail_count -= have; + caps_reserve_count += have; + BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + + caps_avail_count); + spin_unlock(&caps_list_lock); + + for (i = have; i < need; i++) { + cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS); + if (!cap) { + ret = -ENOMEM; + goto out_alloc_count; + } + list_add(&cap->caps_item, &newcaps); + alloc++; + } + BUG_ON(have + alloc != need); + + spin_lock(&caps_list_lock); + caps_total_count += alloc; + caps_reserve_count += alloc; + list_splice(&newcaps, &caps_list); + + BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + + caps_avail_count); + spin_unlock(&caps_list_lock); + + ctx->count = need; + dout("reserve caps ctx=%p %d = %d used + %d resv + %d avail\n", + ctx, caps_total_count, caps_use_count, caps_reserve_count, + caps_avail_count); + return 0; + +out_alloc_count: + /* we didn't manage to reserve as much as we needed */ + pr_warning("reserve caps ctx=%p ENOMEM need=%d got=%d\n", + ctx, need, have); + return ret; +} + +int ceph_unreserve_caps(struct ceph_cap_reservation *ctx) +{ + dout("unreserve caps ctx=%p count=%d\n", ctx, ctx->count); + if (ctx->count) { + spin_lock(&caps_list_lock); + BUG_ON(caps_reserve_count < ctx->count); + caps_reserve_count -= ctx->count; + caps_avail_count += ctx->count; + ctx->count = 0; + dout("unreserve caps %d = %d used + %d resv + %d avail\n", + caps_total_count, caps_use_count, caps_reserve_count, + caps_avail_count); + BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + + caps_avail_count); + spin_unlock(&caps_list_lock); + } + return 0; +} + +static struct ceph_cap *get_cap(struct ceph_cap_reservation *ctx) +{ + struct ceph_cap *cap = NULL; + + /* temporary, until we do something about cap import/export */ + if (!ctx) + return kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS); + + spin_lock(&caps_list_lock); + dout("get_cap ctx=%p (%d) %d = %d used + %d resv + %d avail\n", + ctx, ctx->count, caps_total_count, caps_use_count, + caps_reserve_count, caps_avail_count); + BUG_ON(!ctx->count); + BUG_ON(ctx->count > caps_reserve_count); + BUG_ON(list_empty(&caps_list)); + + ctx->count--; + caps_reserve_count--; + caps_use_count++; + + cap = list_first_entry(&caps_list, struct ceph_cap, caps_item); + list_del(&cap->caps_item); + + BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + + caps_avail_count); + spin_unlock(&caps_list_lock); + return cap; +} + +static void put_cap(struct ceph_cap *cap, + struct ceph_cap_reservation *ctx) +{ + spin_lock(&caps_list_lock); + dout("put_cap ctx=%p (%d) %d = %d used + %d resv + %d avail\n", + ctx, ctx ? ctx->count : 0, caps_total_count, caps_use_count, + caps_reserve_count, caps_avail_count); + caps_use_count--; + /* + * Keep some preallocated caps around, at least enough to do a + * readdir (which needs to preallocate lots of them), to avoid + * lots of free/alloc churn. + */ + if (caps_avail_count >= caps_reserve_count + + ceph_client(cap->ci->vfs_inode.i_sb)->mount_args.max_readdir) { + caps_total_count--; + kmem_cache_free(ceph_cap_cachep, cap); + } else { + if (ctx) { + ctx->count++; + caps_reserve_count++; + } else { + caps_avail_count++; + } + list_add(&cap->caps_item, &caps_list); + } + + BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + + caps_avail_count); + spin_unlock(&caps_list_lock); +} + +void ceph_reservation_status(struct ceph_client *client, + int *total, int *avail, int *used, int *reserved) +{ + if (total) + *total = caps_total_count; + if (avail) + *avail = caps_avail_count; + if (used) + *used = caps_use_count; + if (reserved) + *reserved = caps_reserve_count; +} + +/* + * Find ceph_cap for given mds, if any. + * + * Called with i_lock held. + */ +static struct ceph_cap *__get_cap_for_mds(struct ceph_inode_info *ci, int mds) +{ + struct ceph_cap *cap; + struct rb_node *n = ci->i_caps.rb_node; + + while (n) { + cap = rb_entry(n, struct ceph_cap, ci_node); + if (mds < cap->mds) + n = n->rb_left; + else if (mds > cap->mds) + n = n->rb_right; + else + return cap; + } + return NULL; +} + +/* + * Return id of any MDS with a cap, preferably FILE_WR|WRBUFFER|EXCL, else + * -1. + */ +static int __ceph_get_cap_mds(struct ceph_inode_info *ci, u32 *mseq) +{ + struct ceph_cap *cap; + int mds = -1; + struct rb_node *p; + + /* prefer mds with WR|WRBUFFER|EXCL caps */ + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + cap = rb_entry(p, struct ceph_cap, ci_node); + mds = cap->mds; + if (mseq) + *mseq = cap->mseq; + if (cap->issued & (CEPH_CAP_FILE_WR | + CEPH_CAP_FILE_BUFFER | + CEPH_CAP_FILE_EXCL)) + break; + } + return mds; +} + +int ceph_get_cap_mds(struct inode *inode) +{ + int mds; + spin_lock(&inode->i_lock); + mds = __ceph_get_cap_mds(ceph_inode(inode), NULL); + spin_unlock(&inode->i_lock); + return mds; +} + +/* + * Called under i_lock. + */ +static void __insert_cap_node(struct ceph_inode_info *ci, + struct ceph_cap *new) +{ + struct rb_node **p = &ci->i_caps.rb_node; + struct rb_node *parent = NULL; + struct ceph_cap *cap = NULL; + + while (*p) { + parent = *p; + cap = rb_entry(parent, struct ceph_cap, ci_node); + if (new->mds < cap->mds) + p = &(*p)->rb_left; + else if (new->mds > cap->mds) + p = &(*p)->rb_right; + else + BUG(); + } + + rb_link_node(&new->ci_node, parent, p); + rb_insert_color(&new->ci_node, &ci->i_caps); +} + +/* + * (re)set cap hold timeouts, which control the delayed release + * of unused caps back to the MDS. Should be called on cap use. + */ +static void __cap_set_timeouts(struct ceph_mds_client *mdsc, + struct ceph_inode_info *ci) +{ + struct ceph_mount_args *ma = &mdsc->client->mount_args; + + ci->i_hold_caps_min = round_jiffies(jiffies + + ma->caps_wanted_delay_min * HZ); + ci->i_hold_caps_max = round_jiffies(jiffies + + ma->caps_wanted_delay_max * HZ); + dout("__cap_set_timeouts %p min %lu max %lu\n", &ci->vfs_inode, + ci->i_hold_caps_min - jiffies, ci->i_hold_caps_max - jiffies); +} + +/* + * (Re)queue cap at the end of the delayed cap release list. + * + * If I_FLUSH is set, leave the inode at the front of the list. + * + * Caller holds i_lock + * -> we take mdsc->cap_delay_lock + */ +static void __cap_delay_requeue(struct ceph_mds_client *mdsc, + struct ceph_inode_info *ci) +{ + __cap_set_timeouts(mdsc, ci); + dout("__cap_delay_requeue %p flags %d at %lu\n", &ci->vfs_inode, + ci->i_ceph_flags, ci->i_hold_caps_max); + if (!mdsc->stopping) { + spin_lock(&mdsc->cap_delay_lock); + if (!list_empty(&ci->i_cap_delay_list)) { + if (ci->i_ceph_flags & CEPH_I_FLUSH) + goto no_change; + list_del_init(&ci->i_cap_delay_list); + } + list_add_tail(&ci->i_cap_delay_list, &mdsc->cap_delay_list); +no_change: + spin_unlock(&mdsc->cap_delay_lock); + } +} + +/* + * Queue an inode for immediate writeback. Mark inode with I_FLUSH, + * indicating we should send a cap message to flush dirty metadata + * asap, and move to the front of the delayed cap list. + */ +static void __cap_delay_requeue_front(struct ceph_mds_client *mdsc, + struct ceph_inode_info *ci) +{ + dout("__cap_delay_requeue_front %p\n", &ci->vfs_inode); + spin_lock(&mdsc->cap_delay_lock); + ci->i_ceph_flags |= CEPH_I_FLUSH; + if (!list_empty(&ci->i_cap_delay_list)) + list_del_init(&ci->i_cap_delay_list); + list_add(&ci->i_cap_delay_list, &mdsc->cap_delay_list); + spin_unlock(&mdsc->cap_delay_lock); +} + +/* + * Cancel delayed work on cap. + * + * Caller must hold i_lock. + */ +static void __cap_delay_cancel(struct ceph_mds_client *mdsc, + struct ceph_inode_info *ci) +{ + dout("__cap_delay_cancel %p\n", &ci->vfs_inode); + if (list_empty(&ci->i_cap_delay_list)) + return; + spin_lock(&mdsc->cap_delay_lock); + list_del_init(&ci->i_cap_delay_list); + spin_unlock(&mdsc->cap_delay_lock); +} + +/* + * Common issue checks for add_cap, handle_cap_grant. + */ +static void __check_cap_issue(struct ceph_inode_info *ci, struct ceph_cap *cap, + unsigned issued) +{ + unsigned had = __ceph_caps_issued(ci, NULL); + + /* + * Each time we receive FILE_CACHE anew, we increment + * i_rdcache_gen. + */ + if ((issued & CEPH_CAP_FILE_CACHE) && + (had & CEPH_CAP_FILE_CACHE) == 0) + ci->i_rdcache_gen++; + + /* + * if we are newly issued FILE_SHARED, clear I_COMPLETE; we + * don't know what happened to this directory while we didn't + * have the cap. + */ + if ((issued & CEPH_CAP_FILE_SHARED) && + (had & CEPH_CAP_FILE_SHARED) == 0) { + ci->i_shared_gen++; + if (S_ISDIR(ci->vfs_inode.i_mode)) { + dout(" marking %p NOT complete\n", &ci->vfs_inode); + ci->i_ceph_flags &= ~CEPH_I_COMPLETE; + } + } +} + +/* + * Add a capability under the given MDS session. + * + * Caller should hold session snap_rwsem (read) and s_mutex. + * + * @fmode is the open file mode, if we are opening a file, otherwise + * it is < 0. (This is so we can atomically add the cap and add an + * open file reference to it.) + */ +int ceph_add_cap(struct inode *inode, + struct ceph_mds_session *session, u64 cap_id, + int fmode, unsigned issued, unsigned wanted, + unsigned seq, unsigned mseq, u64 realmino, int flags, + struct ceph_cap_reservation *caps_reservation) +{ + struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_cap *new_cap = NULL; + struct ceph_cap *cap; + int mds = session->s_mds; + int actual_wanted; + + dout("add_cap %p mds%d cap %llx %s seq %d\n", inode, + session->s_mds, cap_id, ceph_cap_string(issued), seq); + + /* + * If we are opening the file, include file mode wanted bits + * in wanted. + */ + if (fmode >= 0) + wanted |= ceph_caps_for_mode(fmode); + +retry: + spin_lock(&inode->i_lock); + cap = __get_cap_for_mds(ci, mds); + if (!cap) { + if (new_cap) { + cap = new_cap; + new_cap = NULL; + } else { + spin_unlock(&inode->i_lock); + new_cap = get_cap(caps_reservation); + if (new_cap == NULL) + return -ENOMEM; + goto retry; + } + + cap->issued = 0; + cap->implemented = 0; + cap->mds = mds; + cap->mds_wanted = 0; + + cap->ci = ci; + __insert_cap_node(ci, cap); + + /* clear out old exporting info? (i.e. on cap import) */ + if (ci->i_cap_exporting_mds == mds) { + ci->i_cap_exporting_issued = 0; + ci->i_cap_exporting_mseq = 0; + ci->i_cap_exporting_mds = -1; + } + + /* add to session cap list */ + cap->session = session; + spin_lock(&session->s_cap_lock); + list_add_tail(&cap->session_caps, &session->s_caps); + session->s_nr_caps++; + spin_unlock(&session->s_cap_lock); + } + + if (!ci->i_snap_realm) { + /* + * add this inode to the appropriate snap realm + */ + struct ceph_snap_realm *realm = ceph_lookup_snap_realm(mdsc, + realmino); + if (realm) { + ceph_get_snap_realm(mdsc, realm); + spin_lock(&realm->inodes_with_caps_lock); + ci->i_snap_realm = realm; + list_add(&ci->i_snap_realm_item, + &realm->inodes_with_caps); + spin_unlock(&realm->inodes_with_caps_lock); + } else { + pr_err("ceph_add_cap: couldn't find snap realm %llx\n", + realmino); + } + } + + __check_cap_issue(ci, cap, issued); + + /* + * If we are issued caps we don't want, or the mds' wanted + * value appears to be off, queue a check so we'll release + * later and/or update the mds wanted value. + */ + actual_wanted = __ceph_caps_wanted(ci); + if ((wanted & ~actual_wanted) || + (issued & ~actual_wanted & CEPH_CAP_ANY_WR)) { + dout(" issued %s, mds wanted %s, actual %s, queueing\n", + ceph_cap_string(issued), ceph_cap_string(wanted), + ceph_cap_string(actual_wanted)); + __cap_delay_requeue(mdsc, ci); + } + + if (flags & CEPH_CAP_FLAG_AUTH) + ci->i_auth_cap = cap; + else if (ci->i_auth_cap == cap) + ci->i_auth_cap = NULL; + + dout("add_cap inode %p (%llx.%llx) cap %p %s now %s seq %d mds%d\n", + inode, ceph_vinop(inode), cap, ceph_cap_string(issued), + ceph_cap_string(issued|cap->issued), seq, mds); + cap->cap_id = cap_id; + cap->issued = issued; + cap->implemented |= issued; + cap->mds_wanted |= wanted; + cap->seq = seq; + cap->issue_seq = seq; + cap->mseq = mseq; + cap->gen = session->s_cap_gen; + + if (fmode >= 0) + __ceph_get_fmode(ci, fmode); + spin_unlock(&inode->i_lock); + wake_up(&ci->i_cap_wq); + return 0; +} + +/* + * Return true if cap has not timed out and belongs to the current + * generation of the MDS session (i.e. has not gone 'stale' due to + * us losing touch with the mds). + */ +static int __cap_is_valid(struct ceph_cap *cap) +{ + unsigned long ttl; + u32 gen; + + spin_lock(&cap->session->s_cap_lock); + gen = cap->session->s_cap_gen; + ttl = cap->session->s_cap_ttl; + spin_unlock(&cap->session->s_cap_lock); + + if (cap->gen < gen || time_after_eq(jiffies, ttl)) { + dout("__cap_is_valid %p cap %p issued %s " + "but STALE (gen %u vs %u)\n", &cap->ci->vfs_inode, + cap, ceph_cap_string(cap->issued), cap->gen, gen); + return 0; + } + + return 1; +} + +/* + * Return set of valid cap bits issued to us. Note that caps time + * out, and may be invalidated in bulk if the client session times out + * and session->s_cap_gen is bumped. + */ +int __ceph_caps_issued(struct ceph_inode_info *ci, int *implemented) +{ + int have = ci->i_snap_caps; + struct ceph_cap *cap; + struct rb_node *p; + + if (implemented) + *implemented = 0; + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + cap = rb_entry(p, struct ceph_cap, ci_node); + if (!__cap_is_valid(cap)) + continue; + dout("__ceph_caps_issued %p cap %p issued %s\n", + &ci->vfs_inode, cap, ceph_cap_string(cap->issued)); + have |= cap->issued; + if (implemented) + *implemented |= cap->implemented; + } + return have; +} + +/* + * Get cap bits issued by caps other than @ocap + */ +int __ceph_caps_issued_other(struct ceph_inode_info *ci, struct ceph_cap *ocap) +{ + int have = ci->i_snap_caps; + struct ceph_cap *cap; + struct rb_node *p; + + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + cap = rb_entry(p, struct ceph_cap, ci_node); + if (cap == ocap) + continue; + if (!__cap_is_valid(cap)) + continue; + have |= cap->issued; + } + return have; +} + +/* + * Move a cap to the end of the LRU (oldest caps at list head, newest + * at list tail). + */ +static void __touch_cap(struct ceph_cap *cap) +{ + struct ceph_mds_session *s = cap->session; + + dout("__touch_cap %p cap %p mds%d\n", &cap->ci->vfs_inode, cap, + s->s_mds); + spin_lock(&s->s_cap_lock); + list_move_tail(&cap->session_caps, &s->s_caps); + spin_unlock(&s->s_cap_lock); +} + +/* + * Check if we hold the given mask. If so, move the cap(s) to the + * front of their respective LRUs. (This is the preferred way for + * callers to check for caps they want.) + */ +int __ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, int touch) +{ + struct ceph_cap *cap; + struct rb_node *p; + int have = ci->i_snap_caps; + + if ((have & mask) == mask) { + dout("__ceph_caps_issued_mask %p snap issued %s" + " (mask %s)\n", &ci->vfs_inode, + ceph_cap_string(have), + ceph_cap_string(mask)); + return 1; + } + + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + cap = rb_entry(p, struct ceph_cap, ci_node); + if (!__cap_is_valid(cap)) + continue; + if ((cap->issued & mask) == mask) { + dout("__ceph_caps_issued_mask %p cap %p issued %s" + " (mask %s)\n", &ci->vfs_inode, cap, + ceph_cap_string(cap->issued), + ceph_cap_string(mask)); + if (touch) + __touch_cap(cap); + return 1; + } + + /* does a combination of caps satisfy mask? */ + have |= cap->issued; + if ((have & mask) == mask) { + dout("__ceph_caps_issued_mask %p combo issued %s" + " (mask %s)\n", &ci->vfs_inode, + ceph_cap_string(cap->issued), + ceph_cap_string(mask)); + if (touch) { + struct rb_node *q; + + /* touch this + preceeding caps */ + __touch_cap(cap); + for (q = rb_first(&ci->i_caps); q != p; + q = rb_next(q)) { + cap = rb_entry(q, struct ceph_cap, + ci_node); + if (!__cap_is_valid(cap)) + continue; + __touch_cap(cap); + } + } + return 1; + } + } + + return 0; +} + +/* + * Return true if mask caps are currently being revoked by an MDS. + */ +int ceph_caps_revoking(struct ceph_inode_info *ci, int mask) +{ + struct inode *inode = &ci->vfs_inode; + struct ceph_cap *cap; + struct rb_node *p; + int ret = 0; + + spin_lock(&inode->i_lock); + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + cap = rb_entry(p, struct ceph_cap, ci_node); + if (__cap_is_valid(cap) && + (cap->implemented & ~cap->issued & mask)) { + ret = 1; + break; + } + } + spin_unlock(&inode->i_lock); + dout("ceph_caps_revoking %p %s = %d\n", inode, + ceph_cap_string(mask), ret); + return ret; +} + +int __ceph_caps_used(struct ceph_inode_info *ci) +{ + int used = 0; + if (ci->i_pin_ref) + used |= CEPH_CAP_PIN; + if (ci->i_rd_ref) + used |= CEPH_CAP_FILE_RD; + if (ci->i_rdcache_ref || ci->i_rdcache_gen) + used |= CEPH_CAP_FILE_CACHE; + if (ci->i_wr_ref) + used |= CEPH_CAP_FILE_WR; + if (ci->i_wrbuffer_ref) + used |= CEPH_CAP_FILE_BUFFER; + return used; +} + +/* + * wanted, by virtue of open file modes + */ +int __ceph_caps_file_wanted(struct ceph_inode_info *ci) +{ + int want = 0; + int mode; + for (mode = 0; mode < 4; mode++) + if (ci->i_nr_by_mode[mode]) + want |= ceph_caps_for_mode(mode); + return want; +} + +/* + * Return caps we have registered with the MDS(s) as 'wanted'. + */ +int __ceph_caps_mds_wanted(struct ceph_inode_info *ci) +{ + struct ceph_cap *cap; + struct rb_node *p; + int mds_wanted = 0; + + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + cap = rb_entry(p, struct ceph_cap, ci_node); + if (!__cap_is_valid(cap)) + continue; + mds_wanted |= cap->mds_wanted; + } + return mds_wanted; +} + +/* + * called under i_lock + */ +static int __ceph_is_any_caps(struct ceph_inode_info *ci) +{ + return !RB_EMPTY_ROOT(&ci->i_caps) || ci->i_cap_exporting_mds >= 0; +} + +/* + * caller should hold i_lock, and session s_mutex. + * returns true if this is the last cap. if so, caller should iput. + */ +void __ceph_remove_cap(struct ceph_cap *cap, + struct ceph_cap_reservation *ctx) +{ + struct ceph_mds_session *session = cap->session; + struct ceph_inode_info *ci = cap->ci; + struct ceph_mds_client *mdsc = &ceph_client(ci->vfs_inode.i_sb)->mdsc; + + dout("__ceph_remove_cap %p from %p\n", cap, &ci->vfs_inode); + + /* remove from session list */ + spin_lock(&session->s_cap_lock); + list_del_init(&cap->session_caps); + session->s_nr_caps--; + spin_unlock(&session->s_cap_lock); + + /* remove from inode list */ + rb_erase(&cap->ci_node, &ci->i_caps); + cap->session = NULL; + if (ci->i_auth_cap == cap) + ci->i_auth_cap = NULL; + + put_cap(cap, ctx); + + if (!__ceph_is_any_caps(ci) && ci->i_snap_realm) { + struct ceph_snap_realm *realm = ci->i_snap_realm; + spin_lock(&realm->inodes_with_caps_lock); + list_del_init(&ci->i_snap_realm_item); + ci->i_snap_realm_counter++; + ci->i_snap_realm = NULL; + spin_unlock(&realm->inodes_with_caps_lock); + ceph_put_snap_realm(mdsc, realm); + } + if (!__ceph_is_any_real_caps(ci)) + __cap_delay_cancel(mdsc, ci); +} + +/* + * Build and send a cap message to the given MDS. + * + * Caller should be holding s_mutex. + */ +static int send_cap_msg(struct ceph_mds_session *session, + u64 ino, u64 cid, int op, + int caps, int wanted, int dirty, + u32 seq, u64 flush_tid, u32 issue_seq, u32 mseq, + u64 size, u64 max_size, + struct timespec *mtime, struct timespec *atime, + u64 time_warp_seq, + uid_t uid, gid_t gid, mode_t mode, + u64 xattr_version, + struct ceph_buffer *xattrs_buf, + u64 follows) +{ + struct ceph_mds_caps *fc; + struct ceph_msg *msg; + + dout("send_cap_msg %s %llx %llx caps %s wanted %s dirty %s" + " seq %u/%u mseq %u follows %lld size %llu/%llu" + " xattr_ver %llu xattr_len %d\n", ceph_cap_op_name(op), + cid, ino, ceph_cap_string(caps), ceph_cap_string(wanted), + ceph_cap_string(dirty), + seq, issue_seq, mseq, follows, size, max_size, + xattr_version, xattrs_buf ? (int)xattrs_buf->vec.iov_len : 0); + + msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, sizeof(*fc), 0, 0, NULL); + if (IS_ERR(msg)) + return PTR_ERR(msg); + + fc = msg->front.iov_base; + + memset(fc, 0, sizeof(*fc)); + + fc->cap_id = cpu_to_le64(cid); + fc->op = cpu_to_le32(op); + fc->seq = cpu_to_le32(seq); + fc->client_tid = cpu_to_le64(flush_tid); + fc->issue_seq = cpu_to_le32(issue_seq); + fc->migrate_seq = cpu_to_le32(mseq); + fc->caps = cpu_to_le32(caps); + fc->wanted = cpu_to_le32(wanted); + fc->dirty = cpu_to_le32(dirty); + fc->ino = cpu_to_le64(ino); + fc->snap_follows = cpu_to_le64(follows); + + fc->size = cpu_to_le64(size); + fc->max_size = cpu_to_le64(max_size); + if (mtime) + ceph_encode_timespec(&fc->mtime, mtime); + if (atime) + ceph_encode_timespec(&fc->atime, atime); + fc->time_warp_seq = cpu_to_le32(time_warp_seq); + + fc->uid = cpu_to_le32(uid); + fc->gid = cpu_to_le32(gid); + fc->mode = cpu_to_le32(mode); + + fc->xattr_version = cpu_to_le64(xattr_version); + if (xattrs_buf) { + msg->middle = ceph_buffer_get(xattrs_buf); + fc->xattr_len = cpu_to_le32(xattrs_buf->vec.iov_len); + msg->hdr.middle_len = cpu_to_le32(xattrs_buf->vec.iov_len); + } + + ceph_con_send(&session->s_con, msg); + return 0; +} + +/* + * Queue cap releases when an inode is dropped from our + * cache. + */ +void ceph_queue_caps_release(struct inode *inode) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct rb_node *p; + + spin_lock(&inode->i_lock); + p = rb_first(&ci->i_caps); + while (p) { + struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); + struct ceph_mds_session *session = cap->session; + struct ceph_msg *msg; + struct ceph_mds_cap_release *head; + struct ceph_mds_cap_item *item; + + spin_lock(&session->s_cap_lock); + BUG_ON(!session->s_num_cap_releases); + msg = list_first_entry(&session->s_cap_releases, + struct ceph_msg, list_head); + + dout(" adding %p release to mds%d msg %p (%d left)\n", + inode, session->s_mds, msg, session->s_num_cap_releases); + + BUG_ON(msg->front.iov_len + sizeof(*item) > PAGE_CACHE_SIZE); + head = msg->front.iov_base; + head->num = cpu_to_le32(le32_to_cpu(head->num) + 1); + item = msg->front.iov_base + msg->front.iov_len; + item->ino = cpu_to_le64(ceph_ino(inode)); + item->cap_id = cpu_to_le64(cap->cap_id); + item->migrate_seq = cpu_to_le32(cap->mseq); + item->seq = cpu_to_le32(cap->issue_seq); + + session->s_num_cap_releases--; + + msg->front.iov_len += sizeof(*item); + if (le32_to_cpu(head->num) == CEPH_CAPS_PER_RELEASE) { + dout(" release msg %p full\n", msg); + list_move_tail(&msg->list_head, + &session->s_cap_releases_done); + } else { + dout(" release msg %p at %d/%d (%d)\n", msg, + (int)le32_to_cpu(head->num), + (int)CEPH_CAPS_PER_RELEASE, + (int)msg->front.iov_len); + } + spin_unlock(&session->s_cap_lock); + p = rb_next(p); + __ceph_remove_cap(cap, NULL); + + } + spin_unlock(&inode->i_lock); +} + +/* + * Send a cap msg on the given inode. Update our caps state, then + * drop i_lock and send the message. + * + * Make note of max_size reported/requested from mds, revoked caps + * that have now been implemented. + * + * Make half-hearted attempt ot to invalidate page cache if we are + * dropping RDCACHE. Note that this will leave behind locked pages + * that we'll then need to deal with elsewhere. + * + * Return non-zero if delayed release, or we experienced an error + * such that the caller should requeue + retry later. + * + * called with i_lock, then drops it. + * caller should hold snap_rwsem (read), s_mutex. + */ +static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, + int op, int used, int want, int retain, int flushing, + unsigned *pflush_tid) + __releases(cap->ci->vfs_inode->i_lock) +{ + struct ceph_inode_info *ci = cap->ci; + struct inode *inode = &ci->vfs_inode; + u64 cap_id = cap->cap_id; + int held = cap->issued | cap->implemented; + int revoking = cap->implemented & ~cap->issued; + int dropping = cap->issued & ~retain; + int keep; + u64 seq, issue_seq, mseq, time_warp_seq, follows; + u64 size, max_size; + struct timespec mtime, atime; + int wake = 0; + mode_t mode; + uid_t uid; + gid_t gid; + struct ceph_mds_session *session; + u64 xattr_version = 0; + int delayed = 0; + u64 flush_tid = 0; + int i; + int ret; + + dout("__send_cap %p cap %p session %p %s -> %s (revoking %s)\n", + inode, cap, cap->session, + ceph_cap_string(held), ceph_cap_string(held & retain), + ceph_cap_string(revoking)); + BUG_ON((retain & CEPH_CAP_PIN) == 0); + + session = cap->session; + + /* don't release wanted unless we've waited a bit. */ + if ((ci->i_ceph_flags & CEPH_I_NODELAY) == 0 && + time_before(jiffies, ci->i_hold_caps_min)) { + dout(" delaying issued %s -> %s, wanted %s -> %s on send\n", + ceph_cap_string(cap->issued), + ceph_cap_string(cap->issued & retain), + ceph_cap_string(cap->mds_wanted), + ceph_cap_string(want)); + want |= cap->mds_wanted; + retain |= cap->issued; + delayed = 1; + } + ci->i_ceph_flags &= ~(CEPH_I_NODELAY | CEPH_I_FLUSH); + + cap->issued &= retain; /* drop bits we don't want */ + if (cap->implemented & ~cap->issued) { + /* + * Wake up any waiters on wanted -> needed transition. + * This is due to the weird transition from buffered + * to sync IO... we need to flush dirty pages _before_ + * allowing sync writes to avoid reordering. + */ + wake = 1; + } + cap->implemented &= cap->issued | used; + cap->mds_wanted = want; + + if (flushing) { + /* + * assign a tid for flush operations so we can avoid + * flush1 -> dirty1 -> flush2 -> flushack1 -> mark + * clean type races. track latest tid for every bit + * so we can handle flush AxFw, flush Fw, and have the + * first ack clean Ax. + */ + flush_tid = ++ci->i_cap_flush_last_tid; + if (pflush_tid) + *pflush_tid = flush_tid; + dout(" cap_flush_tid %d\n", (int)flush_tid); + for (i = 0; i < CEPH_CAP_BITS; i++) + if (flushing & (1 << i)) + ci->i_cap_flush_tid[i] = flush_tid; + } + + keep = cap->implemented; + seq = cap->seq; + issue_seq = cap->issue_seq; + mseq = cap->mseq; + size = inode->i_size; + ci->i_reported_size = size; + max_size = ci->i_wanted_max_size; + ci->i_requested_max_size = max_size; + mtime = inode->i_mtime; + atime = inode->i_atime; + time_warp_seq = ci->i_time_warp_seq; + follows = ci->i_snap_realm->cached_context->seq; + uid = inode->i_uid; + gid = inode->i_gid; + mode = inode->i_mode; + + if (dropping & CEPH_CAP_XATTR_EXCL) { + __ceph_build_xattrs_blob(ci); + xattr_version = ci->i_xattrs.version + 1; + } + + spin_unlock(&inode->i_lock); + + if (dropping & CEPH_CAP_FILE_CACHE) { + /* invalidate what we can */ + dout("invalidating pages on %p\n", inode); + invalidate_mapping_pages(&inode->i_data, 0, -1); + } + + ret = send_cap_msg(session, ceph_vino(inode).ino, cap_id, + op, keep, want, flushing, seq, flush_tid, issue_seq, mseq, + size, max_size, &mtime, &atime, time_warp_seq, + uid, gid, mode, + xattr_version, + (flushing & CEPH_CAP_XATTR_EXCL) ? ci->i_xattrs.blob : NULL, + follows); + if (ret < 0) { + dout("error sending cap msg, must requeue %p\n", inode); + delayed = 1; + } + + if (wake) + wake_up(&ci->i_cap_wq); + + return delayed; +} + +/* + * When a snapshot is taken, clients accumulate dirty metadata on + * inodes with capabilities in ceph_cap_snaps to describe the file + * state at the time the snapshot was taken. This must be flushed + * asynchronously back to the MDS once sync writes complete and dirty + * data is written out. + * + * Called under i_lock. Takes s_mutex as needed. + */ +void __ceph_flush_snaps(struct ceph_inode_info *ci, + struct ceph_mds_session **psession) +{ + struct inode *inode = &ci->vfs_inode; + int mds; + struct ceph_cap_snap *capsnap; + u32 mseq; + struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_session *session = NULL; /* if session != NULL, we hold + session->s_mutex */ + u64 next_follows = 0; /* keep track of how far we've gotten through the + i_cap_snaps list, and skip these entries next time + around to avoid an infinite loop */ + + if (psession) + session = *psession; + + dout("__flush_snaps %p\n", inode); +retry: + list_for_each_entry(capsnap, &ci->i_cap_snaps, ci_item) { + /* avoid an infiniute loop after retry */ + if (capsnap->follows < next_follows) + continue; + /* + * we need to wait for sync writes to complete and for dirty + * pages to be written out. + */ + if (capsnap->dirty_pages || capsnap->writing) + continue; + + /* pick mds, take s_mutex */ + mds = __ceph_get_cap_mds(ci, &mseq); + if (session && session->s_mds != mds) { + dout("oops, wrong session %p mutex\n", session); + mutex_unlock(&session->s_mutex); + ceph_put_mds_session(session); + session = NULL; + } + if (!session) { + spin_unlock(&inode->i_lock); + mutex_lock(&mdsc->mutex); + session = __ceph_lookup_mds_session(mdsc, mds); + mutex_unlock(&mdsc->mutex); + if (session) { + dout("inverting session/ino locks on %p\n", + session); + mutex_lock(&session->s_mutex); + } + /* + * if session == NULL, we raced against a cap + * deletion. retry, and we'll get a better + * @mds value next time. + */ + spin_lock(&inode->i_lock); + goto retry; + } + + capsnap->flush_tid = ++ci->i_cap_flush_last_tid; + atomic_inc(&capsnap->nref); + if (!list_empty(&capsnap->flushing_item)) + list_del_init(&capsnap->flushing_item); + list_add_tail(&capsnap->flushing_item, + &session->s_cap_snaps_flushing); + spin_unlock(&inode->i_lock); + + dout("flush_snaps %p cap_snap %p follows %lld size %llu\n", + inode, capsnap, next_follows, capsnap->size); + send_cap_msg(session, ceph_vino(inode).ino, 0, + CEPH_CAP_OP_FLUSHSNAP, capsnap->issued, 0, + capsnap->dirty, 0, capsnap->flush_tid, 0, mseq, + capsnap->size, 0, + &capsnap->mtime, &capsnap->atime, + capsnap->time_warp_seq, + capsnap->uid, capsnap->gid, capsnap->mode, + 0, NULL, + capsnap->follows); + + next_follows = capsnap->follows + 1; + ceph_put_cap_snap(capsnap); + + spin_lock(&inode->i_lock); + goto retry; + } + + /* we flushed them all; remove this inode from the queue */ + spin_lock(&mdsc->snap_flush_lock); + list_del_init(&ci->i_snap_flush_item); + spin_unlock(&mdsc->snap_flush_lock); + + if (psession) + *psession = session; + else if (session) { + mutex_unlock(&session->s_mutex); + ceph_put_mds_session(session); + } +} + +static void ceph_flush_snaps(struct ceph_inode_info *ci) +{ + struct inode *inode = &ci->vfs_inode; + + spin_lock(&inode->i_lock); + __ceph_flush_snaps(ci, NULL); + spin_unlock(&inode->i_lock); +} + +/* + * Add dirty inode to the flushing list. Assigned a seq number so we + * can wait for caps to flush without starving. + */ +static void __mark_caps_flushing(struct inode *inode, + struct ceph_mds_session *session) +{ + struct ceph_mds_client *mdsc = &ceph_client(inode->i_sb)->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + + BUG_ON(list_empty(&ci->i_dirty_item)); + spin_lock(&mdsc->cap_dirty_lock); + if (list_empty(&ci->i_flushing_item)) { + list_add_tail(&ci->i_flushing_item, &session->s_cap_flushing); + mdsc->num_cap_flushing++; + ci->i_cap_flush_seq = ++mdsc->cap_flush_seq; + dout(" inode %p now flushing seq %lld\n", &ci->vfs_inode, + ci->i_cap_flush_seq); + } + spin_unlock(&mdsc->cap_dirty_lock); +} + +/* + * Swiss army knife function to examine currently used and wanted + * versus held caps. Release, flush, ack revoked caps to mds as + * appropriate. + * + * CHECK_CAPS_NODELAY - caller is delayed work and we should not delay + * cap release further. + * CHECK_CAPS_AUTHONLY - we should only check the auth cap + * CHECK_CAPS_FLUSH - we should flush any dirty caps immediately, without + * further delay. + */ +void ceph_check_caps(struct ceph_inode_info *ci, int flags, + struct ceph_mds_session *session) +{ + struct ceph_client *client = ceph_inode_to_client(&ci->vfs_inode); + struct ceph_mds_client *mdsc = &client->mdsc; + struct inode *inode = &ci->vfs_inode; + struct ceph_cap *cap; + int file_wanted, used; + int took_snap_rwsem = 0; /* true if mdsc->snap_rwsem held */ + int drop_session_lock = session ? 0 : 1; + int want, retain, revoking, flushing = 0; + int mds = -1; /* keep track of how far we've gone through i_caps list + to avoid an infinite loop on retry */ + struct rb_node *p; + int tried_invalidate = 0; + int delayed = 0, sent = 0, force_requeue = 0, num; + int is_delayed = flags & CHECK_CAPS_NODELAY; + + /* if we are unmounting, flush any unused caps immediately. */ + if (mdsc->stopping) + is_delayed = 1; + + spin_lock(&inode->i_lock); + + if (ci->i_ceph_flags & CEPH_I_FLUSH) + flags |= CHECK_CAPS_FLUSH; + + /* flush snaps first time around only */ + if (!list_empty(&ci->i_cap_snaps)) + __ceph_flush_snaps(ci, &session); + goto retry_locked; +retry: + spin_lock(&inode->i_lock); +retry_locked: + file_wanted = __ceph_caps_file_wanted(ci); + used = __ceph_caps_used(ci); + want = file_wanted | used; + + retain = want | CEPH_CAP_PIN; + if (!mdsc->stopping && inode->i_nlink > 0) { + if (want) { + retain |= CEPH_CAP_ANY; /* be greedy */ + } else { + retain |= CEPH_CAP_ANY_SHARED; + /* + * keep RD only if we didn't have the file open RW, + * because then the mds would revoke it anyway to + * journal max_size=0. + */ + if (ci->i_max_size == 0) + retain |= CEPH_CAP_ANY_RD; + } + } + + dout("check_caps %p file_want %s used %s dirty %s flushing %s" + " issued %s retain %s %s%s%s\n", inode, + ceph_cap_string(file_wanted), + ceph_cap_string(used), ceph_cap_string(ci->i_dirty_caps), + ceph_cap_string(ci->i_flushing_caps), + ceph_cap_string(__ceph_caps_issued(ci, NULL)), + ceph_cap_string(retain), + (flags & CHECK_CAPS_AUTHONLY) ? " AUTHONLY" : "", + (flags & CHECK_CAPS_NODELAY) ? " NODELAY" : "", + (flags & CHECK_CAPS_FLUSH) ? " FLUSH" : ""); + + /* + * If we no longer need to hold onto old our caps, and we may + * have cached pages, but don't want them, then try to invalidate. + * If we fail, it's because pages are locked.... try again later. + */ + if ((!is_delayed || mdsc->stopping) && + ci->i_wrbuffer_ref == 0 && /* no dirty pages... */ + ci->i_rdcache_gen && /* may have cached pages */ + file_wanted == 0 && /* no open files */ + !ci->i_truncate_pending && + !tried_invalidate) { + u32 invalidating_gen = ci->i_rdcache_gen; + int ret; + + dout("check_caps trying to invalidate on %p\n", inode); + spin_unlock(&inode->i_lock); + ret = invalidate_inode_pages2(&inode->i_data); + spin_lock(&inode->i_lock); + if (ret == 0 && invalidating_gen == ci->i_rdcache_gen) { + /* success. */ + ci->i_rdcache_gen = 0; + ci->i_rdcache_revoking = 0; + } else { + dout("check_caps failed to invalidate pages\n"); + /* we failed to invalidate pages. check these + caps again later. */ + force_requeue = 1; + __cap_set_timeouts(mdsc, ci); + } + tried_invalidate = 1; + goto retry_locked; + } + + num = 0; + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + cap = rb_entry(p, struct ceph_cap, ci_node); + num++; + + /* avoid looping forever */ + if (mds >= cap->mds || + ((flags & CHECK_CAPS_AUTHONLY) && cap != ci->i_auth_cap)) + continue; + + /* NOTE: no side-effects allowed, until we take s_mutex */ + + revoking = cap->implemented & ~cap->issued; + if (revoking) + dout("mds%d revoking %s\n", cap->mds, + ceph_cap_string(revoking)); + + if (cap == ci->i_auth_cap && + (cap->issued & CEPH_CAP_FILE_WR)) { + /* request larger max_size from MDS? */ + if (ci->i_wanted_max_size > ci->i_max_size && + ci->i_wanted_max_size > ci->i_requested_max_size) { + dout("requesting new max_size\n"); + goto ack; + } + + /* approaching file_max? */ + if ((inode->i_size << 1) >= ci->i_max_size && + (ci->i_reported_size << 1) < ci->i_max_size) { + dout("i_size approaching max_size\n"); + goto ack; + } + } + /* flush anything dirty? */ + if (cap == ci->i_auth_cap && (flags & CHECK_CAPS_FLUSH) && + ci->i_dirty_caps) { + dout("flushing dirty caps\n"); + goto ack; + } + + /* completed revocation? going down and there are no caps? */ + if (revoking && (revoking & used) == 0) { + dout("completed revocation of %s\n", + ceph_cap_string(cap->implemented & ~cap->issued)); + goto ack; + } + + /* want more caps from mds? */ + if (want & ~(cap->mds_wanted | cap->issued)) + goto ack; + + /* things we might delay */ + if ((cap->issued & ~retain) == 0 && + cap->mds_wanted == want) + continue; /* nope, all good */ + + if (is_delayed) + goto ack; + + /* delay? */ + if ((ci->i_ceph_flags & CEPH_I_NODELAY) == 0 && + time_before(jiffies, ci->i_hold_caps_max)) { + dout(" delaying issued %s -> %s, wanted %s -> %s\n", + ceph_cap_string(cap->issued), + ceph_cap_string(cap->issued & retain), + ceph_cap_string(cap->mds_wanted), + ceph_cap_string(want)); + delayed++; + continue; + } + +ack: + if (session && session != cap->session) { + dout("oops, wrong session %p mutex\n", session); + mutex_unlock(&session->s_mutex); + session = NULL; + } + if (!session) { + session = cap->session; + if (mutex_trylock(&session->s_mutex) == 0) { + dout("inverting session/ino locks on %p\n", + session); + spin_unlock(&inode->i_lock); + if (took_snap_rwsem) { + up_read(&mdsc->snap_rwsem); + took_snap_rwsem = 0; + } + mutex_lock(&session->s_mutex); + goto retry; + } + } + /* take snap_rwsem after session mutex */ + if (!took_snap_rwsem) { + if (down_read_trylock(&mdsc->snap_rwsem) == 0) { + dout("inverting snap/in locks on %p\n", + inode); + spin_unlock(&inode->i_lock); + down_read(&mdsc->snap_rwsem); + took_snap_rwsem = 1; + goto retry; + } + took_snap_rwsem = 1; + } + + if (cap == ci->i_auth_cap && ci->i_dirty_caps) { + /* update dirty, flushing bits */ + flushing = ci->i_dirty_caps; + dout(" flushing %s, flushing_caps %s -> %s\n", + ceph_cap_string(flushing), + ceph_cap_string(ci->i_flushing_caps), + ceph_cap_string(ci->i_flushing_caps | flushing)); + ci->i_flushing_caps |= flushing; + ci->i_dirty_caps = 0; + __mark_caps_flushing(inode, session); + } + + mds = cap->mds; /* remember mds, so we don't repeat */ + sent++; + + /* __send_cap drops i_lock */ + delayed += __send_cap(mdsc, cap, CEPH_CAP_OP_UPDATE, used, want, + retain, flushing, NULL); + goto retry; /* retake i_lock and restart our cap scan. */ + } + + /* + * Reschedule delayed caps release if we delayed anything, + * otherwise cancel. + */ + if (delayed && is_delayed) + force_requeue = 1; /* __send_cap delayed release; requeue */ + if (!delayed && !is_delayed) + __cap_delay_cancel(mdsc, ci); + else if (!is_delayed || force_requeue) + __cap_delay_requeue(mdsc, ci); + + spin_unlock(&inode->i_lock); + + if (session && drop_session_lock) + mutex_unlock(&session->s_mutex); + if (took_snap_rwsem) + up_read(&mdsc->snap_rwsem); +} + +/* + * Mark caps dirty. If inode is newly dirty, add to the global dirty + * list. + */ +int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) +{ + struct ceph_mds_client *mdsc = &ceph_client(ci->vfs_inode.i_sb)->mdsc; + struct inode *inode = &ci->vfs_inode; + int was = __ceph_caps_dirty(ci); + int dirty = 0; + + dout("__mark_dirty_caps %p %s dirty %s -> %s\n", &ci->vfs_inode, + ceph_cap_string(mask), ceph_cap_string(ci->i_dirty_caps), + ceph_cap_string(ci->i_dirty_caps | mask)); + ci->i_dirty_caps |= mask; + if (!was) { + dout(" inode %p now dirty\n", &ci->vfs_inode); + spin_lock(&mdsc->cap_dirty_lock); + list_add(&ci->i_dirty_item, &mdsc->cap_dirty); + spin_unlock(&mdsc->cap_dirty_lock); + igrab(inode); + dirty |= I_DIRTY_SYNC; + } + if ((was & CEPH_CAP_FILE_BUFFER) && + (mask & CEPH_CAP_FILE_BUFFER)) + dirty |= I_DIRTY_DATASYNC; + if (dirty) + __mark_inode_dirty(inode, dirty); + __cap_delay_requeue(mdsc, ci); + return was; +} + +/* + * Try to flush dirty caps back to the auth mds. + */ +static int try_flush_caps(struct inode *inode, struct ceph_mds_session *session, + unsigned *flush_tid) +{ + struct ceph_mds_client *mdsc = &ceph_client(inode->i_sb)->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + int unlock_session = session ? 0 : 1; + int flushing = 0; + +retry: + spin_lock(&inode->i_lock); + if (ci->i_dirty_caps && ci->i_auth_cap) { + struct ceph_cap *cap = ci->i_auth_cap; + int used = __ceph_caps_used(ci); + int want = __ceph_caps_wanted(ci); + int delayed; + + if (!session) { + spin_unlock(&inode->i_lock); + session = cap->session; + mutex_lock(&session->s_mutex); + goto retry; + } + BUG_ON(session != cap->session); + if (cap->session->s_state < CEPH_MDS_SESSION_OPEN) + goto out; + + __mark_caps_flushing(inode, session); + + flushing = ci->i_dirty_caps; + dout(" flushing %s, flushing_caps %s -> %s\n", + ceph_cap_string(flushing), + ceph_cap_string(ci->i_flushing_caps), + ceph_cap_string(ci->i_flushing_caps | flushing)); + ci->i_flushing_caps |= flushing; + ci->i_dirty_caps = 0; + + /* __send_cap drops i_lock */ + delayed = __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH, used, want, + cap->issued | cap->implemented, flushing, + flush_tid); + if (!delayed) + goto out_unlocked; + + spin_lock(&inode->i_lock); + __cap_delay_requeue(mdsc, ci); + } +out: + spin_unlock(&inode->i_lock); +out_unlocked: + if (session && unlock_session) + mutex_unlock(&session->s_mutex); + return flushing; +} + +/* + * Return true if we've flushed caps through the given flush_tid. + */ +static int caps_are_flushed(struct inode *inode, unsigned tid) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int dirty, i, ret = 1; + + spin_lock(&inode->i_lock); + dirty = __ceph_caps_dirty(ci); + for (i = 0; i < CEPH_CAP_BITS; i++) + if ((ci->i_flushing_caps & (1 << i)) && + ci->i_cap_flush_tid[i] <= tid) { + /* still flushing this bit */ + ret = 0; + break; + } + spin_unlock(&inode->i_lock); + return ret; +} + +/* + * Wait on any unsafe replies for the given inode. First wait on the + * newest request, and make that the upper bound. Then, if there are + * more requests, keep waiting on the oldest as long as it is still older + * than the original request. + */ +static void sync_write_wait(struct inode *inode) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct list_head *head = &ci->i_unsafe_writes; + struct ceph_osd_request *req; + u64 last_tid; + + spin_lock(&ci->i_unsafe_lock); + if (list_empty(head)) + goto out; + + /* set upper bound as _last_ entry in chain */ + req = list_entry(head->prev, struct ceph_osd_request, + r_unsafe_item); + last_tid = req->r_tid; + + do { + ceph_osdc_get_request(req); + spin_unlock(&ci->i_unsafe_lock); + dout("sync_write_wait on tid %llu (until %llu)\n", + req->r_tid, last_tid); + wait_for_completion(&req->r_safe_completion); + spin_lock(&ci->i_unsafe_lock); + ceph_osdc_put_request(req); + + /* + * from here on look at first entry in chain, since we + * only want to wait for anything older than last_tid + */ + if (list_empty(head)) + break; + req = list_entry(head->next, struct ceph_osd_request, + r_unsafe_item); + } while (req->r_tid < last_tid); +out: + spin_unlock(&ci->i_unsafe_lock); +} + +int ceph_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + unsigned flush_tid; + int ret; + int dirty; + + dout("fsync %p%s\n", inode, datasync ? " datasync" : ""); + sync_write_wait(inode); + + ret = filemap_write_and_wait(inode->i_mapping); + if (ret < 0) + return ret; + + dirty = try_flush_caps(inode, NULL, &flush_tid); + dout("fsync dirty caps are %s\n", ceph_cap_string(dirty)); + + /* + * only wait on non-file metadata writeback (the mds + * can recover size and mtime, so we don't need to + * wait for that) + */ + if (!datasync && (dirty & ~CEPH_CAP_ANY_FILE_WR)) { + dout("fsync waiting for flush_tid %u\n", flush_tid); + ret = wait_event_interruptible(ci->i_cap_wq, + caps_are_flushed(inode, flush_tid)); + } + + dout("fsync %p%s done\n", inode, datasync ? " datasync" : ""); + return ret; +} + +/* + * Flush any dirty caps back to the mds. If we aren't asked to wait, + * queue inode for flush but don't do so immediately, because we can + * get by with fewer MDS messages if we wait for data writeback to + * complete first. + */ +int ceph_write_inode(struct inode *inode, int wait) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + unsigned flush_tid; + int err = 0; + int dirty; + + dout("write_inode %p wait=%d\n", inode, wait); + if (wait) { + dirty = try_flush_caps(inode, NULL, &flush_tid); + if (dirty) + err = wait_event_interruptible(ci->i_cap_wq, + caps_are_flushed(inode, flush_tid)); + } else { + struct ceph_mds_client *mdsc = &ceph_client(inode->i_sb)->mdsc; + + spin_lock(&inode->i_lock); + if (__ceph_caps_dirty(ci)) + __cap_delay_requeue_front(mdsc, ci); + spin_unlock(&inode->i_lock); + } + return err; +} + +/* + * After a recovering MDS goes active, we need to resend any caps + * we were flushing. + * + * Caller holds session->s_mutex. + */ +static void kick_flushing_capsnaps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_cap_snap *capsnap; + + dout("kick_flushing_capsnaps mds%d\n", session->s_mds); + list_for_each_entry(capsnap, &session->s_cap_snaps_flushing, + flushing_item) { + struct ceph_inode_info *ci = capsnap->ci; + struct inode *inode = &ci->vfs_inode; + struct ceph_cap *cap; + + spin_lock(&inode->i_lock); + cap = ci->i_auth_cap; + if (cap && cap->session == session) { + dout("kick_flushing_caps %p cap %p capsnap %p\n", inode, + cap, capsnap); + __ceph_flush_snaps(ci, &session); + } else { + pr_err("%p auth cap %p not mds%d ???\n", inode, + cap, session->s_mds); + spin_unlock(&inode->i_lock); + } + } +} + +void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_inode_info *ci; + + kick_flushing_capsnaps(mdsc, session); + + dout("kick_flushing_caps mds%d\n", session->s_mds); + list_for_each_entry(ci, &session->s_cap_flushing, i_flushing_item) { + struct inode *inode = &ci->vfs_inode; + struct ceph_cap *cap; + int delayed = 0; + + spin_lock(&inode->i_lock); + cap = ci->i_auth_cap; + if (cap && cap->session == session) { + dout("kick_flushing_caps %p cap %p %s\n", inode, + cap, ceph_cap_string(ci->i_flushing_caps)); + delayed = __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH, + __ceph_caps_used(ci), + __ceph_caps_wanted(ci), + cap->issued | cap->implemented, + ci->i_flushing_caps, NULL); + if (delayed) { + spin_lock(&inode->i_lock); + __cap_delay_requeue(mdsc, ci); + spin_unlock(&inode->i_lock); + } + } else { + pr_err("%p auth cap %p not mds%d ???\n", inode, + cap, session->s_mds); + spin_unlock(&inode->i_lock); + } + } +} + + +/* + * Take references to capabilities we hold, so that we don't release + * them to the MDS prematurely. + * + * Protected by i_lock. + */ +static void __take_cap_refs(struct ceph_inode_info *ci, int got) +{ + if (got & CEPH_CAP_PIN) + ci->i_pin_ref++; + if (got & CEPH_CAP_FILE_RD) + ci->i_rd_ref++; + if (got & CEPH_CAP_FILE_CACHE) + ci->i_rdcache_ref++; + if (got & CEPH_CAP_FILE_WR) + ci->i_wr_ref++; + if (got & CEPH_CAP_FILE_BUFFER) { + if (ci->i_wrbuffer_ref == 0) + igrab(&ci->vfs_inode); + ci->i_wrbuffer_ref++; + dout("__take_cap_refs %p wrbuffer %d -> %d (?)\n", + &ci->vfs_inode, ci->i_wrbuffer_ref-1, ci->i_wrbuffer_ref); + } +} + +/* + * Try to grab cap references. Specify those refs we @want, and the + * minimal set we @need. Also include the larger offset we are writing + * to (when applicable), and check against max_size here as well. + * Note that caller is responsible for ensuring max_size increases are + * requested from the MDS. + */ +static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want, + int *got, loff_t endoff, int *check_max, int *err) +{ + struct inode *inode = &ci->vfs_inode; + int ret = 0; + int have, implemented; + + dout("get_cap_refs %p need %s want %s\n", inode, + ceph_cap_string(need), ceph_cap_string(want)); + spin_lock(&inode->i_lock); + + /* make sure we _have_ some caps! */ + if (!__ceph_is_any_caps(ci)) { + dout("get_cap_refs %p no real caps\n", inode); + *err = -EBADF; + ret = 1; + goto out; + } + + if (need & CEPH_CAP_FILE_WR) { + if (endoff >= 0 && endoff > (loff_t)ci->i_max_size) { + dout("get_cap_refs %p endoff %llu > maxsize %llu\n", + inode, endoff, ci->i_max_size); + if (endoff > ci->i_wanted_max_size) { + *check_max = 1; + ret = 1; + } + goto out; + } + /* + * If a sync write is in progress, we must wait, so that we + * can get a final snapshot value for size+mtime. + */ + if (__ceph_have_pending_cap_snap(ci)) { + dout("get_cap_refs %p cap_snap_pending\n", inode); + goto out; + } + } + have = __ceph_caps_issued(ci, &implemented); + + /* + * disallow writes while a truncate is pending + */ + if (ci->i_truncate_pending) + have &= ~CEPH_CAP_FILE_WR; + + if ((have & need) == need) { + /* + * Look at (implemented & ~have & not) so that we keep waiting + * on transition from wanted -> needed caps. This is needed + * for WRBUFFER|WR -> WR to avoid a new WR sync write from + * going before a prior buffered writeback happens. + */ + int not = want & ~(have & need); + int revoking = implemented & ~have; + dout("get_cap_refs %p have %s but not %s (revoking %s)\n", + inode, ceph_cap_string(have), ceph_cap_string(not), + ceph_cap_string(revoking)); + if ((revoking & not) == 0) { + *got = need | (have & want); + __take_cap_refs(ci, *got); + ret = 1; + } + } else { + dout("get_cap_refs %p have %s needed %s\n", inode, + ceph_cap_string(have), ceph_cap_string(need)); + } +out: + spin_unlock(&inode->i_lock); + dout("get_cap_refs %p ret %d got %s\n", inode, + ret, ceph_cap_string(*got)); + return ret; +} + +/* + * Check the offset we are writing up to against our current + * max_size. If necessary, tell the MDS we want to write to + * a larger offset. + */ +static void check_max_size(struct inode *inode, loff_t endoff) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int check = 0; + + /* do we need to explicitly request a larger max_size? */ + spin_lock(&inode->i_lock); + if ((endoff >= ci->i_max_size || + endoff > (inode->i_size << 1)) && + endoff > ci->i_wanted_max_size) { + dout("write %p at large endoff %llu, req max_size\n", + inode, endoff); + ci->i_wanted_max_size = endoff; + check = 1; + } + spin_unlock(&inode->i_lock); + if (check) + ceph_check_caps(ci, CHECK_CAPS_AUTHONLY, NULL); +} + +/* + * Wait for caps, and take cap references. If we can't get a WR cap + * due to a small max_size, make sure we check_max_size (and possibly + * ask the mds) so we don't get hung up indefinitely. + */ +int ceph_get_caps(struct ceph_inode_info *ci, int need, int want, int *got, + loff_t endoff) +{ + int check_max, ret, err; + +retry: + if (endoff > 0) + check_max_size(&ci->vfs_inode, endoff); + check_max = 0; + err = 0; + ret = wait_event_interruptible(ci->i_cap_wq, + try_get_cap_refs(ci, need, want, + got, endoff, + &check_max, &err)); + if (err) + ret = err; + if (check_max) + goto retry; + return ret; +} + +/* + * Take cap refs. Caller must already know we hold at least one ref + * on the caps in question or we don't know this is safe. + */ +void ceph_get_cap_refs(struct ceph_inode_info *ci, int caps) +{ + spin_lock(&ci->vfs_inode.i_lock); + __take_cap_refs(ci, caps); + spin_unlock(&ci->vfs_inode.i_lock); +} + +/* + * Release cap refs. + * + * If we released the last ref on any given cap, call ceph_check_caps + * to release (or schedule a release). + * + * If we are releasing a WR cap (from a sync write), finalize any affected + * cap_snap, and wake up any waiters. + */ +void ceph_put_cap_refs(struct ceph_inode_info *ci, int had) +{ + struct inode *inode = &ci->vfs_inode; + int last = 0, put = 0, flushsnaps = 0, wake = 0; + struct ceph_cap_snap *capsnap; + + spin_lock(&inode->i_lock); + if (had & CEPH_CAP_PIN) + --ci->i_pin_ref; + if (had & CEPH_CAP_FILE_RD) + if (--ci->i_rd_ref == 0) + last++; + if (had & CEPH_CAP_FILE_CACHE) + if (--ci->i_rdcache_ref == 0) + last++; + if (had & CEPH_CAP_FILE_BUFFER) { + if (--ci->i_wrbuffer_ref == 0) { + last++; + put++; + } + dout("put_cap_refs %p wrbuffer %d -> %d (?)\n", + inode, ci->i_wrbuffer_ref+1, ci->i_wrbuffer_ref); + } + if (had & CEPH_CAP_FILE_WR) + if (--ci->i_wr_ref == 0) { + last++; + if (!list_empty(&ci->i_cap_snaps)) { + capsnap = list_first_entry(&ci->i_cap_snaps, + struct ceph_cap_snap, + ci_item); + if (capsnap->writing) { + capsnap->writing = 0; + flushsnaps = + __ceph_finish_cap_snap(ci, + capsnap); + wake = 1; + } + } + } + spin_unlock(&inode->i_lock); + + dout("put_cap_refs %p had %s %s\n", inode, ceph_cap_string(had), + last ? "last" : ""); + + if (last && !flushsnaps) + ceph_check_caps(ci, 0, NULL); + else if (flushsnaps) + ceph_flush_snaps(ci); + if (wake) + wake_up(&ci->i_cap_wq); + if (put) + iput(inode); +} + +/* + * Release @nr WRBUFFER refs on dirty pages for the given @snapc snap + * context. Adjust per-snap dirty page accounting as appropriate. + * Once all dirty data for a cap_snap is flushed, flush snapped file + * metadata back to the MDS. If we dropped the last ref, call + * ceph_check_caps. + */ +void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr, + struct ceph_snap_context *snapc) +{ + struct inode *inode = &ci->vfs_inode; + int last = 0; + int last_snap = 0; + int found = 0; + struct ceph_cap_snap *capsnap = NULL; + + spin_lock(&inode->i_lock); + ci->i_wrbuffer_ref -= nr; + last = !ci->i_wrbuffer_ref; + + if (ci->i_head_snapc == snapc) { + ci->i_wrbuffer_ref_head -= nr; + if (!ci->i_wrbuffer_ref_head) { + ceph_put_snap_context(ci->i_head_snapc); + ci->i_head_snapc = NULL; + } + dout("put_wrbuffer_cap_refs on %p head %d/%d -> %d/%d %s\n", + inode, + ci->i_wrbuffer_ref+nr, ci->i_wrbuffer_ref_head+nr, + ci->i_wrbuffer_ref, ci->i_wrbuffer_ref_head, + last ? " LAST" : ""); + } else { + list_for_each_entry(capsnap, &ci->i_cap_snaps, ci_item) { + if (capsnap->context == snapc) { + found = 1; + capsnap->dirty_pages -= nr; + last_snap = !capsnap->dirty_pages; + break; + } + } + BUG_ON(!found); + dout("put_wrbuffer_cap_refs on %p cap_snap %p " + " snap %lld %d/%d -> %d/%d %s%s\n", + inode, capsnap, capsnap->context->seq, + ci->i_wrbuffer_ref+nr, capsnap->dirty_pages + nr, + ci->i_wrbuffer_ref, capsnap->dirty_pages, + last ? " (wrbuffer last)" : "", + last_snap ? " (capsnap last)" : ""); + } + + spin_unlock(&inode->i_lock); + + if (last) { + ceph_check_caps(ci, CHECK_CAPS_AUTHONLY, NULL); + iput(inode); + } else if (last_snap) { + ceph_flush_snaps(ci); + wake_up(&ci->i_cap_wq); + } +} + +/* + * Handle a cap GRANT message from the MDS. (Note that a GRANT may + * actually be a revocation if it specifies a smaller cap set.) + * + * caller holds s_mutex. + * return value: + * 0 - ok + * 1 - check_caps on auth cap only (writeback) + * 2 - check_caps (ack revoke) + */ +static int handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, + struct ceph_mds_session *session, + struct ceph_cap *cap, + struct ceph_buffer *xattr_buf) + __releases(inode->i_lock) + +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int mds = session->s_mds; + int seq = le32_to_cpu(grant->seq); + int newcaps = le32_to_cpu(grant->caps); + int issued, implemented, used, wanted, dirty; + u64 size = le64_to_cpu(grant->size); + u64 max_size = le64_to_cpu(grant->max_size); + struct timespec mtime, atime, ctime; + int reply = 0; + int wake = 0; + int writeback = 0; + int revoked_rdcache = 0; + int invalidate_async = 0; + int tried_invalidate = 0; + int ret; + + dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n", + inode, cap, mds, seq, ceph_cap_string(newcaps)); + dout(" size %llu max_size %llu, i_size %llu\n", size, max_size, + inode->i_size); + + /* + * If CACHE is being revoked, and we have no dirty buffers, + * try to invalidate (once). (If there are dirty buffers, we + * will invalidate _after_ writeback.) + */ +restart: + if (((cap->issued & ~newcaps) & CEPH_CAP_FILE_CACHE) && + !ci->i_wrbuffer_ref && !tried_invalidate) { + dout("CACHE invalidation\n"); + spin_unlock(&inode->i_lock); + tried_invalidate = 1; + + ret = invalidate_inode_pages2(&inode->i_data); + spin_lock(&inode->i_lock); + if (ret < 0) { + /* there were locked pages.. invalidate later + in a separate thread. */ + if (ci->i_rdcache_revoking != ci->i_rdcache_gen) { + invalidate_async = 1; + ci->i_rdcache_revoking = ci->i_rdcache_gen; + } + } else { + /* we successfully invalidated those pages */ + revoked_rdcache = 1; + ci->i_rdcache_gen = 0; + ci->i_rdcache_revoking = 0; + } + goto restart; + } + + /* side effects now are allowed */ + + issued = __ceph_caps_issued(ci, &implemented); + issued |= implemented | __ceph_caps_dirty(ci); + + cap->gen = session->s_cap_gen; + + __check_cap_issue(ci, cap, newcaps); + + if ((issued & CEPH_CAP_AUTH_EXCL) == 0) { + inode->i_mode = le32_to_cpu(grant->mode); + inode->i_uid = le32_to_cpu(grant->uid); + inode->i_gid = le32_to_cpu(grant->gid); + dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, + inode->i_uid, inode->i_gid); + } + + if ((issued & CEPH_CAP_LINK_EXCL) == 0) + inode->i_nlink = le32_to_cpu(grant->nlink); + + if ((issued & CEPH_CAP_XATTR_EXCL) == 0 && grant->xattr_len) { + int len = le32_to_cpu(grant->xattr_len); + u64 version = le64_to_cpu(grant->xattr_version); + + if (version > ci->i_xattrs.version) { + dout(" got new xattrs v%llu on %p len %d\n", + version, inode, len); + if (ci->i_xattrs.blob) + ceph_buffer_put(ci->i_xattrs.blob); + ci->i_xattrs.blob = ceph_buffer_get(xattr_buf); + ci->i_xattrs.version = version; + } + } + + /* size/ctime/mtime/atime? */ + ceph_fill_file_size(inode, issued, + le32_to_cpu(grant->truncate_seq), + le64_to_cpu(grant->truncate_size), size); + ceph_decode_timespec(&mtime, &grant->mtime); + ceph_decode_timespec(&atime, &grant->atime); + ceph_decode_timespec(&ctime, &grant->ctime); + ceph_fill_file_time(inode, issued, + le32_to_cpu(grant->time_warp_seq), &ctime, &mtime, + &atime); + + /* max size increase? */ + if (max_size != ci->i_max_size) { + dout("max_size %lld -> %llu\n", ci->i_max_size, max_size); + ci->i_max_size = max_size; + if (max_size >= ci->i_wanted_max_size) { + ci->i_wanted_max_size = 0; /* reset */ + ci->i_requested_max_size = 0; + } + wake = 1; + } + + /* check cap bits */ + wanted = __ceph_caps_wanted(ci); + used = __ceph_caps_used(ci); + dirty = __ceph_caps_dirty(ci); + dout(" my wanted = %s, used = %s, dirty %s\n", + ceph_cap_string(wanted), + ceph_cap_string(used), + ceph_cap_string(dirty)); + if (wanted != le32_to_cpu(grant->wanted)) { + dout("mds wanted %s -> %s\n", + ceph_cap_string(le32_to_cpu(grant->wanted)), + ceph_cap_string(wanted)); + grant->wanted = cpu_to_le32(wanted); + } + + cap->seq = seq; + + /* file layout may have changed */ + ci->i_layout = grant->layout; + + /* revocation, grant, or no-op? */ + if (cap->issued & ~newcaps) { + dout("revocation: %s -> %s\n", ceph_cap_string(cap->issued), + ceph_cap_string(newcaps)); + if ((used & ~newcaps) & CEPH_CAP_FILE_BUFFER) + writeback = 1; /* will delay ack */ + else if (dirty & ~newcaps) + reply = 1; /* initiate writeback in check_caps */ + else if (((used & ~newcaps) & CEPH_CAP_FILE_CACHE) == 0 || + revoked_rdcache) + reply = 2; /* send revoke ack in check_caps */ + cap->issued = newcaps; + } else if (cap->issued == newcaps) { + dout("caps unchanged: %s -> %s\n", + ceph_cap_string(cap->issued), ceph_cap_string(newcaps)); + } else { + dout("grant: %s -> %s\n", ceph_cap_string(cap->issued), + ceph_cap_string(newcaps)); + cap->issued = newcaps; + cap->implemented |= newcaps; /* add bits only, to + * avoid stepping on a + * pending revocation */ + wake = 1; + } + + spin_unlock(&inode->i_lock); + if (writeback) { + /* + * queue inode for writeback: we can't actually call + * filemap_write_and_wait, etc. from message handler + * context. + */ + dout("queueing %p for writeback\n", inode); + if (ceph_queue_writeback(inode)) + igrab(inode); + } + if (invalidate_async) { + dout("queueing %p for page invalidation\n", inode); + if (ceph_queue_page_invalidation(inode)) + igrab(inode); + } + if (wake) + wake_up(&ci->i_cap_wq); + return reply; +} + +/* + * Handle FLUSH_ACK from MDS, indicating that metadata we sent to the + * MDS has been safely committed. + */ +static void handle_cap_flush_ack(struct inode *inode, + struct ceph_mds_caps *m, + struct ceph_mds_session *session, + struct ceph_cap *cap) + __releases(inode->i_lock) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = &ceph_client(inode->i_sb)->mdsc; + unsigned seq = le32_to_cpu(m->seq); + int dirty = le32_to_cpu(m->dirty); + int cleaned = 0; + u64 flush_tid = le64_to_cpu(m->client_tid); + int old_dirty = 0, new_dirty = 0; + int i; + + for (i = 0; i < CEPH_CAP_BITS; i++) + if ((dirty & (1 << i)) && + flush_tid == ci->i_cap_flush_tid[i]) + cleaned |= 1 << i; + + dout("handle_cap_flush_ack inode %p mds%d seq %d on %s cleaned %s," + " flushing %s -> %s\n", + inode, session->s_mds, seq, ceph_cap_string(dirty), + ceph_cap_string(cleaned), ceph_cap_string(ci->i_flushing_caps), + ceph_cap_string(ci->i_flushing_caps & ~cleaned)); + + if (ci->i_flushing_caps == (ci->i_flushing_caps & ~cleaned)) + goto out; + + old_dirty = ci->i_dirty_caps | ci->i_flushing_caps; + ci->i_flushing_caps &= ~cleaned; + new_dirty = ci->i_dirty_caps | ci->i_flushing_caps; + + spin_lock(&mdsc->cap_dirty_lock); + if (ci->i_flushing_caps == 0) { + list_del_init(&ci->i_flushing_item); + if (!list_empty(&session->s_cap_flushing)) + dout(" mds%d still flushing cap on %p\n", + session->s_mds, + &list_entry(session->s_cap_flushing.next, + struct ceph_inode_info, + i_flushing_item)->vfs_inode); + mdsc->num_cap_flushing--; + wake_up(&mdsc->cap_flushing_wq); + dout(" inode %p now !flushing\n", inode); + } + if (old_dirty && !new_dirty) { + dout(" inode %p now clean\n", inode); + list_del_init(&ci->i_dirty_item); + } + spin_unlock(&mdsc->cap_dirty_lock); + wake_up(&ci->i_cap_wq); + +out: + spin_unlock(&inode->i_lock); + if (old_dirty && !new_dirty) + iput(inode); +} + +/* + * Handle FLUSHSNAP_ACK. MDS has flushed snap data to disk and we can + * throw away our cap_snap. + * + * Caller hold s_mutex. + */ +static void handle_cap_flushsnap_ack(struct inode *inode, + struct ceph_mds_caps *m, + struct ceph_mds_session *session) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + u64 follows = le64_to_cpu(m->snap_follows); + u64 flush_tid = le64_to_cpu(m->client_tid); + struct ceph_cap_snap *capsnap; + int drop = 0; + + dout("handle_cap_flushsnap_ack inode %p ci %p mds%d follows %lld\n", + inode, ci, session->s_mds, follows); + + spin_lock(&inode->i_lock); + list_for_each_entry(capsnap, &ci->i_cap_snaps, ci_item) { + if (capsnap->follows == follows) { + if (capsnap->flush_tid != flush_tid) { + dout(" cap_snap %p follows %lld tid %lld !=" + " %lld\n", capsnap, follows, + flush_tid, capsnap->flush_tid); + break; + } + WARN_ON(capsnap->dirty_pages || capsnap->writing); + dout(" removing cap_snap %p follows %lld\n", + capsnap, follows); + ceph_put_snap_context(capsnap->context); + list_del(&capsnap->ci_item); + list_del(&capsnap->flushing_item); + ceph_put_cap_snap(capsnap); + drop = 1; + break; + } else { + dout(" skipping cap_snap %p follows %lld\n", + capsnap, capsnap->follows); + } + } + spin_unlock(&inode->i_lock); + if (drop) + iput(inode); +} + +/* + * Handle TRUNC from MDS, indicating file truncation. + * + * caller hold s_mutex. + */ +static void handle_cap_trunc(struct inode *inode, + struct ceph_mds_caps *trunc, + struct ceph_mds_session *session) + __releases(inode->i_lock) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int mds = session->s_mds; + int seq = le32_to_cpu(trunc->seq); + u32 truncate_seq = le32_to_cpu(trunc->truncate_seq); + u64 truncate_size = le64_to_cpu(trunc->truncate_size); + u64 size = le64_to_cpu(trunc->size); + int implemented = 0; + int dirty = __ceph_caps_dirty(ci); + int issued = __ceph_caps_issued(ceph_inode(inode), &implemented); + int queue_trunc = 0; + + issued |= implemented | dirty; + + dout("handle_cap_trunc inode %p mds%d seq %d to %lld seq %d\n", + inode, mds, seq, truncate_size, truncate_seq); + queue_trunc = ceph_fill_file_size(inode, issued, + truncate_seq, truncate_size, size); + spin_unlock(&inode->i_lock); + + if (queue_trunc) + if (queue_work(ceph_client(inode->i_sb)->trunc_wq, + &ci->i_vmtruncate_work)) + igrab(inode); +} + +/* + * Handle EXPORT from MDS. Cap is being migrated _from_ this mds to a + * different one. If we are the most recent migration we've seen (as + * indicated by mseq), make note of the migrating cap bits for the + * duration (until we see the corresponding IMPORT). + * + * caller holds s_mutex + */ +static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, + struct ceph_mds_session *session) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int mds = session->s_mds; + unsigned mseq = le32_to_cpu(ex->migrate_seq); + struct ceph_cap *cap = NULL, *t; + struct rb_node *p; + int remember = 1; + + dout("handle_cap_export inode %p ci %p mds%d mseq %d\n", + inode, ci, mds, mseq); + + spin_lock(&inode->i_lock); + + /* make sure we haven't seen a higher mseq */ + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + t = rb_entry(p, struct ceph_cap, ci_node); + if (ceph_seq_cmp(t->mseq, mseq) > 0) { + dout(" higher mseq on cap from mds%d\n", + t->session->s_mds); + remember = 0; + } + if (t->session->s_mds == mds) + cap = t; + } + + if (cap) { + if (remember) { + /* make note */ + ci->i_cap_exporting_mds = mds; + ci->i_cap_exporting_mseq = mseq; + ci->i_cap_exporting_issued = cap->issued; + } + __ceph_remove_cap(cap, NULL); + } else { + WARN_ON(!cap); + } + + spin_unlock(&inode->i_lock); +} + +/* + * Handle cap IMPORT. If there are temp bits from an older EXPORT, + * clean them up. + * + * caller holds s_mutex. + */ +static void handle_cap_import(struct ceph_mds_client *mdsc, + struct inode *inode, struct ceph_mds_caps *im, + struct ceph_mds_session *session, + void *snaptrace, int snaptrace_len) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int mds = session->s_mds; + unsigned issued = le32_to_cpu(im->caps); + unsigned wanted = le32_to_cpu(im->wanted); + unsigned seq = le32_to_cpu(im->seq); + unsigned mseq = le32_to_cpu(im->migrate_seq); + u64 realmino = le64_to_cpu(im->realm); + u64 cap_id = le64_to_cpu(im->cap_id); + + if (ci->i_cap_exporting_mds >= 0 && + ceph_seq_cmp(ci->i_cap_exporting_mseq, mseq) < 0) { + dout("handle_cap_import inode %p ci %p mds%d mseq %d" + " - cleared exporting from mds%d\n", + inode, ci, mds, mseq, + ci->i_cap_exporting_mds); + ci->i_cap_exporting_issued = 0; + ci->i_cap_exporting_mseq = 0; + ci->i_cap_exporting_mds = -1; + } else { + dout("handle_cap_import inode %p ci %p mds%d mseq %d\n", + inode, ci, mds, mseq); + } + + down_write(&mdsc->snap_rwsem); + ceph_update_snap_trace(mdsc, snaptrace, snaptrace+snaptrace_len, + false); + downgrade_write(&mdsc->snap_rwsem); + ceph_add_cap(inode, session, cap_id, -1, + issued, wanted, seq, mseq, realmino, CEPH_CAP_FLAG_AUTH, + NULL /* no caps context */); + try_flush_caps(inode, session, NULL); + up_read(&mdsc->snap_rwsem); +} + +/* + * Handle a caps message from the MDS. + * + * Identify the appropriate session, inode, and call the right handler + * based on the cap op. + */ +void ceph_handle_caps(struct ceph_mds_session *session, + struct ceph_msg *msg) +{ + struct ceph_mds_client *mdsc = session->s_mdsc; + struct super_block *sb = mdsc->client->sb; + struct inode *inode; + struct ceph_cap *cap; + struct ceph_mds_caps *h; + int mds = le64_to_cpu(msg->hdr.src.name.num); + int op; + u32 seq; + struct ceph_vino vino; + u64 cap_id; + u64 size, max_size; + int check_caps = 0; + int r; + + dout("handle_caps from mds%d\n", mds); + + /* decode */ + if (msg->front.iov_len < sizeof(*h)) + goto bad; + h = msg->front.iov_base; + op = le32_to_cpu(h->op); + vino.ino = le64_to_cpu(h->ino); + vino.snap = CEPH_NOSNAP; + cap_id = le64_to_cpu(h->cap_id); + seq = le32_to_cpu(h->seq); + size = le64_to_cpu(h->size); + max_size = le64_to_cpu(h->max_size); + + mutex_lock(&session->s_mutex); + session->s_seq++; + dout(" mds%d seq %lld cap seq %u\n", session->s_mds, session->s_seq, + (unsigned)seq); + + /* lookup ino */ + inode = ceph_find_inode(sb, vino); + dout(" op %s ino %llx.%llx inode %p\n", ceph_cap_op_name(op), vino.ino, + vino.snap, inode); + if (!inode) { + dout(" i don't have ino %llx\n", vino.ino); + goto done; + } + + /* these will work even if we don't have a cap yet */ + switch (op) { + case CEPH_CAP_OP_FLUSHSNAP_ACK: + handle_cap_flushsnap_ack(inode, h, session); + goto done; + + case CEPH_CAP_OP_EXPORT: + handle_cap_export(inode, h, session); + goto done; + + case CEPH_CAP_OP_IMPORT: + handle_cap_import(mdsc, inode, h, session, + msg->middle, + le32_to_cpu(h->snap_trace_len)); + check_caps = 1; /* we may have sent a RELEASE to the old auth */ + goto done; + } + + /* the rest require a cap */ + spin_lock(&inode->i_lock); + cap = __get_cap_for_mds(ceph_inode(inode), mds); + if (!cap) { + dout("no cap on %p ino %llx.%llx from mds%d, releasing\n", + inode, ceph_ino(inode), ceph_snap(inode), mds); + spin_unlock(&inode->i_lock); + goto done; + } + + /* note that each of these drops i_lock for us */ + switch (op) { + case CEPH_CAP_OP_REVOKE: + case CEPH_CAP_OP_GRANT: + r = handle_cap_grant(inode, h, session, cap, msg->middle); + if (r == 1) + ceph_check_caps(ceph_inode(inode), + CHECK_CAPS_NODELAY|CHECK_CAPS_AUTHONLY, + session); + else if (r == 2) + ceph_check_caps(ceph_inode(inode), + CHECK_CAPS_NODELAY, + session); + break; + + case CEPH_CAP_OP_FLUSH_ACK: + handle_cap_flush_ack(inode, h, session, cap); + break; + + case CEPH_CAP_OP_TRUNC: + handle_cap_trunc(inode, h, session); + break; + + default: + spin_unlock(&inode->i_lock); + pr_err("ceph_handle_caps: unknown cap op %d %s\n", op, + ceph_cap_op_name(op)); + } + +done: + mutex_unlock(&session->s_mutex); + + if (check_caps) + ceph_check_caps(ceph_inode(inode), CHECK_CAPS_NODELAY, NULL); + if (inode) + iput(inode); + return; + +bad: + pr_err("ceph_handle_caps: corrupt message\n"); + return; +} + +/* + * Delayed work handler to process end of delayed cap release LRU list. + */ +void ceph_check_delayed_caps(struct ceph_mds_client *mdsc, int flushdirty) +{ + struct ceph_inode_info *ci; + int flags = CHECK_CAPS_NODELAY; + + if (flushdirty) + flags |= CHECK_CAPS_FLUSH; + + dout("check_delayed_caps\n"); + while (1) { + spin_lock(&mdsc->cap_delay_lock); + if (list_empty(&mdsc->cap_delay_list)) + break; + ci = list_first_entry(&mdsc->cap_delay_list, + struct ceph_inode_info, + i_cap_delay_list); + if ((ci->i_ceph_flags & CEPH_I_FLUSH) == 0 && + time_before(jiffies, ci->i_hold_caps_max)) + break; + list_del_init(&ci->i_cap_delay_list); + spin_unlock(&mdsc->cap_delay_lock); + dout("check_delayed_caps on %p\n", &ci->vfs_inode); + ceph_check_caps(ci, flags, NULL); + } + spin_unlock(&mdsc->cap_delay_lock); +} + +/* + * Drop open file reference. If we were the last open file, + * we may need to release capabilities to the MDS (or schedule + * their delayed release). + */ +void ceph_put_fmode(struct ceph_inode_info *ci, int fmode) +{ + struct inode *inode = &ci->vfs_inode; + int last = 0; + + spin_lock(&inode->i_lock); + dout("put_fmode %p fmode %d %d -> %d\n", inode, fmode, + ci->i_nr_by_mode[fmode], ci->i_nr_by_mode[fmode]-1); + BUG_ON(ci->i_nr_by_mode[fmode] == 0); + if (--ci->i_nr_by_mode[fmode] == 0) + last++; + spin_unlock(&inode->i_lock); + + if (last && ci->i_vino.snap == CEPH_NOSNAP) + ceph_check_caps(ci, 0, NULL); +} + +/* + * Helpers for embedding cap and dentry lease releases into mds + * requests. + * + * @force is used by dentry_release (below) to force inclusion of a + * record for the directory inode, even when there aren't any caps to + * drop. + */ +int ceph_encode_inode_release(void **p, struct inode *inode, + int mds, int drop, int unless, int force) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_cap *cap; + struct ceph_mds_request_release *rel = *p; + int ret = 0; + + dout("encode_inode_release %p mds%d drop %s unless %s\n", inode, + mds, ceph_cap_string(drop), ceph_cap_string(unless)); + + spin_lock(&inode->i_lock); + cap = __get_cap_for_mds(ci, mds); + if (cap && __cap_is_valid(cap)) { + if (force || + ((cap->issued & drop) && + (cap->issued & unless) == 0)) { + if ((cap->issued & drop) && + (cap->issued & unless) == 0) { + dout("encode_inode_release %p cap %p %s -> " + "%s\n", inode, cap, + ceph_cap_string(cap->issued), + ceph_cap_string(cap->issued & ~drop)); + cap->issued &= ~drop; + cap->implemented &= ~drop; + if (ci->i_ceph_flags & CEPH_I_NODELAY) { + int wanted = __ceph_caps_wanted(ci); + dout(" wanted %s -> %s (act %s)\n", + ceph_cap_string(cap->mds_wanted), + ceph_cap_string(cap->mds_wanted & + ~wanted), + ceph_cap_string(wanted)); + cap->mds_wanted &= wanted; + } + } else { + dout("encode_inode_release %p cap %p %s" + " (force)\n", inode, cap, + ceph_cap_string(cap->issued)); + } + + rel->ino = cpu_to_le64(ceph_ino(inode)); + rel->cap_id = cpu_to_le64(cap->cap_id); + rel->seq = cpu_to_le32(cap->seq); + rel->issue_seq = cpu_to_le32(cap->issue_seq), + rel->mseq = cpu_to_le32(cap->mseq); + rel->caps = cpu_to_le32(cap->issued); + rel->wanted = cpu_to_le32(cap->mds_wanted); + rel->dname_len = 0; + rel->dname_seq = 0; + *p += sizeof(*rel); + ret = 1; + } else { + dout("encode_inode_release %p cap %p %s\n", + inode, cap, ceph_cap_string(cap->issued)); + } + } + spin_unlock(&inode->i_lock); + return ret; +} + +int ceph_encode_dentry_release(void **p, struct dentry *dentry, + int mds, int drop, int unless) +{ + struct inode *dir = dentry->d_parent->d_inode; + struct ceph_mds_request_release *rel = *p; + struct ceph_dentry_info *di = ceph_dentry(dentry); + int force = 0; + int ret; + + /* + * force an record for the directory caps if we have a dentry lease. + * this is racy (can't take i_lock and d_lock together), but it + * doesn't have to be perfect; the mds will revoke anything we don't + * release. + */ + spin_lock(&dentry->d_lock); + if (di->lease_session && di->lease_session->s_mds == mds) + force = 1; + spin_unlock(&dentry->d_lock); + + ret = ceph_encode_inode_release(p, dir, mds, drop, unless, force); + + spin_lock(&dentry->d_lock); + if (ret && di->lease_session && di->lease_session->s_mds == mds) { + dout("encode_dentry_release %p mds%d seq %d\n", + dentry, mds, (int)di->lease_seq); + rel->dname_len = cpu_to_le32(dentry->d_name.len); + memcpy(*p, dentry->d_name.name, dentry->d_name.len); + *p += dentry->d_name.len; + rel->dname_seq = cpu_to_le32(di->lease_seq); + } + spin_unlock(&dentry->d_lock); + return ret; +} -- cgit v0.10.2 From 963b61eb041e8850807d95f8d7a4c6a454c45000 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:12 -0700 Subject: ceph: snapshot management Ceph snapshots rely on client cooperation in determining which operations apply to which snapshots, and appropriately flushing snapshotted data and metadata back to the OSD and MDS clusters. Because snapshots apply to subtrees of the file hierarchy and can be created at any time, there is a fair bit of bookkeeping required to make this work. Portions of the hierarchy that belong to the same set of snapshots are described by a single 'snap realm.' A 'snap context' describes the set of snapshots that exist for a given file or directory. Signed-off-by: Sage Weil diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c new file mode 100644 index 0000000..2e3cb40 --- /dev/null +++ b/fs/ceph/snap.c @@ -0,0 +1,897 @@ +#include "ceph_debug.h" + +#include +#include + +#include "super.h" +#include "decode.h" + +/* + * Snapshots in ceph are driven in large part by cooperation from the + * client. In contrast to local file systems or file servers that + * implement snapshots at a single point in the system, ceph's + * distributed access to storage requires clients to help decide + * whether a write logically occurs before or after a recently created + * snapshot. + * + * This provides a perfect instantanous client-wide snapshot. Between + * clients, however, snapshots may appear to be applied at slightly + * different points in time, depending on delays in delivering the + * snapshot notification. + * + * Snapshots are _not_ file system-wide. Instead, each snapshot + * applies to the subdirectory nested beneath some directory. This + * effectively divides the hierarchy into multiple "realms," where all + * of the files contained by each realm share the same set of + * snapshots. An individual realm's snap set contains snapshots + * explicitly created on that realm, as well as any snaps in its + * parent's snap set _after_ the point at which the parent became it's + * parent (due to, say, a rename). Similarly, snaps from prior parents + * during the time intervals during which they were the parent are included. + * + * The client is spared most of this detail, fortunately... it must only + * maintains a hierarchy of realms reflecting the current parent/child + * realm relationship, and for each realm has an explicit list of snaps + * inherited from prior parents. + * + * A snap_realm struct is maintained for realms containing every inode + * with an open cap in the system. (The needed snap realm information is + * provided by the MDS whenever a cap is issued, i.e., on open.) A 'seq' + * version number is used to ensure that as realm parameters change (new + * snapshot, new parent, etc.) the client's realm hierarchy is updated. + * + * The realm hierarchy drives the generation of a 'snap context' for each + * realm, which simply lists the resulting set of snaps for the realm. This + * is attached to any writes sent to OSDs. + */ +/* + * Unfortunately error handling is a bit mixed here. If we get a snap + * update, but don't have enough memory to update our realm hierarchy, + * it's not clear what we can do about it (besides complaining to the + * console). + */ + + +/* + * increase ref count for the realm + * + * caller must hold snap_rwsem for write. + */ +void ceph_get_snap_realm(struct ceph_mds_client *mdsc, + struct ceph_snap_realm *realm) +{ + dout("get_realm %p %d -> %d\n", realm, + atomic_read(&realm->nref), atomic_read(&realm->nref)+1); + /* + * since we _only_ increment realm refs or empty the empty + * list with snap_rwsem held, adjusting the empty list here is + * safe. we do need to protect against concurrent empty list + * additions, however. + */ + if (atomic_read(&realm->nref) == 0) { + spin_lock(&mdsc->snap_empty_lock); + list_del_init(&realm->empty_item); + spin_unlock(&mdsc->snap_empty_lock); + } + + atomic_inc(&realm->nref); +} + +/* + * create and get the realm rooted at @ino and bump its ref count. + * + * caller must hold snap_rwsem for write. + */ +static struct ceph_snap_realm *ceph_create_snap_realm( + struct ceph_mds_client *mdsc, + u64 ino) +{ + struct ceph_snap_realm *realm; + + realm = kzalloc(sizeof(*realm), GFP_NOFS); + if (!realm) + return ERR_PTR(-ENOMEM); + + radix_tree_insert(&mdsc->snap_realms, ino, realm); + + atomic_set(&realm->nref, 0); /* tree does not take a ref */ + realm->ino = ino; + INIT_LIST_HEAD(&realm->children); + INIT_LIST_HEAD(&realm->child_item); + INIT_LIST_HEAD(&realm->empty_item); + INIT_LIST_HEAD(&realm->inodes_with_caps); + spin_lock_init(&realm->inodes_with_caps_lock); + dout("create_snap_realm %llx %p\n", realm->ino, realm); + return realm; +} + +/* + * find and get (if found) the realm rooted at @ino and bump its ref count. + * + * caller must hold snap_rwsem for write. + */ +struct ceph_snap_realm *ceph_lookup_snap_realm(struct ceph_mds_client *mdsc, + u64 ino) +{ + struct ceph_snap_realm *realm; + + realm = radix_tree_lookup(&mdsc->snap_realms, ino); + if (realm) + dout("lookup_snap_realm %llx %p\n", realm->ino, realm); + return realm; +} + +static void __put_snap_realm(struct ceph_mds_client *mdsc, + struct ceph_snap_realm *realm); + +/* + * called with snap_rwsem (write) + */ +static void __destroy_snap_realm(struct ceph_mds_client *mdsc, + struct ceph_snap_realm *realm) +{ + dout("__destroy_snap_realm %p %llx\n", realm, realm->ino); + + radix_tree_delete(&mdsc->snap_realms, realm->ino); + + if (realm->parent) { + list_del_init(&realm->child_item); + __put_snap_realm(mdsc, realm->parent); + } + + kfree(realm->prior_parent_snaps); + kfree(realm->snaps); + ceph_put_snap_context(realm->cached_context); + kfree(realm); +} + +/* + * caller holds snap_rwsem (write) + */ +static void __put_snap_realm(struct ceph_mds_client *mdsc, + struct ceph_snap_realm *realm) +{ + dout("__put_snap_realm %llx %p %d -> %d\n", realm->ino, realm, + atomic_read(&realm->nref), atomic_read(&realm->nref)-1); + if (atomic_dec_and_test(&realm->nref)) + __destroy_snap_realm(mdsc, realm); +} + +/* + * caller needn't hold any locks + */ +void ceph_put_snap_realm(struct ceph_mds_client *mdsc, + struct ceph_snap_realm *realm) +{ + dout("put_snap_realm %llx %p %d -> %d\n", realm->ino, realm, + atomic_read(&realm->nref), atomic_read(&realm->nref)-1); + if (!atomic_dec_and_test(&realm->nref)) + return; + + if (down_write_trylock(&mdsc->snap_rwsem)) { + __destroy_snap_realm(mdsc, realm); + up_write(&mdsc->snap_rwsem); + } else { + spin_lock(&mdsc->snap_empty_lock); + list_add(&mdsc->snap_empty, &realm->empty_item); + spin_unlock(&mdsc->snap_empty_lock); + } +} + +/* + * Clean up any realms whose ref counts have dropped to zero. Note + * that this does not include realms who were created but not yet + * used. + * + * Called under snap_rwsem (write) + */ +static void __cleanup_empty_realms(struct ceph_mds_client *mdsc) +{ + struct ceph_snap_realm *realm; + + spin_lock(&mdsc->snap_empty_lock); + while (!list_empty(&mdsc->snap_empty)) { + realm = list_first_entry(&mdsc->snap_empty, + struct ceph_snap_realm, empty_item); + list_del(&realm->empty_item); + spin_unlock(&mdsc->snap_empty_lock); + __destroy_snap_realm(mdsc, realm); + spin_lock(&mdsc->snap_empty_lock); + } + spin_unlock(&mdsc->snap_empty_lock); +} + +void ceph_cleanup_empty_realms(struct ceph_mds_client *mdsc) +{ + down_write(&mdsc->snap_rwsem); + __cleanup_empty_realms(mdsc); + up_write(&mdsc->snap_rwsem); +} + +/* + * adjust the parent realm of a given @realm. adjust child list, and parent + * pointers, and ref counts appropriately. + * + * return true if parent was changed, 0 if unchanged, <0 on error. + * + * caller must hold snap_rwsem for write. + */ +static int adjust_snap_realm_parent(struct ceph_mds_client *mdsc, + struct ceph_snap_realm *realm, + u64 parentino) +{ + struct ceph_snap_realm *parent; + + if (realm->parent_ino == parentino) + return 0; + + parent = ceph_lookup_snap_realm(mdsc, parentino); + if (IS_ERR(parent)) + return PTR_ERR(parent); + if (!parent) { + parent = ceph_create_snap_realm(mdsc, parentino); + if (IS_ERR(parent)) + return PTR_ERR(parent); + } + dout("adjust_snap_realm_parent %llx %p: %llx %p -> %llx %p\n", + realm->ino, realm, realm->parent_ino, realm->parent, + parentino, parent); + if (realm->parent) { + list_del_init(&realm->child_item); + ceph_put_snap_realm(mdsc, realm->parent); + } + realm->parent_ino = parentino; + realm->parent = parent; + ceph_get_snap_realm(mdsc, parent); + list_add(&realm->child_item, &parent->children); + return 1; +} + + +static int cmpu64_rev(const void *a, const void *b) +{ + if (*(u64 *)a < *(u64 *)b) + return 1; + if (*(u64 *)a > *(u64 *)b) + return -1; + return 0; +} + +/* + * build the snap context for a given realm. + */ +static int build_snap_context(struct ceph_snap_realm *realm) +{ + struct ceph_snap_realm *parent = realm->parent; + struct ceph_snap_context *snapc; + int err = 0; + int i; + int num = realm->num_prior_parent_snaps + realm->num_snaps; + + /* + * build parent context, if it hasn't been built. + * conservatively estimate that all parent snaps might be + * included by us. + */ + if (parent) { + if (!parent->cached_context) { + err = build_snap_context(parent); + if (err) + goto fail; + } + num += parent->cached_context->num_snaps; + } + + /* do i actually need to update? not if my context seq + matches realm seq, and my parents' does to. (this works + because we rebuild_snap_realms() works _downward_ in + hierarchy after each update.) */ + if (realm->cached_context && + realm->cached_context->seq <= realm->seq && + (!parent || + realm->cached_context->seq <= parent->cached_context->seq)) { + dout("build_snap_context %llx %p: %p seq %lld (%d snaps)" + " (unchanged)\n", + realm->ino, realm, realm->cached_context, + realm->cached_context->seq, + realm->cached_context->num_snaps); + return 0; + } + + /* alloc new snap context */ + err = -ENOMEM; + if (num > ULONG_MAX / sizeof(u64) - sizeof(*snapc)) + goto fail; + snapc = kzalloc(sizeof(*snapc) + num*sizeof(u64), GFP_NOFS); + if (!snapc) + goto fail; + atomic_set(&snapc->nref, 1); + + /* build (reverse sorted) snap vector */ + num = 0; + snapc->seq = realm->seq; + if (parent) { + /* include any of parent's snaps occuring _after_ my + parent became my parent */ + for (i = 0; i < parent->cached_context->num_snaps; i++) + if (parent->cached_context->snaps[i] >= + realm->parent_since) + snapc->snaps[num++] = + parent->cached_context->snaps[i]; + if (parent->cached_context->seq > snapc->seq) + snapc->seq = parent->cached_context->seq; + } + memcpy(snapc->snaps + num, realm->snaps, + sizeof(u64)*realm->num_snaps); + num += realm->num_snaps; + memcpy(snapc->snaps + num, realm->prior_parent_snaps, + sizeof(u64)*realm->num_prior_parent_snaps); + num += realm->num_prior_parent_snaps; + + sort(snapc->snaps, num, sizeof(u64), cmpu64_rev, NULL); + snapc->num_snaps = num; + dout("build_snap_context %llx %p: %p seq %lld (%d snaps)\n", + realm->ino, realm, snapc, snapc->seq, snapc->num_snaps); + + if (realm->cached_context) + ceph_put_snap_context(realm->cached_context); + realm->cached_context = snapc; + return 0; + +fail: + /* + * if we fail, clear old (incorrect) cached_context... hopefully + * we'll have better luck building it later + */ + if (realm->cached_context) { + ceph_put_snap_context(realm->cached_context); + realm->cached_context = NULL; + } + pr_err("build_snap_context %llx %p fail %d\n", realm->ino, + realm, err); + return err; +} + +/* + * rebuild snap context for the given realm and all of its children. + */ +static void rebuild_snap_realms(struct ceph_snap_realm *realm) +{ + struct ceph_snap_realm *child; + + dout("rebuild_snap_realms %llx %p\n", realm->ino, realm); + build_snap_context(realm); + + list_for_each_entry(child, &realm->children, child_item) + rebuild_snap_realms(child); +} + + +/* + * helper to allocate and decode an array of snapids. free prior + * instance, if any. + */ +static int dup_array(u64 **dst, __le64 *src, int num) +{ + int i; + + kfree(*dst); + if (num) { + *dst = kcalloc(num, sizeof(u64), GFP_NOFS); + if (!*dst) + return -ENOMEM; + for (i = 0; i < num; i++) + (*dst)[i] = get_unaligned_le64(src + i); + } else { + *dst = NULL; + } + return 0; +} + + +/* + * When a snapshot is applied, the size/mtime inode metadata is queued + * in a ceph_cap_snap (one for each snapshot) until writeback + * completes and the metadata can be flushed back to the MDS. + * + * However, if a (sync) write is currently in-progress when we apply + * the snapshot, we have to wait until the write succeeds or fails + * (and a final size/mtime is known). In this case the + * cap_snap->writing = 1, and is said to be "pending." When the write + * finishes, we __ceph_finish_cap_snap(). + * + * Caller must hold snap_rwsem for read (i.e., the realm topology won't + * change). + */ +void ceph_queue_cap_snap(struct ceph_inode_info *ci, + struct ceph_snap_context *snapc) +{ + struct inode *inode = &ci->vfs_inode; + struct ceph_cap_snap *capsnap; + int used; + + capsnap = kzalloc(sizeof(*capsnap), GFP_NOFS); + if (!capsnap) { + pr_err("ENOMEM allocating ceph_cap_snap on %p\n", inode); + return; + } + + spin_lock(&inode->i_lock); + used = __ceph_caps_used(ci); + if (__ceph_have_pending_cap_snap(ci)) { + /* there is no point in queuing multiple "pending" cap_snaps, + as no new writes are allowed to start when pending, so any + writes in progress now were started before the previous + cap_snap. lucky us. */ + dout("queue_cap_snap %p snapc %p seq %llu used %d" + " already pending\n", inode, snapc, snapc->seq, used); + kfree(capsnap); + } else if (ci->i_wrbuffer_ref_head || (used & CEPH_CAP_FILE_WR)) { + igrab(inode); + + atomic_set(&capsnap->nref, 1); + capsnap->ci = ci; + INIT_LIST_HEAD(&capsnap->ci_item); + INIT_LIST_HEAD(&capsnap->flushing_item); + + capsnap->follows = snapc->seq - 1; + capsnap->context = ceph_get_snap_context(snapc); + capsnap->issued = __ceph_caps_issued(ci, NULL); + capsnap->dirty = __ceph_caps_dirty(ci); + + capsnap->mode = inode->i_mode; + capsnap->uid = inode->i_uid; + capsnap->gid = inode->i_gid; + + /* fixme? */ + capsnap->xattr_blob = NULL; + capsnap->xattr_len = 0; + + /* dirty page count moved from _head to this cap_snap; + all subsequent writes page dirties occur _after_ this + snapshot. */ + capsnap->dirty_pages = ci->i_wrbuffer_ref_head; + ci->i_wrbuffer_ref_head = 0; + ceph_put_snap_context(ci->i_head_snapc); + ci->i_head_snapc = NULL; + list_add_tail(&capsnap->ci_item, &ci->i_cap_snaps); + + if (used & CEPH_CAP_FILE_WR) { + dout("queue_cap_snap %p cap_snap %p snapc %p" + " seq %llu used WR, now pending\n", inode, + capsnap, snapc, snapc->seq); + capsnap->writing = 1; + } else { + /* note mtime, size NOW. */ + __ceph_finish_cap_snap(ci, capsnap); + } + } else { + dout("queue_cap_snap %p nothing dirty|writing\n", inode); + kfree(capsnap); + } + + spin_unlock(&inode->i_lock); +} + +/* + * Finalize the size, mtime for a cap_snap.. that is, settle on final values + * to be used for the snapshot, to be flushed back to the mds. + * + * If capsnap can now be flushed, add to snap_flush list, and return 1. + * + * Caller must hold i_lock. + */ +int __ceph_finish_cap_snap(struct ceph_inode_info *ci, + struct ceph_cap_snap *capsnap) +{ + struct inode *inode = &ci->vfs_inode; + struct ceph_mds_client *mdsc = &ceph_client(inode->i_sb)->mdsc; + + BUG_ON(capsnap->writing); + capsnap->size = inode->i_size; + capsnap->mtime = inode->i_mtime; + capsnap->atime = inode->i_atime; + capsnap->ctime = inode->i_ctime; + capsnap->time_warp_seq = ci->i_time_warp_seq; + if (capsnap->dirty_pages) { + dout("finish_cap_snap %p cap_snap %p snapc %p %llu s=%llu " + "still has %d dirty pages\n", inode, capsnap, + capsnap->context, capsnap->context->seq, + capsnap->size, capsnap->dirty_pages); + return 0; + } + dout("finish_cap_snap %p cap_snap %p snapc %p %llu s=%llu clean\n", + inode, capsnap, capsnap->context, + capsnap->context->seq, capsnap->size); + + spin_lock(&mdsc->snap_flush_lock); + list_add_tail(&ci->i_snap_flush_item, &mdsc->snap_flush_list); + spin_unlock(&mdsc->snap_flush_lock); + return 1; /* caller may want to ceph_flush_snaps */ +} + + +/* + * Parse and apply a snapblob "snap trace" from the MDS. This specifies + * the snap realm parameters from a given realm and all of its ancestors, + * up to the root. + * + * Caller must hold snap_rwsem for write. + */ +int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + void *p, void *e, bool deletion) +{ + struct ceph_mds_snap_realm *ri; /* encoded */ + __le64 *snaps; /* encoded */ + __le64 *prior_parent_snaps; /* encoded */ + struct ceph_snap_realm *realm; + int invalidate = 0; + int err = -ENOMEM; + + dout("update_snap_trace deletion=%d\n", deletion); +more: + ceph_decode_need(&p, e, sizeof(*ri), bad); + ri = p; + p += sizeof(*ri); + ceph_decode_need(&p, e, sizeof(u64)*(le32_to_cpu(ri->num_snaps) + + le32_to_cpu(ri->num_prior_parent_snaps)), bad); + snaps = p; + p += sizeof(u64) * le32_to_cpu(ri->num_snaps); + prior_parent_snaps = p; + p += sizeof(u64) * le32_to_cpu(ri->num_prior_parent_snaps); + + realm = ceph_lookup_snap_realm(mdsc, le64_to_cpu(ri->ino)); + if (IS_ERR(realm)) { + err = PTR_ERR(realm); + goto fail; + } + if (!realm) { + realm = ceph_create_snap_realm(mdsc, le64_to_cpu(ri->ino)); + if (IS_ERR(realm)) { + err = PTR_ERR(realm); + goto fail; + } + } + + if (le64_to_cpu(ri->seq) > realm->seq) { + dout("update_snap_trace updating %llx %p %lld -> %lld\n", + realm->ino, realm, realm->seq, le64_to_cpu(ri->seq)); + /* + * if the realm seq has changed, queue a cap_snap for every + * inode with open caps. we do this _before_ we update + * the realm info so that we prepare for writeback under the + * _previous_ snap context. + * + * ...unless it's a snap deletion! + */ + if (!deletion) { + struct ceph_inode_info *ci; + struct inode *lastinode = NULL; + + spin_lock(&realm->inodes_with_caps_lock); + list_for_each_entry(ci, &realm->inodes_with_caps, + i_snap_realm_item) { + struct inode *inode = igrab(&ci->vfs_inode); + if (!inode) + continue; + spin_unlock(&realm->inodes_with_caps_lock); + if (lastinode) + iput(lastinode); + lastinode = inode; + ceph_queue_cap_snap(ci, realm->cached_context); + spin_lock(&realm->inodes_with_caps_lock); + } + spin_unlock(&realm->inodes_with_caps_lock); + if (lastinode) + iput(lastinode); + dout("update_snap_trace cap_snaps queued\n"); + } + + } else { + dout("update_snap_trace %llx %p seq %lld unchanged\n", + realm->ino, realm, realm->seq); + } + + /* ensure the parent is correct */ + err = adjust_snap_realm_parent(mdsc, realm, le64_to_cpu(ri->parent)); + if (err < 0) + goto fail; + invalidate += err; + + if (le64_to_cpu(ri->seq) > realm->seq) { + /* update realm parameters, snap lists */ + realm->seq = le64_to_cpu(ri->seq); + realm->created = le64_to_cpu(ri->created); + realm->parent_since = le64_to_cpu(ri->parent_since); + + realm->num_snaps = le32_to_cpu(ri->num_snaps); + err = dup_array(&realm->snaps, snaps, realm->num_snaps); + if (err < 0) + goto fail; + + realm->num_prior_parent_snaps = + le32_to_cpu(ri->num_prior_parent_snaps); + err = dup_array(&realm->prior_parent_snaps, prior_parent_snaps, + realm->num_prior_parent_snaps); + if (err < 0) + goto fail; + + invalidate = 1; + } else if (!realm->cached_context) { + invalidate = 1; + } + + dout("done with %llx %p, invalidated=%d, %p %p\n", realm->ino, + realm, invalidate, p, e); + + if (p < e) + goto more; + + /* invalidate when we reach the _end_ (root) of the trace */ + if (invalidate) + rebuild_snap_realms(realm); + + __cleanup_empty_realms(mdsc); + return 0; + +bad: + err = -EINVAL; +fail: + pr_err("update_snap_trace error %d\n", err); + return err; +} + + +/* + * Send any cap_snaps that are queued for flush. Try to carry + * s_mutex across multiple snap flushes to avoid locking overhead. + * + * Caller holds no locks. + */ +static void flush_snaps(struct ceph_mds_client *mdsc) +{ + struct ceph_inode_info *ci; + struct inode *inode; + struct ceph_mds_session *session = NULL; + + dout("flush_snaps\n"); + spin_lock(&mdsc->snap_flush_lock); + while (!list_empty(&mdsc->snap_flush_list)) { + ci = list_first_entry(&mdsc->snap_flush_list, + struct ceph_inode_info, i_snap_flush_item); + inode = &ci->vfs_inode; + igrab(inode); + spin_unlock(&mdsc->snap_flush_lock); + spin_lock(&inode->i_lock); + __ceph_flush_snaps(ci, &session); + spin_unlock(&inode->i_lock); + iput(inode); + spin_lock(&mdsc->snap_flush_lock); + } + spin_unlock(&mdsc->snap_flush_lock); + + if (session) { + mutex_unlock(&session->s_mutex); + ceph_put_mds_session(session); + } + dout("flush_snaps done\n"); +} + + +/* + * Handle a snap notification from the MDS. + * + * This can take two basic forms: the simplest is just a snap creation + * or deletion notification on an existing realm. This should update the + * realm and its children. + * + * The more difficult case is realm creation, due to snap creation at a + * new point in the file hierarchy, or due to a rename that moves a file or + * directory into another realm. + */ +void ceph_handle_snap(struct ceph_mds_client *mdsc, + struct ceph_msg *msg) +{ + struct super_block *sb = mdsc->client->sb; + struct ceph_mds_session *session; + int mds; + u64 split; + int op; + int trace_len; + struct ceph_snap_realm *realm = NULL; + void *p = msg->front.iov_base; + void *e = p + msg->front.iov_len; + struct ceph_mds_snap_head *h; + int num_split_inos, num_split_realms; + __le64 *split_inos = NULL, *split_realms = NULL; + int i; + int locked_rwsem = 0; + + if (msg->hdr.src.name.type != CEPH_ENTITY_TYPE_MDS) + return; + mds = le64_to_cpu(msg->hdr.src.name.num); + + /* decode */ + if (msg->front.iov_len < sizeof(*h)) + goto bad; + h = p; + op = le32_to_cpu(h->op); + split = le64_to_cpu(h->split); /* non-zero if we are splitting an + * existing realm */ + num_split_inos = le32_to_cpu(h->num_split_inos); + num_split_realms = le32_to_cpu(h->num_split_realms); + trace_len = le32_to_cpu(h->trace_len); + p += sizeof(*h); + + dout("handle_snap from mds%d op %s split %llx tracelen %d\n", mds, + ceph_snap_op_name(op), split, trace_len); + + /* find session */ + mutex_lock(&mdsc->mutex); + session = __ceph_lookup_mds_session(mdsc, mds); + mutex_unlock(&mdsc->mutex); + if (!session) { + dout("WTF, got snap but no session for mds%d\n", mds); + return; + } + + mutex_lock(&session->s_mutex); + session->s_seq++; + mutex_unlock(&session->s_mutex); + + down_write(&mdsc->snap_rwsem); + locked_rwsem = 1; + + if (op == CEPH_SNAP_OP_SPLIT) { + struct ceph_mds_snap_realm *ri; + + /* + * A "split" breaks part of an existing realm off into + * a new realm. The MDS provides a list of inodes + * (with caps) and child realms that belong to the new + * child. + */ + split_inos = p; + p += sizeof(u64) * num_split_inos; + split_realms = p; + p += sizeof(u64) * num_split_realms; + ceph_decode_need(&p, e, sizeof(*ri), bad); + /* we will peek at realm info here, but will _not_ + * advance p, as the realm update will occur below in + * ceph_update_snap_trace. */ + ri = p; + + realm = ceph_lookup_snap_realm(mdsc, split); + if (IS_ERR(realm)) + goto out; + if (!realm) { + realm = ceph_create_snap_realm(mdsc, split); + if (IS_ERR(realm)) + goto out; + } + ceph_get_snap_realm(mdsc, realm); + + dout("splitting snap_realm %llx %p\n", realm->ino, realm); + for (i = 0; i < num_split_inos; i++) { + struct ceph_vino vino = { + .ino = le64_to_cpu(split_inos[i]), + .snap = CEPH_NOSNAP, + }; + struct inode *inode = ceph_find_inode(sb, vino); + struct ceph_inode_info *ci; + + if (!inode) + continue; + ci = ceph_inode(inode); + + spin_lock(&inode->i_lock); + if (!ci->i_snap_realm) + goto skip_inode; + /* + * If this inode belongs to a realm that was + * created after our new realm, we experienced + * a race (due to another split notifications + * arriving from a different MDS). So skip + * this inode. + */ + if (ci->i_snap_realm->created > + le64_to_cpu(ri->created)) { + dout(" leaving %p in newer realm %llx %p\n", + inode, ci->i_snap_realm->ino, + ci->i_snap_realm); + goto skip_inode; + } + dout(" will move %p to split realm %llx %p\n", + inode, realm->ino, realm); + /* + * Remove the inode from the realm's inode + * list, but don't add it to the new realm + * yet. We don't want the cap_snap to be + * queued (again) by ceph_update_snap_trace() + * below. Queue it _now_, under the old context. + */ + list_del_init(&ci->i_snap_realm_item); + spin_unlock(&inode->i_lock); + + ceph_queue_cap_snap(ci, + ci->i_snap_realm->cached_context); + + iput(inode); + continue; + +skip_inode: + spin_unlock(&inode->i_lock); + iput(inode); + } + + /* we may have taken some of the old realm's children. */ + for (i = 0; i < num_split_realms; i++) { + struct ceph_snap_realm *child = + ceph_lookup_snap_realm(mdsc, + le64_to_cpu(split_realms[i])); + if (IS_ERR(child)) + continue; + if (!child) + continue; + adjust_snap_realm_parent(mdsc, child, realm->ino); + } + } + + /* + * update using the provided snap trace. if we are deleting a + * snap, we can avoid queueing cap_snaps. + */ + ceph_update_snap_trace(mdsc, p, e, + op == CEPH_SNAP_OP_DESTROY); + + if (op == CEPH_SNAP_OP_SPLIT) { + /* + * ok, _now_ add the inodes into the new realm. + */ + for (i = 0; i < num_split_inos; i++) { + struct ceph_vino vino = { + .ino = le64_to_cpu(split_inos[i]), + .snap = CEPH_NOSNAP, + }; + struct inode *inode = ceph_find_inode(sb, vino); + struct ceph_inode_info *ci; + + if (!inode) + continue; + ci = ceph_inode(inode); + spin_lock(&inode->i_lock); + if (!ci->i_snap_realm) + goto split_skip_inode; + ceph_put_snap_realm(mdsc, ci->i_snap_realm); + spin_lock(&realm->inodes_with_caps_lock); + list_add(&ci->i_snap_realm_item, + &realm->inodes_with_caps); + ci->i_snap_realm = realm; + spin_unlock(&realm->inodes_with_caps_lock); + ceph_get_snap_realm(mdsc, realm); +split_skip_inode: + spin_unlock(&inode->i_lock); + iput(inode); + } + + /* we took a reference when we created the realm, above */ + ceph_put_snap_realm(mdsc, realm); + } + + __cleanup_empty_realms(mdsc); + + up_write(&mdsc->snap_rwsem); + + flush_snaps(mdsc); + return; + +bad: + pr_err("corrupt snap message from mds%d\n", mds); +out: + if (locked_rwsem) + up_write(&mdsc->snap_rwsem); + return; +} + + + -- cgit v0.10.2 From 31b8006e1d79e127a776c9414e3e0b5f9508047e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:13 -0700 Subject: ceph: messenger library A generic message passing library is used to communicate with all other components in the Ceph file system. The messenger library provides ordered, reliable delivery of messages between two nodes in the system. This implementation is based on TCP. Signed-off-by: Sage Weil diff --git a/fs/ceph/decode.h b/fs/ceph/decode.h new file mode 100644 index 0000000..fc2769d --- /dev/null +++ b/fs/ceph/decode.h @@ -0,0 +1,136 @@ +#ifndef __CEPH_DECODE_H +#define __CEPH_DECODE_H + +#include + +/* + * in all cases, + * void **p pointer to position pointer + * void *end pointer to end of buffer (last byte + 1) + */ + +/* + * bounds check input. + */ +#define ceph_decode_need(p, end, n, bad) \ + do { \ + if (unlikely(*(p) + (n) > (end))) \ + goto bad; \ + } while (0) + +#define ceph_decode_64(p, v) \ + do { \ + v = get_unaligned_le64(*(p)); \ + *(p) += sizeof(u64); \ + } while (0) +#define ceph_decode_32(p, v) \ + do { \ + v = get_unaligned_le32(*(p)); \ + *(p) += sizeof(u32); \ + } while (0) +#define ceph_decode_16(p, v) \ + do { \ + v = get_unaligned_le16(*(p)); \ + *(p) += sizeof(u16); \ + } while (0) +#define ceph_decode_8(p, v) \ + do { \ + v = *(u8 *)*(p); \ + (*p)++; \ + } while (0) + +#define ceph_decode_copy(p, pv, n) \ + do { \ + memcpy(pv, *(p), n); \ + *(p) += n; \ + } while (0) + +/* bounds check too */ +#define ceph_decode_64_safe(p, end, v, bad) \ + do { \ + ceph_decode_need(p, end, sizeof(u64), bad); \ + ceph_decode_64(p, v); \ + } while (0) +#define ceph_decode_32_safe(p, end, v, bad) \ + do { \ + ceph_decode_need(p, end, sizeof(u32), bad); \ + ceph_decode_32(p, v); \ + } while (0) +#define ceph_decode_16_safe(p, end, v, bad) \ + do { \ + ceph_decode_need(p, end, sizeof(u16), bad); \ + ceph_decode_16(p, v); \ + } while (0) + +#define ceph_decode_copy_safe(p, end, pv, n, bad) \ + do { \ + ceph_decode_need(p, end, n, bad); \ + ceph_decode_copy(p, pv, n); \ + } while (0) + +/* + * struct ceph_timespec <-> struct timespec + */ +#define ceph_decode_timespec(ts, tv) \ + do { \ + (ts)->tv_sec = le32_to_cpu((tv)->tv_sec); \ + (ts)->tv_nsec = le32_to_cpu((tv)->tv_nsec); \ + } while (0) +#define ceph_encode_timespec(tv, ts) \ + do { \ + (tv)->tv_sec = cpu_to_le32((ts)->tv_sec); \ + (tv)->tv_nsec = cpu_to_le32((ts)->tv_nsec); \ + } while (0) + + +/* + * encoders + */ +#define ceph_encode_64(p, v) \ + do { \ + put_unaligned_le64(v, (__le64 *)*(p)); \ + *(p) += sizeof(u64); \ + } while (0) +#define ceph_encode_32(p, v) \ + do { \ + put_unaligned_le32(v, (__le32 *)*(p)); \ + *(p) += sizeof(u32); \ + } while (0) +#define ceph_encode_16(p, v) \ + do { \ + put_unaligned_le16(v), (__le16 *)*(p)); \ + *(p) += sizeof(u16); \ + } while (0) +#define ceph_encode_8(p, v) \ + do { \ + *(u8 *)*(p) = v; \ + (*(p))++; \ + } while (0) + +/* + * filepath, string encoders + */ +static inline void ceph_encode_filepath(void **p, void *end, + u64 ino, const char *path) +{ + u32 len = path ? strlen(path) : 0; + BUG_ON(*p + sizeof(ino) + sizeof(len) + len > end); + ceph_encode_64(p, ino); + ceph_encode_32(p, len); + if (len) + memcpy(*p, path, len); + *p += len; +} + +static inline void ceph_encode_string(void **p, void *end, + const char *s, u32 len) +{ + BUG_ON(*p + sizeof(len) + len > end); + ceph_encode_32(p, len); + if (len) + memcpy(*p, s, len); + *p += len; +} + + +#endif diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c new file mode 100644 index 0000000..63f7f13 --- /dev/null +++ b/fs/ceph/messenger.c @@ -0,0 +1,2019 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "super.h" +#include "messenger.h" + +/* + * Ceph uses the messenger to exchange ceph_msg messages with other + * hosts in the system. The messenger provides ordered and reliable + * delivery. We tolerate TCP disconnects by reconnecting (with + * exponential backoff) in the case of a fault (disconnection, bad + * crc, protocol error). Acks allow sent messages to be discarded by + * the sender. + */ + +/* static tag bytes (protocol control messages) */ +static char tag_msg = CEPH_MSGR_TAG_MSG; +static char tag_ack = CEPH_MSGR_TAG_ACK; +static char tag_keepalive = CEPH_MSGR_TAG_KEEPALIVE; + + +static void queue_con(struct ceph_connection *con); +static void con_work(struct work_struct *); +static void ceph_fault(struct ceph_connection *con); + +const char *ceph_name_type_str(int t) +{ + switch (t) { + case CEPH_ENTITY_TYPE_MON: return "mon"; + case CEPH_ENTITY_TYPE_MDS: return "mds"; + case CEPH_ENTITY_TYPE_OSD: return "osd"; + case CEPH_ENTITY_TYPE_CLIENT: return "client"; + case CEPH_ENTITY_TYPE_ADMIN: return "admin"; + default: return "???"; + } +} + +/* + * nicely render a sockaddr as a string. + */ +#define MAX_ADDR_STR 20 +static char addr_str[MAX_ADDR_STR][40]; +static DEFINE_SPINLOCK(addr_str_lock); +static int last_addr_str; + +const char *pr_addr(const struct sockaddr_storage *ss) +{ + int i; + char *s; + struct sockaddr_in *in4 = (void *)ss; + unsigned char *quad = (void *)&in4->sin_addr.s_addr; + struct sockaddr_in6 *in6 = (void *)ss; + + spin_lock(&addr_str_lock); + i = last_addr_str++; + if (last_addr_str == MAX_ADDR_STR) + last_addr_str = 0; + spin_unlock(&addr_str_lock); + s = addr_str[i]; + + switch (ss->ss_family) { + case AF_INET: + sprintf(s, "%u.%u.%u.%u:%u", + (unsigned int)quad[0], + (unsigned int)quad[1], + (unsigned int)quad[2], + (unsigned int)quad[3], + (unsigned int)ntohs(in4->sin_port)); + break; + + case AF_INET6: + sprintf(s, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%u", + in6->sin6_addr.s6_addr16[0], + in6->sin6_addr.s6_addr16[1], + in6->sin6_addr.s6_addr16[2], + in6->sin6_addr.s6_addr16[3], + in6->sin6_addr.s6_addr16[4], + in6->sin6_addr.s6_addr16[5], + in6->sin6_addr.s6_addr16[6], + in6->sin6_addr.s6_addr16[7], + (unsigned int)ntohs(in6->sin6_port)); + break; + + default: + sprintf(s, "(unknown sockaddr family %d)", (int)ss->ss_family); + } + + return s; +} + +/* + * work queue for all reading and writing to/from the socket. + */ +struct workqueue_struct *ceph_msgr_wq; + +int __init ceph_msgr_init(void) +{ + ceph_msgr_wq = create_workqueue("ceph-msgr"); + if (IS_ERR(ceph_msgr_wq)) { + int ret = PTR_ERR(ceph_msgr_wq); + pr_err("msgr_init failed to create workqueue: %d\n", ret); + ceph_msgr_wq = NULL; + return ret; + } + return 0; +} + +void ceph_msgr_exit(void) +{ + destroy_workqueue(ceph_msgr_wq); +} + +/* + * socket callback functions + */ + +/* data available on socket, or listen socket received a connect */ +static void ceph_data_ready(struct sock *sk, int count_unused) +{ + struct ceph_connection *con = + (struct ceph_connection *)sk->sk_user_data; + if (sk->sk_state != TCP_CLOSE_WAIT) { + dout("ceph_data_ready on %p state = %lu, queueing work\n", + con, con->state); + queue_con(con); + } +} + +/* socket has buffer space for writing */ +static void ceph_write_space(struct sock *sk) +{ + struct ceph_connection *con = + (struct ceph_connection *)sk->sk_user_data; + + /* only queue to workqueue if there is data we want to write. */ + if (test_bit(WRITE_PENDING, &con->state)) { + dout("ceph_write_space %p queueing write work\n", con); + queue_con(con); + } else { + dout("ceph_write_space %p nothing to write\n", con); + } + + /* since we have our own write_space, clear the SOCK_NOSPACE flag */ + clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); +} + +/* socket's state has changed */ +static void ceph_state_change(struct sock *sk) +{ + struct ceph_connection *con = + (struct ceph_connection *)sk->sk_user_data; + + dout("ceph_state_change %p state = %lu sk_state = %u\n", + con, con->state, sk->sk_state); + + if (test_bit(CLOSED, &con->state)) + return; + + switch (sk->sk_state) { + case TCP_CLOSE: + dout("ceph_state_change TCP_CLOSE\n"); + case TCP_CLOSE_WAIT: + dout("ceph_state_change TCP_CLOSE_WAIT\n"); + if (test_and_set_bit(SOCK_CLOSED, &con->state) == 0) { + if (test_bit(CONNECTING, &con->state)) + con->error_msg = "connection failed"; + else + con->error_msg = "socket closed"; + queue_con(con); + } + break; + case TCP_ESTABLISHED: + dout("ceph_state_change TCP_ESTABLISHED\n"); + queue_con(con); + break; + } +} + +/* + * set up socket callbacks + */ +static void set_sock_callbacks(struct socket *sock, + struct ceph_connection *con) +{ + struct sock *sk = sock->sk; + sk->sk_user_data = (void *)con; + sk->sk_data_ready = ceph_data_ready; + sk->sk_write_space = ceph_write_space; + sk->sk_state_change = ceph_state_change; +} + + +/* + * socket helpers + */ + +/* + * initiate connection to a remote socket. + */ +static struct socket *ceph_tcp_connect(struct ceph_connection *con) +{ + struct sockaddr *paddr = (struct sockaddr *)&con->peer_addr.in_addr; + struct socket *sock; + int ret; + + BUG_ON(con->sock); + ret = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + if (ret) + return ERR_PTR(ret); + con->sock = sock; + sock->sk->sk_allocation = GFP_NOFS; + + set_sock_callbacks(sock, con); + + dout("connect %s\n", pr_addr(&con->peer_addr.in_addr)); + + ret = sock->ops->connect(sock, paddr, sizeof(*paddr), O_NONBLOCK); + if (ret == -EINPROGRESS) { + dout("connect %s EINPROGRESS sk_state = %u\n", + pr_addr(&con->peer_addr.in_addr), + sock->sk->sk_state); + ret = 0; + } + if (ret < 0) { + pr_err("connect %s error %d\n", + pr_addr(&con->peer_addr.in_addr), ret); + sock_release(sock); + con->sock = NULL; + con->error_msg = "connect error"; + } + + if (ret < 0) + return ERR_PTR(ret); + return sock; +} + +static int ceph_tcp_recvmsg(struct socket *sock, void *buf, size_t len) +{ + struct kvec iov = {buf, len}; + struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL }; + + return kernel_recvmsg(sock, &msg, &iov, 1, len, msg.msg_flags); +} + +/* + * write something. @more is true if caller will be sending more data + * shortly. + */ +static int ceph_tcp_sendmsg(struct socket *sock, struct kvec *iov, + size_t kvlen, size_t len, int more) +{ + struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL }; + + if (more) + msg.msg_flags |= MSG_MORE; + else + msg.msg_flags |= MSG_EOR; /* superfluous, but what the hell */ + + return kernel_sendmsg(sock, &msg, iov, kvlen, len); +} + + +/* + * Shutdown/close the socket for the given connection. + */ +static int con_close_socket(struct ceph_connection *con) +{ + int rc; + + dout("con_close_socket on %p sock %p\n", con, con->sock); + if (!con->sock) + return 0; + set_bit(SOCK_CLOSED, &con->state); + rc = con->sock->ops->shutdown(con->sock, SHUT_RDWR); + sock_release(con->sock); + con->sock = NULL; + clear_bit(SOCK_CLOSED, &con->state); + return rc; +} + +/* + * Reset a connection. Discard all incoming and outgoing messages + * and clear *_seq state. + */ +static void ceph_msg_remove(struct ceph_msg *msg) +{ + list_del_init(&msg->list_head); + ceph_msg_put(msg); +} +static void ceph_msg_remove_list(struct list_head *head) +{ + while (!list_empty(head)) { + struct ceph_msg *msg = list_first_entry(head, struct ceph_msg, + list_head); + ceph_msg_remove(msg); + } +} + +static void reset_connection(struct ceph_connection *con) +{ + /* reset connection, out_queue, msg_ and connect_seq */ + /* discard existing out_queue and msg_seq */ + mutex_lock(&con->out_mutex); + ceph_msg_remove_list(&con->out_queue); + ceph_msg_remove_list(&con->out_sent); + + con->connect_seq = 0; + con->out_seq = 0; + con->out_msg = NULL; + con->in_seq = 0; + mutex_unlock(&con->out_mutex); +} + +/* + * mark a peer down. drop any open connections. + */ +void ceph_con_close(struct ceph_connection *con) +{ + dout("con_close %p peer %s\n", con, pr_addr(&con->peer_addr.in_addr)); + set_bit(CLOSED, &con->state); /* in case there's queued work */ + clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ + reset_connection(con); + queue_con(con); +} + +/* + * clean up connection state + */ +void ceph_con_shutdown(struct ceph_connection *con) +{ + dout("con_shutdown %p\n", con); + reset_connection(con); + set_bit(DEAD, &con->state); + con_close_socket(con); /* silently ignore errors */ +} + +/* + * Reopen a closed connection, with a new peer address. + */ +void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr) +{ + dout("con_open %p %s\n", con, pr_addr(&addr->in_addr)); + set_bit(OPENING, &con->state); + clear_bit(CLOSED, &con->state); + memcpy(&con->peer_addr, addr, sizeof(*addr)); + queue_con(con); +} + +/* + * generic get/put + */ +struct ceph_connection *ceph_con_get(struct ceph_connection *con) +{ + dout("con_get %p nref = %d -> %d\n", con, + atomic_read(&con->nref), atomic_read(&con->nref) + 1); + if (atomic_inc_not_zero(&con->nref)) + return con; + return NULL; +} + +void ceph_con_put(struct ceph_connection *con) +{ + dout("con_put %p nref = %d -> %d\n", con, + atomic_read(&con->nref), atomic_read(&con->nref) - 1); + BUG_ON(atomic_read(&con->nref) == 0); + if (atomic_dec_and_test(&con->nref)) { + ceph_con_shutdown(con); + kfree(con); + } +} + +/* + * initialize a new connection. + */ +void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con) +{ + dout("con_init %p\n", con); + memset(con, 0, sizeof(*con)); + atomic_set(&con->nref, 1); + con->msgr = msgr; + mutex_init(&con->out_mutex); + INIT_LIST_HEAD(&con->out_queue); + INIT_LIST_HEAD(&con->out_sent); + INIT_DELAYED_WORK(&con->work, con_work); +} + + +/* + * We maintain a global counter to order connection attempts. Get + * a unique seq greater than @gt. + */ +static u32 get_global_seq(struct ceph_messenger *msgr, u32 gt) +{ + u32 ret; + + spin_lock(&msgr->global_seq_lock); + if (msgr->global_seq < gt) + msgr->global_seq = gt; + ret = ++msgr->global_seq; + spin_unlock(&msgr->global_seq_lock); + return ret; +} + + +/* + * Prepare footer for currently outgoing message, and finish things + * off. Assumes out_kvec* are already valid.. we just add on to the end. + */ +static void prepare_write_message_footer(struct ceph_connection *con, int v) +{ + struct ceph_msg *m = con->out_msg; + + dout("prepare_write_message_footer %p\n", con); + con->out_kvec_is_msg = true; + con->out_kvec[v].iov_base = &m->footer; + con->out_kvec[v].iov_len = sizeof(m->footer); + con->out_kvec_bytes += sizeof(m->footer); + con->out_kvec_left++; + con->out_more = m->more_to_follow; + con->out_msg = NULL; /* we're done with this one */ +} + +/* + * Prepare headers for the next outgoing message. + */ +static void prepare_write_message(struct ceph_connection *con) +{ + struct ceph_msg *m; + int v = 0; + + con->out_kvec_bytes = 0; + con->out_kvec_is_msg = true; + + /* Sneak an ack in there first? If we can get it into the same + * TCP packet that's a good thing. */ + if (con->in_seq > con->in_seq_acked) { + con->in_seq_acked = con->in_seq; + con->out_kvec[v].iov_base = &tag_ack; + con->out_kvec[v++].iov_len = 1; + con->out_temp_ack = cpu_to_le64(con->in_seq_acked); + con->out_kvec[v].iov_base = &con->out_temp_ack; + con->out_kvec[v++].iov_len = sizeof(con->out_temp_ack); + con->out_kvec_bytes = 1 + sizeof(con->out_temp_ack); + } + + /* move message to sending/sent list */ + m = list_first_entry(&con->out_queue, + struct ceph_msg, list_head); + list_move_tail(&m->list_head, &con->out_sent); + con->out_msg = m; /* we don't bother taking a reference here. */ + + m->hdr.seq = cpu_to_le64(++con->out_seq); + + dout("prepare_write_message %p seq %lld type %d len %d+%d+%d %d pgs\n", + m, con->out_seq, le16_to_cpu(m->hdr.type), + le32_to_cpu(m->hdr.front_len), le32_to_cpu(m->hdr.middle_len), + le32_to_cpu(m->hdr.data_len), + m->nr_pages); + BUG_ON(le32_to_cpu(m->hdr.front_len) != m->front.iov_len); + + /* tag + hdr + front + middle */ + con->out_kvec[v].iov_base = &tag_msg; + con->out_kvec[v++].iov_len = 1; + con->out_kvec[v].iov_base = &m->hdr; + con->out_kvec[v++].iov_len = sizeof(m->hdr); + con->out_kvec[v++] = m->front; + if (m->middle) + con->out_kvec[v++] = m->middle->vec; + con->out_kvec_left = v; + con->out_kvec_bytes += 1 + sizeof(m->hdr) + m->front.iov_len + + (m->middle ? m->middle->vec.iov_len : 0); + con->out_kvec_cur = con->out_kvec; + + /* fill in crc (except data pages), footer */ + con->out_msg->hdr.crc = + cpu_to_le32(crc32c(0, (void *)&m->hdr, + sizeof(m->hdr) - sizeof(m->hdr.crc))); + con->out_msg->footer.flags = CEPH_MSG_FOOTER_COMPLETE; + con->out_msg->footer.front_crc = + cpu_to_le32(crc32c(0, m->front.iov_base, m->front.iov_len)); + if (m->middle) + con->out_msg->footer.middle_crc = + cpu_to_le32(crc32c(0, m->middle->vec.iov_base, + m->middle->vec.iov_len)); + else + con->out_msg->footer.middle_crc = 0; + con->out_msg->footer.data_crc = 0; + dout("prepare_write_message front_crc %u data_crc %u\n", + le32_to_cpu(con->out_msg->footer.front_crc), + le32_to_cpu(con->out_msg->footer.middle_crc)); + + /* is there a data payload? */ + if (le32_to_cpu(m->hdr.data_len) > 0) { + /* initialize page iterator */ + con->out_msg_pos.page = 0; + con->out_msg_pos.page_pos = + le16_to_cpu(m->hdr.data_off) & ~PAGE_MASK; + con->out_msg_pos.data_pos = 0; + con->out_msg_pos.did_page_crc = 0; + con->out_more = 1; /* data + footer will follow */ + } else { + /* no, queue up footer too and be done */ + prepare_write_message_footer(con, v); + } + + set_bit(WRITE_PENDING, &con->state); +} + +/* + * Prepare an ack. + */ +static void prepare_write_ack(struct ceph_connection *con) +{ + dout("prepare_write_ack %p %llu -> %llu\n", con, + con->in_seq_acked, con->in_seq); + con->in_seq_acked = con->in_seq; + + con->out_kvec[0].iov_base = &tag_ack; + con->out_kvec[0].iov_len = 1; + con->out_temp_ack = cpu_to_le64(con->in_seq_acked); + con->out_kvec[1].iov_base = &con->out_temp_ack; + con->out_kvec[1].iov_len = sizeof(con->out_temp_ack); + con->out_kvec_left = 2; + con->out_kvec_bytes = 1 + sizeof(con->out_temp_ack); + con->out_kvec_cur = con->out_kvec; + con->out_more = 1; /* more will follow.. eventually.. */ + set_bit(WRITE_PENDING, &con->state); +} + +/* + * Prepare to write keepalive byte. + */ +static void prepare_write_keepalive(struct ceph_connection *con) +{ + dout("prepare_write_keepalive %p\n", con); + con->out_kvec[0].iov_base = &tag_keepalive; + con->out_kvec[0].iov_len = 1; + con->out_kvec_left = 1; + con->out_kvec_bytes = 1; + con->out_kvec_cur = con->out_kvec; + set_bit(WRITE_PENDING, &con->state); +} + +/* + * Connection negotiation. + */ + +/* + * We connected to a peer and are saying hello. + */ +static void prepare_write_connect(struct ceph_messenger *msgr, + struct ceph_connection *con) +{ + int len = strlen(CEPH_BANNER); + unsigned global_seq = get_global_seq(con->msgr, 0); + int proto; + + switch (con->peer_name.type) { + case CEPH_ENTITY_TYPE_MON: + proto = CEPH_MONC_PROTOCOL; + break; + case CEPH_ENTITY_TYPE_OSD: + proto = CEPH_OSDC_PROTOCOL; + break; + case CEPH_ENTITY_TYPE_MDS: + proto = CEPH_MDSC_PROTOCOL; + break; + default: + BUG(); + } + + dout("prepare_write_connect %p cseq=%d gseq=%d proto=%d\n", con, + con->connect_seq, global_seq, proto); + con->out_connect.host_type = cpu_to_le32(CEPH_ENTITY_TYPE_CLIENT); + con->out_connect.connect_seq = cpu_to_le32(con->connect_seq); + con->out_connect.global_seq = cpu_to_le32(global_seq); + con->out_connect.protocol_version = cpu_to_le32(proto); + con->out_connect.flags = 0; + if (test_bit(LOSSYTX, &con->state)) + con->out_connect.flags = CEPH_MSG_CONNECT_LOSSY; + + con->out_kvec[0].iov_base = CEPH_BANNER; + con->out_kvec[0].iov_len = len; + con->out_kvec[1].iov_base = &msgr->inst.addr; + con->out_kvec[1].iov_len = sizeof(msgr->inst.addr); + con->out_kvec[2].iov_base = &con->out_connect; + con->out_kvec[2].iov_len = sizeof(con->out_connect); + con->out_kvec_left = 3; + con->out_kvec_bytes = len + sizeof(msgr->inst.addr) + + sizeof(con->out_connect); + con->out_kvec_cur = con->out_kvec; + con->out_more = 0; + set_bit(WRITE_PENDING, &con->state); +} + +static void prepare_write_connect_retry(struct ceph_messenger *msgr, + struct ceph_connection *con) +{ + dout("prepare_write_connect_retry %p\n", con); + con->out_connect.connect_seq = cpu_to_le32(con->connect_seq); + con->out_connect.global_seq = + cpu_to_le32(get_global_seq(con->msgr, 0)); + + con->out_kvec[0].iov_base = &con->out_connect; + con->out_kvec[0].iov_len = sizeof(con->out_connect); + con->out_kvec_left = 1; + con->out_kvec_bytes = sizeof(con->out_connect); + con->out_kvec_cur = con->out_kvec; + con->out_more = 0; + set_bit(WRITE_PENDING, &con->state); +} + + +/* + * write as much of pending kvecs to the socket as we can. + * 1 -> done + * 0 -> socket full, but more to do + * <0 -> error + */ +static int write_partial_kvec(struct ceph_connection *con) +{ + int ret; + + dout("write_partial_kvec %p %d left\n", con, con->out_kvec_bytes); + while (con->out_kvec_bytes > 0) { + ret = ceph_tcp_sendmsg(con->sock, con->out_kvec_cur, + con->out_kvec_left, con->out_kvec_bytes, + con->out_more); + if (ret <= 0) + goto out; + con->out_kvec_bytes -= ret; + if (con->out_kvec_bytes == 0) + break; /* done */ + while (ret > 0) { + if (ret >= con->out_kvec_cur->iov_len) { + ret -= con->out_kvec_cur->iov_len; + con->out_kvec_cur++; + con->out_kvec_left--; + } else { + con->out_kvec_cur->iov_len -= ret; + con->out_kvec_cur->iov_base += ret; + ret = 0; + break; + } + } + } + con->out_kvec_left = 0; + con->out_kvec_is_msg = false; + ret = 1; +out: + dout("write_partial_kvec %p %d left in %d kvecs ret = %d\n", con, + con->out_kvec_bytes, con->out_kvec_left, ret); + return ret; /* done! */ +} + +/* + * Write as much message data payload as we can. If we finish, queue + * up the footer. + * 1 -> done, footer is now queued in out_kvec[]. + * 0 -> socket full, but more to do + * <0 -> error + */ +static int write_partial_msg_pages(struct ceph_connection *con) +{ + struct ceph_msg *msg = con->out_msg; + unsigned data_len = le32_to_cpu(msg->hdr.data_len); + size_t len; + int crc = con->msgr->nocrc; + int ret; + + dout("write_partial_msg_pages %p msg %p page %d/%d offset %d\n", + con, con->out_msg, con->out_msg_pos.page, con->out_msg->nr_pages, + con->out_msg_pos.page_pos); + + while (con->out_msg_pos.page < con->out_msg->nr_pages) { + struct page *page = NULL; + void *kaddr = NULL; + + /* + * if we are calculating the data crc (the default), we need + * to map the page. if our pages[] has been revoked, use the + * zero page. + */ + if (msg->pages) { + page = msg->pages[con->out_msg_pos.page]; + if (crc) + kaddr = kmap(page); + } else { + page = con->msgr->zero_page; + if (crc) + kaddr = page_address(con->msgr->zero_page); + } + len = min((int)(PAGE_SIZE - con->out_msg_pos.page_pos), + (int)(data_len - con->out_msg_pos.data_pos)); + if (crc && !con->out_msg_pos.did_page_crc) { + void *base = kaddr + con->out_msg_pos.page_pos; + u32 tmpcrc = le32_to_cpu(con->out_msg->footer.data_crc); + + BUG_ON(kaddr == NULL); + con->out_msg->footer.data_crc = + cpu_to_le32(crc32c(tmpcrc, base, len)); + con->out_msg_pos.did_page_crc = 1; + } + + ret = kernel_sendpage(con->sock, page, + con->out_msg_pos.page_pos, len, + MSG_DONTWAIT | MSG_NOSIGNAL | + MSG_MORE); + + if (crc && msg->pages) + kunmap(page); + + if (ret <= 0) + goto out; + + con->out_msg_pos.data_pos += ret; + con->out_msg_pos.page_pos += ret; + if (ret == len) { + con->out_msg_pos.page_pos = 0; + con->out_msg_pos.page++; + con->out_msg_pos.did_page_crc = 0; + } + } + + dout("write_partial_msg_pages %p msg %p done\n", con, msg); + + /* prepare and queue up footer, too */ + if (!crc) + con->out_msg->footer.flags |= CEPH_MSG_FOOTER_NOCRC; + con->out_kvec_bytes = 0; + con->out_kvec_left = 0; + con->out_kvec_cur = con->out_kvec; + prepare_write_message_footer(con, 0); + ret = 1; +out: + return ret; +} + +/* + * write some zeros + */ +static int write_partial_skip(struct ceph_connection *con) +{ + int ret; + + while (con->out_skip > 0) { + struct kvec iov = { + .iov_base = page_address(con->msgr->zero_page), + .iov_len = min(con->out_skip, (int)PAGE_CACHE_SIZE) + }; + + ret = ceph_tcp_sendmsg(con->sock, &iov, 1, iov.iov_len, 1); + if (ret <= 0) + goto out; + con->out_skip -= ret; + } + ret = 1; +out: + return ret; +} + +/* + * Prepare to read connection handshake, or an ack. + */ +static void prepare_read_connect(struct ceph_connection *con) +{ + dout("prepare_read_connect %p\n", con); + con->in_base_pos = 0; +} + +static void prepare_read_ack(struct ceph_connection *con) +{ + dout("prepare_read_ack %p\n", con); + con->in_base_pos = 0; +} + +static void prepare_read_tag(struct ceph_connection *con) +{ + dout("prepare_read_tag %p\n", con); + con->in_base_pos = 0; + con->in_tag = CEPH_MSGR_TAG_READY; +} + +/* + * Prepare to read a message. + */ +static int prepare_read_message(struct ceph_connection *con) +{ + dout("prepare_read_message %p\n", con); + BUG_ON(con->in_msg != NULL); + con->in_base_pos = 0; + con->in_front_crc = con->in_middle_crc = con->in_data_crc = 0; + return 0; +} + + +static int read_partial(struct ceph_connection *con, + int *to, int size, void *object) +{ + *to += size; + while (con->in_base_pos < *to) { + int left = *to - con->in_base_pos; + int have = size - left; + int ret = ceph_tcp_recvmsg(con->sock, object + have, left); + if (ret <= 0) + return ret; + con->in_base_pos += ret; + } + return 1; +} + + +/* + * Read all or part of the connect-side handshake on a new connection + */ +static int read_partial_connect(struct ceph_connection *con) +{ + int ret, to = 0; + + dout("read_partial_connect %p at %d\n", con, con->in_base_pos); + + /* peer's banner */ + ret = read_partial(con, &to, strlen(CEPH_BANNER), con->in_banner); + if (ret <= 0) + goto out; + ret = read_partial(con, &to, sizeof(con->actual_peer_addr), + &con->actual_peer_addr); + if (ret <= 0) + goto out; + ret = read_partial(con, &to, sizeof(con->peer_addr_for_me), + &con->peer_addr_for_me); + if (ret <= 0) + goto out; + ret = read_partial(con, &to, sizeof(con->in_reply), &con->in_reply); + if (ret <= 0) + goto out; + + dout("read_partial_connect %p connect_seq = %u, global_seq = %u\n", + con, le32_to_cpu(con->in_reply.connect_seq), + le32_to_cpu(con->in_reply.global_seq)); +out: + return ret; +} + +/* + * Verify the hello banner looks okay. + */ +static int verify_hello(struct ceph_connection *con) +{ + if (memcmp(con->in_banner, CEPH_BANNER, strlen(CEPH_BANNER))) { + pr_err("connect to/from %s has bad banner\n", + pr_addr(&con->peer_addr.in_addr)); + con->error_msg = "protocol error, bad banner"; + return -1; + } + return 0; +} + +static bool addr_is_blank(struct sockaddr_storage *ss) +{ + switch (ss->ss_family) { + case AF_INET: + return ((struct sockaddr_in *)ss)->sin_addr.s_addr == 0; + case AF_INET6: + return + ((struct sockaddr_in6 *)ss)->sin6_addr.s6_addr32[0] == 0 && + ((struct sockaddr_in6 *)ss)->sin6_addr.s6_addr32[1] == 0 && + ((struct sockaddr_in6 *)ss)->sin6_addr.s6_addr32[2] == 0 && + ((struct sockaddr_in6 *)ss)->sin6_addr.s6_addr32[3] == 0; + } + return false; +} + +static int addr_port(struct sockaddr_storage *ss) +{ + switch (ss->ss_family) { + case AF_INET: + return ((struct sockaddr_in *)ss)->sin_port; + case AF_INET6: + return ((struct sockaddr_in6 *)ss)->sin6_port; + } + return 0; +} + +static void addr_set_port(struct sockaddr_storage *ss, int p) +{ + switch (ss->ss_family) { + case AF_INET: + ((struct sockaddr_in *)ss)->sin_port = htons(p); + case AF_INET6: + ((struct sockaddr_in6 *)ss)->sin6_port = htons(p); + } +} + +/* + * Parse an ip[:port] list into an addr array. Use the default + * monitor port if a port isn't specified. + */ +int ceph_parse_ips(const char *c, const char *end, + struct ceph_entity_addr *addr, + int max_count, int *count) +{ + int i; + const char *p = c; + + dout("parse_ips on '%.*s'\n", (int)(end-c), c); + for (i = 0; i < max_count; i++) { + const char *ipend; + struct sockaddr_storage *ss = &addr[i].in_addr; + struct sockaddr_in *in4 = (void *)ss; + struct sockaddr_in6 *in6 = (void *)ss; + int port; + + memset(ss, 0, sizeof(*ss)); + if (in4_pton(p, end - p, (u8 *)&in4->sin_addr.s_addr, + ',', &ipend)) { + ss->ss_family = AF_INET; + } else if (in6_pton(p, end - p, (u8 *)&in6->sin6_addr.s6_addr, + ',', &ipend)) { + ss->ss_family = AF_INET6; + } else { + goto bad; + } + p = ipend; + + /* port? */ + if (p < end && *p == ':') { + port = 0; + p++; + while (p < end && *p >= '0' && *p <= '9') { + port = (port * 10) + (*p - '0'); + p++; + } + if (port > 65535 || port == 0) + goto bad; + } else { + port = CEPH_MON_PORT; + } + + addr_set_port(ss, port); + + dout("parse_ips got %s\n", pr_addr(ss)); + + if (p == end) + break; + if (*p != ',') + goto bad; + p++; + } + + if (p != end) + goto bad; + + if (count) + *count = i + 1; + return 0; + +bad: + pr_err("parse_ips bad ip '%s'\n", c); + return -EINVAL; +} + +static int process_connect(struct ceph_connection *con) +{ + dout("process_connect on %p tag %d\n", con, (int)con->in_tag); + + if (verify_hello(con) < 0) + return -1; + + /* + * Make sure the other end is who we wanted. note that the other + * end may not yet know their ip address, so if it's 0.0.0.0, give + * them the benefit of the doubt. + */ + if (!ceph_entity_addr_is_local(&con->peer_addr, + &con->actual_peer_addr) && + !(addr_is_blank(&con->actual_peer_addr.in_addr) && + con->actual_peer_addr.nonce == con->peer_addr.nonce)) { + pr_err("wrong peer, want %s/%d, " + "got %s/%d, wtf\n", + pr_addr(&con->peer_addr.in_addr), + con->peer_addr.nonce, + pr_addr(&con->actual_peer_addr.in_addr), + con->actual_peer_addr.nonce); + con->error_msg = "protocol error, wrong peer"; + return -1; + } + + /* + * did we learn our address? + */ + if (addr_is_blank(&con->msgr->inst.addr.in_addr)) { + int port = addr_port(&con->msgr->inst.addr.in_addr); + + memcpy(&con->msgr->inst.addr.in_addr, + &con->peer_addr_for_me.in_addr, + sizeof(con->peer_addr_for_me.in_addr)); + addr_set_port(&con->msgr->inst.addr.in_addr, port); + dout("process_connect learned my addr is %s\n", + pr_addr(&con->msgr->inst.addr.in_addr)); + } + + switch (con->in_reply.tag) { + case CEPH_MSGR_TAG_BADPROTOVER: + dout("process_connect got BADPROTOVER my %d != their %d\n", + le32_to_cpu(con->out_connect.protocol_version), + le32_to_cpu(con->in_reply.protocol_version)); + pr_err("%s%lld %s protocol version mismatch," + " my %d != server's %d\n", + ENTITY_NAME(con->peer_name), + pr_addr(&con->peer_addr.in_addr), + le32_to_cpu(con->out_connect.protocol_version), + le32_to_cpu(con->in_reply.protocol_version)); + con->error_msg = "protocol version mismatch"; + if (con->ops->bad_proto) + con->ops->bad_proto(con); + reset_connection(con); + set_bit(CLOSED, &con->state); /* in case there's queued work */ + return -1; + + + case CEPH_MSGR_TAG_RESETSESSION: + /* + * If we connected with a large connect_seq but the peer + * has no record of a session with us (no connection, or + * connect_seq == 0), they will send RESETSESION to indicate + * that they must have reset their session, and may have + * dropped messages. + */ + dout("process_connect got RESET peer seq %u\n", + le32_to_cpu(con->in_connect.connect_seq)); + pr_err("%s%lld %s connection reset\n", + ENTITY_NAME(con->peer_name), + pr_addr(&con->peer_addr.in_addr)); + reset_connection(con); + prepare_write_connect_retry(con->msgr, con); + prepare_read_connect(con); + + /* Tell ceph about it. */ + pr_info("reset on %s%lld\n", ENTITY_NAME(con->peer_name)); + if (con->ops->peer_reset) + con->ops->peer_reset(con); + break; + + case CEPH_MSGR_TAG_RETRY_SESSION: + /* + * If we sent a smaller connect_seq than the peer has, try + * again with a larger value. + */ + dout("process_connect got RETRY my seq = %u, peer_seq = %u\n", + le32_to_cpu(con->out_connect.connect_seq), + le32_to_cpu(con->in_connect.connect_seq)); + con->connect_seq = le32_to_cpu(con->in_connect.connect_seq); + prepare_write_connect_retry(con->msgr, con); + prepare_read_connect(con); + break; + + case CEPH_MSGR_TAG_RETRY_GLOBAL: + /* + * If we sent a smaller global_seq than the peer has, try + * again with a larger value. + */ + dout("process_connect got RETRY_GLOBAL my %u, peer_gseq = %u\n", + con->peer_global_seq, + le32_to_cpu(con->in_connect.global_seq)); + get_global_seq(con->msgr, + le32_to_cpu(con->in_connect.global_seq)); + prepare_write_connect_retry(con->msgr, con); + prepare_read_connect(con); + break; + + case CEPH_MSGR_TAG_READY: + clear_bit(CONNECTING, &con->state); + if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY) + set_bit(LOSSYRX, &con->state); + con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq); + con->connect_seq++; + dout("process_connect got READY gseq %d cseq %d (%d)\n", + con->peer_global_seq, + le32_to_cpu(con->in_reply.connect_seq), + con->connect_seq); + WARN_ON(con->connect_seq != + le32_to_cpu(con->in_reply.connect_seq)); + + con->delay = 0; /* reset backoff memory */ + prepare_read_tag(con); + break; + + case CEPH_MSGR_TAG_WAIT: + /* + * If there is a connection race (we are opening + * connections to each other), one of us may just have + * to WAIT. This shouldn't happen if we are the + * client. + */ + pr_err("process_connect peer connecting WAIT\n"); + + default: + pr_err("connect protocol error, will retry\n"); + con->error_msg = "protocol error, garbage tag during connect"; + return -1; + } + return 0; +} + + +/* + * read (part of) an ack + */ +static int read_partial_ack(struct ceph_connection *con) +{ + int to = 0; + + return read_partial(con, &to, sizeof(con->in_temp_ack), + &con->in_temp_ack); +} + + +/* + * We can finally discard anything that's been acked. + */ +static void process_ack(struct ceph_connection *con) +{ + struct ceph_msg *m; + u64 ack = le64_to_cpu(con->in_temp_ack); + u64 seq; + + mutex_lock(&con->out_mutex); + while (!list_empty(&con->out_sent)) { + m = list_first_entry(&con->out_sent, struct ceph_msg, + list_head); + seq = le64_to_cpu(m->hdr.seq); + if (seq > ack) + break; + dout("got ack for seq %llu type %d at %p\n", seq, + le16_to_cpu(m->hdr.type), m); + ceph_msg_remove(m); + } + mutex_unlock(&con->out_mutex); + prepare_read_tag(con); +} + + + + + + +/* + * read (part of) a message. + */ +static int read_partial_message(struct ceph_connection *con) +{ + struct ceph_msg *m = con->in_msg; + void *p; + int ret; + int to, want, left; + unsigned front_len, middle_len, data_len, data_off; + int datacrc = con->msgr->nocrc; + + dout("read_partial_message con %p msg %p\n", con, m); + + /* header */ + while (con->in_base_pos < sizeof(con->in_hdr)) { + left = sizeof(con->in_hdr) - con->in_base_pos; + ret = ceph_tcp_recvmsg(con->sock, + (char *)&con->in_hdr + con->in_base_pos, + left); + if (ret <= 0) + return ret; + con->in_base_pos += ret; + if (con->in_base_pos == sizeof(con->in_hdr)) { + u32 crc = crc32c(0, (void *)&con->in_hdr, + sizeof(con->in_hdr) - sizeof(con->in_hdr.crc)); + if (crc != le32_to_cpu(con->in_hdr.crc)) { + pr_err("read_partial_message bad hdr " + " crc %u != expected %u\n", + crc, con->in_hdr.crc); + return -EBADMSG; + } + } + } + + front_len = le32_to_cpu(con->in_hdr.front_len); + if (front_len > CEPH_MSG_MAX_FRONT_LEN) + return -EIO; + middle_len = le32_to_cpu(con->in_hdr.middle_len); + if (middle_len > CEPH_MSG_MAX_DATA_LEN) + return -EIO; + data_len = le32_to_cpu(con->in_hdr.data_len); + if (data_len > CEPH_MSG_MAX_DATA_LEN) + return -EIO; + + /* allocate message? */ + if (!con->in_msg) { + dout("got hdr type %d front %d data %d\n", con->in_hdr.type, + con->in_hdr.front_len, con->in_hdr.data_len); + con->in_msg = con->ops->alloc_msg(con, &con->in_hdr); + if (!con->in_msg) { + /* skip this message */ + dout("alloc_msg returned NULL, skipping message\n"); + con->in_base_pos = -front_len - middle_len - data_len - + sizeof(m->footer); + con->in_tag = CEPH_MSGR_TAG_READY; + return 0; + } + if (IS_ERR(con->in_msg)) { + ret = PTR_ERR(con->in_msg); + con->in_msg = NULL; + con->error_msg = "out of memory for incoming message"; + return ret; + } + m = con->in_msg; + m->front.iov_len = 0; /* haven't read it yet */ + memcpy(&m->hdr, &con->in_hdr, sizeof(con->in_hdr)); + } + + /* front */ + while (m->front.iov_len < front_len) { + BUG_ON(m->front.iov_base == NULL); + left = front_len - m->front.iov_len; + ret = ceph_tcp_recvmsg(con->sock, (char *)m->front.iov_base + + m->front.iov_len, left); + if (ret <= 0) + return ret; + m->front.iov_len += ret; + if (m->front.iov_len == front_len) + con->in_front_crc = crc32c(0, m->front.iov_base, + m->front.iov_len); + } + + /* middle */ + while (middle_len > 0 && (!m->middle || + m->middle->vec.iov_len < middle_len)) { + if (m->middle == NULL) { + ret = -EOPNOTSUPP; + if (con->ops->alloc_middle) + ret = con->ops->alloc_middle(con, m); + if (ret < 0) { + dout("alloc_middle failed, skipping payload\n"); + con->in_base_pos = -middle_len - data_len + - sizeof(m->footer); + ceph_msg_put(con->in_msg); + con->in_msg = NULL; + con->in_tag = CEPH_MSGR_TAG_READY; + return 0; + } + m->middle->vec.iov_len = 0; + } + left = middle_len - m->middle->vec.iov_len; + ret = ceph_tcp_recvmsg(con->sock, + (char *)m->middle->vec.iov_base + + m->middle->vec.iov_len, left); + if (ret <= 0) + return ret; + m->middle->vec.iov_len += ret; + if (m->middle->vec.iov_len == middle_len) + con->in_middle_crc = crc32c(0, m->middle->vec.iov_base, + m->middle->vec.iov_len); + } + + /* (page) data */ + data_off = le16_to_cpu(m->hdr.data_off); + if (data_len == 0) + goto no_data; + + if (m->nr_pages == 0) { + con->in_msg_pos.page = 0; + con->in_msg_pos.page_pos = data_off & ~PAGE_MASK; + con->in_msg_pos.data_pos = 0; + /* find pages for data payload */ + want = calc_pages_for(data_off & ~PAGE_MASK, data_len); + ret = -1; + if (con->ops->prepare_pages) + ret = con->ops->prepare_pages(con, m, want); + if (ret < 0) { + dout("%p prepare_pages failed, skipping payload\n", m); + con->in_base_pos = -data_len - sizeof(m->footer); + ceph_msg_put(con->in_msg); + con->in_msg = NULL; + con->in_tag = CEPH_MSGR_TAG_READY; + return 0; + } + BUG_ON(m->nr_pages < want); + } + while (con->in_msg_pos.data_pos < data_len) { + left = min((int)(data_len - con->in_msg_pos.data_pos), + (int)(PAGE_SIZE - con->in_msg_pos.page_pos)); + BUG_ON(m->pages == NULL); + p = kmap(m->pages[con->in_msg_pos.page]); + ret = ceph_tcp_recvmsg(con->sock, p + con->in_msg_pos.page_pos, + left); + if (ret > 0 && datacrc) + con->in_data_crc = + crc32c(con->in_data_crc, + p + con->in_msg_pos.page_pos, ret); + kunmap(m->pages[con->in_msg_pos.page]); + if (ret <= 0) + return ret; + con->in_msg_pos.data_pos += ret; + con->in_msg_pos.page_pos += ret; + if (con->in_msg_pos.page_pos == PAGE_SIZE) { + con->in_msg_pos.page_pos = 0; + con->in_msg_pos.page++; + } + } + +no_data: + /* footer */ + to = sizeof(m->hdr) + sizeof(m->footer); + while (con->in_base_pos < to) { + left = to - con->in_base_pos; + ret = ceph_tcp_recvmsg(con->sock, (char *)&m->footer + + (con->in_base_pos - sizeof(m->hdr)), + left); + if (ret <= 0) + return ret; + con->in_base_pos += ret; + } + dout("read_partial_message got msg %p %d (%u) + %d (%u) + %d (%u)\n", + m, front_len, m->footer.front_crc, middle_len, + m->footer.middle_crc, data_len, m->footer.data_crc); + + /* crc ok? */ + if (con->in_front_crc != le32_to_cpu(m->footer.front_crc)) { + pr_err("read_partial_message %p front crc %u != exp. %u\n", + m, con->in_front_crc, m->footer.front_crc); + return -EBADMSG; + } + if (con->in_middle_crc != le32_to_cpu(m->footer.middle_crc)) { + pr_err("read_partial_message %p middle crc %u != exp %u\n", + m, con->in_middle_crc, m->footer.middle_crc); + return -EBADMSG; + } + if (datacrc && + (m->footer.flags & CEPH_MSG_FOOTER_NOCRC) == 0 && + con->in_data_crc != le32_to_cpu(m->footer.data_crc)) { + pr_err("read_partial_message %p data crc %u != exp. %u\n", m, + con->in_data_crc, le32_to_cpu(m->footer.data_crc)); + return -EBADMSG; + } + + return 1; /* done! */ +} + +/* + * Process message. This happens in the worker thread. The callback should + * be careful not to do anything that waits on other incoming messages or it + * may deadlock. + */ +static void process_message(struct ceph_connection *con) +{ + struct ceph_msg *msg = con->in_msg; + + con->in_msg = NULL; + + /* if first message, set peer_name */ + if (con->peer_name.type == 0) + con->peer_name = msg->hdr.src.name; + + mutex_lock(&con->out_mutex); + con->in_seq++; + mutex_unlock(&con->out_mutex); + + dout("===== %p %llu from %s%lld %d=%s len %d+%d (%u %u %u) =====\n", + msg, le64_to_cpu(msg->hdr.seq), + ENTITY_NAME(msg->hdr.src.name), + le16_to_cpu(msg->hdr.type), + ceph_msg_type_name(le16_to_cpu(msg->hdr.type)), + le32_to_cpu(msg->hdr.front_len), + le32_to_cpu(msg->hdr.data_len), + con->in_front_crc, con->in_middle_crc, con->in_data_crc); + con->ops->dispatch(con, msg); + prepare_read_tag(con); +} + + +/* + * Write something to the socket. Called in a worker thread when the + * socket appears to be writeable and we have something ready to send. + */ +static int try_write(struct ceph_connection *con) +{ + struct ceph_messenger *msgr = con->msgr; + int ret = 1; + + dout("try_write start %p state %lu nref %d\n", con, con->state, + atomic_read(&con->nref)); + + mutex_lock(&con->out_mutex); +more: + dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes); + + /* open the socket first? */ + if (con->sock == NULL) { + /* + * if we were STANDBY and are reconnecting _this_ + * connection, bump connect_seq now. Always bump + * global_seq. + */ + if (test_and_clear_bit(STANDBY, &con->state)) + con->connect_seq++; + + prepare_write_connect(msgr, con); + prepare_read_connect(con); + set_bit(CONNECTING, &con->state); + + con->in_tag = CEPH_MSGR_TAG_READY; + dout("try_write initiating connect on %p new state %lu\n", + con, con->state); + con->sock = ceph_tcp_connect(con); + if (IS_ERR(con->sock)) { + con->sock = NULL; + con->error_msg = "connect error"; + ret = -1; + goto out; + } + } + +more_kvec: + /* kvec data queued? */ + if (con->out_skip) { + ret = write_partial_skip(con); + if (ret <= 0) + goto done; + if (ret < 0) { + dout("try_write write_partial_skip err %d\n", ret); + goto done; + } + } + if (con->out_kvec_left) { + ret = write_partial_kvec(con); + if (ret <= 0) + goto done; + if (ret < 0) { + dout("try_write write_partial_kvec err %d\n", ret); + goto done; + } + } + + /* msg pages? */ + if (con->out_msg) { + ret = write_partial_msg_pages(con); + if (ret == 1) + goto more_kvec; /* we need to send the footer, too! */ + if (ret == 0) + goto done; + if (ret < 0) { + dout("try_write write_partial_msg_pages err %d\n", + ret); + goto done; + } + } + + if (!test_bit(CONNECTING, &con->state)) { + /* is anything else pending? */ + if (!list_empty(&con->out_queue)) { + prepare_write_message(con); + goto more; + } + if (con->in_seq > con->in_seq_acked) { + prepare_write_ack(con); + goto more; + } + if (test_and_clear_bit(KEEPALIVE_PENDING, &con->state)) { + prepare_write_keepalive(con); + goto more; + } + } + + /* Nothing to do! */ + clear_bit(WRITE_PENDING, &con->state); + dout("try_write nothing else to write.\n"); +done: + ret = 0; +out: + mutex_unlock(&con->out_mutex); + dout("try_write done on %p\n", con); + return ret; +} + + + +/* + * Read what we can from the socket. + */ +static int try_read(struct ceph_connection *con) +{ + struct ceph_messenger *msgr; + int ret = -1; + + if (!con->sock) + return 0; + + if (test_bit(STANDBY, &con->state)) + return 0; + + dout("try_read start on %p\n", con); + msgr = con->msgr; + +more: + dout("try_read tag %d in_base_pos %d\n", (int)con->in_tag, + con->in_base_pos); + if (test_bit(CONNECTING, &con->state)) { + dout("try_read connecting\n"); + ret = read_partial_connect(con); + if (ret <= 0) + goto done; + if (process_connect(con) < 0) { + ret = -1; + goto out; + } + goto more; + } + + if (con->in_base_pos < 0) { + /* + * skipping + discarding content. + * + * FIXME: there must be a better way to do this! + */ + static char buf[1024]; + int skip = min(1024, -con->in_base_pos); + dout("skipping %d / %d bytes\n", skip, -con->in_base_pos); + ret = ceph_tcp_recvmsg(con->sock, buf, skip); + if (ret <= 0) + goto done; + con->in_base_pos += ret; + if (con->in_base_pos) + goto more; + } + if (con->in_tag == CEPH_MSGR_TAG_READY) { + /* + * what's next? + */ + ret = ceph_tcp_recvmsg(con->sock, &con->in_tag, 1); + if (ret <= 0) + goto done; + dout("try_read got tag %d\n", (int)con->in_tag); + switch (con->in_tag) { + case CEPH_MSGR_TAG_MSG: + prepare_read_message(con); + break; + case CEPH_MSGR_TAG_ACK: + prepare_read_ack(con); + break; + case CEPH_MSGR_TAG_CLOSE: + set_bit(CLOSED, &con->state); /* fixme */ + goto done; + default: + goto bad_tag; + } + } + if (con->in_tag == CEPH_MSGR_TAG_MSG) { + ret = read_partial_message(con); + if (ret <= 0) { + switch (ret) { + case -EBADMSG: + con->error_msg = "bad crc"; + ret = -EIO; + goto out; + case -EIO: + con->error_msg = "io error"; + goto out; + default: + goto done; + } + } + if (con->in_tag == CEPH_MSGR_TAG_READY) + goto more; + process_message(con); + goto more; + } + if (con->in_tag == CEPH_MSGR_TAG_ACK) { + ret = read_partial_ack(con); + if (ret <= 0) + goto done; + process_ack(con); + goto more; + } + +done: + ret = 0; +out: + dout("try_read done on %p\n", con); + return ret; + +bad_tag: + pr_err("try_read bad con->in_tag = %d\n", (int)con->in_tag); + con->error_msg = "protocol error, garbage tag"; + ret = -1; + goto out; +} + + +/* + * Atomically queue work on a connection. Bump @con reference to + * avoid races with connection teardown. + * + * There is some trickery going on with QUEUED and BUSY because we + * only want a _single_ thread operating on each connection at any + * point in time, but we want to use all available CPUs. + * + * The worker thread only proceeds if it can atomically set BUSY. It + * clears QUEUED and does it's thing. When it thinks it's done, it + * clears BUSY, then rechecks QUEUED.. if it's set again, it loops + * (tries again to set BUSY). + * + * To queue work, we first set QUEUED, _then_ if BUSY isn't set, we + * try to queue work. If that fails (work is already queued, or BUSY) + * we give up (work also already being done or is queued) but leave QUEUED + * set so that the worker thread will loop if necessary. + */ +static void queue_con(struct ceph_connection *con) +{ + if (test_bit(DEAD, &con->state)) { + dout("queue_con %p ignoring: DEAD\n", + con); + return; + } + + if (!con->ops->get(con)) { + dout("queue_con %p ref count 0\n", con); + return; + } + + set_bit(QUEUED, &con->state); + if (test_bit(BUSY, &con->state)) { + dout("queue_con %p - already BUSY\n", con); + con->ops->put(con); + } else if (!queue_work(ceph_msgr_wq, &con->work.work)) { + dout("queue_con %p - already queued\n", con); + con->ops->put(con); + } else { + dout("queue_con %p\n", con); + } +} + +/* + * Do some work on a connection. Drop a connection ref when we're done. + */ +static void con_work(struct work_struct *work) +{ + struct ceph_connection *con = container_of(work, struct ceph_connection, + work.work); + int backoff = 0; + +more: + if (test_and_set_bit(BUSY, &con->state) != 0) { + dout("con_work %p BUSY already set\n", con); + goto out; + } + dout("con_work %p start, clearing QUEUED\n", con); + clear_bit(QUEUED, &con->state); + + if (test_bit(CLOSED, &con->state)) { /* e.g. if we are replaced */ + dout("con_work CLOSED\n"); + con_close_socket(con); + goto done; + } + if (test_and_clear_bit(OPENING, &con->state)) { + /* reopen w/ new peer */ + dout("con_work OPENING\n"); + con_close_socket(con); + } + + if (test_and_clear_bit(SOCK_CLOSED, &con->state) || + try_read(con) < 0 || + try_write(con) < 0) { + backoff = 1; + ceph_fault(con); /* error/fault path */ + } + +done: + clear_bit(BUSY, &con->state); + dout("con->state=%lu\n", con->state); + if (test_bit(QUEUED, &con->state)) { + if (!backoff) { + dout("con_work %p QUEUED reset, looping\n", con); + goto more; + } + dout("con_work %p QUEUED reset, but just faulted\n", con); + clear_bit(QUEUED, &con->state); + } + dout("con_work %p done\n", con); + +out: + con->ops->put(con); +} + + +/* + * Generic error/fault handler. A retry mechanism is used with + * exponential backoff + */ +static void ceph_fault(struct ceph_connection *con) +{ + pr_err("%s%lld %s %s\n", ENTITY_NAME(con->peer_name), + pr_addr(&con->peer_addr.in_addr), con->error_msg); + dout("fault %p state %lu to peer %s\n", + con, con->state, pr_addr(&con->peer_addr.in_addr)); + + if (test_bit(LOSSYTX, &con->state)) { + dout("fault on LOSSYTX channel\n"); + goto out; + } + + clear_bit(BUSY, &con->state); /* to avoid an improbable race */ + + con_close_socket(con); + con->in_msg = NULL; + + /* If there are no messages in the queue, place the connection + * in a STANDBY state (i.e., don't try to reconnect just yet). */ + mutex_lock(&con->out_mutex); + if (list_empty(&con->out_queue) && !con->out_keepalive_pending) { + dout("fault setting STANDBY\n"); + set_bit(STANDBY, &con->state); + mutex_unlock(&con->out_mutex); + goto out; + } + + /* Requeue anything that hasn't been acked, and retry after a + * delay. */ + list_splice_init(&con->out_sent, &con->out_queue); + mutex_unlock(&con->out_mutex); + + if (con->delay == 0) + con->delay = BASE_DELAY_INTERVAL; + else if (con->delay < MAX_DELAY_INTERVAL) + con->delay *= 2; + + /* explicitly schedule work to try to reconnect again later. */ + dout("fault queueing %p delay %lu\n", con, con->delay); + con->ops->get(con); + if (queue_delayed_work(ceph_msgr_wq, &con->work, + round_jiffies_relative(con->delay)) == 0) + con->ops->put(con); + +out: + if (con->ops->fault) + con->ops->fault(con); +} + + + +/* + * create a new messenger instance + */ +struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr) +{ + struct ceph_messenger *msgr; + + msgr = kzalloc(sizeof(*msgr), GFP_KERNEL); + if (msgr == NULL) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&msgr->global_seq_lock); + + /* the zero page is needed if a request is "canceled" while the message + * is being written over the socket */ + msgr->zero_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!msgr->zero_page) { + kfree(msgr); + return ERR_PTR(-ENOMEM); + } + kmap(msgr->zero_page); + + if (myaddr) + msgr->inst.addr = *myaddr; + + /* select a random nonce */ + get_random_bytes(&msgr->inst.addr.nonce, + sizeof(msgr->inst.addr.nonce)); + + dout("messenger_create %p\n", msgr); + return msgr; +} + +void ceph_messenger_destroy(struct ceph_messenger *msgr) +{ + dout("destroy %p\n", msgr); + kunmap(msgr->zero_page); + __free_page(msgr->zero_page); + kfree(msgr); + dout("destroyed messenger %p\n", msgr); +} + +/* + * Queue up an outgoing message on the given connection. + */ +void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) +{ + if (test_bit(CLOSED, &con->state)) { + dout("con_send %p closed, dropping %p\n", con, msg); + ceph_msg_put(msg); + return; + } + + /* set src+dst */ + msg->hdr.src = con->msgr->inst; + msg->hdr.orig_src = con->msgr->inst; + msg->hdr.dst_erank = con->peer_addr.erank; + + /* queue */ + mutex_lock(&con->out_mutex); + BUG_ON(!list_empty(&msg->list_head)); + list_add_tail(&msg->list_head, &con->out_queue); + dout("----- %p to %s%lld %d=%s len %d+%d+%d -----\n", msg, + ENTITY_NAME(con->peer_name), le16_to_cpu(msg->hdr.type), + ceph_msg_type_name(le16_to_cpu(msg->hdr.type)), + le32_to_cpu(msg->hdr.front_len), + le32_to_cpu(msg->hdr.middle_len), + le32_to_cpu(msg->hdr.data_len)); + mutex_unlock(&con->out_mutex); + + /* if there wasn't anything waiting to send before, queue + * new work */ + if (test_and_set_bit(WRITE_PENDING, &con->state) == 0) + queue_con(con); +} + +/* + * Revoke a message that was previously queued for send + */ +void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) +{ + mutex_lock(&con->out_mutex); + if (!list_empty(&msg->list_head)) { + dout("con_revoke %p msg %p\n", con, msg); + list_del_init(&msg->list_head); + ceph_msg_put(msg); + msg->hdr.seq = 0; + if (con->out_msg == msg) + con->out_msg = NULL; + if (con->out_kvec_is_msg) { + con->out_skip = con->out_kvec_bytes; + con->out_kvec_is_msg = false; + } + } else { + dout("con_revoke %p msg %p - not queued (sent?)\n", con, msg); + } + mutex_unlock(&con->out_mutex); +} + +/* + * Queue a keepalive byte to ensure the tcp connection is alive. + */ +void ceph_con_keepalive(struct ceph_connection *con) +{ + if (test_and_set_bit(KEEPALIVE_PENDING, &con->state) == 0 && + test_and_set_bit(WRITE_PENDING, &con->state) == 0) + queue_con(con); +} + + +/* + * construct a new message with given type, size + * the new msg has a ref count of 1. + */ +struct ceph_msg *ceph_msg_new(int type, int front_len, + int page_len, int page_off, struct page **pages) +{ + struct ceph_msg *m; + + m = kmalloc(sizeof(*m), GFP_NOFS); + if (m == NULL) + goto out; + atomic_set(&m->nref, 1); + INIT_LIST_HEAD(&m->list_head); + + m->hdr.type = cpu_to_le16(type); + m->hdr.front_len = cpu_to_le32(front_len); + m->hdr.middle_len = 0; + m->hdr.data_len = cpu_to_le32(page_len); + m->hdr.data_off = cpu_to_le16(page_off); + m->hdr.priority = cpu_to_le16(CEPH_MSG_PRIO_DEFAULT); + m->footer.front_crc = 0; + m->footer.middle_crc = 0; + m->footer.data_crc = 0; + m->front_max = front_len; + m->front_is_vmalloc = false; + m->more_to_follow = false; + m->pool = NULL; + + /* front */ + if (front_len) { + if (front_len > PAGE_CACHE_SIZE) { + m->front.iov_base = __vmalloc(front_len, GFP_NOFS, + PAGE_KERNEL); + m->front_is_vmalloc = true; + } else { + m->front.iov_base = kmalloc(front_len, GFP_NOFS); + } + if (m->front.iov_base == NULL) { + pr_err("msg_new can't allocate %d bytes\n", + front_len); + goto out2; + } + } else { + m->front.iov_base = NULL; + } + m->front.iov_len = front_len; + + /* middle */ + m->middle = NULL; + + /* data */ + m->nr_pages = calc_pages_for(page_off, page_len); + m->pages = pages; + + dout("ceph_msg_new %p page %d~%d -> %d\n", m, page_off, page_len, + m->nr_pages); + return m; + +out2: + ceph_msg_put(m); +out: + pr_err("msg_new can't create type %d len %d\n", type, front_len); + return ERR_PTR(-ENOMEM); +} + +/* + * Generic message allocator, for incoming messages. + */ +struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, + struct ceph_msg_header *hdr) +{ + int type = le16_to_cpu(hdr->type); + int front_len = le32_to_cpu(hdr->front_len); + struct ceph_msg *msg = ceph_msg_new(type, front_len, 0, 0, NULL); + + if (!msg) { + pr_err("unable to allocate msg type %d len %d\n", + type, front_len); + return ERR_PTR(-ENOMEM); + } + return msg; +} + +/* + * Allocate "middle" portion of a message, if it is needed and wasn't + * allocated by alloc_msg. This allows us to read a small fixed-size + * per-type header in the front and then gracefully fail (i.e., + * propagate the error to the caller based on info in the front) when + * the middle is too large. + */ +int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg) +{ + int type = le16_to_cpu(msg->hdr.type); + int middle_len = le32_to_cpu(msg->hdr.middle_len); + + dout("alloc_middle %p type %d %s middle_len %d\n", msg, type, + ceph_msg_type_name(type), middle_len); + BUG_ON(!middle_len); + BUG_ON(msg->middle); + + msg->middle = ceph_buffer_new_alloc(middle_len, GFP_NOFS); + if (!msg->middle) + return -ENOMEM; + return 0; +} + + +/* + * Free a generically kmalloc'd message. + */ +void ceph_msg_kfree(struct ceph_msg *m) +{ + dout("msg_kfree %p\n", m); + if (m->front_is_vmalloc) + vfree(m->front.iov_base); + else + kfree(m->front.iov_base); + kfree(m); +} + +/* + * Drop a msg ref. Destroy as needed. + */ +void ceph_msg_put(struct ceph_msg *m) +{ + dout("ceph_msg_put %p %d -> %d\n", m, atomic_read(&m->nref), + atomic_read(&m->nref)-1); + if (atomic_read(&m->nref) <= 0) { + pr_err("bad ceph_msg_put on %p %llu %d=%s %d+%d\n", + m, le64_to_cpu(m->hdr.seq), + le16_to_cpu(m->hdr.type), + ceph_msg_type_name(le16_to_cpu(m->hdr.type)), + le32_to_cpu(m->hdr.front_len), + le32_to_cpu(m->hdr.data_len)); + WARN_ON(1); + } + if (atomic_dec_and_test(&m->nref)) { + dout("ceph_msg_put last one on %p\n", m); + WARN_ON(!list_empty(&m->list_head)); + + /* drop middle, data, if any */ + if (m->middle) { + ceph_buffer_put(m->middle); + m->middle = NULL; + } + m->nr_pages = 0; + m->pages = NULL; + + if (m->pool) + ceph_msgpool_put(m->pool, m); + else + ceph_msg_kfree(m); + } +} diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h new file mode 100644 index 0000000..dcd98b6 --- /dev/null +++ b/fs/ceph/messenger.h @@ -0,0 +1,243 @@ +#ifndef __FS_CEPH_MESSENGER_H +#define __FS_CEPH_MESSENGER_H + +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "buffer.h" + +struct ceph_msg; +struct ceph_connection; + +extern struct workqueue_struct *ceph_msgr_wq; /* receive work queue */ + +/* + * Ceph defines these callbacks for handling connection events. + */ +struct ceph_connection_operations { + struct ceph_connection *(*get)(struct ceph_connection *); + void (*put)(struct ceph_connection *); + + /* handle an incoming message. */ + void (*dispatch) (struct ceph_connection *con, struct ceph_msg *m); + + /* protocol version mismatch */ + void (*bad_proto) (struct ceph_connection *con); + + /* there was some error on the socket (disconnect, whatever) */ + void (*fault) (struct ceph_connection *con); + + /* a remote host as terminated a message exchange session, and messages + * we sent (or they tried to send us) may be lost. */ + void (*peer_reset) (struct ceph_connection *con); + + struct ceph_msg * (*alloc_msg) (struct ceph_connection *con, + struct ceph_msg_header *hdr); + int (*alloc_middle) (struct ceph_connection *con, + struct ceph_msg *msg); + /* an incoming message has a data payload; tell me what pages I + * should read the data into. */ + int (*prepare_pages) (struct ceph_connection *con, struct ceph_msg *m, + int want); +}; + +extern const char *ceph_name_type_str(int t); + +/* use format string %s%d */ +#define ENTITY_NAME(n) ceph_name_type_str((n).type), le64_to_cpu((n).num) + +struct ceph_messenger { + struct ceph_entity_inst inst; /* my name+address */ + struct page *zero_page; /* used in certain error cases */ + + bool nocrc; + + /* + * the global_seq counts connections i (attempt to) initiate + * in order to disambiguate certain connect race conditions. + */ + u32 global_seq; + spinlock_t global_seq_lock; +}; + +/* + * a single message. it contains a header (src, dest, message type, etc.), + * footer (crc values, mainly), a "front" message body, and possibly a + * data payload (stored in some number of pages). + */ +struct ceph_msg { + struct ceph_msg_header hdr; /* header */ + struct ceph_msg_footer footer; /* footer */ + struct kvec front; /* unaligned blobs of message */ + struct ceph_buffer *middle; + struct page **pages; /* data payload. NOT OWNER. */ + unsigned nr_pages; /* size of page array */ + struct list_head list_head; + atomic_t nref; + bool front_is_vmalloc; + bool more_to_follow; + int front_max; + + struct ceph_msgpool *pool; +}; + +struct ceph_msg_pos { + int page, page_pos; /* which page; offset in page */ + int data_pos; /* offset in data payload */ + int did_page_crc; /* true if we've calculated crc for current page */ +}; + +/* ceph connection fault delay defaults, for exponential backoff */ +#define BASE_DELAY_INTERVAL (HZ/2) +#define MAX_DELAY_INTERVAL (5 * 60 * HZ) + +/* + * ceph_connection state bit flags + * + * QUEUED and BUSY are used together to ensure that only a single + * thread is currently opening, reading or writing data to the socket. + */ +#define LOSSYTX 0 /* we can close channel or drop messages on errors */ +#define LOSSYRX 1 /* peer may reset/drop messages */ +#define CONNECTING 2 +#define KEEPALIVE_PENDING 3 +#define WRITE_PENDING 4 /* we have data ready to send */ +#define QUEUED 5 /* there is work queued on this connection */ +#define BUSY 6 /* work is being done */ +#define STANDBY 8 /* no outgoing messages, socket closed. we keep + * the ceph_connection around to maintain shared + * state with the peer. */ +#define CLOSED 10 /* we've closed the connection */ +#define SOCK_CLOSED 11 /* socket state changed to closed */ +#define REGISTERED 12 /* connection appears in con_tree */ +#define OPENING 13 /* open connection w/ (possibly new) peer */ +#define DEAD 14 /* dead, about to kfree */ + +/* + * A single connection with another host. + * + * We maintain a queue of outgoing messages, and some session state to + * ensure that we can preserve the lossless, ordered delivery of + * messages in the case of a TCP disconnect. + */ +struct ceph_connection { + void *private; + atomic_t nref; + + const struct ceph_connection_operations *ops; + + struct ceph_messenger *msgr; + struct socket *sock; + unsigned long state; /* connection state (see flags above) */ + const char *error_msg; /* error message, if any */ + + struct ceph_entity_addr peer_addr; /* peer address */ + struct ceph_entity_name peer_name; /* peer name */ + struct ceph_entity_addr peer_addr_for_me; + u32 connect_seq; /* identify the most recent connection + attempt for this connection, client */ + u32 peer_global_seq; /* peer's global seq for this connection */ + + /* out queue */ + struct mutex out_mutex; + struct list_head out_queue; + struct list_head out_sent; /* sending or sent but unacked */ + u64 out_seq; /* last message queued for send */ + u64 out_seq_sent; /* last message sent */ + bool out_keepalive_pending; + + u64 in_seq, in_seq_acked; /* last message received, acked */ + + /* connection negotiation temps */ + char in_banner[CEPH_BANNER_MAX_LEN]; + union { + struct { /* outgoing connection */ + struct ceph_msg_connect out_connect; + struct ceph_msg_connect_reply in_reply; + }; + struct { /* incoming */ + struct ceph_msg_connect in_connect; + struct ceph_msg_connect_reply out_reply; + }; + }; + struct ceph_entity_addr actual_peer_addr; + + /* message out temps */ + struct ceph_msg *out_msg; /* sending message (== tail of + out_sent) */ + struct ceph_msg_pos out_msg_pos; + + struct kvec out_kvec[8], /* sending header/footer data */ + *out_kvec_cur; + int out_kvec_left; /* kvec's left in out_kvec */ + int out_skip; /* skip this many bytes */ + int out_kvec_bytes; /* total bytes left */ + bool out_kvec_is_msg; /* kvec refers to out_msg */ + int out_more; /* there is more data after the kvecs */ + __le64 out_temp_ack; /* for writing an ack */ + + /* message in temps */ + struct ceph_msg_header in_hdr; + struct ceph_msg *in_msg; + struct ceph_msg_pos in_msg_pos; + u32 in_front_crc, in_middle_crc, in_data_crc; /* calculated crc */ + + char in_tag; /* protocol control byte */ + int in_base_pos; /* bytes read */ + __le64 in_temp_ack; /* for reading an ack */ + + struct delayed_work work; /* send|recv work */ + unsigned long delay; /* current delay interval */ +}; + + +extern const char *pr_addr(const struct sockaddr_storage *ss); +extern int ceph_parse_ips(const char *c, const char *end, + struct ceph_entity_addr *addr, + int max_count, int *count); + + +extern int ceph_msgr_init(void); +extern void ceph_msgr_exit(void); + +extern struct ceph_messenger *ceph_messenger_create( + struct ceph_entity_addr *myaddr); +extern void ceph_messenger_destroy(struct ceph_messenger *); + +extern void ceph_con_init(struct ceph_messenger *msgr, + struct ceph_connection *con); +extern void ceph_con_shutdown(struct ceph_connection *con); +extern void ceph_con_open(struct ceph_connection *con, + struct ceph_entity_addr *addr); +extern void ceph_con_close(struct ceph_connection *con); +extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg); +extern void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg); +extern void ceph_con_keepalive(struct ceph_connection *con); +extern struct ceph_connection *ceph_con_get(struct ceph_connection *con); +extern void ceph_con_put(struct ceph_connection *con); + +extern struct ceph_msg *ceph_msg_new(int type, int front_len, + int page_len, int page_off, + struct page **pages); +extern void ceph_msg_kfree(struct ceph_msg *m); + +extern struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, + struct ceph_msg_header *hdr); +extern int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg); + + +static inline struct ceph_msg *ceph_msg_get(struct ceph_msg *msg) +{ + dout("ceph_msg_get %p %d -> %d\n", msg, atomic_read(&msg->nref), + atomic_read(&msg->nref)+1); + atomic_inc(&msg->nref); + return msg; +} +extern void ceph_msg_put(struct ceph_msg *msg); + +#endif -- cgit v0.10.2 From 8fc91fd85950d106883852c6d215614ec28cc92d Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:13 -0700 Subject: ceph: message pools The msgpool is a basic mempool_t-like structure to preallocate messages we expect to receive over the wire. This ensures we have the necessary memory preallocated to process replies to requests, or to process unsolicited messages from various servers. Signed-off-by: Sage Weil diff --git a/fs/ceph/msgpool.c b/fs/ceph/msgpool.c new file mode 100644 index 0000000..39d4d7e --- /dev/null +++ b/fs/ceph/msgpool.c @@ -0,0 +1,167 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include + +#include "msgpool.h" + +/* + * We use msg pools to preallocate memory for messages we expect to + * receive over the wire, to avoid getting ourselves into OOM + * conditions at unexpected times. We take use a few different + * strategies: + * + * - for request/response type interactions, we preallocate the + * memory needed for the response when we generate the request. + * + * - for messages we can receive at any time from the MDS, we preallocate + * a pool of messages we can re-use. + * + * - for writeback, we preallocate some number of messages to use for + * requests and their replies, so that we always make forward + * progress. + * + * The msgpool behaves like a mempool_t, but keeps preallocated + * ceph_msgs strung together on a list_head instead of using a pointer + * vector. This avoids vector reallocation when we adjust the number + * of preallocated items (which happens frequently). + */ + + +/* + * Allocate or release as necessary to meet our target pool size. + */ +static int __fill_msgpool(struct ceph_msgpool *pool) +{ + struct ceph_msg *msg; + + while (pool->num < pool->min) { + dout("fill_msgpool %p %d/%d allocating\n", pool, pool->num, + pool->min); + spin_unlock(&pool->lock); + msg = ceph_msg_new(0, pool->front_len, 0, 0, NULL); + spin_lock(&pool->lock); + if (IS_ERR(msg)) + return PTR_ERR(msg); + msg->pool = pool; + list_add(&msg->list_head, &pool->msgs); + pool->num++; + } + while (pool->num > pool->min) { + msg = list_first_entry(&pool->msgs, struct ceph_msg, list_head); + dout("fill_msgpool %p %d/%d releasing %p\n", pool, pool->num, + pool->min, msg); + list_del_init(&msg->list_head); + pool->num--; + ceph_msg_kfree(msg); + } + return 0; +} + +int ceph_msgpool_init(struct ceph_msgpool *pool, + int front_len, int min, bool blocking) +{ + int ret; + + dout("msgpool_init %p front_len %d min %d\n", pool, front_len, min); + spin_lock_init(&pool->lock); + pool->front_len = front_len; + INIT_LIST_HEAD(&pool->msgs); + pool->num = 0; + pool->min = min; + pool->blocking = blocking; + init_waitqueue_head(&pool->wait); + + spin_lock(&pool->lock); + ret = __fill_msgpool(pool); + spin_unlock(&pool->lock); + return ret; +} + +void ceph_msgpool_destroy(struct ceph_msgpool *pool) +{ + dout("msgpool_destroy %p\n", pool); + spin_lock(&pool->lock); + pool->min = 0; + __fill_msgpool(pool); + spin_unlock(&pool->lock); +} + +int ceph_msgpool_resv(struct ceph_msgpool *pool, int delta) +{ + int ret; + + spin_lock(&pool->lock); + dout("msgpool_resv %p delta %d\n", pool, delta); + pool->min += delta; + ret = __fill_msgpool(pool); + spin_unlock(&pool->lock); + return ret; +} + +struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool) +{ + wait_queue_t wait; + struct ceph_msg *msg; + + if (pool->blocking) { + /* mempool_t behavior; first try to alloc */ + msg = ceph_msg_new(0, pool->front_len, 0, 0, NULL); + if (!IS_ERR(msg)) + return msg; + } + + while (1) { + spin_lock(&pool->lock); + if (likely(pool->num)) { + msg = list_entry(pool->msgs.next, struct ceph_msg, + list_head); + list_del_init(&msg->list_head); + pool->num--; + dout("msgpool_get %p got %p, now %d/%d\n", pool, msg, + pool->num, pool->min); + spin_unlock(&pool->lock); + return msg; + } + pr_err("msgpool_get %p now %d/%d, %s\n", pool, pool->num, + pool->min, pool->blocking ? "waiting" : "failing"); + spin_unlock(&pool->lock); + + if (!pool->blocking) { + WARN_ON(1); + + /* maybe we can allocate it now? */ + msg = ceph_msg_new(0, pool->front_len, 0, 0, NULL); + if (!IS_ERR(msg)) + return msg; + + return ERR_PTR(-ENOMEM); + } + + init_wait(&wait); + prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE); + schedule(); + finish_wait(&pool->wait, &wait); + } +} + +void ceph_msgpool_put(struct ceph_msgpool *pool, struct ceph_msg *msg) +{ + spin_lock(&pool->lock); + if (pool->num < pool->min) { + ceph_msg_get(msg); /* retake a single ref */ + list_add(&msg->list_head, &pool->msgs); + pool->num++; + dout("msgpool_put %p reclaim %p, now %d/%d\n", pool, msg, + pool->num, pool->min); + spin_unlock(&pool->lock); + wake_up(&pool->wait); + } else { + dout("msgpool_put %p drop %p, at %d/%d\n", pool, msg, + pool->num, pool->min); + spin_unlock(&pool->lock); + ceph_msg_kfree(msg); + } +} diff --git a/fs/ceph/msgpool.h b/fs/ceph/msgpool.h new file mode 100644 index 0000000..07a2dec --- /dev/null +++ b/fs/ceph/msgpool.h @@ -0,0 +1,26 @@ +#ifndef _FS_CEPH_MSGPOOL +#define _FS_CEPH_MSGPOOL + +#include "messenger.h" + +/* + * we use memory pools for preallocating messages we may receive, to + * avoid unexpected OOM conditions. + */ +struct ceph_msgpool { + spinlock_t lock; + int front_len; /* preallocated payload size */ + struct list_head msgs; /* msgs in the pool; each has 1 ref */ + int num, min; /* cur, min # msgs in the pool */ + bool blocking; + wait_queue_head_t wait; +}; + +extern int ceph_msgpool_init(struct ceph_msgpool *pool, + int front_len, int size, bool blocking); +extern void ceph_msgpool_destroy(struct ceph_msgpool *pool); +extern int ceph_msgpool_resv(struct ceph_msgpool *, int delta); +extern struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *); +extern void ceph_msgpool_put(struct ceph_msgpool *, struct ceph_msg *); + +#endif -- cgit v0.10.2 From a8e63b7d51cce4557ee7bcd8f51be5cae8547d20 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:13 -0700 Subject: ceph: nfs re-export support Basic NFS re-export support is included. This mostly works. However, Ceph's MDS design precludes the ability to generate a (small) filehandle that will be valid forever, so this is of limited utility. Signed-off-by: Sage Weil diff --git a/fs/ceph/export.c b/fs/ceph/export.c new file mode 100644 index 0000000..fc68e39 --- /dev/null +++ b/fs/ceph/export.c @@ -0,0 +1,223 @@ +#include "ceph_debug.h" + +#include +#include + +#include "super.h" + +/* + * NFS export support + * + * NFS re-export of a ceph mount is, at present, only semireliable. + * The basic issue is that the Ceph architectures doesn't lend itself + * well to generating filehandles that will remain valid forever. + * + * So, we do our best. If you're lucky, your inode will be in the + * client's cache. If it's not, and you have a connectable fh, then + * the MDS server may be able to find it for you. Otherwise, you get + * ESTALE. + * + * There are ways to this more reliable, but in the non-connectable fh + * case, we won't every work perfectly, and in the connectable case, + * some changes are needed on the MDS side to work better. + */ + +/* + * Basic fh + */ +struct ceph_nfs_fh { + u64 ino; +} __attribute__ ((packed)); + +/* + * Larger 'connectable' fh that includes parent ino and name hash. + * Use this whenever possible, as it works more reliably. + */ +struct ceph_nfs_confh { + u64 ino, parent_ino; + u32 parent_name_hash; +} __attribute__ ((packed)); + +static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len, + int connectable) +{ + struct ceph_nfs_fh *fh = (void *)rawfh; + struct ceph_nfs_confh *cfh = (void *)rawfh; + struct dentry *parent = dentry->d_parent; + struct inode *inode = dentry->d_inode; + int type; + + /* don't re-export snaps */ + if (ceph_snap(inode) != CEPH_NOSNAP) + return -EINVAL; + + if (*max_len >= sizeof(*cfh)) { + dout("encode_fh %p connectable\n", dentry); + cfh->ino = ceph_ino(dentry->d_inode); + cfh->parent_ino = ceph_ino(parent->d_inode); + cfh->parent_name_hash = parent->d_name.hash; + *max_len = sizeof(*cfh); + type = 2; + } else if (*max_len > sizeof(*fh)) { + if (connectable) + return -ENOSPC; + dout("encode_fh %p\n", dentry); + fh->ino = ceph_ino(dentry->d_inode); + *max_len = sizeof(*fh); + type = 1; + } else { + return -ENOSPC; + } + return type; +} + +/* + * convert regular fh to dentry + * + * FIXME: we should try harder by querying the mds for the ino. + */ +static struct dentry *__fh_to_dentry(struct super_block *sb, + struct ceph_nfs_fh *fh) +{ + struct inode *inode; + struct dentry *dentry; + struct ceph_vino vino; + int err; + + dout("__fh_to_dentry %llx\n", fh->ino); + vino.ino = fh->ino; + vino.snap = CEPH_NOSNAP; + inode = ceph_find_inode(sb, vino); + if (!inode) + return ERR_PTR(-ESTALE); + + dentry = d_obtain_alias(inode); + if (!dentry) { + pr_err("fh_to_dentry %llx -- inode %p but ENOMEM\n", + fh->ino, inode); + iput(inode); + return ERR_PTR(-ENOMEM); + } + err = ceph_init_dentry(dentry); + + if (err < 0) { + iput(inode); + return ERR_PTR(err); + } + dout("__fh_to_dentry %llx %p dentry %p\n", fh->ino, inode, dentry); + return dentry; +} + +/* + * convert connectable fh to dentry + */ +static struct dentry *__cfh_to_dentry(struct super_block *sb, + struct ceph_nfs_confh *cfh) +{ + struct ceph_mds_client *mdsc = &ceph_client(sb)->mdsc; + struct inode *inode; + struct dentry *dentry; + struct ceph_vino vino; + int err; + + dout("__cfh_to_dentry %llx (%llx/%x)\n", + cfh->ino, cfh->parent_ino, cfh->parent_name_hash); + + vino.ino = cfh->ino; + vino.snap = CEPH_NOSNAP; + inode = ceph_find_inode(sb, vino); + if (!inode) { + struct ceph_mds_request *req; + + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPHASH, + USE_ANY_MDS); + if (IS_ERR(req)) + return ERR_PTR(PTR_ERR(req)); + + req->r_ino1 = vino; + req->r_ino2.ino = cfh->parent_ino; + req->r_ino2.snap = CEPH_NOSNAP; + req->r_path2 = kmalloc(16, GFP_NOFS); + snprintf(req->r_path2, 16, "%d", cfh->parent_name_hash); + req->r_num_caps = 1; + err = ceph_mdsc_do_request(mdsc, NULL, req); + ceph_mdsc_put_request(req); + inode = ceph_find_inode(sb, vino); + if (!inode) + return ERR_PTR(err ? err : -ESTALE); + } + + dentry = d_obtain_alias(inode); + if (!dentry) { + pr_err("cfh_to_dentry %llx -- inode %p but ENOMEM\n", + cfh->ino, inode); + iput(inode); + return ERR_PTR(-ENOMEM); + } + err = ceph_init_dentry(dentry); + if (err < 0) { + iput(inode); + return ERR_PTR(err); + } + dout("__cfh_to_dentry %llx %p dentry %p\n", cfh->ino, inode, dentry); + return dentry; +} + +static struct dentry *ceph_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + if (fh_type == 1) + return __fh_to_dentry(sb, (struct ceph_nfs_fh *)fid->raw); + else + return __cfh_to_dentry(sb, (struct ceph_nfs_confh *)fid->raw); +} + +/* + * get parent, if possible. + * + * FIXME: we could do better by querying the mds to discover the + * parent. + */ +static struct dentry *ceph_fh_to_parent(struct super_block *sb, + struct fid *fid, + int fh_len, int fh_type) +{ + struct ceph_nfs_confh *cfh = (void *)fid->raw; + struct ceph_vino vino; + struct inode *inode; + struct dentry *dentry; + int err; + + if (fh_type == 1) + return ERR_PTR(-ESTALE); + + pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino, + cfh->parent_name_hash); + + vino.ino = cfh->ino; + vino.snap = CEPH_NOSNAP; + inode = ceph_find_inode(sb, vino); + if (!inode) + return ERR_PTR(-ESTALE); + + dentry = d_obtain_alias(inode); + if (!dentry) { + pr_err("fh_to_parent %llx -- inode %p but ENOMEM\n", + cfh->ino, inode); + iput(inode); + return ERR_PTR(-ENOMEM); + } + err = ceph_init_dentry(dentry); + if (err < 0) { + iput(inode); + return ERR_PTR(err); + } + dout("fh_to_parent %llx %p dentry %p\n", cfh->ino, inode, dentry); + return dentry; +} + +const struct export_operations ceph_export_ops = { + .encode_fh = ceph_encode_fh, + .fh_to_dentry = ceph_fh_to_dentry, + .fh_to_parent = ceph_fh_to_parent, +}; -- cgit v0.10.2 From 8f4e91dee2a245e4be6942f4a8d83a769e13a47d Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:14 -0700 Subject: ceph: ioctls A few Ceph ioctls for getting and setting file layout (striping) parameters, and learning the identity and network address of the OSD a given region of a file is stored on. Signed-off-by: Sage Weil diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 9473749..91cfdd7 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -182,6 +182,7 @@ Code Seq# Include File Comments 0x90 00 drivers/cdrom/sbpcd.h 0x93 60-7F linux/auto_fs.h +0x97 00-7F fs/ceph/ioctl.h Ceph file system 0x99 00-0F 537-Addinboard driver 0xA0 all linux/sdp/sdp.h Industrial Device Project diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c new file mode 100644 index 0000000..e4f99ef --- /dev/null +++ b/fs/ceph/ioctl.c @@ -0,0 +1,157 @@ +#include + +#include "ioctl.h" +#include "super.h" +#include "ceph_debug.h" + + +/* + * ioctls + */ + +/* + * get and set the file layout + */ +static long ceph_ioctl_get_layout(struct file *file, void __user *arg) +{ + struct ceph_inode_info *ci = ceph_inode(file->f_dentry->d_inode); + struct ceph_ioctl_layout l; + int err; + + err = ceph_do_getattr(file->f_dentry->d_inode, CEPH_STAT_CAP_LAYOUT); + if (!err) { + l.stripe_unit = ceph_file_layout_su(ci->i_layout); + l.stripe_count = ceph_file_layout_stripe_count(ci->i_layout); + l.object_size = ceph_file_layout_object_size(ci->i_layout); + l.data_pool = le32_to_cpu(ci->i_layout.fl_pg_pool); + if (copy_to_user(arg, &l, sizeof(l))) + return -EFAULT; + } + + return err; +} + +static long ceph_ioctl_set_layout(struct file *file, void __user *arg) +{ + struct inode *inode = file->f_dentry->d_inode; + struct inode *parent_inode = file->f_dentry->d_parent->d_inode; + struct ceph_mds_client *mdsc = &ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_request *req; + struct ceph_ioctl_layout l; + int err, i; + + /* copy and validate */ + if (copy_from_user(&l, arg, sizeof(l))) + return -EFAULT; + + if ((l.object_size & ~PAGE_MASK) || + (l.stripe_unit & ~PAGE_MASK) || + !l.stripe_unit || + (l.object_size && + (unsigned)l.object_size % (unsigned)l.stripe_unit)) + return -EINVAL; + + /* make sure it's a valid data pool */ + if (l.data_pool > 0) { + mutex_lock(&mdsc->mutex); + err = -EINVAL; + for (i = 0; i < mdsc->mdsmap->m_num_data_pg_pools; i++) + if (mdsc->mdsmap->m_data_pg_pools[i] == l.data_pool) { + err = 0; + break; + } + mutex_unlock(&mdsc->mutex); + if (err) + return err; + } + + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETLAYOUT, + USE_AUTH_MDS); + if (IS_ERR(req)) + return PTR_ERR(req); + req->r_inode = igrab(inode); + req->r_inode_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_EXCL; + + req->r_args.setlayout.layout.fl_stripe_unit = + cpu_to_le32(l.stripe_unit); + req->r_args.setlayout.layout.fl_stripe_count = + cpu_to_le32(l.stripe_count); + req->r_args.setlayout.layout.fl_object_size = + cpu_to_le32(l.object_size); + req->r_args.setlayout.layout.fl_pg_pool = cpu_to_le32(l.data_pool); + req->r_args.setlayout.layout.fl_pg_preferred = cpu_to_le32((s32)-1); + + err = ceph_mdsc_do_request(mdsc, parent_inode, req); + ceph_mdsc_put_request(req); + return err; +} + +/* + * Return object name, size/offset information, and location (OSD + * number, network address) for a given file offset. + */ +static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg) +{ + struct ceph_ioctl_dataloc dl; + struct inode *inode = file->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_osd_client *osdc = &ceph_client(inode->i_sb)->osdc; + u64 len = 1, olen; + u64 tmp; + struct ceph_object_layout ol; + union ceph_pg pgid; + + /* copy and validate */ + if (copy_from_user(&dl, arg, sizeof(dl))) + return -EFAULT; + + down_read(&osdc->map_sem); + ceph_calc_file_object_mapping(&ci->i_layout, dl.file_offset, &len, + &dl.object_no, &dl.object_offset, &olen); + dl.file_offset -= dl.object_offset; + dl.object_size = ceph_file_layout_object_size(ci->i_layout); + dl.block_size = ceph_file_layout_su(ci->i_layout); + + /* block_offset = object_offset % block_size */ + tmp = dl.object_offset; + dl.block_offset = do_div(tmp, dl.block_size); + + snprintf(dl.object_name, sizeof(dl.object_name), "%llx.%08llx", + ceph_ino(inode), dl.object_no); + ceph_calc_object_layout(&ol, dl.object_name, &ci->i_layout, + osdc->osdmap); + + pgid.pg64 = le64_to_cpu(ol.ol_pgid); + dl.osd = ceph_calc_pg_primary(osdc->osdmap, pgid); + if (dl.osd >= 0) { + struct ceph_entity_addr *a = + ceph_osd_addr(osdc->osdmap, dl.osd); + if (a) + memcpy(&dl.osd_addr, &a->in_addr, sizeof(dl.osd_addr)); + } else { + memset(&dl.osd_addr, 0, sizeof(dl.osd_addr)); + } + up_read(&osdc->map_sem); + + /* send result back to user */ + if (copy_to_user(arg, &dl, sizeof(dl))) + return -EFAULT; + + return 0; +} + +long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg); + switch (cmd) { + case CEPH_IOC_GET_LAYOUT: + return ceph_ioctl_get_layout(file, (void __user *)arg); + + case CEPH_IOC_SET_LAYOUT: + return ceph_ioctl_set_layout(file, (void __user *)arg); + + case CEPH_IOC_GET_DATALOC: + return ceph_ioctl_get_dataloc(file, (void __user *)arg); + } + return -ENOTTY; +} diff --git a/fs/ceph/ioctl.h b/fs/ceph/ioctl.h new file mode 100644 index 0000000..3c511da --- /dev/null +++ b/fs/ceph/ioctl.h @@ -0,0 +1,39 @@ +#ifndef FS_CEPH_IOCTL_H +#define FS_CEPH_IOCTL_H + +#include +#include + +#define CEPH_IOCTL_MAGIC 0x97 + +/* just use u64 to align sanely on all archs */ +struct ceph_ioctl_layout { + __u64 stripe_unit, stripe_count, object_size; + __u64 data_pool; +}; + +#define CEPH_IOC_GET_LAYOUT _IOR(CEPH_IOCTL_MAGIC, 1, \ + struct ceph_ioctl_layout) +#define CEPH_IOC_SET_LAYOUT _IOW(CEPH_IOCTL_MAGIC, 2, \ + struct ceph_ioctl_layout) + +/* + * Extract identity, address of the OSD and object storing a given + * file offset. + */ +struct ceph_ioctl_dataloc { + __u64 file_offset; /* in+out: file offset */ + __u64 object_offset; /* out: offset in object */ + __u64 object_no; /* out: object # */ + __u64 object_size; /* out: object size */ + char object_name[64]; /* out: object name */ + __u64 block_offset; /* out: offset in block */ + __u64 block_size; /* out: block length */ + __s64 osd; /* out: osd # */ + struct sockaddr_storage osd_addr; /* out: osd address */ +}; + +#define CEPH_IOC_GET_DATALOC _IOWR(CEPH_IOCTL_MAGIC, 3, \ + struct ceph_ioctl_dataloc) + +#endif -- cgit v0.10.2 From 76aa844d5b2fb8c839180d3f5874e333b297e5fd Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:14 -0700 Subject: ceph: debugfs Basic state information is available via /sys/kernel/debug/ceph, including instances of the client, fsids, current monitor, mds and osd maps, outstanding server requests, and hooks to adjust debug levels. Signed-off-by: Sage Weil diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c new file mode 100644 index 0000000..9edbad3 --- /dev/null +++ b/fs/ceph/debugfs.c @@ -0,0 +1,425 @@ +#include "ceph_debug.h" + +#include +#include +#include +#include + +#include "super.h" +#include "mds_client.h" + +/* + * Implement /sys/kernel/debug/ceph fun + * + * /sys/kernel/debug/ceph/client* - an instance of the ceph client + * .../osdmap - current osdmap + * .../mdsmap - current mdsmap + * .../monmap - current monmap + * .../osdc - active osd requests + * .../mdsc - active mds requests + * .../monc - mon client state + * .../dentry_lru - dump contents of dentry lru + * .../caps - expose cap (reservation) stats + */ + +static struct dentry *ceph_debugfs_dir; + +static int monmap_show(struct seq_file *s, void *p) +{ + int i; + struct ceph_client *client = s->private; + + if (client->monc.monmap == NULL) + return 0; + + seq_printf(s, "epoch %d\n", client->monc.monmap->epoch); + for (i = 0; i < client->monc.monmap->num_mon; i++) { + struct ceph_entity_inst *inst = + &client->monc.monmap->mon_inst[i]; + + seq_printf(s, "\t%s%lld\t%s\n", + ENTITY_NAME(inst->name), + pr_addr(&inst->addr.in_addr)); + } + return 0; +} + +static int mdsmap_show(struct seq_file *s, void *p) +{ + int i; + struct ceph_client *client = s->private; + + if (client->mdsc.mdsmap == NULL) + return 0; + seq_printf(s, "epoch %d\n", client->mdsc.mdsmap->m_epoch); + seq_printf(s, "root %d\n", client->mdsc.mdsmap->m_root); + seq_printf(s, "session_timeout %d\n", + client->mdsc.mdsmap->m_session_timeout); + seq_printf(s, "session_autoclose %d\n", + client->mdsc.mdsmap->m_session_autoclose); + for (i = 0; i < client->mdsc.mdsmap->m_max_mds; i++) { + struct ceph_entity_addr *addr = + &client->mdsc.mdsmap->m_info[i].addr; + int state = client->mdsc.mdsmap->m_info[i].state; + + seq_printf(s, "\tmds%d\t%s\t(%s)\n", i, pr_addr(&addr->in_addr), + ceph_mds_state_name(state)); + } + return 0; +} + +static int osdmap_show(struct seq_file *s, void *p) +{ + int i; + struct ceph_client *client = s->private; + + if (client->osdc.osdmap == NULL) + return 0; + seq_printf(s, "epoch %d\n", client->osdc.osdmap->epoch); + seq_printf(s, "flags%s%s\n", + (client->osdc.osdmap->flags & CEPH_OSDMAP_NEARFULL) ? + " NEARFULL" : "", + (client->osdc.osdmap->flags & CEPH_OSDMAP_FULL) ? + " FULL" : ""); + for (i = 0; i < client->osdc.osdmap->num_pools; i++) { + struct ceph_pg_pool_info *pool = + &client->osdc.osdmap->pg_pool[i]; + seq_printf(s, "pg_pool %d pg_num %d / %d, lpg_num %d / %d\n", + i, pool->v.pg_num, pool->pg_num_mask, + pool->v.lpg_num, pool->lpg_num_mask); + } + for (i = 0; i < client->osdc.osdmap->max_osd; i++) { + struct ceph_entity_addr *addr = + &client->osdc.osdmap->osd_addr[i]; + int state = client->osdc.osdmap->osd_state[i]; + char sb[64]; + + seq_printf(s, "\tosd%d\t%s\t%3d%%\t(%s)\n", + i, pr_addr(&addr->in_addr), + ((client->osdc.osdmap->osd_weight[i]*100) >> 16), + ceph_osdmap_state_str(sb, sizeof(sb), state)); + } + return 0; +} + +static int monc_show(struct seq_file *s, void *p) +{ + struct ceph_client *client = s->private; + struct ceph_mon_statfs_request *req; + u64 nexttid = 0; + int got; + struct ceph_mon_client *monc = &client->monc; + + mutex_lock(&monc->mutex); + + if (monc->have_mdsmap) + seq_printf(s, "have mdsmap %u\n", (unsigned)monc->have_mdsmap); + if (monc->have_osdmap) + seq_printf(s, "have osdmap %u\n", (unsigned)monc->have_osdmap); + if (monc->want_next_osdmap) + seq_printf(s, "want next osdmap\n"); + + while (nexttid < monc->last_tid) { + got = radix_tree_gang_lookup(&monc->statfs_request_tree, + (void **)&req, nexttid, 1); + if (got == 0) + break; + nexttid = req->tid + 1; + + seq_printf(s, "%lld statfs\n", req->tid); + } + mutex_unlock(&monc->mutex); + + return 0; +} + +static int mdsc_show(struct seq_file *s, void *p) +{ + struct ceph_client *client = s->private; + struct ceph_mds_request *req; + u64 nexttid = 0; + int got; + struct ceph_mds_client *mdsc = &client->mdsc; + int pathlen; + u64 pathbase; + char *path; + + mutex_lock(&mdsc->mutex); + while (nexttid < mdsc->last_tid) { + got = radix_tree_gang_lookup(&mdsc->request_tree, + (void **)&req, nexttid, 1); + if (got == 0) + break; + nexttid = req->r_tid + 1; + + if (req->r_request) + seq_printf(s, "%lld\tmds%d\t", req->r_tid, req->r_mds); + else + seq_printf(s, "%lld\t(no request)\t", req->r_tid); + + seq_printf(s, "%s", ceph_mds_op_name(req->r_op)); + + if (req->r_got_unsafe) + seq_printf(s, "\t(unsafe)"); + else + seq_printf(s, "\t"); + + if (req->r_inode) { + seq_printf(s, " #%llx", ceph_ino(req->r_inode)); + } else if (req->r_dentry) { + path = ceph_mdsc_build_path(req->r_dentry, &pathlen, + &pathbase, 0); + spin_lock(&req->r_dentry->d_lock); + seq_printf(s, " #%llx/%.*s (%s)", + ceph_ino(req->r_dentry->d_parent->d_inode), + req->r_dentry->d_name.len, + req->r_dentry->d_name.name, + path ? path : ""); + spin_unlock(&req->r_dentry->d_lock); + kfree(path); + } else if (req->r_path1) { + seq_printf(s, " #%llx/%s", req->r_ino1.ino, + req->r_path1); + } + + if (req->r_old_dentry) { + path = ceph_mdsc_build_path(req->r_old_dentry, &pathlen, + &pathbase, 0); + spin_lock(&req->r_old_dentry->d_lock); + seq_printf(s, " #%llx/%.*s (%s)", + ceph_ino(req->r_old_dentry->d_parent->d_inode), + req->r_old_dentry->d_name.len, + req->r_old_dentry->d_name.name, + path ? path : ""); + spin_unlock(&req->r_old_dentry->d_lock); + kfree(path); + } else if (req->r_path2) { + if (req->r_ino2.ino) + seq_printf(s, " #%llx/%s", req->r_ino2.ino, + req->r_path2); + else + seq_printf(s, " %s", req->r_path2); + } + + seq_printf(s, "\n"); + } + mutex_unlock(&mdsc->mutex); + + return 0; +} + +static int osdc_show(struct seq_file *s, void *pp) +{ + struct ceph_client *client = s->private; + struct ceph_osd_client *osdc = &client->osdc; + struct rb_node *p; + + mutex_lock(&osdc->request_mutex); + for (p = rb_first(&osdc->requests); p; p = rb_next(p)) { + struct ceph_osd_request *req; + struct ceph_osd_request_head *head; + struct ceph_osd_op *op; + int num_ops; + int opcode, olen; + int i; + + req = rb_entry(p, struct ceph_osd_request, r_node); + + seq_printf(s, "%lld\tosd%d\t", req->r_tid, + req->r_osd ? req->r_osd->o_osd : -1); + + head = req->r_request->front.iov_base; + op = (void *)(head + 1); + + num_ops = le16_to_cpu(head->num_ops); + olen = le32_to_cpu(head->object_len); + seq_printf(s, "%.*s", olen, + (const char *)(head->ops + num_ops)); + + if (req->r_reassert_version.epoch) + seq_printf(s, "\t%u'%llu", + (unsigned)le32_to_cpu(req->r_reassert_version.epoch), + le64_to_cpu(req->r_reassert_version.version)); + else + seq_printf(s, "\t"); + + for (i = 0; i < num_ops; i++) { + opcode = le16_to_cpu(op->op); + seq_printf(s, "\t%s", ceph_osd_op_name(opcode)); + op++; + } + + seq_printf(s, "\n"); + } + mutex_unlock(&osdc->request_mutex); + return 0; +} + +static int caps_show(struct seq_file *s, void *p) +{ + struct ceph_client *client = p; + int total, avail, used, reserved; + + ceph_reservation_status(client, &total, &avail, &used, &reserved); + seq_printf(s, "total\t\t%d\n" + "avail\t\t%d\n" + "used\t\t%d\n" + "reserved\t%d\n", + total, avail, used, reserved); + return 0; +} + +static int dentry_lru_show(struct seq_file *s, void *ptr) +{ + struct ceph_client *client = s->private; + struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_dentry_info *di; + + spin_lock(&mdsc->dentry_lru_lock); + list_for_each_entry(di, &mdsc->dentry_lru, lru) { + struct dentry *dentry = di->dentry; + seq_printf(s, "%p %p\t%.*s\n", + di, dentry, dentry->d_name.len, dentry->d_name.name); + } + spin_unlock(&mdsc->dentry_lru_lock); + + return 0; +} + +#define DEFINE_SHOW_FUNC(name) \ +static int name##_open(struct inode *inode, struct file *file) \ +{ \ + struct seq_file *sf; \ + int ret; \ + \ + ret = single_open(file, name, NULL); \ + sf = file->private_data; \ + sf->private = inode->i_private; \ + return ret; \ +} \ + \ +static const struct file_operations name##_fops = { \ + .open = name##_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +}; + +DEFINE_SHOW_FUNC(monmap_show) +DEFINE_SHOW_FUNC(mdsmap_show) +DEFINE_SHOW_FUNC(osdmap_show) +DEFINE_SHOW_FUNC(monc_show) +DEFINE_SHOW_FUNC(mdsc_show) +DEFINE_SHOW_FUNC(osdc_show) +DEFINE_SHOW_FUNC(dentry_lru_show) +DEFINE_SHOW_FUNC(caps_show) + +int __init ceph_debugfs_init(void) +{ + ceph_debugfs_dir = debugfs_create_dir("ceph", NULL); + if (!ceph_debugfs_dir) + return -ENOMEM; + return 0; +} + +void ceph_debugfs_cleanup(void) +{ + debugfs_remove(ceph_debugfs_dir); +} + +int ceph_debugfs_client_init(struct ceph_client *client) +{ + int ret = 0; + char name[80]; + + snprintf(name, sizeof(name), FSID_FORMAT ".client%lld", + PR_FSID(&client->monc.monmap->fsid), client->whoami); + + client->debugfs_dir = debugfs_create_dir(name, ceph_debugfs_dir); + if (!client->debugfs_dir) + goto out; + + client->monc.debugfs_file = debugfs_create_file("monc", + 0600, + client->debugfs_dir, + client, + &monc_show_fops); + if (!client->monc.debugfs_file) + goto out; + + client->mdsc.debugfs_file = debugfs_create_file("mdsc", + 0600, + client->debugfs_dir, + client, + &mdsc_show_fops); + if (!client->mdsc.debugfs_file) + goto out; + + client->osdc.debugfs_file = debugfs_create_file("osdc", + 0600, + client->debugfs_dir, + client, + &osdc_show_fops); + if (!client->osdc.debugfs_file) + goto out; + + client->debugfs_monmap = debugfs_create_file("monmap", + 0600, + client->debugfs_dir, + client, + &monmap_show_fops); + if (!client->debugfs_monmap) + goto out; + + client->debugfs_mdsmap = debugfs_create_file("mdsmap", + 0600, + client->debugfs_dir, + client, + &mdsmap_show_fops); + if (!client->debugfs_mdsmap) + goto out; + + client->debugfs_osdmap = debugfs_create_file("osdmap", + 0600, + client->debugfs_dir, + client, + &osdmap_show_fops); + if (!client->debugfs_osdmap) + goto out; + + client->debugfs_dentry_lru = debugfs_create_file("dentry_lru", + 0600, + client->debugfs_dir, + client, + &dentry_lru_show_fops); + if (!client->debugfs_dentry_lru) + goto out; + + client->debugfs_caps = debugfs_create_file("caps", + 0400, + client->debugfs_dir, + client, + &caps_show_fops); + if (!client->debugfs_caps) + goto out; + + return 0; + +out: + ceph_debugfs_client_cleanup(client); + return ret; +} + +void ceph_debugfs_client_cleanup(struct ceph_client *client) +{ + debugfs_remove(client->debugfs_caps); + debugfs_remove(client->debugfs_dentry_lru); + debugfs_remove(client->debugfs_osdmap); + debugfs_remove(client->debugfs_mdsmap); + debugfs_remove(client->debugfs_monmap); + debugfs_remove(client->osdc.debugfs_file); + debugfs_remove(client->mdsc.debugfs_file); + debugfs_remove(client->monc.debugfs_file); + debugfs_remove(client->debugfs_dir); +} + -- cgit v0.10.2 From 9030aaf9bf0a1eee47a154c316c789e959638b0f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 11:31:15 -0700 Subject: ceph: Kconfig, Makefile Kconfig options and Makefile. Signed-off-by: Sage Weil diff --git a/MAINTAINERS b/MAINTAINERS index c450f3a..9b680ff 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1294,6 +1294,15 @@ F: arch/powerpc/include/asm/spu*.h F: arch/powerpc/oprofile/*cell* F: arch/powerpc/platforms/cell/ +CEPH DISTRIBUTED FILE SYSTEM CLIENT +M: Sage Weil +L: ceph-devel@lists.sourceforge.net +W: http://ceph.newdream.net/ +T: git git://ceph.newdream.net/linux-ceph-client.git +S: Supported +F: Documentation/filesystems/ceph.txt +F: fs/ceph + CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM: M: David Vrabel L: linux-usb@vger.kernel.org diff --git a/fs/Kconfig b/fs/Kconfig index d4bf8ca..ca68709 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -234,6 +234,7 @@ config NFS_COMMON source "net/sunrpc/Kconfig" source "fs/smbfs/Kconfig" +source "fs/ceph/Kconfig" source "fs/cifs/Kconfig" source "fs/ncpfs/Kconfig" source "fs/coda/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index af6d047..5ef73a0 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -124,3 +124,4 @@ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ obj-$(CONFIG_EXOFS_FS) += exofs/ +obj-$(CONFIG_CEPH_FS) += ceph/ diff --git a/fs/ceph/Kconfig b/fs/ceph/Kconfig new file mode 100644 index 0000000..bc1fbd9 --- /dev/null +++ b/fs/ceph/Kconfig @@ -0,0 +1,26 @@ +config CEPH_FS + tristate "Ceph distributed file system (EXPERIMENTAL)" + depends on INET && EXPERIMENTAL + select LIBCRC32C + help + Choose Y or M here to include support for mounting the + experimental Ceph distributed file system. Ceph is an extremely + scalable file system designed to provide high performance, + reliable access to petabytes of storage. + + More information at http://ceph.newdream.net/. + + If unsure, say N. + +config CEPH_FS_PRETTYDEBUG + bool "Include file:line in ceph debug output" + depends on CEPH_FS + default n + help + If you say Y here, debug output will include a filename and + line to aid debugging. This icnreases kernel size and slows + execution slightly when debug call sites are enabled (e.g., + via CONFIG_DYNAMIC_DEBUG). + + If unsure, say N. + diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile new file mode 100644 index 0000000..7da6d69 --- /dev/null +++ b/fs/ceph/Makefile @@ -0,0 +1,36 @@ +# +# Makefile for CEPH filesystem. +# + +ifneq ($(KERNELRELEASE),) + +obj-$(CONFIG_CEPH_FS) += ceph.o + +ceph-objs := super.o inode.o dir.o file.o addr.o ioctl.o \ + export.o caps.o snap.o xattr.o \ + messenger.o msgpool.o buffer.o \ + mds_client.o mdsmap.o \ + mon_client.o \ + osd_client.o osdmap.o crush/crush.o crush/mapper.o \ + debugfs.o \ + ceph_fs.o ceph_strings.o ceph_frag.o + +else +#Otherwise we were called directly from the command +# line; invoke the kernel build system. + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: all + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) CONFIG_CEPH_FS=m modules + +modules_install: + $(MAKE) -C $(KERNELDIR) M=$(PWD) CONFIG_CEPH_FS=m modules_install + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean + +endif -- cgit v0.10.2 From e324b8f991679a43e09dd13500bf1988c0bfc0ea Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 6 Oct 2009 12:20:56 -0700 Subject: ceph: document shared files in README Document files shared between kernel and user code trees. Signed-off-by: Sage Weil diff --git a/fs/ceph/README b/fs/ceph/README new file mode 100644 index 0000000..231a1df --- /dev/null +++ b/fs/ceph/README @@ -0,0 +1,17 @@ +# +# The following files are shared by (and manually synchronized +# between) the Ceph userland and kernel client. +# +# userland kernel +src/include/ceph_fs.h fs/ceph/ceph_fs.h +src/include/ceph_fs.cc fs/ceph/ceph_fs.c +src/include/msgr.h fs/ceph/msgr.h +src/include/rados.h fs/ceph/rados.h +src/include/ceph_strings.cc fs/ceph/ceph_strings.c +src/include/ceph_frag.h fs/ceph/ceph_frag.h +src/include/ceph_frag.cc fs/ceph/ceph_frag.c +src/crush/crush.c fs/ceph/crush/crush.c +src/crush/crush.h fs/ceph/crush/crush.h +src/crush/mapper.c fs/ceph/crush/mapper.c +src/crush/mapper.h fs/ceph/crush/mapper.h +src/crush/hash.h fs/ceph/crush/hash.h -- cgit v0.10.2 From fa0b72e9e2900ee87886aaf8bc4c4701be1e081d Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 7 Oct 2009 10:59:10 -0700 Subject: ceph: show meaningful version on module load Kill the old git revision; print the ceph version and protocol versions instead. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_ver.h b/fs/ceph/ceph_ver.h deleted file mode 100644 index 66c3727..0000000 --- a/fs/ceph/ceph_ver.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __CEPH_VERSION_H -#define __CEPH_VERSION_H - -#define CEPH_GIT_VER 335cd8f952b457095ea2a66aee3db50efb63c91d - -#endif diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 0723fb6..b3404a3 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -16,7 +16,6 @@ #include #include -#include "ceph_ver.h" #include "decode.h" #include "super.h" #include "mon_client.h" @@ -903,7 +902,9 @@ static int __init init_ceph(void) if (ret) goto out_icache; - pr_info("loaded (%s)\n", STRINGIFY(CEPH_GIT_VER)); + pr_info("loaded %d.%d.%d (mon/mds/osd proto %d/%d/%d)\n", + CEPH_VERSION_MAJOR, CEPH_VERSION_MINOR, CEPH_VERSION_PATCH, + CEPH_MONC_PROTOCOL, CEPH_MDSC_PROTOCOL, CEPH_OSDC_PROTOCOL); return 0; out_icache: -- cgit v0.10.2 From b195befd9acb514dd2afb722e63fdd880ed63217 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 7 Oct 2009 10:59:30 -0700 Subject: ceph: include preferred_osd in file layout virtual xattr Signed-off-by: Sage Weil diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 8eaac04..65b3a84 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -91,11 +91,18 @@ static struct ceph_vxattr_cb ceph_dir_vxattrs[] = { static size_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val, size_t size) { - return snprintf(val, size, + int ret; + + ret = snprintf(val, size, "chunk_bytes=%lld\nstripe_count=%lld\nobject_size=%lld\n", (unsigned long long)ceph_file_layout_su(ci->i_layout), (unsigned long long)ceph_file_layout_stripe_count(ci->i_layout), (unsigned long long)ceph_file_layout_object_size(ci->i_layout)); + if (ceph_file_layout_pg_preferred(ci->i_layout)) + ret += snprintf(val + ret, size, "preferred_osd=%lld\n", + (unsigned long long)ceph_file_layout_pg_preferred( + ci->i_layout)); + return ret; } static struct ceph_vxattr_cb ceph_file_vxattrs[] = { -- cgit v0.10.2 From b28813a61d6ffe05ad353a86965607bb7a7fd60f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 7 Oct 2009 10:59:34 -0700 Subject: ceph: gracefully avoid empty crush buckets This avoids a divide by zero when the input and/or map are malformed. Signed-off-by: Sage Weil diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c index 0f0730c..c268393 100644 --- a/fs/ceph/crush/mapper.c +++ b/fs/ceph/crush/mapper.c @@ -299,7 +299,7 @@ static int crush_choose(struct crush_map *map, struct crush_bucket *in = bucket; int r; int i; - int item; + int item = 0; int itemtype; int collide, reject; const int orig_tries = 5; /* attempts before we fall back to search */ @@ -316,6 +316,7 @@ static int crush_choose(struct crush_map *map, /* choose through intervening buckets */ flocal = 0; do { + collide = 0; retry_bucket = 0; r = rep; if (in->alg == CRUSH_BUCKET_UNIFORM) { @@ -340,6 +341,10 @@ static int crush_choose(struct crush_map *map, } /* bucket choose */ + if (in->size == 0) { + reject = 1; + goto reject; + } if (flocal >= (in->size>>1) && flocal > orig_tries) item = bucket_perm_choose(in, x, r); @@ -363,7 +368,6 @@ static int crush_choose(struct crush_map *map, } /* collision? */ - collide = 0; for (i = 0; i < outpos; i++) { if (out[i] == item) { collide = 1; @@ -388,6 +392,7 @@ static int crush_choose(struct crush_map *map, reject = 0; } +reject: if (reject || collide) { ftotal++; flocal++; -- cgit v0.10.2 From e251e288082d5e89604eee1fef0c31bed1fe8f02 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 7 Oct 2009 16:38:19 -0700 Subject: ceph: fix mdsmap decoding when multiple mds's are present A misplaced sizeof() around namelen was throwing things off. Signed-off-by: Sage Weil diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 15913cbe..09180d8 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -85,28 +85,28 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) void *pexport_targets = NULL; ceph_decode_need(p, end, sizeof(addr) + 1 + sizeof(u32), bad); - *p += sizeof(addr); /* skip addr key */ + ceph_decode_copy(p, &addr, sizeof(addr)); ceph_decode_8(p, infoversion); ceph_decode_32(p, namelen); /* skip mds name */ *p += namelen; ceph_decode_need(p, end, - 5*sizeof(u32) + sizeof(u64) + + 4*sizeof(u32) + sizeof(u64) + sizeof(addr) + sizeof(struct ceph_timespec), bad); ceph_decode_32(p, mds); ceph_decode_32(p, inc); ceph_decode_32(p, state); ceph_decode_64(p, state_seq); - ceph_decode_copy(p, &addr, sizeof(addr)); + *p += sizeof(addr); *p += sizeof(struct ceph_timespec); *p += sizeof(u32); ceph_decode_32_safe(p, end, namelen, bad); - *p += sizeof(namelen); + *p += namelen; if (infoversion >= 2) { ceph_decode_32_safe(p, end, num_export_targets, bad); pexport_targets = *p; - *p += sizeof(num_export_targets * sizeof(u32)); + *p += num_export_targets * sizeof(u32); } else { num_export_targets = 0; } -- cgit v0.10.2 From 0656d11ba6ffa3dee0e8916a1903f96185651217 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 8 Oct 2009 10:25:46 -0700 Subject: ceph: renew mon subscription before it expires Be conservative: renew subscription once half the interval has expired. Do not reuse sub expiration to control hunting. Signed-off-by: Sage Weil diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index b0c95ce..9c34df1 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -213,7 +213,7 @@ static void handle_subscribe_ack(struct ceph_mon_client *monc, monc->hunting = false; } dout("handle_subscribe_ack after %d seconds\n", seconds); - monc->sub_renew_after = monc->sub_sent + seconds*HZ - 1; + monc->sub_renew_after = monc->sub_sent + (seconds >> 1)*HZ - 1; monc->sub_sent = 0; mutex_unlock(&monc->mutex); return; @@ -512,7 +512,7 @@ static void delayed_work(struct work_struct *work) if (monc->want_mount) { __request_mount(monc); } else { - if (__sub_expired(monc)) { + if (monc->hunting) { __close_session(monc); __open_session(monc); /* continue hunting */ } else { -- cgit v0.10.2 From c1ea8823be69ebebaface912142190e910711984 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 8 Oct 2009 16:55:47 -0700 Subject: ceph: fix osd request submission race The osd request submission path registers the request, drops and retakes the request_mutex, then sends it to the OSD. A racing kick_requests could sent it during that interval, causing the same msg to be sent twice and BUGing in the msgr. Fix by only sending the message if it hasn't been touched by other threads. Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 978593a..d14019d 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -837,7 +837,8 @@ static void kick_requests(struct ceph_osd_client *osdc, } kick: - dout("kicking tid %llu osd%d\n", req->r_tid, req->r_osd->o_osd); + dout("kicking %p tid %llu osd%d\n", req, req->r_tid, + req->r_osd->o_osd); req->r_flags |= CEPH_OSD_FLAG_RETRY; err = __send_request(osdc, req); if (err) { @@ -1016,7 +1017,7 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req, bool nofail) { - int rc; + int rc = 0; req->r_request->pages = req->r_pages; req->r_request->nr_pages = req->r_num_pages; @@ -1025,15 +1026,22 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, down_read(&osdc->map_sem); mutex_lock(&osdc->request_mutex); - rc = __send_request(osdc, req); - if (rc) { - if (nofail) { - dout("osdc_start_request failed send, marking %lld\n", - req->r_tid); - req->r_resend = true; - rc = 0; - } else { - __unregister_request(osdc, req); + /* + * a racing kick_requests() may have sent the message for us + * while we dropped request_mutex above, so only send now if + * the request still han't been touched yet. + */ + if (req->r_sent == 0) { + rc = __send_request(osdc, req); + if (rc) { + if (nofail) { + dout("osdc_start_request failed send, " + " marking %lld\n", req->r_tid); + req->r_resend = true; + rc = 0; + } else { + __unregister_request(osdc, req); + } } } mutex_unlock(&osdc->request_mutex); -- cgit v0.10.2 From 0ba6478df7c6bef0f4b2625554545f941f89fb97 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 8 Oct 2009 16:57:16 -0700 Subject: ceph: revoke osd request message on request completion If an osd has failed or returned and a request has been sent twice, it's possible to get a reply and unregister the request while the request message is queued for delivery. Since the message references the caller's page vector, we need to revoke it before completing. Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index d14019d..0aea8af 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -469,10 +469,15 @@ static void __unregister_request(struct ceph_osd_client *osdc, rb_erase(&req->r_node, &osdc->requests); osdc->num_requests--; - list_del_init(&req->r_osd_item); - if (list_empty(&req->r_osd->o_requests)) - remove_osd(osdc, req->r_osd); - req->r_osd = NULL; + if (req->r_osd) { + /* make sure the original request isn't in flight. */ + ceph_con_revoke(&req->r_osd->o_con, req->r_request); + + list_del_init(&req->r_osd_item); + if (list_empty(&req->r_osd->o_requests)) + remove_osd(osdc, req->r_osd); + req->r_osd = NULL; + } ceph_osdc_put_request(req); -- cgit v0.10.2 From 991abb6ecfc8edf9863aa6a3f43249e63f9d4d4e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 8 Oct 2009 22:22:37 -0700 Subject: ceph: fail gracefully on corrupt osdmap (bad pg_temp mapping) Return an error and report a corrupt map instead of crying BUG(). Signed-off-by: Sage Weil diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index e38fe63..342e5f8 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -366,8 +366,8 @@ static int osdmap_set_max_osd(struct ceph_osdmap *map, int max) /* * Insert a new pg_temp mapping */ -static void __insert_pg_mapping(struct ceph_pg_mapping *new, - struct rb_root *root) +static int __insert_pg_mapping(struct ceph_pg_mapping *new, + struct rb_root *root) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -381,11 +381,12 @@ static void __insert_pg_mapping(struct ceph_pg_mapping *new, else if (new->pgid > pg->pgid) p = &(*p)->rb_right; else - BUG(); + return -EEXIST; } rb_link_node(&new->node, parent, p); rb_insert_color(&new->node, root); + return 0; } /* @@ -481,7 +482,9 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) for (j = 0; j < n; j++) ceph_decode_32(p, pg->osds[j]); - __insert_pg_mapping(pg, &map->pg_temp); + err = __insert_pg_mapping(pg, &map->pg_temp); + if (err) + goto bad; dout(" added pg_temp %llx len %d\n", pgid, len); } @@ -681,7 +684,9 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, pg->len = pglen; for (j = 0; j < len; j++) ceph_decode_32(p, pg->osds[j]); - __insert_pg_mapping(pg, &map->pg_temp); + err = __insert_pg_mapping(pg, &map->pg_temp); + if (err) + goto bad; dout(" added pg_temp %llx len %d\n", pgid, pglen); } } -- cgit v0.10.2 From 81b024e70fed635a2cf5a4bf911db1649bb005f5 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 9 Oct 2009 10:29:18 -0700 Subject: ceph: reset osd session on fault, not peer_reset The peer_reset just takes longer (until we reconnect and discover the osd dropped the session... which it will). Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 0aea8af..4a8b4f0 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -294,10 +294,7 @@ __lookup_request_ge(struct ceph_osd_client *osdc, /* - * The messaging layer will reconnect to the osd as needed. If the - * session has dropped, the OSD will have dropped the session state, - * and we'll get notified by the messaging layer. If that happens, we - * need to resubmit all requests for that osd. + * If the osd connection drops, we need to resubmit all requests. */ static void osd_reset(struct ceph_connection *con) { @@ -1301,7 +1298,7 @@ const static struct ceph_connection_operations osd_con_ops = { .put = put_osd_con, .dispatch = dispatch, .alloc_msg = alloc_msg, - .peer_reset = osd_reset, + .fault = osd_reset, .alloc_middle = ceph_alloc_middle, .prepare_pages = prepare_pages, }; -- cgit v0.10.2 From 266673db423e6ab247170d6c6d72ec36e530a911 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 9 Oct 2009 10:31:32 -0700 Subject: ceph: cancel osd requests before resending them This ensures we don't submit the same request twice if we are kicking a specific osd (as with an osd_reset), or when we hit a transient error and resend. Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 4a8b4f0..8e33928 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -813,10 +813,13 @@ static void kick_requests(struct ceph_osd_client *osdc, if (req->r_resend) { dout(" r_resend set on tid %llu\n", req->r_tid); + __cancel_request(req); goto kick; } - if (req->r_osd && kickosd == req->r_osd) + if (req->r_osd && kickosd == req->r_osd) { + __cancel_request(req); goto kick; + } err = __map_osds(osdc, req); if (err == 0) -- cgit v0.10.2 From 13e38c8ae771d73bf6d1f0f98e35f99c0f0d48ff Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 9 Oct 2009 16:36:34 -0700 Subject: ceph: update to mon client protocol v15 The mon request headers now include session_mon information that must be properly initialized. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 21ed51b..acf24c6 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -37,10 +37,10 @@ */ #define CEPH_OSD_PROTOCOL 7 /* cluster internal */ #define CEPH_MDS_PROTOCOL 9 /* cluster internal */ -#define CEPH_MON_PROTOCOL 4 /* cluster internal */ +#define CEPH_MON_PROTOCOL 5 /* cluster internal */ #define CEPH_OSDC_PROTOCOL 20 /* server/client */ #define CEPH_MDSC_PROTOCOL 29 /* server/client */ -#define CEPH_MONC_PROTOCOL 14 /* server/client */ +#define CEPH_MONC_PROTOCOL 15 /* server/client */ #define CEPH_INO_ROOT 1 @@ -118,9 +118,14 @@ struct ceph_file_layout { #define CEPH_MSG_OSD_OP 42 #define CEPH_MSG_OSD_OPREPLY 43 +struct ceph_mon_request_header { + __le64 have_version; + __le16 session_mon; + __le64 session_mon_tid; +} __attribute__ ((packed)); struct ceph_mon_statfs { - __le64 have_version; + struct ceph_mon_request_header monhdr; struct ceph_fsid fsid; __le64 tid; } __attribute__ ((packed)); @@ -138,22 +143,22 @@ struct ceph_mon_statfs_reply { } __attribute__ ((packed)); struct ceph_osd_getmap { - __le64 have_version; + struct ceph_mon_request_header monhdr; struct ceph_fsid fsid; __le32 start; } __attribute__ ((packed)); struct ceph_mds_getmap { - __le64 have_version; + struct ceph_mon_request_header monhdr; struct ceph_fsid fsid; } __attribute__ ((packed)); struct ceph_client_mount { - __le64 have_version; + struct ceph_mon_request_header monhdr; } __attribute__ ((packed)); struct ceph_mon_subscribe_item { - __le64 have; + __le64 have_version; __le64 have; __u8 onetime; } __attribute__ ((packed)); diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 63f7f13..b48abc0 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -857,7 +857,7 @@ out: static int verify_hello(struct ceph_connection *con) { if (memcmp(con->in_banner, CEPH_BANNER, strlen(CEPH_BANNER))) { - pr_err("connect to/from %s has bad banner\n", + pr_err("connect to %s got bad banner\n", pr_addr(&con->peer_addr.in_addr)); con->error_msg = "protocol error, bad banner"; return -1; diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 9c34df1..dc698ca 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -273,7 +273,9 @@ static void __request_mount(struct ceph_mon_client *monc) if (IS_ERR(msg)) return; h = msg->front.iov_base; - h->have_version = 0; + h->monhdr.have_version = 0; + h->monhdr.session_mon = cpu_to_le16(-1); + h->monhdr.session_mon_tid = 0; ceph_con_send(monc->con, msg); } @@ -422,7 +424,9 @@ static int send_statfs(struct ceph_mon_client *monc, return PTR_ERR(msg); req->request = msg; h = msg->front.iov_base; - h->have_version = 0; + h->monhdr.have_version = 0; + h->monhdr.session_mon = cpu_to_le16(-1); + h->monhdr.session_mon_tid = 0; h->fsid = monc->monmap->fsid; h->tid = cpu_to_le64(req->tid); ceph_con_send(monc->con, msg); -- cgit v0.10.2 From 752727a1b21a462d6ef634d552f180ae692f8947 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 9 Oct 2009 16:38:45 -0700 Subject: ceph: add file layout validation This tracks updates to code shared with userspace. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.c b/fs/ceph/ceph_fs.c index 9371ff1..a950b40 100644 --- a/fs/ceph/ceph_fs.c +++ b/fs/ceph/ceph_fs.c @@ -3,6 +3,30 @@ */ #include "types.h" +/* + * return true if @layout appears to be valid + */ +int ceph_file_layout_is_valid(const struct ceph_file_layout *layout) +{ + __u32 su = le32_to_cpu(layout->fl_stripe_unit); + __u32 sc = le32_to_cpu(layout->fl_stripe_count); + __u32 os = le32_to_cpu(layout->fl_object_size); + + /* stripe unit, object size must be non-zero, 64k increment */ + if (!su || (su & (CEPH_MIN_STRIPE_UNIT-1))) + return 0; + if (!os || (os & (CEPH_MIN_STRIPE_UNIT-1))) + return 0; + /* object size must be a multiple of stripe unit */ + if (os < su || os % su) + return 0; + /* stripe count must be non-zero */ + if (!sc) + return 0; + return 1; +} + + int ceph_flags_to_mode(int flags) { #ifdef O_DIRECTORY /* fixme */ diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index acf24c6..b3bbab1 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -72,7 +72,9 @@ struct ceph_file_layout { __le32 fl_pg_pool; /* namespace, crush ruleset, rep level */ } __attribute__ ((packed)); +#define CEPH_MIN_STRIPE_UNIT 65536 +int ceph_file_layout_is_valid(const struct ceph_file_layout *layout); /********************************************* -- cgit v0.10.2 From 8fc57da4d32767cc6096ecaed24636dabefd1dbc Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 12 Oct 2009 10:28:13 -0700 Subject: ceph: ignore trailing data in monamp This lets us extend the format more easily. Signed-off-by: Sage Weil diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index dc698ca..d6c8e78 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -60,9 +60,6 @@ struct ceph_monmap *ceph_monmap_decode(void *p, void *end) m->num_mon = num_mon; ceph_decode_copy(&p, m->mon_inst, num_mon*sizeof(m->mon_inst[0])); - if (p != end) - goto bad; - dout("monmap_decode epoch %d, num_mon %d\n", m->epoch, m->num_mon); for (i = 0; i < m->num_mon; i++) -- cgit v0.10.2 From 572033069dbc2cff8d4a2d2b34c576e1813fda70 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 9 Oct 2009 21:52:34 -0700 Subject: ceph: remove unused CEPH_MSG_{OSD,MDS}_GETMAP Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index b3bbab1..56af192 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -100,7 +100,6 @@ int ceph_file_layout_is_valid(const struct ceph_file_layout *layout); #define CEPH_MSG_MON_SUBSCRIBE_ACK 16 /* client <-> mds */ -#define CEPH_MSG_MDS_GETMAP 20 #define CEPH_MSG_MDS_MAP 21 #define CEPH_MSG_CLIENT_SESSION 22 @@ -115,7 +114,6 @@ int ceph_file_layout_is_valid(const struct ceph_file_layout *layout); #define CEPH_MSG_CLIENT_CAPRELEASE 0x313 /* osd */ -#define CEPH_MSG_OSD_GETMAP 40 #define CEPH_MSG_OSD_MAP 41 #define CEPH_MSG_OSD_OP 42 #define CEPH_MSG_OSD_OPREPLY 43 diff --git a/fs/ceph/super.c b/fs/ceph/super.c index b3404a3..442a990 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -231,7 +231,6 @@ const char *ceph_msg_type_name(int type) case CEPH_MSG_CLIENT_MOUNT_ACK: return "client_mount_ack"; case CEPH_MSG_STATFS: return "statfs"; case CEPH_MSG_STATFS_REPLY: return "statfs_reply"; - case CEPH_MSG_MDS_GETMAP: return "mds_getmap"; case CEPH_MSG_MDS_MAP: return "mds_map"; case CEPH_MSG_CLIENT_SESSION: return "client_session"; case CEPH_MSG_CLIENT_RECONNECT: return "client_reconnect"; @@ -242,7 +241,6 @@ const char *ceph_msg_type_name(int type) case CEPH_MSG_CLIENT_CAPRELEASE: return "client_cap_release"; case CEPH_MSG_CLIENT_SNAP: return "client_snap"; case CEPH_MSG_CLIENT_LEASE: return "client_lease"; - case CEPH_MSG_OSD_GETMAP: return "osd_getmap"; case CEPH_MSG_OSD_MAP: return "osd_map"; case CEPH_MSG_OSD_OP: return "osd_op"; case CEPH_MSG_OSD_OPREPLY: return "osd_opreply"; -- cgit v0.10.2 From 535bbb530764b1b2b3b732837f0e61e1baae7109 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 13 Oct 2009 12:55:26 -0700 Subject: ceph: add version field to message header This makes it easier for individual message types to indicate their particular encoding, and make future changes backward compatible. Signed-off-by: Sage Weil diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h index 73921ae..9abc879 100644 --- a/fs/ceph/msgr.h +++ b/fs/ceph/msgr.h @@ -21,7 +21,7 @@ * whenever the wire protocol changes. try to keep this string length * constant. */ -#define CEPH_BANNER "ceph v021" +#define CEPH_BANNER "ceph v022" #define CEPH_BANNER_MAX_LEN 30 @@ -125,6 +125,7 @@ struct ceph_msg_header { __le64 seq; /* message seq# for this session */ __le16 type; /* message type */ __le16 priority; /* priority. higher value == higher priority */ + __le16 version; /* version of message encoding */ __le32 front_len; /* bytes in main payload */ __le32 middle_len;/* bytes in middle payload */ -- cgit v0.10.2 From c89136ea4253c73e89e97f5138bb22d97ad9f564 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 14 Oct 2009 09:59:09 -0700 Subject: ceph: convert encode/decode macros to inlines This avoids the fugly pass by reference and makes the code a bit easier to read. Signed-off-by: Sage Weil diff --git a/fs/ceph/decode.h b/fs/ceph/decode.h index fc2769d..91179fb 100644 --- a/fs/ceph/decode.h +++ b/fs/ceph/decode.h @@ -3,12 +3,44 @@ #include +#include "types.h" + /* * in all cases, * void **p pointer to position pointer * void *end pointer to end of buffer (last byte + 1) */ +static inline u64 ceph_decode_64(void **p) +{ + u64 v = get_unaligned_le64(*p); + *p += sizeof(u64); + return v; +} +static inline u32 ceph_decode_32(void **p) +{ + u32 v = get_unaligned_le32(*p); + *p += sizeof(u32); + return v; +} +static inline u16 ceph_decode_16(void **p) +{ + u16 v = get_unaligned_le16(*p); + *p += sizeof(u16); + return v; +} +static inline u8 ceph_decode_8(void **p) +{ + u8 v = *(u8 *)*p; + (*p)++; + return v; +} +static inline void ceph_decode_copy(void **p, void *pv, size_t n) +{ + memcpy(pv, *p, n); + *p += n; +} + /* * bounds check input. */ @@ -18,48 +50,20 @@ goto bad; \ } while (0) -#define ceph_decode_64(p, v) \ - do { \ - v = get_unaligned_le64(*(p)); \ - *(p) += sizeof(u64); \ - } while (0) -#define ceph_decode_32(p, v) \ - do { \ - v = get_unaligned_le32(*(p)); \ - *(p) += sizeof(u32); \ - } while (0) -#define ceph_decode_16(p, v) \ - do { \ - v = get_unaligned_le16(*(p)); \ - *(p) += sizeof(u16); \ - } while (0) -#define ceph_decode_8(p, v) \ - do { \ - v = *(u8 *)*(p); \ - (*p)++; \ - } while (0) - -#define ceph_decode_copy(p, pv, n) \ - do { \ - memcpy(pv, *(p), n); \ - *(p) += n; \ - } while (0) - -/* bounds check too */ #define ceph_decode_64_safe(p, end, v, bad) \ do { \ ceph_decode_need(p, end, sizeof(u64), bad); \ - ceph_decode_64(p, v); \ + v = ceph_decode_64(p); \ } while (0) #define ceph_decode_32_safe(p, end, v, bad) \ do { \ ceph_decode_need(p, end, sizeof(u32), bad); \ - ceph_decode_32(p, v); \ + v = ceph_decode_32(p); \ } while (0) #define ceph_decode_16_safe(p, end, v, bad) \ do { \ ceph_decode_need(p, end, sizeof(u16), bad); \ - ceph_decode_16(p, v); \ + v = ceph_decode_16(p); \ } while (0) #define ceph_decode_copy_safe(p, end, pv, n, bad) \ @@ -71,41 +75,42 @@ /* * struct ceph_timespec <-> struct timespec */ -#define ceph_decode_timespec(ts, tv) \ - do { \ - (ts)->tv_sec = le32_to_cpu((tv)->tv_sec); \ - (ts)->tv_nsec = le32_to_cpu((tv)->tv_nsec); \ - } while (0) -#define ceph_encode_timespec(tv, ts) \ - do { \ - (tv)->tv_sec = cpu_to_le32((ts)->tv_sec); \ - (tv)->tv_nsec = cpu_to_le32((ts)->tv_nsec); \ - } while (0) - +static inline void ceph_decode_timespec(struct timespec *ts, + struct ceph_timespec *tv) +{ + ts->tv_sec = le32_to_cpu(tv->tv_sec); + ts->tv_nsec = le32_to_cpu(tv->tv_nsec); +} +static inline void ceph_encode_timespec(struct ceph_timespec *tv, + struct timespec *ts) +{ + tv->tv_sec = cpu_to_le32(ts->tv_sec); + tv->tv_nsec = cpu_to_le32(ts->tv_nsec); +} /* * encoders */ -#define ceph_encode_64(p, v) \ - do { \ - put_unaligned_le64(v, (__le64 *)*(p)); \ - *(p) += sizeof(u64); \ - } while (0) -#define ceph_encode_32(p, v) \ - do { \ - put_unaligned_le32(v, (__le32 *)*(p)); \ - *(p) += sizeof(u32); \ - } while (0) -#define ceph_encode_16(p, v) \ - do { \ - put_unaligned_le16(v), (__le16 *)*(p)); \ - *(p) += sizeof(u16); \ - } while (0) -#define ceph_encode_8(p, v) \ - do { \ - *(u8 *)*(p) = v; \ - (*(p))++; \ - } while (0) +static inline void ceph_encode_64(void **p, u64 v) +{ + put_unaligned_le64(v, (__le64 *)*p); + *p += sizeof(u64); +} +static inline void ceph_encode_32(void **p, u32 v) +{ + put_unaligned_le32(v, (__le32 *)*p); + *p += sizeof(u32); +} +static inline void ceph_encode_16(void **p, u16 v) +{ + put_unaligned_le16(v, (__le16 *)*p); + *p += sizeof(u16); +} +static inline void ceph_encode_8(void **p, u8 v) +{ + *(u8 *)*p = v; + (*p)++; +} /* * filepath, string encoders diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index de8ba4a..2b19da3 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -136,9 +136,9 @@ static int parse_reply_info_dir(void **p, void *end, goto bad; ceph_decode_need(p, end, sizeof(num) + 2, bad); - ceph_decode_32(p, num); - ceph_decode_8(p, info->dir_end); - ceph_decode_8(p, info->dir_complete); + num = ceph_decode_32(p); + info->dir_end = ceph_decode_8(p); + info->dir_complete = ceph_decode_8(p); if (num == 0) goto done; @@ -160,7 +160,7 @@ static int parse_reply_info_dir(void **p, void *end, while (num) { /* dentry */ ceph_decode_need(p, end, sizeof(u32)*2, bad); - ceph_decode_32(p, info->dir_dname_len[i]); + info->dir_dname_len[i] = ceph_decode_32(p); ceph_decode_need(p, end, info->dir_dname_len[i], bad); info->dir_dname[i] = *p; *p += info->dir_dname_len[i]; @@ -1791,10 +1791,10 @@ static void handle_forward(struct ceph_mds_client *mdsc, struct ceph_msg *msg) from_mds = le64_to_cpu(msg->hdr.src.name.num); ceph_decode_need(&p, end, sizeof(u64)+2*sizeof(u32), bad); - ceph_decode_64(&p, tid); - ceph_decode_32(&p, next_mds); - ceph_decode_32(&p, fwd_seq); - ceph_decode_8(&p, must_resend); + tid = ceph_decode_64(&p); + next_mds = ceph_decode_32(&p); + fwd_seq = ceph_decode_32(&p); + must_resend = ceph_decode_8(&p); WARN_ON(must_resend); /* shouldn't happen. */ @@ -2783,8 +2783,8 @@ void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc, struct ceph_msg *msg) pr_err("got mdsmap with wrong fsid\n"); return; } - ceph_decode_32(&p, epoch); - ceph_decode_32(&p, maplen); + epoch = ceph_decode_32(&p); + maplen = ceph_decode_32(&p); dout("handle_map epoch %u len %d\n", epoch, (int)maplen); /* do we need it? */ diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 09180d8..80daea0 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -60,21 +60,21 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) ceph_decode_16_safe(p, end, version, bad); ceph_decode_need(p, end, 8*sizeof(u32) + sizeof(u64), bad); - ceph_decode_32(p, m->m_epoch); - ceph_decode_32(p, m->m_client_epoch); - ceph_decode_32(p, m->m_last_failure); - ceph_decode_32(p, m->m_root); - ceph_decode_32(p, m->m_session_timeout); - ceph_decode_32(p, m->m_session_autoclose); - ceph_decode_64(p, m->m_max_file_size); - ceph_decode_32(p, m->m_max_mds); + m->m_epoch = ceph_decode_32(p); + m->m_client_epoch = ceph_decode_32(p); + m->m_last_failure = ceph_decode_32(p); + m->m_root = ceph_decode_32(p); + m->m_session_timeout = ceph_decode_32(p); + m->m_session_autoclose = ceph_decode_32(p); + m->m_max_file_size = ceph_decode_64(p); + m->m_max_mds = ceph_decode_32(p); m->m_info = kcalloc(m->m_max_mds, sizeof(*m->m_info), GFP_NOFS); if (m->m_info == NULL) goto badmem; /* pick out active nodes from mds_info (state > 0) */ - ceph_decode_32(p, n); + n = ceph_decode_32(p); for (i = 0; i < n; i++) { u32 namelen; s32 mds, inc, state; @@ -86,18 +86,18 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) ceph_decode_need(p, end, sizeof(addr) + 1 + sizeof(u32), bad); ceph_decode_copy(p, &addr, sizeof(addr)); - ceph_decode_8(p, infoversion); - ceph_decode_32(p, namelen); /* skip mds name */ + infoversion = ceph_decode_8(p); + namelen = ceph_decode_32(p); /* skip mds name */ *p += namelen; ceph_decode_need(p, end, 4*sizeof(u32) + sizeof(u64) + sizeof(addr) + sizeof(struct ceph_timespec), bad); - ceph_decode_32(p, mds); - ceph_decode_32(p, inc); - ceph_decode_32(p, state); - ceph_decode_64(p, state_seq); + mds = ceph_decode_32(p); + inc = ceph_decode_32(p); + state = ceph_decode_32(p); + state_seq = ceph_decode_64(p); *p += sizeof(addr); *p += sizeof(struct ceph_timespec); *p += sizeof(u32); @@ -123,8 +123,8 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) kcalloc(num_export_targets, sizeof(u32), GFP_NOFS); for (j = 0; j < num_export_targets; j++) - ceph_decode_32(&pexport_targets, - m->m_info[mds].export_targets[j]); + m->m_info[mds].export_targets[j] = + ceph_decode_32(&pexport_targets); } else { m->m_info[mds].export_targets = NULL; } @@ -139,8 +139,8 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) goto badmem; ceph_decode_need(p, end, sizeof(u32)*(n+1), bad); for (i = 0; i < n; i++) - ceph_decode_32(p, m->m_data_pg_pools[i]); - ceph_decode_32(p, m->m_cas_pg_pool); + m->m_data_pg_pools[i] = ceph_decode_32(p); + m->m_cas_pg_pool = ceph_decode_32(p); /* ok, we don't care about the rest. */ dout("mdsmap_decode success epoch %u\n", m->m_epoch); diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index d6c8e78..bea2be90 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -45,9 +45,9 @@ struct ceph_monmap *ceph_monmap_decode(void *p, void *end) ceph_decode_need(&p, end, sizeof(fsid) + 2*sizeof(u32), bad); ceph_decode_copy(&p, &fsid, sizeof(fsid)); - ceph_decode_32(&p, epoch); + epoch = ceph_decode_32(&p); - ceph_decode_32(&p, num_mon); + num_mon = ceph_decode_32(&p); ceph_decode_need(&p, end, num_mon*sizeof(m->mon_inst[0]), bad); if (num_mon >= CEPH_MAX_MON) diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 8e33928..bbd9a5d 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -894,8 +894,8 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) dout(" %d inc maps\n", nr_maps); while (nr_maps > 0) { ceph_decode_need(&p, end, 2*sizeof(u32), bad); - ceph_decode_32(&p, epoch); - ceph_decode_32(&p, maplen); + epoch = ceph_decode_32(&p); + maplen = ceph_decode_32(&p); ceph_decode_need(&p, end, maplen, bad); next = p + maplen; if (osdc->osdmap && osdc->osdmap->epoch+1 == epoch) { @@ -927,8 +927,8 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) dout(" %d full maps\n", nr_maps); while (nr_maps) { ceph_decode_need(&p, end, 2*sizeof(u32), bad); - ceph_decode_32(&p, epoch); - ceph_decode_32(&p, maplen); + epoch = ceph_decode_32(&p); + maplen = ceph_decode_32(&p); ceph_decode_need(&p, end, maplen, bad); if (nr_maps > 1) { dout("skipping non-latest full map %u len %d\n", diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 342e5f8..6f0aeff 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -67,7 +67,7 @@ static int crush_decode_uniform_bucket(void **p, void *end, { dout("crush_decode_uniform_bucket %p to %p\n", *p, end); ceph_decode_need(p, end, (1+b->h.size) * sizeof(u32), bad); - ceph_decode_32(p, b->item_weight); + b->item_weight = ceph_decode_32(p); return 0; bad: return -EINVAL; @@ -86,8 +86,8 @@ static int crush_decode_list_bucket(void **p, void *end, return -ENOMEM; ceph_decode_need(p, end, 2 * b->h.size * sizeof(u32), bad); for (j = 0; j < b->h.size; j++) { - ceph_decode_32(p, b->item_weights[j]); - ceph_decode_32(p, b->sum_weights[j]); + b->item_weights[j] = ceph_decode_32(p); + b->sum_weights[j] = ceph_decode_32(p); } return 0; bad: @@ -105,7 +105,7 @@ static int crush_decode_tree_bucket(void **p, void *end, return -ENOMEM; ceph_decode_need(p, end, b->num_nodes * sizeof(u32), bad); for (j = 0; j < b->num_nodes; j++) - ceph_decode_32(p, b->node_weights[j]); + b->node_weights[j] = ceph_decode_32(p); return 0; bad: return -EINVAL; @@ -124,8 +124,8 @@ static int crush_decode_straw_bucket(void **p, void *end, return -ENOMEM; ceph_decode_need(p, end, 2 * b->h.size * sizeof(u32), bad); for (j = 0; j < b->h.size; j++) { - ceph_decode_32(p, b->item_weights[j]); - ceph_decode_32(p, b->straws[j]); + b->item_weights[j] = ceph_decode_32(p); + b->straws[j] = ceph_decode_32(p); } return 0; bad: @@ -148,15 +148,15 @@ static struct crush_map *crush_decode(void *pbyval, void *end) return ERR_PTR(-ENOMEM); ceph_decode_need(p, end, 4*sizeof(u32), bad); - ceph_decode_32(p, magic); + magic = ceph_decode_32(p); if (magic != CRUSH_MAGIC) { pr_err("crush_decode magic %x != current %x\n", (unsigned)magic, (unsigned)CRUSH_MAGIC); goto bad; } - ceph_decode_32(p, c->max_buckets); - ceph_decode_32(p, c->max_rules); - ceph_decode_32(p, c->max_devices); + c->max_buckets = ceph_decode_32(p); + c->max_rules = ceph_decode_32(p); + c->max_devices = ceph_decode_32(p); c->device_parents = kcalloc(c->max_devices, sizeof(u32), GFP_NOFS); if (c->device_parents == NULL) @@ -208,11 +208,11 @@ static struct crush_map *crush_decode(void *pbyval, void *end) goto badmem; ceph_decode_need(p, end, 4*sizeof(u32), bad); - ceph_decode_32(p, b->id); - ceph_decode_16(p, b->type); - ceph_decode_16(p, b->alg); - ceph_decode_32(p, b->weight); - ceph_decode_32(p, b->size); + b->id = ceph_decode_32(p); + b->type = ceph_decode_16(p); + b->alg = ceph_decode_16(p); + b->weight = ceph_decode_32(p); + b->size = ceph_decode_32(p); dout("crush_decode bucket size %d off %x %p to %p\n", b->size, (int)(*p-start), *p, end); @@ -227,7 +227,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end) ceph_decode_need(p, end, b->size*sizeof(u32), bad); for (j = 0; j < b->size; j++) - ceph_decode_32(p, b->items[j]); + b->items[j] = ceph_decode_32(p); switch (b->alg) { case CRUSH_BUCKET_UNIFORM: @@ -290,9 +290,9 @@ static struct crush_map *crush_decode(void *pbyval, void *end) ceph_decode_copy_safe(p, end, &r->mask, 4, bad); /* 4 u8's */ ceph_decode_need(p, end, r->len*3*sizeof(u32), bad); for (j = 0; j < r->len; j++) { - ceph_decode_32(p, r->steps[j].op); - ceph_decode_32(p, r->steps[j].arg1); - ceph_decode_32(p, r->steps[j].arg2); + r->steps[j].op = ceph_decode_32(p); + r->steps[j].arg1 = ceph_decode_32(p); + r->steps[j].arg2 = ceph_decode_32(p); } } @@ -411,11 +411,11 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) ceph_decode_need(p, end, 2*sizeof(u64)+6*sizeof(u32), bad); ceph_decode_copy(p, &map->fsid, sizeof(map->fsid)); - ceph_decode_32(p, map->epoch); + map->epoch = ceph_decode_32(p); ceph_decode_copy(p, &map->created, sizeof(map->created)); ceph_decode_copy(p, &map->modified, sizeof(map->modified)); - ceph_decode_32(p, map->num_pools); + map->num_pools = ceph_decode_32(p); map->pg_pool = kcalloc(map->num_pools, sizeof(*map->pg_pool), GFP_NOFS); if (!map->pg_pool) { @@ -425,7 +425,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) ceph_decode_32_safe(p, end, max, bad); while (max--) { ceph_decode_need(p, end, 4+sizeof(map->pg_pool->v), bad); - ceph_decode_32(p, i); + i = ceph_decode_32(p); if (i >= map->num_pools) goto bad; ceph_decode_copy(p, &map->pg_pool[i].v, @@ -438,7 +438,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) ceph_decode_32_safe(p, end, map->flags, bad); - ceph_decode_32(p, max); + max = ceph_decode_32(p); /* (re)alloc osd arrays */ err = osdmap_set_max_osd(map, max); @@ -456,7 +456,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) *p += 4; /* skip length field (should match max) */ for (i = 0; i < map->max_osd; i++) - ceph_decode_32(p, map->osd_weight[i]); + map->osd_weight[i] = ceph_decode_32(p); *p += 4; /* skip length field (should match max) */ ceph_decode_copy(p, map->osd_addr, map->max_osd*sizeof(*map->osd_addr)); @@ -469,8 +469,8 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) struct ceph_pg_mapping *pg; ceph_decode_need(p, end, sizeof(u32) + sizeof(u64), bad); - ceph_decode_64(p, pgid); - ceph_decode_32(p, n); + pgid = ceph_decode_64(p); + n = ceph_decode_32(p); ceph_decode_need(p, end, n * sizeof(u32), bad); pg = kmalloc(sizeof(*pg) + n*sizeof(u32), GFP_NOFS); if (!pg) { @@ -480,7 +480,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) pg->pgid = pgid; pg->len = n; for (j = 0; j < n; j++) - ceph_decode_32(p, pg->osds[j]); + pg->osds[j] = ceph_decode_32(p); err = __insert_pg_mapping(pg, &map->pg_temp); if (err) @@ -537,10 +537,10 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, ceph_decode_need(p, end, sizeof(fsid)+sizeof(modified)+2*sizeof(u32), bad); ceph_decode_copy(p, &fsid, sizeof(fsid)); - ceph_decode_32(p, epoch); + epoch = ceph_decode_32(p); BUG_ON(epoch != map->epoch+1); ceph_decode_copy(p, &modified, sizeof(modified)); - ceph_decode_32(p, new_flags); + new_flags = ceph_decode_32(p); /* full map? */ ceph_decode_32_safe(p, end, len, bad); @@ -568,7 +568,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, ceph_decode_need(p, end, 5*sizeof(u32), bad); /* new max? */ - ceph_decode_32(p, max); + max = ceph_decode_32(p); if (max >= 0) { err = osdmap_set_max_osd(map, max); if (err < 0) @@ -641,8 +641,8 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, while (len--) { u32 osd, off; ceph_decode_need(p, end, sizeof(u32)*2, bad); - ceph_decode_32(p, osd); - ceph_decode_32(p, off); + osd = ceph_decode_32(p); + off = ceph_decode_32(p); pr_info("osd%d weight 0x%x %s\n", osd, off, off == CEPH_OSD_IN ? "(in)" : (off == CEPH_OSD_OUT ? "(out)" : "")); @@ -659,8 +659,8 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, u64 pgid; u32 pglen; ceph_decode_need(p, end, sizeof(u64) + sizeof(u32), bad); - ceph_decode_64(p, pgid); - ceph_decode_32(p, pglen); + pgid = ceph_decode_64(p); + pglen = ceph_decode_32(p); /* remove any? */ while (rbp && rb_entry(rbp, struct ceph_pg_mapping, @@ -683,7 +683,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, pg->pgid = pgid; pg->len = pglen; for (j = 0; j < len; j++) - ceph_decode_32(p, pg->osds[j]); + pg->osds[j] = ceph_decode_32(p); err = __insert_pg_mapping(pg, &map->pg_temp); if (err) goto bad; -- cgit v0.10.2 From f2cf418cec8d61df0651a0140a92a8c75246e14f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 14 Oct 2009 14:09:07 -0700 Subject: ceph: initialize sb->s_bdi, bdi_unregister after kill_anon_super Writeback doesn't work without the bdi set, and writeback on umount doesn't work if we unregister the bdi too early. Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 442a990..7f7d475 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -97,6 +97,7 @@ static int ceph_syncfs(struct super_block *sb, int wait) dout("sync_fs %d\n", wait); ceph_osdc_sync(&ceph_client(sb)->osdc); ceph_mdsc_sync(&ceph_client(sb)->mdsc); + dout("sync_fs %d done\n", wait); return 0; } @@ -777,6 +778,7 @@ static int ceph_init_bdi(struct super_block *sb, struct ceph_client *client) err = bdi_init(&client->backing_dev_info); if (err < 0) return err; + sb->s_bdi = &client->backing_dev_info; /* set ra_pages based on rsize mount option? */ if (client->mount_args.rsize >= PAGE_CACHE_SIZE) @@ -861,8 +863,8 @@ static void ceph_kill_sb(struct super_block *s) struct ceph_client *client = ceph_sb_to_client(s); dout("kill_sb %p\n", s); ceph_mdsc_pre_umount(&client->mdsc); - bdi_unregister(&client->backing_dev_info); kill_anon_super(s); /* will call put_super after sb is r/o */ + bdi_unregister(&client->backing_dev_info); bdi_destroy(&client->backing_dev_info); ceph_destroy_client(client); } -- cgit v0.10.2 From cdc35f96277314bbfeefd0505410cabd69aebd8d Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 14 Oct 2009 14:24:19 -0700 Subject: ceph: move generic flushing code into helper Both callers of __mark_caps_flushing() do the same work; move it into the helper. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 5c7d0e9..111439d 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1272,16 +1272,30 @@ static void ceph_flush_snaps(struct ceph_inode_info *ci) /* * Add dirty inode to the flushing list. Assigned a seq number so we * can wait for caps to flush without starving. + * + * Called under i_lock. */ -static void __mark_caps_flushing(struct inode *inode, +static int __mark_caps_flushing(struct inode *inode, struct ceph_mds_session *session) { struct ceph_mds_client *mdsc = &ceph_client(inode->i_sb)->mdsc; struct ceph_inode_info *ci = ceph_inode(inode); - + int flushing; + + BUG_ON(ci->i_dirty_caps == 0); BUG_ON(list_empty(&ci->i_dirty_item)); + + flushing = ci->i_dirty_caps; + dout("__mark_caps_flushing flushing %s, flushing_caps %s -> %s\n", + ceph_cap_string(flushing), + ceph_cap_string(ci->i_flushing_caps), + ceph_cap_string(ci->i_flushing_caps | flushing)); + ci->i_flushing_caps |= flushing; + ci->i_dirty_caps = 0; + spin_lock(&mdsc->cap_dirty_lock); if (list_empty(&ci->i_flushing_item)) { + list_del_init(&ci->i_dirty_item); list_add_tail(&ci->i_flushing_item, &session->s_cap_flushing); mdsc->num_cap_flushing++; ci->i_cap_flush_seq = ++mdsc->cap_flush_seq; @@ -1289,6 +1303,8 @@ static void __mark_caps_flushing(struct inode *inode, ci->i_cap_flush_seq); } spin_unlock(&mdsc->cap_dirty_lock); + + return flushing; } /* @@ -1504,17 +1520,8 @@ ack: took_snap_rwsem = 1; } - if (cap == ci->i_auth_cap && ci->i_dirty_caps) { - /* update dirty, flushing bits */ - flushing = ci->i_dirty_caps; - dout(" flushing %s, flushing_caps %s -> %s\n", - ceph_cap_string(flushing), - ceph_cap_string(ci->i_flushing_caps), - ceph_cap_string(ci->i_flushing_caps | flushing)); - ci->i_flushing_caps |= flushing; - ci->i_dirty_caps = 0; - __mark_caps_flushing(inode, session); - } + if (cap == ci->i_auth_cap && ci->i_dirty_caps) + flushing = __mark_caps_flushing(inode, session); mds = cap->mds; /* remember mds, so we don't repeat */ sent++; @@ -1605,15 +1612,7 @@ retry: if (cap->session->s_state < CEPH_MDS_SESSION_OPEN) goto out; - __mark_caps_flushing(inode, session); - - flushing = ci->i_dirty_caps; - dout(" flushing %s, flushing_caps %s -> %s\n", - ceph_cap_string(flushing), - ceph_cap_string(ci->i_flushing_caps), - ceph_cap_string(ci->i_flushing_caps | flushing)); - ci->i_flushing_caps |= flushing; - ci->i_dirty_caps = 0; + flushing = __mark_caps_flushing(inode, session); /* __send_cap drops i_lock */ delayed = __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH, used, want, -- cgit v0.10.2 From afcdaea3f2a78ce4873bd7e98a6d603bda23d167 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 14 Oct 2009 14:27:38 -0700 Subject: ceph: flush dirty caps via the cap_dirty list Previously we were flushing dirty caps by passing an extra flag when traversing the delayed caps list. Besides being a bit ugly, that can also miss caps that are dirty but didn't result in a cap requeue: notably, mark_caps_dirty(). Separate the flushing into a separate helper, and traverse the cap_dirty list. This also brings i_dirty_item in line with i_dirty_caps: we are on the list IFF caps != 0. We carry an inode ref IFF dirty_caps|flushing_caps != 0. Lose the unused return value from __ceph_mark_caps_dirty(). Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 111439d..40b8d34 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -997,7 +997,7 @@ void ceph_queue_caps_release(struct inode *inode) if (le32_to_cpu(head->num) == CEPH_CAPS_PER_RELEASE) { dout(" release msg %p full\n", msg); list_move_tail(&msg->list_head, - &session->s_cap_releases_done); + &session->s_cap_releases_done); } else { dout(" release msg %p at %d/%d (%d)\n", msg, (int)le32_to_cpu(head->num), @@ -1292,14 +1292,20 @@ static int __mark_caps_flushing(struct inode *inode, ceph_cap_string(ci->i_flushing_caps | flushing)); ci->i_flushing_caps |= flushing; ci->i_dirty_caps = 0; + dout(" inode %p now !dirty\n", inode); spin_lock(&mdsc->cap_dirty_lock); + list_del_init(&ci->i_dirty_item); + + ci->i_cap_flush_seq = ++mdsc->cap_flush_seq; if (list_empty(&ci->i_flushing_item)) { - list_del_init(&ci->i_dirty_item); list_add_tail(&ci->i_flushing_item, &session->s_cap_flushing); mdsc->num_cap_flushing++; - ci->i_cap_flush_seq = ++mdsc->cap_flush_seq; - dout(" inode %p now flushing seq %lld\n", &ci->vfs_inode, + dout(" inode %p now flushing seq %lld\n", inode, + ci->i_cap_flush_seq); + } else { + list_move_tail(&ci->i_flushing_item, &session->s_cap_flushing); + dout(" inode %p now flushing (more) seq %lld\n", inode, ci->i_cap_flush_seq); } spin_unlock(&mdsc->cap_dirty_lock); @@ -1555,32 +1561,33 @@ ack: * Mark caps dirty. If inode is newly dirty, add to the global dirty * list. */ -int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) +void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) { struct ceph_mds_client *mdsc = &ceph_client(ci->vfs_inode.i_sb)->mdsc; struct inode *inode = &ci->vfs_inode; - int was = __ceph_caps_dirty(ci); + int was_dirty = ci->i_dirty_caps; int dirty = 0; dout("__mark_dirty_caps %p %s dirty %s -> %s\n", &ci->vfs_inode, ceph_cap_string(mask), ceph_cap_string(ci->i_dirty_caps), ceph_cap_string(ci->i_dirty_caps | mask)); ci->i_dirty_caps |= mask; - if (!was) { + if (!was_dirty) { dout(" inode %p now dirty\n", &ci->vfs_inode); spin_lock(&mdsc->cap_dirty_lock); list_add(&ci->i_dirty_item, &mdsc->cap_dirty); spin_unlock(&mdsc->cap_dirty_lock); - igrab(inode); - dirty |= I_DIRTY_SYNC; + if (ci->i_flushing_caps == 0) { + igrab(inode); + dirty |= I_DIRTY_SYNC; + } } - if ((was & CEPH_CAP_FILE_BUFFER) && + if (((was_dirty | ci->i_flushing_caps) & CEPH_CAP_FILE_BUFFER) && (mask & CEPH_CAP_FILE_BUFFER)) dirty |= I_DIRTY_DATASYNC; if (dirty) __mark_inode_dirty(inode, dirty); __cap_delay_requeue(mdsc, ci); - return was; } /* @@ -2327,7 +2334,7 @@ static void handle_cap_flush_ack(struct inode *inode, int dirty = le32_to_cpu(m->dirty); int cleaned = 0; u64 flush_tid = le64_to_cpu(m->client_tid); - int old_dirty = 0, new_dirty = 0; + int drop = 0; int i; for (i = 0; i < CEPH_CAP_BITS; i++) @@ -2344,9 +2351,7 @@ static void handle_cap_flush_ack(struct inode *inode, if (ci->i_flushing_caps == (ci->i_flushing_caps & ~cleaned)) goto out; - old_dirty = ci->i_dirty_caps | ci->i_flushing_caps; ci->i_flushing_caps &= ~cleaned; - new_dirty = ci->i_dirty_caps | ci->i_flushing_caps; spin_lock(&mdsc->cap_dirty_lock); if (ci->i_flushing_caps == 0) { @@ -2360,17 +2365,19 @@ static void handle_cap_flush_ack(struct inode *inode, mdsc->num_cap_flushing--; wake_up(&mdsc->cap_flushing_wq); dout(" inode %p now !flushing\n", inode); - } - if (old_dirty && !new_dirty) { - dout(" inode %p now clean\n", inode); - list_del_init(&ci->i_dirty_item); + + if (ci->i_dirty_caps == 0) { + dout(" inode %p now clean\n", inode); + BUG_ON(!list_empty(&ci->i_dirty_item)); + drop = 1; + } } spin_unlock(&mdsc->cap_dirty_lock); wake_up(&ci->i_cap_wq); out: spin_unlock(&inode->i_lock); - if (old_dirty && !new_dirty) + if (drop) iput(inode); } @@ -2676,14 +2683,11 @@ bad: /* * Delayed work handler to process end of delayed cap release LRU list. */ -void ceph_check_delayed_caps(struct ceph_mds_client *mdsc, int flushdirty) +void ceph_check_delayed_caps(struct ceph_mds_client *mdsc) { struct ceph_inode_info *ci; int flags = CHECK_CAPS_NODELAY; - if (flushdirty) - flags |= CHECK_CAPS_FLUSH; - dout("check_delayed_caps\n"); while (1) { spin_lock(&mdsc->cap_delay_lock); @@ -2704,6 +2708,32 @@ void ceph_check_delayed_caps(struct ceph_mds_client *mdsc, int flushdirty) } /* + * Flush all dirty caps to the mds + */ +void ceph_flush_dirty_caps(struct ceph_mds_client *mdsc) +{ + struct ceph_inode_info *ci; + struct inode *inode; + + dout("flush_dirty_caps\n"); + spin_lock(&mdsc->cap_dirty_lock); + while (!list_empty(&mdsc->cap_dirty)) { + ci = list_first_entry(&mdsc->cap_dirty, + struct ceph_inode_info, + i_dirty_item); + inode = igrab(&ci->vfs_inode); + spin_unlock(&mdsc->cap_dirty_lock); + if (inode) { + ceph_check_caps(ci, CHECK_CAPS_NODELAY|CHECK_CAPS_FLUSH, + NULL); + iput(inode); + } + spin_lock(&mdsc->cap_dirty_lock); + } + spin_unlock(&mdsc->cap_dirty_lock); +} + +/* * Drop open file reference. If we were the last open file, * we may need to release capabilities to the MDS (or schedule * their delayed release). diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 2b19da3..12d66c0 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2504,7 +2504,7 @@ static void delayed_work(struct work_struct *work) int renew_caps; dout("mdsc delayed_work\n"); - ceph_check_delayed_caps(mdsc, 0); + ceph_check_delayed_caps(mdsc); mutex_lock(&mdsc->mutex); renew_interval = mdsc->mdsmap->m_session_timeout >> 2; @@ -2627,7 +2627,7 @@ void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc) mdsc->stopping = 1; drop_leases(mdsc); - ceph_check_delayed_caps(mdsc, 1); + ceph_flush_dirty_caps(mdsc); wait_requests(mdsc); } @@ -2677,7 +2677,7 @@ void ceph_mdsc_sync(struct ceph_mds_client *mdsc) mutex_unlock(&mdsc->mutex); dout("sync want tid %lld flush_seq %lld\n", want_tid, want_flush); - ceph_check_delayed_caps(mdsc, 1); + ceph_flush_dirty_caps(mdsc); wait_unsafe_requests(mdsc, want_tid); wait_event(mdsc->cap_flushing_wq, check_cap_flush(mdsc, want_flush)); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index cfd39ef..0bbf58a 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -524,7 +524,7 @@ static inline int __ceph_caps_dirty(struct ceph_inode_info *ci) { return ci->i_dirty_caps | ci->i_flushing_caps; } -extern int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask); +extern void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask); extern int ceph_caps_revoking(struct ceph_inode_info *ci, int mask); extern int __ceph_caps_used(struct ceph_inode_info *ci); @@ -814,8 +814,8 @@ extern void __ceph_flush_snaps(struct ceph_inode_info *ci, struct ceph_mds_session **psession); extern void ceph_check_caps(struct ceph_inode_info *ci, int flags, struct ceph_mds_session *session); -extern void ceph_check_delayed_caps(struct ceph_mds_client *mdsc, - int flushdirty); +extern void ceph_check_delayed_caps(struct ceph_mds_client *mdsc); +extern void ceph_flush_dirty_caps(struct ceph_mds_client *mdsc); extern int ceph_encode_inode_release(void **p, struct inode *inode, int mds, int drop, int unless, int force); -- cgit v0.10.2 From 07bd10fb9853a41a7f0bb271721cca97d15eccae Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 14 Oct 2009 17:26:40 -0700 Subject: ceph: correct subscribe_ack msgpool payload size Defined a struct for the SUBSCRIBE_ACK, and use that to size the msgpool. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 56af192..9b16e2e 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -162,6 +162,11 @@ struct ceph_mon_subscribe_item { __u8 onetime; } __attribute__ ((packed)); +struct ceph_mon_subscribe_ack { + __le32 duration; /* seconds */ + struct ceph_fsid fsid; +} __attribute__ ((packed)); + /* * mds states * > 0 -> in diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index bea2be90..d52e529 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -199,10 +199,12 @@ static void handle_subscribe_ack(struct ceph_mon_client *monc, struct ceph_msg *msg) { unsigned seconds; - void *p = msg->front.iov_base; - void *end = p + msg->front.iov_len; + struct ceph_mon_subscribe_ack *h = msg->front.iov_base; + + if (msg->front.iov_len < sizeof(*h)) + goto bad; + seconds = le32_to_cpu(h->duration); - ceph_decode_32_safe(&p, end, seconds, bad); mutex_lock(&monc->mutex); if (monc->hunting) { pr_info("mon%d %s session established\n", @@ -541,7 +543,8 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) err = ceph_msgpool_init(&monc->msgpool_mount_ack, 4096, 1, false); if (err < 0) goto out; - err = ceph_msgpool_init(&monc->msgpool_subscribe_ack, 8, 1, false); + err = ceph_msgpool_init(&monc->msgpool_subscribe_ack, + sizeof(struct ceph_mon_subscribe_ack), 1, false); if (err < 0) goto out; err = ceph_msgpool_init(&monc->msgpool_statfs_reply, -- cgit v0.10.2 From 8f3bc053c610826a657714649ea596f07875db2e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 14 Oct 2009 17:36:07 -0700 Subject: ceph: warn on allocation from msgpool with larger front_len Pass the front_len we need when pulling a message off a msgpool, and WARN if it is greater than the pool's size. Then try to allocate a new message (to continue without failing). Signed-off-by: Sage Weil diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index d52e529..e6e954c 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -639,14 +639,15 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, { struct ceph_mon_client *monc = con->private; int type = le16_to_cpu(hdr->type); + int front = le32_to_cpu(hdr->front_len); switch (type) { case CEPH_MSG_CLIENT_MOUNT_ACK: - return ceph_msgpool_get(&monc->msgpool_mount_ack); + return ceph_msgpool_get(&monc->msgpool_mount_ack, front); case CEPH_MSG_MON_SUBSCRIBE_ACK: - return ceph_msgpool_get(&monc->msgpool_subscribe_ack); + return ceph_msgpool_get(&monc->msgpool_subscribe_ack, front); case CEPH_MSG_STATFS_REPLY: - return ceph_msgpool_get(&monc->msgpool_statfs_reply); + return ceph_msgpool_get(&monc->msgpool_statfs_reply, front); } return ceph_alloc_msg(con, hdr); } diff --git a/fs/ceph/msgpool.c b/fs/ceph/msgpool.c index 39d4d7e..7599b33 100644 --- a/fs/ceph/msgpool.c +++ b/fs/ceph/msgpool.c @@ -101,14 +101,28 @@ int ceph_msgpool_resv(struct ceph_msgpool *pool, int delta) return ret; } -struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool) +struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool, int front_len) { wait_queue_t wait; struct ceph_msg *msg; + if (front_len && front_len > pool->front_len) { + pr_err("msgpool_get pool %p need front %d, pool size is %d\n", + pool, front_len, pool->front_len); + WARN_ON(1); + + /* try to alloc a fresh message */ + msg = ceph_msg_new(0, front_len, 0, 0, NULL); + if (!IS_ERR(msg)) + return msg; + } + + if (!front_len) + front_len = pool->front_len; + if (pool->blocking) { /* mempool_t behavior; first try to alloc */ - msg = ceph_msg_new(0, pool->front_len, 0, 0, NULL); + msg = ceph_msg_new(0, front_len, 0, 0, NULL); if (!IS_ERR(msg)) return msg; } @@ -133,7 +147,7 @@ struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool) WARN_ON(1); /* maybe we can allocate it now? */ - msg = ceph_msg_new(0, pool->front_len, 0, 0, NULL); + msg = ceph_msg_new(0, front_len, 0, 0, NULL); if (!IS_ERR(msg)) return msg; diff --git a/fs/ceph/msgpool.h b/fs/ceph/msgpool.h index 07a2dec..bc834bf 100644 --- a/fs/ceph/msgpool.h +++ b/fs/ceph/msgpool.h @@ -20,7 +20,8 @@ extern int ceph_msgpool_init(struct ceph_msgpool *pool, int front_len, int size, bool blocking); extern void ceph_msgpool_destroy(struct ceph_msgpool *pool); extern int ceph_msgpool_resv(struct ceph_msgpool *, int delta); -extern struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *); +extern struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *, + int front_len); extern void ceph_msgpool_put(struct ceph_msgpool *, struct ceph_msg *); #endif diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index bbd9a5d..0a25405 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -161,7 +161,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, if (snapc) msg_size += sizeof(u64) * snapc->num_snaps; if (use_mempool) - msg = ceph_msgpool_get(&osdc->msgpool_op); + msg = ceph_msgpool_get(&osdc->msgpool_op, 0); else msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, 0, 0, NULL); if (IS_ERR(msg)) { @@ -1271,10 +1271,11 @@ static struct ceph_msg *alloc_msg(struct ceph_connection *con, struct ceph_osd *osd = con->private; struct ceph_osd_client *osdc = osd->o_osdc; int type = le16_to_cpu(hdr->type); + int front = le32_to_cpu(hdr->front_len); switch (type) { case CEPH_MSG_OSD_OPREPLY: - return ceph_msgpool_get(&osdc->msgpool_op_reply); + return ceph_msgpool_get(&osdc->msgpool_op_reply, front); } return ceph_alloc_msg(con, hdr); } -- cgit v0.10.2 From 76e3b390d41db9d69e254a09dd1aedd3e6aac25f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 15 Oct 2009 18:13:53 -0700 Subject: ceph: move dirty caps code around Cleanup only. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 40b8d34..7d16618 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1270,6 +1270,41 @@ static void ceph_flush_snaps(struct ceph_inode_info *ci) } /* + * Mark caps dirty. If inode is newly dirty, add to the global dirty + * list. + */ +void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) +{ + struct ceph_mds_client *mdsc = &ceph_client(ci->vfs_inode.i_sb)->mdsc; + struct inode *inode = &ci->vfs_inode; + int was = ci->i_dirty_caps; + int dirty = 0; + + dout("__mark_dirty_caps %p %s dirty %s -> %s\n", &ci->vfs_inode, + ceph_cap_string(mask), ceph_cap_string(was), + ceph_cap_string(was | mask)); + ci->i_dirty_caps |= mask; + if (was == 0) { + dout(" inode %p now dirty\n", &ci->vfs_inode); + BUG_ON(!list_empty(&ci->i_dirty_item)); + spin_lock(&mdsc->cap_dirty_lock); + list_add(&ci->i_dirty_item, &mdsc->cap_dirty); + spin_unlock(&mdsc->cap_dirty_lock); + if (ci->i_flushing_caps == 0) { + igrab(inode); + dirty |= I_DIRTY_SYNC; + } + } + BUG_ON(list_empty(&ci->i_dirty_item)); + if (((was | ci->i_flushing_caps) & CEPH_CAP_FILE_BUFFER) && + (mask & CEPH_CAP_FILE_BUFFER)) + dirty |= I_DIRTY_DATASYNC; + if (dirty) + __mark_inode_dirty(inode, dirty); + __cap_delay_requeue(mdsc, ci); +} + +/* * Add dirty inode to the flushing list. Assigned a seq number so we * can wait for caps to flush without starving. * @@ -1558,39 +1593,6 @@ ack: } /* - * Mark caps dirty. If inode is newly dirty, add to the global dirty - * list. - */ -void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) -{ - struct ceph_mds_client *mdsc = &ceph_client(ci->vfs_inode.i_sb)->mdsc; - struct inode *inode = &ci->vfs_inode; - int was_dirty = ci->i_dirty_caps; - int dirty = 0; - - dout("__mark_dirty_caps %p %s dirty %s -> %s\n", &ci->vfs_inode, - ceph_cap_string(mask), ceph_cap_string(ci->i_dirty_caps), - ceph_cap_string(ci->i_dirty_caps | mask)); - ci->i_dirty_caps |= mask; - if (!was_dirty) { - dout(" inode %p now dirty\n", &ci->vfs_inode); - spin_lock(&mdsc->cap_dirty_lock); - list_add(&ci->i_dirty_item, &mdsc->cap_dirty); - spin_unlock(&mdsc->cap_dirty_lock); - if (ci->i_flushing_caps == 0) { - igrab(inode); - dirty |= I_DIRTY_SYNC; - } - } - if (((was_dirty | ci->i_flushing_caps) & CEPH_CAP_FILE_BUFFER) && - (mask & CEPH_CAP_FILE_BUFFER)) - dirty |= I_DIRTY_DATASYNC; - if (dirty) - __mark_inode_dirty(inode, dirty); - __cap_delay_requeue(mdsc, ci); -} - -/* * Try to flush dirty caps back to the auth mds. */ static int try_flush_caps(struct inode *inode, struct ceph_mds_session *session, @@ -2370,6 +2372,8 @@ static void handle_cap_flush_ack(struct inode *inode, dout(" inode %p now clean\n", inode); BUG_ON(!list_empty(&ci->i_dirty_item)); drop = 1; + } else { + BUG_ON(list_empty(&ci->i_dirty_item)); } } spin_unlock(&mdsc->cap_dirty_lock); -- cgit v0.10.2 From 8fa9765576875200a7412a5300b5f0537211f038 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 16 Oct 2009 14:44:35 -0700 Subject: ceph: enable readahead Initialized bdi->ra_pages to enable readahead. Use 512KB default. Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 7f7d475..ab950fc 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -327,6 +327,7 @@ static int parse_mount_args(struct ceph_client *client, args->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; /* seconds */ args->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT; args->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT; + args->rsize = CEPH_MOUNT_RSIZE_DEFAULT; args->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL); args->cap_release_safety = CEPH_CAPS_PER_RELEASE * 4; args->max_readdir = 1024; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 0bbf58a..75556e9 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -69,7 +69,7 @@ struct ceph_mount_args { * defaults */ #define CEPH_MOUNT_TIMEOUT_DEFAULT 60 -#define CEPH_MOUNT_RSIZE_DEFAULT (128*1024) /* readahead */ +#define CEPH_MOUNT_RSIZE_DEFAULT (512*1024) /* readahead */ #define CEPH_MSG_MAX_FRONT_LEN (16*1024*1024) #define CEPH_MSG_MAX_DATA_LEN (16*1024*1024) -- cgit v0.10.2 From ee7fdfaff7702bd209e3a013b2fc4643233f5465 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 19 Oct 2009 11:41:51 -0700 Subject: ceph: include preferred osd in placement seed Mix the preferred osd (if any) into the placement seed that is fed into the CRUSH object placement calculation. This prevents all the placement pgs from peering with the same osds. Rev the osd client protocol with this change. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 9b16e2e..f8f27e2 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -38,7 +38,7 @@ #define CEPH_OSD_PROTOCOL 7 /* cluster internal */ #define CEPH_MDS_PROTOCOL 9 /* cluster internal */ #define CEPH_MON_PROTOCOL 5 /* cluster internal */ -#define CEPH_OSDC_PROTOCOL 20 /* server/client */ +#define CEPH_OSDC_PROTOCOL 21 /* server/client */ #define CEPH_MDSC_PROTOCOL 29 /* server/client */ #define CEPH_MONC_PROTOCOL 15 /* server/client */ diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 6f0aeff..72d75a2 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -791,6 +791,8 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol, pgid.pg64 = 0; /* start with it zeroed out */ pgid.pg.ps = ceph_full_name_hash(oid, strlen(oid)); pgid.pg.preferred = preferred; + if (preferred >= 0) + pgid.pg.ps += preferred; pgid.pg.pool = le32_to_cpu(fl->fl_pg_pool); if (preferred >= 0) dout("calc_object_layout '%s' pgid %d.%xp%d (%llx)\n", oid, -- cgit v0.10.2 From bb097ffaf833a40335b6dd5e4fa6f5ed0b223bdc Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 19 Oct 2009 16:17:31 -0700 Subject: ceph: v0.17 of client Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index f8f27e2..ae52382 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -19,8 +19,8 @@ * Ceph release version */ #define CEPH_VERSION_MAJOR 0 -#define CEPH_VERSION_MINOR 16 -#define CEPH_VERSION_PATCH 1 +#define CEPH_VERSION_MINOR 17 +#define CEPH_VERSION_PATCH 0 #define _CEPH_STRINGIFY(x) #x #define CEPH_STRINGIFY(x) _CEPH_STRINGIFY(x) -- cgit v0.10.2 From 232d4b01319767b3ffa5d08962a81c805962be49 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 21 Oct 2009 11:21:49 -0700 Subject: ceph: move directory size logic to ceph_getattr We can't fill i_size with rbytes at the fill_file_size stage without adding additional checks for directories. Notably, we want st_blocks to remain 0 on directories so that 'du' still works. Fill in i_blocks, i_size specially in ceph_getattr instead. Signed-off-by: Sage Weil diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 6097af7..036873c 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -568,8 +568,6 @@ static int fill_inode(struct inode *inode, queue_trunc = ceph_fill_file_size(inode, issued, le32_to_cpu(info->truncate_seq), le64_to_cpu(info->truncate_size), - S_ISDIR(inode->i_mode) ? - ci->i_rbytes : le64_to_cpu(info->size)); ceph_fill_file_time(inode, issued, le32_to_cpu(info->time_warp_seq), @@ -1603,6 +1601,7 @@ int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct inode *inode = dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); int err; err = ceph_do_getattr(inode, CEPH_STAT_CAP_INODE_ALL); @@ -1613,8 +1612,11 @@ int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry, stat->dev = ceph_snap(inode); else stat->dev = 0; - if (S_ISDIR(inode->i_mode)) + if (S_ISDIR(inode->i_mode)) { + stat->size = ci->i_rbytes; + stat->blocks = 0; stat->blksize = 65536; + } } return err; } -- cgit v0.10.2 From ecb19c4649d7396737eb0d91a475661fe9d7c028 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 22 Oct 2009 10:53:02 -0700 Subject: ceph: remove small mon addr limit; use CEPH_MAX_MON where appropriate Get rid of separate max mon limit; use the system limit instead. This allows mounts when there are lots of mon addrs provided by mount.ceph (as with a host with lots of A/AAAA records). Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index ab950fc..8191625 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -314,7 +314,7 @@ static int parse_mount_args(struct ceph_client *client, int err; substring_t argstr[MAX_OPT_ARGS]; int num_mon; - struct ceph_entity_addr mon_addr[CEPH_MAX_MON_MOUNT_ADDR]; + struct ceph_entity_addr mon_addr[CEPH_MAX_MON]; int i; dout("parse_mount_args dev_name '%s'\n", dev_name); @@ -344,7 +344,7 @@ static int parse_mount_args(struct ceph_client *client, /* get mon ip(s) */ err = ceph_parse_ips(dev_name, *path, mon_addr, - CEPH_MAX_MON_MOUNT_ADDR, &num_mon); + CEPH_MAX_MON, &num_mon); if (err < 0) return err; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 75556e9..3af42d9 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -47,8 +47,6 @@ (!!((client)->mount_args.flags & CEPH_OPT_##opt)) -#define CEPH_MAX_MON_MOUNT_ADDR 5 - struct ceph_mount_args { int sb_flags; int flags; -- cgit v0.10.2 From 7b813c46021e8f4909772a5bbfb5212bd140764c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 26 Oct 2009 22:07:53 -0700 Subject: ceph: reduce parse_mount_args stack usage Since we've increased the max mon count, we shouldn't put the addr array on the parse_mount_args stack. Put it on the heap instead. Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 8191625..deb51bd 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -314,12 +314,16 @@ static int parse_mount_args(struct ceph_client *client, int err; substring_t argstr[MAX_OPT_ARGS]; int num_mon; - struct ceph_entity_addr mon_addr[CEPH_MAX_MON]; + struct ceph_entity_addr *mon_addr; int i; dout("parse_mount_args dev_name '%s'\n", dev_name); memset(args, 0, sizeof(*args)); + mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*mon_addr), GFP_KERNEL); + if (!mon_addr) + return -ENOMEM; + /* start with defaults */ args->sb_flags = flags; args->flags = CEPH_OPT_DEFAULT; @@ -333,27 +337,29 @@ static int parse_mount_args(struct ceph_client *client, args->max_readdir = 1024; /* ip1[:port1][,ip2[:port2]...]:/subdir/in/fs */ + err = -EINVAL; if (!dev_name) - return -EINVAL; + goto out; *path = strstr(dev_name, ":/"); if (*path == NULL) { pr_err("device name is missing path (no :/ in %s)\n", dev_name); - return -EINVAL; + goto out; } /* get mon ip(s) */ err = ceph_parse_ips(dev_name, *path, mon_addr, CEPH_MAX_MON, &num_mon); if (err < 0) - return err; + goto out; /* build initial monmap */ + err = -ENOMEM; client->monc.monmap = kzalloc(sizeof(*client->monc.monmap) + num_mon*sizeof(client->monc.monmap->mon_inst[0]), GFP_KERNEL); if (!client->monc.monmap) - return -ENOMEM; + goto out; for (i = 0; i < num_mon; i++) { client->monc.monmap->mon_inst[i].addr = mon_addr[i]; client->monc.monmap->mon_inst[i].addr.erank = 0; @@ -374,11 +380,11 @@ static int parse_mount_args(struct ceph_client *client, int token, intval, ret; if (!*c) continue; + err = -EINVAL; token = match_token((char *)c, arg_tokens, argstr); if (token < 0) { pr_err("bad mount option at '%s'\n", c); - return -EINVAL; - + goto out; } if (token < Opt_ip) { ret = match_int(&argstr[0], &intval); @@ -468,8 +474,11 @@ static int parse_mount_args(struct ceph_client *client, BUG_ON(token); } } + err = 0; - return 0; +out: + kfree(mon_addr); + return err; } static void release_mount_args(struct ceph_mount_args *args) -- cgit v0.10.2 From 6ca874e92d5e50beb8e351dfd8121947bafc79ec Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 26 Oct 2009 22:06:22 -0700 Subject: ceph: silence uninitialized variable warning Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index deb51bd..924e6ca 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -808,7 +808,7 @@ static int ceph_get_sb(struct file_system_type *fs_type, struct ceph_client *client; int err; int (*compare_super)(struct super_block *, void *) = ceph_compare_super; - const char *path; + const char *path = 0; dout("ceph_get_sb\n"); -- cgit v0.10.2 From e53c2fe075feda1fd4f009956ac026dc24c3a199 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 27 Oct 2009 10:19:28 -0700 Subject: ceph: fix, clean up string mount arg parsing Clearly demark int and string argument options, and do not try to convert string arguments to ints. Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 924e6ca..b094f50 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -264,9 +264,11 @@ enum { Opt_caps_wanted_delay_min, Opt_caps_wanted_delay_max, Opt_readdir_max_entries, + Opt_last_int, /* int args above */ Opt_snapdirname, Opt_secret, + Opt_last_string, /* string args above */ Opt_ip, Opt_noshare, @@ -386,14 +388,19 @@ static int parse_mount_args(struct ceph_client *client, pr_err("bad mount option at '%s'\n", c); goto out; } - if (token < Opt_ip) { + if (token < Opt_last_int) { ret = match_int(&argstr[0], &intval); if (ret < 0) { pr_err("bad mount option arg (not int) " "at '%s'\n", c); continue; } - dout("got token %d intval %d\n", token, intval); + dout("got int token %d val %d\n", token, intval); + } else if (token > Opt_last_int && token < Opt_last_string) { + dout("got string token %d val %s\n", token, + argstr[0].from); + } else { + dout("got token %d\n", token); } switch (token) { case Opt_fsidmajor: -- cgit v0.10.2 From 6b8051855d983db8480ff1ea1b02ef2b49203c22 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 27 Oct 2009 11:50:50 -0700 Subject: ceph: allocate and parse mount args before client instance This simplifies much of the error handling during mount. It also means that we have the mount args before client creation, and we can initialize based on those options. Signed-off-by: Sage Weil diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index c7d673f..bf53581 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -600,8 +600,8 @@ static int ceph_writepages_start(struct address_space *mapping, pr_warning("writepage_start %p on forced umount\n", inode); return -EIO; /* we're in a forced umount, don't write! */ } - if (client->mount_args.wsize && client->mount_args.wsize < wsize) - wsize = client->mount_args.wsize; + if (client->mount_args->wsize && client->mount_args->wsize < wsize) + wsize = client->mount_args->wsize; if (wsize < PAGE_CACHE_SIZE) wsize = PAGE_CACHE_SIZE; max_pages_ever = wsize >> PAGE_CACHE_SHIFT; diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 7d16618..8b863db 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -270,7 +270,7 @@ static void put_cap(struct ceph_cap *cap, * lots of free/alloc churn. */ if (caps_avail_count >= caps_reserve_count + - ceph_client(cap->ci->vfs_inode.i_sb)->mount_args.max_readdir) { + ceph_client(cap->ci->vfs_inode.i_sb)->mount_args->max_readdir) { caps_total_count--; kmem_cache_free(ceph_cap_cachep, cap); } else { @@ -388,7 +388,7 @@ static void __insert_cap_node(struct ceph_inode_info *ci, static void __cap_set_timeouts(struct ceph_mds_client *mdsc, struct ceph_inode_info *ci) { - struct ceph_mount_args *ma = &mdsc->client->mount_args; + struct ceph_mount_args *ma = mdsc->client->mount_args; ci->i_hold_caps_min = round_jiffies(jiffies + ma->caps_wanted_delay_min * HZ); diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 7bb8db5..4f74679 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -225,7 +225,7 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir) int err; u32 ftype; struct ceph_mds_reply_info_parsed *rinfo; - const int max_entries = client->mount_args.max_readdir; + const int max_entries = client->mount_args->max_readdir; dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off); if (fi->at_end) @@ -479,7 +479,8 @@ struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, /* .snap dir? */ if (err == -ENOENT && ceph_vino(parent).ino != CEPH_INO_ROOT && /* no .snap in root dir */ - strcmp(dentry->d_name.name, client->mount_args.snapdir_name) == 0) { + strcmp(dentry->d_name.name, + client->mount_args->snapdir_name) == 0) { struct inode *inode = ceph_get_snapdir(parent); dout("ENOENT on snapdir %p '%.*s', linking to snapdir %p\n", dentry, dentry->d_name.len, dentry->d_name.name, inode); @@ -550,7 +551,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, spin_lock(&dir->i_lock); dout(" dir %p flags are %d\n", dir, ci->i_ceph_flags); if (strncmp(dentry->d_name.name, - client->mount_args.snapdir_name, + client->mount_args->snapdir_name, dentry->d_name.len) && (ci->i_ceph_flags & CEPH_I_COMPLETE) && (__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) { diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 12d66c0..210cb66 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -943,7 +943,7 @@ static int add_cap_releases(struct ceph_mds_client *mdsc, int err = -ENOMEM; if (extra < 0) - extra = mdsc->client->mount_args.cap_release_safety; + extra = mdsc->client->mount_args->cap_release_safety; spin_lock(&session->s_cap_lock); @@ -2601,7 +2601,7 @@ static void wait_requests(struct ceph_mds_client *mdsc) mutex_unlock(&mdsc->mutex); dout("wait_requests waiting for requests\n"); wait_for_completion_timeout(&mdsc->safe_umount_waiters, - client->mount_args.mount_timeout * HZ); + client->mount_args->mount_timeout * HZ); mutex_lock(&mdsc->mutex); /* tear down remaining requests */ @@ -2693,7 +2693,7 @@ void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc) int i; int n; struct ceph_client *client = mdsc->client; - unsigned long started, timeout = client->mount_args.mount_timeout * HZ; + unsigned long started, timeout = client->mount_args->mount_timeout * HZ; dout("close_sessions\n"); diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index e6e954c..61263c9 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -527,6 +527,40 @@ static void delayed_work(struct work_struct *work) mutex_unlock(&monc->mutex); } +/* + * On startup, we build a temporary monmap populated with the IPs + * provided by mount(2). + */ +static int build_initial_monmap(struct ceph_mon_client *monc) +{ + struct ceph_mount_args *args = monc->client->mount_args; + struct ceph_entity_addr *mon_addr = args->mon_addr; + int num_mon = args->num_mon; + int i; + + /* build initial monmap */ + monc->monmap = kzalloc(sizeof(*monc->monmap) + + num_mon*sizeof(monc->monmap->mon_inst[0]), + GFP_KERNEL); + if (!monc->monmap) + return -ENOMEM; + for (i = 0; i < num_mon; i++) { + monc->monmap->mon_inst[i].addr = mon_addr[i]; + monc->monmap->mon_inst[i].addr.erank = 0; + monc->monmap->mon_inst[i].addr.nonce = 0; + monc->monmap->mon_inst[i].name.type = + CEPH_ENTITY_TYPE_MON; + monc->monmap->mon_inst[i].name.num = cpu_to_le64(i); + } + monc->monmap->num_mon = num_mon; + + /* release addr memory */ + kfree(args->mon_addr); + args->mon_addr = NULL; + args->num_mon = 0; + return 0; +} + int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) { int err = 0; @@ -537,6 +571,10 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) monc->monmap = NULL; mutex_init(&monc->mutex); + err = build_initial_monmap(monc); + if (err) + goto out; + monc->con = NULL; /* msg pools */ diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 0a25405..7dc0f62 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -444,7 +444,7 @@ static void register_request(struct ceph_osd_client *osdc, osdc->num_requests++; req->r_timeout_stamp = - jiffies + osdc->client->mount_args.osd_timeout*HZ; + jiffies + osdc->client->mount_args->osd_timeout*HZ; if (osdc->num_requests == 1) { osdc->timeout_tid = req->r_tid; @@ -609,7 +609,7 @@ static int __send_request(struct ceph_osd_client *osdc, reqhead->flags |= cpu_to_le32(req->r_flags); /* e.g., RETRY */ reqhead->reassert_version = req->r_reassert_version; - req->r_timeout_stamp = jiffies+osdc->client->mount_args.osd_timeout*HZ; + req->r_timeout_stamp = jiffies+osdc->client->mount_args->osd_timeout*HZ; ceph_msg_get(req->r_request); /* send consumes a ref */ ceph_con_send(&req->r_osd->o_con, req->r_request); @@ -632,7 +632,7 @@ static void handle_timeout(struct work_struct *work) container_of(work, struct ceph_osd_client, timeout_work.work); struct ceph_osd_request *req; struct ceph_osd *osd; - unsigned long timeout = osdc->client->mount_args.osd_timeout * HZ; + unsigned long timeout = osdc->client->mount_args->osd_timeout * HZ; unsigned long next_timeout = timeout + jiffies; struct rb_node *p; diff --git a/fs/ceph/super.c b/fs/ceph/super.c index b094f50..9b7815d 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -110,7 +110,7 @@ static int ceph_syncfs(struct super_block *sb, int wait) static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt) { struct ceph_client *client = ceph_sb_to_client(mnt->mnt_sb); - struct ceph_mount_args *args = &client->mount_args; + struct ceph_mount_args *args = client->mount_args; if (args->flags & CEPH_OPT_FSID) seq_printf(m, ",fsidmajor=%llu,fsidminor%llu", @@ -307,24 +307,24 @@ static match_table_t arg_tokens = { }; -static int parse_mount_args(struct ceph_client *client, - int flags, char *options, const char *dev_name, - const char **path) +static struct ceph_mount_args *parse_mount_args(int flags, char *options, + const char *dev_name, + const char **path) { - struct ceph_mount_args *args = &client->mount_args; + struct ceph_mount_args *args; const char *c; - int err; + int err = -ENOMEM; substring_t argstr[MAX_OPT_ARGS]; - int num_mon; - struct ceph_entity_addr *mon_addr; - int i; - dout("parse_mount_args dev_name '%s'\n", dev_name); - memset(args, 0, sizeof(*args)); + args = kzalloc(sizeof(*args), GFP_KERNEL); + if (!args) + return ERR_PTR(-ENOMEM); + args->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*args->mon_addr), + GFP_KERNEL); + if (!args->mon_addr) + goto out; - mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*mon_addr), GFP_KERNEL); - if (!mon_addr) - return -ENOMEM; + dout("parse_mount_args %p, dev_name '%s'\n", args, dev_name); /* start with defaults */ args->sb_flags = flags; @@ -350,29 +350,11 @@ static int parse_mount_args(struct ceph_client *client, } /* get mon ip(s) */ - err = ceph_parse_ips(dev_name, *path, mon_addr, - CEPH_MAX_MON, &num_mon); + err = ceph_parse_ips(dev_name, *path, args->mon_addr, + CEPH_MAX_MON, &args->num_mon); if (err < 0) goto out; - /* build initial monmap */ - err = -ENOMEM; - client->monc.monmap = kzalloc(sizeof(*client->monc.monmap) + - num_mon*sizeof(client->monc.monmap->mon_inst[0]), - GFP_KERNEL); - if (!client->monc.monmap) - goto out; - for (i = 0; i < num_mon; i++) { - client->monc.monmap->mon_inst[i].addr = mon_addr[i]; - client->monc.monmap->mon_inst[i].addr.erank = 0; - client->monc.monmap->mon_inst[i].addr.nonce = 0; - client->monc.monmap->mon_inst[i].name.type = - CEPH_ENTITY_TYPE_MON; - client->monc.monmap->mon_inst[i].name.num = cpu_to_le64(i); - } - client->monc.monmap->num_mon = num_mon; - memset(&args->my_addr.in_addr, 0, sizeof(args->my_addr.in_addr)); - /* path on server */ *path += 2; dout("server path '%s'\n", *path); @@ -415,7 +397,7 @@ static int parse_mount_args(struct ceph_client *client, &args->my_addr, 1, NULL); if (err < 0) - return err; + goto out; args->flags |= CEPH_OPT_MYIP; break; @@ -481,25 +463,28 @@ static int parse_mount_args(struct ceph_client *client, BUG_ON(token); } } - err = 0; + return args; out: - kfree(mon_addr); - return err; + kfree(args->mon_addr); + kfree(args); + return ERR_PTR(err); } -static void release_mount_args(struct ceph_mount_args *args) +static void destroy_mount_args(struct ceph_mount_args *args) { + dout("destroy_mount_args %p\n", args); kfree(args->snapdir_name); args->snapdir_name = NULL; kfree(args->secret); args->secret = NULL; + kfree(args); } /* * create a fresh client instance */ -static struct ceph_client *ceph_create_client(void) +static struct ceph_client *ceph_create_client(struct ceph_mount_args *args) { struct ceph_client *client; int err = -ENOMEM; @@ -515,6 +500,7 @@ static struct ceph_client *ceph_create_client(void) client->sb = NULL; client->mount_state = CEPH_MOUNT_MOUNTING; client->whoami = -1; + client->mount_args = args; client->msgr = NULL; @@ -577,7 +563,7 @@ static void ceph_destroy_client(struct ceph_client *client) if (client->wb_pagevec_pool) mempool_destroy(client->wb_pagevec_pool); - release_mount_args(&client->mount_args); + destroy_mount_args(client->mount_args); kfree(client); dout("destroy_client %p done\n", client); @@ -613,7 +599,7 @@ static struct dentry *open_root_dentry(struct ceph_client *client, req->r_ino1.ino = CEPH_INO_ROOT; req->r_ino1.snap = CEPH_NOSNAP; req->r_started = started; - req->r_timeout = client->mount_args.mount_timeout * HZ; + req->r_timeout = client->mount_args->mount_timeout * HZ; req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE); req->r_num_caps = 2; err = ceph_mdsc_do_request(mdsc, NULL, req); @@ -641,7 +627,7 @@ static int ceph_mount(struct ceph_client *client, struct vfsmount *mnt, { struct ceph_entity_addr *myaddr = NULL; int err; - unsigned long timeout = client->mount_args.mount_timeout * HZ; + unsigned long timeout = client->mount_args->mount_timeout * HZ; unsigned long started = jiffies; /* note the start time */ struct dentry *root; @@ -651,7 +637,7 @@ static int ceph_mount(struct ceph_client *client, struct vfsmount *mnt, /* initialize the messenger */ if (client->msgr == NULL) { if (ceph_test_opt(client, MYIP)) - myaddr = &client->mount_args.my_addr; + myaddr = &client->mount_args->my_addr; client->msgr = ceph_messenger_create(myaddr); if (IS_ERR(client->msgr)) { err = PTR_ERR(client->msgr); @@ -727,7 +713,7 @@ static int ceph_set_super(struct super_block *s, void *data) dout("set_super %p data %p\n", s, data); - s->s_flags = client->mount_args.sb_flags; + s->s_flags = client->mount_args->sb_flags; s->s_maxbytes = 1ULL << 40; /* temp value until we get mdsmap */ s->s_fs_info = client; @@ -756,7 +742,7 @@ fail: static int ceph_compare_super(struct super_block *sb, void *data) { struct ceph_client *new = data; - struct ceph_mount_args *args = &new->mount_args; + struct ceph_mount_args *args = new->mount_args; struct ceph_client *other = ceph_sb_to_client(sb); int i; @@ -778,7 +764,7 @@ static int ceph_compare_super(struct super_block *sb, void *data) } dout("mon ip matches existing sb %p\n", sb); } - if (args->sb_flags != other->mount_args.sb_flags) { + if (args->sb_flags != other->mount_args->sb_flags) { dout("flags differ\n"); return 0; } @@ -798,9 +784,9 @@ static int ceph_init_bdi(struct super_block *sb, struct ceph_client *client) sb->s_bdi = &client->backing_dev_info; /* set ra_pages based on rsize mount option? */ - if (client->mount_args.rsize >= PAGE_CACHE_SIZE) + if (client->mount_args->rsize >= PAGE_CACHE_SIZE) client->backing_dev_info.ra_pages = - (client->mount_args.rsize + PAGE_CACHE_SIZE - 1) + (client->mount_args->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_SHIFT; err = bdi_register_dev(&client->backing_dev_info, sb->s_dev); @@ -816,19 +802,23 @@ static int ceph_get_sb(struct file_system_type *fs_type, int err; int (*compare_super)(struct super_block *, void *) = ceph_compare_super; const char *path = 0; + struct ceph_mount_args *args; dout("ceph_get_sb\n"); + args = parse_mount_args(flags, data, dev_name, &path); + if (IS_ERR(args)) { + err = PTR_ERR(args); + goto out_final; + } /* create client (which we may/may not use) */ - client = ceph_create_client(); - if (IS_ERR(client)) - return PTR_ERR(client); - - err = parse_mount_args(client, flags, data, dev_name, &path); - if (err < 0) - goto out; + client = ceph_create_client(args); + if (IS_ERR(client)) { + err = PTR_ERR(client); + goto out_final; + } - if (client->mount_args.flags & CEPH_OPT_NOSHARE) + if (client->mount_args->flags & CEPH_OPT_NOSHARE) compare_super = NULL; sb = sget(fs_type, compare_super, ceph_set_super, client); if (IS_ERR(sb)) { @@ -846,7 +836,7 @@ static int ceph_get_sb(struct file_system_type *fs_type, /* set up mempools */ err = -ENOMEM; client->wb_pagevec_pool = mempool_create_kmalloc_pool(10, - client->mount_args.wsize >> PAGE_CACHE_SHIFT); + client->mount_args->wsize >> PAGE_CACHE_SHIFT); if (!client->wb_pagevec_pool) goto out_splat; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 3af42d9..a3d4943 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -42,13 +42,15 @@ #define CEPH_OPT_DEFAULT (CEPH_OPT_RBYTES) #define ceph_set_opt(client, opt) \ - (client)->mount_args.flags |= CEPH_OPT_##opt; + (client)->mount_args->flags |= CEPH_OPT_##opt; #define ceph_test_opt(client, opt) \ - (!!((client)->mount_args.flags & CEPH_OPT_##opt)) + (!!((client)->mount_args->flags & CEPH_OPT_##opt)) struct ceph_mount_args { int sb_flags; + int num_mon; + struct ceph_entity_addr *mon_addr; int flags; int mount_timeout; int caps_wanted_delay_min, caps_wanted_delay_max; @@ -115,7 +117,7 @@ struct ceph_client { struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps; struct mutex mount_mutex; /* serialize mount attempts */ - struct ceph_mount_args mount_args; + struct ceph_mount_args *mount_args; struct ceph_fsid fsid; struct super_block *sb; -- cgit v0.10.2 From fbbccec9c6218cbc9ff47c6d88bfc6b52079e3ea Mon Sep 17 00:00:00 2001 From: Noah Watkins Date: Wed, 28 Oct 2009 11:54:49 -0700 Subject: ceph: replace list_entry with container_of Usage of non-list.h list_entry function for container_of functionality replaced with direct use of container_of. Signed-off-by: Noah Watkins Signed-off-by: Sage Weil diff --git a/fs/ceph/super.h b/fs/ceph/super.h index a3d4943..05947b9 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -364,7 +364,7 @@ struct ceph_inode_info { static inline struct ceph_inode_info *ceph_inode(struct inode *inode) { - return list_entry(inode, struct ceph_inode_info, vfs_inode); + return container_of(inode, struct ceph_inode_info, vfs_inode); } static inline void ceph_i_clear(struct inode *inode, unsigned mask) -- cgit v0.10.2 From 35e054a66e07f508aa7cfabc7db1757379093689 Mon Sep 17 00:00:00 2001 From: Noah Watkins Date: Wed, 28 Oct 2009 14:04:48 -0700 Subject: ceph: remove redundant use of le32_to_cpu Using stripe unit size calculated and saved on the stack to avoid a redundant call to le32_to_cpu. Signed-off-by: Noah Watkins Signed-off-by: Sage Weil diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 72d75a2..60012e0 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -735,7 +735,7 @@ void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, dout("mapping %llu~%llu osize %u fl_su %u\n", off, *plen, osize, su); - su_per_object = osize / le32_to_cpu(layout->fl_stripe_unit); + su_per_object = osize / su; dout("osize %u / su %u = su_per_object %u\n", osize, su, su_per_object); -- cgit v0.10.2 From 5600f5ebd318f7af6f4b19a29f08d18bb85264e5 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 28 Oct 2009 14:57:25 -0700 Subject: ceph: correct comment to match striping calculation The object extent offset is the file offset _modulo_ the stripe unit. The code was correct, the comment was wrong. Reported-by: Noah Watkins Signed-off-by: Sage Weil diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 60012e0..a9a4143 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -752,7 +752,7 @@ void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, *bno = objsetno * sc + stripepos; dout("objset %u * sc %u = bno %u\n", objsetno, sc, (unsigned)*bno); - /* *oxoff = *off / layout->fl_stripe_unit; */ + /* *oxoff = *off % layout->fl_stripe_unit; */ t = off; *oxoff = do_div(t, su); *oxlen = min_t(u64, *plen, su - *oxoff); -- cgit v0.10.2 From 645a102581b3639836b17d147c35d574fd6e8267 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 28 Oct 2009 15:15:05 -0700 Subject: ceph: fix object striping calculation for non-default striping schemes We were incorrectly calculationing of object offset. If we have multiple stripe units per object, we need to shift to the start of the current su in addition to the offset within the su. Also rename bno to ono (object number) to avoid some variable naming confusion. Signed-off-by: Sage Weil diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index a9a4143..5a5520c 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -723,7 +723,7 @@ bad: */ void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, u64 off, u64 *plen, - u64 *bno, + u64 *ono, u64 *oxoff, u64 *oxlen) { u32 osize = le32_to_cpu(layout->fl_object_size); @@ -750,11 +750,14 @@ void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, stripepos = bl % sc; objsetno = stripeno / su_per_object; - *bno = objsetno * sc + stripepos; - dout("objset %u * sc %u = bno %u\n", objsetno, sc, (unsigned)*bno); - /* *oxoff = *off % layout->fl_stripe_unit; */ + *ono = objsetno * sc + stripepos; + dout("objset %u * sc %u = ono %u\n", objsetno, sc, (unsigned)*ono); + + /* *oxoff = *off % layout->fl_stripe_unit; # offset in su */ t = off; *oxoff = do_div(t, su); + *oxoff += (stripeno % su_per_object) * su; + *oxlen = min_t(u64, *plen, su - *oxoff); *plen = *oxlen; -- cgit v0.10.2 From ff1d1f7179363209b7f1493ea39b666f50d05cf4 Mon Sep 17 00:00:00 2001 From: Noah Watkins Date: Fri, 30 Oct 2009 12:57:30 -0700 Subject: ceph: fix intra strip unit length calculation Commit 645a102581b3639836b17d147c35d574fd6e8267 fixes calculation of object offset for layouts with multiple stripes per object. This updates the calculation of the length written to take into account multiple stripes per object. Signed-off-by: Noah Watkins Signed-off-by: Sage Weil diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 5a5520c..d62e111 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -731,7 +731,7 @@ void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, u32 sc = le32_to_cpu(layout->fl_stripe_count); u32 bl, stripeno, stripepos, objsetno; u32 su_per_object; - u64 t; + u64 t, su_offset; dout("mapping %llu~%llu osize %u fl_su %u\n", off, *plen, osize, su); @@ -755,10 +755,15 @@ void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, /* *oxoff = *off % layout->fl_stripe_unit; # offset in su */ t = off; - *oxoff = do_div(t, su); - *oxoff += (stripeno % su_per_object) * su; - - *oxlen = min_t(u64, *plen, su - *oxoff); + su_offset = do_div(t, su); + *oxoff = su_offset + (stripeno % su_per_object) * su; + + /* + * Calculate the length of the extent being written to the selected + * object. This is the minimum of the full length requested (plen) or + * the remainder of the current stripe being written to. + */ + *oxlen = min_t(u64, *plen, su - su_offset); *plen = *oxlen; dout(" obj extent %llu~%llu\n", *oxoff, *oxlen); -- cgit v0.10.2 From 63ff78b25c4b204075b5b98afcac6ad3639d43fe Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 1 Nov 2009 17:51:15 -0800 Subject: ceph: fix uninitialized err variable Fixes warning fs/ceph/xattr.c: In function '__build_xattrs': fs/ceph/xattr.c:353: warning: 'err' may be used uninitialized in this function Signed-off-by: Sage Weil diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 65b3a84..1a48a55 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -350,7 +350,7 @@ static int __build_xattrs(struct inode *inode) struct ceph_inode_info *ci = ceph_inode(inode); int xattr_version; struct ceph_inode_xattr **xattrs = NULL; - int err; + int err = 0; int i; dout("__build_xattrs() len=%d\n", -- cgit v0.10.2 From 33aa96e7430d215e2ee779f65cdad0f6d4571fe1 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 1 Nov 2009 17:53:24 -0800 Subject: crush: always return a value from crush_bucket_choose Even when we encounter a corrupt bucket. We still BUG(). This fixes the warning fs/ceph/crush/mapper.c: In function 'crush_choose': fs/ceph/crush/mapper.c:352: warning: control may reach end of non-void function 'crush_bucket_choose' being inlined Signed-off-by: Sage Weil diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c index c268393..54f3f40 100644 --- a/fs/ceph/crush/mapper.c +++ b/fs/ceph/crush/mapper.c @@ -253,7 +253,7 @@ static int crush_bucket_choose(struct crush_bucket *in, int x, int r) x, r); default: BUG_ON(1); -/* return in->items[0] */; + return in->items[0]; } } -- cgit v0.10.2 From 859e7b149362475672e2a996f29b8f45cbb34d82 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 2 Nov 2009 09:32:47 -0800 Subject: ceph: init/destroy bdi in client create/destroy helpers This keeps bdi setup/teardown in line with client life cycle. Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 9b7815d..0ae40ba 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -508,10 +508,14 @@ static struct ceph_client *ceph_create_client(struct ceph_mount_args *args) client->signed_ticket = NULL; client->signed_ticket_len = 0; + err = bdi_init(&client->backing_dev_info); + if (err < 0) + goto fail; + err = -ENOMEM; client->wb_wq = create_workqueue("ceph-writeback"); if (client->wb_wq == NULL) - goto fail; + goto fail_bdi; client->pg_inv_wq = create_singlethread_workqueue("ceph-pg-invalid"); if (client->pg_inv_wq == NULL) goto fail_wb_wq; @@ -537,6 +541,8 @@ fail_pg_inv_wq: destroy_workqueue(client->pg_inv_wq); fail_wb_wq: destroy_workqueue(client->wb_wq); +fail_bdi: + bdi_destroy(&client->backing_dev_info); fail: kfree(client); return ERR_PTR(err); @@ -774,13 +780,10 @@ static int ceph_compare_super(struct super_block *sb, void *data) /* * construct our own bdi so we can control readahead, etc. */ -static int ceph_init_bdi(struct super_block *sb, struct ceph_client *client) +static int ceph_register_bdi(struct super_block *sb, struct ceph_client *client) { int err; - err = bdi_init(&client->backing_dev_info); - if (err < 0) - return err; sb->s_bdi = &client->backing_dev_info; /* set ra_pages based on rsize mount option? */ @@ -840,7 +843,7 @@ static int ceph_get_sb(struct file_system_type *fs_type, if (!client->wb_pagevec_pool) goto out_splat; - err = ceph_init_bdi(sb, client); + err = ceph_register_bdi(sb, client); if (err < 0) goto out_splat; } -- cgit v0.10.2 From 63f2d211954b790fea0a9caeae605c7956535af6 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 3 Nov 2009 15:17:56 -0800 Subject: ceph: use fixed endian encoding for ceph_entity_addr We exchange struct ceph_entity_addr over the wire and store it on disk. The sockaddr_storage.ss_family field, however, is host endianness. So, fix ss_family endianness to big endian when sending/receiving over the wire. Signed-off-by: Sage Weil diff --git a/fs/ceph/decode.h b/fs/ceph/decode.h index 91179fb..a382aec 100644 --- a/fs/ceph/decode.h +++ b/fs/ceph/decode.h @@ -76,19 +76,31 @@ static inline void ceph_decode_copy(void **p, void *pv, size_t n) * struct ceph_timespec <-> struct timespec */ static inline void ceph_decode_timespec(struct timespec *ts, - struct ceph_timespec *tv) + const struct ceph_timespec *tv) { ts->tv_sec = le32_to_cpu(tv->tv_sec); ts->tv_nsec = le32_to_cpu(tv->tv_nsec); } static inline void ceph_encode_timespec(struct ceph_timespec *tv, - struct timespec *ts) + const struct timespec *ts) { tv->tv_sec = cpu_to_le32(ts->tv_sec); tv->tv_nsec = cpu_to_le32(ts->tv_nsec); } /* + * sockaddr_storage <-> ceph_sockaddr + */ +static inline void ceph_encode_addr(struct ceph_entity_addr *a) +{ + a->in_addr.ss_family = htons(a->in_addr.ss_family); +} +static inline void ceph_decode_addr(struct ceph_entity_addr *a) +{ + a->in_addr.ss_family = ntohs(a->in_addr.ss_family); +} + +/* * encoders */ static inline void ceph_encode_64(void **p, u64 v) diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 80daea0..4226c81 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -86,6 +86,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) ceph_decode_need(p, end, sizeof(addr) + 1 + sizeof(u32), bad); ceph_decode_copy(p, &addr, sizeof(addr)); + ceph_decode_addr(&addr); infoversion = ceph_decode_8(p); namelen = ceph_decode_32(p); /* skip mds name */ *p += namelen; diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index b48abc0..6ff44bb 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -12,6 +12,7 @@ #include "super.h" #include "messenger.h" +#include "decode.h" /* * Ceph uses the messenger to exchange ceph_msg messages with other @@ -97,6 +98,12 @@ const char *pr_addr(const struct sockaddr_storage *ss) return s; } +static void encode_my_addr(struct ceph_messenger *msgr) +{ + memcpy(&msgr->my_enc_addr, &msgr->inst.addr, sizeof(msgr->my_enc_addr)); + ceph_encode_addr(&msgr->my_enc_addr); +} + /* * work queue for all reading and writing to/from the socket. */ @@ -590,12 +597,12 @@ static void prepare_write_connect(struct ceph_messenger *msgr, con->out_kvec[0].iov_base = CEPH_BANNER; con->out_kvec[0].iov_len = len; - con->out_kvec[1].iov_base = &msgr->inst.addr; - con->out_kvec[1].iov_len = sizeof(msgr->inst.addr); + con->out_kvec[1].iov_base = &msgr->my_enc_addr; + con->out_kvec[1].iov_len = sizeof(msgr->my_enc_addr); con->out_kvec[2].iov_base = &con->out_connect; con->out_kvec[2].iov_len = sizeof(con->out_connect); con->out_kvec_left = 3; - con->out_kvec_bytes = len + sizeof(msgr->inst.addr) + + con->out_kvec_bytes = len + sizeof(msgr->my_enc_addr) + sizeof(con->out_connect); con->out_kvec_cur = con->out_kvec; con->out_more = 0; @@ -976,6 +983,9 @@ static int process_connect(struct ceph_connection *con) if (verify_hello(con) < 0) return -1; + ceph_decode_addr(&con->actual_peer_addr); + ceph_decode_addr(&con->peer_addr_for_me); + /* * Make sure the other end is who we wanted. note that the other * end may not yet know their ip address, so if it's 0.0.0.0, give @@ -1005,6 +1015,7 @@ static int process_connect(struct ceph_connection *con) &con->peer_addr_for_me.in_addr, sizeof(con->peer_addr_for_me.in_addr)); addr_set_port(&con->msgr->inst.addr.in_addr, port); + encode_my_addr(con->msgr); dout("process_connect learned my addr is %s\n", pr_addr(&con->msgr->inst.addr.in_addr)); } @@ -1780,6 +1791,7 @@ struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr) /* select a random nonce */ get_random_bytes(&msgr->inst.addr.nonce, sizeof(msgr->inst.addr.nonce)); + encode_my_addr(msgr); dout("messenger_create %p\n", msgr); return msgr; @@ -1806,8 +1818,9 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) } /* set src+dst */ - msg->hdr.src = con->msgr->inst; - msg->hdr.orig_src = con->msgr->inst; + msg->hdr.src.name = con->msgr->inst.name; + msg->hdr.src.addr = con->msgr->my_enc_addr; + msg->hdr.orig_src = msg->hdr.src; msg->hdr.dst_erank = con->peer_addr.erank; /* queue */ diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index dcd98b6..e016fa7 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -53,6 +53,7 @@ extern const char *ceph_name_type_str(int t); struct ceph_messenger { struct ceph_entity_inst inst; /* my name+address */ + struct ceph_entity_addr my_enc_addr; struct page *zero_page; /* used in certain error cases */ bool nocrc; diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 61263c9..95b76e7 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -59,6 +59,8 @@ struct ceph_monmap *ceph_monmap_decode(void *p, void *end) m->epoch = epoch; m->num_mon = num_mon; ceph_decode_copy(&p, m->mon_inst, num_mon*sizeof(m->mon_inst[0])); + for (i = 0; i < num_mon; i++) + ceph_decode_addr(&m->mon_inst[i].addr); dout("monmap_decode epoch %d, num_mon %d\n", m->epoch, m->num_mon); diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h index 9abc879..8e3ea2e 100644 --- a/fs/ceph/msgr.h +++ b/fs/ceph/msgr.h @@ -21,7 +21,7 @@ * whenever the wire protocol changes. try to keep this string length * constant. */ -#define CEPH_BANNER "ceph v022" +#define CEPH_BANNER "ceph v023" #define CEPH_BANNER_MAX_LEN 30 diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index d62e111..cd7bb26 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -460,6 +460,8 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) *p += 4; /* skip length field (should match max) */ ceph_decode_copy(p, map->osd_addr, map->max_osd*sizeof(*map->osd_addr)); + for (i = 0; i < map->max_osd; i++) + ceph_decode_addr(&map->osd_addr[i]); /* pg_temp */ ceph_decode_32_safe(p, end, len, bad); @@ -619,6 +621,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, struct ceph_entity_addr addr; ceph_decode_32_safe(p, end, osd, bad); ceph_decode_copy_safe(p, end, &addr, sizeof(addr), bad); + ceph_decode_addr(&addr); pr_info("osd%d up\n", osd); BUG_ON(osd >= map->max_osd); map->osd_state[osd] |= CEPH_OSD_UP; -- cgit v0.10.2 From 51042122d4f85e0f8ee577a4230f172fcc57c456 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 4 Nov 2009 11:39:12 -0800 Subject: ceph: fix endian conversions for ceph_pg The endian conversions don't quite work with the old union ceph_pg. Just make it a regular struct, and make each field __le. This is simpler and it has the added bonus of actually working. Signed-off-by: Sage Weil diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index e4f99ef..4c33e19 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -99,7 +99,7 @@ static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg) u64 len = 1, olen; u64 tmp; struct ceph_object_layout ol; - union ceph_pg pgid; + struct ceph_pg pgid; /* copy and validate */ if (copy_from_user(&dl, arg, sizeof(dl))) @@ -121,7 +121,7 @@ static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg) ceph_calc_object_layout(&ol, dl.object_name, &ci->i_layout, osdc->osdmap); - pgid.pg64 = le64_to_cpu(ol.ol_pgid); + pgid = ol.ol_pgid; dl.osd = ceph_calc_pg_primary(osdc->osdmap, pgid); if (dl.osd >= 0) { struct ceph_entity_addr *a = diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 7dc0f62..7db14ba 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -520,7 +520,7 @@ static int __map_osds(struct ceph_osd_client *osdc, struct ceph_osd_request *req) { struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; - union ceph_pg pgid; + struct ceph_pg pgid; int o = -1; int err; struct ceph_osd *newosd = NULL; @@ -530,7 +530,7 @@ static int __map_osds(struct ceph_osd_client *osdc, &req->r_file_layout, osdc->osdmap); if (err) return err; - pgid.pg64 = le64_to_cpu(reqhead->layout.ol_pgid); + pgid = reqhead->layout.ol_pgid; o = ceph_calc_pg_primary(osdc->osdmap, pgid); if ((req->r_osd && req->r_osd->o_osd == o && @@ -538,8 +538,8 @@ static int __map_osds(struct ceph_osd_client *osdc, (req->r_osd == NULL && o == -1)) return 0; /* no change */ - dout("map_osds tid %llu pgid %llx pool %d osd%d (was osd%d)\n", - req->r_tid, pgid.pg64, pgid.pg.pool, o, + dout("map_osds tid %llu pgid %d.%x osd%d (was osd%d)\n", + req->r_tid, le32_to_cpu(pgid.pool), le16_to_cpu(pgid.ps), o, req->r_osd ? req->r_osd->o_osd : -1); if (req->r_osd) { diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index cd7bb26..8b0cd11 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -366,19 +366,33 @@ static int osdmap_set_max_osd(struct ceph_osdmap *map, int max) /* * Insert a new pg_temp mapping */ +static int pgid_cmp(struct ceph_pg l, struct ceph_pg r) +{ + u64 a = *(u64 *)&l; + u64 b = *(u64 *)&r; + + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + static int __insert_pg_mapping(struct ceph_pg_mapping *new, struct rb_root *root) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct ceph_pg_mapping *pg = NULL; + int c; while (*p) { parent = *p; pg = rb_entry(parent, struct ceph_pg_mapping, node); - if (new->pgid < pg->pgid) + c = pgid_cmp(new->pgid, pg->pgid); + if (c < 0) p = &(*p)->rb_left; - else if (new->pgid > pg->pgid) + else if (c > 0) p = &(*p)->rb_right; else return -EEXIST; @@ -467,11 +481,11 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) ceph_decode_32_safe(p, end, len, bad); for (i = 0; i < len; i++) { int n, j; - u64 pgid; + struct ceph_pg pgid; struct ceph_pg_mapping *pg; ceph_decode_need(p, end, sizeof(u32) + sizeof(u64), bad); - pgid = ceph_decode_64(p); + ceph_decode_copy(p, &pgid, sizeof(pgid)); n = ceph_decode_32(p); ceph_decode_need(p, end, n * sizeof(u32), bad); pg = kmalloc(sizeof(*pg) + n*sizeof(u32), GFP_NOFS); @@ -487,7 +501,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) err = __insert_pg_mapping(pg, &map->pg_temp); if (err) goto bad; - dout(" added pg_temp %llx len %d\n", pgid, len); + dout(" added pg_temp %llx len %d\n", *(u64 *)&pgid, len); } /* crush */ @@ -659,19 +673,20 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, while (len--) { struct ceph_pg_mapping *pg; int j; - u64 pgid; + struct ceph_pg pgid; u32 pglen; ceph_decode_need(p, end, sizeof(u64) + sizeof(u32), bad); - pgid = ceph_decode_64(p); + ceph_decode_copy(p, &pgid, sizeof(pgid)); pglen = ceph_decode_32(p); /* remove any? */ - while (rbp && rb_entry(rbp, struct ceph_pg_mapping, - node)->pgid <= pgid) { + while (rbp && pgid_cmp(rb_entry(rbp, struct ceph_pg_mapping, + node)->pgid, pgid) <= 0) { struct rb_node *cur = rbp; rbp = rb_next(rbp); dout(" removed pg_temp %llx\n", - rb_entry(cur, struct ceph_pg_mapping, node)->pgid); + *(u64 *)&rb_entry(cur, struct ceph_pg_mapping, + node)->pgid); rb_erase(cur, &map->pg_temp); } @@ -690,14 +705,16 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, err = __insert_pg_mapping(pg, &map->pg_temp); if (err) goto bad; - dout(" added pg_temp %llx len %d\n", pgid, pglen); + dout(" added pg_temp %llx len %d\n", *(u64 *)&pgid, + pglen); } } while (rbp) { struct rb_node *cur = rbp; rbp = rb_next(rbp); dout(" removed pg_temp %llx\n", - rb_entry(cur, struct ceph_pg_mapping, node)->pgid); + *(u64 *)&rb_entry(cur, struct ceph_pg_mapping, + node)->pgid); rb_erase(cur, &map->pg_temp); } @@ -782,16 +799,19 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol, struct ceph_osdmap *osdmap) { unsigned num, num_mask; - union ceph_pg pgid; + struct ceph_pg pgid; s32 preferred = (s32)le32_to_cpu(fl->fl_pg_preferred); int poolid = le32_to_cpu(fl->fl_pg_pool); struct ceph_pg_pool_info *pool; + unsigned ps; if (poolid >= osdmap->num_pools) return -EIO; - pool = &osdmap->pg_pool[poolid]; + pool = &osdmap->pg_pool[poolid]; + ps = ceph_full_name_hash(oid, strlen(oid)); if (preferred >= 0) { + ps += preferred; num = le32_to_cpu(pool->v.lpg_num); num_mask = pool->lpg_num_mask; } else { @@ -799,22 +819,17 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol, num_mask = pool->pg_num_mask; } - pgid.pg64 = 0; /* start with it zeroed out */ - pgid.pg.ps = ceph_full_name_hash(oid, strlen(oid)); - pgid.pg.preferred = preferred; - if (preferred >= 0) - pgid.pg.ps += preferred; - pgid.pg.pool = le32_to_cpu(fl->fl_pg_pool); + pgid.ps = cpu_to_le16(ps); + pgid.preferred = cpu_to_le16(preferred); + pgid.pool = fl->fl_pg_pool; if (preferred >= 0) - dout("calc_object_layout '%s' pgid %d.%xp%d (%llx)\n", oid, - pgid.pg.pool, pgid.pg.ps, (int)preferred, pgid.pg64); + dout("calc_object_layout '%s' pgid %d.%xp%d\n", oid, poolid, ps, + (int)preferred); else - dout("calc_object_layout '%s' pgid %d.%x (%llx)\n", oid, - pgid.pg.pool, pgid.pg.ps, pgid.pg64); + dout("calc_object_layout '%s' pgid %d.%x\n", oid, poolid, ps); - ol->ol_pgid = cpu_to_le64(pgid.pg64); + ol->ol_pgid = pgid; ol->ol_stripe_unit = fl->fl_object_stripe_unit; - return 0; } @@ -822,21 +837,24 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol, * Calculate raw osd vector for the given pgid. Return pointer to osd * array, or NULL on failure. */ -static int *calc_pg_raw(struct ceph_osdmap *osdmap, union ceph_pg pgid, +static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, int *osds, int *num) { struct rb_node *n = osdmap->pg_temp.rb_node; struct ceph_pg_mapping *pg; struct ceph_pg_pool_info *pool; int ruleno; - unsigned pps; /* placement ps */ + unsigned poolid, ps, pps; + int preferred; + int c; /* pg_temp? */ while (n) { pg = rb_entry(n, struct ceph_pg_mapping, node); - if (pgid.pg64 < pg->pgid) + c = pgid_cmp(pgid, pg->pgid); + if (c < 0) n = n->rb_left; - else if (pgid.pg64 > pg->pgid) + else if (c > 0) n = n->rb_right; else { *num = pg->len; @@ -845,36 +863,40 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, union ceph_pg pgid, } /* crush */ - if (pgid.pg.pool >= osdmap->num_pools) + poolid = le32_to_cpu(pgid.pool); + ps = le16_to_cpu(pgid.ps); + preferred = (s16)le16_to_cpu(pgid.preferred); + + if (poolid >= osdmap->num_pools) return NULL; - pool = &osdmap->pg_pool[pgid.pg.pool]; + pool = &osdmap->pg_pool[poolid]; ruleno = crush_find_rule(osdmap->crush, pool->v.crush_ruleset, pool->v.type, pool->v.size); if (ruleno < 0) { pr_err("no crush rule pool %d type %d size %d\n", - pgid.pg.pool, pool->v.type, pool->v.size); + poolid, pool->v.type, pool->v.size); return NULL; } - if (pgid.pg.preferred >= 0) - pps = ceph_stable_mod(pgid.pg.ps, + if (preferred >= 0) + pps = ceph_stable_mod(ps, le32_to_cpu(pool->v.lpgp_num), pool->lpgp_num_mask); else - pps = ceph_stable_mod(pgid.pg.ps, + pps = ceph_stable_mod(ps, le32_to_cpu(pool->v.pgp_num), pool->pgp_num_mask); - pps += pgid.pg.pool; + pps += poolid; *num = crush_do_rule(osdmap->crush, ruleno, pps, osds, min_t(int, pool->v.size, *num), - pgid.pg.preferred, osdmap->osd_weight); + preferred, osdmap->osd_weight); return osds; } /* * Return primary osd for given pgid, or -1 if none. */ -int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, union ceph_pg pgid) +int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, struct ceph_pg pgid) { int rawosds[10], *osds; int i, num = ARRAY_SIZE(rawosds); diff --git a/fs/ceph/osdmap.h b/fs/ceph/osdmap.h index 07127c6..c4af841 100644 --- a/fs/ceph/osdmap.h +++ b/fs/ceph/osdmap.h @@ -25,7 +25,7 @@ struct ceph_pg_pool_info { struct ceph_pg_mapping { struct rb_node node; - u64 pgid; + struct ceph_pg pgid; int len; int osds[]; }; @@ -118,6 +118,7 @@ extern int ceph_calc_object_layout(struct ceph_object_layout *ol, const char *oid, struct ceph_file_layout *fl, struct ceph_osdmap *osdmap); -extern int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, union ceph_pg pgid); +extern int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, + struct ceph_pg pgid); #endif diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h index a48cf4a..85bdef7 100644 --- a/fs/ceph/rados.h +++ b/fs/ceph/rados.h @@ -55,13 +55,10 @@ struct ceph_timespec { * placement group. * we encode this into one __le64. */ -union ceph_pg { - __u64 pg64; - struct { - __s16 preferred; /* preferred primary osd */ - __u16 ps; /* placement seed */ - __u32 pool; /* object pool */ - } __attribute__ ((packed)) pg; +struct ceph_pg { + __le16 preferred; /* preferred primary osd */ + __le16 ps; /* placement seed */ + __le32 pool; /* object pool */ } __attribute__ ((packed)); /* @@ -117,7 +114,7 @@ static inline int ceph_stable_mod(int x, int b, int bmask) * object layout - how a given object should be stored. */ struct ceph_object_layout { - __le64 ol_pgid; /* raw pg, with _full_ ps precision. */ + struct ceph_pg ol_pgid; /* raw pg, with _full_ ps precision. */ __le32 ol_stripe_unit; /* for per-object parity, if any */ } __attribute__ ((packed)); -- cgit v0.10.2 From 6a18be16f7513ea8a4923c161ce073987932cbdb Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 4 Nov 2009 11:40:05 -0800 Subject: ceph: fix sparse endian warning Use the __le macro, even though for -1 it doesn't matter. Signed-off-by: Sage Weil diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 1bd57c8..fc8aff4 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -52,7 +52,7 @@ prepare_open_request(struct super_block *sb, int flags, int create_mode) req->r_fmode = ceph_flags_to_mode(flags); req->r_args.open.flags = cpu_to_le32(flags); req->r_args.open.mode = cpu_to_le32(create_mode); - req->r_args.open.preferred = -1; + req->r_args.open.preferred = cpu_to_le32(-1); out: return req; } diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 0ae40ba..1ac7b07 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -804,7 +804,7 @@ static int ceph_get_sb(struct file_system_type *fs_type, struct ceph_client *client; int err; int (*compare_super)(struct super_block *, void *) = ceph_compare_super; - const char *path = 0; + const char *path = NULL; struct ceph_mount_args *args; dout("ceph_get_sb\n"); -- cgit v0.10.2 From f28bcfbe660a3246621a367020054d4f1a179cd9 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 4 Nov 2009 11:46:35 -0800 Subject: ceph: convert port endianness The port is informational only, but we should make it correct. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 6ff44bb..5cc3748 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -891,9 +891,9 @@ static int addr_port(struct sockaddr_storage *ss) { switch (ss->ss_family) { case AF_INET: - return ((struct sockaddr_in *)ss)->sin_port; + return ntohs(((struct sockaddr_in *)ss)->sin_port); case AF_INET6: - return ((struct sockaddr_in6 *)ss)->sin6_port; + return ntohs(((struct sockaddr_in6 *)ss)->sin6_port); } return 0; } -- cgit v0.10.2 From 1bdb70e59026838a79f77c440f8fe480a66e65e8 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 6 Nov 2009 13:57:49 -0800 Subject: ceph: clean up 'osd%d down' console msg No ceph prefix. Signed-off-by: Sage Weil diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 8b0cd11..a025555 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -648,7 +648,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, u32 osd; ceph_decode_32_safe(p, end, osd, bad); (*p)++; /* clean flag */ - pr_info("ceph osd%d down\n", osd); + pr_info("osd%d down\n", osd); if (osd < map->max_osd) map->osd_state[osd] &= ~CEPH_OSD_UP; } -- cgit v0.10.2 From c6cf726316abd613cfb7c325d950f3629f964ec6 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 6 Nov 2009 16:39:26 -0800 Subject: ceph: make CRUSH hash functions non-inline These are way to big to be inline. I missed crush/* when doing the inline audit for akpm's review. Signed-off-by: Sage Weil diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile index 7da6d69..8bad70a 100644 --- a/fs/ceph/Makefile +++ b/fs/ceph/Makefile @@ -11,7 +11,7 @@ ceph-objs := super.o inode.o dir.o file.o addr.o ioctl.o \ messenger.o msgpool.o buffer.o \ mds_client.o mdsmap.o \ mon_client.o \ - osd_client.o osdmap.o crush/crush.o crush/mapper.o \ + osd_client.o osdmap.o crush/crush.o crush/mapper.o crush/hash.o \ debugfs.o \ ceph_fs.o ceph_strings.o ceph_frag.o diff --git a/fs/ceph/README b/fs/ceph/README index 231a1df..660e000 100644 --- a/fs/ceph/README +++ b/fs/ceph/README @@ -15,3 +15,4 @@ src/crush/crush.h fs/ceph/crush/crush.h src/crush/mapper.c fs/ceph/crush/mapper.c src/crush/mapper.h fs/ceph/crush/mapper.h src/crush/hash.h fs/ceph/crush/hash.h +src/crush/hash.c fs/ceph/crush/hash.c diff --git a/fs/ceph/crush/crush.c b/fs/ceph/crush/crush.c index 13755cd..fabd302 100644 --- a/fs/ceph/crush/crush.c +++ b/fs/ceph/crush/crush.c @@ -10,6 +10,17 @@ #include "crush.h" +const char *crush_bucket_alg_name(int alg) +{ + switch (alg) { + case CRUSH_BUCKET_UNIFORM: return "uniform"; + case CRUSH_BUCKET_LIST: return "list"; + case CRUSH_BUCKET_TREE: return "tree"; + case CRUSH_BUCKET_STRAW: return "straw"; + default: return "unknown"; + } +} + /** * crush_get_bucket_item_weight - Get weight of an item in given bucket * @b: bucket pointer diff --git a/fs/ceph/crush/crush.h b/fs/ceph/crush/crush.h index 9ac7e09..92c6b3c 100644 --- a/fs/ceph/crush/crush.h +++ b/fs/ceph/crush/crush.h @@ -97,16 +97,7 @@ enum { CRUSH_BUCKET_TREE = 3, CRUSH_BUCKET_STRAW = 4 }; -static inline const char *crush_bucket_alg_name(int alg) -{ - switch (alg) { - case CRUSH_BUCKET_UNIFORM: return "uniform"; - case CRUSH_BUCKET_LIST: return "list"; - case CRUSH_BUCKET_TREE: return "tree"; - case CRUSH_BUCKET_STRAW: return "straw"; - default: return "unknown"; - } -} +extern const char *crush_bucket_alg_name(int alg); struct crush_bucket { __s32 id; /* this'll be negative */ diff --git a/fs/ceph/crush/hash.c b/fs/ceph/crush/hash.c new file mode 100644 index 0000000..b438c5d --- /dev/null +++ b/fs/ceph/crush/hash.c @@ -0,0 +1,86 @@ + +#include + +/* + * Robert Jenkins' function for mixing 32-bit values + * http://burtleburtle.net/bob/hash/evahash.html + * a, b = random bits, c = input and output + */ +#define crush_hashmix(a, b, c) do { \ + a = a-b; a = a-c; a = a^(c>>13); \ + b = b-c; b = b-a; b = b^(a<<8); \ + c = c-a; c = c-b; c = c^(b>>13); \ + a = a-b; a = a-c; a = a^(c>>12); \ + b = b-c; b = b-a; b = b^(a<<16); \ + c = c-a; c = c-b; c = c^(b>>5); \ + a = a-b; a = a-c; a = a^(c>>3); \ + b = b-c; b = b-a; b = b^(a<<10); \ + c = c-a; c = c-b; c = c^(b>>15); \ + } while (0) + +#define crush_hash_seed 1315423911 + +__u32 crush_hash32(__u32 a) +{ + __u32 hash = crush_hash_seed ^ a; + __u32 b = a; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(b, x, hash); + crush_hashmix(y, a, hash); + return hash; +} + +__u32 crush_hash32_2(__u32 a, __u32 b) +{ + __u32 hash = crush_hash_seed ^ a ^ b; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(a, b, hash); + crush_hashmix(x, a, hash); + crush_hashmix(b, y, hash); + return hash; +} + +__u32 crush_hash32_3(__u32 a, __u32 b, __u32 c) +{ + __u32 hash = crush_hash_seed ^ a ^ b ^ c; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(a, b, hash); + crush_hashmix(c, x, hash); + crush_hashmix(y, a, hash); + crush_hashmix(b, x, hash); + crush_hashmix(y, c, hash); + return hash; +} + +__u32 crush_hash32_4(__u32 a, __u32 b, __u32 c, __u32 d) +{ + __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(a, b, hash); + crush_hashmix(c, d, hash); + crush_hashmix(a, x, hash); + crush_hashmix(y, b, hash); + crush_hashmix(c, x, hash); + crush_hashmix(y, d, hash); + return hash; +} + +__u32 crush_hash32_5(__u32 a, __u32 b, __u32 c, __u32 d, __u32 e) +{ + __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d ^ e; + __u32 x = 231232; + __u32 y = 1232; + crush_hashmix(a, b, hash); + crush_hashmix(c, d, hash); + crush_hashmix(e, x, hash); + crush_hashmix(y, a, hash); + crush_hashmix(b, x, hash); + crush_hashmix(y, c, hash); + crush_hashmix(d, x, hash); + crush_hashmix(y, e, hash); + return hash; +} diff --git a/fs/ceph/crush/hash.h b/fs/ceph/crush/hash.h index 42f3312..9ce89f8 100644 --- a/fs/ceph/crush/hash.h +++ b/fs/ceph/crush/hash.h @@ -1,90 +1,12 @@ #ifndef _CRUSH_HASH_H #define _CRUSH_HASH_H -/* - * Robert Jenkins' function for mixing 32-bit values - * http://burtleburtle.net/bob/hash/evahash.html - * a, b = random bits, c = input and output - */ -#define crush_hashmix(a, b, c) do { \ - a = a-b; a = a-c; a = a^(c>>13); \ - b = b-c; b = b-a; b = b^(a<<8); \ - c = c-a; c = c-b; c = c^(b>>13); \ - a = a-b; a = a-c; a = a^(c>>12); \ - b = b-c; b = b-a; b = b^(a<<16); \ - c = c-a; c = c-b; c = c^(b>>5); \ - a = a-b; a = a-c; a = a^(c>>3); \ - b = b-c; b = b-a; b = b^(a<<10); \ - c = c-a; c = c-b; c = c^(b>>15); \ - } while (0) - -#define crush_hash_seed 1315423911 - -static inline __u32 crush_hash32(__u32 a) -{ - __u32 hash = crush_hash_seed ^ a; - __u32 b = a; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(b, x, hash); - crush_hashmix(y, a, hash); - return hash; -} - -static inline __u32 crush_hash32_2(__u32 a, __u32 b) -{ - __u32 hash = crush_hash_seed ^ a ^ b; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(a, b, hash); - crush_hashmix(x, a, hash); - crush_hashmix(b, y, hash); - return hash; -} - -static inline __u32 crush_hash32_3(__u32 a, __u32 b, __u32 c) -{ - __u32 hash = crush_hash_seed ^ a ^ b ^ c; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(a, b, hash); - crush_hashmix(c, x, hash); - crush_hashmix(y, a, hash); - crush_hashmix(b, x, hash); - crush_hashmix(y, c, hash); - return hash; -} - -static inline __u32 crush_hash32_4(__u32 a, __u32 b, __u32 c, - __u32 d) -{ - __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(a, b, hash); - crush_hashmix(c, d, hash); - crush_hashmix(a, x, hash); - crush_hashmix(y, b, hash); - crush_hashmix(c, x, hash); - crush_hashmix(y, d, hash); - return hash; -} - -static inline __u32 crush_hash32_5(__u32 a, __u32 b, __u32 c, - __u32 d, __u32 e) -{ - __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d ^ e; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(a, b, hash); - crush_hashmix(c, d, hash); - crush_hashmix(e, x, hash); - crush_hashmix(y, a, hash); - crush_hashmix(b, x, hash); - crush_hashmix(y, c, hash); - crush_hashmix(d, x, hash); - crush_hashmix(y, e, hash); - return hash; -} +extern __u32 crush_hash32(__u32 a); +extern __u32 crush_hash32_2(__u32 a, __u32 b); +extern __u32 crush_hash32_3(__u32 a, __u32 b, __u32 c); +extern __u32 crush_hash32_4(__u32 a, __u32 b, __u32 c, + __u32 d); +extern __u32 crush_hash32_5(__u32 a, __u32 b, __u32 c, + __u32 d, __u32 e); #endif -- cgit v0.10.2 From cfbbcd24a6bfd794295ee7ad76dfbff40ad6b934 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 6 Nov 2009 16:44:05 -0800 Subject: ceph: use strong hash function for mapping objects to pgs We were using the (weak) dcache hash function, but it was leaving lower bits consecutive for consecutive (inode) objects. We really want to make the object to pg mapping random and uniform, so use a proper hash function here. This is Robert Jenkin's public domain hash function (with some minor cleanup): http://burtleburtle.net/bob/hash/evahash.html This is a protocol revision. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.c b/fs/ceph/ceph_fs.c index a950b40..b3ecf1b 100644 --- a/fs/ceph/ceph_fs.c +++ b/fs/ceph/ceph_fs.c @@ -73,32 +73,79 @@ int ceph_caps_for_mode(int mode) return 0; } -/* Name hashing routines. Initial hash value */ -/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */ -#define ceph_init_name_hash() 0 - -/* partial hash update function. Assume roughly 4 bits per character */ -static unsigned long ceph_partial_name_hash(unsigned long c, - unsigned long prevhash) -{ - return (prevhash + (c << 4) + (c >> 4)) * 11; -} - /* - * Finally: cut down the number of bits to a int value (and try to avoid - * losing bits) + * Robert Jenkin's hash function. + * http://burtleburtle.net/bob/hash/evahash.html + * This is in the public domain. */ -static unsigned long ceph_end_name_hash(unsigned long hash) -{ - return hash & 0xffffffff; -} +#define mix(a, b, c) \ + do { \ + a = a - b; a = a - c; a = a ^ (c >> 13); \ + b = b - c; b = b - a; b = b ^ (a << 8); \ + c = c - a; c = c - b; c = c ^ (b >> 13); \ + a = a - b; a = a - c; a = a ^ (c >> 12); \ + b = b - c; b = b - a; b = b ^ (a << 16); \ + c = c - a; c = c - b; c = c ^ (b >> 5); \ + a = a - b; a = a - c; a = a ^ (c >> 3); \ + b = b - c; b = b - a; b = b ^ (a << 10); \ + c = c - a; c = c - b; c = c ^ (b >> 15); \ + } while (0) -/* Compute the hash for a name string. */ -unsigned int ceph_full_name_hash(const char *name, unsigned int len) +unsigned int ceph_full_name_hash(const char *str, unsigned int length) { - unsigned long hash = ceph_init_name_hash(); - while (len--) - hash = ceph_partial_name_hash(*name++, hash); - return ceph_end_name_hash(hash); + const unsigned char *k = (const unsigned char *)str; + __u32 a, b, c; /* the internal state */ + __u32 len; /* how many key bytes still need mixing */ + + /* Set up the internal state */ + len = length; + a = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + b = a; + c = 0; /* variable initialization of internal state */ + + /* handle most of the key */ + while (len >= 12) { + a = a + (k[0] + ((__u32)k[1] << 8) + ((__u32)k[2] << 16) + + ((__u32)k[3] << 24)); + b = b + (k[4] + ((__u32)k[5] << 8) + ((__u32)k[6] << 16) + + ((__u32)k[7] << 24)); + c = c + (k[8] + ((__u32)k[9] << 8) + ((__u32)k[10] << 16) + + ((__u32)k[11] << 24)); + mix(a, b, c); + k = k + 12; + len = len - 12; + } + + /* handle the last 11 bytes */ + c = c + length; + switch (len) { /* all the case statements fall through */ + case 11: + c = c + ((__u32)k[10] << 24); + case 10: + c = c + ((__u32)k[9] << 16); + case 9: + c = c + ((__u32)k[8] << 8); + /* the first byte of c is reserved for the length */ + case 8: + b = b + ((__u32)k[7] << 24); + case 7: + b = b + ((__u32)k[6] << 16); + case 6: + b = b + ((__u32)k[5] << 8); + case 5: + b = b + k[4]; + case 4: + a = a + ((__u32)k[3] << 24); + case 3: + a = a + ((__u32)k[2] << 16); + case 2: + a = a + ((__u32)k[1] << 8); + case 1: + a = a + k[0]; + /* case 0: nothing left to add */ + } + mix(a, b, c); + + return c; } diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index ae52382..25fc537 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -38,7 +38,7 @@ #define CEPH_OSD_PROTOCOL 7 /* cluster internal */ #define CEPH_MDS_PROTOCOL 9 /* cluster internal */ #define CEPH_MON_PROTOCOL 5 /* cluster internal */ -#define CEPH_OSDC_PROTOCOL 21 /* server/client */ +#define CEPH_OSDC_PROTOCOL 22 /* server/client */ #define CEPH_MDSC_PROTOCOL 29 /* server/client */ #define CEPH_MONC_PROTOCOL 15 /* server/client */ -- cgit v0.10.2 From 1654dd0cf5ee1827322aca156af7d96d757201c7 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 6 Nov 2009 21:55:25 -0800 Subject: ceph: make object hash a pg_pool property The object will be hashed to a placement seed (ps) based on the pg_pool's hash function. This allows new hashes to be introduced into an existing object store, or selection of a hash appropriate to the objects that will be stored in a particular pool. Signed-off-by: Sage Weil diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile index 8bad70a..bdd3e6f 100644 --- a/fs/ceph/Makefile +++ b/fs/ceph/Makefile @@ -13,7 +13,7 @@ ceph-objs := super.o inode.o dir.o file.o addr.o ioctl.o \ mon_client.o \ osd_client.o osdmap.o crush/crush.o crush/mapper.o crush/hash.o \ debugfs.o \ - ceph_fs.o ceph_strings.o ceph_frag.o + ceph_fs.o ceph_strings.o ceph_hash.o ceph_frag.o else #Otherwise we were called directly from the command diff --git a/fs/ceph/README b/fs/ceph/README index 660e000..18352fa 100644 --- a/fs/ceph/README +++ b/fs/ceph/README @@ -10,6 +10,8 @@ src/include/rados.h fs/ceph/rados.h src/include/ceph_strings.cc fs/ceph/ceph_strings.c src/include/ceph_frag.h fs/ceph/ceph_frag.h src/include/ceph_frag.cc fs/ceph/ceph_frag.c +src/include/ceph_hash.h fs/ceph/ceph_hash.h +src/include/ceph_hash.cc fs/ceph/ceph_hash.c src/crush/crush.c fs/ceph/crush/crush.c src/crush/crush.h fs/ceph/crush/crush.h src/crush/mapper.c fs/ceph/crush/mapper.c diff --git a/fs/ceph/ceph_fs.c b/fs/ceph/ceph_fs.c index b3ecf1b..79d76bc 100644 --- a/fs/ceph/ceph_fs.c +++ b/fs/ceph/ceph_fs.c @@ -72,80 +72,3 @@ int ceph_caps_for_mode(int mode) } return 0; } - -/* - * Robert Jenkin's hash function. - * http://burtleburtle.net/bob/hash/evahash.html - * This is in the public domain. - */ -#define mix(a, b, c) \ - do { \ - a = a - b; a = a - c; a = a ^ (c >> 13); \ - b = b - c; b = b - a; b = b ^ (a << 8); \ - c = c - a; c = c - b; c = c ^ (b >> 13); \ - a = a - b; a = a - c; a = a ^ (c >> 12); \ - b = b - c; b = b - a; b = b ^ (a << 16); \ - c = c - a; c = c - b; c = c ^ (b >> 5); \ - a = a - b; a = a - c; a = a ^ (c >> 3); \ - b = b - c; b = b - a; b = b ^ (a << 10); \ - c = c - a; c = c - b; c = c ^ (b >> 15); \ - } while (0) - -unsigned int ceph_full_name_hash(const char *str, unsigned int length) -{ - const unsigned char *k = (const unsigned char *)str; - __u32 a, b, c; /* the internal state */ - __u32 len; /* how many key bytes still need mixing */ - - /* Set up the internal state */ - len = length; - a = 0x9e3779b9; /* the golden ratio; an arbitrary value */ - b = a; - c = 0; /* variable initialization of internal state */ - - /* handle most of the key */ - while (len >= 12) { - a = a + (k[0] + ((__u32)k[1] << 8) + ((__u32)k[2] << 16) + - ((__u32)k[3] << 24)); - b = b + (k[4] + ((__u32)k[5] << 8) + ((__u32)k[6] << 16) + - ((__u32)k[7] << 24)); - c = c + (k[8] + ((__u32)k[9] << 8) + ((__u32)k[10] << 16) + - ((__u32)k[11] << 24)); - mix(a, b, c); - k = k + 12; - len = len - 12; - } - - /* handle the last 11 bytes */ - c = c + length; - switch (len) { /* all the case statements fall through */ - case 11: - c = c + ((__u32)k[10] << 24); - case 10: - c = c + ((__u32)k[9] << 16); - case 9: - c = c + ((__u32)k[8] << 8); - /* the first byte of c is reserved for the length */ - case 8: - b = b + ((__u32)k[7] << 24); - case 7: - b = b + ((__u32)k[6] << 16); - case 6: - b = b + ((__u32)k[5] << 8); - case 5: - b = b + k[4]; - case 4: - a = a + ((__u32)k[3] << 24); - case 3: - a = a + ((__u32)k[2] << 16); - case 2: - a = a + ((__u32)k[1] << 8); - case 1: - a = a + k[0]; - /* case 0: nothing left to add */ - } - mix(a, b, c); - - return c; -} - diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 25fc537..36becb0 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -49,8 +49,6 @@ #define CEPH_MAX_MON 31 -unsigned int ceph_full_name_hash(const char *name, unsigned int len); - /* * ceph_file_layout - describe data layout for a file/inode diff --git a/fs/ceph/ceph_hash.c b/fs/ceph/ceph_hash.c new file mode 100644 index 0000000..ac8be54 --- /dev/null +++ b/fs/ceph/ceph_hash.c @@ -0,0 +1,118 @@ + +#include "types.h" + +/* + * Robert Jenkin's hash function. + * http://burtleburtle.net/bob/hash/evahash.html + * This is in the public domain. + */ +#define mix(a, b, c) \ + do { \ + a = a - b; a = a - c; a = a ^ (c >> 13); \ + b = b - c; b = b - a; b = b ^ (a << 8); \ + c = c - a; c = c - b; c = c ^ (b >> 13); \ + a = a - b; a = a - c; a = a ^ (c >> 12); \ + b = b - c; b = b - a; b = b ^ (a << 16); \ + c = c - a; c = c - b; c = c ^ (b >> 5); \ + a = a - b; a = a - c; a = a ^ (c >> 3); \ + b = b - c; b = b - a; b = b ^ (a << 10); \ + c = c - a; c = c - b; c = c ^ (b >> 15); \ + } while (0) + +unsigned ceph_str_hash_rjenkins(const char *str, unsigned length) +{ + const unsigned char *k = (const unsigned char *)str; + __u32 a, b, c; /* the internal state */ + __u32 len; /* how many key bytes still need mixing */ + + /* Set up the internal state */ + len = length; + a = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + b = a; + c = 0; /* variable initialization of internal state */ + + /* handle most of the key */ + while (len >= 12) { + a = a + (k[0] + ((__u32)k[1] << 8) + ((__u32)k[2] << 16) + + ((__u32)k[3] << 24)); + b = b + (k[4] + ((__u32)k[5] << 8) + ((__u32)k[6] << 16) + + ((__u32)k[7] << 24)); + c = c + (k[8] + ((__u32)k[9] << 8) + ((__u32)k[10] << 16) + + ((__u32)k[11] << 24)); + mix(a, b, c); + k = k + 12; + len = len - 12; + } + + /* handle the last 11 bytes */ + c = c + length; + switch (len) { /* all the case statements fall through */ + case 11: + c = c + ((__u32)k[10] << 24); + case 10: + c = c + ((__u32)k[9] << 16); + case 9: + c = c + ((__u32)k[8] << 8); + /* the first byte of c is reserved for the length */ + case 8: + b = b + ((__u32)k[7] << 24); + case 7: + b = b + ((__u32)k[6] << 16); + case 6: + b = b + ((__u32)k[5] << 8); + case 5: + b = b + k[4]; + case 4: + a = a + ((__u32)k[3] << 24); + case 3: + a = a + ((__u32)k[2] << 16); + case 2: + a = a + ((__u32)k[1] << 8); + case 1: + a = a + k[0]; + /* case 0: nothing left to add */ + } + mix(a, b, c); + + return c; +} + +/* + * linux dcache hash + */ +unsigned ceph_str_hash_linux(const char *str, unsigned length) +{ + unsigned long hash = 0; + unsigned char c; + + while (length-- > 0) { + c = *str++; + hash = (hash + (c << 4) + (c >> 4)) * 11; + } + return hash; +} + + +unsigned ceph_str_hash(int type, const char *s, unsigned len) +{ + switch (type) { + case CEPH_STR_HASH_LINUX: + return ceph_str_hash_linux(s, len); + case CEPH_STR_HASH_RJENKINS: + return ceph_str_hash_rjenkins(s, len); + default: + return -1; + } +} + +const char *ceph_str_hash_name(int type) +{ + switch (type) { + case CEPH_STR_HASH_LINUX: + return "linux"; + case CEPH_STR_HASH_RJENKINS: + return "rjenkins"; + default: + return "unknown"; + } +} diff --git a/fs/ceph/ceph_hash.h b/fs/ceph/ceph_hash.h new file mode 100644 index 0000000..5ac470c --- /dev/null +++ b/fs/ceph/ceph_hash.h @@ -0,0 +1,13 @@ +#ifndef _FS_CEPH_HASH_H +#define _FS_CEPH_HASH_H + +#define CEPH_STR_HASH_LINUX 0x1 /* linux dcache hash */ +#define CEPH_STR_HASH_RJENKINS 0x2 /* robert jenkins' */ + +extern unsigned ceph_str_hash_linux(const char *s, unsigned len); +extern unsigned ceph_str_hash_rjenkins(const char *s, unsigned len); + +extern unsigned ceph_str_hash(int type, const char *s, unsigned len); +extern const char *ceph_str_hash_name(int type); + +#endif diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index a025555..6847827 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -809,7 +809,7 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol, return -EIO; pool = &osdmap->pg_pool[poolid]; - ps = ceph_full_name_hash(oid, strlen(oid)); + ps = ceph_str_hash(pool->v.object_hash, oid, strlen(oid)); if (preferred >= 0) { ps += preferred; num = le32_to_cpu(pool->v.lpg_num); diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h index 85bdef7..fb23ff9 100644 --- a/fs/ceph/rados.h +++ b/fs/ceph/rados.h @@ -84,6 +84,7 @@ struct ceph_pg_pool { __u8 type; /* CEPH_PG_TYPE_* */ __u8 size; /* number of osds in each pg */ __u8 crush_ruleset; /* crush placement rule */ + __u8 object_hash; /* hash mapping object name to ps */ __le32 pg_num, pgp_num; /* number of pg's */ __le32 lpg_num, lpgp_num; /* number of localized pg's */ __le32 last_change; /* most recent epoch changed */ diff --git a/fs/ceph/types.h b/fs/ceph/types.h index 8a51456..28b35a0 100644 --- a/fs/ceph/types.h +++ b/fs/ceph/types.h @@ -9,6 +9,7 @@ #include "ceph_fs.h" #include "ceph_frag.h" +#include "ceph_hash.h" /* * Identify inodes by both their ino AND snapshot id (a u64). -- cgit v0.10.2 From fb690390e305ea51e1883b105c7d3c52d7100ba5 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sat, 7 Nov 2009 20:18:22 -0800 Subject: ceph: make CRUSH hash function a bucket property Make the integer hash function a property of the bucket it is used on. This allows us to gracefully add support for new hash functions without starting from scatch. Signed-off-by: Sage Weil diff --git a/fs/ceph/crush/crush.h b/fs/ceph/crush/crush.h index 92c6b3c..dcd7e75 100644 --- a/fs/ceph/crush/crush.h +++ b/fs/ceph/crush/crush.h @@ -102,7 +102,8 @@ extern const char *crush_bucket_alg_name(int alg); struct crush_bucket { __s32 id; /* this'll be negative */ __u16 type; /* non-zero; type=0 is reserved for devices */ - __u16 alg; /* one of CRUSH_BUCKET_* */ + __u8 alg; /* one of CRUSH_BUCKET_* */ + __u8 hash; /* which hash function to use, CRUSH_HASH_* */ __u32 weight; /* 16-bit fixed point */ __u32 size; /* num items */ __s32 *items; diff --git a/fs/ceph/crush/hash.c b/fs/ceph/crush/hash.c index b438c5d..5873aed 100644 --- a/fs/ceph/crush/hash.c +++ b/fs/ceph/crush/hash.c @@ -1,5 +1,6 @@ #include +#include "hash.h" /* * Robert Jenkins' function for mixing 32-bit values @@ -20,7 +21,7 @@ #define crush_hash_seed 1315423911 -__u32 crush_hash32(__u32 a) +static __u32 crush_hash32_rjenkins1(__u32 a) { __u32 hash = crush_hash_seed ^ a; __u32 b = a; @@ -31,7 +32,7 @@ __u32 crush_hash32(__u32 a) return hash; } -__u32 crush_hash32_2(__u32 a, __u32 b) +static __u32 crush_hash32_rjenkins1_2(__u32 a, __u32 b) { __u32 hash = crush_hash_seed ^ a ^ b; __u32 x = 231232; @@ -42,7 +43,7 @@ __u32 crush_hash32_2(__u32 a, __u32 b) return hash; } -__u32 crush_hash32_3(__u32 a, __u32 b, __u32 c) +static __u32 crush_hash32_rjenkins1_3(__u32 a, __u32 b, __u32 c) { __u32 hash = crush_hash_seed ^ a ^ b ^ c; __u32 x = 231232; @@ -55,7 +56,7 @@ __u32 crush_hash32_3(__u32 a, __u32 b, __u32 c) return hash; } -__u32 crush_hash32_4(__u32 a, __u32 b, __u32 c, __u32 d) +static __u32 crush_hash32_rjenkins1_4(__u32 a, __u32 b, __u32 c, __u32 d) { __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d; __u32 x = 231232; @@ -69,7 +70,8 @@ __u32 crush_hash32_4(__u32 a, __u32 b, __u32 c, __u32 d) return hash; } -__u32 crush_hash32_5(__u32 a, __u32 b, __u32 c, __u32 d, __u32 e) +static __u32 crush_hash32_rjenkins1_5(__u32 a, __u32 b, __u32 c, __u32 d, + __u32 e) { __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d ^ e; __u32 x = 231232; @@ -84,3 +86,64 @@ __u32 crush_hash32_5(__u32 a, __u32 b, __u32 c, __u32 d, __u32 e) crush_hashmix(y, e, hash); return hash; } + + +__u32 crush_hash32(int type, __u32 a) +{ + switch (type) { + case CRUSH_HASH_RJENKINS1: + return crush_hash32_rjenkins1(a); + default: + return 0; + } +} + +__u32 crush_hash32_2(int type, __u32 a, __u32 b) +{ + switch (type) { + case CRUSH_HASH_RJENKINS1: + return crush_hash32_rjenkins1_2(a, b); + default: + return 0; + } +} + +__u32 crush_hash32_3(int type, __u32 a, __u32 b, __u32 c) +{ + switch (type) { + case CRUSH_HASH_RJENKINS1: + return crush_hash32_rjenkins1_3(a, b, c); + default: + return 0; + } +} + +__u32 crush_hash32_4(int type, __u32 a, __u32 b, __u32 c, __u32 d) +{ + switch (type) { + case CRUSH_HASH_RJENKINS1: + return crush_hash32_rjenkins1_4(a, b, c, d); + default: + return 0; + } +} + +__u32 crush_hash32_5(int type, __u32 a, __u32 b, __u32 c, __u32 d, __u32 e) +{ + switch (type) { + case CRUSH_HASH_RJENKINS1: + return crush_hash32_rjenkins1_5(a, b, c, d, e); + default: + return 0; + } +} + +const char *crush_hash_name(int type) +{ + switch (type) { + case CRUSH_HASH_RJENKINS1: + return "rjenkins1"; + default: + return "unknown"; + } +} diff --git a/fs/ceph/crush/hash.h b/fs/ceph/crush/hash.h index 9ce89f8..ff48e110 100644 --- a/fs/ceph/crush/hash.h +++ b/fs/ceph/crush/hash.h @@ -1,12 +1,17 @@ #ifndef _CRUSH_HASH_H #define _CRUSH_HASH_H -extern __u32 crush_hash32(__u32 a); -extern __u32 crush_hash32_2(__u32 a, __u32 b); -extern __u32 crush_hash32_3(__u32 a, __u32 b, __u32 c); -extern __u32 crush_hash32_4(__u32 a, __u32 b, __u32 c, - __u32 d); -extern __u32 crush_hash32_5(__u32 a, __u32 b, __u32 c, - __u32 d, __u32 e); +#define CRUSH_HASH_RJENKINS1 0 + +#define CRUSH_HASH_DEFAULT CRUSH_HASH_RJENKINS1 + +extern const char *crush_hash_name(int type); + +extern __u32 crush_hash32(int type, __u32 a); +extern __u32 crush_hash32_2(int type, __u32 a, __u32 b); +extern __u32 crush_hash32_3(int type, __u32 a, __u32 b, __u32 c); +extern __u32 crush_hash32_4(int type, __u32 a, __u32 b, __u32 c, __u32 d); +extern __u32 crush_hash32_5(int type, __u32 a, __u32 b, __u32 c, __u32 d, + __u32 e); #endif diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c index 54f3f40..2523d44 100644 --- a/fs/ceph/crush/mapper.c +++ b/fs/ceph/crush/mapper.c @@ -78,7 +78,7 @@ static int bucket_perm_choose(struct crush_bucket *bucket, /* optimize common r=0 case */ if (pr == 0) { - s = crush_hash32_3(x, bucket->id, 0) % + s = crush_hash32_3(bucket->hash, x, bucket->id, 0) % bucket->size; bucket->perm[0] = s; bucket->perm_n = 0xffff; /* magic value, see below */ @@ -103,7 +103,7 @@ static int bucket_perm_choose(struct crush_bucket *bucket, unsigned p = bucket->perm_n; /* no point in swapping the final entry */ if (p < bucket->size - 1) { - i = crush_hash32_3(x, bucket->id, p) % + i = crush_hash32_3(bucket->hash, x, bucket->id, p) % (bucket->size - p); if (i) { unsigned t = bucket->perm[p + i]; @@ -138,8 +138,8 @@ static int bucket_list_choose(struct crush_bucket_list *bucket, int i; for (i = bucket->h.size-1; i >= 0; i--) { - __u64 w = crush_hash32_4(x, bucket->h.items[i], r, - bucket->h.id); + __u64 w = crush_hash32_4(bucket->h.hash,x, bucket->h.items[i], + r, bucket->h.id); w &= 0xffff; dprintk("list_choose i=%d x=%d r=%d item %d weight %x " "sw %x rand %llx", @@ -198,7 +198,8 @@ static int bucket_tree_choose(struct crush_bucket_tree *bucket, while (!terminal(n)) { /* pick point in [0, w) */ w = bucket->node_weights[n]; - t = (__u64)crush_hash32_4(x, n, r, bucket->h.id) * (__u64)w; + t = (__u64)crush_hash32_4(bucket->h.hash, x, n, r, + bucket->h.id) * (__u64)w; t = t >> 32; /* descend to the left or right? */ @@ -224,7 +225,7 @@ static int bucket_straw_choose(struct crush_bucket_straw *bucket, __u64 draw; for (i = 0; i < bucket->h.size; i++) { - draw = crush_hash32_3(x, bucket->h.items[i], r); + draw = crush_hash32_3(bucket->h.hash, x, bucket->h.items[i], r); draw &= 0xffff; draw *= bucket->straws[i]; if (i == 0 || draw > high_draw) { @@ -267,7 +268,8 @@ static int is_out(struct crush_map *map, __u32 *weight, int item, int x) return 0; if (weight[item] == 0) return 1; - if ((crush_hash32_2(x, item) & 0xffff) < weight[item]) + if ((crush_hash32_2(CRUSH_HASH_RJENKINS1, x, item) & 0xffff) + < weight[item]) return 0; return 1; } diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 6847827..8c994c7 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -210,7 +210,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end) ceph_decode_need(p, end, 4*sizeof(u32), bad); b->id = ceph_decode_32(p); b->type = ceph_decode_16(p); - b->alg = ceph_decode_16(p); + b->alg = ceph_decode_8(p); + b->hash = ceph_decode_8(p); b->weight = ceph_decode_32(p); b->size = ceph_decode_32(p); -- cgit v0.10.2 From 685f9a5d14194fc35db73e5e7370740ccc14b64a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 9 Nov 2009 12:05:48 -0800 Subject: ceph: do not confuse stale and dead (unreconnected) caps We were using the cap_gen to track both stale caps (caps that timed out due to temporarily losing touch with the mds) and dead caps that did not reconnect after an MDS failure. Introduce a recon_gen counter to track reconnections to restarted MDSs and kill dead caps based on that instead. Rename gen to cap_gen while we're at it to make it more clear which is which. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 8b863db..775e6f6 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -609,7 +609,8 @@ retry: cap->seq = seq; cap->issue_seq = seq; cap->mseq = mseq; - cap->gen = session->s_cap_gen; + cap->cap_gen = session->s_cap_gen; + cap->recon_gen = session->s_recon_gen; if (fmode >= 0) __ceph_get_fmode(ci, fmode); @@ -626,17 +627,25 @@ retry: static int __cap_is_valid(struct ceph_cap *cap) { unsigned long ttl; - u32 gen; + u32 gen, recon_gen; spin_lock(&cap->session->s_cap_lock); gen = cap->session->s_cap_gen; + recon_gen = cap->session->s_recon_gen; ttl = cap->session->s_cap_ttl; spin_unlock(&cap->session->s_cap_lock); - if (cap->gen < gen || time_after_eq(jiffies, ttl)) { + if (cap->recon_gen != recon_gen) { + dout("__cap_is_valid %p cap %p issued %s " + "but DEAD (recon_gen %u vs %u)\n", &cap->ci->vfs_inode, + cap, ceph_cap_string(cap->issued), cap->recon_gen, + recon_gen); + return 0; + } + if (cap->cap_gen < gen || time_after_eq(jiffies, ttl)) { dout("__cap_is_valid %p cap %p issued %s " "but STALE (gen %u vs %u)\n", &cap->ci->vfs_inode, - cap, ceph_cap_string(cap->issued), cap->gen, gen); + cap, ceph_cap_string(cap->issued), cap->cap_gen, gen); return 0; } @@ -2203,7 +2212,8 @@ restart: issued = __ceph_caps_issued(ci, &implemented); issued |= implemented | __ceph_caps_dirty(ci); - cap->gen = session->s_cap_gen; + cap->cap_gen = session->s_cap_gen; + cap->recon_gen = session->s_recon_gen; __check_cap_issue(ci, cap, newcaps); diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 210cb66..828417a 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -329,6 +329,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, ceph_con_open(&s->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); spin_lock_init(&s->s_cap_lock); + s->s_recon_gen = 0; s->s_cap_gen = 0; s->s_cap_ttl = 0; s->s_renew_requested = 0; @@ -738,10 +739,11 @@ static int wake_up_session_cb(struct inode *inode, struct ceph_cap *cap, struct ceph_mds_session *session = arg; spin_lock(&inode->i_lock); - if (cap->gen != session->s_cap_gen) { + if (cap->recon_gen != session->s_recon_gen) { pr_err("failed reconnect %p %llx.%llx cap %p " - "(gen %d < session %d)\n", inode, ceph_vinop(inode), - cap, cap->gen, session->s_cap_gen); + "(recon_gen %d < session %d)\n", inode, + ceph_vinop(inode), cap, + cap->recon_gen, session->s_recon_gen); __ceph_remove_cap(cap, NULL); } wake_up(&ceph_inode(inode)->i_cap_wq); @@ -2050,6 +2052,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, int mds) session->s_state = CEPH_MDS_SESSION_RECONNECTING; session->s_seq = 0; + session->s_recon_gen++; ceph_con_open(&session->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index f566e9c..c0846b1 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -98,6 +98,8 @@ struct ceph_mds_session { u64 s_seq; /* incoming msg seq # */ struct mutex s_mutex; /* serialize session messages */ + int s_recon_gen; /* inc on reconnect to recovered mds */ + struct ceph_connection s_con; /* protected by s_cap_lock */ diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 05947b9..2579355 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -169,7 +169,9 @@ struct ceph_cap { int issued; /* latest, from the mds */ int implemented; /* implemented superset of issued (for revocation) */ int mds_wanted; - u32 seq, issue_seq, mseq, gen; + u32 seq, issue_seq, mseq; + u32 cap_gen; /* active/stale cycle */ + u32 recon_gen; /* mds restart reconnect cycle */ unsigned long last_used; struct list_head caps_item; }; -- cgit v0.10.2 From eed0ef2caf928327332da54d23579debe629d5bc Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 10 Nov 2009 14:34:36 -0800 Subject: ceph: separate banner and connect during handshake into distinct stages We need to make sure we only swab the address during the banner once. So break process_banner out of process_connect, and clean up the surrounding code so that these are distinct phases of the handshake. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 5cc3748..e389656 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -564,10 +564,26 @@ static void prepare_write_keepalive(struct ceph_connection *con) /* * We connected to a peer and are saying hello. */ -static void prepare_write_connect(struct ceph_messenger *msgr, - struct ceph_connection *con) +static void prepare_write_banner(struct ceph_messenger *msgr, + struct ceph_connection *con) { int len = strlen(CEPH_BANNER); + + con->out_kvec[0].iov_base = CEPH_BANNER; + con->out_kvec[0].iov_len = len; + con->out_kvec[1].iov_base = &msgr->my_enc_addr; + con->out_kvec[1].iov_len = sizeof(msgr->my_enc_addr); + con->out_kvec_left = 2; + con->out_kvec_bytes = len + sizeof(msgr->my_enc_addr); + con->out_kvec_cur = con->out_kvec; + con->out_more = 0; + set_bit(WRITE_PENDING, &con->state); +} + +static void prepare_write_connect(struct ceph_messenger *msgr, + struct ceph_connection *con, + int after_banner) +{ unsigned global_seq = get_global_seq(con->msgr, 0); int proto; @@ -595,32 +611,14 @@ static void prepare_write_connect(struct ceph_messenger *msgr, if (test_bit(LOSSYTX, &con->state)) con->out_connect.flags = CEPH_MSG_CONNECT_LOSSY; - con->out_kvec[0].iov_base = CEPH_BANNER; - con->out_kvec[0].iov_len = len; - con->out_kvec[1].iov_base = &msgr->my_enc_addr; - con->out_kvec[1].iov_len = sizeof(msgr->my_enc_addr); - con->out_kvec[2].iov_base = &con->out_connect; - con->out_kvec[2].iov_len = sizeof(con->out_connect); - con->out_kvec_left = 3; - con->out_kvec_bytes = len + sizeof(msgr->my_enc_addr) + - sizeof(con->out_connect); - con->out_kvec_cur = con->out_kvec; - con->out_more = 0; - set_bit(WRITE_PENDING, &con->state); -} - -static void prepare_write_connect_retry(struct ceph_messenger *msgr, - struct ceph_connection *con) -{ - dout("prepare_write_connect_retry %p\n", con); - con->out_connect.connect_seq = cpu_to_le32(con->connect_seq); - con->out_connect.global_seq = - cpu_to_le32(get_global_seq(con->msgr, 0)); - - con->out_kvec[0].iov_base = &con->out_connect; - con->out_kvec[0].iov_len = sizeof(con->out_connect); - con->out_kvec_left = 1; - con->out_kvec_bytes = sizeof(con->out_connect); + if (!after_banner) { + con->out_kvec_left = 0; + con->out_kvec_bytes = 0; + } + con->out_kvec[con->out_kvec_left].iov_base = &con->out_connect; + con->out_kvec[con->out_kvec_left].iov_len = sizeof(con->out_connect); + con->out_kvec_left++; + con->out_kvec_bytes += sizeof(con->out_connect); con->out_kvec_cur = con->out_kvec; con->out_more = 0; set_bit(WRITE_PENDING, &con->state); @@ -778,6 +776,12 @@ out: /* * Prepare to read connection handshake, or an ack. */ +static void prepare_read_banner(struct ceph_connection *con) +{ + dout("prepare_read_banner %p\n", con); + con->in_base_pos = 0; +} + static void prepare_read_connect(struct ceph_connection *con) { dout("prepare_read_connect %p\n", con); @@ -829,11 +833,11 @@ static int read_partial(struct ceph_connection *con, /* * Read all or part of the connect-side handshake on a new connection */ -static int read_partial_connect(struct ceph_connection *con) +static int read_partial_banner(struct ceph_connection *con) { int ret, to = 0; - dout("read_partial_connect %p at %d\n", con, con->in_base_pos); + dout("read_partial_banner %p at %d\n", con, con->in_base_pos); /* peer's banner */ ret = read_partial(con, &to, strlen(CEPH_BANNER), con->in_banner); @@ -847,6 +851,16 @@ static int read_partial_connect(struct ceph_connection *con) &con->peer_addr_for_me); if (ret <= 0) goto out; +out: + return ret; +} + +static int read_partial_connect(struct ceph_connection *con) +{ + int ret, to = 0; + + dout("read_partial_connect %p at %d\n", con, con->in_base_pos); + ret = read_partial(con, &to, sizeof(con->in_reply), &con->in_reply); if (ret <= 0) goto out; @@ -856,6 +870,7 @@ static int read_partial_connect(struct ceph_connection *con) le32_to_cpu(con->in_reply.global_seq)); out: return ret; + } /* @@ -976,9 +991,9 @@ bad: return -EINVAL; } -static int process_connect(struct ceph_connection *con) +static int process_banner(struct ceph_connection *con) { - dout("process_connect on %p tag %d\n", con, (int)con->in_tag); + dout("process_banner on %p\n", con); if (verify_hello(con) < 0) return -1; @@ -1016,10 +1031,19 @@ static int process_connect(struct ceph_connection *con) sizeof(con->peer_addr_for_me.in_addr)); addr_set_port(&con->msgr->inst.addr.in_addr, port); encode_my_addr(con->msgr); - dout("process_connect learned my addr is %s\n", + dout("process_banner learned my addr is %s\n", pr_addr(&con->msgr->inst.addr.in_addr)); } + set_bit(NEGOTIATING, &con->state); + prepare_read_connect(con); + return 0; +} + +static int process_connect(struct ceph_connection *con) +{ + dout("process_connect on %p tag %d\n", con, (int)con->in_tag); + switch (con->in_reply.tag) { case CEPH_MSGR_TAG_BADPROTOVER: dout("process_connect got BADPROTOVER my %d != their %d\n", @@ -1053,7 +1077,7 @@ static int process_connect(struct ceph_connection *con) ENTITY_NAME(con->peer_name), pr_addr(&con->peer_addr.in_addr)); reset_connection(con); - prepare_write_connect_retry(con->msgr, con); + prepare_write_connect(con->msgr, con, 0); prepare_read_connect(con); /* Tell ceph about it. */ @@ -1071,7 +1095,7 @@ static int process_connect(struct ceph_connection *con) le32_to_cpu(con->out_connect.connect_seq), le32_to_cpu(con->in_connect.connect_seq)); con->connect_seq = le32_to_cpu(con->in_connect.connect_seq); - prepare_write_connect_retry(con->msgr, con); + prepare_write_connect(con->msgr, con, 0); prepare_read_connect(con); break; @@ -1080,19 +1104,17 @@ static int process_connect(struct ceph_connection *con) * If we sent a smaller global_seq than the peer has, try * again with a larger value. */ - dout("process_connect got RETRY_GLOBAL my %u, peer_gseq = %u\n", + dout("process_connect got RETRY_GLOBAL my %u peer_gseq %u\n", con->peer_global_seq, le32_to_cpu(con->in_connect.global_seq)); get_global_seq(con->msgr, le32_to_cpu(con->in_connect.global_seq)); - prepare_write_connect_retry(con->msgr, con); + prepare_write_connect(con->msgr, con, 0); prepare_read_connect(con); break; case CEPH_MSGR_TAG_READY: clear_bit(CONNECTING, &con->state); - if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY) - set_bit(LOSSYRX, &con->state); con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq); con->connect_seq++; dout("process_connect got READY gseq %d cseq %d (%d)\n", @@ -1420,9 +1442,11 @@ more: if (test_and_clear_bit(STANDBY, &con->state)) con->connect_seq++; - prepare_write_connect(msgr, con); - prepare_read_connect(con); + prepare_write_banner(msgr, con); + prepare_write_connect(msgr, con, 1); + prepare_read_banner(con); set_bit(CONNECTING, &con->state); + clear_bit(NEGOTIATING, &con->state); con->in_tag = CEPH_MSGR_TAG_READY; dout("try_write initiating connect on %p new state %lu\n", @@ -1521,7 +1545,16 @@ more: dout("try_read tag %d in_base_pos %d\n", (int)con->in_tag, con->in_base_pos); if (test_bit(CONNECTING, &con->state)) { - dout("try_read connecting\n"); + if (!test_bit(NEGOTIATING, &con->state)) { + dout("try_read connecting\n"); + ret = read_partial_banner(con); + if (ret <= 0) + goto done; + if (process_banner(con) < 0) { + ret = -1; + goto out; + } + } ret = read_partial_connect(con); if (ret <= 0) goto done; diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index e016fa7..80f7e1e 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -104,8 +104,8 @@ struct ceph_msg_pos { * thread is currently opening, reading or writing data to the socket. */ #define LOSSYTX 0 /* we can close channel or drop messages on errors */ -#define LOSSYRX 1 /* peer may reset/drop messages */ -#define CONNECTING 2 +#define CONNECTING 1 +#define NEGOTIATING 2 #define KEEPALIVE_PENDING 3 #define WRITE_PENDING 4 /* we have data ready to send */ #define QUEUED 5 /* there is work queued on this connection */ -- cgit v0.10.2 From cdac830313fa6bf2831693af80fefe4aaac11b7d Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 10 Nov 2009 16:02:23 -0800 Subject: ceph: remove recon_gen logic We don't get an explicit affirmative confirmation that our caps reconnect, nor do we necessarily want to pay that cost. So, take all this code out for now. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 775e6f6..d8132b6 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -610,7 +610,6 @@ retry: cap->issue_seq = seq; cap->mseq = mseq; cap->cap_gen = session->s_cap_gen; - cap->recon_gen = session->s_recon_gen; if (fmode >= 0) __ceph_get_fmode(ci, fmode); @@ -627,21 +626,13 @@ retry: static int __cap_is_valid(struct ceph_cap *cap) { unsigned long ttl; - u32 gen, recon_gen; + u32 gen; spin_lock(&cap->session->s_cap_lock); gen = cap->session->s_cap_gen; - recon_gen = cap->session->s_recon_gen; ttl = cap->session->s_cap_ttl; spin_unlock(&cap->session->s_cap_lock); - if (cap->recon_gen != recon_gen) { - dout("__cap_is_valid %p cap %p issued %s " - "but DEAD (recon_gen %u vs %u)\n", &cap->ci->vfs_inode, - cap, ceph_cap_string(cap->issued), cap->recon_gen, - recon_gen); - return 0; - } if (cap->cap_gen < gen || time_after_eq(jiffies, ttl)) { dout("__cap_is_valid %p cap %p issued %s " "but STALE (gen %u vs %u)\n", &cap->ci->vfs_inode, @@ -2213,7 +2204,6 @@ restart: issued |= implemented | __ceph_caps_dirty(ci); cap->cap_gen = session->s_cap_gen; - cap->recon_gen = session->s_recon_gen; __check_cap_issue(ci, cap, newcaps); diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 828417a..aad10d9 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -329,7 +329,6 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, ceph_con_open(&s->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); spin_lock_init(&s->s_cap_lock); - s->s_recon_gen = 0; s->s_cap_gen = 0; s->s_cap_ttl = 0; s->s_renew_requested = 0; @@ -736,25 +735,14 @@ static void remove_session_caps(struct ceph_mds_session *session) static int wake_up_session_cb(struct inode *inode, struct ceph_cap *cap, void *arg) { - struct ceph_mds_session *session = arg; - - spin_lock(&inode->i_lock); - if (cap->recon_gen != session->s_recon_gen) { - pr_err("failed reconnect %p %llx.%llx cap %p " - "(recon_gen %d < session %d)\n", inode, - ceph_vinop(inode), cap, - cap->recon_gen, session->s_recon_gen); - __ceph_remove_cap(cap, NULL); - } wake_up(&ceph_inode(inode)->i_cap_wq); - spin_unlock(&inode->i_lock); return 0; } static void wake_up_session_caps(struct ceph_mds_session *session) { dout("wake_up_session_caps %p mds%d\n", session, session->s_mds); - iterate_session_caps(session, wake_up_session_cb, session); + iterate_session_caps(session, wake_up_session_cb, NULL); } /* @@ -2052,7 +2040,6 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, int mds) session->s_state = CEPH_MDS_SESSION_RECONNECTING; session->s_seq = 0; - session->s_recon_gen++; ceph_con_open(&session->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index c0846b1..f566e9c 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -98,8 +98,6 @@ struct ceph_mds_session { u64 s_seq; /* incoming msg seq # */ struct mutex s_mutex; /* serialize session messages */ - int s_recon_gen; /* inc on reconnect to recovered mds */ - struct ceph_connection s_con; /* protected by s_cap_lock */ diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 2579355..06b62c02 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -171,7 +171,6 @@ struct ceph_cap { int mds_wanted; u32 seq, issue_seq, mseq; u32 cap_gen; /* active/stale cycle */ - u32 recon_gen; /* mds restart reconnect cycle */ unsigned long last_used; struct list_head caps_item; }; -- cgit v0.10.2 From 09b8a7d2af83ae96dc052f9708e50140d06a9b6c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 11 Nov 2009 15:21:27 -0800 Subject: ceph: exclude snapdir from readdir results It was hidden from sync readdir, but not the cached dcache version. Signed-off-by: Sage Weil diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 4f74679..32ef543 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -131,6 +131,7 @@ more: goto out_unlock; } if (!d_unhashed(dentry) && dentry->d_inode && + ceph_snap(dentry->d_inode) != CEPH_SNAPDIR && filp->f_pos <= di->offset) break; dout(" skipping %p %.*s at %llu (%llu)%s%s\n", dentry, -- cgit v0.10.2 From b377ff13b31778c19203f3089d14080beb40a692 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 11 Nov 2009 15:22:37 -0800 Subject: ceph: initialize i_size/i_rbytes on snapdir Signed-off-by: Sage Weil diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 036873c..074ee42 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -62,6 +62,7 @@ struct inode *ceph_get_snapdir(struct inode *parent) .snap = CEPH_SNAPDIR, }; struct inode *inode = ceph_get_inode(parent->i_sb, vino); + struct ceph_inode_info *ci = ceph_inode(inode); BUG_ON(!S_ISDIR(parent->i_mode)); if (IS_ERR(inode)) @@ -71,7 +72,8 @@ struct inode *ceph_get_snapdir(struct inode *parent) inode->i_gid = parent->i_gid; inode->i_op = &ceph_dir_iops; inode->i_fop = &ceph_dir_fops; - ceph_inode(inode)->i_snap_caps = CEPH_CAP_PIN; /* so we can open */ + ci->i_snap_caps = CEPH_CAP_PIN; /* so we can open */ + ci->i_rbytes = 0; return inode; } -- cgit v0.10.2 From fef320ff8887c702cde7ca6b8dbfff3a341d49fe Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 11 Nov 2009 15:50:12 -0800 Subject: ceph: pr_info when mds reconnect completes This helps the user know what's going on during the (involved) reconnect process. They already see when the mds fails and reconnect starts. Signed-off-by: Sage Weil diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index aad10d9..44cac57 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2234,6 +2234,7 @@ static void check_new_map(struct ceph_mds_client *mdsc, */ if (oldstate < CEPH_MDS_STATE_ACTIVE && newstate >= CEPH_MDS_STATE_ACTIVE) { + pr_info("mds%d reconnect completed\n", s->s_mds); kick_requests(mdsc, i, 1); ceph_kick_flushing_caps(mdsc, s); } -- cgit v0.10.2 From 039934b895c89c2bb40aa5132efe00e60b70efca Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 12 Nov 2009 15:05:52 -0800 Subject: ceph: build cleanly without CONFIG_DEBUG_FS Signed-off-by: Sage Weil diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 9edbad3..9b20206 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -8,6 +8,8 @@ #include "super.h" #include "mds_client.h" +#ifdef CONFIG_DEBUG_FS + /* * Implement /sys/kernel/debug/ceph fun * @@ -423,3 +425,24 @@ void ceph_debugfs_client_cleanup(struct ceph_client *client) debugfs_remove(client->debugfs_dir); } +#else // CONFIG_DEBUG_FS + +int __init ceph_debugfs_init(void) +{ + return 0; +} + +void ceph_debugfs_cleanup(void) +{ +} + +int ceph_debugfs_client_init(struct ceph_client *client) +{ + return 0; +} + +void ceph_debugfs_client_cleanup(struct ceph_client *client) +{ +} + +#endif // CONFIG_DEBUG_FS diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index f566e9c..0751b82 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -256,7 +256,9 @@ struct ceph_mds_client { spinlock_t cap_dirty_lock; /* protects above items */ wait_queue_head_t cap_flushing_wq; +#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_file; +#endif spinlock_t dentry_lru_lock; struct list_head dentry_lru; diff --git a/fs/ceph/mon_client.h b/fs/ceph/mon_client.h index 5258c56..9f6db45 100644 --- a/fs/ceph/mon_client.h +++ b/fs/ceph/mon_client.h @@ -78,7 +78,9 @@ struct ceph_mon_client { int want_next_osdmap; /* 1 = want, 2 = want+asked */ u32 have_osdmap, have_mdsmap; +#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_file; +#endif }; extern struct ceph_monmap *ceph_monmap_decode(void *p, void *end); diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index 9a4addf..766c8dc 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -83,7 +83,9 @@ struct ceph_osd_client { struct rb_root requests; /* pending requests */ int num_requests; struct delayed_work timeout_work; +#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_file; +#endif mempool_t *req_mempool; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 06b62c02..8aa1ffb 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -112,9 +112,11 @@ static inline unsigned long time_sub(unsigned long a, unsigned long b) */ struct ceph_client { __s64 whoami; /* my client number */ +#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_monmap; struct dentry *debugfs_mdsmap, *debugfs_osdmap; struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps; +#endif struct mutex mount_mutex; /* serialize mount attempts */ struct ceph_mount_args *mount_args; -- cgit v0.10.2 From 11ea8eda064aa4dc6e44a6dade1891b69ebd5255 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 12 Nov 2009 15:07:24 -0800 Subject: ceph: fix page invalidation deadlock We occasionally want to make a best-effort attempt to invalidate cache pages without fear of blocking. If this fails, we fall back to an async invalidate in another thread. Use invalidate_mapping_pages instead of invalidate_inode_page2, as that will skip locked pages, and not deadlock. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index d8132b6..9dd1106 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1440,7 +1440,7 @@ retry_locked: dout("check_caps trying to invalidate on %p\n", inode); spin_unlock(&inode->i_lock); - ret = invalidate_inode_pages2(&inode->i_data); + ret = invalidate_mapping_pages(&inode->i_data, 0, -1); spin_lock(&inode->i_lock); if (ret == 0 && invalidating_gen == ci->i_rdcache_gen) { /* success. */ @@ -2180,7 +2180,7 @@ restart: spin_unlock(&inode->i_lock); tried_invalidate = 1; - ret = invalidate_inode_pages2(&inode->i_data); + ret = invalidate_mapping_pages(&inode->i_data, 0, -1); spin_lock(&inode->i_lock); if (ret < 0) { /* there were locked pages.. invalidate later -- cgit v0.10.2 From 42ce56e50d948fc7c1c8c3a749ee5a21a7e134f6 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 18 Nov 2009 11:22:36 -0800 Subject: ceph: remove bad calls to ceph_con_shutdown We want to ceph_con_close when we're done with the connection, before the ref count reaches 0. Once it does, do not call ceph_con_shutdown, as that takes the con mutex and may sleep, and besides that is unnecessary. Signed-off-by: Sage Weil diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 44cac57..fdecf99 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -274,10 +274,8 @@ void ceph_put_mds_session(struct ceph_mds_session *s) { dout("mdsc put_session %p %d -> %d\n", s, atomic_read(&s->s_ref), atomic_read(&s->s_ref)-1); - if (atomic_dec_and_test(&s->s_ref)) { - ceph_con_shutdown(&s->s_con); + if (atomic_dec_and_test(&s->s_ref)) kfree(s); - } } /* @@ -326,7 +324,6 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_con.ops = &mds_con_ops; s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; s->s_con.peer_name.num = cpu_to_le64(mds); - ceph_con_open(&s->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); spin_lock_init(&s->s_cap_lock); s->s_cap_gen = 0; @@ -352,7 +349,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, dout("register_session realloc to %d\n", newmax); sa = kcalloc(newmax, sizeof(void *), GFP_NOFS); if (sa == NULL) - return ERR_PTR(-ENOMEM); + goto fail_realloc; if (mdsc->sessions) { memcpy(sa, mdsc->sessions, mdsc->max_sessions * sizeof(void *)); @@ -363,17 +360,26 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, } mdsc->sessions[mds] = s; atomic_inc(&s->s_ref); /* one ref to sessions[], one to caller */ + + ceph_con_open(&s->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); + return s; + +fail_realloc: + kfree(s); + return ERR_PTR(-ENOMEM); } /* * called under mdsc->mutex */ -static void unregister_session(struct ceph_mds_client *mdsc, int mds) +static void unregister_session(struct ceph_mds_client *mdsc, + struct ceph_mds_session *s) { - dout("unregister_session mds%d %p\n", mds, mdsc->sessions[mds]); - ceph_put_mds_session(mdsc->sessions[mds]); - mdsc->sessions[mds] = NULL; + dout("unregister_session mds%d %p\n", s->s_mds, s); + mdsc->sessions[s->s_mds] = NULL; + ceph_con_close(&s->s_con); + ceph_put_mds_session(s); } /* @@ -1870,7 +1876,7 @@ static void handle_session(struct ceph_mds_session *session, break; case CEPH_SESSION_CLOSE: - unregister_session(mdsc, mds); + unregister_session(mdsc, session); remove_session_caps(session); wake = 1; /* for good measure */ complete(&mdsc->session_close_waiters); @@ -2199,7 +2205,7 @@ static void check_new_map(struct ceph_mds_client *mdsc, /* the session never opened, just close it * out now */ __wake_requests(mdsc, &s->s_waiting); - unregister_session(mdsc, i); + unregister_session(mdsc, s); } else { /* just close it */ mutex_unlock(&mdsc->mutex); @@ -2724,7 +2730,7 @@ void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc) for (i = 0; i < mdsc->max_sessions; i++) { if (mdsc->sessions[i]) { session = get_session(mdsc->sessions[i]); - unregister_session(mdsc, i); + unregister_session(mdsc, session); mutex_unlock(&mdsc->mutex); mutex_lock(&session->s_mutex); remove_session_caps(session); diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index 80f7e1e..4bd85c3 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -212,7 +212,6 @@ extern void ceph_messenger_destroy(struct ceph_messenger *); extern void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con); -extern void ceph_con_shutdown(struct ceph_connection *con); extern void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr); extern void ceph_con_close(struct ceph_connection *con); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 7db14ba..bcb9fe6 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -350,10 +350,8 @@ static void put_osd(struct ceph_osd *osd) { dout("put_osd %p %d -> %d\n", osd, atomic_read(&osd->o_ref), atomic_read(&osd->o_ref) - 1); - if (atomic_dec_and_test(&osd->o_ref)) { - ceph_con_shutdown(&osd->o_con); + if (atomic_dec_and_test(&osd->o_ref)) kfree(osd); - } } /* -- cgit v0.10.2 From 71ececdacae24be333c534869cb1b06357f0e215 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 18 Nov 2009 11:27:06 -0800 Subject: ceph: remove unnecessary ceph_con_shutdown We require that ceph_con_close be called before we drop the connection, so this is unneeded. Just BUG if con->sock != NULL. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index e389656..d8a6a56 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -340,17 +340,6 @@ void ceph_con_close(struct ceph_connection *con) } /* - * clean up connection state - */ -void ceph_con_shutdown(struct ceph_connection *con) -{ - dout("con_shutdown %p\n", con); - reset_connection(con); - set_bit(DEAD, &con->state); - con_close_socket(con); /* silently ignore errors */ -} - -/* * Reopen a closed connection, with a new peer address. */ void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr) @@ -380,7 +369,7 @@ void ceph_con_put(struct ceph_connection *con) atomic_read(&con->nref), atomic_read(&con->nref) - 1); BUG_ON(atomic_read(&con->nref) == 0); if (atomic_dec_and_test(&con->nref)) { - ceph_con_shutdown(con); + BUG_ON(con->sock); kfree(con); } } -- cgit v0.10.2 From 5f44f142601bf94c448e2d463f0f18fd159da164 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 18 Nov 2009 14:52:18 -0800 Subject: ceph: handle errors during osd client init Unwind initializing if we get ENOMEM during client initialization. Signed-off-by: Sage Weil diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index fdecf99..69feeb1 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2552,7 +2552,7 @@ static void delayed_work(struct work_struct *work) } -void ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client) +int ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client) { mdsc->client = client; mutex_init(&mdsc->mutex); @@ -2582,6 +2582,7 @@ void ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client) init_waitqueue_head(&mdsc->cap_flushing_wq); spin_lock_init(&mdsc->dentry_lru_lock); INIT_LIST_HEAD(&mdsc->dentry_lru); + return 0; } /* diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 0751b82..7c43948 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -282,7 +282,7 @@ extern void ceph_put_mds_session(struct ceph_mds_session *s); extern int ceph_send_msg_mds(struct ceph_mds_client *mdsc, struct ceph_msg *msg, int mds); -extern void ceph_mdsc_init(struct ceph_mds_client *mdsc, +extern int ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client); extern void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc); extern void ceph_mdsc_stop(struct ceph_mds_client *mdsc); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index bcb9fe6..0a16c4f 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -1127,19 +1127,26 @@ int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client) osdc->num_requests = 0; INIT_DELAYED_WORK(&osdc->timeout_work, handle_timeout); + err = -ENOMEM; osdc->req_mempool = mempool_create_kmalloc_pool(10, sizeof(struct ceph_osd_request)); if (!osdc->req_mempool) - return -ENOMEM; + goto out; err = ceph_msgpool_init(&osdc->msgpool_op, 4096, 10, true); if (err < 0) - return -ENOMEM; + goto out_mempool; err = ceph_msgpool_init(&osdc->msgpool_op_reply, 512, 0, false); if (err < 0) - return -ENOMEM; - + goto out_msgpool; return 0; + +out_msgpool: + ceph_msgpool_destroy(&osdc->msgpool_op); +out_mempool: + mempool_destroy(osdc->req_mempool); +out: + return err; } void ceph_osdc_stop(struct ceph_osd_client *osdc) diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 1ac7b07..fe0a596 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -530,9 +530,13 @@ static struct ceph_client *ceph_create_client(struct ceph_mount_args *args) err = ceph_osdc_init(&client->osdc, client); if (err < 0) goto fail_monc; - ceph_mdsc_init(&client->mdsc, client); + err = ceph_mdsc_init(&client->mdsc, client); + if (err < 0) + goto fail_osdc; return client; +fail_osdc: + ceph_osdc_stop(&client->osdc); fail_monc: ceph_monc_stop(&client->monc); fail_trunc_wq: -- cgit v0.10.2 From 4e7a5dcd1bbab6560fbc8ada29a840e7a20ed7bc Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 18 Nov 2009 16:19:57 -0800 Subject: ceph: negotiate authentication protocol; implement AUTH_NONE protocol When we open a monitor session, we send an initial AUTH message listing the auth protocols we support, our entity name, and (possibly) a previously assigned global_id. The monitor chooses a protocol and responds with an initial message. Initially implement AUTH_NONE, a dummy protocol that provides no security, but works within the new framework. It generates 'authorizers' that are used when connecting to (mds, osd) services that simply state our entity name and global_id. This is a wire protocol change. Signed-off-by: Sage Weil diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile index bdd3e6f..827629c8 100644 --- a/fs/ceph/Makefile +++ b/fs/ceph/Makefile @@ -13,6 +13,7 @@ ceph-objs := super.o inode.o dir.o file.o addr.o ioctl.o \ mon_client.o \ osd_client.o osdmap.o crush/crush.o crush/mapper.o crush/hash.o \ debugfs.o \ + auth.o auth_none.o \ ceph_fs.o ceph_strings.o ceph_hash.o ceph_frag.o else diff --git a/fs/ceph/auth.c b/fs/ceph/auth.c new file mode 100644 index 0000000..c4d1eee --- /dev/null +++ b/fs/ceph/auth.c @@ -0,0 +1,220 @@ +#include "ceph_debug.h" + +#include +#include + +#include "types.h" +#include "auth_none.h" +#include "decode.h" +#include "super.h" + +#include "messenger.h" + +/* + * get protocol handler + */ +static u32 supported_protocols[] = { + CEPH_AUTH_NONE +}; + +int ceph_auth_init_protocol(struct ceph_auth_client *ac, int protocol) +{ + switch (protocol) { + case CEPH_AUTH_NONE: + return ceph_auth_none_init(ac); + default: + return -ENOENT; + } +} + +/* + * setup, teardown. + */ +struct ceph_auth_client *ceph_auth_init(const char *name, const char *secret) +{ + struct ceph_auth_client *ac; + int ret; + + dout("auth_init name '%s' secret '%s'\n", name, secret); + + ret = -ENOMEM; + ac = kzalloc(sizeof(*ac), GFP_NOFS); + if (!ac) + goto out; + + ac->negotiating = true; + if (name) + ac->name = name; + else + ac->name = CEPH_AUTH_NAME_DEFAULT; + dout("auth_init name %s secret %s\n", ac->name, secret); + ac->secret = secret; + return ac; + +out: + return ERR_PTR(ret); +} + +void ceph_auth_destroy(struct ceph_auth_client *ac) +{ + dout("auth_destroy %p\n", ac); + if (ac->ops) + ac->ops->destroy(ac); + kfree(ac); +} + +/* + * Reset occurs when reconnecting to the monitor. + */ +void ceph_auth_reset(struct ceph_auth_client *ac) +{ + dout("auth_reset %p\n", ac); + if (ac->ops && !ac->negotiating) + ac->ops->reset(ac); + ac->negotiating = true; +} + +int ceph_entity_name_encode(const char *name, void **p, void *end) +{ + int len = strlen(name); + + if (*p + 2*sizeof(u32) + len > end) + return -ERANGE; + ceph_encode_32(p, CEPH_ENTITY_TYPE_CLIENT); + ceph_encode_32(p, len); + ceph_encode_copy(p, name, len); + return 0; +} + +/* + * Initiate protocol negotiation with monitor. Include entity name + * and list supported protocols. + */ +int ceph_auth_build_hello(struct ceph_auth_client *ac, void *buf, size_t len) +{ + struct ceph_mon_request_header *monhdr = buf; + void *p = monhdr + 1, *end = buf + len, *lenp; + int i, num; + int ret; + + dout("auth_build_hello\n"); + monhdr->have_version = 0; + monhdr->session_mon = cpu_to_le16(-1); + monhdr->session_mon_tid = 0; + + ceph_encode_32(&p, 0); /* no protocol, yet */ + + lenp = p; + p += sizeof(u32); + + num = ARRAY_SIZE(supported_protocols); + ceph_encode_32(&p, num); + for (i = 0; i < num; i++) + ceph_encode_32(&p, supported_protocols[i]); + + ret = ceph_entity_name_encode(ac->name, &p, end); + if (ret < 0) + return ret; + ceph_decode_need(&p, end, sizeof(u64), bad); + ceph_encode_64(&p, ac->global_id); + + ceph_encode_32(&lenp, p - lenp - sizeof(u32)); + return p - buf; + +bad: + return -ERANGE; +} + +/* + * Handle auth message from monitor. + */ +int ceph_handle_auth_reply(struct ceph_auth_client *ac, + void *buf, size_t len, + void *reply_buf, size_t reply_len) +{ + void *p = buf; + void *end = buf + len; + int protocol; + s32 result; + u64 global_id; + void *payload, *payload_end; + int payload_len; + char *result_msg; + int result_msg_len; + int ret = -EINVAL; + + dout("handle_auth_reply %p %p\n", p, end); + ceph_decode_need(&p, end, sizeof(u32) * 3 + sizeof(u64), bad); + protocol = ceph_decode_32(&p); + result = ceph_decode_32(&p); + global_id = ceph_decode_64(&p); + payload_len = ceph_decode_32(&p); + payload = p; + p += payload_len; + ceph_decode_need(&p, end, sizeof(u32), bad); + result_msg_len = ceph_decode_32(&p); + result_msg = p; + p += result_msg_len; + if (p != end) + goto bad; + + dout(" result %d '%.*s' gid %llu len %d\n", result, result_msg_len, + result_msg, global_id, payload_len); + + payload_end = payload + payload_len; + + if (global_id && ac->global_id != global_id) { + dout(" set global_id %lld -> %lld\n", ac->global_id, global_id); + ac->global_id = global_id; + } + + if (ac->negotiating) { + /* set up (new) protocol handler? */ + if (ac->protocol && ac->protocol != protocol) { + ac->ops->destroy(ac); + ac->protocol = 0; + ac->ops = NULL; + } + if (ac->protocol != protocol) { + ret = ceph_auth_init_protocol(ac, protocol); + if (ret) { + pr_err("error %d on auth protocol %d init\n", + ret, protocol); + goto out; + } + } + } + + ret = ac->ops->handle_reply(ac, result, payload, payload_end); + if (ret == -EAGAIN) { + struct ceph_mon_request_header *monhdr = reply_buf; + void *p = reply_buf + 1; + void *end = reply_buf + reply_len; + + monhdr->have_version = 0; + monhdr->session_mon = cpu_to_le16(-1); + monhdr->session_mon_tid = 0; + + ceph_encode_32(&p, ac->protocol); + + ret = ac->ops->build_request(ac, p + sizeof(u32), end); + if (ret < 0) { + pr_err("error %d building request\n", ret); + goto out; + } + dout(" built request %d bytes\n", ret); + ceph_encode_32(&p, ret); + return p + ret - reply_buf; + } else if (ret) { + pr_err("authentication error %d\n", ret); + return ret; + } + return 0; + +bad: + pr_err("failed to decode auth msg\n"); +out: + return ret; +} + + diff --git a/fs/ceph/auth.h b/fs/ceph/auth.h new file mode 100644 index 0000000..4d8cdf6 --- /dev/null +++ b/fs/ceph/auth.h @@ -0,0 +1,77 @@ +#ifndef _FS_CEPH_AUTH_H +#define _FS_CEPH_AUTH_H + +#include "types.h" +#include "buffer.h" + +/* + * Abstract interface for communicating with the authenticate module. + * There is some handshake that takes place between us and the monitor + * to acquire the necessary keys. These are used to generate an + * 'authorizer' that we use when connecting to a service (mds, osd). + */ + +struct ceph_auth_client; +struct ceph_authorizer; + +struct ceph_auth_client_ops { + /* + * true if we are authenticated and can connect to + * services. + */ + int (*is_authenticated)(struct ceph_auth_client *ac); + + /* + * build requests and process replies during monitor + * handshake. if handle_reply returns -EAGAIN, we build + * another request. + */ + int (*build_request)(struct ceph_auth_client *ac, void *buf, void *end); + int (*handle_reply)(struct ceph_auth_client *ac, int result, + void *buf, void *end); + + /* + * Create authorizer for connecting to a service, and verify + * the response to authenticate the service. + */ + int (*create_authorizer)(struct ceph_auth_client *ac, int peer_type, + struct ceph_authorizer **a, + void **buf, size_t *len, + void **reply_buf, size_t *reply_len); + int (*verify_authorizer_reply)(struct ceph_auth_client *ac, + struct ceph_authorizer *a, size_t len); + void (*destroy_authorizer)(struct ceph_auth_client *ac, + struct ceph_authorizer *a); + + /* reset when we (re)connect to a monitor */ + void (*reset)(struct ceph_auth_client *ac); + + void (*destroy)(struct ceph_auth_client *ac); +}; + +struct ceph_auth_client { + u32 protocol; /* CEPH_AUTH_* */ + void *private; /* for use by protocol implementation */ + const struct ceph_auth_client_ops *ops; /* null iff protocol==0 */ + + bool negotiating; /* true if negotiating protocol */ + const char *name; /* entity name */ + u64 global_id; /* our unique id in system */ + const char *secret; /* our secret key */ + unsigned want_keys; /* which services we want */ +}; + +extern struct ceph_auth_client *ceph_auth_init(const char *name, + const char *secret); +extern void ceph_auth_destroy(struct ceph_auth_client *ac); + +extern void ceph_auth_reset(struct ceph_auth_client *ac); + +extern int ceph_auth_build_hello(struct ceph_auth_client *ac, + void *buf, size_t len); +extern int ceph_handle_auth_reply(struct ceph_auth_client *ac, + void *buf, size_t len, + void *reply_buf, size_t reply_len); +extern int ceph_entity_name_encode(const char *name, void **p, void *end); + +#endif diff --git a/fs/ceph/auth_none.c b/fs/ceph/auth_none.c new file mode 100644 index 0000000..631017e --- /dev/null +++ b/fs/ceph/auth_none.c @@ -0,0 +1,120 @@ + +#include "ceph_debug.h" + +#include +#include +#include + +#include "auth_none.h" +#include "auth.h" +#include "decode.h" + +static void reset(struct ceph_auth_client *ac) +{ + struct ceph_auth_none_info *xi = ac->private; + + xi->starting = true; + xi->built_authorizer = false; +} + +static void destroy(struct ceph_auth_client *ac) +{ + kfree(ac->private); + ac->private = NULL; +} + +static int is_authenticated(struct ceph_auth_client *ac) +{ + struct ceph_auth_none_info *xi = ac->private; + + return !xi->starting; +} + +/* + * the generic auth code decode the global_id, and we carry no actual + * authenticate state, so nothing happens here. + */ +static int handle_reply(struct ceph_auth_client *ac, int result, + void *buf, void *end) +{ + struct ceph_auth_none_info *xi = ac->private; + + xi->starting = false; + return result; +} + +/* + * build an 'authorizer' with our entity_name and global_id. we can + * reuse a single static copy since it is identical for all services + * we connect to. + */ +static int ceph_auth_none_create_authorizer( + struct ceph_auth_client *ac, int peer_type, + struct ceph_authorizer **a, + void **buf, size_t *len, + void **reply_buf, size_t *reply_len) +{ + struct ceph_auth_none_info *ai = ac->private; + struct ceph_none_authorizer *au = &ai->au; + void *p, *end; + int ret; + + if (!ai->built_authorizer) { + p = au->buf; + end = p + sizeof(au->buf); + ret = ceph_entity_name_encode(ac->name, &p, end - 8); + if (ret < 0) + goto bad; + ceph_decode_need(&p, end, sizeof(u64), bad2); + ceph_encode_64(&p, ac->global_id); + au->buf_len = p - (void *)au->buf; + ai->built_authorizer = true; + dout("built authorizer len %d\n", au->buf_len); + } + + *a = (struct ceph_authorizer *)au; + *buf = au->buf; + *len = au->buf_len; + *reply_buf = au->reply_buf; + *reply_len = sizeof(au->reply_buf); + return 0; + +bad2: + ret = -ERANGE; +bad: + return ret; +} + +static void ceph_auth_none_destroy_authorizer(struct ceph_auth_client *ac, + struct ceph_authorizer *a) +{ + /* nothing to do */ +} + +static const struct ceph_auth_client_ops ceph_auth_none_ops = { + .reset = reset, + .destroy = destroy, + .is_authenticated = is_authenticated, + .handle_reply = handle_reply, + .create_authorizer = ceph_auth_none_create_authorizer, + .destroy_authorizer = ceph_auth_none_destroy_authorizer, +}; + +int ceph_auth_none_init(struct ceph_auth_client *ac) +{ + struct ceph_auth_none_info *xi; + + dout("ceph_auth_none_init %p\n", ac); + xi = kzalloc(sizeof(*xi), GFP_NOFS); + if (!xi) + return -ENOMEM; + + xi->starting = true; + xi->built_authorizer = false; + + ac->protocol = CEPH_AUTH_NONE; + ac->private = xi; + ac->ops = &ceph_auth_none_ops; + return 0; +} + diff --git a/fs/ceph/auth_none.h b/fs/ceph/auth_none.h new file mode 100644 index 0000000..56c0553 --- /dev/null +++ b/fs/ceph/auth_none.h @@ -0,0 +1,28 @@ +#ifndef _FS_CEPH_AUTH_NONE_H +#define _FS_CEPH_AUTH_NONE_H + +#include "auth.h" + +/* + * null security mode. + * + * we use a single static authorizer that simply encodes our entity name + * and global id. + */ + +struct ceph_none_authorizer { + char buf[128]; + int buf_len; + char reply_buf[0]; +}; + +struct ceph_auth_none_info { + bool starting; + bool built_authorizer; + struct ceph_none_authorizer au; /* we only need one; it's static */ +}; + +extern int ceph_auth_none_init(struct ceph_auth_client *ac); + +#endif + diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 36becb0..1e96a9a 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -75,6 +75,16 @@ struct ceph_file_layout { int ceph_file_layout_is_valid(const struct ceph_file_layout *layout); +/* crypto algorithms */ +#define CEPH_CRYPTO_NONE 0x0 +#define CEPH_CRYPTO_AES 0x1 + +/* security/authentication protocols */ +#define CEPH_AUTH_UNKNOWN 0x0 +#define CEPH_AUTH_NONE 0x1 +#define CEPH_AUTH_CEPHX 0x2 + + /********************************************* * message layer */ @@ -90,12 +100,12 @@ int ceph_file_layout_is_valid(const struct ceph_file_layout *layout); /* client <-> monitor */ #define CEPH_MSG_MON_MAP 4 #define CEPH_MSG_MON_GET_MAP 5 -#define CEPH_MSG_CLIENT_MOUNT 10 -#define CEPH_MSG_CLIENT_MOUNT_ACK 11 #define CEPH_MSG_STATFS 13 #define CEPH_MSG_STATFS_REPLY 14 #define CEPH_MSG_MON_SUBSCRIBE 15 #define CEPH_MSG_MON_SUBSCRIBE_ACK 16 +#define CEPH_MSG_AUTH 17 +#define CEPH_MSG_AUTH_REPLY 18 /* client <-> mds */ #define CEPH_MSG_MDS_MAP 21 diff --git a/fs/ceph/ceph_strings.c b/fs/ceph/ceph_strings.c index 90d19d9..8e4be6a 100644 --- a/fs/ceph/ceph_strings.c +++ b/fs/ceph/ceph_strings.c @@ -3,6 +3,19 @@ */ #include "types.h" +const char *ceph_entity_type_name(int type) +{ + switch (type) { + case CEPH_ENTITY_TYPE_MDS: return "mds"; + case CEPH_ENTITY_TYPE_OSD: return "osd"; + case CEPH_ENTITY_TYPE_MON: return "mon"; + case CEPH_ENTITY_TYPE_CLIENT: return "client"; + case CEPH_ENTITY_TYPE_ADMIN: return "admin"; + case CEPH_ENTITY_TYPE_AUTH: return "auth"; + default: return "unknown"; + } +} + const char *ceph_osd_op_name(int op) { switch (op) { diff --git a/fs/ceph/decode.h b/fs/ceph/decode.h index a382aec..10de848 100644 --- a/fs/ceph/decode.h +++ b/fs/ceph/decode.h @@ -98,6 +98,7 @@ static inline void ceph_encode_addr(struct ceph_entity_addr *a) static inline void ceph_decode_addr(struct ceph_entity_addr *a) { a->in_addr.ss_family = ntohs(a->in_addr.ss_family); + WARN_ON(a->in_addr.ss_family == 512); } /* @@ -123,6 +124,11 @@ static inline void ceph_encode_8(void **p, u8 v) *(u8 *)*p = v; (*p)++; } +static inline void ceph_encode_copy(void **p, const void *s, int len) +{ + memcpy(*p, s, len); + *p += len; +} /* * filepath, string encoders diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 69feeb1..8a28515 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -8,6 +8,7 @@ #include "super.h" #include "messenger.h" #include "decode.h" +#include "auth.h" /* * A cluster of MDS (metadata server) daemons is responsible for @@ -274,8 +275,12 @@ void ceph_put_mds_session(struct ceph_mds_session *s) { dout("mdsc put_session %p %d -> %d\n", s, atomic_read(&s->s_ref), atomic_read(&s->s_ref)-1); - if (atomic_dec_and_test(&s->s_ref)) + if (atomic_dec_and_test(&s->s_ref)) { + if (s->s_authorizer) + s->s_mdsc->client->monc.auth->ops->destroy_authorizer( + s->s_mdsc->client->monc.auth, s->s_authorizer); kfree(s); + } } /* @@ -2777,9 +2782,15 @@ void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc, struct ceph_msg *msg) ceph_decode_need(&p, end, sizeof(fsid)+2*sizeof(u32), bad); ceph_decode_copy(&p, &fsid, sizeof(fsid)); - if (ceph_fsid_compare(&fsid, &mdsc->client->monc.monmap->fsid)) { - pr_err("got mdsmap with wrong fsid\n"); - return; + if (mdsc->client->monc.have_fsid) { + if (ceph_fsid_compare(&fsid, + &mdsc->client->monc.monmap->fsid)) { + pr_err("got mdsmap with wrong fsid\n"); + return; + } + } else { + ceph_fsid_set(&mdsc->client->monc.monmap->fsid, &fsid); + mdsc->client->monc.have_fsid = true; } epoch = ceph_decode_32(&p); maplen = ceph_decode_32(&p); @@ -2895,10 +2906,60 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) ceph_msg_put(msg); } +/* + * authentication + */ +static int get_authorizer(struct ceph_connection *con, + void **buf, int *len, int *proto, + void **reply_buf, int *reply_len, int force_new) +{ + struct ceph_mds_session *s = con->private; + struct ceph_mds_client *mdsc = s->s_mdsc; + struct ceph_auth_client *ac = mdsc->client->monc.auth; + int ret = 0; + + if (force_new && s->s_authorizer) { + ac->ops->destroy_authorizer(ac, s->s_authorizer); + s->s_authorizer = NULL; + } + if (s->s_authorizer == NULL) { + if (ac->ops->create_authorizer) { + ret = ac->ops->create_authorizer( + ac, CEPH_ENTITY_TYPE_MDS, + &s->s_authorizer, + &s->s_authorizer_buf, + &s->s_authorizer_buf_len, + &s->s_authorizer_reply_buf, + &s->s_authorizer_reply_buf_len); + if (ret) + return ret; + } + } + + *proto = ac->protocol; + *buf = s->s_authorizer_buf; + *len = s->s_authorizer_buf_len; + *reply_buf = s->s_authorizer_reply_buf; + *reply_len = s->s_authorizer_reply_buf_len; + return 0; +} + + +static int verify_authorizer_reply(struct ceph_connection *con, int len) +{ + struct ceph_mds_session *s = con->private; + struct ceph_mds_client *mdsc = s->s_mdsc; + struct ceph_auth_client *ac = mdsc->client->monc.auth; + + return ac->ops->verify_authorizer_reply(ac, s->s_authorizer, len); +} + const static struct ceph_connection_operations mds_con_ops = { .get = con_get, .put = con_put, .dispatch = dispatch, + .get_authorizer = get_authorizer, + .verify_authorizer_reply = verify_authorizer_reply, .peer_reset = peer_reset, .alloc_msg = ceph_alloc_msg, .alloc_middle = ceph_alloc_middle, diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 7c43948..9faa1b2 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -100,6 +100,10 @@ struct ceph_mds_session { struct ceph_connection s_con; + struct ceph_authorizer *s_authorizer; + void *s_authorizer_buf, *s_authorizer_reply_buf; + size_t s_authorizer_buf_len, s_authorizer_reply_buf_len; + /* protected by s_cap_lock */ spinlock_t s_cap_lock; u32 s_cap_gen; /* inc each time we get mds stale msg */ diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index d8a6a56..0b16748 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -550,6 +550,27 @@ static void prepare_write_keepalive(struct ceph_connection *con) * Connection negotiation. */ +static void prepare_connect_authorizer(struct ceph_connection *con) +{ + void *auth_buf; + int auth_len = 0; + int auth_protocol = 0; + + if (con->ops->get_authorizer) + con->ops->get_authorizer(con, &auth_buf, &auth_len, + &auth_protocol, &con->auth_reply_buf, + &con->auth_reply_buf_len, + con->auth_retry); + + con->out_connect.authorizer_protocol = cpu_to_le32(auth_protocol); + con->out_connect.authorizer_len = cpu_to_le32(auth_len); + + con->out_kvec[con->out_kvec_left].iov_base = auth_buf; + con->out_kvec[con->out_kvec_left].iov_len = auth_len; + con->out_kvec_left++; + con->out_kvec_bytes += auth_len; +} + /* * We connected to a peer and are saying hello. */ @@ -592,6 +613,7 @@ static void prepare_write_connect(struct ceph_messenger *msgr, dout("prepare_write_connect %p cseq=%d gseq=%d proto=%d\n", con, con->connect_seq, global_seq, proto); + con->out_connect.host_type = cpu_to_le32(CEPH_ENTITY_TYPE_CLIENT); con->out_connect.connect_seq = cpu_to_le32(con->connect_seq); con->out_connect.global_seq = cpu_to_le32(global_seq); @@ -611,6 +633,8 @@ static void prepare_write_connect(struct ceph_messenger *msgr, con->out_kvec_cur = con->out_kvec; con->out_more = 0; set_bit(WRITE_PENDING, &con->state); + + prepare_connect_authorizer(con); } @@ -777,6 +801,13 @@ static void prepare_read_connect(struct ceph_connection *con) con->in_base_pos = 0; } +static void prepare_read_connect_retry(struct ceph_connection *con) +{ + dout("prepare_read_connect_retry %p\n", con); + con->in_base_pos = strlen(CEPH_BANNER) + sizeof(con->actual_peer_addr) + + sizeof(con->peer_addr_for_me); +} + static void prepare_read_ack(struct ceph_connection *con) { dout("prepare_read_ack %p\n", con); @@ -853,9 +884,14 @@ static int read_partial_connect(struct ceph_connection *con) ret = read_partial(con, &to, sizeof(con->in_reply), &con->in_reply); if (ret <= 0) goto out; + ret = read_partial(con, &to, le32_to_cpu(con->in_reply.authorizer_len), + con->auth_reply_buf); + if (ret <= 0) + goto out; - dout("read_partial_connect %p connect_seq = %u, global_seq = %u\n", - con, le32_to_cpu(con->in_reply.connect_seq), + dout("read_partial_connect %p tag %d, con_seq = %u, g_seq = %u\n", + con, (int)con->in_reply.tag, + le32_to_cpu(con->in_reply.connect_seq), le32_to_cpu(con->in_reply.global_seq)); out: return ret; @@ -1051,6 +1087,20 @@ static int process_connect(struct ceph_connection *con) set_bit(CLOSED, &con->state); /* in case there's queued work */ return -1; + case CEPH_MSGR_TAG_BADAUTHORIZER: + con->auth_retry++; + dout("process_connect %p got BADAUTHORIZER attempt %d\n", con, + con->auth_retry); + if (con->auth_retry == 2) { + con->error_msg = "connect authorization failure"; + reset_connection(con); + set_bit(CLOSED, &con->state); + return -1; + } + con->auth_retry = 1; + prepare_write_connect(con->msgr, con, 0); + prepare_read_connect_retry(con); + break; case CEPH_MSGR_TAG_RESETSESSION: /* diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index 4bd85c3..f9c9f64 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -26,6 +26,12 @@ struct ceph_connection_operations { /* handle an incoming message. */ void (*dispatch) (struct ceph_connection *con, struct ceph_msg *m); + /* authorize an outgoing connection */ + int (*get_authorizer) (struct ceph_connection *con, + void **buf, int *len, int *proto, + void **reply_buf, int *reply_len, int force_new); + int (*verify_authorizer_reply) (struct ceph_connection *con, int len); + /* protocol version mismatch */ void (*bad_proto) (struct ceph_connection *con); @@ -144,6 +150,10 @@ struct ceph_connection { attempt for this connection, client */ u32 peer_global_seq; /* peer's global seq for this connection */ + int auth_retry; /* true if we need a newer authorizer */ + void *auth_reply_buf; /* where to put the authorizer reply */ + int auth_reply_buf_len; + /* out queue */ struct mutex out_mutex; struct list_head out_queue; diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 95b76e7..017d5ae 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -6,6 +6,7 @@ #include "mon_client.h" #include "super.h" +#include "auth.h" #include "decode.h" /* @@ -38,6 +39,10 @@ struct ceph_monmap *ceph_monmap_decode(void *p, void *end) struct ceph_fsid fsid; u32 epoch, num_mon; u16 version; + u32 len; + + ceph_decode_32_safe(&p, end, len, bad); + ceph_decode_need(&p, end, len, bad); dout("monmap_decode %p %p len %d\n", p, end, (int)(end-p)); @@ -95,8 +100,10 @@ static void __close_session(struct ceph_mon_client *monc) { if (monc->con) { dout("__close_session closing mon%d\n", monc->cur_mon); + ceph_con_revoke(monc->con, monc->m_auth); ceph_con_close(monc->con); monc->cur_mon = -1; + ceph_auth_reset(monc->auth); } } @@ -106,6 +113,7 @@ static void __close_session(struct ceph_mon_client *monc) static int __open_session(struct ceph_mon_client *monc) { char r; + int ret; if (monc->cur_mon < 0) { get_random_bytes(&r, 1); @@ -121,6 +129,15 @@ static int __open_session(struct ceph_mon_client *monc) monc->con->peer_name.num = cpu_to_le64(monc->cur_mon); ceph_con_open(monc->con, &monc->monmap->mon_inst[monc->cur_mon].addr); + + /* initiatiate authentication handshake */ + ret = ceph_auth_build_hello(monc->auth, + monc->m_auth->front.iov_base, + monc->m_auth->front_max); + monc->m_auth->front.iov_len = ret; + monc->m_auth->hdr.front_len = cpu_to_le32(ret); + ceph_msg_get(monc->m_auth); /* keep our ref */ + ceph_con_send(monc->con, monc->m_auth); } else { dout("open_session mon%d already open\n", monc->cur_mon); } @@ -139,7 +156,7 @@ static void __schedule_delayed(struct ceph_mon_client *monc) { unsigned delay; - if (monc->cur_mon < 0 || monc->want_mount || __sub_expired(monc)) + if (monc->cur_mon < 0 || __sub_expired(monc)) delay = 10 * HZ; else delay = 20 * HZ; @@ -161,7 +178,7 @@ static void __send_subscribe(struct ceph_mon_client *monc) struct ceph_mon_subscribe_item *i; void *p, *end; - msg = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE, 64, 0, 0, NULL); + msg = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE, 96, 0, 0, NULL); if (!msg) return; @@ -173,7 +190,7 @@ static void __send_subscribe(struct ceph_mon_client *monc) if (monc->want_next_osdmap) { dout("__send_subscribe to 'osdmap' %u\n", (unsigned)monc->have_osdmap); - ceph_encode_32(&p, 2); + ceph_encode_32(&p, 3); ceph_encode_string(&p, end, "osdmap", 6); i = p; i->have = cpu_to_le64(monc->have_osdmap); @@ -181,13 +198,18 @@ static void __send_subscribe(struct ceph_mon_client *monc) p += sizeof(*i); monc->want_next_osdmap = 2; /* requested */ } else { - ceph_encode_32(&p, 1); + ceph_encode_32(&p, 2); } ceph_encode_string(&p, end, "mdsmap", 6); i = p; i->have = cpu_to_le64(monc->have_mdsmap); i->onetime = 0; p += sizeof(*i); + ceph_encode_string(&p, end, "monmap", 6); + i = p; + i->have = 0; + i->onetime = 0; + p += sizeof(*i); msg->front.iov_len = p - msg->front.iov_base; msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); @@ -256,7 +278,7 @@ void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc) mutex_unlock(&monc->mutex); } - +#if 0 /* * mount */ @@ -264,12 +286,8 @@ static void __request_mount(struct ceph_mon_client *monc) { struct ceph_msg *msg; struct ceph_client_mount *h; - int err; dout("__request_mount\n"); - err = __open_session(monc); - if (err) - return; msg = ceph_msg_new(CEPH_MSG_CLIENT_MOUNT, sizeof(*h), 0, 0, NULL); if (IS_ERR(msg)) return; @@ -279,8 +297,12 @@ static void __request_mount(struct ceph_mon_client *monc) h->monhdr.session_mon_tid = 0; ceph_con_send(monc->con, msg); } +#endif -int ceph_monc_request_mount(struct ceph_mon_client *monc) +/* + * + */ +int ceph_monc_open_session(struct ceph_mon_client *monc) { if (!monc->con) { monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); @@ -292,12 +314,14 @@ int ceph_monc_request_mount(struct ceph_mon_client *monc) } mutex_lock(&monc->mutex); - __request_mount(monc); + __open_session(monc); __schedule_delayed(monc); mutex_unlock(&monc->mutex); return 0; } +#if 0 + /* * The monitor responds with mount ack indicate mount success. The * included client ticket allows the client to talk to MDSs and OSDs. @@ -372,9 +396,65 @@ out: mutex_unlock(&monc->mutex); wake_up(&client->mount_wq); } +#endif + +/* + * The monitor responds with mount ack indicate mount success. The + * included client ticket allows the client to talk to MDSs and OSDs. + */ +static void ceph_monc_handle_map(struct ceph_mon_client *monc, struct ceph_msg *msg) +{ + struct ceph_client *client = monc->client; + struct ceph_monmap *monmap = NULL, *old = monc->monmap; + void *p, *end; + + mutex_lock(&monc->mutex); + + dout("handle_monmap\n"); + p = msg->front.iov_base; + end = p + msg->front.iov_len; + + monmap = ceph_monmap_decode(p, end); + if (IS_ERR(monmap)) { + pr_err("problem decoding monmap, %d\n", + (int)PTR_ERR(monmap)); + return; + } + if (monc->have_fsid && + ceph_fsid_compare(&monmap->fsid, &monc->monmap->fsid)) { + print_hex_dump(KERN_ERR, "monmap->fsid: ", DUMP_PREFIX_NONE, 16, 1, + (void *)&monmap->fsid, 16, 0); + print_hex_dump(KERN_ERR, "monc->monmap->fsid: ", DUMP_PREFIX_NONE, 16, 1, + (void *)&monc->monmap->fsid, 16, 0); + + pr_err("fsid mismatch, got a previous map with different fsid"); + kfree(monmap); + return; + } + + client->monc.monmap = monmap; + client->monc.have_fsid = true; + kfree(old); + + mutex_unlock(&monc->mutex); + wake_up(&client->mount_wq); +} + +/* + * init client info after authentication + */ +static void __init_authenticated_client(struct ceph_mon_client *monc) +{ + struct ceph_client *client = monc->client; + client->signed_ticket = NULL; + client->signed_ticket_len = 0; + client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; + client->msgr->inst.name.num = monc->auth->global_id; + ceph_debugfs_client_init(client); +} /* * statfs @@ -414,12 +494,8 @@ static int send_statfs(struct ceph_mon_client *monc, { struct ceph_msg *msg; struct ceph_mon_statfs *h; - int err; dout("send_statfs tid %llu\n", req->tid); - err = __open_session(monc); - if (err) - return err; msg = ceph_msg_new(CEPH_MSG_STATFS, sizeof(*h), 0, 0, NULL); if (IS_ERR(msg)) return PTR_ERR(msg); @@ -514,17 +590,14 @@ static void delayed_work(struct work_struct *work) dout("monc delayed_work\n"); mutex_lock(&monc->mutex); - if (monc->want_mount) { - __request_mount(monc); + if (monc->hunting) { + __close_session(monc); + __open_session(monc); /* continue hunting */ } else { - if (monc->hunting) { - __close_session(monc); - __open_session(monc); /* continue hunting */ - } else { - ceph_con_keepalive(monc->con); - } + ceph_con_keepalive(monc->con); + if (monc->auth->ops->is_authenticated(monc->auth)) + __send_subscribe(monc); } - __send_subscribe(monc); __schedule_delayed(monc); mutex_unlock(&monc->mutex); } @@ -555,6 +628,7 @@ static int build_initial_monmap(struct ceph_mon_client *monc) monc->monmap->mon_inst[i].name.num = cpu_to_le64(i); } monc->monmap->num_mon = num_mon; + monc->have_fsid = false; /* release addr memory */ kfree(args->mon_addr); @@ -579,21 +653,37 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) monc->con = NULL; + /* authentication */ + monc->auth = ceph_auth_init(cl->mount_args->name, + cl->mount_args->secret); + if (IS_ERR(monc->auth)) + return PTR_ERR(monc->auth); + monc->auth->want_keys = + CEPH_ENTITY_TYPE_AUTH | CEPH_ENTITY_TYPE_MON | + CEPH_ENTITY_TYPE_OSD | CEPH_ENTITY_TYPE_MDS; + /* msg pools */ - err = ceph_msgpool_init(&monc->msgpool_mount_ack, 4096, 1, false); - if (err < 0) - goto out; err = ceph_msgpool_init(&monc->msgpool_subscribe_ack, sizeof(struct ceph_mon_subscribe_ack), 1, false); if (err < 0) - goto out; + goto out_monmap; err = ceph_msgpool_init(&monc->msgpool_statfs_reply, sizeof(struct ceph_mon_statfs_reply), 0, false); if (err < 0) - goto out; + goto out_pool1; + err = ceph_msgpool_init(&monc->msgpool_auth_reply, 4096, 1, false); + if (err < 0) + goto out_pool2; + + monc->m_auth = ceph_msg_new(CEPH_MSG_AUTH, 4096, 0, 0, NULL); + if (IS_ERR(monc->m_auth)) { + err = PTR_ERR(monc->m_auth); + monc->m_auth = NULL; + goto out_pool3; + } monc->cur_mon = -1; - monc->hunting = false; /* not really */ + monc->hunting = true; monc->sub_renew_after = jiffies; monc->sub_sent = 0; @@ -605,7 +695,16 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) monc->have_mdsmap = 0; monc->have_osdmap = 0; monc->want_next_osdmap = 1; - monc->want_mount = true; + return 0; + +out_pool3: + ceph_msgpool_destroy(&monc->msgpool_auth_reply); +out_pool2: + ceph_msgpool_destroy(&monc->msgpool_subscribe_ack); +out_pool1: + ceph_msgpool_destroy(&monc->msgpool_statfs_reply); +out_monmap: + kfree(monc->monmap); out: return err; } @@ -624,14 +723,44 @@ void ceph_monc_stop(struct ceph_mon_client *monc) } mutex_unlock(&monc->mutex); - ceph_msgpool_destroy(&monc->msgpool_mount_ack); + ceph_auth_destroy(monc->auth); + + ceph_msg_put(monc->m_auth); ceph_msgpool_destroy(&monc->msgpool_subscribe_ack); ceph_msgpool_destroy(&monc->msgpool_statfs_reply); + ceph_msgpool_destroy(&monc->msgpool_auth_reply); kfree(monc->monmap); } +static void handle_auth_reply(struct ceph_mon_client *monc, + struct ceph_msg *msg) +{ + int ret; + + mutex_lock(&monc->mutex); + ret = ceph_handle_auth_reply(monc->auth, msg->front.iov_base, + msg->front.iov_len, + monc->m_auth->front.iov_base, + monc->m_auth->front_max); + if (ret < 0) { + monc->client->mount_err = ret; + wake_up(&monc->client->mount_wq); + } else if (ret > 0) { + monc->m_auth->front.iov_len = ret; + monc->m_auth->hdr.front_len = cpu_to_le32(ret); + ceph_msg_get(monc->m_auth); /* keep our ref */ + ceph_con_send(monc->con, monc->m_auth); + } else if (monc->auth->ops->is_authenticated(monc->auth)) { + dout("authenticated, starting session\n"); + __init_authenticated_client(monc); + __send_subscribe(monc); + __resend_statfs(monc); + } + mutex_unlock(&monc->mutex); +} + /* * handle incoming message */ @@ -644,8 +773,8 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) return; switch (type) { - case CEPH_MSG_CLIENT_MOUNT_ACK: - handle_mount_ack(monc, msg); + case CEPH_MSG_AUTH_REPLY: + handle_auth_reply(monc, msg); break; case CEPH_MSG_MON_SUBSCRIBE_ACK: @@ -656,6 +785,10 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) handle_statfs_reply(monc, msg); break; + case CEPH_MSG_MON_MAP: + ceph_monc_handle_map(monc, msg); + break; + case CEPH_MSG_MDS_MAP: ceph_mdsc_handle_map(&monc->client->mdsc, msg); break; @@ -682,12 +815,12 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, int front = le32_to_cpu(hdr->front_len); switch (type) { - case CEPH_MSG_CLIENT_MOUNT_ACK: - return ceph_msgpool_get(&monc->msgpool_mount_ack, front); case CEPH_MSG_MON_SUBSCRIBE_ACK: return ceph_msgpool_get(&monc->msgpool_subscribe_ack, front); case CEPH_MSG_STATFS_REPLY: return ceph_msgpool_get(&monc->msgpool_statfs_reply, front); + case CEPH_MSG_AUTH_REPLY: + return ceph_msgpool_get(&monc->msgpool_auth_reply, front); } return ceph_alloc_msg(con, hdr); } @@ -717,10 +850,7 @@ static void mon_fault(struct ceph_connection *con) if (!monc->hunting) { /* start hunting */ monc->hunting = true; - if (__open_session(monc) == 0) { - __send_subscribe(monc); - __resend_statfs(monc); - } + __open_session(monc); } else { /* already hunting, let's wait a bit */ __schedule_delayed(monc); diff --git a/fs/ceph/mon_client.h b/fs/ceph/mon_client.h index 9f6db45..c75b533 100644 --- a/fs/ceph/mon_client.h +++ b/fs/ceph/mon_client.h @@ -9,6 +9,7 @@ struct ceph_client; struct ceph_mount_args; +struct ceph_auth_client; /* * The monitor map enumerates the set of all monitors. @@ -58,23 +59,26 @@ struct ceph_mon_client { struct mutex mutex; struct delayed_work delayed_work; + struct ceph_auth_client *auth; + struct ceph_msg *m_auth; + bool hunting; int cur_mon; /* last monitor i contacted */ unsigned long sub_sent, sub_renew_after; struct ceph_connection *con; + bool have_fsid; /* msg pools */ - struct ceph_msgpool msgpool_mount_ack; struct ceph_msgpool msgpool_subscribe_ack; struct ceph_msgpool msgpool_statfs_reply; + struct ceph_msgpool msgpool_auth_reply; /* pending statfs requests */ struct radix_tree_root statfs_request_tree; int num_statfs_requests; u64 last_tid; - /* mds/osd map or mount requests */ - bool want_mount; + /* mds/osd map */ int want_next_osdmap; /* 1 = want, 2 = want+asked */ u32 have_osdmap, have_mdsmap; @@ -101,11 +105,11 @@ extern int ceph_monc_got_osdmap(struct ceph_mon_client *monc, u32 have); extern void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc); -extern int ceph_monc_request_mount(struct ceph_mon_client *monc); - extern int ceph_monc_do_statfs(struct ceph_mon_client *monc, struct ceph_statfs *buf); +extern int ceph_monc_open_session(struct ceph_mon_client *monc); + #endif diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h index 8e3ea2e..c758e8f 100644 --- a/fs/ceph/msgr.h +++ b/fs/ceph/msgr.h @@ -21,7 +21,7 @@ * whenever the wire protocol changes. try to keep this string length * constant. */ -#define CEPH_BANNER "ceph v023" +#define CEPH_BANNER "ceph v024" #define CEPH_BANNER_MAX_LEN 30 @@ -46,11 +46,16 @@ struct ceph_entity_name { __le64 num; } __attribute__ ((packed)); -#define CEPH_ENTITY_TYPE_MON 1 -#define CEPH_ENTITY_TYPE_MDS 2 -#define CEPH_ENTITY_TYPE_OSD 3 -#define CEPH_ENTITY_TYPE_CLIENT 4 -#define CEPH_ENTITY_TYPE_ADMIN 5 +#define CEPH_ENTITY_TYPE_MON 0x01 +#define CEPH_ENTITY_TYPE_MDS 0x02 +#define CEPH_ENTITY_TYPE_OSD 0x04 +#define CEPH_ENTITY_TYPE_CLIENT 0x08 +#define CEPH_ENTITY_TYPE_ADMIN 0x10 +#define CEPH_ENTITY_TYPE_AUTH 0x20 + +#define CEPH_ENTITY_TYPE_ANY 0xFF + +extern const char *ceph_entity_type_name(int type); /* * entity_addr -- network address @@ -94,6 +99,7 @@ struct ceph_entity_inst { #define CEPH_MSGR_TAG_ACK 8 /* message ack */ #define CEPH_MSGR_TAG_KEEPALIVE 9 /* just a keepalive byte! */ #define CEPH_MSGR_TAG_BADPROTOVER 10 /* bad protocol version */ +#define CEPH_MSGR_TAG_BADAUTHORIZER 11 /* bad authorizer */ /* @@ -104,6 +110,8 @@ struct ceph_msg_connect { __le32 global_seq; /* count connections initiated by this host */ __le32 connect_seq; /* count connections initiated in this session */ __le32 protocol_version; + __le32 authorizer_protocol; + __le32 authorizer_len; __u8 flags; /* CEPH_MSG_CONNECT_* */ } __attribute__ ((packed)); @@ -112,6 +120,7 @@ struct ceph_msg_connect_reply { __le32 global_seq; __le32 connect_seq; __le32 protocol_version; + __le32 authorizer_len; __u8 flags; } __attribute__ ((packed)); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 0a16c4f..ca0ee68 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -11,6 +11,7 @@ #include "osd_client.h" #include "messenger.h" #include "decode.h" +#include "auth.h" const static struct ceph_connection_operations osd_con_ops; @@ -331,6 +332,7 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc) osd->o_con.private = osd; osd->o_con.ops = &osd_con_ops; osd->o_con.peer_name.type = CEPH_ENTITY_TYPE_OSD; + return osd; } @@ -880,9 +882,15 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) /* verify fsid */ ceph_decode_need(&p, end, sizeof(fsid), bad); ceph_decode_copy(&p, &fsid, sizeof(fsid)); - if (ceph_fsid_compare(&fsid, &osdc->client->monc.monmap->fsid)) { - pr_err("got osdmap with wrong fsid, ignoring\n"); - return; + if (osdc->client->monc.have_fsid) { + if (ceph_fsid_compare(&fsid, + &osdc->client->monc.monmap->fsid)) { + pr_err("got osdmap with wrong fsid, ignoring\n"); + return; + } + } else { + ceph_fsid_set(&osdc->client->monc.monmap->fsid, &fsid); + osdc->client->monc.have_fsid = true; } down_write(&osdc->map_sem); @@ -1302,10 +1310,59 @@ static void put_osd_con(struct ceph_connection *con) put_osd(osd); } +/* + * authentication + */ +static int get_authorizer(struct ceph_connection *con, + void **buf, int *len, int *proto, + void **reply_buf, int *reply_len, int force_new) +{ + struct ceph_osd *o = con->private; + struct ceph_osd_client *osdc = o->o_osdc; + struct ceph_auth_client *ac = osdc->client->monc.auth; + int ret = 0; + + if (force_new && o->o_authorizer) { + ac->ops->destroy_authorizer(ac, o->o_authorizer); + o->o_authorizer = NULL; + } + if (o->o_authorizer == NULL) { + ret = ac->ops->create_authorizer( + ac, CEPH_ENTITY_TYPE_OSD, + &o->o_authorizer, + &o->o_authorizer_buf, + &o->o_authorizer_buf_len, + &o->o_authorizer_reply_buf, + &o->o_authorizer_reply_buf_len); + if (ret) + return ret; + } + + *proto = ac->protocol; + *buf = o->o_authorizer_buf; + *len = o->o_authorizer_buf_len; + *reply_buf = o->o_authorizer_reply_buf; + *reply_len = o->o_authorizer_reply_buf_len; + return 0; +} + + +static int verify_authorizer_reply(struct ceph_connection *con, int len) +{ + struct ceph_osd *o = con->private; + struct ceph_osd_client *osdc = o->o_osdc; + struct ceph_auth_client *ac = osdc->client->monc.auth; + + return ac->ops->verify_authorizer_reply(ac, o->o_authorizer, len); +} + + const static struct ceph_connection_operations osd_con_ops = { .get = get_osd_con, .put = put_osd_con, .dispatch = dispatch, + .get_authorizer = get_authorizer, + .verify_authorizer_reply = verify_authorizer_reply, .alloc_msg = alloc_msg, .fault = osd_reset, .alloc_middle = ceph_alloc_middle, diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index 766c8dc..3d4ae65 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -13,6 +13,7 @@ struct ceph_msg; struct ceph_snap_context; struct ceph_osd_request; struct ceph_osd_client; +struct ceph_authorizer; /* * completion callback for async writepages @@ -29,6 +30,9 @@ struct ceph_osd { struct rb_node o_node; struct ceph_connection o_con; struct list_head o_requests; + struct ceph_authorizer *o_authorizer; + void *o_authorizer_buf, *o_authorizer_reply_buf; + size_t o_authorizer_buf_len, o_authorizer_reply_buf_len; }; /* an in-flight request */ diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h index fb23ff9..12bfb2f 100644 --- a/fs/ceph/rados.h +++ b/fs/ceph/rados.h @@ -157,7 +157,6 @@ struct ceph_eversion { #define CEPH_OSD_OP_MODE_WR 0x2000 #define CEPH_OSD_OP_MODE_RMW 0x3000 #define CEPH_OSD_OP_MODE_SUB 0x4000 -#define CEPH_OSD_OP_MODE_EXEC 0x8000 #define CEPH_OSD_OP_TYPE 0x0f00 #define CEPH_OSD_OP_TYPE_LOCK 0x0100 @@ -285,6 +284,7 @@ enum { CEPH_OSD_FLAG_BALANCE_READS = 256, CEPH_OSD_FLAG_PARALLELEXEC = 512, /* execute op in parallel */ CEPH_OSD_FLAG_PGOP = 1024, /* pg op, no object */ + CEPH_OSD_FLAG_EXEC = 2048, /* op may exec */ }; enum { diff --git a/fs/ceph/super.c b/fs/ceph/super.c index fe0a596..c901395 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -128,6 +128,8 @@ static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt) seq_puts(m, ",noasyncreaddir"); if (strcmp(args->snapdir_name, CEPH_SNAPDIRNAME_DEFAULT)) seq_printf(m, ",snapdirname=%s", args->snapdir_name); + if (args->name) + seq_printf(m, ",name=%s", args->name); if (args->secret) seq_puts(m, ",secret="); return 0; @@ -224,12 +226,12 @@ const char *ceph_msg_type_name(int type) switch (type) { case CEPH_MSG_SHUTDOWN: return "shutdown"; case CEPH_MSG_PING: return "ping"; + case CEPH_MSG_AUTH: return "auth"; + case CEPH_MSG_AUTH_REPLY: return "auth_reply"; case CEPH_MSG_MON_MAP: return "mon_map"; case CEPH_MSG_MON_GET_MAP: return "mon_get_map"; case CEPH_MSG_MON_SUBSCRIBE: return "mon_subscribe"; case CEPH_MSG_MON_SUBSCRIBE_ACK: return "mon_subscribe_ack"; - case CEPH_MSG_CLIENT_MOUNT: return "client_mount"; - case CEPH_MSG_CLIENT_MOUNT_ACK: return "client_mount_ack"; case CEPH_MSG_STATFS: return "statfs"; case CEPH_MSG_STATFS_REPLY: return "statfs_reply"; case CEPH_MSG_MDS_MAP: return "mds_map"; @@ -267,6 +269,7 @@ enum { Opt_last_int, /* int args above */ Opt_snapdirname, + Opt_name, Opt_secret, Opt_last_string, /* string args above */ @@ -293,6 +296,7 @@ static match_table_t arg_tokens = { {Opt_readdir_max_entries, "readdir_max_entries=%d"}, /* int args above */ {Opt_snapdirname, "snapdirname=%s"}, + {Opt_name, "name=%s"}, {Opt_secret, "secret=%s"}, /* string args above */ {Opt_ip, "ip=%s"}, @@ -407,6 +411,11 @@ static struct ceph_mount_args *parse_mount_args(int flags, char *options, argstr[0].to-argstr[0].from, GFP_KERNEL); break; + case Opt_name: + args->name = kstrndup(argstr[0].from, + argstr[0].to-argstr[0].from, + GFP_KERNEL); + break; case Opt_secret: args->secret = kstrndup(argstr[0].from, argstr[0].to-argstr[0].from, @@ -476,6 +485,8 @@ static void destroy_mount_args(struct ceph_mount_args *args) dout("destroy_mount_args %p\n", args); kfree(args->snapdir_name); args->snapdir_name = NULL; + kfree(args->name); + args->name = NULL; kfree(args->secret); args->secret = NULL; kfree(args); @@ -657,27 +668,23 @@ static int ceph_mount(struct ceph_client *client, struct vfsmount *mnt, client->msgr->nocrc = ceph_test_opt(client, NOCRC); } - /* send mount request, and wait for mon, mds, and osd maps */ - err = ceph_monc_request_mount(&client->monc); + /* open session, and wait for mon, mds, and osd maps */ + err = ceph_monc_open_session(&client->monc); if (err < 0) goto out; - while (!have_mon_map(client) && !client->mount_err) { + while (!have_mon_map(client)) { err = -EIO; if (timeout && time_after_eq(jiffies, started + timeout)) goto out; /* wait */ - dout("mount waiting for mount\n"); - err = wait_event_interruptible_timeout(client->mount_wq, - client->mount_err || have_mon_map(client), + dout("mount waiting for mon_map\n"); + err = wait_event_interruptible_timeout(client->mount_wq, /* FIXME */ + have_mon_map(client), timeout); if (err == -EINTR || err == -ERESTARTSYS) goto out; - if (client->mount_err) { - err = client->mount_err; - goto out; - } } dout("mount opening root\n"); @@ -795,7 +802,6 @@ static int ceph_register_bdi(struct super_block *sb, struct ceph_client *client) client->backing_dev_info.ra_pages = (client->mount_args->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_SHIFT; - err = bdi_register_dev(&client->backing_dev_info, sb->s_dev); return err; } diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 8aa1ffb..e0e8130 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -61,6 +61,7 @@ struct ceph_mount_args { int max_readdir; /* max readdir size */ int osd_timeout; char *snapdir_name; /* default ".snap" */ + char *name; char *secret; int cap_release_safety; }; @@ -75,6 +76,7 @@ struct ceph_mount_args { #define CEPH_MSG_MAX_DATA_LEN (16*1024*1024) #define CEPH_SNAPDIRNAME_DEFAULT ".snap" +#define CEPH_AUTH_NAME_DEFAULT "guest" /* * Delay telling the MDS we no longer want caps, in case we reopen -- cgit v0.10.2 From b9bfb93ce2b1ef668254f0b9e16fcc5246d65d8e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 18 Nov 2009 15:08:44 -0800 Subject: ceph: move mempool creation to ceph_create_client Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index c901395..df05617 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -534,10 +534,18 @@ static struct ceph_client *ceph_create_client(struct ceph_mount_args *args) if (client->trunc_wq == NULL) goto fail_pg_inv_wq; + /* set up mempools */ + err = -ENOMEM; + client->wb_pagevec_pool = mempool_create_kmalloc_pool(10, + client->mount_args->wsize >> PAGE_CACHE_SHIFT); + if (!client->wb_pagevec_pool) + goto fail_trunc_wq; + + /* subsystems */ err = ceph_monc_init(&client->monc, client); if (err < 0) - goto fail_trunc_wq; + goto fail_mempool; err = ceph_osdc_init(&client->osdc, client); if (err < 0) goto fail_monc; @@ -550,6 +558,8 @@ fail_osdc: ceph_osdc_stop(&client->osdc); fail_monc: ceph_monc_stop(&client->monc); +fail_mempool: + mempool_destroy(client->wb_pagevec_pool); fail_trunc_wq: destroy_workqueue(client->trunc_wq); fail_pg_inv_wq: @@ -581,8 +591,7 @@ static void ceph_destroy_client(struct ceph_client *client) if (client->msgr) ceph_messenger_destroy(client->msgr); - if (client->wb_pagevec_pool) - mempool_destroy(client->wb_pagevec_pool); + mempool_destroy(client->wb_pagevec_pool); destroy_mount_args(client->mount_args); @@ -845,14 +854,6 @@ static int ceph_get_sb(struct file_system_type *fs_type, dout("get_sb got existing client %p\n", client); } else { dout("get_sb using new client %p\n", client); - - /* set up mempools */ - err = -ENOMEM; - client->wb_pagevec_pool = mempool_create_kmalloc_pool(10, - client->mount_args->wsize >> PAGE_CACHE_SHIFT); - if (!client->wb_pagevec_pool) - goto out_splat; - err = ceph_register_bdi(sb, client); if (err < 0) goto out_splat; -- cgit v0.10.2 From cfea1cf42b614583c02727d5bffd5a2384e92bda Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 18 Nov 2009 16:50:55 -0800 Subject: ceph: small cleanup in hash function Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_hash.c b/fs/ceph/ceph_hash.c index ac8be54..1c44e43 100644 --- a/fs/ceph/ceph_hash.c +++ b/fs/ceph/ceph_hash.c @@ -85,7 +85,7 @@ unsigned ceph_str_hash_linux(const char *str, unsigned length) unsigned long hash = 0; unsigned char c; - while (length-- > 0) { + while (length--) { c = *str++; hash = (hash + (c << 4) + (c >> 4)) * 11; } -- cgit v0.10.2 From 0743304d871559cb4c7c066357de2caa60e94c2f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 18 Nov 2009 16:50:41 -0800 Subject: ceph: fix debugfs entry, simplify fsid checks We may first learn our fsid from any of the mon, osd, or mds maps (whichever the monitor sends first). Consolidate checks in a single helper. Initialize the client debugfs entry then, since we need the fsid (and global_id) for the directory name. Also remove dead mount code. Signed-off-by: Sage Weil diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 9b20206..b90fc3e 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -7,6 +7,8 @@ #include "super.h" #include "mds_client.h" +#include "mon_client.h" +#include "auth.h" #ifdef CONFIG_DEBUG_FS @@ -335,7 +337,7 @@ int ceph_debugfs_client_init(struct ceph_client *client) char name[80]; snprintf(name, sizeof(name), FSID_FORMAT ".client%lld", - PR_FSID(&client->monc.monmap->fsid), client->whoami); + PR_FSID(&client->fsid), client->monc.auth->global_id); client->debugfs_dir = debugfs_create_dir(name, ceph_debugfs_dir); if (!client->debugfs_dir) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 8a28515..8d95b0f 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2782,16 +2782,8 @@ void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc, struct ceph_msg *msg) ceph_decode_need(&p, end, sizeof(fsid)+2*sizeof(u32), bad); ceph_decode_copy(&p, &fsid, sizeof(fsid)); - if (mdsc->client->monc.have_fsid) { - if (ceph_fsid_compare(&fsid, - &mdsc->client->monc.monmap->fsid)) { - pr_err("got mdsmap with wrong fsid\n"); - return; - } - } else { - ceph_fsid_set(&mdsc->client->monc.monmap->fsid, &fsid); - mdsc->client->monc.have_fsid = true; - } + if (ceph_check_fsid(mdsc->client, &fsid) < 0) + return; epoch = ceph_decode_32(&p); maplen = ceph_decode_32(&p); dout("handle_map epoch %u len %d\n", epoch, (int)maplen); diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 017d5ae..b742b3b 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -320,89 +320,12 @@ int ceph_monc_open_session(struct ceph_mon_client *monc) return 0; } -#if 0 - -/* - * The monitor responds with mount ack indicate mount success. The - * included client ticket allows the client to talk to MDSs and OSDs. - */ -static void handle_mount_ack(struct ceph_mon_client *monc, struct ceph_msg *msg) -{ - struct ceph_client *client = monc->client; - struct ceph_monmap *monmap = NULL, *old = monc->monmap; - void *p, *end; - s32 result; - u32 len; - s64 cnum; - int err = -EINVAL; - - if (client->whoami >= 0) { - dout("handle_mount_ack - already mounted\n"); - return; - } - - mutex_lock(&monc->mutex); - - dout("handle_mount_ack\n"); - p = msg->front.iov_base; - end = p + msg->front.iov_len; - - ceph_decode_64_safe(&p, end, cnum, bad); - ceph_decode_32_safe(&p, end, result, bad); - ceph_decode_32_safe(&p, end, len, bad); - if (result) { - pr_err("mount denied: %.*s (%d)\n", len, (char *)p, - result); - err = result; - goto out; - } - p += len; - - ceph_decode_32_safe(&p, end, len, bad); - ceph_decode_need(&p, end, len, bad); - monmap = ceph_monmap_decode(p, p + len); - if (IS_ERR(monmap)) { - pr_err("problem decoding monmap, %d\n", - (int)PTR_ERR(monmap)); - err = -EINVAL; - goto out; - } - p += len; - - client->monc.monmap = monmap; - kfree(old); - - client->signed_ticket = NULL; - client->signed_ticket_len = 0; - - monc->want_mount = false; - - client->whoami = cnum; - client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; - client->msgr->inst.name.num = cpu_to_le64(cnum); - pr_info("client%lld fsid " FSID_FORMAT "\n", - client->whoami, PR_FSID(&client->monc.monmap->fsid)); - - ceph_debugfs_client_init(client); - __send_subscribe(monc); - - err = 0; - goto out; - -bad: - pr_err("error decoding mount_ack message\n"); -out: - client->mount_err = err; - mutex_unlock(&monc->mutex); - wake_up(&client->mount_wq); -} -#endif - /* * The monitor responds with mount ack indicate mount success. The * included client ticket allows the client to talk to MDSs and OSDs. */ -static void ceph_monc_handle_map(struct ceph_mon_client *monc, struct ceph_msg *msg) +static void ceph_monc_handle_map(struct ceph_mon_client *monc, + struct ceph_msg *msg) { struct ceph_client *client = monc->client; struct ceph_monmap *monmap = NULL, *old = monc->monmap; @@ -420,42 +343,19 @@ static void ceph_monc_handle_map(struct ceph_mon_client *monc, struct ceph_msg * (int)PTR_ERR(monmap)); return; } - if (monc->have_fsid && - ceph_fsid_compare(&monmap->fsid, &monc->monmap->fsid)) { - print_hex_dump(KERN_ERR, "monmap->fsid: ", DUMP_PREFIX_NONE, 16, 1, - (void *)&monmap->fsid, 16, 0); - print_hex_dump(KERN_ERR, "monc->monmap->fsid: ", DUMP_PREFIX_NONE, 16, 1, - (void *)&monc->monmap->fsid, 16, 0); - - pr_err("fsid mismatch, got a previous map with different fsid"); + + if (ceph_check_fsid(monc->client, &monmap->fsid) < 0) { kfree(monmap); return; } client->monc.monmap = monmap; - client->monc.have_fsid = true; kfree(old); mutex_unlock(&monc->mutex); wake_up(&client->mount_wq); } - -/* - * init client info after authentication - */ -static void __init_authenticated_client(struct ceph_mon_client *monc) -{ - struct ceph_client *client = monc->client; - - client->signed_ticket = NULL; - client->signed_ticket_len = 0; - client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; - client->msgr->inst.name.num = monc->auth->global_id; - - ceph_debugfs_client_init(client); -} - /* * statfs */ @@ -754,7 +654,10 @@ static void handle_auth_reply(struct ceph_mon_client *monc, ceph_con_send(monc->con, monc->m_auth); } else if (monc->auth->ops->is_authenticated(monc->auth)) { dout("authenticated, starting session\n"); - __init_authenticated_client(monc); + + monc->client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; + monc->client->msgr->inst.name.num = monc->auth->global_id; + __send_subscribe(monc); __resend_statfs(monc); } diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index ca0ee68..d63f192 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -882,16 +882,8 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) /* verify fsid */ ceph_decode_need(&p, end, sizeof(fsid), bad); ceph_decode_copy(&p, &fsid, sizeof(fsid)); - if (osdc->client->monc.have_fsid) { - if (ceph_fsid_compare(&fsid, - &osdc->client->monc.monmap->fsid)) { - pr_err("got osdmap with wrong fsid, ignoring\n"); - return; - } - } else { - ceph_fsid_set(&osdc->client->monc.monmap->fsid, &fsid); - osdc->client->monc.have_fsid = true; - } + if (ceph_check_fsid(osdc->client, &fsid) < 0) + return; down_write(&osdc->map_sem); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index df05617..3df6d4a 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -19,6 +19,7 @@ #include "decode.h" #include "super.h" #include "mon_client.h" +#include "auth.h" /* * Ceph superblock operations @@ -510,14 +511,11 @@ static struct ceph_client *ceph_create_client(struct ceph_mount_args *args) client->sb = NULL; client->mount_state = CEPH_MOUNT_MOUNTING; - client->whoami = -1; client->mount_args = args; client->msgr = NULL; client->mount_err = 0; - client->signed_ticket = NULL; - client->signed_ticket_len = 0; err = bdi_init(&client->backing_dev_info); if (err < 0) @@ -582,8 +580,6 @@ static void ceph_destroy_client(struct ceph_client *client) ceph_monc_stop(&client->monc); ceph_osdc_stop(&client->osdc); - kfree(client->signed_ticket); - ceph_debugfs_client_cleanup(client); destroy_workqueue(client->wb_wq); destroy_workqueue(client->pg_inv_wq); @@ -600,6 +596,32 @@ static void ceph_destroy_client(struct ceph_client *client) } /* + * Initially learn our fsid, or verify an fsid matches. + */ +int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid) +{ + if (client->have_fsid) { + if (ceph_fsid_compare(&client->fsid, fsid)) { + print_hex_dump(KERN_ERR, "this fsid: ", + DUMP_PREFIX_NONE, 16, 1, + (void *)fsid, 16, 0); + print_hex_dump(KERN_ERR, " old fsid: ", + DUMP_PREFIX_NONE, 16, 1, + (void *)&client->fsid, 16, 0); + pr_err("fsid mismatch\n"); + return -1; + } + } else { + pr_info("client%lld fsid " FSID_FORMAT "\n", + client->monc.auth->global_id, PR_FSID(fsid)); + memcpy(&client->fsid, fsid, sizeof(*fsid)); + ceph_debugfs_client_init(client); + client->have_fsid = true; + } + return 0; +} + +/* * true if we have the mon map (and have thus joined the cluster) */ static int have_mon_map(struct ceph_client *client) diff --git a/fs/ceph/super.h b/fs/ceph/super.h index e0e8130..de5e324 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -113,16 +113,11 @@ static inline unsigned long time_sub(unsigned long a, unsigned long b) * mounting the same ceph filesystem/cluster. */ struct ceph_client { - __s64 whoami; /* my client number */ -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_monmap; - struct dentry *debugfs_mdsmap, *debugfs_osdmap; - struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps; -#endif + struct ceph_fsid fsid; + bool have_fsid; struct mutex mount_mutex; /* serialize mount attempts */ struct ceph_mount_args *mount_args; - struct ceph_fsid fsid; struct super_block *sb; @@ -130,8 +125,6 @@ struct ceph_client { wait_queue_head_t mount_wq; int mount_err; - void *signed_ticket; /* our keys to the kingdom */ - int signed_ticket_len; struct ceph_messenger *msgr; /* messenger instance */ struct ceph_mon_client monc; @@ -145,6 +138,12 @@ struct ceph_client { struct workqueue_struct *trunc_wq; struct backing_dev_info backing_dev_info; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_monmap; + struct dentry *debugfs_mdsmap, *debugfs_osdmap; + struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps; +#endif }; static inline struct ceph_client *ceph_client(struct super_block *sb) @@ -735,6 +734,7 @@ extern struct kmem_cache *ceph_dentry_cachep; extern struct kmem_cache *ceph_file_cachep; extern const char *ceph_msg_type_name(int type); +extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid); #define FSID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \ "%02x%02x%02x%02x%02x%02x" -- cgit v0.10.2 From 94045e115ee72aee3b17295791da07078f2f778c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 19 Nov 2009 15:31:50 -0800 Subject: ceph: decode updated mdsmap format The mds map now uses the global_id as the 'key' (instead of the addr, which was a poor choice). This is protocol change. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 1e96a9a..4e5f49c 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -39,7 +39,7 @@ #define CEPH_MDS_PROTOCOL 9 /* cluster internal */ #define CEPH_MON_PROTOCOL 5 /* cluster internal */ #define CEPH_OSDC_PROTOCOL 22 /* server/client */ -#define CEPH_MDSC_PROTOCOL 29 /* server/client */ +#define CEPH_MDSC_PROTOCOL 30 /* server/client */ #define CEPH_MONC_PROTOCOL 15 /* server/client */ diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 4226c81..cad8d25 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -76,6 +76,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) /* pick out active nodes from mds_info (state > 0) */ n = ceph_decode_32(p); for (i = 0; i < n; i++) { + u64 global_id; u32 namelen; s32 mds, inc, state; u64 state_seq; @@ -84,10 +85,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) u32 num_export_targets; void *pexport_targets = NULL; - ceph_decode_need(p, end, sizeof(addr) + 1 + sizeof(u32), bad); - ceph_decode_copy(p, &addr, sizeof(addr)); - ceph_decode_addr(&addr); + ceph_decode_need(p, end, sizeof(u64)*2 + 1 + sizeof(u32), bad); + global_id = ceph_decode_64(p); infoversion = ceph_decode_8(p); + *p += sizeof(u64); namelen = ceph_decode_32(p); /* skip mds name */ *p += namelen; @@ -99,7 +100,8 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) inc = ceph_decode_32(p); state = ceph_decode_32(p); state_seq = ceph_decode_64(p); - *p += sizeof(addr); + ceph_decode_copy(p, &addr, sizeof(addr)); + ceph_decode_addr(&addr); *p += sizeof(struct ceph_timespec); *p += sizeof(u32); ceph_decode_32_safe(p, end, namelen, bad); @@ -112,10 +114,11 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) num_export_targets = 0; } - dout("mdsmap_decode %d/%d mds%d.%d %s %s\n", - i+1, n, mds, inc, pr_addr(&addr.in_addr), + dout("mdsmap_decode %d/%d %lld mds%d.%d %s %s\n", + i+1, n, global_id, mds, inc, pr_addr(&addr.in_addr), ceph_mds_state_name(state)); if (mds >= 0 && mds < m->m_max_mds && state > 0) { + m->m_info[mds].global_id = global_id; m->m_info[mds].state = state; m->m_info[mds].addr = addr; m->m_info[mds].num_export_targets = num_export_targets; diff --git a/fs/ceph/mdsmap.h b/fs/ceph/mdsmap.h index d317308..eacc131 100644 --- a/fs/ceph/mdsmap.h +++ b/fs/ceph/mdsmap.h @@ -9,6 +9,7 @@ * we limit fields to those the client actually xcares about */ struct ceph_mds_info { + u64 global_id; struct ceph_entity_addr addr; s32 state; int num_export_targets; -- cgit v0.10.2 From dc14657c9c946f25b84a98e9ffa41b812a70699e Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Fri, 20 Nov 2009 13:59:13 -0800 Subject: ceph: mount fails immediately on error Signed-off-by: Yehuda Sadeh diff --git a/fs/ceph/auth.c b/fs/ceph/auth.c index c4d1eee..32f2e2a 100644 --- a/fs/ceph/auth.c +++ b/fs/ceph/auth.c @@ -169,6 +169,11 @@ int ceph_handle_auth_reply(struct ceph_auth_client *ac, } if (ac->negotiating) { + /* server does not support our protocols? */ + if (!protocol && result < 0) { + ret = result; + goto out; + } /* set up (new) protocol handler? */ if (ac->protocol && ac->protocol != protocol) { ac->ops->destroy(ac); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 3df6d4a..a828943 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -712,10 +712,14 @@ static int ceph_mount(struct ceph_client *client, struct vfsmount *mnt, /* wait */ dout("mount waiting for mon_map\n"); err = wait_event_interruptible_timeout(client->mount_wq, /* FIXME */ - have_mon_map(client), + have_mon_map(client) || (client->mount_err < 0), timeout); if (err == -EINTR || err == -ERESTARTSYS) goto out; + if (client->mount_err < 0) { + err = client->mount_err; + goto out; + } } dout("mount opening root\n"); -- cgit v0.10.2 From 0dc2570fab222affe7739b88b5ed04c511d433dc Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 20 Nov 2009 13:43:45 -0800 Subject: ceph: reset requested max_size after mds reconnect The max_size increase request to the MDS can get lost during an MDS restart and reconnect. Reset our requested value after the MDS recovers, so that any blocked writes will re-request a larger max_size upon waking. Also, explicit wake session caps after the reconnect. Normally the cap renewal catches this, but not in the cases where the caps didn't go stale in the first place, which would leave writers waiting on max_size asleep. Signed-off-by: Sage Weil diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 8d95b0f..7da8369 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -746,14 +746,24 @@ static void remove_session_caps(struct ceph_mds_session *session) static int wake_up_session_cb(struct inode *inode, struct ceph_cap *cap, void *arg) { - wake_up(&ceph_inode(inode)->i_cap_wq); + struct ceph_inode_info *ci = ceph_inode(inode); + + wake_up(&ci->i_cap_wq); + if (arg) { + spin_lock(&inode->i_lock); + ci->i_wanted_max_size = 0; + ci->i_requested_max_size = 0; + spin_unlock(&inode->i_lock); + } return 0; } -static void wake_up_session_caps(struct ceph_mds_session *session) +static void wake_up_session_caps(struct ceph_mds_session *session, + int reconnect) { dout("wake_up_session_caps %p mds%d\n", session, session->s_mds); - iterate_session_caps(session, wake_up_session_cb, NULL); + iterate_session_caps(session, wake_up_session_cb, + (void *)(unsigned long)reconnect); } /* @@ -794,6 +804,8 @@ static int send_renew_caps(struct ceph_mds_client *mdsc, /* * Note new cap ttl, and any transition from stale -> not stale (fresh?). + * + * Called under session->s_mutex */ static void renewed_caps(struct ceph_mds_client *mdsc, struct ceph_mds_session *session, int is_renew) @@ -822,7 +834,7 @@ static void renewed_caps(struct ceph_mds_client *mdsc, spin_unlock(&session->s_cap_lock); if (wake) - wake_up_session_caps(session); + wake_up_session_caps(session, 0); } /* @@ -2248,6 +2260,7 @@ static void check_new_map(struct ceph_mds_client *mdsc, pr_info("mds%d reconnect completed\n", s->s_mds); kick_requests(mdsc, i, 1); ceph_kick_flushing_caps(mdsc, s); + wake_up_session_caps(s, 1); } } } -- cgit v0.10.2 From 03c677e1d189ff62891d9f278c55bb798a418b81 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 20 Nov 2009 15:14:15 -0800 Subject: ceph: reset msgr backoff during open, not after successful handshake Reset the backoff delay when we reopen the connection, so that the delays for any initial connection problems are reasonable. We were resetting only after a successful handshake, which was of limited utility. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 0b16748..45cec31 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -348,6 +348,7 @@ void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr) set_bit(OPENING, &con->state); clear_bit(CLOSED, &con->state); memcpy(&con->peer_addr, addr, sizeof(*addr)); + con->delay = 0; /* reset backoff memory */ queue_con(con); } @@ -1162,8 +1163,6 @@ static int process_connect(struct ceph_connection *con) con->connect_seq); WARN_ON(con->connect_seq != le32_to_cpu(con->in_reply.connect_seq)); - - con->delay = 0; /* reset backoff memory */ prepare_read_tag(con); break; -- cgit v0.10.2 From b19a29af74c09553b9fef95cdf6e9af3df65f544 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 20 Nov 2009 14:44:18 -0800 Subject: ceph: remove dead code Left over from mount/auth protocol changes. Signed-off-by: Sage Weil diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index b742b3b..9ff2da6 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -278,27 +278,6 @@ void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc) mutex_unlock(&monc->mutex); } -#if 0 -/* - * mount - */ -static void __request_mount(struct ceph_mon_client *monc) -{ - struct ceph_msg *msg; - struct ceph_client_mount *h; - - dout("__request_mount\n"); - msg = ceph_msg_new(CEPH_MSG_CLIENT_MOUNT, sizeof(*h), 0, 0, NULL); - if (IS_ERR(msg)) - return; - h = msg->front.iov_base; - h->monhdr.have_version = 0; - h->monhdr.session_mon = cpu_to_le16(-1); - h->monhdr.session_mon_tid = 0; - ceph_con_send(monc->con, msg); -} -#endif - /* * */ -- cgit v0.10.2 From 60d877334f7d9f5f2417ea4a83c1def769286102 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 21 Nov 2009 12:53:08 +0100 Subject: fs/ceph: introduce missing kfree Error handling code following a kmalloc should free the allocated data. The semantic match that finds this problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; statement S; expression E; identifier f,f1,l; position p1,p2; expression *ptr != NULL; @@ x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S <... when != x when != if (...) { <+...x...+> } ( x->f1 = E | (x->f1 == NULL || ...) | f(...,x->f1,...) ) ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // Signed-off-by: Julia Lawall Signed-off-by: Sage Weil diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 1a48a55..04769a3 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -655,8 +655,10 @@ static int ceph_sync_setxattr(struct dentry *dentry, const char *name, /* do request */ req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETXATTR, USE_AUTH_MDS); - if (IS_ERR(req)) - return PTR_ERR(req); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto out; + } req->r_inode = igrab(inode); req->r_inode_drop = CEPH_CAP_XATTR_SHARED; req->r_num_caps = 1; -- cgit v0.10.2 From 32c895e776a0dd2cb701d60fbd6440280c09ce35 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 21 Nov 2009 16:53:16 +0100 Subject: fs/ceph: Move a dereference below a NULL test If the NULL test is necessary, then the dereference should be moved below the NULL test. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/). // @@ type T; expression E; identifier i,fld; statement S; @@ - T i = E->fld; + T i; ... when != E when != i if (E == NULL) S + i = E->fld; // Signed-off-by: Julia Lawall Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index d63f192..5d30d59 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -1249,11 +1249,12 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) { struct ceph_osd *osd = con->private; - struct ceph_osd_client *osdc = osd->o_osdc; + struct ceph_osd_client *osdc; int type = le16_to_cpu(msg->hdr.type); if (!osd) return; + osdc = osd->o_osdc; switch (type) { case CEPH_MSG_OSD_MAP: -- cgit v0.10.2 From 75eb3592811028e5b01835126483d115532a3aa1 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sat, 21 Nov 2009 13:08:14 -0800 Subject: ceph: remove useless IS_ERR checks ceph_lookup_snap_realm either returns a valid pointer or NULL; there is no need to check IS_ERR(result). Reported-by: Julia Lawall Signed-off-by: Sage Weil diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 2e3cb40..52f46a1 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -226,8 +226,6 @@ static int adjust_snap_realm_parent(struct ceph_mds_client *mdsc, return 0; parent = ceph_lookup_snap_realm(mdsc, parentino); - if (IS_ERR(parent)) - return PTR_ERR(parent); if (!parent) { parent = ceph_create_snap_realm(mdsc, parentino); if (IS_ERR(parent)) @@ -541,10 +539,6 @@ more: p += sizeof(u64) * le32_to_cpu(ri->num_prior_parent_snaps); realm = ceph_lookup_snap_realm(mdsc, le64_to_cpu(ri->ino)); - if (IS_ERR(realm)) { - err = PTR_ERR(realm); - goto fail; - } if (!realm) { realm = ceph_create_snap_realm(mdsc, le64_to_cpu(ri->ino)); if (IS_ERR(realm)) { @@ -762,8 +756,6 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc, ri = p; realm = ceph_lookup_snap_realm(mdsc, split); - if (IS_ERR(realm)) - goto out; if (!realm) { realm = ceph_create_snap_realm(mdsc, split); if (IS_ERR(realm)) @@ -829,8 +821,6 @@ skip_inode: struct ceph_snap_realm *child = ceph_lookup_snap_realm(mdsc, le64_to_cpu(split_realms[i])); - if (IS_ERR(child)) - continue; if (!child) continue; adjust_snap_realm_parent(mdsc, child, realm->ino); -- cgit v0.10.2 From 34b43a56b9b103a7a820032177131532d9dbdbe8 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 1 Dec 2009 12:23:54 -0800 Subject: ceph: plug leak of request_mutex Fix leak of osd client request_mutex on receiving dup ack. Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 5d30d59..d600073 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -739,6 +739,7 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg) req->r_got_reply = 1; } else if ((flags & CEPH_OSD_FLAG_ONDISK) == 0) { dout("handle_reply tid %llu dup ack\n", tid); + mutex_unlock(&osdc->request_mutex); goto done; } -- cgit v0.10.2 From 50b885b96c903e420a1eac54dd27626244704a06 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 1 Dec 2009 14:12:07 -0800 Subject: ceph: whitespace cleanup Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 9dd1106..9b9ce14 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1316,7 +1316,7 @@ static int __mark_caps_flushing(struct inode *inode, struct ceph_mds_client *mdsc = &ceph_client(inode->i_sb)->mdsc; struct ceph_inode_info *ci = ceph_inode(inode); int flushing; - + BUG_ON(ci->i_dirty_caps == 0); BUG_ON(list_empty(&ci->i_dirty_item)); diff --git a/fs/ceph/ceph_hash.c b/fs/ceph/ceph_hash.c index 1c44e43..bd57001 100644 --- a/fs/ceph/ceph_hash.c +++ b/fs/ceph/ceph_hash.c @@ -82,14 +82,14 @@ unsigned ceph_str_hash_rjenkins(const char *str, unsigned length) */ unsigned ceph_str_hash_linux(const char *str, unsigned length) { - unsigned long hash = 0; + unsigned long hash = 0; unsigned char c; - while (length--) { + while (length--) { c = *str++; hash = (hash + (c << 4) + (c >> 4)) * 11; } - return hash; + return hash; } @@ -105,7 +105,7 @@ unsigned ceph_str_hash(int type, const char *s, unsigned len) } } -const char *ceph_str_hash_name(int type) +const char *ceph_str_hash_name(int type) { switch (type) { case CEPH_STR_HASH_LINUX: diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c index 2523d44..9ba54ef 100644 --- a/fs/ceph/crush/mapper.c +++ b/fs/ceph/crush/mapper.c @@ -254,7 +254,7 @@ static int crush_bucket_choose(struct crush_bucket *in, int x, int r) x, r); default: BUG_ON(1); - return in->items[0]; + return in->items[0]; } } diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 9ff2da6..1dd0dc2 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -279,7 +279,7 @@ void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc) } /* - * + * */ int ceph_monc_open_session(struct ceph_mon_client *monc) { diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index d600073..d639c74 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -1308,8 +1308,8 @@ static void put_osd_con(struct ceph_connection *con) * authentication */ static int get_authorizer(struct ceph_connection *con, - void **buf, int *len, int *proto, - void **reply_buf, int *reply_len, int force_new) + void **buf, int *len, int *proto, + void **reply_buf, int *reply_len, int force_new) { struct ceph_osd *o = con->private; struct ceph_osd_client *osdc = o->o_osdc; -- cgit v0.10.2 From 1d1de9160e0d8aff0d67a21137b62e63ffd6f184 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 2 Dec 2009 11:54:25 -0800 Subject: ceph: hide /.ceph from readdir results We need to skip /.ceph in (cached) readdir results, and exclude "/.ceph" from the cached ENOENT lookup check. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 4e5f49c..699196a 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -44,6 +44,7 @@ #define CEPH_INO_ROOT 1 +#define CEPH_INO_CEPH 2 /* hidden .ceph dir */ /* arbitrary limit on max # of monitors (cluster of 3 is typical) */ #define CEPH_MAX_MON 31 diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 32ef543..89ce3ba 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -132,6 +132,7 @@ more: } if (!d_unhashed(dentry) && dentry->d_inode && ceph_snap(dentry->d_inode) != CEPH_SNAPDIR && + ceph_ino(dentry->d_inode) != CEPH_INO_CEPH && filp->f_pos <= di->offset) break; dout(" skipping %p %.*s at %llu (%llu)%s%s\n", dentry, @@ -512,6 +513,12 @@ struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, return dentry; } +static int is_root_ceph_dentry(struct inode *inode, struct dentry *dentry) +{ + return ceph_ino(inode) == CEPH_INO_ROOT && + strncmp(dentry->d_name.name, ".ceph", 5) == 0; +} + /* * Look up a single dir entry. If there is a lookup intent, inform * the MDS so that it gets our 'caps wanted' value in a single op. @@ -554,6 +561,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, if (strncmp(dentry->d_name.name, client->mount_args->snapdir_name, dentry->d_name.len) && + !is_root_ceph_dentry(dir, dentry) && (ci->i_ceph_flags & CEPH_I_COMPLETE) && (__ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1))) { di->offset = ci->i_max_offset++; -- cgit v0.10.2 From 33d4909ccc094b8262667bccdd52e01458bee0df Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 2 Dec 2009 14:42:39 -0800 Subject: ceph: allow preferred osd to be get/set via layout ioctl There is certainly no reason not to report this. The only real downside to allowing the user to set it is that you don't get default values by zeroing the layout struct (the default is -1). Signed-off-by: Sage Weil diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index 4c33e19..8a5bcae 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -24,6 +24,8 @@ static long ceph_ioctl_get_layout(struct file *file, void __user *arg) l.stripe_count = ceph_file_layout_stripe_count(ci->i_layout); l.object_size = ceph_file_layout_object_size(ci->i_layout); l.data_pool = le32_to_cpu(ci->i_layout.fl_pg_pool); + l.preferred_osd = + (s32)le32_to_cpu(ci->i_layout.fl_pg_preferred); if (copy_to_user(arg, &l, sizeof(l))) return -EFAULT; } @@ -79,7 +81,8 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg) req->r_args.setlayout.layout.fl_object_size = cpu_to_le32(l.object_size); req->r_args.setlayout.layout.fl_pg_pool = cpu_to_le32(l.data_pool); - req->r_args.setlayout.layout.fl_pg_preferred = cpu_to_le32((s32)-1); + req->r_args.setlayout.layout.fl_pg_preferred = + cpu_to_le32(l.preferred_osd); err = ceph_mdsc_do_request(mdsc, parent_inode, req); ceph_mdsc_put_request(req); diff --git a/fs/ceph/ioctl.h b/fs/ceph/ioctl.h index 3c511da..25e4f1a 100644 --- a/fs/ceph/ioctl.h +++ b/fs/ceph/ioctl.h @@ -10,6 +10,7 @@ struct ceph_ioctl_layout { __u64 stripe_unit, stripe_count, object_size; __u64 data_pool; + __s64 preferred_osd; }; #define CEPH_IOC_GET_LAYOUT _IOR(CEPH_IOCTL_MAGIC, 1, \ -- cgit v0.10.2 From fb99f8810965b7e5a00e9754bd7bf556b00ba0c2 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 3 Dec 2009 15:04:08 -0800 Subject: ceph: update MAINTAINERS entry with correct git URL Signed-off-by: Sage Weil diff --git a/MAINTAINERS b/MAINTAINERS index 9b680ff..6fc10aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1298,7 +1298,7 @@ CEPH DISTRIBUTED FILE SYSTEM CLIENT M: Sage Weil L: ceph-devel@lists.sourceforge.net W: http://ceph.newdream.net/ -T: git git://ceph.newdream.net/linux-ceph-client.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git S: Supported F: Documentation/filesystems/ceph.txt F: fs/ceph -- cgit v0.10.2 From 2f2ffd35822688a3650e503197b8724f47312748 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 4 Dec 2009 10:27:17 -0800 Subject: ceph: mark v0.18 release Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 699196a..e2fd024 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -19,7 +19,7 @@ * Ceph release version */ #define CEPH_VERSION_MAJOR 0 -#define CEPH_VERSION_MINOR 17 +#define CEPH_VERSION_MINOR 18 #define CEPH_VERSION_PATCH 0 #define _CEPH_STRINGIFY(x) #x -- cgit v0.10.2 From dd26d857a7bf1b5b734a23180c19eac3e46db944 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sat, 5 Dec 2009 10:13:33 -0800 Subject: ceph: use kref for ceph_buffer Signed-off-by: Sage Weil diff --git a/fs/ceph/buffer.c b/fs/ceph/buffer.c index cf9aacc..847c5da 100644 --- a/fs/ceph/buffer.c +++ b/fs/ceph/buffer.c @@ -9,13 +9,25 @@ struct ceph_buffer *ceph_buffer_new(gfp_t gfp) b = kmalloc(sizeof(*b), gfp); if (!b) return NULL; - atomic_set(&b->nref, 1); + kref_init(&b->kref); b->vec.iov_base = NULL; b->vec.iov_len = 0; b->alloc_len = 0; return b; } +void ceph_buffer_release(struct kref *kref) +{ + struct ceph_buffer *b = container_of(kref, struct ceph_buffer, kref); + if (b->vec.iov_base) { + if (b->is_vmalloc) + vfree(b->vec.iov_base); + else + kfree(b->vec.iov_base); + } + kfree(b); +} + int ceph_buffer_alloc(struct ceph_buffer *b, int len, gfp_t gfp) { b->vec.iov_base = kmalloc(len, gfp | __GFP_NOWARN); diff --git a/fs/ceph/buffer.h b/fs/ceph/buffer.h index 16b1930..3f541a1 100644 --- a/fs/ceph/buffer.h +++ b/fs/ceph/buffer.h @@ -1,6 +1,7 @@ #ifndef __FS_CEPH_BUFFER_H #define __FS_CEPH_BUFFER_H +#include #include #include #include @@ -13,7 +14,7 @@ * sizes. */ struct ceph_buffer { - atomic_t nref; + struct kref kref; struct kvec vec; size_t alloc_len; bool is_vmalloc; @@ -24,21 +25,16 @@ int ceph_buffer_alloc(struct ceph_buffer *b, int len, gfp_t gfp); static inline struct ceph_buffer *ceph_buffer_get(struct ceph_buffer *b) { - atomic_inc(&b->nref); + kref_get(&b->kref); return b; } +void ceph_buffer_release(struct kref *kref); + static inline void ceph_buffer_put(struct ceph_buffer *b) { - if (b && atomic_dec_and_test(&b->nref)) { - if (b->vec.iov_base) { - if (b->is_vmalloc) - vfree(b->vec.iov_base); - else - kfree(b->vec.iov_base); - } - kfree(b); - } + if (b) + kref_put(&b->kref, ceph_buffer_release); } static inline struct ceph_buffer *ceph_buffer_new_alloc(int len, gfp_t gfp) -- cgit v0.10.2 From b6c1d5b81ea0841ae9d3ce2cda319ab986b081cf Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 7 Dec 2009 12:17:17 -0800 Subject: ceph: simplify ceph_buffer interface We never allocate the ceph_buffer and buffer separtely, so use a single constructor. Disallow put on NULL buffer; make the caller check. Signed-off-by: Sage Weil diff --git a/fs/ceph/buffer.c b/fs/ceph/buffer.c index 847c5da..2576bd4 100644 --- a/fs/ceph/buffer.c +++ b/fs/ceph/buffer.c @@ -2,23 +2,38 @@ #include "ceph_debug.h" #include "buffer.h" -struct ceph_buffer *ceph_buffer_new(gfp_t gfp) +struct ceph_buffer *ceph_buffer_new(size_t len, gfp_t gfp) { struct ceph_buffer *b; b = kmalloc(sizeof(*b), gfp); if (!b) return NULL; + + b->vec.iov_base = kmalloc(len, gfp | __GFP_NOWARN); + if (b->vec.iov_base) { + b->is_vmalloc = false; + } else { + b->vec.iov_base = __vmalloc(len, gfp, PAGE_KERNEL); + if (!b->vec.iov_base) { + kfree(b); + return NULL; + } + b->is_vmalloc = true; + } + kref_init(&b->kref); - b->vec.iov_base = NULL; - b->vec.iov_len = 0; - b->alloc_len = 0; + b->alloc_len = len; + b->vec.iov_len = len; + dout("buffer_new %p\n", b); return b; } void ceph_buffer_release(struct kref *kref) { struct ceph_buffer *b = container_of(kref, struct ceph_buffer, kref); + + dout("buffer_release %p\n", b); if (b->vec.iov_base) { if (b->is_vmalloc) vfree(b->vec.iov_base); diff --git a/fs/ceph/buffer.h b/fs/ceph/buffer.h index 3f541a1..47b9514 100644 --- a/fs/ceph/buffer.h +++ b/fs/ceph/buffer.h @@ -20,8 +20,8 @@ struct ceph_buffer { bool is_vmalloc; }; -struct ceph_buffer *ceph_buffer_new(gfp_t gfp); -int ceph_buffer_alloc(struct ceph_buffer *b, int len, gfp_t gfp); +extern struct ceph_buffer *ceph_buffer_new(size_t len, gfp_t gfp); +extern void ceph_buffer_release(struct kref *kref); static inline struct ceph_buffer *ceph_buffer_get(struct ceph_buffer *b) { @@ -29,23 +29,9 @@ static inline struct ceph_buffer *ceph_buffer_get(struct ceph_buffer *b) return b; } -void ceph_buffer_release(struct kref *kref); - static inline void ceph_buffer_put(struct ceph_buffer *b) { - if (b) - kref_put(&b->kref, ceph_buffer_release); -} - -static inline struct ceph_buffer *ceph_buffer_new_alloc(int len, gfp_t gfp) -{ - struct ceph_buffer *b = ceph_buffer_new(gfp); - - if (b && ceph_buffer_alloc(b, len, gfp) < 0) { - ceph_buffer_put(b); - b = NULL; - } - return b; + kref_put(&b->kref, ceph_buffer_release); } #endif diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 074ee42..db68468 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -383,8 +383,10 @@ void ceph_destroy_inode(struct inode *inode) } __ceph_destroy_xattrs(ci); - ceph_buffer_put(ci->i_xattrs.blob); - ceph_buffer_put(ci->i_xattrs.prealloc_blob); + if (ci->i_xattrs.blob) + ceph_buffer_put(ci->i_xattrs.blob); + if (ci->i_xattrs.prealloc_blob) + ceph_buffer_put(ci->i_xattrs.prealloc_blob); kmem_cache_free(ceph_inode_cachep, ci); } @@ -526,7 +528,7 @@ static int fill_inode(struct inode *inode, * bytes are the xattr count). */ if (iinfo->xattr_len > 4) { - xattr_blob = ceph_buffer_new_alloc(iinfo->xattr_len, GFP_NOFS); + xattr_blob = ceph_buffer_new(iinfo->xattr_len, GFP_NOFS); if (!xattr_blob) pr_err("fill_inode ENOMEM xattr blob %d bytes\n", iinfo->xattr_len); @@ -715,7 +717,8 @@ no_change: err = 0; out: - ceph_buffer_put(xattr_blob); + if (xattr_blob) + ceph_buffer_put(xattr_blob); return err; } diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 45cec31..bf76210 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -2047,7 +2047,7 @@ int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg) BUG_ON(!middle_len); BUG_ON(msg->middle); - msg->middle = ceph_buffer_new_alloc(middle_len, GFP_NOFS); + msg->middle = ceph_buffer_new(middle_len, GFP_NOFS); if (!msg->middle) return -ENOMEM; return 0; diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 04769a3..37d6ce6 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -482,7 +482,8 @@ void __ceph_build_xattrs_blob(struct ceph_inode_info *ci) ci->i_xattrs.prealloc_blob->vec.iov_len = dest - ci->i_xattrs.prealloc_blob->vec.iov_base; - ceph_buffer_put(ci->i_xattrs.blob); + if (ci->i_xattrs.blob) + ceph_buffer_put(ci->i_xattrs.blob); ci->i_xattrs.blob = ci->i_xattrs.prealloc_blob; ci->i_xattrs.prealloc_blob = NULL; ci->i_xattrs.dirty = false; @@ -745,11 +746,12 @@ retry: spin_unlock(&inode->i_lock); dout(" preaallocating new blob size=%d\n", required_blob_size); - blob = ceph_buffer_new_alloc(required_blob_size, GFP_NOFS); + blob = ceph_buffer_new(required_blob_size, GFP_NOFS); if (!blob) goto out; spin_lock(&inode->i_lock); - ceph_buffer_put(ci->i_xattrs.prealloc_blob); + if (ci->i_xattrs.prealloc_blob) + ceph_buffer_put(ci->i_xattrs.prealloc_blob); ci->i_xattrs.prealloc_blob = blob; goto retry; } -- cgit v0.10.2 From 153c8e6bf7ffee561e046e60b26ef6486c6fc9f2 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 7 Dec 2009 12:31:09 -0800 Subject: ceph: use kref for struct ceph_mds_request Signed-off-by: Sage Weil diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 7da8369..739093f 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -400,41 +400,40 @@ static void put_request_session(struct ceph_mds_request *req) } } -void ceph_mdsc_put_request(struct ceph_mds_request *req) -{ - dout("mdsc put_request %p %d -> %d\n", req, - atomic_read(&req->r_ref), atomic_read(&req->r_ref)-1); - if (atomic_dec_and_test(&req->r_ref)) { - if (req->r_request) - ceph_msg_put(req->r_request); - if (req->r_reply) { - ceph_msg_put(req->r_reply); - destroy_reply_info(&req->r_reply_info); - } - if (req->r_inode) { - ceph_put_cap_refs(ceph_inode(req->r_inode), - CEPH_CAP_PIN); - iput(req->r_inode); - } - if (req->r_locked_dir) - ceph_put_cap_refs(ceph_inode(req->r_locked_dir), - CEPH_CAP_PIN); - if (req->r_target_inode) - iput(req->r_target_inode); - if (req->r_dentry) - dput(req->r_dentry); - if (req->r_old_dentry) { - ceph_put_cap_refs( - ceph_inode(req->r_old_dentry->d_parent->d_inode), - CEPH_CAP_PIN); - dput(req->r_old_dentry); - } - kfree(req->r_path1); - kfree(req->r_path2); - put_request_session(req); - ceph_unreserve_caps(&req->r_caps_reservation); - kfree(req); +void ceph_mdsc_release_request(struct kref *kref) +{ + struct ceph_mds_request *req = container_of(kref, + struct ceph_mds_request, + r_kref); + if (req->r_request) + ceph_msg_put(req->r_request); + if (req->r_reply) { + ceph_msg_put(req->r_reply); + destroy_reply_info(&req->r_reply_info); + } + if (req->r_inode) { + ceph_put_cap_refs(ceph_inode(req->r_inode), + CEPH_CAP_PIN); + iput(req->r_inode); + } + if (req->r_locked_dir) + ceph_put_cap_refs(ceph_inode(req->r_locked_dir), + CEPH_CAP_PIN); + if (req->r_target_inode) + iput(req->r_target_inode); + if (req->r_dentry) + dput(req->r_dentry); + if (req->r_old_dentry) { + ceph_put_cap_refs( + ceph_inode(req->r_old_dentry->d_parent->d_inode), + CEPH_CAP_PIN); + dput(req->r_old_dentry); } + kfree(req->r_path1); + kfree(req->r_path2); + put_request_session(req); + ceph_unreserve_caps(&req->r_caps_reservation); + kfree(req); } /* @@ -1097,7 +1096,7 @@ ceph_mdsc_create_request(struct ceph_mds_client *mdsc, int op, int mode) req->r_resend_mds = -1; INIT_LIST_HEAD(&req->r_unsafe_dir_item); req->r_fmode = -1; - atomic_set(&req->r_ref, 1); /* one for request_tree, one for caller */ + kref_init(&req->r_kref); INIT_LIST_HEAD(&req->r_wait); init_completion(&req->r_completion); init_completion(&req->r_safe_completion); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 9faa1b2..41af5ca 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -2,6 +2,7 @@ #define _FS_CEPH_MDS_CLIENT_H #include +#include #include #include #include @@ -203,7 +204,7 @@ struct ceph_mds_request { int r_num_stale; int r_resend_mds; /* mds to resend to next, if any*/ - atomic_t r_ref; + struct kref r_kref; struct list_head r_wait; struct completion r_completion; struct completion r_safe_completion; @@ -306,9 +307,13 @@ extern int ceph_mdsc_do_request(struct ceph_mds_client *mdsc, struct ceph_mds_request *req); static inline void ceph_mdsc_get_request(struct ceph_mds_request *req) { - atomic_inc(&req->r_ref); + kref_get(&req->r_kref); +} +extern void ceph_mdsc_release_request(struct kref *kref); +static inline void ceph_mdsc_put_request(struct ceph_mds_request *req) +{ + kref_put(&req->r_kref, ceph_mdsc_release_request); } -extern void ceph_mdsc_put_request(struct ceph_mds_request *req); extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc); -- cgit v0.10.2 From 415e49a9c4faf1a1480b1497da2037608e5aa2c5 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 7 Dec 2009 13:37:03 -0800 Subject: ceph: use kref for ceph_osd_request Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index d639c74..67ef8ab 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -77,25 +77,24 @@ static void calc_layout(struct ceph_osd_client *osdc, /* * requests */ -void ceph_osdc_put_request(struct ceph_osd_request *req) +void ceph_osdc_release_request(struct kref *kref) { - dout("osdc put_request %p %d -> %d\n", req, atomic_read(&req->r_ref), - atomic_read(&req->r_ref)-1); - BUG_ON(atomic_read(&req->r_ref) <= 0); - if (atomic_dec_and_test(&req->r_ref)) { - if (req->r_request) - ceph_msg_put(req->r_request); - if (req->r_reply) - ceph_msg_put(req->r_reply); - if (req->r_own_pages) - ceph_release_page_vector(req->r_pages, - req->r_num_pages); - ceph_put_snap_context(req->r_snapc); - if (req->r_mempool) - mempool_free(req, req->r_osdc->req_mempool); - else - kfree(req); - } + struct ceph_osd_request *req = container_of(kref, + struct ceph_osd_request, + r_kref); + + if (req->r_request) + ceph_msg_put(req->r_request); + if (req->r_reply) + ceph_msg_put(req->r_reply); + if (req->r_own_pages) + ceph_release_page_vector(req->r_pages, + req->r_num_pages); + ceph_put_snap_context(req->r_snapc); + if (req->r_mempool) + mempool_free(req, req->r_osdc->req_mempool); + else + kfree(req); } /* @@ -149,7 +148,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, req->r_osdc = osdc; req->r_mempool = use_mempool; - atomic_set(&req->r_ref, 1); + kref_init(&req->r_kref); init_completion(&req->r_completion); init_completion(&req->r_safe_completion); INIT_LIST_HEAD(&req->r_unsafe_item); diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index 3d4ae65..20ee618 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -2,6 +2,7 @@ #define _FS_CEPH_OSD_CLIENT_H #include +#include #include #include @@ -49,7 +50,7 @@ struct ceph_osd_request { int r_prepared_pages, r_got_reply; struct ceph_osd_client *r_osdc; - atomic_t r_ref; + struct kref r_kref; bool r_mempool; struct completion r_completion, r_safe_completion; ceph_osdc_callback_t r_callback, r_safe_callback; @@ -118,9 +119,13 @@ extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, static inline void ceph_osdc_get_request(struct ceph_osd_request *req) { - atomic_inc(&req->r_ref); + kref_get(&req->r_kref); +} +extern void ceph_osdc_release_request(struct kref *kref); +static inline void ceph_osdc_put_request(struct ceph_osd_request *req) +{ + kref_put(&req->r_kref, ceph_osdc_release_request); } -extern void ceph_osdc_put_request(struct ceph_osd_request *req); extern int ceph_osdc_start_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req, -- cgit v0.10.2 From c2e552e76e2c6907ca50cd9a4b747a2e2e8c615e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 7 Dec 2009 15:55:05 -0800 Subject: ceph: use kref for ceph_msg Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index bf76210..b0571b0 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1958,7 +1958,7 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, m = kmalloc(sizeof(*m), GFP_NOFS); if (m == NULL) goto out; - atomic_set(&m->nref, 1); + kref_init(&m->kref); INIT_LIST_HEAD(&m->list_head); m->hdr.type = cpu_to_le16(type); @@ -2070,34 +2070,23 @@ void ceph_msg_kfree(struct ceph_msg *m) /* * Drop a msg ref. Destroy as needed. */ -void ceph_msg_put(struct ceph_msg *m) -{ - dout("ceph_msg_put %p %d -> %d\n", m, atomic_read(&m->nref), - atomic_read(&m->nref)-1); - if (atomic_read(&m->nref) <= 0) { - pr_err("bad ceph_msg_put on %p %llu %d=%s %d+%d\n", - m, le64_to_cpu(m->hdr.seq), - le16_to_cpu(m->hdr.type), - ceph_msg_type_name(le16_to_cpu(m->hdr.type)), - le32_to_cpu(m->hdr.front_len), - le32_to_cpu(m->hdr.data_len)); - WARN_ON(1); - } - if (atomic_dec_and_test(&m->nref)) { - dout("ceph_msg_put last one on %p\n", m); - WARN_ON(!list_empty(&m->list_head)); - - /* drop middle, data, if any */ - if (m->middle) { - ceph_buffer_put(m->middle); - m->middle = NULL; - } - m->nr_pages = 0; - m->pages = NULL; +void ceph_msg_last_put(struct kref *kref) +{ + struct ceph_msg *m = container_of(kref, struct ceph_msg, kref); - if (m->pool) - ceph_msgpool_put(m->pool, m); - else - ceph_msg_kfree(m); + dout("ceph_msg_put last one on %p\n", m); + WARN_ON(!list_empty(&m->list_head)); + + /* drop middle, data, if any */ + if (m->middle) { + ceph_buffer_put(m->middle); + m->middle = NULL; } + m->nr_pages = 0; + m->pages = NULL; + + if (m->pool) + ceph_msgpool_put(m->pool, m); + else + ceph_msg_kfree(m); } diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index f9c9f64..981b7c0 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -1,6 +1,7 @@ #ifndef __FS_CEPH_MESSENGER_H #define __FS_CEPH_MESSENGER_H +#include #include #include #include @@ -85,7 +86,7 @@ struct ceph_msg { struct page **pages; /* data payload. NOT OWNER. */ unsigned nr_pages; /* size of page array */ struct list_head list_head; - atomic_t nref; + struct kref kref; bool front_is_vmalloc; bool more_to_follow; int front_max; @@ -243,11 +244,13 @@ extern int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg); static inline struct ceph_msg *ceph_msg_get(struct ceph_msg *msg) { - dout("ceph_msg_get %p %d -> %d\n", msg, atomic_read(&msg->nref), - atomic_read(&msg->nref)+1); - atomic_inc(&msg->nref); + kref_get(&msg->kref); return msg; } -extern void ceph_msg_put(struct ceph_msg *msg); +extern void ceph_msg_last_put(struct kref *kref); +static inline void ceph_msg_put(struct ceph_msg *msg) +{ + kref_put(&msg->kref, ceph_msg_last_put); +} #endif diff --git a/fs/ceph/msgpool.c b/fs/ceph/msgpool.c index 7599b33..ad5482c 100644 --- a/fs/ceph/msgpool.c +++ b/fs/ceph/msgpool.c @@ -165,7 +165,7 @@ void ceph_msgpool_put(struct ceph_msgpool *pool, struct ceph_msg *msg) { spin_lock(&pool->lock); if (pool->num < pool->min) { - ceph_msg_get(msg); /* retake a single ref */ + kref_set(&msg->kref, 1); /* retake a single ref */ list_add(&msg->list_head, &pool->msgs); pool->num++; dout("msgpool_put %p reclaim %p, now %d/%d\n", pool, msg, -- cgit v0.10.2 From 767ea5c33a360ce88da24e296e802dace5821799 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 9 Dec 2009 12:34:01 -0800 Subject: ceph: do not feed bad device ids to crush Do not feed bad (large) device ids to CRUSH. Signed-off-by: Sage Weil diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 8c994c7..be5318a 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -868,6 +868,11 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, ps = le16_to_cpu(pgid.ps); preferred = (s16)le16_to_cpu(pgid.preferred); + /* don't forcefeed bad device ids to crush */ + if (preferred >= osdmap->max_osd || + preferred >= osdmap->crush->max_devices) + preferred = -1; + if (poolid >= osdmap->num_pools) return NULL; pool = &osdmap->pg_pool[poolid]; -- cgit v0.10.2 From d4a780ce8821a37dd135f15b6150a5bfc5604f29 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 11 Dec 2009 08:55:23 -0800 Subject: ceph: fix leak of monc mutex Fix leak of monc mutex on ENOMEM or bad fsid when receiving new mon map. Audited all other users. Signed-off-by: Sage Weil diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 1dd0dc2..a76da5e 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -320,17 +320,18 @@ static void ceph_monc_handle_map(struct ceph_mon_client *monc, if (IS_ERR(monmap)) { pr_err("problem decoding monmap, %d\n", (int)PTR_ERR(monmap)); - return; + goto out; } if (ceph_check_fsid(monc->client, &monmap->fsid) < 0) { kfree(monmap); - return; + goto out; } client->monc.monmap = monmap; kfree(old); +out: mutex_unlock(&monc->mutex); wake_up(&client->mount_wq); } -- cgit v0.10.2 From 550ee52d823845f882ddadcd032b33169866a739 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 27 Nov 2009 13:47:09 +0100 Subject: [ARM] pxa/raumfeld: add platform support This adds support for Raumfeld's 'Controller', 'Connector', 'Speaker S' and 'Speaker M' devices. They're all based on PXA303 SoCs. Signed-off-by: Daniel Mack Signed-off-by: Eric Miao diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index 8a0837e..fdb966e 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig @@ -415,6 +415,24 @@ config MACH_TREO680 Say Y here if you intend to run this kernel on Palm Treo 680 smartphone. +config MACH_RAUMFELD_RC + bool "Raumfeld Controller" + select PXA3xx + select CPU_PXA300 + select HAVE_PWM + +config MACH_RAUMFELD_CONNECTOR + bool "Raumfeld Connector" + select PXA3xx + select CPU_PXA300 + select PXA_SSP + +config MACH_RAUMFELD_SPEAKER + bool "Raumfeld Speaker" + select PXA3xx + select CPU_PXA300 + select PXA_SSP + config PXA_SHARPSL bool "SHARP Zaurus SL-5600, SL-C7xx and SL-Cxx00 Models" select SHARP_SCOOP diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile index f64afda..9d83193 100644 --- a/arch/arm/mach-pxa/Makefile +++ b/arch/arm/mach-pxa/Makefile @@ -89,6 +89,9 @@ obj-$(CONFIG_MACH_E740) += e740.o obj-$(CONFIG_MACH_E750) += e750.o obj-$(CONFIG_MACH_E400) += e400.o obj-$(CONFIG_MACH_E800) += e800.o +obj-$(CONFIG_MACH_RAUMFELD_RC) += raumfeld.o +obj-$(CONFIG_MACH_RAUMFELD_CONNECTOR) += raumfeld.o +obj-$(CONFIG_MACH_RAUMFELD_SPEAKER) += raumfeld.o # Support for blinky lights led-y := leds.o diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c new file mode 100644 index 0000000..06717d7 --- /dev/null +++ b/arch/arm/mach-pxa/raumfeld.c @@ -0,0 +1,1100 @@ +/* + * arch/arm/mach-pxa/raumfeld.c + * + * Support for the following Raumfeld devices: + * + * * Controller + * * Connector + * * Speaker S/M + * + * See http://www.raumfeld.com for details. + * + * Copyright (c) 2009 Daniel Mack + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "generic.h" +#include "devices.h" +#include "clock.h" + +/* common GPIO definitions */ + +/* inputs */ +#define GPIO_ON_OFF (14) +#define GPIO_VOLENC_A (19) +#define GPIO_VOLENC_B (20) +#define GPIO_CHARGE_DONE (23) +#define GPIO_CHARGE_IND (27) +#define GPIO_TOUCH_IRQ (32) +#define GPIO_ETH_IRQ (40) +#define GPIO_SPI_MISO (98) +#define GPIO_ACCEL_IRQ (104) +#define GPIO_RESCUE_BOOT (115) +#define GPIO_DOCK_DETECT (116) +#define GPIO_KEY1 (117) +#define GPIO_KEY2 (118) +#define GPIO_KEY3 (119) +#define GPIO_CHARGE_USB_OK (112) +#define GPIO_CHARGE_DC_OK (101) +#define GPIO_CHARGE_USB_SUSP (102) + +/* outputs */ +#define GPIO_SHUTDOWN_SUPPLY (16) +#define GPIO_SHUTDOWN_BATT (18) +#define GPIO_CHRG_PEN2 (31) +#define GPIO_TFT_VA_EN (33) +#define GPIO_SPDIF_CS (34) +#define GPIO_LED2 (35) +#define GPIO_LED1 (36) +#define GPIO_SPDIF_RESET (38) +#define GPIO_SPI_CLK (95) +#define GPIO_MCLK_DAC_CS (96) +#define GPIO_SPI_MOSI (97) +#define GPIO_W1_PULLUP_ENABLE (105) +#define GPIO_DISPLAY_ENABLE (106) +#define GPIO_MCLK_RESET (111) +#define GPIO_W2W_RESET (113) +#define GPIO_W2W_PDN (114) +#define GPIO_CODEC_RESET (120) +#define GPIO_AUDIO_VA_ENABLE (124) +#define GPIO_ACCEL_CS (125) +#define GPIO_ONE_WIRE (126) + +/* + * GPIO configurations + */ +static mfp_cfg_t raumfeld_controller_pin_config[] __initdata = { + /* UART1 */ + GPIO77_UART1_RXD, + GPIO78_UART1_TXD, + GPIO79_UART1_CTS, + GPIO81_UART1_DSR, + GPIO83_UART1_DTR, + GPIO84_UART1_RTS, + + /* UART3 */ + GPIO110_UART3_RXD, + + /* USB Host */ + GPIO0_2_USBH_PEN, + GPIO1_2_USBH_PWR, + + /* I2C */ + GPIO21_I2C_SCL | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + GPIO22_I2C_SDA | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + + /* SPI */ + GPIO34_GPIO, /* SPDIF_CS */ + GPIO96_GPIO, /* MCLK_CS */ + GPIO125_GPIO, /* ACCEL_CS */ + + /* MMC */ + GPIO3_MMC1_DAT0, + GPIO4_MMC1_DAT1, + GPIO5_MMC1_DAT2, + GPIO6_MMC1_DAT3, + GPIO7_MMC1_CLK, + GPIO8_MMC1_CMD, + + /* One-wire */ + GPIO126_GPIO | MFP_LPM_FLOAT, + GPIO105_GPIO | MFP_PULL_LOW | MFP_LPM_PULL_LOW, + + /* CHRG_USB_OK */ + GPIO101_GPIO | MFP_PULL_HIGH, + /* CHRG_USB_OK */ + GPIO112_GPIO | MFP_PULL_HIGH, + /* CHRG_USB_SUSP */ + GPIO102_GPIO, + /* DISPLAY_ENABLE */ + GPIO106_GPIO, + /* DOCK_DETECT */ + GPIO116_GPIO | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + + /* LCD */ + GPIO54_LCD_LDD_0, + GPIO55_LCD_LDD_1, + GPIO56_LCD_LDD_2, + GPIO57_LCD_LDD_3, + GPIO58_LCD_LDD_4, + GPIO59_LCD_LDD_5, + GPIO60_LCD_LDD_6, + GPIO61_LCD_LDD_7, + GPIO62_LCD_LDD_8, + GPIO63_LCD_LDD_9, + GPIO64_LCD_LDD_10, + GPIO65_LCD_LDD_11, + GPIO66_LCD_LDD_12, + GPIO67_LCD_LDD_13, + GPIO68_LCD_LDD_14, + GPIO69_LCD_LDD_15, + GPIO70_LCD_LDD_16, + GPIO71_LCD_LDD_17, + GPIO72_LCD_FCLK, + GPIO73_LCD_LCLK, + GPIO74_LCD_PCLK, + GPIO75_LCD_BIAS, +}; + +static mfp_cfg_t raumfeld_connector_pin_config[] __initdata = { + /* UART1 */ + GPIO77_UART1_RXD, + GPIO78_UART1_TXD, + GPIO79_UART1_CTS, + GPIO81_UART1_DSR, + GPIO83_UART1_DTR, + GPIO84_UART1_RTS, + + /* UART3 */ + GPIO110_UART3_RXD, + + /* USB Host */ + GPIO0_2_USBH_PEN, + GPIO1_2_USBH_PWR, + + /* I2C */ + GPIO21_I2C_SCL | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + GPIO22_I2C_SDA | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + + /* SPI */ + GPIO34_GPIO, /* SPDIF_CS */ + GPIO96_GPIO, /* MCLK_CS */ + GPIO125_GPIO, /* ACCEL_CS */ + + /* MMC */ + GPIO3_MMC1_DAT0, + GPIO4_MMC1_DAT1, + GPIO5_MMC1_DAT2, + GPIO6_MMC1_DAT3, + GPIO7_MMC1_CLK, + GPIO8_MMC1_CMD, + + /* Ethernet */ + GPIO1_nCS2, /* CS */ + GPIO40_GPIO | MFP_PULL_HIGH, /* IRQ */ + + /* SSP for I2S */ + GPIO85_SSP1_SCLK, + GPIO89_SSP1_EXTCLK, + GPIO86_SSP1_FRM, + GPIO87_SSP1_TXD, + GPIO88_SSP1_RXD, + GPIO90_SSP1_SYSCLK, + + /* SSP2 for S/PDIF */ + GPIO25_SSP2_SCLK, + GPIO26_SSP2_FRM, + GPIO27_SSP2_TXD, + GPIO29_SSP2_EXTCLK, +}; + +static mfp_cfg_t raumfeld_speaker_pin_config[] __initdata = { + /* UART1 */ + GPIO77_UART1_RXD, + GPIO78_UART1_TXD, + GPIO79_UART1_CTS, + GPIO81_UART1_DSR, + GPIO83_UART1_DTR, + GPIO84_UART1_RTS, + + /* UART3 */ + GPIO110_UART3_RXD, + + /* USB Host */ + GPIO0_2_USBH_PEN, + GPIO1_2_USBH_PWR, + + /* I2C */ + GPIO21_I2C_SCL | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + GPIO22_I2C_SDA | MFP_LPM_FLOAT | MFP_PULL_FLOAT, + + /* SPI */ + GPIO34_GPIO, /* SPDIF_CS */ + GPIO96_GPIO, /* MCLK_CS */ + GPIO125_GPIO, /* ACCEL_CS */ + + /* MMC */ + GPIO3_MMC1_DAT0, + GPIO4_MMC1_DAT1, + GPIO5_MMC1_DAT2, + GPIO6_MMC1_DAT3, + GPIO7_MMC1_CLK, + GPIO8_MMC1_CMD, + + /* Ethernet */ + GPIO1_nCS2, /* CS */ + GPIO40_GPIO | MFP_PULL_HIGH, /* IRQ */ + + /* SSP for I2S */ + GPIO85_SSP1_SCLK, + GPIO89_SSP1_EXTCLK, + GPIO86_SSP1_FRM, + GPIO87_SSP1_TXD, + GPIO88_SSP1_RXD, + GPIO90_SSP1_SYSCLK, +}; + +/* + * SMSC LAN9220 Ethernet + */ + +static struct resource smc91x_resources[] = { + { + .start = PXA3xx_CS2_PHYS, + .end = PXA3xx_CS2_PHYS + 0xfffff, + .flags = IORESOURCE_MEM, + }, + { + .start = gpio_to_irq(GPIO_ETH_IRQ), + .end = gpio_to_irq(GPIO_ETH_IRQ), + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_FALLING, + } +}; + +static struct smsc911x_platform_config raumfeld_smsc911x_config = { + .phy_interface = PHY_INTERFACE_MODE_MII, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, + .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS, +}; + +static struct platform_device smc91x_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, + .dev = { + .platform_data = &raumfeld_smsc911x_config, + } +}; + +/** + * NAND + */ + +static struct mtd_partition raumfeld_nand_partitions[] = { + { + .name = "Bootloader", + .offset = 0, + .size = 0xa0000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "BootloaderEnvironment", + .offset = 0xa0000, + .size = 0x20000, + }, + { + .name = "BootloaderSplashScreen", + .offset = 0xc0000, + .size = 0x60000, + }, + { + .name = "UBI", + .offset = 0x120000, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct pxa3xx_nand_platform_data raumfeld_nand_info = { + .enable_arbiter = 1, + .keep_config = 1, + .parts = raumfeld_nand_partitions, + .nr_parts = ARRAY_SIZE(raumfeld_nand_partitions), +}; + +/** + * USB (OHCI) support + */ + +static struct pxaohci_platform_data raumfeld_ohci_info = { + .port_mode = PMM_GLOBAL_MODE, + .flags = ENABLE_PORT1, +}; + +/** + * Rotary encoder input device + */ + +static struct rotary_encoder_platform_data raumfeld_rotary_encoder_info = { + .steps = 24, + .axis = REL_X, + .relative_axis = 1, + .gpio_a = GPIO_VOLENC_A, + .gpio_b = GPIO_VOLENC_B, + .inverted_a = 1, + .inverted_b = 0, +}; + +static struct platform_device rotary_encoder_device = { + .name = "rotary-encoder", + .id = 0, + .dev = { + .platform_data = &raumfeld_rotary_encoder_info, + } +}; + +/** + * GPIO buttons + */ + +static struct gpio_keys_button gpio_keys_button[] = { + { + .code = KEY_F1, + .type = EV_KEY, + .gpio = GPIO_KEY1, + .active_low = 1, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "Button 1", + }, + { + .code = KEY_F2, + .type = EV_KEY, + .gpio = GPIO_KEY2, + .active_low = 1, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "Button 2", + }, + { + .code = KEY_F3, + .type = EV_KEY, + .gpio = GPIO_KEY3, + .active_low = 1, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "Button 3", + }, + { + .code = KEY_F4, + .type = EV_KEY, + .gpio = GPIO_RESCUE_BOOT, + .active_low = 0, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "rescue boot button", + }, + { + .code = KEY_F5, + .type = EV_KEY, + .gpio = GPIO_DOCK_DETECT, + .active_low = 1, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "dock detect", + }, + { + .code = KEY_F6, + .type = EV_KEY, + .gpio = GPIO_ON_OFF, + .active_low = 0, + .wakeup = 0, + .debounce_interval = 5, /* ms */ + .desc = "on/off button", + }, +}; + +static struct gpio_keys_platform_data gpio_keys_platform_data = { + .buttons = gpio_keys_button, + .nbuttons = ARRAY_SIZE(gpio_keys_button), + .rep = 0, +}; + +static struct platform_device raumfeld_gpio_keys_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &gpio_keys_platform_data, + } +}; + +/** + * GPIO LEDs + */ + +static struct gpio_led raumfeld_leds[] = { + { + .name = "raumfeld:1", + .gpio = GPIO_LED1, + .active_low = 1, + .default_state = LEDS_GPIO_DEFSTATE_ON, + }, + { + .name = "raumfeld:2", + .gpio = GPIO_LED2, + .active_low = 0, + .default_state = LEDS_GPIO_DEFSTATE_OFF, + } +}; + +static struct gpio_led_platform_data raumfeld_led_platform_data = { + .leds = raumfeld_leds, + .num_leds = ARRAY_SIZE(raumfeld_leds), +}; + +static struct platform_device raumfeld_led_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &raumfeld_led_platform_data, + }, +}; + +/** + * One-wire (W1 bus) support + */ + +static void w1_enable_external_pullup(int enable) +{ + gpio_set_value(GPIO_W1_PULLUP_ENABLE, enable); + msleep(100); +} + +static struct w1_gpio_platform_data w1_gpio_platform_data = { + .pin = GPIO_ONE_WIRE, + .is_open_drain = 0, + .enable_external_pullup = w1_enable_external_pullup, +}; + +struct platform_device raumfeld_w1_gpio_device = { + .name = "w1-gpio", + .dev = { + .platform_data = &w1_gpio_platform_data + } +}; + +static void __init raumfeld_w1_init(void) +{ + int ret = gpio_request(GPIO_W1_PULLUP_ENABLE, + "W1 external pullup enable"); + + if (ret < 0) + pr_warning("Unable to request GPIO_W1_PULLUP_ENABLE\n"); + else + gpio_direction_output(GPIO_W1_PULLUP_ENABLE, 0); + + platform_device_register(&raumfeld_w1_gpio_device); +} + +/** + * Framebuffer device + */ + +/* PWM controlled backlight */ +static struct platform_pwm_backlight_data raumfeld_pwm_backlight_data = { + .pwm_id = 0, + .max_brightness = 100, + .dft_brightness = 100, + /* 10000 ns = 10 ms ^= 100 kHz */ + .pwm_period_ns = 10000, +}; + +static struct platform_device raumfeld_pwm_backlight_device = { + .name = "pwm-backlight", + .dev = { + .parent = &pxa27x_device_pwm0.dev, + .platform_data = &raumfeld_pwm_backlight_data, + } +}; + +/* LT3593 controlled backlight */ +static struct gpio_led raumfeld_lt3593_led = { + .name = "backlight", + .gpio = mfp_to_gpio(MFP_PIN_GPIO17), + .default_state = LEDS_GPIO_DEFSTATE_ON, +}; + +static struct gpio_led_platform_data raumfeld_lt3593_platform_data = { + .leds = &raumfeld_lt3593_led, + .num_leds = 1, +}; + +static struct platform_device raumfeld_lt3593_device = { + .name = "leds-lt3593", + .id = -1, + .dev = { + .platform_data = &raumfeld_lt3593_platform_data, + }, +}; + +static struct pxafb_mode_info sharp_lq043t3dx02_mode = { + .pixclock = 111000, + .xres = 480, + .yres = 272, + .bpp = 16, + .hsync_len = 4, + .left_margin = 2, + .right_margin = 1, + .vsync_len = 1, + .upper_margin = 3, + .lower_margin = 1, + .sync = 0, +}; + +static struct pxafb_mach_info raumfeld_sharp_lcd_info = { + .modes = &sharp_lq043t3dx02_mode, + .num_modes = 1, + .video_mem_size = 0x400000, + .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL, +}; + +static void __init raumfeld_lcd_init(void) +{ + int ret; + + set_pxa_fb_info(&raumfeld_sharp_lcd_info); + + /* Earlier devices had the backlight regulator controlled + * via PWM, later versions use another controller for that */ + if ((system_rev & 0xff) < 2) { + mfp_cfg_t raumfeld_pwm_pin_config = GPIO17_PWM0_OUT; + pxa3xx_mfp_config(&raumfeld_pwm_pin_config, 1); + platform_device_register(&raumfeld_pwm_backlight_device); + } else + platform_device_register(&raumfeld_lt3593_device); + + ret = gpio_request(GPIO_TFT_VA_EN, "display VA enable"); + if (ret < 0) + pr_warning("Unable to request GPIO_TFT_VA_EN\n"); + else + gpio_direction_output(GPIO_TFT_VA_EN, 1); + + ret = gpio_request(GPIO_DISPLAY_ENABLE, "display enable"); + if (ret < 0) + pr_warning("Unable to request GPIO_DISPLAY_ENABLE\n"); + else + gpio_direction_output(GPIO_DISPLAY_ENABLE, 1); +} + +/** + * SPI devices + */ + +struct spi_gpio_platform_data raumfeld_spi_platform_data = { + .sck = GPIO_SPI_CLK, + .mosi = GPIO_SPI_MOSI, + .miso = GPIO_SPI_MISO, + .num_chipselect = 3, +}; + +static struct platform_device raumfeld_spi_device = { + .name = "spi_gpio", + .id = 0, + .dev = { + .platform_data = &raumfeld_spi_platform_data, + } +}; + +static struct lis3lv02d_platform_data lis3_pdata = { + .click_flags = LIS3_CLICK_SINGLE_X | + LIS3_CLICK_SINGLE_Y | + LIS3_CLICK_SINGLE_Z, + .irq_cfg = LIS3_IRQ1_CLICK | LIS3_IRQ2_CLICK, + .wakeup_flags = LIS3_WAKEUP_X_LO | LIS3_WAKEUP_X_HI | + LIS3_WAKEUP_Y_LO | LIS3_WAKEUP_Y_HI | + LIS3_WAKEUP_Z_LO | LIS3_WAKEUP_Z_HI, + .wakeup_thresh = 10, + .click_thresh_x = 10, + .click_thresh_y = 10, + .click_thresh_z = 10, +}; + +#define SPI_AK4104 \ +{ \ + .modalias = "ak4104", \ + .max_speed_hz = 10000, \ + .bus_num = 0, \ + .chip_select = 0, \ + .controller_data = (void *) GPIO_SPDIF_CS, \ +} + +#define SPI_LIS3 \ +{ \ + .modalias = "lis3lv02d_spi", \ + .max_speed_hz = 1000000, \ + .bus_num = 0, \ + .chip_select = 1, \ + .controller_data = (void *) GPIO_ACCEL_CS, \ + .platform_data = &lis3_pdata, \ + .irq = gpio_to_irq(GPIO_ACCEL_IRQ), \ +} + +#define SPI_DAC7512 \ +{ \ + .modalias = "dac7512", \ + .max_speed_hz = 1000000, \ + .bus_num = 0, \ + .chip_select = 2, \ + .controller_data = (void *) GPIO_MCLK_DAC_CS, \ +} + +static struct spi_board_info connector_spi_devices[] __initdata = { + SPI_AK4104, + SPI_DAC7512, +}; + +static struct spi_board_info speaker_spi_devices[] __initdata = { + SPI_DAC7512, +}; + +static struct spi_board_info controller_spi_devices[] __initdata = { + SPI_LIS3, +}; + +/** + * MMC for Marvell Libertas 8688 via SDIO + */ + +static int raumfeld_mci_init(struct device *dev, irq_handler_t isr, void *data) +{ + gpio_set_value(GPIO_W2W_RESET, 1); + gpio_set_value(GPIO_W2W_PDN, 1); + + return 0; +} + +static void raumfeld_mci_exit(struct device *dev, void *data) +{ + gpio_set_value(GPIO_W2W_RESET, 0); + gpio_set_value(GPIO_W2W_PDN, 0); +} + +static struct pxamci_platform_data raumfeld_mci_platform_data = { + .init = raumfeld_mci_init, + .exit = raumfeld_mci_exit, + .detect_delay = 20, + .gpio_card_detect = -1, + .gpio_card_ro = -1, + .gpio_power = -1, +}; + +/* + * External power / charge logic + */ + +static int power_supply_init(struct device *dev) +{ + return 0; +} + +static void power_supply_exit(struct device *dev) +{ +} + +static int raumfeld_is_ac_online(void) +{ + return !gpio_get_value(GPIO_CHARGE_DC_OK); +} + +static int raumfeld_is_usb_online(void) +{ + return 0; +} + +static char *raumfeld_power_supplicants[] = { "ds2760-battery.0" }; + +static struct pda_power_pdata power_supply_info = { + .init = power_supply_init, + .is_ac_online = raumfeld_is_ac_online, + .is_usb_online = raumfeld_is_usb_online, + .exit = power_supply_exit, + .supplied_to = raumfeld_power_supplicants, + .num_supplicants = ARRAY_SIZE(raumfeld_power_supplicants) +}; + +static struct resource power_supply_resources[] = { + { + .name = "ac", + .flags = IORESOURCE_IRQ | + IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, + .start = GPIO_CHARGE_DC_OK, + .end = GPIO_CHARGE_DC_OK, + }, +}; + +static irqreturn_t charge_done_irq(int irq, void *dev_id) +{ + struct power_supply *psy; + + psy = power_supply_get_by_name("ds2760-battery.0"); + + if (psy) + power_supply_set_battery_charged(psy); + + return IRQ_HANDLED; +} + +static struct platform_device raumfeld_power_supply = { + .name = "pda-power", + .id = -1, + .dev = { + .platform_data = &power_supply_info, + }, + .resource = power_supply_resources, + .num_resources = ARRAY_SIZE(power_supply_resources), +}; + +static void __init raumfeld_power_init(void) +{ + int ret; + + /* Set PEN2 high to enable maximum charge current */ + ret = gpio_request(GPIO_CHRG_PEN2, "CHRG_PEN2"); + if (ret < 0) + pr_warning("Unable to request GPIO_CHRG_PEN2\n"); + else + gpio_direction_output(GPIO_CHRG_PEN2, 1); + + ret = gpio_request(GPIO_CHARGE_DC_OK, "CABLE_DC_OK"); + if (ret < 0) + pr_warning("Unable to request GPIO_CHARGE_DC_OK\n"); + + ret = gpio_request(GPIO_CHARGE_USB_SUSP, "CHARGE_USB_SUSP"); + if (ret < 0) + pr_warning("Unable to request GPIO_CHARGE_USB_SUSP\n"); + else + gpio_direction_output(GPIO_CHARGE_USB_SUSP, 0); + + power_supply_resources[0].start = gpio_to_irq(GPIO_CHARGE_DC_OK); + power_supply_resources[0].end = gpio_to_irq(GPIO_CHARGE_DC_OK); + + ret = request_irq(gpio_to_irq(GPIO_CHARGE_DONE), + &charge_done_irq, IORESOURCE_IRQ_LOWEDGE, + "charge_done", NULL); + + if (ret < 0) + printk(KERN_ERR "%s: unable to register irq %d\n", __func__, + GPIO_CHARGE_DONE); + else + platform_device_register(&raumfeld_power_supply); +} + +/* Fixed regulator for AUDIO_VA, 0-0048 maps to the cs4270 codec device */ + +static struct regulator_consumer_supply audio_va_consumer_supply = + REGULATOR_SUPPLY("va", "0-0048"); + +struct regulator_init_data audio_va_initdata = { + .consumer_supplies = &audio_va_consumer_supply, + .num_consumer_supplies = 1, + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +static struct fixed_voltage_config audio_va_config = { + .supply_name = "audio_va", + .microvolts = 5000000, + .gpio = GPIO_AUDIO_VA_ENABLE, + .enable_high = 1, + .enabled_at_boot = 0, + .init_data = &audio_va_initdata, +}; + +static struct platform_device audio_va_device = { + .name = "reg-fixed-voltage", + .id = 0, + .dev = { + .platform_data = &audio_va_config, + }, +}; + +/* Dummy supplies for Codec's VD/VLC */ + +static struct regulator_consumer_supply audio_dummy_supplies[] = { + REGULATOR_SUPPLY("vd", "0-0048"), + REGULATOR_SUPPLY("vlc", "0-0048"), +}; + +struct regulator_init_data audio_dummy_initdata = { + .consumer_supplies = audio_dummy_supplies, + .num_consumer_supplies = ARRAY_SIZE(audio_dummy_supplies), + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +static struct fixed_voltage_config audio_dummy_config = { + .supply_name = "audio_vd", + .microvolts = 3300000, + .gpio = -1, + .init_data = &audio_dummy_initdata, +}; + +static struct platform_device audio_supply_dummy_device = { + .name = "reg-fixed-voltage", + .id = 1, + .dev = { + .platform_data = &audio_dummy_config, + }, +}; + +static struct platform_device *audio_regulator_devices[] = { + &audio_va_device, + &audio_supply_dummy_device, +}; + +/** + * Regulator support via MAX8660 + */ + +static struct regulator_consumer_supply vcc_mmc_supply = + REGULATOR_SUPPLY("vmmc", "pxa2xx-mci.0"); + +static struct regulator_init_data vcc_mmc_init_data = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE, + }, + .consumer_supplies = &vcc_mmc_supply, + .num_consumer_supplies = 1, +}; + +struct max8660_subdev_data max8660_v6_subdev_data = { + .id = MAX8660_V6, + .name = "vmmc", + .platform_data = &vcc_mmc_init_data, +}; + +static struct max8660_platform_data max8660_pdata = { + .subdevs = &max8660_v6_subdev_data, + .num_subdevs = 1, +}; + +/** + * I2C devices + */ + +static struct i2c_board_info raumfeld_pwri2c_board_info = { + .type = "max8660", + .addr = 0x34, + .platform_data = &max8660_pdata, +}; + +static struct i2c_board_info raumfeld_connector_i2c_board_info __initdata = { + .type = "cs4270", + .addr = 0x48, +}; + +static struct eeti_ts_platform_data eeti_ts_pdata = { + .irq_active_high = 1, +}; + +static struct i2c_board_info raumfeld_controller_i2c_board_info __initdata = { + .type = "eeti_ts", + .addr = 0x0a, + .irq = gpio_to_irq(GPIO_TOUCH_IRQ), + .platform_data = &eeti_ts_pdata, +}; + +static struct platform_device *raumfeld_common_devices[] = { + &raumfeld_gpio_keys_device, + &raumfeld_led_device, + &raumfeld_spi_device, +}; + +static void __init raumfeld_audio_init(void) +{ + int ret; + + ret = gpio_request(GPIO_CODEC_RESET, "cs4270 reset"); + if (ret < 0) + pr_warning("unable to request GPIO_CODEC_RESET\n"); + else + gpio_direction_output(GPIO_CODEC_RESET, 1); + + ret = gpio_request(GPIO_SPDIF_RESET, "ak4104 s/pdif reset"); + if (ret < 0) + pr_warning("unable to request GPIO_SPDIF_RESET\n"); + else + gpio_direction_output(GPIO_SPDIF_RESET, 1); + + ret = gpio_request(GPIO_MCLK_RESET, "MCLK reset"); + if (ret < 0) + pr_warning("unable to request GPIO_MCLK_RESET\n"); + else + gpio_direction_output(GPIO_MCLK_RESET, 1); + + platform_add_devices(ARRAY_AND_SIZE(audio_regulator_devices)); +} + +static void __init raumfeld_common_init(void) +{ + int ret; + + /* The on/off button polarity has changed after revision 1 */ + if ((system_rev & 0xff) > 1) { + int i; + + for (i = 0; i < ARRAY_SIZE(gpio_keys_button); i++) + if (!strcmp(gpio_keys_button[i].desc, "on/off button")) + gpio_keys_button[i].active_low = 1; + } + + enable_irq_wake(IRQ_WAKEUP0); + + pxa3xx_set_nand_info(&raumfeld_nand_info); + pxa3xx_set_i2c_power_info(NULL); + pxa_set_ohci_info(&raumfeld_ohci_info); + pxa_set_mci_info(&raumfeld_mci_platform_data); + pxa_set_i2c_info(NULL); + pxa_set_ffuart_info(NULL); + + ret = gpio_request(GPIO_W2W_RESET, "Wi2Wi reset"); + if (ret < 0) + pr_warning("Unable to request GPIO_W2W_RESET\n"); + else + gpio_direction_output(GPIO_W2W_RESET, 0); + + ret = gpio_request(GPIO_W2W_PDN, "Wi2Wi powerup"); + if (ret < 0) + pr_warning("Unable to request GPIO_W2W_PDN\n"); + else + gpio_direction_output(GPIO_W2W_PDN, 0); + + /* this can be used to switch off the device */ + ret = gpio_request(GPIO_SHUTDOWN_SUPPLY, + "supply shutdown"); + if (ret < 0) + pr_warning("Unable to request GPIO_SHUTDOWN_SUPPLY\n"); + else + gpio_direction_output(GPIO_SHUTDOWN_SUPPLY, 0); + + platform_add_devices(ARRAY_AND_SIZE(raumfeld_common_devices)); + i2c_register_board_info(1, &raumfeld_pwri2c_board_info, 1); +} + +static void __init raumfeld_controller_init(void) +{ + int ret; + + pxa3xx_mfp_config(ARRAY_AND_SIZE(raumfeld_controller_pin_config)); + platform_device_register(&rotary_encoder_device); + spi_register_board_info(ARRAY_AND_SIZE(controller_spi_devices)); + i2c_register_board_info(0, &raumfeld_controller_i2c_board_info, 1); + + ret = gpio_request(GPIO_SHUTDOWN_BATT, "battery shutdown"); + if (ret < 0) + pr_warning("Unable to request GPIO_SHUTDOWN_BATT\n"); + else + gpio_direction_output(GPIO_SHUTDOWN_BATT, 0); + + raumfeld_common_init(); + raumfeld_power_init(); + raumfeld_lcd_init(); + raumfeld_w1_init(); +} + +static void __init raumfeld_connector_init(void) +{ + pxa3xx_mfp_config(ARRAY_AND_SIZE(raumfeld_connector_pin_config)); + spi_register_board_info(ARRAY_AND_SIZE(connector_spi_devices)); + i2c_register_board_info(0, &raumfeld_connector_i2c_board_info, 1); + + platform_device_register(&smc91x_device); + + raumfeld_audio_init(); + raumfeld_common_init(); +} + +static void __init raumfeld_speaker_init(void) +{ + pxa3xx_mfp_config(ARRAY_AND_SIZE(raumfeld_speaker_pin_config)); + spi_register_board_info(ARRAY_AND_SIZE(speaker_spi_devices)); + i2c_register_board_info(0, &raumfeld_connector_i2c_board_info, 1); + + platform_device_register(&smc91x_device); + platform_device_register(&rotary_encoder_device); + + raumfeld_audio_init(); + raumfeld_common_init(); +} + +/* physical memory regions */ +#define RAUMFELD_SDRAM_BASE 0xa0000000 /* SDRAM region */ + +#ifdef CONFIG_MACH_RAUMFELD_RC +MACHINE_START(RAUMFELD_RC, "Raumfeld Controller") + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .boot_params = RAUMFELD_SDRAM_BASE + 0x100, + .init_machine = raumfeld_controller_init, + .map_io = pxa_map_io, + .init_irq = pxa3xx_init_irq, + .timer = &pxa_timer, +MACHINE_END +#endif + +#ifdef CONFIG_MACH_RAUMFELD_CONNECTOR +MACHINE_START(RAUMFELD_CONNECTOR, "Raumfeld Connector") + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .boot_params = RAUMFELD_SDRAM_BASE + 0x100, + .init_machine = raumfeld_connector_init, + .map_io = pxa_map_io, + .init_irq = pxa3xx_init_irq, + .timer = &pxa_timer, +MACHINE_END +#endif + +#ifdef CONFIG_MACH_RAUMFELD_SPEAKER +MACHINE_START(RAUMFELD_SPEAKER, "Raumfeld Speaker") + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .boot_params = RAUMFELD_SDRAM_BASE + 0x100, + .init_machine = raumfeld_speaker_init, + .map_io = pxa_map_io, + .init_irq = pxa3xx_init_irq, + .timer = &pxa_timer, +MACHINE_END +#endif -- cgit v0.10.2 From e863c884cd18dca9533c352655af73d921f4a311 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 27 Nov 2009 13:47:11 +0100 Subject: [ARM] pxa/raumfeld: add defconfig Signed-off-by: Daniel Mack Signed-off-by: Eric Miao diff --git a/arch/arm/configs/raumfeld_defconfig b/arch/arm/configs/raumfeld_defconfig new file mode 100644 index 0000000..acb1a8f --- /dev/null +++ b/arch/arm/configs/raumfeld_defconfig @@ -0,0 +1,1898 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.32-rc5 +# Sun Nov 1 21:57:32 2009 +# +CONFIG_ARM=y +CONFIG_HAVE_PWM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +# CONFIG_GROUP_SCHED is not set +# CONFIG_CGROUPS is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_NET_NS is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +# CONFIG_EMBEDDED is not set +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y + +# +# Kernel Performance Events And Counters +# +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +CONFIG_SLOW_WORK=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_PNX4008 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_BCMRING is not set + +# +# Intel PXA2xx/PXA3xx Implementations +# + +# +# Supported PXA3xx Processor Variants +# +CONFIG_CPU_PXA300=y +# CONFIG_CPU_PXA310 is not set +CONFIG_CPU_PXA320=y +# CONFIG_CPU_PXA930 is not set +# CONFIG_CPU_PXA935 is not set +# CONFIG_CPU_PXA950 is not set + +# +# Intel/Marvell Dev Platforms (sorted by hardware release time) +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_MACH_ZYLONITE is not set +# CONFIG_MACH_LITTLETON is not set +# CONFIG_MACH_TAVOREVB is not set +# CONFIG_MACH_SAAR is not set + +# +# Third Party Dev Platforms (sorted by vendor name) +# +# CONFIG_ARCH_PXA_IDP is not set +# CONFIG_ARCH_VIPER is not set +# CONFIG_MACH_BALLOON3 is not set +# CONFIG_MACH_CSB726 is not set +# CONFIG_MACH_ARMCORE is not set +# CONFIG_MACH_EM_X270 is not set +# CONFIG_MACH_EXEDA is not set +# CONFIG_MACH_CM_X300 is not set +# CONFIG_ARCH_GUMSTIX is not set +# CONFIG_MACH_INTELMOTE2 is not set +# CONFIG_MACH_STARGATE2 is not set +# CONFIG_MACH_XCEP is not set +# CONFIG_TRIZEPS_PXA is not set +# CONFIG_MACH_LOGICPD_PXA270 is not set +# CONFIG_MACH_PCM027 is not set +# CONFIG_MACH_COLIBRI is not set +# CONFIG_MACH_COLIBRI300 is not set +# CONFIG_MACH_COLIBRI320 is not set + +# +# End-user Products (sorted by vendor name) +# +# CONFIG_MACH_H4700 is not set +# CONFIG_MACH_H5000 is not set +# CONFIG_MACH_HIMALAYA is not set +# CONFIG_MACH_MAGICIAN is not set +# CONFIG_MACH_MIOA701 is not set +# CONFIG_PXA_EZX is not set +# CONFIG_MACH_MP900C is not set +# CONFIG_ARCH_PXA_PALM is not set +CONFIG_MACH_RAUMFELD_RC=y +CONFIG_MACH_RAUMFELD_CONNECTOR=y +CONFIG_MACH_RAUMFELD_PROTO=y +CONFIG_MACH_RAUMFELD_SPEAKER=y +# CONFIG_PXA_SHARPSL is not set +# CONFIG_ARCH_PXA_ESERIES is not set +CONFIG_PXA3xx=y +CONFIG_PXA_SSP=y +CONFIG_PLAT_PXA=y + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSC3=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +CONFIG_IO_36=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_OUTER_CACHE=y +CONFIG_CACHE_XSC3L2=y +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_IWMMXT=y +CONFIG_COMMON_CLKDEV=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_HAVE_MLOCK=y +CONFIG_HAVE_MLOCKED_PAGE_BIT=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="console=ttyS0,115200 rw" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_APM_EMULATION=y +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +CONFIG_CFG80211_REG_DEBUG=y +CONFIG_CFG80211_DEFAULT_PS=y +CONFIG_CFG80211_DEFAULT_PS_VALUE=1 +CONFIG_WIRELESS_OLD_REGULATORY=y +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +CONFIG_MAC80211=y +CONFIG_MAC80211_RC_MINSTREL=y +# CONFIG_MAC80211_RC_DEFAULT_PID is not set +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel" +# CONFIG_MAC80211_MESH is not set +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +CONFIG_NFTL=y +CONFIG_NFTL_RW=y +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +CONFIG_MTD_BLOCK2MTD=y + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_H1900 is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_SHARPSL is not set +CONFIG_MTD_NAND_PXA3xx=y +# CONFIG_MTD_NAND_PXA3xx_BUILTIN is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +CONFIG_MISC_DEVICES=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_ISL29003=y +CONFIG_TI_DAC7512=y +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +CONFIG_SMSC911X=y +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +# CONFIG_WLAN_PRE80211 is not set +CONFIG_WLAN_80211=y +CONFIG_LIBERTAS=y +# CONFIG_LIBERTAS_USB is not set +CONFIG_LIBERTAS_SDIO=m +# CONFIG_LIBERTAS_SPI is not set +# CONFIG_LIBERTAS_DEBUG is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_RTL8187 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_P54_COMMON is not set +# CONFIG_ATH_COMMON is not set +# CONFIG_HOSTAP is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_ZD1211RW is not set +# CONFIG_RT2X00 is not set +# CONFIG_WL12XX is not set +# CONFIG_IWM is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y +# CONFIG_USB_NET_AX8817X is not set +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +CONFIG_USB_NET_MCS7830=y +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +CONFIG_INPUT_POLLDEV=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_PXA27x is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +CONFIG_TOUCHSCREEN_EETI=m +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_UINPUT is not set +CONFIG_INPUT_GPIO_ROTARY_ENCODER=y + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +CONFIG_SPI_DEBUG=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_BITBANG=y +CONFIG_SPI_GPIO=y +# CONFIG_SPI_PXA2XX is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=y +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +CONFIG_DEBUG_GPIO=y +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set + +# +# AC97 GPIO expanders: +# +CONFIG_W1=m + +# +# 1-wire Bus Masters +# +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS1WM is not set +CONFIG_W1_MASTER_GPIO=m + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +CONFIG_W1_SLAVE_DS2760=m +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +CONFIG_PDA_POWER=y +# CONFIG_APM_POWER is not set +CONFIG_BATTERY_DS2760=m +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +CONFIG_SENSORS_LIS3_SPI=y +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13783 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_EZX_PCAP is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +CONFIG_REGULATOR_MAX8660=y +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA=y +# CONFIG_FB_PXA_OVERLAY is not set +# CONFIG_FB_PXA_SMARTPANEL is not set +# CONFIG_FB_PXA_PARAMETERS is not set +CONFIG_PXA3XX_GCU=y +# CONFIG_FB_MBX is not set +# CONFIG_FB_W100 is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_BACKLIGHT_PWM=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +# CONFIG_LOGO_LINUX_CLUT224 is not set +CONFIG_LOGO_RAUMFELD_CLUT224=y +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +# CONFIG_SND_DRIVERS is not set +CONFIG_SND_ARM=y +CONFIG_SND_PXA2XX_LIB=y +# CONFIG_SND_PXA2XX_AC97 is not set +CONFIG_SND_SPI=y +# CONFIG_SND_USB is not set +CONFIG_SND_SOC=y +CONFIG_SND_PXA2XX_SOC=y +CONFIG_SND_PXA_SOC_SSP=y +CONFIG_SND_SOC_RAUMFELD=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_AK4104=y +CONFIG_SND_SOC_CS4270=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +# CONFIG_DRAGONRISE_FF is not set +CONFIG_HID_EZKEY=y +CONFIG_HID_KYE=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_NTRIG=y +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +# CONFIG_GREENASIA_FF is not set +CONFIG_HID_SMARTJOYPLUS=y +# CONFIG_SMARTJOYPLUS_FF is not set +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +# CONFIG_THRUSTMASTER_FF is not set +CONFIG_HID_ZEROPLUS=y +# CONFIG_ZEROPLUS_FF is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=y +CONFIG_USB_DEBUG=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +# CONFIG_USB_GADGET is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_PXA=m +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_AT91 is not set +# CONFIG_MMC_ATMELMCI is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_PWM is not set +# CONFIG_LEDS_BD2802 is not set +CONFIG_LEDS_LT3593=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_TIMER is not set +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_SA1100 is not set +CONFIG_RTC_DRV_PXA=y +CONFIG_DMADEVICES=y + +# +# DMA Devices +# +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +# CONFIG_UIO_SMX is not set +# CONFIG_UIO_SERCOS3 is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT2_FS_XIP=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4_FS is not set +CONFIG_FS_XIP=y +CONFIG_JBD=y +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +CONFIG_FSCACHE=y +CONFIG_FSCACHE_STATS=y +# CONFIG_FSCACHE_HISTOGRAM is not set +# CONFIG_FSCACHE_DEBUG is not set +CONFIG_CACHEFILES=y +# CONFIG_CACHEFILES_DEBUG is not set +# CONFIG_CACHEFILES_HISTOGRAM is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +CONFIG_ROOT_NFS=y +CONFIG_NFS_FSCACHE=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=y +CONFIG_NLS_CODEPAGE_775=y +CONFIG_NLS_CODEPAGE_850=y +CONFIG_NLS_CODEPAGE_852=y +CONFIG_NLS_CODEPAGE_855=y +CONFIG_NLS_CODEPAGE_857=y +CONFIG_NLS_CODEPAGE_860=y +CONFIG_NLS_CODEPAGE_861=y +CONFIG_NLS_CODEPAGE_862=y +CONFIG_NLS_CODEPAGE_863=y +CONFIG_NLS_CODEPAGE_864=y +CONFIG_NLS_CODEPAGE_865=y +CONFIG_NLS_CODEPAGE_866=y +CONFIG_NLS_CODEPAGE_869=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_CODEPAGE_950=y +CONFIG_NLS_CODEPAGE_932=y +CONFIG_NLS_CODEPAGE_949=y +CONFIG_NLS_CODEPAGE_874=y +CONFIG_NLS_ISO8859_8=y +CONFIG_NLS_CODEPAGE_1250=y +CONFIG_NLS_CODEPAGE_1251=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=y +CONFIG_NLS_ISO8859_3=y +CONFIG_NLS_ISO8859_4=y +CONFIG_NLS_ISO8859_5=y +CONFIG_NLS_ISO8859_6=y +CONFIG_NLS_ISO8859_7=y +CONFIG_NLS_ISO8859_9=y +CONFIG_NLS_ISO8859_13=y +CONFIG_NLS_ISO8859_14=y +CONFIG_NLS_ISO8859_15=y +CONFIG_NLS_KOI8_R=y +CONFIG_NLS_KOI8_U=y +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y -- cgit v0.10.2 From e7cc894120bb809738cebcd87b5ed3057bbe51fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 17 Dec 2009 14:56:57 +0100 Subject: [ARM] pxa/ttc_dkb: remove duplicate macro definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARRAY_AND_SIZE is already defined arch/arm/mach-mmp/common.h which is already included. Signed-off-by: Uwe Kleine-König Cc: Russell King Cc: Bin Yang Acked-by: Haojian Zhuang Signed-off-by: Eric Miao diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c index 8f49b2b..b22dec4 100644 --- a/arch/arm/mach-mmp/ttc_dkb.c +++ b/arch/arm/mach-mmp/ttc_dkb.c @@ -24,8 +24,6 @@ #include "common.h" -#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) - static unsigned long ttc_dkb_pin_config[] __initdata = { /* UART2 */ GPIO47_UART2_RXD, -- cgit v0.10.2 From c86a2930ccbd90d77c54d04b5c2bbec95b989e40 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 14 Dec 2009 14:04:30 -0800 Subject: ceph: carry explicit msg reference for currently sending message Carry a ceph_msg reference for connection->out_msg. This will allow us to make out_sent optional. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index b0571b0..96fd556 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -322,7 +322,10 @@ static void reset_connection(struct ceph_connection *con) con->connect_seq = 0; con->out_seq = 0; - con->out_msg = NULL; + if (con->out_msg) { + ceph_msg_put(con->out_msg); + con->out_msg = NULL; + } con->in_seq = 0; mutex_unlock(&con->out_mutex); } @@ -423,7 +426,7 @@ static void prepare_write_message_footer(struct ceph_connection *con, int v) con->out_kvec_bytes += sizeof(m->footer); con->out_kvec_left++; con->out_more = m->more_to_follow; - con->out_msg = NULL; /* we're done with this one */ + con->out_msg_done = true; } /* @@ -436,6 +439,7 @@ static void prepare_write_message(struct ceph_connection *con) con->out_kvec_bytes = 0; con->out_kvec_is_msg = true; + con->out_msg_done = false; /* Sneak an ack in there first? If we can get it into the same * TCP packet that's a good thing. */ @@ -452,8 +456,9 @@ static void prepare_write_message(struct ceph_connection *con) /* move message to sending/sent list */ m = list_first_entry(&con->out_queue, struct ceph_msg, list_head); + con->out_msg = m; + ceph_msg_get(m); list_move_tail(&m->list_head, &con->out_sent); - con->out_msg = m; /* we don't bother taking a reference here. */ m->hdr.seq = cpu_to_le64(++con->out_seq); @@ -1521,6 +1526,12 @@ more_kvec: /* msg pages? */ if (con->out_msg) { + if (con->out_msg_done) { + ceph_msg_put(con->out_msg); + con->out_msg = NULL; /* we're done with this one */ + goto do_next; + } + ret = write_partial_msg_pages(con); if (ret == 1) goto more_kvec; /* we need to send the footer, too! */ @@ -1533,6 +1544,7 @@ more_kvec: } } +do_next: if (!test_bit(CONNECTING, &con->state)) { /* is anything else pending? */ if (!list_empty(&con->out_queue)) { @@ -1923,8 +1935,10 @@ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) list_del_init(&msg->list_head); ceph_msg_put(msg); msg->hdr.seq = 0; - if (con->out_msg == msg) + if (con->out_msg == msg) { + ceph_msg_put(con->out_msg); con->out_msg = NULL; + } if (con->out_kvec_is_msg) { con->out_skip = con->out_kvec_bytes; con->out_kvec_is_msg = false; diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index 981b7c0..eff5cb5 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -182,6 +182,7 @@ struct ceph_connection { /* message out temps */ struct ceph_msg *out_msg; /* sending message (== tail of out_sent) */ + bool out_msg_done; struct ceph_msg_pos out_msg_pos; struct kvec out_kvec[8], /* sending header/footer data */ -- cgit v0.10.2 From 5e095e8b40b0402ad3bcadc5b8d84c38b26c30b2 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 14 Dec 2009 14:30:34 -0800 Subject: ceph: plug msg leak in con_fault Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 96fd556..98519bd 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1434,8 +1434,9 @@ no_data: */ static void process_message(struct ceph_connection *con) { - struct ceph_msg *msg = con->in_msg; + struct ceph_msg *msg; + msg = con->in_msg; con->in_msg = NULL; /* if first message, set peer_name */ @@ -1810,7 +1811,11 @@ static void ceph_fault(struct ceph_connection *con) clear_bit(BUSY, &con->state); /* to avoid an improbable race */ con_close_socket(con); - con->in_msg = NULL; + + if (con->in_msg) { + ceph_msg_put(con->in_msg); + con->in_msg = NULL; + } /* If there are no messages in the queue, place the connection * in a STANDBY state (i.e., don't try to reconnect just yet). */ -- cgit v0.10.2 From 92ac41d0a4ab26fb68d3f841332e5d1f15d79123 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 14 Dec 2009 14:56:56 -0800 Subject: ceph: detect lossy state of connection The server indicates whether a connection is lossy; set our LOSSYTX bit appropriately. Do not set lossy bit on outgoing connections. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 98519bd..986d8fb 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -625,8 +625,6 @@ static void prepare_write_connect(struct ceph_messenger *msgr, con->out_connect.global_seq = cpu_to_le32(global_seq); con->out_connect.protocol_version = cpu_to_le32(proto); con->out_connect.flags = 0; - if (test_bit(LOSSYTX, &con->state)) - con->out_connect.flags = CEPH_MSG_CONNECT_LOSSY; if (!after_banner) { con->out_kvec_left = 0; @@ -1168,6 +1166,10 @@ static int process_connect(struct ceph_connection *con) con->connect_seq); WARN_ON(con->connect_seq != le32_to_cpu(con->in_reply.connect_seq)); + + if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY) + set_bit(LOSSYTX, &con->state); + prepare_read_tag(con); break; -- cgit v0.10.2 From b3d1dbbdd5670d8a9fb01f7dfb1cac522ff6795a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 14 Dec 2009 14:58:11 -0800 Subject: ceph: don't save sent messages on lossy connections For lossy connections we drop all state on socket errors, so there is no reason to keep sent ceph_msg's around. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 986d8fb..d5eef76 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -453,12 +453,16 @@ static void prepare_write_message(struct ceph_connection *con) con->out_kvec_bytes = 1 + sizeof(con->out_temp_ack); } - /* move message to sending/sent list */ m = list_first_entry(&con->out_queue, struct ceph_msg, list_head); con->out_msg = m; - ceph_msg_get(m); - list_move_tail(&m->list_head, &con->out_sent); + if (test_bit(LOSSYTX, &con->state)) { + /* put message on sent list */ + ceph_msg_get(m); + list_move_tail(&m->list_head, &con->out_sent); + } else { + list_del_init(&m->list_head); + } m->hdr.seq = cpu_to_le64(++con->out_seq); -- cgit v0.10.2 From 93c20d98c29ccefa039c3843ccc37122caaf3d31 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 15 Dec 2009 09:50:36 -0800 Subject: ceph: fix msgpool reservation leak Signed-off-by: Yehuda Sadeh diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 67ef8ab..63482ef 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -145,6 +145,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, ceph_osdc_put_request(req); return ERR_PTR(-ENOMEM); } + req->r_num_prealloc_reply = num_reply; req->r_osdc = osdc; req->r_mempool = use_mempool; @@ -165,7 +166,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, else msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, 0, 0, NULL); if (IS_ERR(msg)) { - ceph_msgpool_resv(&osdc->msgpool_op_reply, num_reply); + ceph_msgpool_resv(&osdc->msgpool_op_reply, -num_reply); ceph_osdc_put_request(req); return ERR_PTR(PTR_ERR(msg)); } @@ -465,6 +466,8 @@ static void __unregister_request(struct ceph_osd_client *osdc, rb_erase(&req->r_node, &osdc->requests); osdc->num_requests--; + ceph_msgpool_resv(&osdc->msgpool_op_reply, -req->r_num_prealloc_reply); + if (req->r_osd) { /* make sure the original request isn't in flight. */ ceph_con_revoke(&req->r_osd->o_con, req->r_request); diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index 20ee618..2e4cfd1 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -48,6 +48,7 @@ struct ceph_osd_request { int r_flags; /* any additional flags for the osd */ u32 r_sent; /* >0 if r_request is sending/sent */ int r_prepared_pages, r_got_reply; + int r_num_prealloc_reply; struct ceph_osd_client *r_osdc; struct kref r_kref; -- cgit v0.10.2 From 9ec7cab14e6de732d4e7c355fe67c5810c32c758 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 14 Dec 2009 15:13:47 -0800 Subject: ceph: hex dump corrupt server data to KERN_DEBUG Also, print fsid using standard format, NOT hex dump. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 9b9ce14..dfb509f 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2681,6 +2681,7 @@ done: bad: pr_err("ceph_handle_caps: corrupt message\n"); + ceph_msg_dump(msg); return; } diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 739093f..29a93fe 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1650,6 +1650,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) return; if (msg->front.iov_len < sizeof(*head)) { pr_err("mdsc_handle_reply got corrupt (short) reply\n"); + ceph_msg_dump(msg); return; } @@ -1740,6 +1741,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) mutex_lock(&session->s_mutex); if (err < 0) { pr_err("mdsc_handle_reply got corrupt reply mds%d\n", mds); + ceph_msg_dump(msg); goto out_err; } @@ -1929,6 +1931,7 @@ static void handle_session(struct ceph_mds_session *session, bad: pr_err("mdsc_handle_session corrupt message mds%d len %d\n", mds, (int)msg->front.iov_len); + ceph_msg_dump(msg); return; } @@ -2394,6 +2397,7 @@ out: bad: pr_err("corrupt lease message\n"); + ceph_msg_dump(msg); } void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session, diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index cad8d25..c4c498e 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -49,6 +49,7 @@ int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m) struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) { struct ceph_mdsmap *m; + const void *start = *p; int i, j, n; int err = -EINVAL; u16 version; @@ -154,6 +155,9 @@ badmem: err = -ENOMEM; bad: pr_err("corrupt mdsmap\n"); + print_hex_dump(KERN_DEBUG, "mdsmap: ", + DUMP_PREFIX_OFFSET, 16, 1, + start, end - start, true); ceph_mdsmap_destroy(m); return ERR_PTR(-EINVAL); } diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index d5eef76..b10f88c 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -2115,3 +2115,23 @@ void ceph_msg_last_put(struct kref *kref) else ceph_msg_kfree(m); } + +void ceph_msg_dump(struct ceph_msg *msg) +{ + pr_debug("msg_dump %p (front_max %d nr_pages %d)\n", msg, + msg->front_max, msg->nr_pages); + print_hex_dump(KERN_DEBUG, "header: ", + DUMP_PREFIX_OFFSET, 16, 1, + &msg->hdr, sizeof(msg->hdr), true); + print_hex_dump(KERN_DEBUG, " front: ", + DUMP_PREFIX_OFFSET, 16, 1, + msg->front.iov_base, msg->front.iov_len, true); + if (msg->middle) + print_hex_dump(KERN_DEBUG, "middle: ", + DUMP_PREFIX_OFFSET, 16, 1, + msg->middle->vec.iov_base, + msg->middle->vec.iov_len, true); + print_hex_dump(KERN_DEBUG, "footer: ", + DUMP_PREFIX_OFFSET, 16, 1, + &msg->footer, sizeof(msg->footer), true); +} diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index eff5cb5..e04c214 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -254,4 +254,6 @@ static inline void ceph_msg_put(struct ceph_msg *msg) kref_put(&msg->kref, ceph_msg_last_put); } +extern void ceph_msg_dump(struct ceph_msg *msg); + #endif diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index a76da5e..775a9c0 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -242,6 +242,7 @@ static void handle_subscribe_ack(struct ceph_mon_client *monc, return; bad: pr_err("got corrupt subscribe-ack msg\n"); + ceph_msg_dump(msg); } /* @@ -364,6 +365,7 @@ static void handle_statfs_reply(struct ceph_mon_client *monc, bad: pr_err("corrupt statfs reply, no tid\n"); + ceph_msg_dump(msg); } /* diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 63482ef..4bfe880 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -773,6 +773,7 @@ bad: pr_err("corrupt osd_op_reply got %d %d expected %d\n", (int)msg->front.iov_len, le32_to_cpu(msg->hdr.front_len), (int)sizeof(*rhead)); + ceph_msg_dump(msg); } @@ -964,6 +965,7 @@ done: bad: pr_err("osdc handle_map corrupt msg\n"); + ceph_msg_dump(msg); up_write(&osdc->map_sem); return; } diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index be5318a..8c8ffe5 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -726,6 +726,9 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, bad: pr_err("corrupt inc osdmap epoch %d off %d (%p of %p-%p)\n", epoch, (int)(*p - start), *p, start, end); + print_hex_dump(KERN_DEBUG, "osdmap: ", + DUMP_PREFIX_OFFSET, 16, 1, + start, end - start, true); if (newcrush) crush_destroy(newcrush); return ERR_PTR(err); diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 52f46a1..dcf18d9 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -877,6 +877,7 @@ split_skip_inode: bad: pr_err("corrupt snap message from mds%d\n", mds); + ceph_msg_dump(msg); out: if (locked_rwsem) up_write(&mdsc->snap_rwsem); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index a828943..6d02a16 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -602,13 +602,8 @@ int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid) { if (client->have_fsid) { if (ceph_fsid_compare(&client->fsid, fsid)) { - print_hex_dump(KERN_ERR, "this fsid: ", - DUMP_PREFIX_NONE, 16, 1, - (void *)fsid, 16, 0); - print_hex_dump(KERN_ERR, " old fsid: ", - DUMP_PREFIX_NONE, 16, 1, - (void *)&client->fsid, 16, 0); - pr_err("fsid mismatch\n"); + pr_err("bad fsid, had " FSID_FORMAT " got " FSID_FORMAT, + PR_FSID(&client->fsid), PR_FSID(fsid)); return -1; } } else { -- cgit v0.10.2 From cf3e5c409b5d66ec66207092a3f7e3e2c42c0f3f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 11 Dec 2009 09:48:05 -0800 Subject: ceph: plug leak of incoming message during connection fault/close If we explicitly close a connection, or there is a socket error, we need to drop any partially received message. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index b10f88c..b12604e 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -320,6 +320,11 @@ static void reset_connection(struct ceph_connection *con) ceph_msg_remove_list(&con->out_queue); ceph_msg_remove_list(&con->out_sent); + if (con->in_msg) { + ceph_msg_put(con->in_msg); + con->in_msg = NULL; + } + con->connect_seq = 0; con->out_seq = 0; if (con->out_msg) { @@ -1288,7 +1293,7 @@ static int read_partial_message(struct ceph_connection *con) con->in_msg = con->ops->alloc_msg(con, &con->in_hdr); if (!con->in_msg) { /* skip this message */ - dout("alloc_msg returned NULL, skipping message\n"); + pr_err("alloc_msg returned NULL, skipping message\n"); con->in_base_pos = -front_len - middle_len - data_len - sizeof(m->footer); con->in_tag = CEPH_MSGR_TAG_READY; @@ -1327,7 +1332,7 @@ static int read_partial_message(struct ceph_connection *con) if (con->ops->alloc_middle) ret = con->ops->alloc_middle(con, m); if (ret < 0) { - dout("alloc_middle failed, skipping payload\n"); + pr_err("alloc_middle fail skipping payload\n"); con->in_base_pos = -middle_len - data_len - sizeof(m->footer); ceph_msg_put(con->in_msg); @@ -1498,6 +1503,7 @@ more: set_bit(CONNECTING, &con->state); clear_bit(NEGOTIATING, &con->state); + BUG_ON(con->in_msg); con->in_tag = CEPH_MSGR_TAG_READY; dout("try_write initiating connect on %p new state %lu\n", con, con->state); -- cgit v0.10.2 From e2885f06ce31d82b556be021acfa2eba160f29cc Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 15 Dec 2009 10:27:48 -0800 Subject: ceph: make mds ops interruptible Signed-off-by: Sage Weil diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 29a93fe..d7cecc3 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1597,14 +1597,17 @@ int ceph_mdsc_do_request(struct ceph_mds_client *mdsc, if (!req->r_reply) { mutex_unlock(&mdsc->mutex); if (req->r_timeout) { - err = wait_for_completion_timeout(&req->r_completion, - req->r_timeout); - if (err > 0) - err = 0; - else if (err == 0) + err = (long)wait_for_completion_interruptible_timeout( + &req->r_completion, req->r_timeout); + if (err == 0) req->r_reply = ERR_PTR(-EIO); + else if (err < 0) + req->r_reply = ERR_PTR(err); } else { - wait_for_completion(&req->r_completion); + err = wait_for_completion_interruptible( + &req->r_completion); + if (err) + req->r_reply = ERR_PTR(err); } mutex_lock(&mdsc->mutex); } -- cgit v0.10.2 From 06edf046dd68ccbc7cf5f70f957a31702d0e7596 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 15 Dec 2009 14:44:32 -0800 Subject: ceph: include link to bdi in debugfs Signed-off-by: Sage Weil diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index b90fc3e..441484a 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -1,5 +1,6 @@ #include "ceph_debug.h" +#include #include #include #include @@ -24,6 +25,7 @@ * .../monc - mon client state * .../dentry_lru - dump contents of dentry lru * .../caps - expose cap (reservation) stats + * .../bdi - symlink to ../../bdi/something */ static struct dentry *ceph_debugfs_dir; @@ -407,6 +409,10 @@ int ceph_debugfs_client_init(struct ceph_client *client) if (!client->debugfs_caps) goto out; + sprintf(name, "../../bdi/%s", dev_name(client->sb->s_bdi->dev)); + client->debugfs_bdi = debugfs_create_symlink("bdi", client->debugfs_dir, + name); + return 0; out: @@ -416,6 +422,7 @@ out: void ceph_debugfs_client_cleanup(struct ceph_client *client) { + debugfs_remove(client->debugfs_bdi); debugfs_remove(client->debugfs_caps); debugfs_remove(client->debugfs_dentry_lru); debugfs_remove(client->debugfs_osdmap); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index de5e324..2304bd2 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -143,6 +143,7 @@ struct ceph_client { struct dentry *debugfs_monmap; struct dentry *debugfs_mdsmap, *debugfs_osdmap; struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps; + struct dentry *debugfs_bdi; #endif }; -- cgit v0.10.2 From 169e16ce816ca417286daf1db25de424a9d65a0c Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Wed, 16 Dec 2009 14:22:17 -0800 Subject: ceph: remove unaccessible code Signed-off-by: Yehuda Sadeh diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index b12604e..2e4e977 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1531,10 +1531,6 @@ more_kvec: ret = write_partial_kvec(con); if (ret <= 0) goto done; - if (ret < 0) { - dout("try_write write_partial_kvec err %d\n", ret); - goto done; - } } /* msg pages? */ -- cgit v0.10.2 From dbd646a851713bec5bfff40ecf624b2e78518fe5 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Wed, 16 Dec 2009 14:51:06 -0800 Subject: ceph: writepage grabs and releases inode Fixes a deadlock that is triggered due to kswapd, while the page was locked and the iput couldn't tear down the address space. Signed-off-by: Yehuda Sadeh diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index bf53581..d0cdceb 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -448,8 +448,13 @@ out: static int ceph_writepage(struct page *page, struct writeback_control *wbc) { - int err = writepage_nounlock(page, wbc); + int err; + struct inode *inode = page->mapping->host; + BUG_ON(!inode); + igrab(inode); + err = writepage_nounlock(page, wbc); unlock_page(page); + iput(inode); return err; } -- cgit v0.10.2 From 2baba25019ec564cd247af74013873d69a0b8190 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Fri, 18 Dec 2009 13:51:57 -0800 Subject: ceph: writeback congestion control Set bdi congestion bit when amount of write data in flight exceeds adjustable threshold. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index d0cdceb..a6850a1 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -47,6 +47,12 @@ * accounting is preserved. */ +#define CONGESTION_ON_THRESH(congestion_kb) (congestion_kb >> (PAGE_SHIFT-10)) +#define CONGESTION_OFF_THRESH(congestion_kb) \ + (CONGESTION_ON_THRESH(congestion_kb) - \ + (CONGESTION_ON_THRESH(congestion_kb) >> 2)) + + /* * Dirty a page. Optimistically adjust accounting, on the assumption @@ -377,6 +383,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) { struct inode *inode; struct ceph_inode_info *ci; + struct ceph_client *client; struct ceph_osd_client *osdc; loff_t page_off = page->index << PAGE_CACHE_SHIFT; int len = PAGE_CACHE_SIZE; @@ -384,6 +391,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) int err = 0; struct ceph_snap_context *snapc; u64 snap_size = 0; + long writeback_stat; dout("writepage %p idx %lu\n", page, page->index); @@ -393,7 +401,8 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) } inode = page->mapping->host; ci = ceph_inode(inode); - osdc = &ceph_inode_to_client(inode)->osdc; + client = ceph_inode_to_client(inode); + osdc = &client->osdc; /* verify this is a writeable snap context */ snapc = (void *)page->private; @@ -420,6 +429,11 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) dout("writepage %p page %p index %lu on %llu~%u\n", inode, page, page->index, page_off, len); + writeback_stat = atomic_long_inc_return(&client->writeback_count); + if (writeback_stat > + CONGESTION_ON_THRESH(client->mount_args->congestion_kb)) + set_bdi_congested(&client->backing_dev_info, BLK_RW_ASYNC); + set_page_writeback(page); err = ceph_osdc_writepages(osdc, ceph_vino(inode), &ci->i_layout, snapc, @@ -499,6 +513,8 @@ static void writepages_finish(struct ceph_osd_request *req, struct writeback_control *wbc = req->r_wbc; __s32 rc = -EIO; u64 bytes = 0; + struct ceph_client *client = ceph_inode_to_client(inode); + long writeback_stat; /* parse reply */ replyhead = msg->front.iov_base; @@ -524,6 +540,13 @@ static void writepages_finish(struct ceph_osd_request *req, BUG_ON(!page); WARN_ON(!PageUptodate(page)); + writeback_stat = + atomic_long_dec_return(&client->writeback_count); + if (writeback_stat < + CONGESTION_OFF_THRESH(client->mount_args->congestion_kb)) + clear_bdi_congested(&client->backing_dev_info, + BLK_RW_ASYNC); + if (i >= wrote) { dout("inode %p skipping page %p\n", inode, page); wbc->pages_skipped++; @@ -666,6 +689,7 @@ retry: u64 offset, len; struct ceph_osd_request_head *reqhead; struct ceph_osd_op *op; + long writeback_stat; next = 0; locked_pages = 0; @@ -773,6 +797,12 @@ get_more_pages: first = i; dout("%p will write page %p idx %lu\n", inode, page, page->index); + + writeback_stat = atomic_long_inc_return(&client->writeback_count); + if (writeback_stat > CONGESTION_ON_THRESH(client->mount_args->congestion_kb)) { + set_bdi_congested(&client->backing_dev_info, BLK_RW_ASYNC); + } + set_page_writeback(page); req->r_pages[locked_pages] = page; locked_pages++; @@ -998,7 +1028,8 @@ static int ceph_write_end(struct file *file, struct address_space *mapping, struct page *page, void *fsdata) { struct inode *inode = file->f_dentry->d_inode; - struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + struct ceph_client *client = ceph_inode_to_client(inode); + struct ceph_mds_client *mdsc = &client->mdsc; unsigned from = pos & (PAGE_CACHE_SIZE - 1); int check_cap = 0; diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 441484a..22d3b47 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -320,6 +320,30 @@ DEFINE_SHOW_FUNC(osdc_show) DEFINE_SHOW_FUNC(dentry_lru_show) DEFINE_SHOW_FUNC(caps_show) +static int congestion_kb_set(void *data, u64 val) +{ + struct ceph_client *client = (struct ceph_client *)data; + + if (client) + client->mount_args->congestion_kb = (int)val; + + return 0; +} + +static int congestion_kb_get(void *data, u64 *val) +{ + struct ceph_client *client = (struct ceph_client *)data; + + if (client) + *val = (u64)client->mount_args->congestion_kb; + + return 0; +} + + +DEFINE_SIMPLE_ATTRIBUTE(congestion_kb_fops, congestion_kb_get, + congestion_kb_set, "%llu\n"); + int __init ceph_debugfs_init(void) { ceph_debugfs_dir = debugfs_create_dir("ceph", NULL); @@ -409,6 +433,14 @@ int ceph_debugfs_client_init(struct ceph_client *client) if (!client->debugfs_caps) goto out; + client->debugfs_congestion_kb = debugfs_create_file("writeback_congestion_kb", + 0600, + client->debugfs_dir, + client, + &congestion_kb_fops); + if (!client->debugfs_congestion_kb) + goto out; + sprintf(name, "../../bdi/%s", dev_name(client->sb->s_bdi->dev)); client->debugfs_bdi = debugfs_create_symlink("bdi", client->debugfs_dir, name); @@ -431,6 +463,7 @@ void ceph_debugfs_client_cleanup(struct ceph_client *client) debugfs_remove(client->osdc.debugfs_file); debugfs_remove(client->mdsc.debugfs_file); debugfs_remove(client->monc.debugfs_file); + debugfs_remove(client->debugfs_congestion_kb); debugfs_remove(client->debugfs_dir); } diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 6d02a16..b9cb8ce 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -150,6 +150,35 @@ static void ceph_inode_init_once(void *foo) inode_init_once(&ci->vfs_inode); } +static int default_congestion_kb(void) +{ + int congestion_kb; + + /* + * Copied from NFS + * + * congestion size, scale with available memory. + * + * 64MB: 8192k + * 128MB: 11585k + * 256MB: 16384k + * 512MB: 23170k + * 1GB: 32768k + * 2GB: 46340k + * 4GB: 65536k + * 8GB: 92681k + * 16GB: 131072k + * + * This allows larger machines to have larger/more transfers. + * Limit the default to 256M + */ + congestion_kb = (16*int_sqrt(totalram_pages)) << (PAGE_SHIFT-10); + if (congestion_kb > 256*1024) + congestion_kb = 256*1024; + + return congestion_kb; +} + static int __init init_caches(void) { ceph_inode_cachep = kmem_cache_create("ceph_inode_info", @@ -267,6 +296,7 @@ enum { Opt_caps_wanted_delay_min, Opt_caps_wanted_delay_max, Opt_readdir_max_entries, + Opt_congestion_kb, Opt_last_int, /* int args above */ Opt_snapdirname, @@ -295,6 +325,7 @@ static match_table_t arg_tokens = { {Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"}, {Opt_caps_wanted_delay_max, "caps_wanted_delay_max=%d"}, {Opt_readdir_max_entries, "readdir_max_entries=%d"}, + {Opt_congestion_kb, "write_congestion_kb=%d"}, /* int args above */ {Opt_snapdirname, "snapdirname=%s"}, {Opt_name, "name=%s"}, @@ -342,6 +373,7 @@ static struct ceph_mount_args *parse_mount_args(int flags, char *options, args->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL); args->cap_release_safety = CEPH_CAPS_PER_RELEASE * 4; args->max_readdir = 1024; + args->congestion_kb = default_congestion_kb(); /* ip1[:port1][,ip2[:port2]...]:/subdir/in/fs */ err = -EINVAL; @@ -445,6 +477,9 @@ static struct ceph_mount_args *parse_mount_args(int flags, char *options, case Opt_readdir_max_entries: args->max_readdir = intval; break; + case Opt_congestion_kb: + args->congestion_kb = intval; + break; case Opt_noshare: args->flags |= CEPH_OPT_NOSHARE; @@ -516,6 +551,7 @@ static struct ceph_client *ceph_create_client(struct ceph_mount_args *args) client->msgr = NULL; client->mount_err = 0; + atomic_long_set(&client->writeback_count, 0); err = bdi_init(&client->backing_dev_info); if (err < 0) diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 2304bd2..62d9ae4 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -59,6 +59,7 @@ struct ceph_mount_args { int wsize; int rsize; /* max readahead */ int max_readdir; /* max readdir size */ + int congestion_kb; /* max readdir size */ int osd_timeout; char *snapdir_name; /* default ".snap" */ char *name; @@ -136,6 +137,7 @@ struct ceph_client { struct workqueue_struct *wb_wq; struct workqueue_struct *pg_inv_wq; struct workqueue_struct *trunc_wq; + atomic_long_t writeback_count; struct backing_dev_info backing_dev_info; @@ -143,6 +145,7 @@ struct ceph_client { struct dentry *debugfs_monmap; struct dentry *debugfs_mdsmap, *debugfs_osdmap; struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps; + struct dentry *debugfs_congestion_kb; struct dentry *debugfs_bdi; #endif }; -- cgit v0.10.2 From c4a29f26d50bea65809ca670992108a33aa2efa6 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Dec 2009 11:42:18 -0800 Subject: ceph: ensure rename target dentry fails revalidation This works around a bug in vfs_rename_dir() that rehashes the target dentry. Ensure such dentries always fail revalidation by timing out the dentry lease and kicking it out of the current directory lease gen. This can be reverted when the vfs bug is fixed. Signed-off-by: Sage Weil diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index db68468..8774b28 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -939,6 +939,10 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, req->r_old_dentry->d_name.len, req->r_old_dentry->d_name.name, dn, dn->d_name.len, dn->d_name.name); + /* ensure target dentry is invalidated, despite + rehashing bug in vfs_rename_dir */ + dn->d_time = jiffies; + ceph_dentry(dn)->lease_shared_gen = 0; /* take overwritten dentry's readdir offset */ ceph_dentry(req->r_old_dentry)->offset = ceph_dentry(dn)->offset; -- cgit v0.10.2 From 5de7bf8afa87f75af5ef3d6f9fce3e171cac834c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Dec 2009 11:48:44 -0800 Subject: ceph: do not drop lease during revalidate We need to hold session s_mutex for __ceph_mdsc_drop_dentry_lease(), which we don't, so skip it. It was purely an optimization. Signed-off-by: Sage Weil diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 89ce3ba..fde839c 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -920,8 +920,6 @@ static int dentry_lease_is_valid(struct dentry *dentry) di->lease_renew_after = 0; di->lease_renew_from = jiffies; } - } else { - __ceph_mdsc_drop_dentry_lease(dentry); } } spin_unlock(&dentry->d_lock); -- cgit v0.10.2 From 30dc6381bbac213987be6fe0b0fb89868ff1f2c0 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Dec 2009 14:49:37 -0800 Subject: ceph: fix error paths for corrupt osdmap messages Both osdmap_decode() and osdmap_apply_incremental() should never return NULL. Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 4bfe880..b474b3a 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -910,6 +910,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) err = PTR_ERR(newmap); goto bad; } + BUG_ON(!newmap); if (newmap != osdc->osdmap) { ceph_osdmap_destroy(osdc->osdmap); osdc->osdmap = newmap; @@ -946,6 +947,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) err = PTR_ERR(newmap); goto bad; } + BUG_ON(!newmap); oldmap = osdc->osdmap; osdc->osdmap = newmap; if (oldmap) diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 8c8ffe5..a941630 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -200,6 +200,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end) size = sizeof(struct crush_bucket_straw); break; default: + err = -EINVAL; goto bad; } BUG_ON(size == 0); @@ -278,6 +279,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end) /* len */ ceph_decode_32_safe(p, end, yes, bad); #if BITS_PER_LONG == 32 + err = -EINVAL; if (yes > ULONG_MAX / sizeof(struct crush_rule_step)) goto bad; #endif @@ -489,11 +491,10 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) ceph_decode_copy(p, &pgid, sizeof(pgid)); n = ceph_decode_32(p); ceph_decode_need(p, end, n * sizeof(u32), bad); + err = -ENOMEM; pg = kmalloc(sizeof(*pg) + n*sizeof(u32), GFP_NOFS); - if (!pg) { - err = -ENOMEM; + if (!pg) goto bad; - } pg->pgid = pgid; pg->len = n; for (j = 0; j < n; j++) @@ -564,8 +565,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, if (len > 0) { dout("apply_incremental full map len %d, %p to %p\n", len, *p, end); - newmap = osdmap_decode(p, min(*p+len, end)); - return newmap; /* error or not */ + return osdmap_decode(p, min(*p+len, end)); } /* new crush? */ @@ -809,6 +809,7 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol, struct ceph_pg_pool_info *pool; unsigned ps; + BUG_ON(!osdmap); if (poolid >= osdmap->num_pools) return -EIO; -- cgit v0.10.2 From 7067f797b8409f1e10ec95ac2c1e17a200173d13 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Dec 2009 16:02:37 -0800 Subject: ceph: fix incremental osdmap pg_temp decoding bug An incremental pg_temp wasn't being decoded properly (wrong bound on for loop). Also remove unused local variable, while we're at it. Signed-off-by: Sage Weil diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index a941630..0dbd606 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -538,7 +538,6 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, struct ceph_osdmap *map, struct ceph_messenger *msgr) { - struct ceph_osdmap *newmap = map; struct crush_map *newcrush = NULL; struct ceph_fsid fsid; u32 epoch = 0; @@ -701,7 +700,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, } pg->pgid = pgid; pg->len = pglen; - for (j = 0; j < len; j++) + for (j = 0; j < pglen; j++) pg->osds[j] = ceph_decode_32(p); err = __insert_pg_mapping(pg, &map->pg_temp); if (err) -- cgit v0.10.2 From 5dacf09121ffb2e5fc7d15b78cae0b77042a1935 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Dec 2009 20:40:34 -0800 Subject: ceph: do not touch_caps while iterating over caps list Avoid confusing iterate_session_caps(), flag the session while we are iterating so that __touch_cap does not rearrange items on the list. All other modifiers of session->s_caps do so under the protection of s_mutex. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index dfb509f..93c1afe 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -697,10 +697,15 @@ static void __touch_cap(struct ceph_cap *cap) { struct ceph_mds_session *s = cap->session; - dout("__touch_cap %p cap %p mds%d\n", &cap->ci->vfs_inode, cap, - s->s_mds); spin_lock(&s->s_cap_lock); - list_move_tail(&cap->session_caps, &s->s_caps); + if (!s->s_iterating_caps) { + dout("__touch_cap %p cap %p mds%d\n", &cap->ci->vfs_inode, cap, + s->s_mds); + list_move_tail(&cap->session_caps, &s->s_caps); + } else { + dout("__touch_cap %p cap %p mds%d NOP, iterating over caps\n", + &cap->ci->vfs_inode, cap, s->s_mds); + } spin_unlock(&s->s_cap_lock); } diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index d7cecc3..63ca3b1 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -337,10 +337,12 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_renew_seq = 0; INIT_LIST_HEAD(&s->s_caps); s->s_nr_caps = 0; + s->s_trim_caps = 0; atomic_set(&s->s_ref, 1); INIT_LIST_HEAD(&s->s_waiting); INIT_LIST_HEAD(&s->s_unsafe); s->s_num_cap_releases = 0; + s->s_iterating_caps = false; INIT_LIST_HEAD(&s->s_cap_releases); INIT_LIST_HEAD(&s->s_cap_releases_done); INIT_LIST_HEAD(&s->s_cap_flushing); @@ -699,6 +701,7 @@ static int iterate_session_caps(struct ceph_mds_session *session, dout("iterate_session_caps %p mds%d\n", session, session->s_mds); spin_lock(&session->s_cap_lock); + session->s_iterating_caps = true; list_for_each_entry_safe(cap, ncap, &session->s_caps, session_caps) { inode = igrab(&cap->ci->vfs_inode); if (!inode) @@ -706,13 +709,15 @@ static int iterate_session_caps(struct ceph_mds_session *session, spin_unlock(&session->s_cap_lock); ret = cb(inode, cap, arg); iput(inode); - if (ret < 0) - return ret; spin_lock(&session->s_cap_lock); + if (ret < 0) + goto out; } + ret = 0; +out: + session->s_iterating_caps = false; spin_unlock(&session->s_cap_lock); - - return 0; + return ret; } static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, @@ -935,6 +940,7 @@ static int trim_caps(struct ceph_mds_client *mdsc, dout("trim_caps mds%d done: %d / %d, trimmed %d\n", session->s_mds, session->s_nr_caps, max_caps, trim_caps - session->s_trim_caps); + session->s_trim_caps = 0; } return 0; } diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 41af5ca..b1c2025 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -114,6 +114,7 @@ struct ceph_mds_session { int s_num_cap_releases; struct list_head s_cap_releases; /* waiting cap_release messages */ struct list_head s_cap_releases_done; /* ready to send */ + bool s_iterating_caps; /* protected by mutex */ struct list_head s_cap_flushing; /* inodes w/ flushing caps */ -- cgit v0.10.2 From e0e3271074e1ebd0b80a912a457ce03c971bcd66 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Dec 2009 21:04:26 -0800 Subject: ceph: only unregister registered bdi Signed-off-by: Sage Weil diff --git a/fs/ceph/super.c b/fs/ceph/super.c index b9cb8ce..cd81c84 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -942,7 +942,8 @@ static void ceph_kill_sb(struct super_block *s) dout("kill_sb %p\n", s); ceph_mdsc_pre_umount(&client->mdsc); kill_anon_super(s); /* will call put_super after sb is r/o */ - bdi_unregister(&client->backing_dev_info); + if (s->s_bdi == &client->backing_dev_info) + bdi_unregister(&client->backing_dev_info); bdi_destroy(&client->backing_dev_info); ceph_destroy_client(client); } -- cgit v0.10.2 From 529cfcc46ffa2cbe4d07641c11e65f67fe7b66e4 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 22 Dec 2009 10:29:39 -0800 Subject: ceph: unregister canceled/timed out osd requests Canceled or timed out osd requests were getting left in the request list and never deallocated (until umount). Unregister if they are canceled (control-c) or time out. Signed-off-by: Sage Weil diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index b474b3a..a1800fb 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -1071,8 +1071,9 @@ int ceph_osdc_wait_request(struct ceph_osd_client *osdc, if (rc < 0) { mutex_lock(&osdc->request_mutex); __cancel_request(req); + __unregister_request(osdc, req); mutex_unlock(&osdc->request_mutex); - dout("wait_request tid %llu timed out\n", req->r_tid); + dout("wait_request tid %llu canceled/timed out\n", req->r_tid); return rc; } -- cgit v0.10.2 From ec302645f4a9bd9ec757c30d185557e1c0972c1a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 22 Dec 2009 10:43:42 -0800 Subject: ceph: use connection mutex to protect read and write stages Use a single mutex (previously out_mutex) to protect both read and write activity from concurrent ceph_con_* calls. Drop the mutex when doing callbacks to avoid nested locking (the callback may need to call something like ceph_con_close). Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 2e4e977..c03b418 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -316,7 +316,6 @@ static void reset_connection(struct ceph_connection *con) { /* reset connection, out_queue, msg_ and connect_seq */ /* discard existing out_queue and msg_seq */ - mutex_lock(&con->out_mutex); ceph_msg_remove_list(&con->out_queue); ceph_msg_remove_list(&con->out_sent); @@ -332,7 +331,6 @@ static void reset_connection(struct ceph_connection *con) con->out_msg = NULL; } con->in_seq = 0; - mutex_unlock(&con->out_mutex); } /* @@ -343,7 +341,9 @@ void ceph_con_close(struct ceph_connection *con) dout("con_close %p peer %s\n", con, pr_addr(&con->peer_addr.in_addr)); set_bit(CLOSED, &con->state); /* in case there's queued work */ clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ + mutex_lock(&con->mutex); reset_connection(con); + mutex_unlock(&con->mutex); queue_con(con); } @@ -392,7 +392,7 @@ void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con) memset(con, 0, sizeof(*con)); atomic_set(&con->nref, 1); con->msgr = msgr; - mutex_init(&con->out_mutex); + mutex_init(&con->mutex); INIT_LIST_HEAD(&con->out_queue); INIT_LIST_HEAD(&con->out_sent); INIT_DELAYED_WORK(&con->work, con_work); @@ -571,11 +571,13 @@ static void prepare_connect_authorizer(struct ceph_connection *con) int auth_len = 0; int auth_protocol = 0; + mutex_unlock(&con->mutex); if (con->ops->get_authorizer) con->ops->get_authorizer(con, &auth_buf, &auth_len, &auth_protocol, &con->auth_reply_buf, &con->auth_reply_buf_len, con->auth_retry); + mutex_lock(&con->mutex); con->out_connect.authorizer_protocol = cpu_to_le32(auth_protocol); con->out_connect.authorizer_len = cpu_to_le32(auth_len); @@ -1094,10 +1096,13 @@ static int process_connect(struct ceph_connection *con) le32_to_cpu(con->out_connect.protocol_version), le32_to_cpu(con->in_reply.protocol_version)); con->error_msg = "protocol version mismatch"; - if (con->ops->bad_proto) - con->ops->bad_proto(con); reset_connection(con); set_bit(CLOSED, &con->state); /* in case there's queued work */ + + mutex_unlock(&con->mutex); + if (con->ops->bad_proto) + con->ops->bad_proto(con); + mutex_lock(&con->mutex); return -1; case CEPH_MSGR_TAG_BADAUTHORIZER: @@ -1133,9 +1138,11 @@ static int process_connect(struct ceph_connection *con) prepare_read_connect(con); /* Tell ceph about it. */ + mutex_unlock(&con->mutex); pr_info("reset on %s%lld\n", ENTITY_NAME(con->peer_name)); if (con->ops->peer_reset) con->ops->peer_reset(con); + mutex_lock(&con->mutex); break; case CEPH_MSGR_TAG_RETRY_SESSION: @@ -1221,7 +1228,6 @@ static void process_ack(struct ceph_connection *con) u64 ack = le64_to_cpu(con->in_temp_ack); u64 seq; - mutex_lock(&con->out_mutex); while (!list_empty(&con->out_sent)) { m = list_first_entry(&con->out_sent, struct ceph_msg, list_head); @@ -1232,7 +1238,6 @@ static void process_ack(struct ceph_connection *con) le16_to_cpu(m->hdr.type), m); ceph_msg_remove(m); } - mutex_unlock(&con->out_mutex); prepare_read_tag(con); } @@ -1366,8 +1371,10 @@ static int read_partial_message(struct ceph_connection *con) /* find pages for data payload */ want = calc_pages_for(data_off & ~PAGE_MASK, data_len); ret = -1; + mutex_unlock(&con->mutex); if (con->ops->prepare_pages) ret = con->ops->prepare_pages(con, m, want); + mutex_lock(&con->mutex); if (ret < 0) { dout("%p prepare_pages failed, skipping payload\n", m); con->in_base_pos = -data_len - sizeof(m->footer); @@ -1454,9 +1461,8 @@ static void process_message(struct ceph_connection *con) if (con->peer_name.type == 0) con->peer_name = msg->hdr.src.name; - mutex_lock(&con->out_mutex); con->in_seq++; - mutex_unlock(&con->out_mutex); + mutex_unlock(&con->mutex); dout("===== %p %llu from %s%lld %d=%s len %d+%d (%u %u %u) =====\n", msg, le64_to_cpu(msg->hdr.seq), @@ -1467,6 +1473,8 @@ static void process_message(struct ceph_connection *con) le32_to_cpu(msg->hdr.data_len), con->in_front_crc, con->in_middle_crc, con->in_data_crc); con->ops->dispatch(con, msg); + + mutex_lock(&con->mutex); prepare_read_tag(con); } @@ -1483,7 +1491,7 @@ static int try_write(struct ceph_connection *con) dout("try_write start %p state %lu nref %d\n", con, con->state, atomic_read(&con->nref)); - mutex_lock(&con->out_mutex); + mutex_lock(&con->mutex); more: dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes); @@ -1576,7 +1584,7 @@ do_next: done: ret = 0; out: - mutex_unlock(&con->out_mutex); + mutex_unlock(&con->mutex); dout("try_write done on %p\n", con); return ret; } @@ -1600,6 +1608,8 @@ static int try_read(struct ceph_connection *con) dout("try_read start on %p\n", con); msgr = con->msgr; + mutex_lock(&con->mutex); + more: dout("try_read tag %d in_base_pos %d\n", (int)con->in_tag, con->in_base_pos); @@ -1693,6 +1703,7 @@ more: done: ret = 0; out: + mutex_unlock(&con->mutex); dout("try_read done on %p\n", con); return ret; @@ -1818,6 +1829,8 @@ static void ceph_fault(struct ceph_connection *con) clear_bit(BUSY, &con->state); /* to avoid an improbable race */ + mutex_lock(&con->mutex); + con_close_socket(con); if (con->in_msg) { @@ -1827,24 +1840,24 @@ static void ceph_fault(struct ceph_connection *con) /* If there are no messages in the queue, place the connection * in a STANDBY state (i.e., don't try to reconnect just yet). */ - mutex_lock(&con->out_mutex); if (list_empty(&con->out_queue) && !con->out_keepalive_pending) { dout("fault setting STANDBY\n"); set_bit(STANDBY, &con->state); - mutex_unlock(&con->out_mutex); + mutex_unlock(&con->mutex); goto out; } /* Requeue anything that hasn't been acked, and retry after a * delay. */ list_splice_init(&con->out_sent, &con->out_queue); - mutex_unlock(&con->out_mutex); if (con->delay == 0) con->delay = BASE_DELAY_INTERVAL; else if (con->delay < MAX_DELAY_INTERVAL) con->delay *= 2; + mutex_unlock(&con->mutex); + /* explicitly schedule work to try to reconnect again later. */ dout("fault queueing %p delay %lu\n", con, con->delay); con->ops->get(con); @@ -1920,7 +1933,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) msg->hdr.dst_erank = con->peer_addr.erank; /* queue */ - mutex_lock(&con->out_mutex); + mutex_lock(&con->mutex); BUG_ON(!list_empty(&msg->list_head)); list_add_tail(&msg->list_head, &con->out_queue); dout("----- %p to %s%lld %d=%s len %d+%d+%d -----\n", msg, @@ -1929,7 +1942,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) le32_to_cpu(msg->hdr.front_len), le32_to_cpu(msg->hdr.middle_len), le32_to_cpu(msg->hdr.data_len)); - mutex_unlock(&con->out_mutex); + mutex_unlock(&con->mutex); /* if there wasn't anything waiting to send before, queue * new work */ @@ -1942,7 +1955,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) */ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) { - mutex_lock(&con->out_mutex); + mutex_lock(&con->mutex); if (!list_empty(&msg->list_head)) { dout("con_revoke %p msg %p\n", con, msg); list_del_init(&msg->list_head); @@ -1959,7 +1972,7 @@ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) } else { dout("con_revoke %p msg %p - not queued (sent?)\n", con, msg); } - mutex_unlock(&con->out_mutex); + mutex_unlock(&con->mutex); } /* diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index e04c214..94b55de 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -155,8 +155,9 @@ struct ceph_connection { void *auth_reply_buf; /* where to put the authorizer reply */ int auth_reply_buf_len; + struct mutex mutex; + /* out queue */ - struct mutex out_mutex; struct list_head out_queue; struct list_head out_sent; /* sending or sent but unacked */ u64 out_seq; /* last message queued for send */ -- cgit v0.10.2 From 350b1c32ea58d29e25d63fc25e92dd48f9339546 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 22 Dec 2009 10:45:45 -0800 Subject: ceph: control access to page vector for incoming data When we issue an OSD read, we specify a vector of pages that the data is to be read into. The request may be sent multiple times, to multiple OSDs, if the osdmap changes, which means we can get more than one reply. Only read data into the page vector if the reply is coming from the OSD we last sent the request to. Keep track of which connection is using the vector by taking a reference. If another connection was already using the vector before and a new reply comes in on the right connection, revoke the pages from the other connection. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index c03b418..506b638 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1976,6 +1976,35 @@ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) } /* + * Revoke a page vector that we may be reading data into + */ +void ceph_con_revoke_pages(struct ceph_connection *con, struct page **pages) +{ + mutex_lock(&con->mutex); + if (con->in_msg && con->in_msg->pages == pages) { + unsigned data_len = le32_to_cpu(con->in_hdr.data_len); + + /* skip rest of message */ + dout("con_revoke_pages %p msg %p pages %p revoked\n", con, + con->in_msg, pages); + if (con->in_msg_pos.data_pos < data_len) + con->in_base_pos = con->in_msg_pos.data_pos - data_len; + else + con->in_base_pos = con->in_base_pos - + sizeof(struct ceph_msg_header) - + sizeof(struct ceph_msg_footer); + con->in_msg->pages = NULL; + ceph_msg_put(con->in_msg); + con->in_msg = NULL; + con->in_tag = CEPH_MSGR_TAG_READY; + } else { + dout("con_revoke_pages %p msg %p pages %p no-op\n", + con, con->in_msg, pages); + } + mutex_unlock(&con->mutex); +} + +/* * Queue a keepalive byte to ensure the tcp connection is alive. */ void ceph_con_keepalive(struct ceph_connection *con) diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index 94b55de..7e2aab1 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -230,6 +230,8 @@ extern void ceph_con_open(struct ceph_connection *con, extern void ceph_con_close(struct ceph_connection *con); extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg); extern void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg); +extern void ceph_con_revoke_pages(struct ceph_connection *con, + struct page **pages); extern void ceph_con_keepalive(struct ceph_connection *con); extern struct ceph_connection *ceph_con_get(struct ceph_connection *con); extern void ceph_con_put(struct ceph_connection *con); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index a1800fb..374f001 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -87,6 +87,13 @@ void ceph_osdc_release_request(struct kref *kref) ceph_msg_put(req->r_request); if (req->r_reply) ceph_msg_put(req->r_reply); + if (req->r_con_filling_pages) { + dout("release_request revoking pages %p from con %p\n", + req->r_pages, req->r_con_filling_pages); + ceph_con_revoke_pages(req->r_con_filling_pages, + req->r_pages); + ceph_con_put(req->r_con_filling_pages); + } if (req->r_own_pages) ceph_release_page_vector(req->r_pages, req->r_num_pages); @@ -687,7 +694,8 @@ static void handle_timeout(struct work_struct *work) * handle osd op reply. either call the callback if it is specified, * or do the completion to wake up the waiting thread. */ -static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg) +static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, + struct ceph_connection *con) { struct ceph_osd_reply_head *rhead = msg->front.iov_base; struct ceph_osd_request *req; @@ -715,6 +723,16 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg) ceph_osdc_get_request(req); flags = le32_to_cpu(rhead->flags); + /* + * if this connection filled our pages, drop our reference now, to + * avoid a (safe but slower) revoke later. + */ + if (req->r_con_filling_pages == con && req->r_pages == msg->pages) { + dout(" got pages, dropping con_filling_pages ref %p\n", con); + req->r_con_filling_pages = NULL; + ceph_con_put(con); + } + if (req->r_reply) { /* * once we see the message has been received, we don't @@ -1007,14 +1025,20 @@ static int prepare_pages(struct ceph_connection *con, struct ceph_msg *m, } dout("prepare_pages tid %llu has %d pages, want %d\n", tid, req->r_num_pages, want); - if (likely(req->r_num_pages >= want && !req->r_prepared_pages)) { - m->pages = req->r_pages; - m->nr_pages = req->r_num_pages; - req->r_reply = m; /* only for duration of read over socket */ - ceph_msg_get(m); - req->r_prepared_pages = 1; - ret = 0; /* success */ + if (unlikely(req->r_num_pages < want)) + goto out; + + if (req->r_con_filling_pages) { + dout("revoking pages %p from old con %p\n", req->r_pages, + req->r_con_filling_pages); + ceph_con_revoke_pages(req->r_con_filling_pages, req->r_pages); + ceph_con_put(req->r_con_filling_pages); } + req->r_con_filling_pages = ceph_con_get(con); + req->r_reply = ceph_msg_get(m); /* for duration of read over socket */ + m->pages = req->r_pages; + m->nr_pages = req->r_num_pages; + ret = 0; /* success */ out: mutex_unlock(&osdc->request_mutex); return ret; @@ -1269,7 +1293,7 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) ceph_osdc_handle_map(osdc, msg); break; case CEPH_MSG_OSD_OPREPLY: - handle_reply(osdc, msg); + handle_reply(osdc, msg, con); break; default: diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index 2e4cfd1..8fef71c 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -43,11 +43,13 @@ struct ceph_osd_request { struct list_head r_osd_item; struct ceph_osd *r_osd; + struct ceph_connection *r_con_filling_pages; + struct ceph_msg *r_request, *r_reply; int r_result; int r_flags; /* any additional flags for the osd */ u32 r_sent; /* >0 if r_request is sending/sent */ - int r_prepared_pages, r_got_reply; + int r_got_reply; int r_num_prealloc_reply; struct ceph_osd_client *r_osdc; -- cgit v0.10.2 From 0cf90ab5b075821940873e73cdbfeb8edc3dabe8 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 22 Dec 2009 10:45:18 -0800 Subject: ceph: more informative msgpool errors Signed-off-by: Sage Weil diff --git a/fs/ceph/msgpool.c b/fs/ceph/msgpool.c index ad5482c..2f04e0f 100644 --- a/fs/ceph/msgpool.c +++ b/fs/ceph/msgpool.c @@ -140,7 +140,7 @@ struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool, int front_len) return msg; } pr_err("msgpool_get %p now %d/%d, %s\n", pool, pool->num, - pool->min, pool->blocking ? "waiting" : "failing"); + pool->min, pool->blocking ? "waiting" : "may fail"); spin_unlock(&pool->lock); if (!pool->blocking) { @@ -151,6 +151,7 @@ struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool, int front_len) if (!IS_ERR(msg)) return msg; + pr_err("msgpool_get %p empty + alloc failed\n", pool); return ERR_PTR(-ENOMEM); } -- cgit v0.10.2 From 6df058c025ce343052c5516b1d8a9a7e73cddd64 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 22 Dec 2009 11:24:33 -0800 Subject: ceph: include transaction id in ceph_msg_header (protocol change) Many (most?) message types include a transaction id. By including it in the fixed size header, we always have it available even when we are unable to allocate memory for the (larger, variable sized) message body. This will allow us to error out the appropriate request instead of (silently) dropping the reply. Signed-off-by: Sage Weil diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 93c1afe..847ae64 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -922,14 +922,14 @@ static int send_cap_msg(struct ceph_mds_session *session, if (IS_ERR(msg)) return PTR_ERR(msg); - fc = msg->front.iov_base; + msg->hdr.tid = cpu_to_le64(flush_tid); + fc = msg->front.iov_base; memset(fc, 0, sizeof(*fc)); fc->cap_id = cpu_to_le64(cid); fc->op = cpu_to_le32(op); fc->seq = cpu_to_le32(seq); - fc->client_tid = cpu_to_le64(flush_tid); fc->issue_seq = cpu_to_le32(issue_seq); fc->migrate_seq = cpu_to_le32(mseq); fc->caps = cpu_to_le32(caps); @@ -2329,7 +2329,7 @@ restart: * Handle FLUSH_ACK from MDS, indicating that metadata we sent to the * MDS has been safely committed. */ -static void handle_cap_flush_ack(struct inode *inode, +static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, struct ceph_mds_caps *m, struct ceph_mds_session *session, struct ceph_cap *cap) @@ -2340,7 +2340,6 @@ static void handle_cap_flush_ack(struct inode *inode, unsigned seq = le32_to_cpu(m->seq); int dirty = le32_to_cpu(m->dirty); int cleaned = 0; - u64 flush_tid = le64_to_cpu(m->client_tid); int drop = 0; int i; @@ -2396,13 +2395,12 @@ out: * * Caller hold s_mutex. */ -static void handle_cap_flushsnap_ack(struct inode *inode, +static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid, struct ceph_mds_caps *m, struct ceph_mds_session *session) { struct ceph_inode_info *ci = ceph_inode(inode); u64 follows = le64_to_cpu(m->snap_follows); - u64 flush_tid = le64_to_cpu(m->client_tid); struct ceph_cap_snap *capsnap; int drop = 0; @@ -2587,12 +2585,14 @@ void ceph_handle_caps(struct ceph_mds_session *session, struct ceph_vino vino; u64 cap_id; u64 size, max_size; + u64 tid; int check_caps = 0; int r; dout("handle_caps from mds%d\n", mds); /* decode */ + tid = le64_to_cpu(msg->hdr.tid); if (msg->front.iov_len < sizeof(*h)) goto bad; h = msg->front.iov_base; @@ -2621,7 +2621,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, /* these will work even if we don't have a cap yet */ switch (op) { case CEPH_CAP_OP_FLUSHSNAP_ACK: - handle_cap_flushsnap_ack(inode, h, session); + handle_cap_flushsnap_ack(inode, tid, h, session); goto done; case CEPH_CAP_OP_EXPORT: @@ -2662,7 +2662,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, break; case CEPH_CAP_OP_FLUSH_ACK: - handle_cap_flush_ack(inode, h, session, cap); + handle_cap_flush_ack(inode, tid, h, session, cap); break; case CEPH_CAP_OP_TRUNC: diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index e2fd024..e87dfa6 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -35,7 +35,7 @@ * internal cluster protocols separately from the public, * client-facing protocol. */ -#define CEPH_OSD_PROTOCOL 7 /* cluster internal */ +#define CEPH_OSD_PROTOCOL 8 /* cluster internal */ #define CEPH_MDS_PROTOCOL 9 /* cluster internal */ #define CEPH_MON_PROTOCOL 5 /* cluster internal */ #define CEPH_OSDC_PROTOCOL 22 /* server/client */ @@ -136,7 +136,6 @@ struct ceph_mon_request_header { struct ceph_mon_statfs { struct ceph_mon_request_header monhdr; struct ceph_fsid fsid; - __le64 tid; } __attribute__ ((packed)); struct ceph_statfs { @@ -146,7 +145,6 @@ struct ceph_statfs { struct ceph_mon_statfs_reply { struct ceph_fsid fsid; - __le64 tid; __le64 version; struct ceph_statfs st; } __attribute__ ((packed)); @@ -333,7 +331,7 @@ union ceph_mds_request_args { #define CEPH_MDS_FLAG_WANT_DENTRY 2 /* want dentry in reply */ struct ceph_mds_request_head { - __le64 tid, oldest_client_tid; + __le64 oldest_client_tid; __le32 mdsmap_epoch; /* on client */ __le32 flags; /* CEPH_MDS_FLAG_* */ __u8 num_retry, num_fwd; /* count retry, fwd attempts */ @@ -356,7 +354,6 @@ struct ceph_mds_request_release { /* client reply */ struct ceph_mds_reply_head { - __le64 tid; __le32 op; __le32 result; __le32 mdsmap_epoch; @@ -542,7 +539,6 @@ struct ceph_mds_caps { __le32 migrate_seq; __le64 snap_follows; __le32 snap_trace_len; - __le64 client_tid; /* for FLUSH(SNAP) -> FLUSH(SNAP)_ACK */ /* authlock */ __le32 uid, gid, mode; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 63ca3b1..ec884e2 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1339,6 +1339,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, if (IS_ERR(msg)) goto out_free2; + msg->hdr.tid = cpu_to_le64(req->r_tid); + head = msg->front.iov_base; p = msg->front.iov_base + sizeof(*head); end = msg->front.iov_base + msg->front.iov_len; @@ -1431,7 +1433,6 @@ static int __prepare_send_request(struct ceph_mds_client *mdsc, req->r_request = msg; rhead = msg->front.iov_base; - rhead->tid = cpu_to_le64(req->r_tid); rhead->oldest_client_tid = cpu_to_le64(__get_oldest_tid(mdsc)); if (req->r_got_unsafe) flags |= CEPH_MDS_FLAG_REPLAY; @@ -1664,7 +1665,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) } /* get request, session */ - tid = le64_to_cpu(head->tid); + tid = le64_to_cpu(msg->hdr.tid); mutex_lock(&mdsc->mutex); req = __lookup_request(mdsc, tid); if (!req) { diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 775a9c0..bb94006 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -349,7 +349,7 @@ static void handle_statfs_reply(struct ceph_mon_client *monc, if (msg->front.iov_len != sizeof(*reply)) goto bad; - tid = le64_to_cpu(reply->tid); + tid = le64_to_cpu(msg->hdr.tid); dout("handle_statfs_reply %p tid %llu\n", msg, tid); mutex_lock(&monc->mutex); @@ -382,12 +382,12 @@ static int send_statfs(struct ceph_mon_client *monc, if (IS_ERR(msg)) return PTR_ERR(msg); req->request = msg; + msg->hdr.tid = cpu_to_le64(req->tid); h = msg->front.iov_base; h->monhdr.have_version = 0; h->monhdr.session_mon = cpu_to_le16(-1); h->monhdr.session_mon_tid = 0; h->fsid = monc->monmap->fsid; - h->tid = cpu_to_le64(req->tid); ceph_con_send(monc->con, msg); return 0; } diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h index c758e8f..e46d8b8 100644 --- a/fs/ceph/msgr.h +++ b/fs/ceph/msgr.h @@ -21,7 +21,7 @@ * whenever the wire protocol changes. try to keep this string length * constant. */ -#define CEPH_BANNER "ceph v024" +#define CEPH_BANNER "ceph v025" #define CEPH_BANNER_MAX_LEN 30 @@ -132,6 +132,7 @@ struct ceph_msg_connect_reply { */ struct ceph_msg_header { __le64 seq; /* message seq# for this session */ + __le64 tid; /* transaction id */ __le16 type; /* message type */ __le16 priority; /* priority. higher value == higher priority */ __le16 version; /* version of message encoding */ diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 374f001..a0aac43 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -439,11 +439,9 @@ static struct ceph_osd *__lookup_osd(struct ceph_osd_client *osdc, int o) static void register_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req) { - struct ceph_osd_request_head *head = req->r_request->front.iov_base; - mutex_lock(&osdc->request_mutex); req->r_tid = ++osdc->last_tid; - head->tid = cpu_to_le64(req->r_tid); + req->r_request->hdr.tid = cpu_to_le64(req->r_tid); dout("register_request %p tid %lld\n", req, req->r_tid); __insert_request(osdc, req); @@ -702,9 +700,9 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, u64 tid; int numops, object_len, flags; + tid = le64_to_cpu(msg->hdr.tid); if (msg->front.iov_len < sizeof(*rhead)) goto bad; - tid = le64_to_cpu(rhead->tid); numops = le32_to_cpu(rhead->num_ops); object_len = le32_to_cpu(rhead->object_len); if (msg->front.iov_len != sizeof(*rhead) + object_len + @@ -1002,7 +1000,6 @@ static int prepare_pages(struct ceph_connection *con, struct ceph_msg *m, { struct ceph_osd *osd = con->private; struct ceph_osd_client *osdc; - struct ceph_osd_reply_head *rhead = m->front.iov_base; struct ceph_osd_request *req; u64 tid; int ret = -1; @@ -1016,7 +1013,7 @@ static int prepare_pages(struct ceph_connection *con, struct ceph_msg *m, if (unlikely(type != CEPH_MSG_OSD_OPREPLY)) return -1; /* hmm! */ - tid = le64_to_cpu(rhead->tid); + tid = le64_to_cpu(m->hdr.tid); mutex_lock(&osdc->request_mutex); req = __lookup_request(osdc, tid); if (!req) { diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h index 12bfb2f..c5614d4 100644 --- a/fs/ceph/rados.h +++ b/fs/ceph/rados.h @@ -331,7 +331,6 @@ struct ceph_osd_op { * ceph_osd_op object operations. */ struct ceph_osd_request_head { - __le64 tid; /* transaction id */ __le32 client_inc; /* client incarnation */ struct ceph_object_layout layout; /* pgid */ __le32 osdmap_epoch; /* client's osdmap epoch */ @@ -352,7 +351,6 @@ struct ceph_osd_request_head { } __attribute__ ((packed)); struct ceph_osd_reply_head { - __le64 tid; /* transaction id */ __le32 client_inc; /* client incarnation */ __le32 flags; struct ceph_object_layout layout; -- cgit v0.10.2 From 04a419f908b5291ff7e8ffd7aa351fa0ac0c08af Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 23 Dec 2009 09:30:21 -0800 Subject: ceph: add feature bits to connection handshake (protocol change) Define supported and required feature set. Fail connection if the server requires features we do not support (TAG_FEATURES), or if the server does not support features we require. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index e87dfa6..db3fed3 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -50,6 +50,12 @@ #define CEPH_MAX_MON 31 +/* + * feature bits + */ +#define CEPH_FEATURE_SUPPORTED 0 +#define CEPH_FEATURE_REQUIRED 0 + /* * ceph_file_layout - describe data layout for a file/inode diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index fde839c..5107384 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1135,9 +1135,9 @@ void ceph_dentry_lru_add(struct dentry *dn) { struct ceph_dentry_info *di = ceph_dentry(dn); struct ceph_mds_client *mdsc; - dout("dentry_lru_add %p %p\t%.*s\n", - di, dn, dn->d_name.len, dn->d_name.name); + dout("dentry_lru_add %p %p '%.*s'\n", di, dn, + dn->d_name.len, dn->d_name.name); if (di) { mdsc = &ceph_client(dn->d_sb)->mdsc; spin_lock(&mdsc->dentry_lru_lock); @@ -1151,9 +1151,9 @@ void ceph_dentry_lru_touch(struct dentry *dn) { struct ceph_dentry_info *di = ceph_dentry(dn); struct ceph_mds_client *mdsc; - dout("dentry_lru_touch %p %p\t%.*s\n", - di, dn, dn->d_name.len, dn->d_name.name); + dout("dentry_lru_touch %p %p '%.*s'\n", di, dn, + dn->d_name.len, dn->d_name.name); if (di) { mdsc = &ceph_client(dn->d_sb)->mdsc; spin_lock(&mdsc->dentry_lru_lock); @@ -1167,8 +1167,8 @@ void ceph_dentry_lru_del(struct dentry *dn) struct ceph_dentry_info *di = ceph_dentry(dn); struct ceph_mds_client *mdsc; - dout("dentry_lru_del %p %p\t%.*s\n", - di, dn, dn->d_name.len, dn->d_name.name); + dout("dentry_lru_del %p %p '%.*s'\n", di, dn, + dn->d_name.len, dn->d_name.name); if (di) { mdsc = &ceph_client(dn->d_sb)->mdsc; spin_lock(&mdsc->dentry_lru_lock); diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 506b638..68052f6 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -631,6 +631,7 @@ static void prepare_write_connect(struct ceph_messenger *msgr, dout("prepare_write_connect %p cseq=%d gseq=%d proto=%d\n", con, con->connect_seq, global_seq, proto); + con->out_connect.features = CEPH_FEATURE_SUPPORTED; con->out_connect.host_type = cpu_to_le32(CEPH_ENTITY_TYPE_CLIENT); con->out_connect.connect_seq = cpu_to_le32(con->connect_seq); con->out_connect.global_seq = cpu_to_le32(global_seq); @@ -1080,15 +1081,37 @@ static int process_banner(struct ceph_connection *con) return 0; } +static void fail_protocol(struct ceph_connection *con) +{ + reset_connection(con); + set_bit(CLOSED, &con->state); /* in case there's queued work */ + + mutex_unlock(&con->mutex); + if (con->ops->bad_proto) + con->ops->bad_proto(con); + mutex_lock(&con->mutex); +} + static int process_connect(struct ceph_connection *con) { + u64 sup_feat = CEPH_FEATURE_SUPPORTED; + u64 req_feat = CEPH_FEATURE_REQUIRED; + u64 server_feat = le64_to_cpu(con->in_reply.features); + dout("process_connect on %p tag %d\n", con, (int)con->in_tag); switch (con->in_reply.tag) { + case CEPH_MSGR_TAG_FEATURES: + pr_err("%s%lld %s feature set mismatch," + " my %llx < server's %llx, missing %llx\n", + ENTITY_NAME(con->peer_name), + pr_addr(&con->peer_addr.in_addr), + sup_feat, server_feat, server_feat & ~sup_feat); + con->error_msg = "missing required protocol features"; + fail_protocol(con); + return -1; + case CEPH_MSGR_TAG_BADPROTOVER: - dout("process_connect got BADPROTOVER my %d != their %d\n", - le32_to_cpu(con->out_connect.protocol_version), - le32_to_cpu(con->in_reply.protocol_version)); pr_err("%s%lld %s protocol version mismatch," " my %d != server's %d\n", ENTITY_NAME(con->peer_name), @@ -1096,13 +1119,7 @@ static int process_connect(struct ceph_connection *con) le32_to_cpu(con->out_connect.protocol_version), le32_to_cpu(con->in_reply.protocol_version)); con->error_msg = "protocol version mismatch"; - reset_connection(con); - set_bit(CLOSED, &con->state); /* in case there's queued work */ - - mutex_unlock(&con->mutex); - if (con->ops->bad_proto) - con->ops->bad_proto(con); - mutex_lock(&con->mutex); + fail_protocol(con); return -1; case CEPH_MSGR_TAG_BADAUTHORIZER: @@ -1173,6 +1190,16 @@ static int process_connect(struct ceph_connection *con) break; case CEPH_MSGR_TAG_READY: + if (req_feat & ~server_feat) { + pr_err("%s%lld %s protocol feature mismatch," + " my required %llx > server's %llx, need %llx\n", + ENTITY_NAME(con->peer_name), + pr_addr(&con->peer_addr.in_addr), + req_feat, server_feat, req_feat & ~server_feat); + con->error_msg = "missing required protocol features"; + fail_protocol(con); + return -1; + } clear_bit(CONNECTING, &con->state); con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq); con->connect_seq++; diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h index e46d8b8..be83f93 100644 --- a/fs/ceph/msgr.h +++ b/fs/ceph/msgr.h @@ -21,7 +21,7 @@ * whenever the wire protocol changes. try to keep this string length * constant. */ -#define CEPH_BANNER "ceph v025" +#define CEPH_BANNER "ceph v026" #define CEPH_BANNER_MAX_LEN 30 @@ -100,12 +100,14 @@ struct ceph_entity_inst { #define CEPH_MSGR_TAG_KEEPALIVE 9 /* just a keepalive byte! */ #define CEPH_MSGR_TAG_BADPROTOVER 10 /* bad protocol version */ #define CEPH_MSGR_TAG_BADAUTHORIZER 11 /* bad authorizer */ +#define CEPH_MSGR_TAG_FEATURES 12 /* insufficient features */ /* * connection negotiation */ struct ceph_msg_connect { + __le64 features; /* supported feature bits */ __le32 host_type; /* CEPH_ENTITY_TYPE_* */ __le32 global_seq; /* count connections initiated by this host */ __le32 connect_seq; /* count connections initiated in this session */ @@ -117,6 +119,7 @@ struct ceph_msg_connect { struct ceph_msg_connect_reply { __u8 tag; + __le64 features; /* feature bits for this session */ __le32 global_seq; __le32 connect_seq; __le32 protocol_version; -- cgit v0.10.2 From 58bb3b374b07a2a43315213f00a48a5ffd6d0915 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 23 Dec 2009 12:12:31 -0800 Subject: ceph: support ceph_pagelist for message payload The ceph_pagelist is a simple list of whole pages, strung together via their lru list_head. It facilitates encoding to a "buffer" of unknown size. Allow its use in place of the ceph_msg page vector. This will be used to fix the huge buffer preallocation woes of MDS reconnection. Signed-off-by: Sage Weil diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile index 827629c8..47caf2f 100644 --- a/fs/ceph/Makefile +++ b/fs/ceph/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_CEPH_FS) += ceph.o ceph-objs := super.o inode.o dir.o file.o addr.o ioctl.o \ export.o caps.o snap.o xattr.o \ - messenger.o msgpool.o buffer.o \ + messenger.o msgpool.o buffer.o pagelist.o \ mds_client.o mdsmap.o \ mon_client.o \ osd_client.o osdmap.o crush/crush.o crush/mapper.o crush/hash.o \ diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 68052f6..c1106e8 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -13,6 +13,7 @@ #include "super.h" #include "messenger.h" #include "decode.h" +#include "pagelist.h" /* * Ceph uses the messenger to exchange ceph_msg messages with other @@ -728,6 +729,11 @@ static int write_partial_msg_pages(struct ceph_connection *con) page = msg->pages[con->out_msg_pos.page]; if (crc) kaddr = kmap(page); + } else if (msg->pagelist) { + page = list_first_entry(&msg->pagelist->head, + struct page, lru); + if (crc) + kaddr = kmap(page); } else { page = con->msgr->zero_page; if (crc) @@ -750,7 +756,7 @@ static int write_partial_msg_pages(struct ceph_connection *con) MSG_DONTWAIT | MSG_NOSIGNAL | MSG_MORE); - if (crc && msg->pages) + if (crc && (msg->pages || msg->pagelist)) kunmap(page); if (ret <= 0) @@ -762,6 +768,9 @@ static int write_partial_msg_pages(struct ceph_connection *con) con->out_msg_pos.page_pos = 0; con->out_msg_pos.page++; con->out_msg_pos.did_page_crc = 0; + if (msg->pagelist) + list_move_tail(&page->lru, + &msg->pagelist->head); } } @@ -1051,13 +1060,13 @@ static int process_banner(struct ceph_connection *con) &con->actual_peer_addr) && !(addr_is_blank(&con->actual_peer_addr.in_addr) && con->actual_peer_addr.nonce == con->peer_addr.nonce)) { - pr_err("wrong peer, want %s/%d, " - "got %s/%d, wtf\n", + pr_warning("wrong peer, want %s/%d, " + "got %s/%d\n", pr_addr(&con->peer_addr.in_addr), con->peer_addr.nonce, pr_addr(&con->actual_peer_addr.in_addr), con->actual_peer_addr.nonce); - con->error_msg = "protocol error, wrong peer"; + con->error_msg = "wrong peer at address"; return -1; } @@ -2096,6 +2105,7 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, /* data */ m->nr_pages = calc_pages_for(page_off, page_len); m->pages = pages; + m->pagelist = NULL; dout("ceph_msg_new %p page %d~%d -> %d\n", m, page_off, page_len, m->nr_pages); @@ -2181,6 +2191,12 @@ void ceph_msg_last_put(struct kref *kref) m->nr_pages = 0; m->pages = NULL; + if (m->pagelist) { + ceph_pagelist_release(m->pagelist); + kfree(m->pagelist); + m->pagelist = NULL; + } + if (m->pool) ceph_msgpool_put(m->pool, m); else diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index 7e2aab1..a7b6841 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -85,6 +85,7 @@ struct ceph_msg { struct ceph_buffer *middle; struct page **pages; /* data payload. NOT OWNER. */ unsigned nr_pages; /* size of page array */ + struct ceph_pagelist *pagelist; /* instead of pages */ struct list_head list_head; struct kref kref; bool front_is_vmalloc; diff --git a/fs/ceph/pagelist.c b/fs/ceph/pagelist.c new file mode 100644 index 0000000..370e936 --- /dev/null +++ b/fs/ceph/pagelist.c @@ -0,0 +1,54 @@ + +#include +#include + +#include "pagelist.h" + +int ceph_pagelist_release(struct ceph_pagelist *pl) +{ + if (pl->mapped_tail) + kunmap(pl->mapped_tail); + while (!list_empty(&pl->head)) { + struct page *page = list_first_entry(&pl->head, struct page, + lru); + list_del(&page->lru); + __free_page(page); + } + return 0; +} + +static int ceph_pagelist_addpage(struct ceph_pagelist *pl) +{ + struct page *page = alloc_page(GFP_NOFS); + if (!page) + return -ENOMEM; + pl->room += PAGE_SIZE; + list_add_tail(&page->lru, &pl->head); + if (pl->mapped_tail) + kunmap(pl->mapped_tail); + pl->mapped_tail = kmap(page); + return 0; +} + +int ceph_pagelist_append(struct ceph_pagelist *pl, void *buf, size_t len) +{ + while (pl->room < len) { + size_t bit = pl->room; + int ret; + + memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK), + buf, bit); + pl->length += bit; + pl->room -= bit; + buf += bit; + len -= bit; + ret = ceph_pagelist_addpage(pl); + if (ret) + return ret; + } + + memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK), buf, len); + pl->length += len; + pl->room -= len; + return 0; +} diff --git a/fs/ceph/pagelist.h b/fs/ceph/pagelist.h new file mode 100644 index 0000000..e8a4187 --- /dev/null +++ b/fs/ceph/pagelist.h @@ -0,0 +1,54 @@ +#ifndef __FS_CEPH_PAGELIST_H +#define __FS_CEPH_PAGELIST_H + +#include + +struct ceph_pagelist { + struct list_head head; + void *mapped_tail; + size_t length; + size_t room; +}; + +static inline void ceph_pagelist_init(struct ceph_pagelist *pl) +{ + INIT_LIST_HEAD(&pl->head); + pl->mapped_tail = NULL; + pl->length = 0; + pl->room = 0; +} +extern int ceph_pagelist_release(struct ceph_pagelist *pl); + +extern int ceph_pagelist_append(struct ceph_pagelist *pl, void *d, size_t l); + +static inline int ceph_pagelist_encode_64(struct ceph_pagelist *pl, u64 v) +{ + __le64 ev = cpu_to_le64(v); + return ceph_pagelist_append(pl, &ev, sizeof(ev)); +} +static inline int ceph_pagelist_encode_32(struct ceph_pagelist *pl, u32 v) +{ + __le32 ev = cpu_to_le32(v); + return ceph_pagelist_append(pl, &ev, sizeof(ev)); +} +static inline int ceph_pagelist_encode_16(struct ceph_pagelist *pl, u16 v) +{ + __le16 ev = cpu_to_le16(v); + return ceph_pagelist_append(pl, &ev, sizeof(ev)); +} +static inline int ceph_pagelist_encode_8(struct ceph_pagelist *pl, u8 v) +{ + return ceph_pagelist_append(pl, &v, 1); +} +static inline int ceph_pagelist_encode_string(struct ceph_pagelist *pl, + char *s, size_t len) +{ + int ret = ceph_pagelist_encode_32(pl, len); + if (ret) + return ret; + if (len) + return ceph_pagelist_append(pl, s, len); + return 0; +} + +#endif -- cgit v0.10.2 From 93cea5bebf91319095db866163a7e35c3e77d8f2 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 23 Dec 2009 12:21:51 -0800 Subject: ceph: use ceph_pagelist for mds reconnect message; change encoding (protocol change) Use the ceph_pagelist to encode the MDS reconnect message. We change the message encoding (protocol change!) at the same time to make our life easier (we don't know how many snaprealms we have when we start encoding). An empty message implies the session is closed/does not exist. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index db3fed3..d0f2557 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -39,7 +39,7 @@ #define CEPH_MDS_PROTOCOL 9 /* cluster internal */ #define CEPH_MON_PROTOCOL 5 /* cluster internal */ #define CEPH_OSDC_PROTOCOL 22 /* server/client */ -#define CEPH_MDSC_PROTOCOL 30 /* server/client */ +#define CEPH_MDSC_PROTOCOL 31 /* server/client */ #define CEPH_MONC_PROTOCOL 15 /* server/client */ diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ec884e2..6e08f48 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -9,6 +9,7 @@ #include "messenger.h" #include "decode.h" #include "auth.h" +#include "pagelist.h" /* * A cluster of MDS (metadata server) daemons is responsible for @@ -1971,20 +1972,12 @@ static void replay_unsafe_requests(struct ceph_mds_client *mdsc, /* * Encode information about a cap for a reconnect with the MDS. */ -struct encode_caps_data { - void **pp; - void *end; - int *num_caps; -}; - static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg) { - struct ceph_mds_cap_reconnect *rec; + struct ceph_mds_cap_reconnect rec; struct ceph_inode_info *ci; - struct encode_caps_data *data = (struct encode_caps_data *)arg; - void *p = *(data->pp); - void *end = data->end; + struct ceph_pagelist *pagelist = arg; char *path; int pathlen, err; u64 pathbase; @@ -1995,8 +1988,9 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, dout(" adding %p ino %llx.%llx cap %p %lld %s\n", inode, ceph_vinop(inode), cap, cap->cap_id, ceph_cap_string(cap->issued)); - ceph_decode_need(&p, end, sizeof(u64), needmore); - ceph_encode_64(&p, ceph_ino(inode)); + err = ceph_pagelist_encode_64(pagelist, ceph_ino(inode)); + if (err) + return err; dentry = d_find_alias(inode); if (dentry) { @@ -2009,33 +2003,29 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, path = NULL; pathlen = 0; } - ceph_decode_need(&p, end, pathlen+4, needmore); - ceph_encode_string(&p, end, path, pathlen); + err = ceph_pagelist_encode_string(pagelist, path, pathlen); + if (err) + goto out; - ceph_decode_need(&p, end, sizeof(*rec), needmore); - rec = p; - p += sizeof(*rec); - BUG_ON(p > end); spin_lock(&inode->i_lock); cap->seq = 0; /* reset cap seq */ cap->issue_seq = 0; /* and issue_seq */ - rec->cap_id = cpu_to_le64(cap->cap_id); - rec->pathbase = cpu_to_le64(pathbase); - rec->wanted = cpu_to_le32(__ceph_caps_wanted(ci)); - rec->issued = cpu_to_le32(cap->issued); - rec->size = cpu_to_le64(inode->i_size); - ceph_encode_timespec(&rec->mtime, &inode->i_mtime); - ceph_encode_timespec(&rec->atime, &inode->i_atime); - rec->snaprealm = cpu_to_le64(ci->i_snap_realm->ino); + rec.cap_id = cpu_to_le64(cap->cap_id); + rec.pathbase = cpu_to_le64(pathbase); + rec.wanted = cpu_to_le32(__ceph_caps_wanted(ci)); + rec.issued = cpu_to_le32(cap->issued); + rec.size = cpu_to_le64(inode->i_size); + ceph_encode_timespec(&rec.mtime, &inode->i_mtime); + ceph_encode_timespec(&rec.atime, &inode->i_atime); + rec.snaprealm = cpu_to_le64(ci->i_snap_realm->ino); spin_unlock(&inode->i_lock); + err = ceph_pagelist_append(pagelist, &rec, sizeof(rec)); + +out: kfree(path); dput(dentry); - (*data->num_caps)++; - *(data->pp) = p; - return 0; -needmore: - return -ENOSPC; + return err; } @@ -2053,19 +2043,26 @@ needmore: */ static void send_mds_reconnect(struct ceph_mds_client *mdsc, int mds) { - struct ceph_mds_session *session; + struct ceph_mds_session *session = NULL; struct ceph_msg *reply; - int newlen, len = 4 + 1; - void *p, *end; int err; - int num_caps, num_realms = 0; int got; u64 next_snap_ino = 0; - __le32 *pnum_caps, *pnum_realms; - struct encode_caps_data iter_args; + struct ceph_pagelist *pagelist; pr_info("reconnect to recovering mds%d\n", mds); + pagelist = kmalloc(sizeof(*pagelist), GFP_NOFS); + if (!pagelist) + goto fail_nopagelist; + ceph_pagelist_init(pagelist); + + reply = ceph_msg_new(CEPH_MSG_CLIENT_RECONNECT, 0, 0, 0, NULL); + if (IS_ERR(reply)) { + err = PTR_ERR(reply); + goto fail_nomsg; + } + /* find session */ session = __ceph_lookup_mds_session(mdsc, mds); mutex_unlock(&mdsc->mutex); /* drop lock for duration */ @@ -2081,12 +2078,6 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, int mds) /* replay unsafe requests */ replay_unsafe_requests(mdsc, session); - - /* estimate needed space */ - len += session->s_nr_caps * - (100+sizeof(struct ceph_mds_cap_reconnect)); - pr_info("estimating i need %d bytes for %d caps\n", - len, session->s_nr_caps); } else { dout("no session for mds%d, will send short reconnect\n", mds); @@ -2094,41 +2085,18 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, int mds) down_read(&mdsc->snap_rwsem); -retry: - /* build reply */ - reply = ceph_msg_new(CEPH_MSG_CLIENT_RECONNECT, len, 0, 0, NULL); - if (IS_ERR(reply)) { - err = PTR_ERR(reply); - pr_err("send_mds_reconnect ENOMEM on %d for mds%d\n", - len, mds); - goto out; - } - p = reply->front.iov_base; - end = p + len; - - if (!session) { - ceph_encode_8(&p, 1); /* session was closed */ - ceph_encode_32(&p, 0); + if (!session) goto send; - } dout("session %p state %s\n", session, session_state_name(session->s_state)); /* traverse this session's caps */ - ceph_encode_8(&p, 0); - pnum_caps = p; - ceph_encode_32(&p, session->s_nr_caps); - num_caps = 0; - - iter_args.pp = &p; - iter_args.end = end; - iter_args.num_caps = &num_caps; - err = iterate_session_caps(session, encode_caps_cb, &iter_args); - if (err == -ENOSPC) - goto needmore; + err = ceph_pagelist_encode_32(pagelist, session->s_nr_caps); + if (err) + goto fail; + err = iterate_session_caps(session, encode_caps_cb, pagelist); if (err < 0) goto out; - *pnum_caps = cpu_to_le32(num_caps); /* * snaprealms. we provide mds with the ino, seq (version), and @@ -2136,14 +2104,9 @@ retry: * it will tell us. */ next_snap_ino = 0; - /* save some space for the snaprealm count */ - pnum_realms = p; - ceph_decode_need(&p, end, sizeof(*pnum_realms), needmore); - p += sizeof(*pnum_realms); - num_realms = 0; while (1) { struct ceph_snap_realm *realm; - struct ceph_mds_snaprealm_reconnect *sr_rec; + struct ceph_mds_snaprealm_reconnect sr_rec; got = radix_tree_gang_lookup(&mdsc->snap_realms, (void **)&realm, next_snap_ino, 1); if (!got) @@ -2151,22 +2114,19 @@ retry: dout(" adding snap realm %llx seq %lld parent %llx\n", realm->ino, realm->seq, realm->parent_ino); - ceph_decode_need(&p, end, sizeof(*sr_rec), needmore); - sr_rec = p; - sr_rec->ino = cpu_to_le64(realm->ino); - sr_rec->seq = cpu_to_le64(realm->seq); - sr_rec->parent = cpu_to_le64(realm->parent_ino); - p += sizeof(*sr_rec); - num_realms++; + sr_rec.ino = cpu_to_le64(realm->ino); + sr_rec.seq = cpu_to_le64(realm->seq); + sr_rec.parent = cpu_to_le64(realm->parent_ino); + err = ceph_pagelist_append(pagelist, &sr_rec, sizeof(sr_rec)); + if (err) + goto fail; next_snap_ino = realm->ino + 1; } - *pnum_realms = cpu_to_le32(num_realms); send: - reply->front.iov_len = p - reply->front.iov_base; - reply->hdr.front_len = cpu_to_le32(reply->front.iov_len); - dout("final len was %u (guessed %d)\n", - (unsigned)reply->front.iov_len, len); + reply->pagelist = pagelist; + reply->hdr.data_len = cpu_to_le32(pagelist->length); + reply->nr_pages = calc_pages_for(0, pagelist->length); ceph_con_send(&session->s_con, reply); if (session) { @@ -2183,18 +2143,14 @@ out: mutex_lock(&mdsc->mutex); return; -needmore: - /* - * we need a larger buffer. this doesn't very accurately - * factor in snap realms, but it's safe. - */ - num_caps += num_realms; - newlen = len * ((100 * (session->s_nr_caps+3)) / (num_caps + 1)) / 100; - pr_info("i guessed %d, and did %d of %d caps, retrying with %d\n", - len, num_caps, session->s_nr_caps, newlen); - len = newlen; +fail: ceph_msg_put(reply); - goto retry; +fail_nomsg: + ceph_pagelist_release(pagelist); + kfree(pagelist); +fail_nopagelist: + pr_err("ENOMEM preparing reconnect for mds%d\n", mds); + goto out; } -- cgit v0.10.2 From 9593bd07ec8eaaa30aba4281b2b3273282fc344f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 24 Dec 2009 00:02:16 -0800 Subject: sony-laptop - remove private workqueue, use keventd instead If we reschedule work instead of having work function sleep for 10 msecs between reads from kfifo we can safely use the main workqueue (keventd) and not bother with creating driver-private one. Signed-off-by: Dmitry Torokhov Signed-off-by: Len Brown diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 5af5334..c42d35b 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -145,7 +145,6 @@ struct sony_laptop_input_s { struct input_dev *key_dev; struct kfifo fifo; spinlock_t fifo_lock; - struct workqueue_struct *wq; }; static struct sony_laptop_input_s sony_laptop_input = { @@ -301,18 +300,28 @@ static int sony_laptop_input_keycode_map[] = { /* release buttons after a short delay if pressed */ static void do_sony_laptop_release_key(struct work_struct *work) { + struct delayed_work *dwork = + container_of(work, struct delayed_work, work); struct sony_laptop_keypress kp; + unsigned long flags; + + spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags); - while (kfifo_out_locked(&sony_laptop_input.fifo, (unsigned char *)&kp, - sizeof(kp), &sony_laptop_input.fifo_lock) - == sizeof(kp)) { - msleep(10); + if (kfifo_out(&sony_laptop_input.fifo, + (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { input_report_key(kp.dev, kp.key, 0); input_sync(kp.dev); } + + /* If there is something in the fifo schedule next release. */ + if (kfifo_len(&sony_laptop_input.fifo) != 0) + schedule_delayed_work(dwork, msecs_to_jiffies(10)); + + spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags); } -static DECLARE_WORK(sony_laptop_release_key_work, - do_sony_laptop_release_key); + +static DECLARE_DELAYED_WORK(sony_laptop_release_key_work, + do_sony_laptop_release_key); /* forward event to the input subsystem */ static void sony_laptop_report_input_event(u8 event) @@ -366,13 +375,13 @@ static void sony_laptop_report_input_event(u8 event) /* we emit the scancode so we can always remap the key */ input_event(kp.dev, EV_MSC, MSC_SCAN, event); input_sync(kp.dev); - kfifo_in_locked(&sony_laptop_input.fifo, - (unsigned char *)&kp, sizeof(kp), - &sony_laptop_input.fifo_lock); - if (!work_pending(&sony_laptop_release_key_work)) - queue_work(sony_laptop_input.wq, - &sony_laptop_release_key_work); + /* schedule key release */ + kfifo_in_locked(&sony_laptop_input.fifo, + (unsigned char *)&kp, sizeof(kp), + &sony_laptop_input.fifo_lock); + schedule_delayed_work(&sony_laptop_release_key_work, + msecs_to_jiffies(10)); } else dprintk("unknown input event %.2x\n", event); } @@ -390,27 +399,18 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) /* kfifo */ spin_lock_init(&sony_laptop_input.fifo_lock); - error = - kfifo_alloc(&sony_laptop_input.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); + error = kfifo_alloc(&sony_laptop_input.fifo, + SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); if (error) { printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); goto err_dec_users; } - /* init workqueue */ - sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); - if (!sony_laptop_input.wq) { - printk(KERN_ERR DRV_PFX - "Unable to create workqueue.\n"); - error = -ENXIO; - goto err_free_kfifo; - } - /* input keys */ key_dev = input_allocate_device(); if (!key_dev) { error = -ENOMEM; - goto err_destroy_wq; + goto err_free_kfifo; } key_dev->name = "Sony Vaio Keys"; @@ -473,9 +473,6 @@ err_unregister_keydev: err_free_keydev: input_free_device(key_dev); -err_destroy_wq: - destroy_workqueue(sony_laptop_input.wq); - err_free_kfifo: kfifo_free(&sony_laptop_input.fifo); @@ -486,12 +483,23 @@ err_dec_users: static void sony_laptop_remove_input(void) { - /* cleanup only after the last user has gone */ + struct sony_laptop_keypress kp = { NULL }; + + /* Cleanup only after the last user has gone */ if (!atomic_dec_and_test(&sony_laptop_input.users)) return; - /* flush workqueue first */ - flush_workqueue(sony_laptop_input.wq); + cancel_delayed_work_sync(&sony_laptop_release_key_work); + + /* + * Generate key-up events for remaining keys. Note that we don't + * need locking since nobody is adding new events to the kfifo. + */ + while (kfifo_out(&sony_laptop_input.fifo, + (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) { + input_report_key(kp.dev, kp.key, 0); + input_sync(kp.dev); + } /* destroy input devs */ input_unregister_device(sony_laptop_input.key_dev); @@ -502,7 +510,6 @@ static void sony_laptop_remove_input(void) sony_laptop_input.jog_dev = NULL; } - destroy_workqueue(sony_laptop_input.wq); kfifo_free(&sony_laptop_input.fifo); } -- cgit v0.10.2 From c45bc9d62c39202b401d1bf7bb2812abb88798a1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 24 Dec 2009 00:02:23 -0800 Subject: sony-laptop - simplify keymap initialization Also use input_set_capability() helper instead of manipulating bits directly. Signed-off-by: Dmitry Torokhov Signed-off-by: Len Brown diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index c42d35b..b7aa959 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -419,18 +419,15 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) key_dev->dev.parent = &acpi_device->dev; /* Initialize the Input Drivers: special keys */ - set_bit(EV_KEY, key_dev->evbit); - set_bit(EV_MSC, key_dev->evbit); - set_bit(MSC_SCAN, key_dev->mscbit); + input_set_capability(key_dev, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, key_dev->evbit); key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]); key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map); key_dev->keycode = &sony_laptop_input_keycode_map; - for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) { - if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) { - set_bit(sony_laptop_input_keycode_map[i], - key_dev->keybit); - } - } + for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) + __set_bit(sony_laptop_input_keycode_map[i], key_dev->keybit); + __clear_bit(KEY_RESERVED, key_dev->keybit); error = input_register_device(key_dev); if (error) @@ -450,9 +447,8 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) jog_dev->id.vendor = PCI_VENDOR_ID_SONY; key_dev->dev.parent = &acpi_device->dev; - jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE); - jog_dev->relbit[0] = BIT_MASK(REL_WHEEL); + input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); + input_set_capability(jog_dev, EV_REL, REL_WHEEL); error = input_register_device(jog_dev); if (error) -- cgit v0.10.2 From cffdde993a016bedbc2f5eb60d00c3a766ffb612 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 24 Dec 2009 00:02:30 -0800 Subject: sony-laptop - switch from workqueue to a timer The function that is executing in workqueue context does not need to sleep so let's switch to a timer which is more lightweight. Signed-off-by: Dmitry Torokhov Signed-off-by: Len Brown diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index b7aa959..cc7172e 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -145,6 +145,7 @@ struct sony_laptop_input_s { struct input_dev *key_dev; struct kfifo fifo; spinlock_t fifo_lock; + struct timer_list release_key_timer; }; static struct sony_laptop_input_s sony_laptop_input = { @@ -298,10 +299,8 @@ static int sony_laptop_input_keycode_map[] = { }; /* release buttons after a short delay if pressed */ -static void do_sony_laptop_release_key(struct work_struct *work) +static void do_sony_laptop_release_key(unsigned long unused) { - struct delayed_work *dwork = - container_of(work, struct delayed_work, work); struct sony_laptop_keypress kp; unsigned long flags; @@ -315,14 +314,12 @@ static void do_sony_laptop_release_key(struct work_struct *work) /* If there is something in the fifo schedule next release. */ if (kfifo_len(&sony_laptop_input.fifo) != 0) - schedule_delayed_work(dwork, msecs_to_jiffies(10)); + mod_timer(&sony_laptop_input.release_key_timer, + jiffies + msecs_to_jiffies(10)); spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags); } -static DECLARE_DELAYED_WORK(sony_laptop_release_key_work, - do_sony_laptop_release_key); - /* forward event to the input subsystem */ static void sony_laptop_report_input_event(u8 event) { @@ -380,8 +377,8 @@ static void sony_laptop_report_input_event(u8 event) kfifo_in_locked(&sony_laptop_input.fifo, (unsigned char *)&kp, sizeof(kp), &sony_laptop_input.fifo_lock); - schedule_delayed_work(&sony_laptop_release_key_work, - msecs_to_jiffies(10)); + mod_timer(&sony_laptop_input.release_key_timer, + jiffies + msecs_to_jiffies(10)); } else dprintk("unknown input event %.2x\n", event); } @@ -406,6 +403,9 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) goto err_dec_users; } + setup_timer(&sony_laptop_input.release_key_timer, + do_sony_laptop_release_key, 0); + /* input keys */ key_dev = input_allocate_device(); if (!key_dev) { @@ -485,7 +485,7 @@ static void sony_laptop_remove_input(void) if (!atomic_dec_and_test(&sony_laptop_input.users)) return; - cancel_delayed_work_sync(&sony_laptop_release_key_work); + del_timer_sync(&sony_laptop_input.release_key_timer); /* * Generate key-up events for remaining keys. Note that we don't -- cgit v0.10.2 From 0c99c5288eb9b1bbc9684b0ec0fd7efc578749b3 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 17 Dec 2009 16:02:08 +0800 Subject: ACPI: Disable explicit power state retrieval on fans If the ACPI power state can be got both directly and indirectly, we prefer to get it indirectly. https://bugzilla.redhat.com/show_bug.cgi?id=531916 describes a system with a _PSC method for the fan that always returns "on". There's no benefit in us always requesting the state of the fan when performing transitions - we want to do everything we can to ensure that the fan turns on when it should do, not risk hardware damage by believing the hardware when it tells us the fan is already on. Given that the Leading Other OS(tm) works fine on this machine, it seems likely that it behaves in much this way. inspired-by: Matthew Garrett Signed-off-by: Zhang Rui Signed-off-by: Len Brown diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index cf761b9..ae9226d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -190,16 +190,16 @@ int acpi_bus_get_power(acpi_handle handle, int *state) * Get the device's power state either directly (via _PSC) or * indirectly (via power resources). */ - if (device->power.flags.explicit_get) { + if (device->power.flags.power_resources) { + result = acpi_power_get_inferred_state(device); + if (result) + return result; + } else if (device->power.flags.explicit_get) { status = acpi_evaluate_integer(device->handle, "_PSC", NULL, &psc); if (ACPI_FAILURE(status)) return -ENODEV; device->power.state = (int)psc; - } else if (device->power.flags.power_resources) { - result = acpi_power_get_inferred_state(device); - if (result) - return result; } *state = device->power.state; -- cgit v0.10.2 From 5113f81520278ba17c64031fa01aeafc9a83f9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 11 Dec 2009 10:07:15 +0100 Subject: imx/mx2: fold crm_regs.h into its only consumer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Holger Schurig Cc: Rabin Vincent diff --git a/arch/arm/mach-mx2/clock_imx21.c b/arch/arm/mach-mx2/clock_imx21.c index 91901b5..df3ad38 100644 --- a/arch/arm/mach-mx2/clock_imx21.c +++ b/arch/arm/mach-mx2/clock_imx21.c @@ -23,11 +23,240 @@ #include #include +#include #include #include #include -#include "crm_regs.h" +/* Register offsets */ +#define CCM_CSCR (IO_ADDRESS(CCM_BASE_ADDR) + 0x0) +#define CCM_MPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x4) +#define CCM_MPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x8) +#define CCM_SPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0xC) +#define CCM_SPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x10) +#define CCM_OSC26MCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x14) +#define CCM_PCDR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x18) +#define CCM_PCDR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x1c) +#define CCM_PCCR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x20) +#define CCM_PCCR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x24) +#define CCM_CCSR (IO_ADDRESS(CCM_BASE_ADDR) + 0x28) +#define CCM_PMCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x2c) +#define CCM_PMCOUNT (IO_ADDRESS(CCM_BASE_ADDR) + 0x30) +#define CCM_WKGDCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x34) + +#define CCM_CSCR_PRESC_OFFSET 29 +#define CCM_CSCR_PRESC_MASK (0x7 << CCM_CSCR_PRESC_OFFSET) + +#define CCM_CSCR_USB_OFFSET 26 +#define CCM_CSCR_USB_MASK (0x7 << CCM_CSCR_USB_OFFSET) +#define CCM_CSCR_SD_OFFSET 24 +#define CCM_CSCR_SD_MASK (0x3 << CCM_CSCR_SD_OFFSET) +#define CCM_CSCR_SPLLRES (1 << 22) +#define CCM_CSCR_MPLLRES (1 << 21) +#define CCM_CSCR_SSI2_OFFSET 20 +#define CCM_CSCR_SSI2 (1 << CCM_CSCR_SSI2_OFFSET) +#define CCM_CSCR_SSI1_OFFSET 19 +#define CCM_CSCR_SSI1 (1 << CCM_CSCR_SSI1_OFFSET) +#define CCM_CSCR_FIR_OFFSET 18 +#define CCM_CSCR_FIR (1 << CCM_CSCR_FIR_OFFSET) +#define CCM_CSCR_SP (1 << 17) +#define CCM_CSCR_MCU (1 << 16) +#define CCM_CSCR_BCLK_OFFSET 10 +#define CCM_CSCR_BCLK_MASK (0xf << CCM_CSCR_BCLK_OFFSET) +#define CCM_CSCR_IPDIV_OFFSET 9 +#define CCM_CSCR_IPDIV (1 << CCM_CSCR_IPDIV_OFFSET) + +#define CCM_CSCR_OSC26MDIV (1 << 4) +#define CCM_CSCR_OSC26M (1 << 3) +#define CCM_CSCR_FPM (1 << 2) +#define CCM_CSCR_SPEN (1 << 1) +#define CCM_CSCR_MPEN 1 + +#define CCM_MPCTL0_CPLM (1 << 31) +#define CCM_MPCTL0_PD_OFFSET 26 +#define CCM_MPCTL0_PD_MASK (0xf << 26) +#define CCM_MPCTL0_MFD_OFFSET 16 +#define CCM_MPCTL0_MFD_MASK (0x3ff << 16) +#define CCM_MPCTL0_MFI_OFFSET 10 +#define CCM_MPCTL0_MFI_MASK (0xf << 10) +#define CCM_MPCTL0_MFN_OFFSET 0 +#define CCM_MPCTL0_MFN_MASK 0x3ff + +#define CCM_MPCTL1_LF (1 << 15) +#define CCM_MPCTL1_BRMO (1 << 6) + +#define CCM_SPCTL0_CPLM (1 << 31) +#define CCM_SPCTL0_PD_OFFSET 26 +#define CCM_SPCTL0_PD_MASK (0xf << 26) +#define CCM_SPCTL0_MFD_OFFSET 16 +#define CCM_SPCTL0_MFD_MASK (0x3ff << 16) +#define CCM_SPCTL0_MFI_OFFSET 10 +#define CCM_SPCTL0_MFI_MASK (0xf << 10) +#define CCM_SPCTL0_MFN_OFFSET 0 +#define CCM_SPCTL0_MFN_MASK 0x3ff + +#define CCM_SPCTL1_LF (1 << 15) +#define CCM_SPCTL1_BRMO (1 << 6) + +#define CCM_OSC26MCTL_PEAK_OFFSET 16 +#define CCM_OSC26MCTL_PEAK_MASK (0x3 << 16) +#define CCM_OSC26MCTL_AGC_OFFSET 8 +#define CCM_OSC26MCTL_AGC_MASK (0x3f << 8) +#define CCM_OSC26MCTL_ANATEST_OFFSET 0 +#define CCM_OSC26MCTL_ANATEST_MASK 0x3f + +#define CCM_PCDR0_SSI2BAUDDIV_OFFSET 26 +#define CCM_PCDR0_SSI2BAUDDIV_MASK (0x3f << 26) +#define CCM_PCDR0_SSI1BAUDDIV_OFFSET 16 +#define CCM_PCDR0_SSI1BAUDDIV_MASK (0x3f << 16) +#define CCM_PCDR0_NFCDIV_OFFSET 12 +#define CCM_PCDR0_NFCDIV_MASK (0xf << 12) +#define CCM_PCDR0_48MDIV_OFFSET 5 +#define CCM_PCDR0_48MDIV_MASK (0x7 << CCM_PCDR0_48MDIV_OFFSET) +#define CCM_PCDR0_FIRIDIV_OFFSET 0 +#define CCM_PCDR0_FIRIDIV_MASK 0x1f +#define CCM_PCDR1_PERDIV4_OFFSET 24 +#define CCM_PCDR1_PERDIV4_MASK (0x3f << 24) +#define CCM_PCDR1_PERDIV3_OFFSET 16 +#define CCM_PCDR1_PERDIV3_MASK (0x3f << 16) +#define CCM_PCDR1_PERDIV2_OFFSET 8 +#define CCM_PCDR1_PERDIV2_MASK (0x3f << 8) +#define CCM_PCDR1_PERDIV1_OFFSET 0 +#define CCM_PCDR1_PERDIV1_MASK 0x3f + +#define CCM_PCCR_HCLK_CSI_OFFSET 31 +#define CCM_PCCR_HCLK_CSI_REG CCM_PCCR0 +#define CCM_PCCR_HCLK_DMA_OFFSET 30 +#define CCM_PCCR_HCLK_DMA_REG CCM_PCCR0 +#define CCM_PCCR_HCLK_BROM_OFFSET 28 +#define CCM_PCCR_HCLK_BROM_REG CCM_PCCR0 +#define CCM_PCCR_HCLK_EMMA_OFFSET 27 +#define CCM_PCCR_HCLK_EMMA_REG CCM_PCCR0 +#define CCM_PCCR_HCLK_LCDC_OFFSET 26 +#define CCM_PCCR_HCLK_LCDC_REG CCM_PCCR0 +#define CCM_PCCR_HCLK_SLCDC_OFFSET 25 +#define CCM_PCCR_HCLK_SLCDC_REG CCM_PCCR0 +#define CCM_PCCR_HCLK_USBOTG_OFFSET 24 +#define CCM_PCCR_HCLK_USBOTG_REG CCM_PCCR0 +#define CCM_PCCR_HCLK_BMI_OFFSET 23 +#define CCM_PCCR_BMI_MASK (1 << CCM_PCCR_BMI_MASK) +#define CCM_PCCR_HCLK_BMI_REG CCM_PCCR0 +#define CCM_PCCR_PERCLK4_OFFSET 22 +#define CCM_PCCR_PERCLK4_REG CCM_PCCR0 +#define CCM_PCCR_SLCDC_OFFSET 21 +#define CCM_PCCR_SLCDC_REG CCM_PCCR0 +#define CCM_PCCR_FIRI_BAUD_OFFSET 20 +#define CCM_PCCR_FIRI_BAUD_MASK (1 << CCM_PCCR_FIRI_BAUD_MASK) +#define CCM_PCCR_FIRI_BAUD_REG CCM_PCCR0 +#define CCM_PCCR_NFC_OFFSET 19 +#define CCM_PCCR_NFC_REG CCM_PCCR0 +#define CCM_PCCR_LCDC_OFFSET 18 +#define CCM_PCCR_LCDC_REG CCM_PCCR0 +#define CCM_PCCR_SSI1_BAUD_OFFSET 17 +#define CCM_PCCR_SSI1_BAUD_REG CCM_PCCR0 +#define CCM_PCCR_SSI2_BAUD_OFFSET 16 +#define CCM_PCCR_SSI2_BAUD_REG CCM_PCCR0 +#define CCM_PCCR_EMMA_OFFSET 15 +#define CCM_PCCR_EMMA_REG CCM_PCCR0 +#define CCM_PCCR_USBOTG_OFFSET 14 +#define CCM_PCCR_USBOTG_REG CCM_PCCR0 +#define CCM_PCCR_DMA_OFFSET 13 +#define CCM_PCCR_DMA_REG CCM_PCCR0 +#define CCM_PCCR_I2C1_OFFSET 12 +#define CCM_PCCR_I2C1_REG CCM_PCCR0 +#define CCM_PCCR_GPIO_OFFSET 11 +#define CCM_PCCR_GPIO_REG CCM_PCCR0 +#define CCM_PCCR_SDHC2_OFFSET 10 +#define CCM_PCCR_SDHC2_REG CCM_PCCR0 +#define CCM_PCCR_SDHC1_OFFSET 9 +#define CCM_PCCR_SDHC1_REG CCM_PCCR0 +#define CCM_PCCR_FIRI_OFFSET 8 +#define CCM_PCCR_FIRI_MASK (1 << CCM_PCCR_BAUD_MASK) +#define CCM_PCCR_FIRI_REG CCM_PCCR0 +#define CCM_PCCR_SSI2_IPG_OFFSET 7 +#define CCM_PCCR_SSI2_REG CCM_PCCR0 +#define CCM_PCCR_SSI1_IPG_OFFSET 6 +#define CCM_PCCR_SSI1_REG CCM_PCCR0 +#define CCM_PCCR_CSPI2_OFFSET 5 +#define CCM_PCCR_CSPI2_REG CCM_PCCR0 +#define CCM_PCCR_CSPI1_OFFSET 4 +#define CCM_PCCR_CSPI1_REG CCM_PCCR0 +#define CCM_PCCR_UART4_OFFSET 3 +#define CCM_PCCR_UART4_REG CCM_PCCR0 +#define CCM_PCCR_UART3_OFFSET 2 +#define CCM_PCCR_UART3_REG CCM_PCCR0 +#define CCM_PCCR_UART2_OFFSET 1 +#define CCM_PCCR_UART2_REG CCM_PCCR0 +#define CCM_PCCR_UART1_OFFSET 0 +#define CCM_PCCR_UART1_REG CCM_PCCR0 + +#define CCM_PCCR_OWIRE_OFFSET 31 +#define CCM_PCCR_OWIRE_REG CCM_PCCR1 +#define CCM_PCCR_KPP_OFFSET 30 +#define CCM_PCCR_KPP_REG CCM_PCCR1 +#define CCM_PCCR_RTC_OFFSET 29 +#define CCM_PCCR_RTC_REG CCM_PCCR1 +#define CCM_PCCR_PWM_OFFSET 28 +#define CCM_PCCR_PWM_REG CCM_PCCR1 +#define CCM_PCCR_GPT3_OFFSET 27 +#define CCM_PCCR_GPT3_REG CCM_PCCR1 +#define CCM_PCCR_GPT2_OFFSET 26 +#define CCM_PCCR_GPT2_REG CCM_PCCR1 +#define CCM_PCCR_GPT1_OFFSET 25 +#define CCM_PCCR_GPT1_REG CCM_PCCR1 +#define CCM_PCCR_WDT_OFFSET 24 +#define CCM_PCCR_WDT_REG CCM_PCCR1 +#define CCM_PCCR_CSPI3_OFFSET 23 +#define CCM_PCCR_CSPI3_REG CCM_PCCR1 + +#define CCM_PCCR_CSPI1_MASK (1 << CCM_PCCR_CSPI1_OFFSET) +#define CCM_PCCR_CSPI2_MASK (1 << CCM_PCCR_CSPI2_OFFSET) +#define CCM_PCCR_CSPI3_MASK (1 << CCM_PCCR_CSPI3_OFFSET) +#define CCM_PCCR_DMA_MASK (1 << CCM_PCCR_DMA_OFFSET) +#define CCM_PCCR_EMMA_MASK (1 << CCM_PCCR_EMMA_OFFSET) +#define CCM_PCCR_GPIO_MASK (1 << CCM_PCCR_GPIO_OFFSET) +#define CCM_PCCR_GPT1_MASK (1 << CCM_PCCR_GPT1_OFFSET) +#define CCM_PCCR_GPT2_MASK (1 << CCM_PCCR_GPT2_OFFSET) +#define CCM_PCCR_GPT3_MASK (1 << CCM_PCCR_GPT3_OFFSET) +#define CCM_PCCR_HCLK_BROM_MASK (1 << CCM_PCCR_HCLK_BROM_OFFSET) +#define CCM_PCCR_HCLK_CSI_MASK (1 << CCM_PCCR_HCLK_CSI_OFFSET) +#define CCM_PCCR_HCLK_DMA_MASK (1 << CCM_PCCR_HCLK_DMA_OFFSET) +#define CCM_PCCR_HCLK_EMMA_MASK (1 << CCM_PCCR_HCLK_EMMA_OFFSET) +#define CCM_PCCR_HCLK_LCDC_MASK (1 << CCM_PCCR_HCLK_LCDC_OFFSET) +#define CCM_PCCR_HCLK_SLCDC_MASK (1 << CCM_PCCR_HCLK_SLCDC_OFFSET) +#define CCM_PCCR_HCLK_USBOTG_MASK (1 << CCM_PCCR_HCLK_USBOTG_OFFSET) +#define CCM_PCCR_I2C1_MASK (1 << CCM_PCCR_I2C1_OFFSET) +#define CCM_PCCR_KPP_MASK (1 << CCM_PCCR_KPP_OFFSET) +#define CCM_PCCR_LCDC_MASK (1 << CCM_PCCR_LCDC_OFFSET) +#define CCM_PCCR_NFC_MASK (1 << CCM_PCCR_NFC_OFFSET) +#define CCM_PCCR_OWIRE_MASK (1 << CCM_PCCR_OWIRE_OFFSET) +#define CCM_PCCR_PERCLK4_MASK (1 << CCM_PCCR_PERCLK4_OFFSET) +#define CCM_PCCR_PWM_MASK (1 << CCM_PCCR_PWM_OFFSET) +#define CCM_PCCR_RTC_MASK (1 << CCM_PCCR_RTC_OFFSET) +#define CCM_PCCR_SDHC1_MASK (1 << CCM_PCCR_SDHC1_OFFSET) +#define CCM_PCCR_SDHC2_MASK (1 << CCM_PCCR_SDHC2_OFFSET) +#define CCM_PCCR_SLCDC_MASK (1 << CCM_PCCR_SLCDC_OFFSET) +#define CCM_PCCR_SSI1_BAUD_MASK (1 << CCM_PCCR_SSI1_BAUD_OFFSET) +#define CCM_PCCR_SSI1_IPG_MASK (1 << CCM_PCCR_SSI1_IPG_OFFSET) +#define CCM_PCCR_SSI2_BAUD_MASK (1 << CCM_PCCR_SSI2_BAUD_OFFSET) +#define CCM_PCCR_SSI2_IPG_MASK (1 << CCM_PCCR_SSI2_IPG_OFFSET) +#define CCM_PCCR_UART1_MASK (1 << CCM_PCCR_UART1_OFFSET) +#define CCM_PCCR_UART2_MASK (1 << CCM_PCCR_UART2_OFFSET) +#define CCM_PCCR_UART3_MASK (1 << CCM_PCCR_UART3_OFFSET) +#define CCM_PCCR_UART4_MASK (1 << CCM_PCCR_UART4_OFFSET) +#define CCM_PCCR_USBOTG_MASK (1 << CCM_PCCR_USBOTG_OFFSET) +#define CCM_PCCR_WDT_MASK (1 << CCM_PCCR_WDT_OFFSET) + +#define CCM_CCSR_32KSR (1 << 15) + +#define CCM_CCSR_CLKMODE1 (1 << 9) +#define CCM_CCSR_CLKMODE0 (1 << 8) + +#define CCM_CCSR_CLKOSEL_OFFSET 0 +#define CCM_CCSR_CLKOSEL_MASK 0x1f + +#define SYS_FMCR 0x14 /* Functional Muxing Control Reg */ +#define SYS_CHIP_ID 0x00 /* The offset of CHIP ID register */ static int _clk_enable(struct clk *clk) { diff --git a/arch/arm/mach-mx2/crm_regs.h b/arch/arm/mach-mx2/crm_regs.h deleted file mode 100644 index 749de76..0000000 --- a/arch/arm/mach-mx2/crm_regs.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2008 Juergen Beisert, kernel@pengutronix.de - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef __ARCH_ARM_MACH_MX2_CRM_REGS_H__ -#define __ARCH_ARM_MACH_MX2_CRM_REGS_H__ - -#include - -/* Register offsets */ -#define CCM_CSCR (IO_ADDRESS(CCM_BASE_ADDR) + 0x0) -#define CCM_MPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x4) -#define CCM_MPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x8) -#define CCM_SPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0xC) -#define CCM_SPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x10) -#define CCM_OSC26MCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x14) -#define CCM_PCDR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x18) -#define CCM_PCDR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x1c) -#define CCM_PCCR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x20) -#define CCM_PCCR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x24) -#define CCM_CCSR (IO_ADDRESS(CCM_BASE_ADDR) + 0x28) -#define CCM_PMCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x2c) -#define CCM_PMCOUNT (IO_ADDRESS(CCM_BASE_ADDR) + 0x30) -#define CCM_WKGDCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x34) - -#define CCM_CSCR_PRESC_OFFSET 29 -#define CCM_CSCR_PRESC_MASK (0x7 << CCM_CSCR_PRESC_OFFSET) - -#define CCM_CSCR_USB_OFFSET 26 -#define CCM_CSCR_USB_MASK (0x7 << CCM_CSCR_USB_OFFSET) -#define CCM_CSCR_SD_OFFSET 24 -#define CCM_CSCR_SD_MASK (0x3 << CCM_CSCR_SD_OFFSET) -#define CCM_CSCR_SPLLRES (1 << 22) -#define CCM_CSCR_MPLLRES (1 << 21) -#define CCM_CSCR_SSI2_OFFSET 20 -#define CCM_CSCR_SSI2 (1 << CCM_CSCR_SSI2_OFFSET) -#define CCM_CSCR_SSI1_OFFSET 19 -#define CCM_CSCR_SSI1 (1 << CCM_CSCR_SSI1_OFFSET) -#define CCM_CSCR_FIR_OFFSET 18 -#define CCM_CSCR_FIR (1 << CCM_CSCR_FIR_OFFSET) -#define CCM_CSCR_SP (1 << 17) -#define CCM_CSCR_MCU (1 << 16) -#define CCM_CSCR_BCLK_OFFSET 10 -#define CCM_CSCR_BCLK_MASK (0xf << CCM_CSCR_BCLK_OFFSET) -#define CCM_CSCR_IPDIV_OFFSET 9 -#define CCM_CSCR_IPDIV (1 << CCM_CSCR_IPDIV_OFFSET) - -#define CCM_CSCR_OSC26MDIV (1 << 4) -#define CCM_CSCR_OSC26M (1 << 3) -#define CCM_CSCR_FPM (1 << 2) -#define CCM_CSCR_SPEN (1 << 1) -#define CCM_CSCR_MPEN 1 - - - -#define CCM_MPCTL0_CPLM (1 << 31) -#define CCM_MPCTL0_PD_OFFSET 26 -#define CCM_MPCTL0_PD_MASK (0xf << 26) -#define CCM_MPCTL0_MFD_OFFSET 16 -#define CCM_MPCTL0_MFD_MASK (0x3ff << 16) -#define CCM_MPCTL0_MFI_OFFSET 10 -#define CCM_MPCTL0_MFI_MASK (0xf << 10) -#define CCM_MPCTL0_MFN_OFFSET 0 -#define CCM_MPCTL0_MFN_MASK 0x3ff - -#define CCM_MPCTL1_LF (1 << 15) -#define CCM_MPCTL1_BRMO (1 << 6) - -#define CCM_SPCTL0_CPLM (1 << 31) -#define CCM_SPCTL0_PD_OFFSET 26 -#define CCM_SPCTL0_PD_MASK (0xf << 26) -#define CCM_SPCTL0_MFD_OFFSET 16 -#define CCM_SPCTL0_MFD_MASK (0x3ff << 16) -#define CCM_SPCTL0_MFI_OFFSET 10 -#define CCM_SPCTL0_MFI_MASK (0xf << 10) -#define CCM_SPCTL0_MFN_OFFSET 0 -#define CCM_SPCTL0_MFN_MASK 0x3ff - -#define CCM_SPCTL1_LF (1 << 15) -#define CCM_SPCTL1_BRMO (1 << 6) - -#define CCM_OSC26MCTL_PEAK_OFFSET 16 -#define CCM_OSC26MCTL_PEAK_MASK (0x3 << 16) -#define CCM_OSC26MCTL_AGC_OFFSET 8 -#define CCM_OSC26MCTL_AGC_MASK (0x3f << 8) -#define CCM_OSC26MCTL_ANATEST_OFFSET 0 -#define CCM_OSC26MCTL_ANATEST_MASK 0x3f - -#define CCM_PCDR0_SSI2BAUDDIV_OFFSET 26 -#define CCM_PCDR0_SSI2BAUDDIV_MASK (0x3f << 26) -#define CCM_PCDR0_SSI1BAUDDIV_OFFSET 16 -#define CCM_PCDR0_SSI1BAUDDIV_MASK (0x3f << 16) -#define CCM_PCDR0_NFCDIV_OFFSET 12 -#define CCM_PCDR0_NFCDIV_MASK (0xf << 12) -#define CCM_PCDR0_48MDIV_OFFSET 5 -#define CCM_PCDR0_48MDIV_MASK (0x7 << CCM_PCDR0_48MDIV_OFFSET) -#define CCM_PCDR0_FIRIDIV_OFFSET 0 -#define CCM_PCDR0_FIRIDIV_MASK 0x1f -#define CCM_PCDR1_PERDIV4_OFFSET 24 -#define CCM_PCDR1_PERDIV4_MASK (0x3f << 24) -#define CCM_PCDR1_PERDIV3_OFFSET 16 -#define CCM_PCDR1_PERDIV3_MASK (0x3f << 16) -#define CCM_PCDR1_PERDIV2_OFFSET 8 -#define CCM_PCDR1_PERDIV2_MASK (0x3f << 8) -#define CCM_PCDR1_PERDIV1_OFFSET 0 -#define CCM_PCDR1_PERDIV1_MASK 0x3f - -#define CCM_PCCR_HCLK_CSI_OFFSET 31 -#define CCM_PCCR_HCLK_CSI_REG CCM_PCCR0 -#define CCM_PCCR_HCLK_DMA_OFFSET 30 -#define CCM_PCCR_HCLK_DMA_REG CCM_PCCR0 -#define CCM_PCCR_HCLK_BROM_OFFSET 28 -#define CCM_PCCR_HCLK_BROM_REG CCM_PCCR0 -#define CCM_PCCR_HCLK_EMMA_OFFSET 27 -#define CCM_PCCR_HCLK_EMMA_REG CCM_PCCR0 -#define CCM_PCCR_HCLK_LCDC_OFFSET 26 -#define CCM_PCCR_HCLK_LCDC_REG CCM_PCCR0 -#define CCM_PCCR_HCLK_SLCDC_OFFSET 25 -#define CCM_PCCR_HCLK_SLCDC_REG CCM_PCCR0 -#define CCM_PCCR_HCLK_USBOTG_OFFSET 24 -#define CCM_PCCR_HCLK_USBOTG_REG CCM_PCCR0 -#define CCM_PCCR_HCLK_BMI_OFFSET 23 -#define CCM_PCCR_BMI_MASK (1 << CCM_PCCR_BMI_MASK) -#define CCM_PCCR_HCLK_BMI_REG CCM_PCCR0 -#define CCM_PCCR_PERCLK4_OFFSET 22 -#define CCM_PCCR_PERCLK4_REG CCM_PCCR0 -#define CCM_PCCR_SLCDC_OFFSET 21 -#define CCM_PCCR_SLCDC_REG CCM_PCCR0 -#define CCM_PCCR_FIRI_BAUD_OFFSET 20 -#define CCM_PCCR_FIRI_BAUD_MASK (1 << CCM_PCCR_FIRI_BAUD_MASK) -#define CCM_PCCR_FIRI_BAUD_REG CCM_PCCR0 -#define CCM_PCCR_NFC_OFFSET 19 -#define CCM_PCCR_NFC_REG CCM_PCCR0 -#define CCM_PCCR_LCDC_OFFSET 18 -#define CCM_PCCR_LCDC_REG CCM_PCCR0 -#define CCM_PCCR_SSI1_BAUD_OFFSET 17 -#define CCM_PCCR_SSI1_BAUD_REG CCM_PCCR0 -#define CCM_PCCR_SSI2_BAUD_OFFSET 16 -#define CCM_PCCR_SSI2_BAUD_REG CCM_PCCR0 -#define CCM_PCCR_EMMA_OFFSET 15 -#define CCM_PCCR_EMMA_REG CCM_PCCR0 -#define CCM_PCCR_USBOTG_OFFSET 14 -#define CCM_PCCR_USBOTG_REG CCM_PCCR0 -#define CCM_PCCR_DMA_OFFSET 13 -#define CCM_PCCR_DMA_REG CCM_PCCR0 -#define CCM_PCCR_I2C1_OFFSET 12 -#define CCM_PCCR_I2C1_REG CCM_PCCR0 -#define CCM_PCCR_GPIO_OFFSET 11 -#define CCM_PCCR_GPIO_REG CCM_PCCR0 -#define CCM_PCCR_SDHC2_OFFSET 10 -#define CCM_PCCR_SDHC2_REG CCM_PCCR0 -#define CCM_PCCR_SDHC1_OFFSET 9 -#define CCM_PCCR_SDHC1_REG CCM_PCCR0 -#define CCM_PCCR_FIRI_OFFSET 8 -#define CCM_PCCR_FIRI_MASK (1 << CCM_PCCR_BAUD_MASK) -#define CCM_PCCR_FIRI_REG CCM_PCCR0 -#define CCM_PCCR_SSI2_IPG_OFFSET 7 -#define CCM_PCCR_SSI2_REG CCM_PCCR0 -#define CCM_PCCR_SSI1_IPG_OFFSET 6 -#define CCM_PCCR_SSI1_REG CCM_PCCR0 -#define CCM_PCCR_CSPI2_OFFSET 5 -#define CCM_PCCR_CSPI2_REG CCM_PCCR0 -#define CCM_PCCR_CSPI1_OFFSET 4 -#define CCM_PCCR_CSPI1_REG CCM_PCCR0 -#define CCM_PCCR_UART4_OFFSET 3 -#define CCM_PCCR_UART4_REG CCM_PCCR0 -#define CCM_PCCR_UART3_OFFSET 2 -#define CCM_PCCR_UART3_REG CCM_PCCR0 -#define CCM_PCCR_UART2_OFFSET 1 -#define CCM_PCCR_UART2_REG CCM_PCCR0 -#define CCM_PCCR_UART1_OFFSET 0 -#define CCM_PCCR_UART1_REG CCM_PCCR0 - -#define CCM_PCCR_OWIRE_OFFSET 31 -#define CCM_PCCR_OWIRE_REG CCM_PCCR1 -#define CCM_PCCR_KPP_OFFSET 30 -#define CCM_PCCR_KPP_REG CCM_PCCR1 -#define CCM_PCCR_RTC_OFFSET 29 -#define CCM_PCCR_RTC_REG CCM_PCCR1 -#define CCM_PCCR_PWM_OFFSET 28 -#define CCM_PCCR_PWM_REG CCM_PCCR1 -#define CCM_PCCR_GPT3_OFFSET 27 -#define CCM_PCCR_GPT3_REG CCM_PCCR1 -#define CCM_PCCR_GPT2_OFFSET 26 -#define CCM_PCCR_GPT2_REG CCM_PCCR1 -#define CCM_PCCR_GPT1_OFFSET 25 -#define CCM_PCCR_GPT1_REG CCM_PCCR1 -#define CCM_PCCR_WDT_OFFSET 24 -#define CCM_PCCR_WDT_REG CCM_PCCR1 -#define CCM_PCCR_CSPI3_OFFSET 23 -#define CCM_PCCR_CSPI3_REG CCM_PCCR1 - -#define CCM_PCCR_CSPI1_MASK (1 << CCM_PCCR_CSPI1_OFFSET) -#define CCM_PCCR_CSPI2_MASK (1 << CCM_PCCR_CSPI2_OFFSET) -#define CCM_PCCR_CSPI3_MASK (1 << CCM_PCCR_CSPI3_OFFSET) -#define CCM_PCCR_DMA_MASK (1 << CCM_PCCR_DMA_OFFSET) -#define CCM_PCCR_EMMA_MASK (1 << CCM_PCCR_EMMA_OFFSET) -#define CCM_PCCR_GPIO_MASK (1 << CCM_PCCR_GPIO_OFFSET) -#define CCM_PCCR_GPT1_MASK (1 << CCM_PCCR_GPT1_OFFSET) -#define CCM_PCCR_GPT2_MASK (1 << CCM_PCCR_GPT2_OFFSET) -#define CCM_PCCR_GPT3_MASK (1 << CCM_PCCR_GPT3_OFFSET) -#define CCM_PCCR_HCLK_BROM_MASK (1 << CCM_PCCR_HCLK_BROM_OFFSET) -#define CCM_PCCR_HCLK_CSI_MASK (1 << CCM_PCCR_HCLK_CSI_OFFSET) -#define CCM_PCCR_HCLK_DMA_MASK (1 << CCM_PCCR_HCLK_DMA_OFFSET) -#define CCM_PCCR_HCLK_EMMA_MASK (1 << CCM_PCCR_HCLK_EMMA_OFFSET) -#define CCM_PCCR_HCLK_LCDC_MASK (1 << CCM_PCCR_HCLK_LCDC_OFFSET) -#define CCM_PCCR_HCLK_SLCDC_MASK (1 << CCM_PCCR_HCLK_SLCDC_OFFSET) -#define CCM_PCCR_HCLK_USBOTG_MASK (1 << CCM_PCCR_HCLK_USBOTG_OFFSET) -#define CCM_PCCR_I2C1_MASK (1 << CCM_PCCR_I2C1_OFFSET) -#define CCM_PCCR_KPP_MASK (1 << CCM_PCCR_KPP_OFFSET) -#define CCM_PCCR_LCDC_MASK (1 << CCM_PCCR_LCDC_OFFSET) -#define CCM_PCCR_NFC_MASK (1 << CCM_PCCR_NFC_OFFSET) -#define CCM_PCCR_OWIRE_MASK (1 << CCM_PCCR_OWIRE_OFFSET) -#define CCM_PCCR_PERCLK4_MASK (1 << CCM_PCCR_PERCLK4_OFFSET) -#define CCM_PCCR_PWM_MASK (1 << CCM_PCCR_PWM_OFFSET) -#define CCM_PCCR_RTC_MASK (1 << CCM_PCCR_RTC_OFFSET) -#define CCM_PCCR_SDHC1_MASK (1 << CCM_PCCR_SDHC1_OFFSET) -#define CCM_PCCR_SDHC2_MASK (1 << CCM_PCCR_SDHC2_OFFSET) -#define CCM_PCCR_SLCDC_MASK (1 << CCM_PCCR_SLCDC_OFFSET) -#define CCM_PCCR_SSI1_BAUD_MASK (1 << CCM_PCCR_SSI1_BAUD_OFFSET) -#define CCM_PCCR_SSI1_IPG_MASK (1 << CCM_PCCR_SSI1_IPG_OFFSET) -#define CCM_PCCR_SSI2_BAUD_MASK (1 << CCM_PCCR_SSI2_BAUD_OFFSET) -#define CCM_PCCR_SSI2_IPG_MASK (1 << CCM_PCCR_SSI2_IPG_OFFSET) -#define CCM_PCCR_UART1_MASK (1 << CCM_PCCR_UART1_OFFSET) -#define CCM_PCCR_UART2_MASK (1 << CCM_PCCR_UART2_OFFSET) -#define CCM_PCCR_UART3_MASK (1 << CCM_PCCR_UART3_OFFSET) -#define CCM_PCCR_UART4_MASK (1 << CCM_PCCR_UART4_OFFSET) -#define CCM_PCCR_USBOTG_MASK (1 << CCM_PCCR_USBOTG_OFFSET) -#define CCM_PCCR_WDT_MASK (1 << CCM_PCCR_WDT_OFFSET) - - -#define CCM_CCSR_32KSR (1 << 15) - -#define CCM_CCSR_CLKMODE1 (1 << 9) -#define CCM_CCSR_CLKMODE0 (1 << 8) - -#define CCM_CCSR_CLKOSEL_OFFSET 0 -#define CCM_CCSR_CLKOSEL_MASK 0x1f - -#define SYS_FMCR 0x14 /* Functional Muxing Control Reg */ -#define SYS_CHIP_ID 0x00 /* The offset of CHIP ID register */ - -#endif /* __ARCH_ARM_MACH_MX2_CRM_REGS_H__ */ -- cgit v0.10.2 From 318859fdff2caf4455749ff0097efb8f3e9b6879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 11 Dec 2009 11:04:06 +0100 Subject: imx/clock-imx21: use a macro to define registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way the base address isn't hard coded in each register definition Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Holger Schurig Cc: Rabin Vincent diff --git a/arch/arm/mach-mx2/clock_imx21.c b/arch/arm/mach-mx2/clock_imx21.c index df3ad38..1922e53 100644 --- a/arch/arm/mach-mx2/clock_imx21.c +++ b/arch/arm/mach-mx2/clock_imx21.c @@ -28,21 +28,23 @@ #include #include +#define IO_ADDR_CCM(off) (IO_ADDRESS(CCM_BASE_ADDR) + (off)) + /* Register offsets */ -#define CCM_CSCR (IO_ADDRESS(CCM_BASE_ADDR) + 0x0) -#define CCM_MPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x4) -#define CCM_MPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x8) -#define CCM_SPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0xC) -#define CCM_SPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x10) -#define CCM_OSC26MCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x14) -#define CCM_PCDR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x18) -#define CCM_PCDR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x1c) -#define CCM_PCCR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x20) -#define CCM_PCCR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x24) -#define CCM_CCSR (IO_ADDRESS(CCM_BASE_ADDR) + 0x28) -#define CCM_PMCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x2c) -#define CCM_PMCOUNT (IO_ADDRESS(CCM_BASE_ADDR) + 0x30) -#define CCM_WKGDCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x34) +#define CCM_CSCR IO_ADDR_CCM(0x0) +#define CCM_MPCTL0 IO_ADDR_CCM(0x4) +#define CCM_MPCTL1 IO_ADDR_CCM(0x8) +#define CCM_SPCTL0 IO_ADDR_CCM(0xc) +#define CCM_SPCTL1 IO_ADDR_CCM(0x10) +#define CCM_OSC26MCTL IO_ADDR_CCM(0x14) +#define CCM_PCDR0 IO_ADDR_CCM(0x18) +#define CCM_PCDR1 IO_ADDR_CCM(0x1c) +#define CCM_PCCR0 IO_ADDR_CCM(0x20) +#define CCM_PCCR1 IO_ADDR_CCM(0x24) +#define CCM_CCSR IO_ADDR_CCM(0x28) +#define CCM_PMCTL IO_ADDR_CCM(0x2c) +#define CCM_PMCOUNT IO_ADDR_CCM(0x30) +#define CCM_WKGDCTL IO_ADDR_CCM(0x34) #define CCM_CSCR_PRESC_OFFSET 29 #define CCM_CSCR_PRESC_MASK (0x7 << CCM_CSCR_PRESC_OFFSET) -- cgit v0.10.2 From b70564686a3a3eac7c2da09722091dcf5b07d5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 11 Dec 2009 09:57:27 +0100 Subject: imx/clock-imx27: use a macro to define registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way the base address isn't hard coded in each register definition Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Rabin Vincent Cc: Javier Martin Cc: Valentin Longchamp diff --git a/arch/arm/mach-mx2/clock_imx27.c b/arch/arm/mach-mx2/clock_imx27.c index b010bf9..a533208 100644 --- a/arch/arm/mach-mx2/clock_imx27.c +++ b/arch/arm/mach-mx2/clock_imx27.c @@ -29,21 +29,23 @@ #include #include +#define IO_ADDR_CCM(off) (IO_ADDRESS(CCM_BASE_ADDR) + (off)) + /* Register offsets */ -#define CCM_CSCR (IO_ADDRESS(CCM_BASE_ADDR) + 0x0) -#define CCM_MPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x4) -#define CCM_MPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x8) -#define CCM_SPCTL0 (IO_ADDRESS(CCM_BASE_ADDR) + 0xC) -#define CCM_SPCTL1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x10) -#define CCM_OSC26MCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x14) -#define CCM_PCDR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x18) -#define CCM_PCDR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x1c) -#define CCM_PCCR0 (IO_ADDRESS(CCM_BASE_ADDR) + 0x20) -#define CCM_PCCR1 (IO_ADDRESS(CCM_BASE_ADDR) + 0x24) -#define CCM_CCSR (IO_ADDRESS(CCM_BASE_ADDR) + 0x28) -#define CCM_PMCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x2c) -#define CCM_PMCOUNT (IO_ADDRESS(CCM_BASE_ADDR) + 0x30) -#define CCM_WKGDCTL (IO_ADDRESS(CCM_BASE_ADDR) + 0x34) +#define CCM_CSCR IO_ADDR_CCM(0x0) +#define CCM_MPCTL0 IO_ADDR_CCM(0x4) +#define CCM_MPCTL1 IO_ADDR_CCM(0x8) +#define CCM_SPCTL0 IO_ADDR_CCM(0xc) +#define CCM_SPCTL1 IO_ADDR_CCM(0x10) +#define CCM_OSC26MCTL IO_ADDR_CCM(0x14) +#define CCM_PCDR0 IO_ADDR_CCM(0x18) +#define CCM_PCDR1 IO_ADDR_CCM(0x1c) +#define CCM_PCCR0 IO_ADDR_CCM(0x20) +#define CCM_PCCR1 IO_ADDR_CCM(0x24) +#define CCM_CCSR IO_ADDR_CCM(0x28) +#define CCM_PMCTL IO_ADDR_CCM(0x2c) +#define CCM_PMCOUNT IO_ADDR_CCM(0x30) +#define CCM_WKGDCTL IO_ADDR_CCM(0x34) #define CCM_CSCR_UPDATE_DIS (1 << 31) #define CCM_CSCR_SSI2 (1 << 23) -- cgit v0.10.2 From 3f35d1f5e4507ea4eb7ff5feaf624737c1b47631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 9 Dec 2009 11:32:11 +0100 Subject: imx/mach-mx2: use constants namespaced by the corresponding SOC (easy part) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This just leaves devices.c, generic.c and serial.c to clean up. As these files are used on more than one SOC they need some more work. Signed-off-by: Uwe Kleine-König Cc: Holger Schurig Cc: Rabin Vincent Cc: Javier Martin Cc: Valentin Longchamp Cc: Jean Delvare Cc: Eric Benard Cc: Ivo Clarysse Cc: Vladimir Barinov Cc: Daniel Schaeffer Cc: Simon POLETTE Cc: Fabio Estevam Cc: Alan Carvalho de Assis Cc: Luotao Fu diff --git a/arch/arm/mach-mx2/clock_imx21.c b/arch/arm/mach-mx2/clock_imx21.c index 1922e53..6ab3c72 100644 --- a/arch/arm/mach-mx2/clock_imx21.c +++ b/arch/arm/mach-mx2/clock_imx21.c @@ -28,7 +28,7 @@ #include #include -#define IO_ADDR_CCM(off) (IO_ADDRESS(CCM_BASE_ADDR) + (off)) +#define IO_ADDR_CCM(off) (IO_ADDRESS(MX21_CCM_BASE_ADDR) + (off)) /* Register offsets */ #define CCM_CSCR IO_ADDR_CCM(0x0) @@ -1235,6 +1235,7 @@ int __init mx21_clocks_init(unsigned long lref, unsigned long href) clk_enable(&uart_clk[0]); #endif - mxc_timer_init(&gpt_clk[0], IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT1); + mxc_timer_init(&gpt_clk[0], IO_ADDRESS(MX21_GPT1_BASE_ADDR), + MX21_INT_GPT1); return 0; } diff --git a/arch/arm/mach-mx2/clock_imx27.c b/arch/arm/mach-mx2/clock_imx27.c index a533208..6db9856 100644 --- a/arch/arm/mach-mx2/clock_imx27.c +++ b/arch/arm/mach-mx2/clock_imx27.c @@ -29,7 +29,7 @@ #include #include -#define IO_ADDR_CCM(off) (IO_ADDRESS(CCM_BASE_ADDR) + (off)) +#define IO_ADDR_CCM(off) (IO_ADDRESS(MX27_CCM_BASE_ADDR) + (off)) /* Register offsets */ #define CCM_CSCR IO_ADDR_CCM(0x0) @@ -757,7 +757,8 @@ int __init mx27_clocks_init(unsigned long fref) clk_enable(&uart1_clk); #endif - mxc_timer_init(&gpt1_clk, IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT1); + mxc_timer_init(&gpt1_clk, IO_ADDRESS(MX27_GPT1_BASE_ADDR), + MX27_INT_GPT1); return 0; } diff --git a/arch/arm/mach-mx2/cpu_imx27.c b/arch/arm/mach-mx2/cpu_imx27.c index d9e3bf9..8c50c5e 100644 --- a/arch/arm/mach-mx2/cpu_imx27.c +++ b/arch/arm/mach-mx2/cpu_imx27.c @@ -39,7 +39,7 @@ static void query_silicon_parameter(void) * the silicon revision very early we read it here to * avoid any further hooks */ - val = __raw_readl(IO_ADDRESS(SYSCTRL_BASE_ADDR) + SYS_CHIP_ID); + val = __raw_readl(IO_ADDRESS(MX27_SYSCTRL_BASE_ADDR) + SYS_CHIP_ID); cpu_silicon_rev = (int)(val >> 28); cpu_partnumber = (int)((val >> 12) & 0xFFFF); diff --git a/arch/arm/mach-mx2/eukrea_cpuimx27.c b/arch/arm/mach-mx2/eukrea_cpuimx27.c index 7b18760..8e4f3d0 100644 --- a/arch/arm/mach-mx2/eukrea_cpuimx27.c +++ b/arch/arm/mach-mx2/eukrea_cpuimx27.c @@ -142,28 +142,28 @@ static struct i2c_board_info eukrea_cpuimx27_i2c_devices[] = { #if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) static struct plat_serial8250_port serial_platform_data[] = { { - .mapbase = (unsigned long)(CS3_BASE_ADDR + 0x200000), + .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x200000), .irq = IRQ_GPIOB(23), .uartclk = 14745600, .regshift = 1, .iotype = UPIO_MEM, .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, }, { - .mapbase = (unsigned long)(CS3_BASE_ADDR + 0x400000), + .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x400000), .irq = IRQ_GPIOB(22), .uartclk = 14745600, .regshift = 1, .iotype = UPIO_MEM, .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, }, { - .mapbase = (unsigned long)(CS3_BASE_ADDR + 0x800000), + .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x800000), .irq = IRQ_GPIOB(27), .uartclk = 14745600, .regshift = 1, .iotype = UPIO_MEM, .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, }, { - .mapbase = (unsigned long)(CS3_BASE_ADDR + 0x1000000), + .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x1000000), .irq = IRQ_GPIOB(30), .uartclk = 14745600, .regshift = 1, @@ -224,8 +224,8 @@ static struct sys_timer eukrea_cpuimx27_timer = { }; MACHINE_START(CPUIMX27, "EUKREA CPUIMX27") - .phys_io = AIPI_BASE_ADDR, - .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx27_map_io, .init_irq = mx27_init_irq, diff --git a/arch/arm/mach-mx2/mx21ads.c b/arch/arm/mach-mx2/mx21ads.c index cf5f77c..eb4a6e8 100644 --- a/arch/arm/mach-mx2/mx21ads.c +++ b/arch/arm/mach-mx2/mx21ads.c @@ -118,8 +118,8 @@ static struct physmap_flash_data mx21ads_flash_data = { }; static struct resource mx21ads_flash_resource = { - .start = CS0_BASE_ADDR, - .end = CS0_BASE_ADDR + 0x02000000 - 1, + .start = MX21_CS0_BASE_ADDR, + .end = MX21_CS0_BASE_ADDR + 0x02000000 - 1, .flags = IORESOURCE_MEM, }; @@ -242,7 +242,7 @@ static struct map_desc mx21ads_io_desc[] __initdata = { */ { .virtual = MX21ADS_MMIO_BASE_ADDR, - .pfn = __phys_to_pfn(CS1_BASE_ADDR), + .pfn = __phys_to_pfn(MX21_CS1_BASE_ADDR), .length = MX21ADS_MMIO_SIZE, .type = MT_DEVICE, }, @@ -284,8 +284,8 @@ static struct sys_timer mx21ads_timer = { MACHINE_START(MX21ADS, "Freescale i.MX21ADS") /* maintainer: Freescale Semiconductor, Inc. */ - .phys_io = AIPI_BASE_ADDR, - .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX21_AIPI_BASE_ADDR, + .io_pg_offst = ((MX21_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx21ads_map_io, .init_irq = mx21_init_irq, diff --git a/arch/arm/mach-mx2/mx27ads.c b/arch/arm/mach-mx2/mx27ads.c index 83e412b..385fc1c 100644 --- a/arch/arm/mach-mx2/mx27ads.c +++ b/arch/arm/mach-mx2/mx27ads.c @@ -320,7 +320,7 @@ static struct sys_timer mx27ads_timer = { static struct map_desc mx27ads_io_desc[] __initdata = { { .virtual = PBC_BASE_ADDRESS, - .pfn = __phys_to_pfn(CS4_BASE_ADDR), + .pfn = __phys_to_pfn(MX27_CS4_BASE_ADDR), .length = SZ_1M, .type = MT_DEVICE, }, @@ -334,8 +334,8 @@ static void __init mx27ads_map_io(void) MACHINE_START(MX27ADS, "Freescale i.MX27ADS") /* maintainer: Freescale Semiconductor, Inc. */ - .phys_io = AIPI_BASE_ADDR, - .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx27ads_map_io, .init_irq = mx27_init_irq, diff --git a/arch/arm/mach-mx2/mx27lite.c b/arch/arm/mach-mx2/mx27lite.c index 82ea227..ca6ab12 100644 --- a/arch/arm/mach-mx2/mx27lite.c +++ b/arch/arm/mach-mx2/mx27lite.c @@ -85,8 +85,8 @@ static struct sys_timer mx27lite_timer = { }; MACHINE_START(IMX27LITE, "LogicPD i.MX27LITE") - .phys_io = AIPI_BASE_ADDR, - .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx27_map_io, .init_irq = mx27_init_irq, diff --git a/arch/arm/mach-mx2/mx27pdk.c b/arch/arm/mach-mx2/mx27pdk.c index 6761d1b..595fea4 100644 --- a/arch/arm/mach-mx2/mx27pdk.c +++ b/arch/arm/mach-mx2/mx27pdk.c @@ -85,8 +85,8 @@ static struct sys_timer mx27pdk_timer = { MACHINE_START(MX27_3DS, "Freescale MX27PDK") /* maintainer: Freescale Semiconductor, Inc. */ - .phys_io = AIPI_BASE_ADDR, - .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx27_map_io, .init_irq = mx27_init_irq, diff --git a/arch/arm/mach-mx2/mxt_td60.c b/arch/arm/mach-mx2/mxt_td60.c index 8bcc1a5..9ed4e49 100644 --- a/arch/arm/mach-mx2/mxt_td60.c +++ b/arch/arm/mach-mx2/mxt_td60.c @@ -284,8 +284,8 @@ static struct sys_timer mxt_td60_timer = { MACHINE_START(MXT_TD60, "Maxtrack i-MXT TD60") /* maintainer: Maxtrack Industrial */ - .phys_io = AIPI_BASE_ADDR, - .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx27_map_io, .init_irq = mx27_init_irq, diff --git a/arch/arm/mach-mx2/pca100.c b/arch/arm/mach-mx2/pca100.c index aea3d34..55dbf5a 100644 --- a/arch/arm/mach-mx2/pca100.c +++ b/arch/arm/mach-mx2/pca100.c @@ -233,8 +233,8 @@ static struct sys_timer pca100_timer = { }; MACHINE_START(PCA100, "phyCARD-i.MX27") - .phys_io = AIPI_BASE_ADDR, - .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx27_map_io, .init_irq = mx27_init_irq, diff --git a/arch/arm/mach-mx2/pcm038.c b/arch/arm/mach-mx2/pcm038.c index 906d59b..a2e2ea1 100644 --- a/arch/arm/mach-mx2/pcm038.c +++ b/arch/arm/mach-mx2/pcm038.c @@ -108,8 +108,8 @@ static struct platdata_mtd_ram pcm038_sram_data = { }; static struct resource pcm038_sram_resource = { - .start = CS1_BASE_ADDR, - .end = CS1_BASE_ADDR + 512 * 1024 - 1, + .start = MX27_CS1_BASE_ADDR, + .end = MX27_CS1_BASE_ADDR + 512 * 1024 - 1, .flags = IORESOURCE_MEM, }; @@ -328,8 +328,8 @@ static struct sys_timer pcm038_timer = { }; MACHINE_START(PCM038, "phyCORE-i.MX27") - .phys_io = AIPI_BASE_ADDR, - .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx27_map_io, .init_irq = mx27_init_irq, diff --git a/arch/arm/mach-mx2/pcm970-baseboard.c b/arch/arm/mach-mx2/pcm970-baseboard.c index 3cb7f45..60d5446 100644 --- a/arch/arm/mach-mx2/pcm970-baseboard.c +++ b/arch/arm/mach-mx2/pcm970-baseboard.c @@ -190,8 +190,8 @@ static struct imx_fb_platform_data pcm038_fb_data = { static struct resource pcm970_sja1000_resources[] = { { - .start = CS4_BASE_ADDR, - .end = CS4_BASE_ADDR + 0x100 - 1, + .start = MX27_CS4_BASE_ADDR, + .end = MX27_CS4_BASE_ADDR + 0x100 - 1, .flags = IORESOURCE_MEM, }, { .start = IRQ_GPIOE(19), -- cgit v0.10.2 From f568dd7f5805bfa9e99f6df866300498b55eb8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 9 Dec 2009 11:57:21 +0100 Subject: imx/mach-mx3: use constants namespaced by the corresponding SOC (easy part) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This just leaves cpu.c, devices.c and mm.c to clean up. As these files are used on more than one SOC they need some more work. Signed-off-by: Uwe Kleine-König Cc: Russell King Cc: Alberto Panizzo Cc: Valentin Longchamp Cc: Daniel Mack Cc: Guennadi Liakhovetski Cc: Yoichi Yuasa Cc: Mark Brown Cc: Magnus Lilja Cc: Fabio Estevam Cc: Sascha Hauer Cc: Harro Haan Cc: Jean Delvare Cc: Luotao Fu Cc: Ilya Yanok diff --git a/arch/arm/mach-mx3/armadillo5x0.c b/arch/arm/mach-mx3/armadillo5x0.c index 54aab40..aac5f81 100644 --- a/arch/arm/mach-mx3/armadillo5x0.c +++ b/arch/arm/mach-mx3/armadillo5x0.c @@ -182,8 +182,8 @@ static struct physmap_flash_data armadillo5x0_nor_flash_pdata = { static struct resource armadillo5x0_nor_flash_resource = { .flags = IORESOURCE_MEM, - .start = CS0_BASE_ADDR, - .end = CS0_BASE_ADDR + SZ_64M - 1, + .start = MX31_CS0_BASE_ADDR, + .end = MX31_CS0_BASE_ADDR + SZ_64M - 1, }; static struct platform_device armadillo5x0_nor_flash = { @@ -311,8 +311,8 @@ static struct imxmmc_platform_data sdhc_pdata = { */ static struct resource armadillo5x0_smc911x_resources[] = { { - .start = CS3_BASE_ADDR, - .end = CS3_BASE_ADDR + SZ_32M - 1, + .start = MX31_CS3_BASE_ADDR, + .end = MX31_CS3_BASE_ADDR + SZ_32M - 1, .flags = IORESOURCE_MEM, }, { .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), @@ -406,8 +406,8 @@ static struct sys_timer armadillo5x0_timer = { MACHINE_START(ARMADILLO5X0, "Armadillo-500") /* Maintainer: Alberto Panizzo */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x00000100, .map_io = mx31_map_io, .init_irq = mx31_init_irq, diff --git a/arch/arm/mach-mx3/clock-imx35.c b/arch/arm/mach-mx3/clock-imx35.c index 7584b4c..e65e862 100644 --- a/arch/arm/mach-mx3/clock-imx35.c +++ b/arch/arm/mach-mx3/clock-imx35.c @@ -28,7 +28,7 @@ #include #include -#define CCM_BASE IO_ADDRESS(CCM_BASE_ADDR) +#define CCM_BASE IO_ADDRESS(MX35_CCM_BASE_ADDR) #define CCM_CCMR 0x00 #define CCM_PDR0 0x04 @@ -504,7 +504,7 @@ int __init mx35_clocks_init() __raw_writel((3 << 26) | ll, CCM_BASE + CCM_CGR2); __raw_writel(0, CCM_BASE + CCM_CGR3); - mxc_timer_init(&gpt_clk, IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT); + mxc_timer_init(&gpt_clk, IO_ADDRESS(MX35_GPT1_BASE_ADDR), MX35_INT_GPT); return 0; } diff --git a/arch/arm/mach-mx3/clock.c b/arch/arm/mach-mx3/clock.c index 27a318a..cc03a61 100644 --- a/arch/arm/mach-mx3/clock.c +++ b/arch/arm/mach-mx3/clock.c @@ -618,14 +618,14 @@ int __init mx31_clocks_init(unsigned long fref) mx31_read_cpu_rev(); - if (mx31_revision() >= CHIP_REV_2_0) { + if (mx31_revision() >= MX31_CHIP_REV_2_0) { reg = __raw_readl(MXC_CCM_PMCR1); /* No PLL restart on DVFS switch; enable auto EMI handshake */ reg |= MXC_CCM_PMCR1_PLLRDIS | MXC_CCM_PMCR1_EMIRQ_EN; __raw_writel(reg, MXC_CCM_PMCR1); } - mxc_timer_init(&ipg_clk, IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT); + mxc_timer_init(&ipg_clk, IO_ADDRESS(MX31_GPT1_BASE_ADDR), MX31_INT_GPT); return 0; } diff --git a/arch/arm/mach-mx3/crm_regs.h b/arch/arm/mach-mx3/crm_regs.h index adfa362..e25cd92 100644 --- a/arch/arm/mach-mx3/crm_regs.h +++ b/arch/arm/mach-mx3/crm_regs.h @@ -24,7 +24,7 @@ #define CKIH_CLK_FREQ_27MHZ 27000000 #define CKIL_CLK_FREQ 32768 -#define MXC_CCM_BASE IO_ADDRESS(CCM_BASE_ADDR) +#define MXC_CCM_BASE IO_ADDRESS(MX31_CCM_BASE_ADDR) /* Register addresses */ #define MXC_CCM_CCMR (MXC_CCM_BASE + 0x00) diff --git a/arch/arm/mach-mx3/iomux.c b/arch/arm/mach-mx3/iomux.c index c66ccbc..6381e56 100644 --- a/arch/arm/mach-mx3/iomux.c +++ b/arch/arm/mach-mx3/iomux.c @@ -29,7 +29,7 @@ /* * IOMUX register (base) addresses */ -#define IOMUX_BASE IO_ADDRESS(IOMUXC_BASE_ADDR) +#define IOMUX_BASE IO_ADDRESS(MX31_IOMUXC_BASE_ADDR) #define IOMUXINT_OBS1 (IOMUX_BASE + 0x000) #define IOMUXINT_OBS2 (IOMUX_BASE + 0x004) #define IOMUXGPR (IOMUX_BASE + 0x008) diff --git a/arch/arm/mach-mx3/kzmarm11.c b/arch/arm/mach-mx3/kzmarm11.c index 6fa99ce..849631e 100644 --- a/arch/arm/mach-mx3/kzmarm11.c +++ b/arch/arm/mach-mx3/kzmarm11.c @@ -128,8 +128,8 @@ static struct smsc911x_platform_config kzm_smsc9118_config = { static struct resource kzm_smsc9118_resources[] = { { - .start = CS5_BASE_ADDR, - .end = CS5_BASE_ADDR + SZ_128K - 1, + .start = MX31_CS5_BASE_ADDR, + .end = MX31_CS5_BASE_ADDR + SZ_128K - 1, .flags = IORESOURCE_MEM, }, { @@ -222,15 +222,15 @@ static void __init kzm_board_init(void) */ static struct map_desc kzm_io_desc[] __initdata = { { - .virtual = CS4_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(CS4_BASE_ADDR), - .length = CS4_SIZE, + .virtual = MX31_CS4_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), + .length = MX31_CS4_SIZE, .type = MT_DEVICE }, { - .virtual = CS5_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(CS5_BASE_ADDR), - .length = CS5_SIZE, + .virtual = MX31_CS5_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS5_BASE_ADDR), + .length = MX31_CS5_SIZE, .type = MT_DEVICE }, }; @@ -258,8 +258,8 @@ static struct sys_timer kzm_timer = { * initialize __mach_desc_KZM_ARM11_01 data structure. */ MACHINE_START(KZM_ARM11_01, "Kyoto Microcomputer Co., Ltd. KZM-ARM11-01") - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = kzm_map_io, .init_irq = mx31_init_irq, diff --git a/arch/arm/mach-mx3/mx31ads.c b/arch/arm/mach-mx3/mx31ads.c index 3e7bafa..6699116 100644 --- a/arch/arm/mach-mx3/mx31ads.c +++ b/arch/arm/mach-mx3/mx31ads.c @@ -60,7 +60,7 @@ static struct plat_serial8250_port serial_platform_data[] = { { .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTA), - .mapbase = (unsigned long)(CS4_BASE_ADDR + PBC_SC16C652_UARTA), + .mapbase = (unsigned long)(MX31_CS4_BASE_ADDR + PBC_SC16C652_UARTA), .irq = EXPIO_INT_XUART_INTA, .uartclk = 14745600, .regshift = 0, @@ -68,7 +68,7 @@ static struct plat_serial8250_port serial_platform_data[] = { .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ, }, { .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTB), - .mapbase = (unsigned long)(CS4_BASE_ADDR + PBC_SC16C652_UARTB), + .mapbase = (unsigned long)(MX31_CS4_BASE_ADDR + PBC_SC16C652_UARTB), .irq = EXPIO_INT_XUART_INTB, .uartclk = 14745600, .regshift = 0, @@ -494,9 +494,9 @@ static void mxc_init_i2c(void) */ static struct map_desc mx31ads_io_desc[] __initdata = { { - .virtual = CS4_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(CS4_BASE_ADDR), - .length = CS4_SIZE / 2, + .virtual = MX31_CS4_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), + .length = MX31_CS4_SIZE / 2, .type = MT_DEVICE }, }; @@ -541,8 +541,8 @@ static struct sys_timer mx31ads_timer = { */ MACHINE_START(MX31ADS, "Freescale MX31ADS") /* Maintainer: Freescale Semiconductor, Inc. */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx31ads_map_io, .init_irq = mx31ads_init_irq, diff --git a/arch/arm/mach-mx3/mx31lilly.c b/arch/arm/mach-mx3/mx31lilly.c index 9ce029f..ffccea9 100644 --- a/arch/arm/mach-mx3/mx31lilly.c +++ b/arch/arm/mach-mx3/mx31lilly.c @@ -57,8 +57,8 @@ static struct resource smsc91x_resources[] = { { - .start = CS4_BASE_ADDR, - .end = CS4_BASE_ADDR + 0xffff, + .start = MX31_CS4_BASE_ADDR, + .end = MX31_CS4_BASE_ADDR + 0xffff, .flags = IORESOURCE_MEM, }, { @@ -195,8 +195,8 @@ static struct sys_timer mx31lilly_timer = { }; MACHINE_START(LILLY1131, "INCO startec LILLY-1131") - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx31_map_io, .init_irq = mx31_init_irq, diff --git a/arch/arm/mach-mx3/mx31lite.c b/arch/arm/mach-mx3/mx31lite.c index 789b20d..1e26397 100644 --- a/arch/arm/mach-mx3/mx31lite.c +++ b/arch/arm/mach-mx3/mx31lite.c @@ -82,8 +82,8 @@ static struct smsc911x_platform_config smsc911x_config = { static struct resource smsc911x_resources[] = { { - .start = CS4_BASE_ADDR, - .end = CS4_BASE_ADDR + 0x100, + .start = MX31_CS4_BASE_ADDR, + .end = MX31_CS4_BASE_ADDR + 0x100, .flags = IORESOURCE_MEM, }, { .start = IOMUX_TO_IRQ(MX31_PIN_SFS6), @@ -214,9 +214,9 @@ static struct platform_device physmap_flash_device = { */ static struct map_desc mx31lite_io_desc[] __initdata = { { - .virtual = CS4_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(CS4_BASE_ADDR), - .length = CS4_SIZE, + .virtual = MX31_CS4_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), + .length = MX31_CS4_SIZE, .type = MT_DEVICE } }; @@ -287,8 +287,8 @@ struct sys_timer mx31lite_timer = { MACHINE_START(MX31LITE, "LogicPD i.MX31 SOM") /* Maintainer: Freescale Semiconductor, Inc. */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx31lite_map_io, .init_irq = mx31_init_irq, diff --git a/arch/arm/mach-mx3/mx31moboard.c b/arch/arm/mach-mx3/mx31moboard.c index cfd605d..9428827 100644 --- a/arch/arm/mach-mx3/mx31moboard.c +++ b/arch/arm/mach-mx3/mx31moboard.c @@ -569,8 +569,8 @@ struct sys_timer mx31moboard_timer = { MACHINE_START(MX31MOBOARD, "EPFL Mobots mx31moboard") /* Maintainer: Valentin Longchamp, EPFL Mobots group */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx31_map_io, .init_irq = mx31_init_irq, diff --git a/arch/arm/mach-mx3/mx31pdk.c b/arch/arm/mach-mx3/mx31pdk.c index 18715f1..e71e3b4 100644 --- a/arch/arm/mach-mx3/mx31pdk.c +++ b/arch/arm/mach-mx3/mx31pdk.c @@ -211,9 +211,9 @@ static int __init mx31pdk_init_expio(void) */ static struct map_desc mx31pdk_io_desc[] __initdata = { { - .virtual = CS5_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(CS5_BASE_ADDR), - .length = CS5_SIZE, + .virtual = MX31_CS5_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS5_BASE_ADDR), + .length = MX31_CS5_SIZE, .type = MT_DEVICE, }, }; @@ -256,8 +256,8 @@ static struct sys_timer mx31pdk_timer = { */ MACHINE_START(MX31_3DS, "Freescale MX31PDK (3DS)") /* Maintainer: Freescale Semiconductor, Inc. */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx31pdk_map_io, .init_irq = mx31_init_irq, diff --git a/arch/arm/mach-mx3/mx35pdk.c b/arch/arm/mach-mx3/mx35pdk.c index 0bbc65e..2d11bf0 100644 --- a/arch/arm/mach-mx3/mx35pdk.c +++ b/arch/arm/mach-mx3/mx35pdk.c @@ -106,8 +106,8 @@ struct sys_timer mx35pdk_timer = { MACHINE_START(MX35_3DS, "Freescale MX35PDK") /* Maintainer: Freescale Semiconductor, Inc */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX35_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX35_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx35_map_io, .init_irq = mx35_init_irq, diff --git a/arch/arm/mach-mx3/pcm037.c b/arch/arm/mach-mx3/pcm037.c index 5be3969..701fac9 100644 --- a/arch/arm/mach-mx3/pcm037.c +++ b/arch/arm/mach-mx3/pcm037.c @@ -248,8 +248,8 @@ static struct imxuart_platform_data uart_pdata = { static struct resource smsc911x_resources[] = { { - .start = CS1_BASE_ADDR + 0x300, - .end = CS1_BASE_ADDR + 0x300 + SZ_64K - 1, + .start = MX31_CS1_BASE_ADDR + 0x300, + .end = MX31_CS1_BASE_ADDR + 0x300 + SZ_64K - 1, .flags = IORESOURCE_MEM, }, { .start = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1), @@ -281,8 +281,8 @@ static struct platdata_mtd_ram pcm038_sram_data = { }; static struct resource pcm038_sram_resource = { - .start = CS4_BASE_ADDR, - .end = CS4_BASE_ADDR + 512 * 1024 - 1, + .start = MX31_CS4_BASE_ADDR, + .end = MX31_CS4_BASE_ADDR + 512 * 1024 - 1, .flags = IORESOURCE_MEM, }; @@ -536,8 +536,8 @@ static struct mx3fb_platform_data mx3fb_pdata = { static struct resource pcm970_sja1000_resources[] = { { - .start = CS5_BASE_ADDR, - .end = CS5_BASE_ADDR + 0x100 - 1, + .start = MX31_CS5_BASE_ADDR, + .end = MX31_CS5_BASE_ADDR + 0x100 - 1, .flags = IORESOURCE_MEM, }, { .start = IOMUX_TO_IRQ(IOMUX_PIN(48, 105)), @@ -636,8 +636,8 @@ struct sys_timer pcm037_timer = { MACHINE_START(PCM037, "Phytec Phycore pcm037") /* Maintainer: Pengutronix */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx31_map_io, .init_irq = mx31_init_irq, diff --git a/arch/arm/mach-mx3/pcm043.c b/arch/arm/mach-mx3/pcm043.c index e3aa829..1212194 100644 --- a/arch/arm/mach-mx3/pcm043.c +++ b/arch/arm/mach-mx3/pcm043.c @@ -248,8 +248,8 @@ struct sys_timer pcm043_timer = { MACHINE_START(PCM043, "Phytec Phycore pcm043") /* Maintainer: Pengutronix */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX35_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX35_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx35_map_io, .init_irq = mx35_init_irq, diff --git a/arch/arm/mach-mx3/qong.c b/arch/arm/mach-mx3/qong.c index 044511f..3c1e736 100644 --- a/arch/arm/mach-mx3/qong.c +++ b/arch/arm/mach-mx3/qong.c @@ -43,7 +43,7 @@ #define QONG_FPGA_VERSION(major, minor, rev) \ (((major & 0xF) << 12) | ((minor & 0xF) << 8) | (rev & 0xFF)) -#define QONG_FPGA_BASEADDR CS1_BASE_ADDR +#define QONG_FPGA_BASEADDR MX31_CS1_BASE_ADDR #define QONG_FPGA_PERIPH_SIZE (1 << 24) #define QONG_FPGA_CTRL_BASEADDR QONG_FPGA_BASEADDR @@ -115,8 +115,8 @@ static struct physmap_flash_data qong_flash_data = { }; static struct resource qong_flash_resource = { - .start = CS0_BASE_ADDR, - .end = CS0_BASE_ADDR + QONG_NOR_SIZE - 1, + .start = MX31_CS0_BASE_ADDR, + .end = MX31_CS0_BASE_ADDR + QONG_NOR_SIZE - 1, .flags = IORESOURCE_MEM, }; @@ -180,8 +180,8 @@ static struct platform_nand_data qong_nand_data = { }; static struct resource qong_nand_resource = { - .start = CS3_BASE_ADDR, - .end = CS3_BASE_ADDR + SZ_32M - 1, + .start = MX31_CS3_BASE_ADDR, + .end = MX31_CS3_BASE_ADDR + SZ_32M - 1, .flags = IORESOURCE_MEM, }; @@ -275,8 +275,8 @@ static struct sys_timer qong_timer = { MACHINE_START(QONG, "Dave/DENX QongEVB-LITE") /* Maintainer: DENX Software Engineering GmbH */ - .phys_io = AIPS1_BASE_ADDR, - .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, .boot_params = PHYS_OFFSET + 0x100, .map_io = mx31_map_io, .init_irq = mx31_init_irq, diff --git a/arch/arm/plat-mxc/include/mach/board-kzmarm11.h b/arch/arm/plat-mxc/include/mach/board-kzmarm11.h index 05ff2f3..93cc66f 100644 --- a/arch/arm/plat-mxc/include/mach/board-kzmarm11.h +++ b/arch/arm/plat-mxc/include/mach/board-kzmarm11.h @@ -21,19 +21,19 @@ /* * KZM-ARM11-01 Board Control Registers on FPGA */ -#define KZM_ARM11_CTL1 (CS4_BASE_ADDR + 0x1000) -#define KZM_ARM11_CTL2 (CS4_BASE_ADDR + 0x1001) -#define KZM_ARM11_RSW1 (CS4_BASE_ADDR + 0x1002) -#define KZM_ARM11_BACK_LIGHT (CS4_BASE_ADDR + 0x1004) -#define KZM_ARM11_FPGA_REV (CS4_BASE_ADDR + 0x1008) -#define KZM_ARM11_7SEG_LED (CS4_BASE_ADDR + 0x1010) -#define KZM_ARM11_LEDS (CS4_BASE_ADDR + 0x1020) -#define KZM_ARM11_DIPSW2 (CS4_BASE_ADDR + 0x1003) +#define KZM_ARM11_CTL1 (MX31_CS4_BASE_ADDR + 0x1000) +#define KZM_ARM11_CTL2 (MX31_CS4_BASE_ADDR + 0x1001) +#define KZM_ARM11_RSW1 (MX31_CS4_BASE_ADDR + 0x1002) +#define KZM_ARM11_BACK_LIGHT (MX31_CS4_BASE_ADDR + 0x1004) +#define KZM_ARM11_FPGA_REV (MX31_CS4_BASE_ADDR + 0x1008) +#define KZM_ARM11_7SEG_LED (MX31_CS4_BASE_ADDR + 0x1010) +#define KZM_ARM11_LEDS (MX31_CS4_BASE_ADDR + 0x1020) +#define KZM_ARM11_DIPSW2 (MX31_CS4_BASE_ADDR + 0x1003) /* * External UART for touch panel on FPGA */ -#define KZM_ARM11_16550 (CS4_BASE_ADDR + 0x1050) +#define KZM_ARM11_16550 (MX31_CS4_BASE_ADDR + 0x1050) #endif /* __ARM_ARCH_BOARD_KZM_ARM11_H */ -- cgit v0.10.2 From 60d644cae9d96e13547c69fbac22e800790e6054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 9 Dec 2009 19:52:57 +0100 Subject: imx/mx3: rename clock.c and iomux.c to {clock,iomux}-imx31.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These files are only used on imx31. This makes the name consistent with the imx35 files. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Daniel Mack Cc: Fabio Estevam Cc: Valentin Longchamp diff --git a/arch/arm/mach-mx3/Makefile b/arch/arm/mach-mx3/Makefile index 93c7b29..03e25d6 100644 --- a/arch/arm/mach-mx3/Makefile +++ b/arch/arm/mach-mx3/Makefile @@ -5,7 +5,7 @@ # Object file lists. obj-y := mm.o devices.o cpu.o -obj-$(CONFIG_ARCH_MX31) += clock.o iomux.o +obj-$(CONFIG_ARCH_MX31) += clock-imx31.o iomux-imx31.o obj-$(CONFIG_ARCH_MX35) += clock-imx35.o obj-$(CONFIG_MACH_MX31ADS) += mx31ads.o obj-$(CONFIG_MACH_MX31LILLY) += mx31lilly.o mx31lilly-db.o diff --git a/arch/arm/mach-mx3/clock-imx31.c b/arch/arm/mach-mx3/clock-imx31.c new file mode 100644 index 0000000..cc03a61 --- /dev/null +++ b/arch/arm/mach-mx3/clock-imx31.c @@ -0,0 +1,632 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2008 by Sascha Hauer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "crm_regs.h" + +#define PRE_DIV_MIN_FREQ 10000000 /* Minimum Frequency after Predivider */ + +static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post) +{ + u32 min_pre, temp_pre, old_err, err; + + if (div >= 512) { + *pre = 8; + *post = 64; + } else if (div >= 64) { + min_pre = (div - 1) / 64 + 1; + old_err = 8; + for (temp_pre = 8; temp_pre >= min_pre; temp_pre--) { + err = div % temp_pre; + if (err == 0) { + *pre = temp_pre; + break; + } + err = temp_pre - err; + if (err < old_err) { + old_err = err; + *pre = temp_pre; + } + } + *post = (div + *pre - 1) / *pre; + } else if (div <= 8) { + *pre = div; + *post = 1; + } else { + *pre = 1; + *post = div; + } +} + +static struct clk mcu_pll_clk; +static struct clk serial_pll_clk; +static struct clk ipg_clk; +static struct clk ckih_clk; + +static int cgr_enable(struct clk *clk) +{ + u32 reg; + + if (!clk->enable_reg) + return 0; + + reg = __raw_readl(clk->enable_reg); + reg |= 3 << clk->enable_shift; + __raw_writel(reg, clk->enable_reg); + + return 0; +} + +static void cgr_disable(struct clk *clk) +{ + u32 reg; + + if (!clk->enable_reg) + return; + + reg = __raw_readl(clk->enable_reg); + reg &= ~(3 << clk->enable_shift); + + /* special case for EMI clock */ + if (clk->enable_reg == MXC_CCM_CGR2 && clk->enable_shift == 8) + reg |= (1 << clk->enable_shift); + + __raw_writel(reg, clk->enable_reg); +} + +static unsigned long pll_ref_get_rate(void) +{ + unsigned long ccmr; + unsigned int prcs; + + ccmr = __raw_readl(MXC_CCM_CCMR); + prcs = (ccmr & MXC_CCM_CCMR_PRCS_MASK) >> MXC_CCM_CCMR_PRCS_OFFSET; + if (prcs == 0x1) + return CKIL_CLK_FREQ * 1024; + else + return clk_get_rate(&ckih_clk); +} + +static unsigned long usb_pll_get_rate(struct clk *clk) +{ + unsigned long reg; + + reg = __raw_readl(MXC_CCM_UPCTL); + + return mxc_decode_pll(reg, pll_ref_get_rate()); +} + +static unsigned long serial_pll_get_rate(struct clk *clk) +{ + unsigned long reg; + + reg = __raw_readl(MXC_CCM_SRPCTL); + + return mxc_decode_pll(reg, pll_ref_get_rate()); +} + +static unsigned long mcu_pll_get_rate(struct clk *clk) +{ + unsigned long reg, ccmr; + + ccmr = __raw_readl(MXC_CCM_CCMR); + + if (!(ccmr & MXC_CCM_CCMR_MPE) || (ccmr & MXC_CCM_CCMR_MDS)) + return clk_get_rate(&ckih_clk); + + reg = __raw_readl(MXC_CCM_MPCTL); + + return mxc_decode_pll(reg, pll_ref_get_rate()); +} + +static int usb_pll_enable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CCMR); + reg |= MXC_CCM_CCMR_UPE; + __raw_writel(reg, MXC_CCM_CCMR); + + /* No lock bit on MX31, so using max time from spec */ + udelay(80); + + return 0; +} + +static void usb_pll_disable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CCMR); + reg &= ~MXC_CCM_CCMR_UPE; + __raw_writel(reg, MXC_CCM_CCMR); +} + +static int serial_pll_enable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CCMR); + reg |= MXC_CCM_CCMR_SPE; + __raw_writel(reg, MXC_CCM_CCMR); + + /* No lock bit on MX31, so using max time from spec */ + udelay(80); + + return 0; +} + +static void serial_pll_disable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CCMR); + reg &= ~MXC_CCM_CCMR_SPE; + __raw_writel(reg, MXC_CCM_CCMR); +} + +#define PDR0(mask, off) ((__raw_readl(MXC_CCM_PDR0) & mask) >> off) +#define PDR1(mask, off) ((__raw_readl(MXC_CCM_PDR1) & mask) >> off) +#define PDR2(mask, off) ((__raw_readl(MXC_CCM_PDR2) & mask) >> off) + +static unsigned long mcu_main_get_rate(struct clk *clk) +{ + u32 pmcr0 = __raw_readl(MXC_CCM_PMCR0); + + if ((pmcr0 & MXC_CCM_PMCR0_DFSUP1) == MXC_CCM_PMCR0_DFSUP1_SPLL) + return clk_get_rate(&serial_pll_clk); + else + return clk_get_rate(&mcu_pll_clk); +} + +static unsigned long ahb_get_rate(struct clk *clk) +{ + unsigned long max_pdf; + + max_pdf = PDR0(MXC_CCM_PDR0_MAX_PODF_MASK, + MXC_CCM_PDR0_MAX_PODF_OFFSET); + return clk_get_rate(clk->parent) / (max_pdf + 1); +} + +static unsigned long ipg_get_rate(struct clk *clk) +{ + unsigned long ipg_pdf; + + ipg_pdf = PDR0(MXC_CCM_PDR0_IPG_PODF_MASK, + MXC_CCM_PDR0_IPG_PODF_OFFSET); + return clk_get_rate(clk->parent) / (ipg_pdf + 1); +} + +static unsigned long nfc_get_rate(struct clk *clk) +{ + unsigned long nfc_pdf; + + nfc_pdf = PDR0(MXC_CCM_PDR0_NFC_PODF_MASK, + MXC_CCM_PDR0_NFC_PODF_OFFSET); + return clk_get_rate(clk->parent) / (nfc_pdf + 1); +} + +static unsigned long hsp_get_rate(struct clk *clk) +{ + unsigned long hsp_pdf; + + hsp_pdf = PDR0(MXC_CCM_PDR0_HSP_PODF_MASK, + MXC_CCM_PDR0_HSP_PODF_OFFSET); + return clk_get_rate(clk->parent) / (hsp_pdf + 1); +} + +static unsigned long usb_get_rate(struct clk *clk) +{ + unsigned long usb_pdf, usb_prepdf; + + usb_pdf = PDR1(MXC_CCM_PDR1_USB_PODF_MASK, + MXC_CCM_PDR1_USB_PODF_OFFSET); + usb_prepdf = PDR1(MXC_CCM_PDR1_USB_PRDF_MASK, + MXC_CCM_PDR1_USB_PRDF_OFFSET); + return clk_get_rate(clk->parent) / (usb_prepdf + 1) / (usb_pdf + 1); +} + +static unsigned long csi_get_rate(struct clk *clk) +{ + u32 reg, pre, post; + + reg = __raw_readl(MXC_CCM_PDR0); + pre = (reg & MXC_CCM_PDR0_CSI_PRDF_MASK) >> + MXC_CCM_PDR0_CSI_PRDF_OFFSET; + pre++; + post = (reg & MXC_CCM_PDR0_CSI_PODF_MASK) >> + MXC_CCM_PDR0_CSI_PODF_OFFSET; + post++; + return clk_get_rate(clk->parent) / (pre * post); +} + +static unsigned long csi_round_rate(struct clk *clk, unsigned long rate) +{ + u32 pre, post, parent = clk_get_rate(clk->parent); + u32 div = parent / rate; + + if (parent % rate) + div++; + + __calc_pre_post_dividers(div, &pre, &post); + + return parent / (pre * post); +} + +static int csi_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, div, pre, post, parent = clk_get_rate(clk->parent); + + div = parent / rate; + + if ((parent / div) != rate) + return -EINVAL; + + __calc_pre_post_dividers(div, &pre, &post); + + /* Set CSI clock divider */ + reg = __raw_readl(MXC_CCM_PDR0) & + ~(MXC_CCM_PDR0_CSI_PODF_MASK | MXC_CCM_PDR0_CSI_PRDF_MASK); + reg |= (post - 1) << MXC_CCM_PDR0_CSI_PODF_OFFSET; + reg |= (pre - 1) << MXC_CCM_PDR0_CSI_PRDF_OFFSET; + __raw_writel(reg, MXC_CCM_PDR0); + + return 0; +} + +static unsigned long ssi1_get_rate(struct clk *clk) +{ + unsigned long ssi1_pdf, ssi1_prepdf; + + ssi1_pdf = PDR1(MXC_CCM_PDR1_SSI1_PODF_MASK, + MXC_CCM_PDR1_SSI1_PODF_OFFSET); + ssi1_prepdf = PDR1(MXC_CCM_PDR1_SSI1_PRE_PODF_MASK, + MXC_CCM_PDR1_SSI1_PRE_PODF_OFFSET); + return clk_get_rate(clk->parent) / (ssi1_prepdf + 1) / (ssi1_pdf + 1); +} + +static unsigned long ssi2_get_rate(struct clk *clk) +{ + unsigned long ssi2_pdf, ssi2_prepdf; + + ssi2_pdf = PDR1(MXC_CCM_PDR1_SSI2_PODF_MASK, + MXC_CCM_PDR1_SSI2_PODF_OFFSET); + ssi2_prepdf = PDR1(MXC_CCM_PDR1_SSI2_PRE_PODF_MASK, + MXC_CCM_PDR1_SSI2_PRE_PODF_OFFSET); + return clk_get_rate(clk->parent) / (ssi2_prepdf + 1) / (ssi2_pdf + 1); +} + +static unsigned long firi_get_rate(struct clk *clk) +{ + unsigned long firi_pdf, firi_prepdf; + + firi_pdf = PDR1(MXC_CCM_PDR1_FIRI_PODF_MASK, + MXC_CCM_PDR1_FIRI_PODF_OFFSET); + firi_prepdf = PDR1(MXC_CCM_PDR1_FIRI_PRE_PODF_MASK, + MXC_CCM_PDR1_FIRI_PRE_PODF_OFFSET); + return clk_get_rate(clk->parent) / (firi_prepdf + 1) / (firi_pdf + 1); +} + +static unsigned long firi_round_rate(struct clk *clk, unsigned long rate) +{ + u32 pre, post; + u32 parent = clk_get_rate(clk->parent); + u32 div = parent / rate; + + if (parent % rate) + div++; + + __calc_pre_post_dividers(div, &pre, &post); + + return parent / (pre * post); + +} + +static int firi_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, div, pre, post, parent = clk_get_rate(clk->parent); + + div = parent / rate; + + if ((parent / div) != rate) + return -EINVAL; + + __calc_pre_post_dividers(div, &pre, &post); + + /* Set FIRI clock divider */ + reg = __raw_readl(MXC_CCM_PDR1) & + ~(MXC_CCM_PDR1_FIRI_PODF_MASK | MXC_CCM_PDR1_FIRI_PRE_PODF_MASK); + reg |= (pre - 1) << MXC_CCM_PDR1_FIRI_PRE_PODF_OFFSET; + reg |= (post - 1) << MXC_CCM_PDR1_FIRI_PODF_OFFSET; + __raw_writel(reg, MXC_CCM_PDR1); + + return 0; +} + +static unsigned long mbx_get_rate(struct clk *clk) +{ + return clk_get_rate(clk->parent) / 2; +} + +static unsigned long mstick1_get_rate(struct clk *clk) +{ + unsigned long msti_pdf; + + msti_pdf = PDR2(MXC_CCM_PDR2_MST1_PDF_MASK, + MXC_CCM_PDR2_MST1_PDF_OFFSET); + return clk_get_rate(clk->parent) / (msti_pdf + 1); +} + +static unsigned long mstick2_get_rate(struct clk *clk) +{ + unsigned long msti_pdf; + + msti_pdf = PDR2(MXC_CCM_PDR2_MST2_PDF_MASK, + MXC_CCM_PDR2_MST2_PDF_OFFSET); + return clk_get_rate(clk->parent) / (msti_pdf + 1); +} + +static unsigned long ckih_rate; + +static unsigned long clk_ckih_get_rate(struct clk *clk) +{ + return ckih_rate; +} + +static unsigned long clk_ckil_get_rate(struct clk *clk) +{ + return CKIL_CLK_FREQ; +} + +static struct clk ckih_clk = { + .get_rate = clk_ckih_get_rate, +}; + +static struct clk mcu_pll_clk = { + .parent = &ckih_clk, + .get_rate = mcu_pll_get_rate, +}; + +static struct clk mcu_main_clk = { + .parent = &mcu_pll_clk, + .get_rate = mcu_main_get_rate, +}; + +static struct clk serial_pll_clk = { + .parent = &ckih_clk, + .get_rate = serial_pll_get_rate, + .enable = serial_pll_enable, + .disable = serial_pll_disable, +}; + +static struct clk usb_pll_clk = { + .parent = &ckih_clk, + .get_rate = usb_pll_get_rate, + .enable = usb_pll_enable, + .disable = usb_pll_disable, +}; + +static struct clk ahb_clk = { + .parent = &mcu_main_clk, + .get_rate = ahb_get_rate, +}; + +#define DEFINE_CLOCK(name, i, er, es, gr, s, p) \ + static struct clk name = { \ + .id = i, \ + .enable_reg = er, \ + .enable_shift = es, \ + .get_rate = gr, \ + .enable = cgr_enable, \ + .disable = cgr_disable, \ + .secondary = s, \ + .parent = p, \ + } + +#define DEFINE_CLOCK1(name, i, er, es, getsetround, s, p) \ + static struct clk name = { \ + .id = i, \ + .enable_reg = er, \ + .enable_shift = es, \ + .get_rate = getsetround##_get_rate, \ + .set_rate = getsetround##_set_rate, \ + .round_rate = getsetround##_round_rate, \ + .enable = cgr_enable, \ + .disable = cgr_disable, \ + .secondary = s, \ + .parent = p, \ + } + +DEFINE_CLOCK(perclk_clk, 0, NULL, 0, NULL, NULL, &ipg_clk); + +DEFINE_CLOCK(sdhc1_clk, 0, MXC_CCM_CGR0, 0, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(sdhc2_clk, 1, MXC_CCM_CGR0, 2, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(gpt_clk, 0, MXC_CCM_CGR0, 4, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(epit1_clk, 0, MXC_CCM_CGR0, 6, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(epit2_clk, 1, MXC_CCM_CGR0, 8, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(iim_clk, 0, MXC_CCM_CGR0, 10, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(ata_clk, 0, MXC_CCM_CGR0, 12, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(sdma_clk1, 0, MXC_CCM_CGR0, 14, NULL, &sdma_clk1, &ahb_clk); +DEFINE_CLOCK(cspi3_clk, 2, MXC_CCM_CGR0, 16, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(rng_clk, 0, MXC_CCM_CGR0, 18, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(uart1_clk, 0, MXC_CCM_CGR0, 20, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(uart2_clk, 1, MXC_CCM_CGR0, 22, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(ssi1_clk, 0, MXC_CCM_CGR0, 24, ssi1_get_rate, NULL, &serial_pll_clk); +DEFINE_CLOCK(i2c1_clk, 0, MXC_CCM_CGR0, 26, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(i2c2_clk, 1, MXC_CCM_CGR0, 28, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(i2c3_clk, 2, MXC_CCM_CGR0, 30, NULL, NULL, &perclk_clk); + +DEFINE_CLOCK(mpeg4_clk, 0, MXC_CCM_CGR1, 0, NULL, NULL, &ahb_clk); +DEFINE_CLOCK(mstick1_clk, 0, MXC_CCM_CGR1, 2, mstick1_get_rate, NULL, &usb_pll_clk); +DEFINE_CLOCK(mstick2_clk, 1, MXC_CCM_CGR1, 4, mstick2_get_rate, NULL, &usb_pll_clk); +DEFINE_CLOCK1(csi_clk, 0, MXC_CCM_CGR1, 6, csi, NULL, &serial_pll_clk); +DEFINE_CLOCK(rtc_clk, 0, MXC_CCM_CGR1, 8, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(wdog_clk, 0, MXC_CCM_CGR1, 10, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(pwm_clk, 0, MXC_CCM_CGR1, 12, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(usb_clk2, 0, MXC_CCM_CGR1, 18, usb_get_rate, NULL, &ahb_clk); +DEFINE_CLOCK(kpp_clk, 0, MXC_CCM_CGR1, 20, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(ipu_clk, 0, MXC_CCM_CGR1, 22, hsp_get_rate, NULL, &mcu_main_clk); +DEFINE_CLOCK(uart3_clk, 2, MXC_CCM_CGR1, 24, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(uart4_clk, 3, MXC_CCM_CGR1, 26, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(uart5_clk, 4, MXC_CCM_CGR1, 28, NULL, NULL, &perclk_clk); +DEFINE_CLOCK(owire_clk, 0, MXC_CCM_CGR1, 30, NULL, NULL, &perclk_clk); + +DEFINE_CLOCK(ssi2_clk, 1, MXC_CCM_CGR2, 0, ssi2_get_rate, NULL, &serial_pll_clk); +DEFINE_CLOCK(cspi1_clk, 0, MXC_CCM_CGR2, 2, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(cspi2_clk, 1, MXC_CCM_CGR2, 4, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(mbx_clk, 0, MXC_CCM_CGR2, 6, mbx_get_rate, NULL, &ahb_clk); +DEFINE_CLOCK(emi_clk, 0, MXC_CCM_CGR2, 8, NULL, NULL, &ahb_clk); +DEFINE_CLOCK(rtic_clk, 0, MXC_CCM_CGR2, 10, NULL, NULL, &ahb_clk); +DEFINE_CLOCK1(firi_clk, 0, MXC_CCM_CGR2, 12, firi, NULL, &usb_pll_clk); + +DEFINE_CLOCK(sdma_clk2, 0, NULL, 0, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(usb_clk1, 0, NULL, 0, usb_get_rate, NULL, &usb_pll_clk); +DEFINE_CLOCK(nfc_clk, 0, NULL, 0, nfc_get_rate, NULL, &ahb_clk); +DEFINE_CLOCK(scc_clk, 0, NULL, 0, NULL, NULL, &ipg_clk); +DEFINE_CLOCK(ipg_clk, 0, NULL, 0, ipg_get_rate, NULL, &ahb_clk); +DEFINE_CLOCK(ckil_clk, 0, NULL, 0, clk_ckil_get_rate, NULL, NULL); + +#define _REGISTER_CLOCK(d, n, c) \ + { \ + .dev_id = d, \ + .con_id = n, \ + .clk = &c, \ + }, + +static struct clk_lookup lookups[] = { + _REGISTER_CLOCK(NULL, "emi", emi_clk) + _REGISTER_CLOCK("spi_imx.0", NULL, cspi1_clk) + _REGISTER_CLOCK("spi_imx.1", NULL, cspi2_clk) + _REGISTER_CLOCK("spi_imx.2", NULL, cspi3_clk) + _REGISTER_CLOCK(NULL, "gpt", gpt_clk) + _REGISTER_CLOCK(NULL, "pwm", pwm_clk) + _REGISTER_CLOCK("imx-wdt.0", NULL, wdog_clk) + _REGISTER_CLOCK(NULL, "rtc", rtc_clk) + _REGISTER_CLOCK(NULL, "epit", epit1_clk) + _REGISTER_CLOCK(NULL, "epit", epit2_clk) + _REGISTER_CLOCK("mxc_nand.0", NULL, nfc_clk) + _REGISTER_CLOCK("ipu-core", NULL, ipu_clk) + _REGISTER_CLOCK("mx3_sdc_fb", NULL, ipu_clk) + _REGISTER_CLOCK(NULL, "kpp", kpp_clk) + _REGISTER_CLOCK("mxc-ehci.0", "usb", usb_clk1) + _REGISTER_CLOCK("mxc-ehci.0", "usb_ahb", usb_clk2) + _REGISTER_CLOCK("mxc-ehci.1", "usb", usb_clk1) + _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", usb_clk2) + _REGISTER_CLOCK("mxc-ehci.2", "usb", usb_clk1) + _REGISTER_CLOCK("mxc-ehci.2", "usb_ahb", usb_clk2) + _REGISTER_CLOCK("fsl-usb2-udc", "usb", usb_clk1) + _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usb_clk2) + _REGISTER_CLOCK("mx3-camera.0", NULL, csi_clk) + _REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk) + _REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk) + _REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk) + _REGISTER_CLOCK("imx-uart.3", NULL, uart4_clk) + _REGISTER_CLOCK("imx-uart.4", NULL, uart5_clk) + _REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk) + _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk) + _REGISTER_CLOCK("imx-i2c.2", NULL, i2c3_clk) + _REGISTER_CLOCK("mxc_w1.0", NULL, owire_clk) + _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc1_clk) + _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc2_clk) + _REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk) + _REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk) + _REGISTER_CLOCK(NULL, "firi", firi_clk) + _REGISTER_CLOCK(NULL, "ata", ata_clk) + _REGISTER_CLOCK(NULL, "rtic", rtic_clk) + _REGISTER_CLOCK(NULL, "rng", rng_clk) + _REGISTER_CLOCK(NULL, "sdma_ahb", sdma_clk1) + _REGISTER_CLOCK(NULL, "sdma_ipg", sdma_clk2) + _REGISTER_CLOCK(NULL, "mstick", mstick1_clk) + _REGISTER_CLOCK(NULL, "mstick", mstick2_clk) + _REGISTER_CLOCK(NULL, "scc", scc_clk) + _REGISTER_CLOCK(NULL, "iim", iim_clk) + _REGISTER_CLOCK(NULL, "mpeg4", mpeg4_clk) + _REGISTER_CLOCK(NULL, "mbx", mbx_clk) + _REGISTER_CLOCK("mxc_rtc", NULL, ckil_clk) +}; + +int __init mx31_clocks_init(unsigned long fref) +{ + u32 reg; + int i; + + ckih_rate = fref; + + for (i = 0; i < ARRAY_SIZE(lookups); i++) + clkdev_add(&lookups[i]); + + /* change the csi_clk parent if necessary */ + reg = __raw_readl(MXC_CCM_CCMR); + if (!(reg & MXC_CCM_CCMR_CSCS)) + if (clk_set_parent(&csi_clk, &usb_pll_clk)) + pr_err("%s: error changing csi_clk parent\n", __func__); + + + /* Turn off all possible clocks */ + __raw_writel((3 << 4), MXC_CCM_CGR0); + __raw_writel(0, MXC_CCM_CGR1); + __raw_writel((3 << 8) | (3 << 14) | (3 << 16)| + 1 << 27 | 1 << 28, /* Bit 27 and 28 are not defined for + MX32, but still required to be set */ + MXC_CCM_CGR2); + + /* + * Before turning off usb_pll make sure ipg_per_clk is generated + * by ipg_clk and not usb_pll. + */ + __raw_writel(__raw_readl(MXC_CCM_CCMR) | (1 << 24), MXC_CCM_CCMR); + + usb_pll_disable(&usb_pll_clk); + + pr_info("Clock input source is %ld\n", clk_get_rate(&ckih_clk)); + + clk_enable(&gpt_clk); + clk_enable(&emi_clk); + clk_enable(&iim_clk); + + clk_enable(&serial_pll_clk); + + mx31_read_cpu_rev(); + + if (mx31_revision() >= MX31_CHIP_REV_2_0) { + reg = __raw_readl(MXC_CCM_PMCR1); + /* No PLL restart on DVFS switch; enable auto EMI handshake */ + reg |= MXC_CCM_PMCR1_PLLRDIS | MXC_CCM_PMCR1_EMIRQ_EN; + __raw_writel(reg, MXC_CCM_PMCR1); + } + + mxc_timer_init(&ipg_clk, IO_ADDRESS(MX31_GPT1_BASE_ADDR), MX31_INT_GPT); + + return 0; +} + diff --git a/arch/arm/mach-mx3/clock.c b/arch/arm/mach-mx3/clock.c deleted file mode 100644 index cc03a61..0000000 --- a/arch/arm/mach-mx3/clock.c +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2008 by Sascha Hauer - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include "crm_regs.h" - -#define PRE_DIV_MIN_FREQ 10000000 /* Minimum Frequency after Predivider */ - -static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post) -{ - u32 min_pre, temp_pre, old_err, err; - - if (div >= 512) { - *pre = 8; - *post = 64; - } else if (div >= 64) { - min_pre = (div - 1) / 64 + 1; - old_err = 8; - for (temp_pre = 8; temp_pre >= min_pre; temp_pre--) { - err = div % temp_pre; - if (err == 0) { - *pre = temp_pre; - break; - } - err = temp_pre - err; - if (err < old_err) { - old_err = err; - *pre = temp_pre; - } - } - *post = (div + *pre - 1) / *pre; - } else if (div <= 8) { - *pre = div; - *post = 1; - } else { - *pre = 1; - *post = div; - } -} - -static struct clk mcu_pll_clk; -static struct clk serial_pll_clk; -static struct clk ipg_clk; -static struct clk ckih_clk; - -static int cgr_enable(struct clk *clk) -{ - u32 reg; - - if (!clk->enable_reg) - return 0; - - reg = __raw_readl(clk->enable_reg); - reg |= 3 << clk->enable_shift; - __raw_writel(reg, clk->enable_reg); - - return 0; -} - -static void cgr_disable(struct clk *clk) -{ - u32 reg; - - if (!clk->enable_reg) - return; - - reg = __raw_readl(clk->enable_reg); - reg &= ~(3 << clk->enable_shift); - - /* special case for EMI clock */ - if (clk->enable_reg == MXC_CCM_CGR2 && clk->enable_shift == 8) - reg |= (1 << clk->enable_shift); - - __raw_writel(reg, clk->enable_reg); -} - -static unsigned long pll_ref_get_rate(void) -{ - unsigned long ccmr; - unsigned int prcs; - - ccmr = __raw_readl(MXC_CCM_CCMR); - prcs = (ccmr & MXC_CCM_CCMR_PRCS_MASK) >> MXC_CCM_CCMR_PRCS_OFFSET; - if (prcs == 0x1) - return CKIL_CLK_FREQ * 1024; - else - return clk_get_rate(&ckih_clk); -} - -static unsigned long usb_pll_get_rate(struct clk *clk) -{ - unsigned long reg; - - reg = __raw_readl(MXC_CCM_UPCTL); - - return mxc_decode_pll(reg, pll_ref_get_rate()); -} - -static unsigned long serial_pll_get_rate(struct clk *clk) -{ - unsigned long reg; - - reg = __raw_readl(MXC_CCM_SRPCTL); - - return mxc_decode_pll(reg, pll_ref_get_rate()); -} - -static unsigned long mcu_pll_get_rate(struct clk *clk) -{ - unsigned long reg, ccmr; - - ccmr = __raw_readl(MXC_CCM_CCMR); - - if (!(ccmr & MXC_CCM_CCMR_MPE) || (ccmr & MXC_CCM_CCMR_MDS)) - return clk_get_rate(&ckih_clk); - - reg = __raw_readl(MXC_CCM_MPCTL); - - return mxc_decode_pll(reg, pll_ref_get_rate()); -} - -static int usb_pll_enable(struct clk *clk) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CCMR); - reg |= MXC_CCM_CCMR_UPE; - __raw_writel(reg, MXC_CCM_CCMR); - - /* No lock bit on MX31, so using max time from spec */ - udelay(80); - - return 0; -} - -static void usb_pll_disable(struct clk *clk) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CCMR); - reg &= ~MXC_CCM_CCMR_UPE; - __raw_writel(reg, MXC_CCM_CCMR); -} - -static int serial_pll_enable(struct clk *clk) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CCMR); - reg |= MXC_CCM_CCMR_SPE; - __raw_writel(reg, MXC_CCM_CCMR); - - /* No lock bit on MX31, so using max time from spec */ - udelay(80); - - return 0; -} - -static void serial_pll_disable(struct clk *clk) -{ - u32 reg; - - reg = __raw_readl(MXC_CCM_CCMR); - reg &= ~MXC_CCM_CCMR_SPE; - __raw_writel(reg, MXC_CCM_CCMR); -} - -#define PDR0(mask, off) ((__raw_readl(MXC_CCM_PDR0) & mask) >> off) -#define PDR1(mask, off) ((__raw_readl(MXC_CCM_PDR1) & mask) >> off) -#define PDR2(mask, off) ((__raw_readl(MXC_CCM_PDR2) & mask) >> off) - -static unsigned long mcu_main_get_rate(struct clk *clk) -{ - u32 pmcr0 = __raw_readl(MXC_CCM_PMCR0); - - if ((pmcr0 & MXC_CCM_PMCR0_DFSUP1) == MXC_CCM_PMCR0_DFSUP1_SPLL) - return clk_get_rate(&serial_pll_clk); - else - return clk_get_rate(&mcu_pll_clk); -} - -static unsigned long ahb_get_rate(struct clk *clk) -{ - unsigned long max_pdf; - - max_pdf = PDR0(MXC_CCM_PDR0_MAX_PODF_MASK, - MXC_CCM_PDR0_MAX_PODF_OFFSET); - return clk_get_rate(clk->parent) / (max_pdf + 1); -} - -static unsigned long ipg_get_rate(struct clk *clk) -{ - unsigned long ipg_pdf; - - ipg_pdf = PDR0(MXC_CCM_PDR0_IPG_PODF_MASK, - MXC_CCM_PDR0_IPG_PODF_OFFSET); - return clk_get_rate(clk->parent) / (ipg_pdf + 1); -} - -static unsigned long nfc_get_rate(struct clk *clk) -{ - unsigned long nfc_pdf; - - nfc_pdf = PDR0(MXC_CCM_PDR0_NFC_PODF_MASK, - MXC_CCM_PDR0_NFC_PODF_OFFSET); - return clk_get_rate(clk->parent) / (nfc_pdf + 1); -} - -static unsigned long hsp_get_rate(struct clk *clk) -{ - unsigned long hsp_pdf; - - hsp_pdf = PDR0(MXC_CCM_PDR0_HSP_PODF_MASK, - MXC_CCM_PDR0_HSP_PODF_OFFSET); - return clk_get_rate(clk->parent) / (hsp_pdf + 1); -} - -static unsigned long usb_get_rate(struct clk *clk) -{ - unsigned long usb_pdf, usb_prepdf; - - usb_pdf = PDR1(MXC_CCM_PDR1_USB_PODF_MASK, - MXC_CCM_PDR1_USB_PODF_OFFSET); - usb_prepdf = PDR1(MXC_CCM_PDR1_USB_PRDF_MASK, - MXC_CCM_PDR1_USB_PRDF_OFFSET); - return clk_get_rate(clk->parent) / (usb_prepdf + 1) / (usb_pdf + 1); -} - -static unsigned long csi_get_rate(struct clk *clk) -{ - u32 reg, pre, post; - - reg = __raw_readl(MXC_CCM_PDR0); - pre = (reg & MXC_CCM_PDR0_CSI_PRDF_MASK) >> - MXC_CCM_PDR0_CSI_PRDF_OFFSET; - pre++; - post = (reg & MXC_CCM_PDR0_CSI_PODF_MASK) >> - MXC_CCM_PDR0_CSI_PODF_OFFSET; - post++; - return clk_get_rate(clk->parent) / (pre * post); -} - -static unsigned long csi_round_rate(struct clk *clk, unsigned long rate) -{ - u32 pre, post, parent = clk_get_rate(clk->parent); - u32 div = parent / rate; - - if (parent % rate) - div++; - - __calc_pre_post_dividers(div, &pre, &post); - - return parent / (pre * post); -} - -static int csi_set_rate(struct clk *clk, unsigned long rate) -{ - u32 reg, div, pre, post, parent = clk_get_rate(clk->parent); - - div = parent / rate; - - if ((parent / div) != rate) - return -EINVAL; - - __calc_pre_post_dividers(div, &pre, &post); - - /* Set CSI clock divider */ - reg = __raw_readl(MXC_CCM_PDR0) & - ~(MXC_CCM_PDR0_CSI_PODF_MASK | MXC_CCM_PDR0_CSI_PRDF_MASK); - reg |= (post - 1) << MXC_CCM_PDR0_CSI_PODF_OFFSET; - reg |= (pre - 1) << MXC_CCM_PDR0_CSI_PRDF_OFFSET; - __raw_writel(reg, MXC_CCM_PDR0); - - return 0; -} - -static unsigned long ssi1_get_rate(struct clk *clk) -{ - unsigned long ssi1_pdf, ssi1_prepdf; - - ssi1_pdf = PDR1(MXC_CCM_PDR1_SSI1_PODF_MASK, - MXC_CCM_PDR1_SSI1_PODF_OFFSET); - ssi1_prepdf = PDR1(MXC_CCM_PDR1_SSI1_PRE_PODF_MASK, - MXC_CCM_PDR1_SSI1_PRE_PODF_OFFSET); - return clk_get_rate(clk->parent) / (ssi1_prepdf + 1) / (ssi1_pdf + 1); -} - -static unsigned long ssi2_get_rate(struct clk *clk) -{ - unsigned long ssi2_pdf, ssi2_prepdf; - - ssi2_pdf = PDR1(MXC_CCM_PDR1_SSI2_PODF_MASK, - MXC_CCM_PDR1_SSI2_PODF_OFFSET); - ssi2_prepdf = PDR1(MXC_CCM_PDR1_SSI2_PRE_PODF_MASK, - MXC_CCM_PDR1_SSI2_PRE_PODF_OFFSET); - return clk_get_rate(clk->parent) / (ssi2_prepdf + 1) / (ssi2_pdf + 1); -} - -static unsigned long firi_get_rate(struct clk *clk) -{ - unsigned long firi_pdf, firi_prepdf; - - firi_pdf = PDR1(MXC_CCM_PDR1_FIRI_PODF_MASK, - MXC_CCM_PDR1_FIRI_PODF_OFFSET); - firi_prepdf = PDR1(MXC_CCM_PDR1_FIRI_PRE_PODF_MASK, - MXC_CCM_PDR1_FIRI_PRE_PODF_OFFSET); - return clk_get_rate(clk->parent) / (firi_prepdf + 1) / (firi_pdf + 1); -} - -static unsigned long firi_round_rate(struct clk *clk, unsigned long rate) -{ - u32 pre, post; - u32 parent = clk_get_rate(clk->parent); - u32 div = parent / rate; - - if (parent % rate) - div++; - - __calc_pre_post_dividers(div, &pre, &post); - - return parent / (pre * post); - -} - -static int firi_set_rate(struct clk *clk, unsigned long rate) -{ - u32 reg, div, pre, post, parent = clk_get_rate(clk->parent); - - div = parent / rate; - - if ((parent / div) != rate) - return -EINVAL; - - __calc_pre_post_dividers(div, &pre, &post); - - /* Set FIRI clock divider */ - reg = __raw_readl(MXC_CCM_PDR1) & - ~(MXC_CCM_PDR1_FIRI_PODF_MASK | MXC_CCM_PDR1_FIRI_PRE_PODF_MASK); - reg |= (pre - 1) << MXC_CCM_PDR1_FIRI_PRE_PODF_OFFSET; - reg |= (post - 1) << MXC_CCM_PDR1_FIRI_PODF_OFFSET; - __raw_writel(reg, MXC_CCM_PDR1); - - return 0; -} - -static unsigned long mbx_get_rate(struct clk *clk) -{ - return clk_get_rate(clk->parent) / 2; -} - -static unsigned long mstick1_get_rate(struct clk *clk) -{ - unsigned long msti_pdf; - - msti_pdf = PDR2(MXC_CCM_PDR2_MST1_PDF_MASK, - MXC_CCM_PDR2_MST1_PDF_OFFSET); - return clk_get_rate(clk->parent) / (msti_pdf + 1); -} - -static unsigned long mstick2_get_rate(struct clk *clk) -{ - unsigned long msti_pdf; - - msti_pdf = PDR2(MXC_CCM_PDR2_MST2_PDF_MASK, - MXC_CCM_PDR2_MST2_PDF_OFFSET); - return clk_get_rate(clk->parent) / (msti_pdf + 1); -} - -static unsigned long ckih_rate; - -static unsigned long clk_ckih_get_rate(struct clk *clk) -{ - return ckih_rate; -} - -static unsigned long clk_ckil_get_rate(struct clk *clk) -{ - return CKIL_CLK_FREQ; -} - -static struct clk ckih_clk = { - .get_rate = clk_ckih_get_rate, -}; - -static struct clk mcu_pll_clk = { - .parent = &ckih_clk, - .get_rate = mcu_pll_get_rate, -}; - -static struct clk mcu_main_clk = { - .parent = &mcu_pll_clk, - .get_rate = mcu_main_get_rate, -}; - -static struct clk serial_pll_clk = { - .parent = &ckih_clk, - .get_rate = serial_pll_get_rate, - .enable = serial_pll_enable, - .disable = serial_pll_disable, -}; - -static struct clk usb_pll_clk = { - .parent = &ckih_clk, - .get_rate = usb_pll_get_rate, - .enable = usb_pll_enable, - .disable = usb_pll_disable, -}; - -static struct clk ahb_clk = { - .parent = &mcu_main_clk, - .get_rate = ahb_get_rate, -}; - -#define DEFINE_CLOCK(name, i, er, es, gr, s, p) \ - static struct clk name = { \ - .id = i, \ - .enable_reg = er, \ - .enable_shift = es, \ - .get_rate = gr, \ - .enable = cgr_enable, \ - .disable = cgr_disable, \ - .secondary = s, \ - .parent = p, \ - } - -#define DEFINE_CLOCK1(name, i, er, es, getsetround, s, p) \ - static struct clk name = { \ - .id = i, \ - .enable_reg = er, \ - .enable_shift = es, \ - .get_rate = getsetround##_get_rate, \ - .set_rate = getsetround##_set_rate, \ - .round_rate = getsetround##_round_rate, \ - .enable = cgr_enable, \ - .disable = cgr_disable, \ - .secondary = s, \ - .parent = p, \ - } - -DEFINE_CLOCK(perclk_clk, 0, NULL, 0, NULL, NULL, &ipg_clk); - -DEFINE_CLOCK(sdhc1_clk, 0, MXC_CCM_CGR0, 0, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(sdhc2_clk, 1, MXC_CCM_CGR0, 2, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(gpt_clk, 0, MXC_CCM_CGR0, 4, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(epit1_clk, 0, MXC_CCM_CGR0, 6, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(epit2_clk, 1, MXC_CCM_CGR0, 8, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(iim_clk, 0, MXC_CCM_CGR0, 10, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(ata_clk, 0, MXC_CCM_CGR0, 12, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(sdma_clk1, 0, MXC_CCM_CGR0, 14, NULL, &sdma_clk1, &ahb_clk); -DEFINE_CLOCK(cspi3_clk, 2, MXC_CCM_CGR0, 16, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(rng_clk, 0, MXC_CCM_CGR0, 18, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(uart1_clk, 0, MXC_CCM_CGR0, 20, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(uart2_clk, 1, MXC_CCM_CGR0, 22, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(ssi1_clk, 0, MXC_CCM_CGR0, 24, ssi1_get_rate, NULL, &serial_pll_clk); -DEFINE_CLOCK(i2c1_clk, 0, MXC_CCM_CGR0, 26, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(i2c2_clk, 1, MXC_CCM_CGR0, 28, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(i2c3_clk, 2, MXC_CCM_CGR0, 30, NULL, NULL, &perclk_clk); - -DEFINE_CLOCK(mpeg4_clk, 0, MXC_CCM_CGR1, 0, NULL, NULL, &ahb_clk); -DEFINE_CLOCK(mstick1_clk, 0, MXC_CCM_CGR1, 2, mstick1_get_rate, NULL, &usb_pll_clk); -DEFINE_CLOCK(mstick2_clk, 1, MXC_CCM_CGR1, 4, mstick2_get_rate, NULL, &usb_pll_clk); -DEFINE_CLOCK1(csi_clk, 0, MXC_CCM_CGR1, 6, csi, NULL, &serial_pll_clk); -DEFINE_CLOCK(rtc_clk, 0, MXC_CCM_CGR1, 8, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(wdog_clk, 0, MXC_CCM_CGR1, 10, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(pwm_clk, 0, MXC_CCM_CGR1, 12, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(usb_clk2, 0, MXC_CCM_CGR1, 18, usb_get_rate, NULL, &ahb_clk); -DEFINE_CLOCK(kpp_clk, 0, MXC_CCM_CGR1, 20, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(ipu_clk, 0, MXC_CCM_CGR1, 22, hsp_get_rate, NULL, &mcu_main_clk); -DEFINE_CLOCK(uart3_clk, 2, MXC_CCM_CGR1, 24, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(uart4_clk, 3, MXC_CCM_CGR1, 26, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(uart5_clk, 4, MXC_CCM_CGR1, 28, NULL, NULL, &perclk_clk); -DEFINE_CLOCK(owire_clk, 0, MXC_CCM_CGR1, 30, NULL, NULL, &perclk_clk); - -DEFINE_CLOCK(ssi2_clk, 1, MXC_CCM_CGR2, 0, ssi2_get_rate, NULL, &serial_pll_clk); -DEFINE_CLOCK(cspi1_clk, 0, MXC_CCM_CGR2, 2, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(cspi2_clk, 1, MXC_CCM_CGR2, 4, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(mbx_clk, 0, MXC_CCM_CGR2, 6, mbx_get_rate, NULL, &ahb_clk); -DEFINE_CLOCK(emi_clk, 0, MXC_CCM_CGR2, 8, NULL, NULL, &ahb_clk); -DEFINE_CLOCK(rtic_clk, 0, MXC_CCM_CGR2, 10, NULL, NULL, &ahb_clk); -DEFINE_CLOCK1(firi_clk, 0, MXC_CCM_CGR2, 12, firi, NULL, &usb_pll_clk); - -DEFINE_CLOCK(sdma_clk2, 0, NULL, 0, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(usb_clk1, 0, NULL, 0, usb_get_rate, NULL, &usb_pll_clk); -DEFINE_CLOCK(nfc_clk, 0, NULL, 0, nfc_get_rate, NULL, &ahb_clk); -DEFINE_CLOCK(scc_clk, 0, NULL, 0, NULL, NULL, &ipg_clk); -DEFINE_CLOCK(ipg_clk, 0, NULL, 0, ipg_get_rate, NULL, &ahb_clk); -DEFINE_CLOCK(ckil_clk, 0, NULL, 0, clk_ckil_get_rate, NULL, NULL); - -#define _REGISTER_CLOCK(d, n, c) \ - { \ - .dev_id = d, \ - .con_id = n, \ - .clk = &c, \ - }, - -static struct clk_lookup lookups[] = { - _REGISTER_CLOCK(NULL, "emi", emi_clk) - _REGISTER_CLOCK("spi_imx.0", NULL, cspi1_clk) - _REGISTER_CLOCK("spi_imx.1", NULL, cspi2_clk) - _REGISTER_CLOCK("spi_imx.2", NULL, cspi3_clk) - _REGISTER_CLOCK(NULL, "gpt", gpt_clk) - _REGISTER_CLOCK(NULL, "pwm", pwm_clk) - _REGISTER_CLOCK("imx-wdt.0", NULL, wdog_clk) - _REGISTER_CLOCK(NULL, "rtc", rtc_clk) - _REGISTER_CLOCK(NULL, "epit", epit1_clk) - _REGISTER_CLOCK(NULL, "epit", epit2_clk) - _REGISTER_CLOCK("mxc_nand.0", NULL, nfc_clk) - _REGISTER_CLOCK("ipu-core", NULL, ipu_clk) - _REGISTER_CLOCK("mx3_sdc_fb", NULL, ipu_clk) - _REGISTER_CLOCK(NULL, "kpp", kpp_clk) - _REGISTER_CLOCK("mxc-ehci.0", "usb", usb_clk1) - _REGISTER_CLOCK("mxc-ehci.0", "usb_ahb", usb_clk2) - _REGISTER_CLOCK("mxc-ehci.1", "usb", usb_clk1) - _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", usb_clk2) - _REGISTER_CLOCK("mxc-ehci.2", "usb", usb_clk1) - _REGISTER_CLOCK("mxc-ehci.2", "usb_ahb", usb_clk2) - _REGISTER_CLOCK("fsl-usb2-udc", "usb", usb_clk1) - _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usb_clk2) - _REGISTER_CLOCK("mx3-camera.0", NULL, csi_clk) - _REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk) - _REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk) - _REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk) - _REGISTER_CLOCK("imx-uart.3", NULL, uart4_clk) - _REGISTER_CLOCK("imx-uart.4", NULL, uart5_clk) - _REGISTER_CLOCK("imx-i2c.0", NULL, i2c1_clk) - _REGISTER_CLOCK("imx-i2c.1", NULL, i2c2_clk) - _REGISTER_CLOCK("imx-i2c.2", NULL, i2c3_clk) - _REGISTER_CLOCK("mxc_w1.0", NULL, owire_clk) - _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc1_clk) - _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc2_clk) - _REGISTER_CLOCK("imx-ssi.0", NULL, ssi1_clk) - _REGISTER_CLOCK("imx-ssi.1", NULL, ssi2_clk) - _REGISTER_CLOCK(NULL, "firi", firi_clk) - _REGISTER_CLOCK(NULL, "ata", ata_clk) - _REGISTER_CLOCK(NULL, "rtic", rtic_clk) - _REGISTER_CLOCK(NULL, "rng", rng_clk) - _REGISTER_CLOCK(NULL, "sdma_ahb", sdma_clk1) - _REGISTER_CLOCK(NULL, "sdma_ipg", sdma_clk2) - _REGISTER_CLOCK(NULL, "mstick", mstick1_clk) - _REGISTER_CLOCK(NULL, "mstick", mstick2_clk) - _REGISTER_CLOCK(NULL, "scc", scc_clk) - _REGISTER_CLOCK(NULL, "iim", iim_clk) - _REGISTER_CLOCK(NULL, "mpeg4", mpeg4_clk) - _REGISTER_CLOCK(NULL, "mbx", mbx_clk) - _REGISTER_CLOCK("mxc_rtc", NULL, ckil_clk) -}; - -int __init mx31_clocks_init(unsigned long fref) -{ - u32 reg; - int i; - - ckih_rate = fref; - - for (i = 0; i < ARRAY_SIZE(lookups); i++) - clkdev_add(&lookups[i]); - - /* change the csi_clk parent if necessary */ - reg = __raw_readl(MXC_CCM_CCMR); - if (!(reg & MXC_CCM_CCMR_CSCS)) - if (clk_set_parent(&csi_clk, &usb_pll_clk)) - pr_err("%s: error changing csi_clk parent\n", __func__); - - - /* Turn off all possible clocks */ - __raw_writel((3 << 4), MXC_CCM_CGR0); - __raw_writel(0, MXC_CCM_CGR1); - __raw_writel((3 << 8) | (3 << 14) | (3 << 16)| - 1 << 27 | 1 << 28, /* Bit 27 and 28 are not defined for - MX32, but still required to be set */ - MXC_CCM_CGR2); - - /* - * Before turning off usb_pll make sure ipg_per_clk is generated - * by ipg_clk and not usb_pll. - */ - __raw_writel(__raw_readl(MXC_CCM_CCMR) | (1 << 24), MXC_CCM_CCMR); - - usb_pll_disable(&usb_pll_clk); - - pr_info("Clock input source is %ld\n", clk_get_rate(&ckih_clk)); - - clk_enable(&gpt_clk); - clk_enable(&emi_clk); - clk_enable(&iim_clk); - - clk_enable(&serial_pll_clk); - - mx31_read_cpu_rev(); - - if (mx31_revision() >= MX31_CHIP_REV_2_0) { - reg = __raw_readl(MXC_CCM_PMCR1); - /* No PLL restart on DVFS switch; enable auto EMI handshake */ - reg |= MXC_CCM_PMCR1_PLLRDIS | MXC_CCM_PMCR1_EMIRQ_EN; - __raw_writel(reg, MXC_CCM_PMCR1); - } - - mxc_timer_init(&ipg_clk, IO_ADDRESS(MX31_GPT1_BASE_ADDR), MX31_INT_GPT); - - return 0; -} - diff --git a/arch/arm/mach-mx3/iomux-imx31.c b/arch/arm/mach-mx3/iomux-imx31.c new file mode 100644 index 0000000..6381e56 --- /dev/null +++ b/arch/arm/mach-mx3/iomux-imx31.c @@ -0,0 +1,181 @@ +/* + * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2008 by Sascha Hauer + * Copyright (C) 2009 by Valentin Longchamp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * IOMUX register (base) addresses + */ +#define IOMUX_BASE IO_ADDRESS(MX31_IOMUXC_BASE_ADDR) +#define IOMUXINT_OBS1 (IOMUX_BASE + 0x000) +#define IOMUXINT_OBS2 (IOMUX_BASE + 0x004) +#define IOMUXGPR (IOMUX_BASE + 0x008) +#define IOMUXSW_MUX_CTL (IOMUX_BASE + 0x00C) +#define IOMUXSW_PAD_CTL (IOMUX_BASE + 0x154) + +static DEFINE_SPINLOCK(gpio_mux_lock); + +#define IOMUX_REG_MASK (IOMUX_PADNUM_MASK & ~0x3) + +unsigned long mxc_pin_alloc_map[NB_PORTS * 32 / BITS_PER_LONG]; +/* + * set the mode for a IOMUX pin. + */ +int mxc_iomux_mode(unsigned int pin_mode) +{ + u32 field, l, mode, ret = 0; + void __iomem *reg; + + reg = IOMUXSW_MUX_CTL + (pin_mode & IOMUX_REG_MASK); + field = pin_mode & 0x3; + mode = (pin_mode & IOMUX_MODE_MASK) >> IOMUX_MODE_SHIFT; + + spin_lock(&gpio_mux_lock); + + l = __raw_readl(reg); + l &= ~(0xff << (field * 8)); + l |= mode << (field * 8); + __raw_writel(l, reg); + + spin_unlock(&gpio_mux_lock); + + return ret; +} +EXPORT_SYMBOL(mxc_iomux_mode); + +/* + * This function configures the pad value for a IOMUX pin. + */ +void mxc_iomux_set_pad(enum iomux_pins pin, u32 config) +{ + u32 field, l; + void __iomem *reg; + + pin &= IOMUX_PADNUM_MASK; + reg = IOMUXSW_PAD_CTL + (pin + 2) / 3 * 4; + field = (pin + 2) % 3; + + pr_debug("%s: reg offset = 0x%x, field = %d\n", + __func__, (pin + 2) / 3, field); + + spin_lock(&gpio_mux_lock); + + l = __raw_readl(reg); + l &= ~(0x1ff << (field * 10)); + l |= config << (field * 10); + __raw_writel(l, reg); + + spin_unlock(&gpio_mux_lock); +} +EXPORT_SYMBOL(mxc_iomux_set_pad); + +/* + * allocs a single pin: + * - reserves the pin so that it is not claimed by another driver + * - setups the iomux according to the configuration + */ +int mxc_iomux_alloc_pin(const unsigned int pin, const char *label) +{ + unsigned pad = pin & IOMUX_PADNUM_MASK; + + if (pad >= (PIN_MAX + 1)) { + printk(KERN_ERR "mxc_iomux: Attempt to request nonexistant pin %u for \"%s\"\n", + pad, label ? label : "?"); + return -EINVAL; + } + + if (test_and_set_bit(pad, mxc_pin_alloc_map)) { + printk(KERN_ERR "mxc_iomux: pin %u already used. Allocation for \"%s\" failed\n", + pad, label ? label : "?"); + return -EBUSY; + } + mxc_iomux_mode(pin); + + return 0; +} +EXPORT_SYMBOL(mxc_iomux_alloc_pin); + +int mxc_iomux_setup_multiple_pins(unsigned int *pin_list, unsigned count, + const char *label) +{ + unsigned int *p = pin_list; + int i; + int ret = -EINVAL; + + for (i = 0; i < count; i++) { + ret = mxc_iomux_alloc_pin(*p, label); + if (ret) + goto setup_error; + p++; + } + return 0; + +setup_error: + mxc_iomux_release_multiple_pins(pin_list, i); + return ret; +} +EXPORT_SYMBOL(mxc_iomux_setup_multiple_pins); + +void mxc_iomux_release_pin(const unsigned int pin) +{ + unsigned pad = pin & IOMUX_PADNUM_MASK; + + if (pad < (PIN_MAX + 1)) + clear_bit(pad, mxc_pin_alloc_map); +} +EXPORT_SYMBOL(mxc_iomux_release_pin); + +void mxc_iomux_release_multiple_pins(unsigned int *pin_list, int count) +{ + unsigned int *p = pin_list; + int i; + + for (i = 0; i < count; i++) { + mxc_iomux_release_pin(*p); + p++; + } +} +EXPORT_SYMBOL(mxc_iomux_release_multiple_pins); + +/* + * This function enables/disables the general purpose function for a particular + * signal. + */ +void mxc_iomux_set_gpr(enum iomux_gp_func gp, bool en) +{ + u32 l; + + spin_lock(&gpio_mux_lock); + l = __raw_readl(IOMUXGPR); + if (en) + l |= gp; + else + l &= ~gp; + + __raw_writel(l, IOMUXGPR); + spin_unlock(&gpio_mux_lock); +} +EXPORT_SYMBOL(mxc_iomux_set_gpr); diff --git a/arch/arm/mach-mx3/iomux.c b/arch/arm/mach-mx3/iomux.c deleted file mode 100644 index 6381e56..0000000 --- a/arch/arm/mach-mx3/iomux.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2008 by Sascha Hauer - * Copyright (C) 2009 by Valentin Longchamp - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * IOMUX register (base) addresses - */ -#define IOMUX_BASE IO_ADDRESS(MX31_IOMUXC_BASE_ADDR) -#define IOMUXINT_OBS1 (IOMUX_BASE + 0x000) -#define IOMUXINT_OBS2 (IOMUX_BASE + 0x004) -#define IOMUXGPR (IOMUX_BASE + 0x008) -#define IOMUXSW_MUX_CTL (IOMUX_BASE + 0x00C) -#define IOMUXSW_PAD_CTL (IOMUX_BASE + 0x154) - -static DEFINE_SPINLOCK(gpio_mux_lock); - -#define IOMUX_REG_MASK (IOMUX_PADNUM_MASK & ~0x3) - -unsigned long mxc_pin_alloc_map[NB_PORTS * 32 / BITS_PER_LONG]; -/* - * set the mode for a IOMUX pin. - */ -int mxc_iomux_mode(unsigned int pin_mode) -{ - u32 field, l, mode, ret = 0; - void __iomem *reg; - - reg = IOMUXSW_MUX_CTL + (pin_mode & IOMUX_REG_MASK); - field = pin_mode & 0x3; - mode = (pin_mode & IOMUX_MODE_MASK) >> IOMUX_MODE_SHIFT; - - spin_lock(&gpio_mux_lock); - - l = __raw_readl(reg); - l &= ~(0xff << (field * 8)); - l |= mode << (field * 8); - __raw_writel(l, reg); - - spin_unlock(&gpio_mux_lock); - - return ret; -} -EXPORT_SYMBOL(mxc_iomux_mode); - -/* - * This function configures the pad value for a IOMUX pin. - */ -void mxc_iomux_set_pad(enum iomux_pins pin, u32 config) -{ - u32 field, l; - void __iomem *reg; - - pin &= IOMUX_PADNUM_MASK; - reg = IOMUXSW_PAD_CTL + (pin + 2) / 3 * 4; - field = (pin + 2) % 3; - - pr_debug("%s: reg offset = 0x%x, field = %d\n", - __func__, (pin + 2) / 3, field); - - spin_lock(&gpio_mux_lock); - - l = __raw_readl(reg); - l &= ~(0x1ff << (field * 10)); - l |= config << (field * 10); - __raw_writel(l, reg); - - spin_unlock(&gpio_mux_lock); -} -EXPORT_SYMBOL(mxc_iomux_set_pad); - -/* - * allocs a single pin: - * - reserves the pin so that it is not claimed by another driver - * - setups the iomux according to the configuration - */ -int mxc_iomux_alloc_pin(const unsigned int pin, const char *label) -{ - unsigned pad = pin & IOMUX_PADNUM_MASK; - - if (pad >= (PIN_MAX + 1)) { - printk(KERN_ERR "mxc_iomux: Attempt to request nonexistant pin %u for \"%s\"\n", - pad, label ? label : "?"); - return -EINVAL; - } - - if (test_and_set_bit(pad, mxc_pin_alloc_map)) { - printk(KERN_ERR "mxc_iomux: pin %u already used. Allocation for \"%s\" failed\n", - pad, label ? label : "?"); - return -EBUSY; - } - mxc_iomux_mode(pin); - - return 0; -} -EXPORT_SYMBOL(mxc_iomux_alloc_pin); - -int mxc_iomux_setup_multiple_pins(unsigned int *pin_list, unsigned count, - const char *label) -{ - unsigned int *p = pin_list; - int i; - int ret = -EINVAL; - - for (i = 0; i < count; i++) { - ret = mxc_iomux_alloc_pin(*p, label); - if (ret) - goto setup_error; - p++; - } - return 0; - -setup_error: - mxc_iomux_release_multiple_pins(pin_list, i); - return ret; -} -EXPORT_SYMBOL(mxc_iomux_setup_multiple_pins); - -void mxc_iomux_release_pin(const unsigned int pin) -{ - unsigned pad = pin & IOMUX_PADNUM_MASK; - - if (pad < (PIN_MAX + 1)) - clear_bit(pad, mxc_pin_alloc_map); -} -EXPORT_SYMBOL(mxc_iomux_release_pin); - -void mxc_iomux_release_multiple_pins(unsigned int *pin_list, int count) -{ - unsigned int *p = pin_list; - int i; - - for (i = 0; i < count; i++) { - mxc_iomux_release_pin(*p); - p++; - } -} -EXPORT_SYMBOL(mxc_iomux_release_multiple_pins); - -/* - * This function enables/disables the general purpose function for a particular - * signal. - */ -void mxc_iomux_set_gpr(enum iomux_gp_func gp, bool en) -{ - u32 l; - - spin_lock(&gpio_mux_lock); - l = __raw_readl(IOMUXGPR); - if (en) - l |= gp; - else - l &= ~gp; - - __raw_writel(l, IOMUXGPR); - spin_unlock(&gpio_mux_lock); -} -EXPORT_SYMBOL(mxc_iomux_set_gpr); -- cgit v0.10.2 From 5c4c62bcb27cf9200553edeaecac1ab8cc2add91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 9 Dec 2009 20:09:35 +0100 Subject: imx/mx1: add newline at end of Makefile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Paulius Zaleckas Cc: Darius Augulis Cc: Mauro Carvalho Chehab diff --git a/arch/arm/mach-mx1/Makefile b/arch/arm/mach-mx1/Makefile index 7f86fe0..b72f536 100644 --- a/arch/arm/mach-mx1/Makefile +++ b/arch/arm/mach-mx1/Makefile @@ -11,4 +11,4 @@ obj-$(CONFIG_MX1_VIDEO) += ksym_mx1.o mx1_camera_fiq.o # Specific board support obj-$(CONFIG_ARCH_MX1ADS) += mx1ads.o -obj-$(CONFIG_MACH_SCB9328) += scb9328.o \ No newline at end of file +obj-$(CONFIG_MACH_SCB9328) += scb9328.o -- cgit v0.10.2 From 95c00464dda726c49ac8c45228bee8bf395ffa97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 9 Dec 2009 20:12:07 +0100 Subject: imx/mx1: rename files defining a machine to mach-$mach.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Paulius Zaleckas Cc: Darius Augulis Cc: Mauro Carvalho Chehab diff --git a/arch/arm/mach-mx1/Makefile b/arch/arm/mach-mx1/Makefile index b72f536..297d172 100644 --- a/arch/arm/mach-mx1/Makefile +++ b/arch/arm/mach-mx1/Makefile @@ -10,5 +10,5 @@ obj-y += generic.o clock.o devices.o obj-$(CONFIG_MX1_VIDEO) += ksym_mx1.o mx1_camera_fiq.o # Specific board support -obj-$(CONFIG_ARCH_MX1ADS) += mx1ads.o -obj-$(CONFIG_MACH_SCB9328) += scb9328.o +obj-$(CONFIG_ARCH_MX1ADS) += mach-mx1ads.o +obj-$(CONFIG_MACH_SCB9328) += mach-scb9328.o diff --git a/arch/arm/mach-mx1/mach-mx1ads.c b/arch/arm/mach-mx1/mach-mx1ads.c new file mode 100644 index 0000000..a39433a --- /dev/null +++ b/arch/arm/mach-mx1/mach-mx1ads.c @@ -0,0 +1,165 @@ +/* + * arch/arm/mach-imx/mach-mx1ads.c + * + * Initially based on: + * linux-2.6.7-imx/arch/arm/mach-imx/scb9328.c + * Copyright (c) 2004 Sascha Hauer + * + * 2004 (c) MontaVista Software, Inc. + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +static int mx1ads_pins[] = { + /* UART1 */ + PC9_PF_UART1_CTS, + PC10_PF_UART1_RTS, + PC11_PF_UART1_TXD, + PC12_PF_UART1_RXD, + /* UART2 */ + PB28_PF_UART2_CTS, + PB29_PF_UART2_RTS, + PB30_PF_UART2_TXD, + PB31_PF_UART2_RXD, + /* I2C */ + PA15_PF_I2C_SDA, + PA16_PF_I2C_SCL, + /* SPI */ + PC13_PF_SPI1_SPI_RDY, + PC14_PF_SPI1_SCLK, + PC15_PF_SPI1_SS, + PC16_PF_SPI1_MISO, + PC17_PF_SPI1_MOSI, +}; + +/* + * UARTs platform data + */ + +static struct imxuart_platform_data uart_pdata[] = { + { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, +}; + +/* + * Physmap flash + */ + +static struct physmap_flash_data mx1ads_flash_data = { + .width = 4, /* bankwidth in bytes */ +}; + +static struct resource flash_resource = { + .start = IMX_CS0_PHYS, + .end = IMX_CS0_PHYS + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device flash_device = { + .name = "physmap-flash", + .id = 0, + .resource = &flash_resource, + .num_resources = 1, +}; + +/* + * I2C + */ +static struct pcf857x_platform_data pcf857x_data[] = { + { + .gpio_base = 4 * 32, + }, { + .gpio_base = 4 * 32 + 16, + } +}; + +static struct imxi2c_platform_data mx1ads_i2c_data = { + .bitrate = 100000, +}; + +static struct i2c_board_info mx1ads_i2c_devices[] = { + { + I2C_BOARD_INFO("pcf8575", 0x22), + .platform_data = &pcf857x_data[0], + }, { + I2C_BOARD_INFO("pcf8575", 0x24), + .platform_data = &pcf857x_data[1], + }, +}; + +/* + * Board init + */ +static void __init mx1ads_init(void) +{ + mxc_gpio_setup_multiple_pins(mx1ads_pins, + ARRAY_SIZE(mx1ads_pins), "mx1ads"); + + /* UART */ + mxc_register_device(&imx_uart1_device, &uart_pdata[0]); + mxc_register_device(&imx_uart2_device, &uart_pdata[1]); + + /* Physmap flash */ + mxc_register_device(&flash_device, &mx1ads_flash_data); + + /* I2C */ + i2c_register_board_info(0, mx1ads_i2c_devices, + ARRAY_SIZE(mx1ads_i2c_devices)); + + mxc_register_device(&imx_i2c_device, &mx1ads_i2c_data); +} + +static void __init mx1ads_timer_init(void) +{ + mx1_clocks_init(32000); +} + +struct sys_timer mx1ads_timer = { + .init = mx1ads_timer_init, +}; + +MACHINE_START(MX1ADS, "Freescale MX1ADS") + /* Maintainer: Sascha Hauer, Pengutronix */ + .phys_io = IMX_IO_PHYS, + .io_pg_offst = (IMX_IO_BASE >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx1_map_io, + .init_irq = mx1_init_irq, + .timer = &mx1ads_timer, + .init_machine = mx1ads_init, +MACHINE_END + +MACHINE_START(MXLADS, "Freescale MXLADS") + .phys_io = IMX_IO_PHYS, + .io_pg_offst = (IMX_IO_BASE >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx1_map_io, + .init_irq = mx1_init_irq, + .timer = &mx1ads_timer, + .init_machine = mx1ads_init, +MACHINE_END diff --git a/arch/arm/mach-mx1/mach-scb9328.c b/arch/arm/mach-mx1/mach-scb9328.c new file mode 100644 index 0000000..b9530d7 --- /dev/null +++ b/arch/arm/mach-mx1/mach-scb9328.c @@ -0,0 +1,158 @@ +/* + * linux/arch/arm/mach-mx1/mach-scb9328.c + * + * Copyright (c) 2004 Sascha Hauer + * Copyright (c) 2006-2008 Juergen Beisert + * + * 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 "devices.h" + +/* + * This scb9328 has a 32MiB flash + */ +static struct resource flash_resource = { + .start = IMX_CS0_PHYS, + .end = IMX_CS0_PHYS + (32 * 1024 * 1024) - 1, + .flags = IORESOURCE_MEM, +}; + +static struct physmap_flash_data scb_flash_data = { + .width = 2, +}; + +static struct platform_device scb_flash_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &scb_flash_data, + }, + .resource = &flash_resource, + .num_resources = 1, +}; + +/* + * scb9328 has a DM9000 network controller + * connected to CS5, with 16 bit data path + * and interrupt connected to GPIO 3 + */ + +/* + * internal datapath is fixed 16 bit + */ +static struct dm9000_plat_data dm9000_platdata = { + .flags = DM9000_PLATF_16BITONLY, +}; + +/* + * the DM9000 drivers wants two defined address spaces + * to gain access to address latch registers and the data path. + */ +static struct resource dm9000x_resources[] = { + { + .name = "address area", + .start = IMX_CS5_PHYS, + .end = IMX_CS5_PHYS + 1, + .flags = IORESOURCE_MEM, /* address access */ + }, { + .name = "data area", + .start = IMX_CS5_PHYS + 4, + .end = IMX_CS5_PHYS + 5, + .flags = IORESOURCE_MEM, /* data access */ + }, { + .start = IRQ_GPIOC(3), + .end = IRQ_GPIOC(3), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, + }, +}; + +static struct platform_device dm9000x_device = { + .name = "dm9000", + .id = 0, + .num_resources = ARRAY_SIZE(dm9000x_resources), + .resource = dm9000x_resources, + .dev = { + .platform_data = &dm9000_platdata, + } +}; + +static int mxc_uart1_pins[] = { + PC9_PF_UART1_CTS, + PC10_PF_UART1_RTS, + PC11_PF_UART1_TXD, + PC12_PF_UART1_RXD, +}; + +static int uart1_mxc_init(struct platform_device *pdev) +{ + return mxc_gpio_setup_multiple_pins(mxc_uart1_pins, + ARRAY_SIZE(mxc_uart1_pins), "UART1"); +} + +static int uart1_mxc_exit(struct platform_device *pdev) +{ + mxc_gpio_release_multiple_pins(mxc_uart1_pins, + ARRAY_SIZE(mxc_uart1_pins)); + return 0; +} + +static struct imxuart_platform_data uart_pdata = { + .init = uart1_mxc_init, + .exit = uart1_mxc_exit, + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct platform_device *devices[] __initdata = { + &scb_flash_device, + &dm9000x_device, +}; + +/* + * scb9328_init - Init the CPU card itself + */ +static void __init scb9328_init(void) +{ + mxc_register_device(&imx_uart1_device, &uart_pdata); + + printk(KERN_INFO"Scb9328: Adding devices\n"); + platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +static void __init scb9328_timer_init(void) +{ + mx1_clocks_init(32000); +} + +static struct sys_timer scb9328_timer = { + .init = scb9328_timer_init, +}; + +MACHINE_START(SCB9328, "Synertronixx scb9328") + /* Sascha Hauer */ + .phys_io = 0x00200000, + .io_pg_offst = ((0xe0200000) >> 18) & 0xfffc, + .boot_params = 0x08000100, + .map_io = mx1_map_io, + .init_irq = mx1_init_irq, + .timer = &scb9328_timer, + .init_machine = scb9328_init, +MACHINE_END diff --git a/arch/arm/mach-mx1/mx1ads.c b/arch/arm/mach-mx1/mx1ads.c deleted file mode 100644 index 30f04e5..0000000 --- a/arch/arm/mach-mx1/mx1ads.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * arch/arm/mach-imx/mx1ads.c - * - * Initially based on: - * linux-2.6.7-imx/arch/arm/mach-imx/scb9328.c - * Copyright (c) 2004 Sascha Hauer - * - * 2004 (c) MontaVista Software, Inc. - * - * 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 -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -static int mx1ads_pins[] = { - /* UART1 */ - PC9_PF_UART1_CTS, - PC10_PF_UART1_RTS, - PC11_PF_UART1_TXD, - PC12_PF_UART1_RXD, - /* UART2 */ - PB28_PF_UART2_CTS, - PB29_PF_UART2_RTS, - PB30_PF_UART2_TXD, - PB31_PF_UART2_RXD, - /* I2C */ - PA15_PF_I2C_SDA, - PA16_PF_I2C_SCL, - /* SPI */ - PC13_PF_SPI1_SPI_RDY, - PC14_PF_SPI1_SCLK, - PC15_PF_SPI1_SS, - PC16_PF_SPI1_MISO, - PC17_PF_SPI1_MOSI, -}; - -/* - * UARTs platform data - */ - -static struct imxuart_platform_data uart_pdata[] = { - { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, -}; - -/* - * Physmap flash - */ - -static struct physmap_flash_data mx1ads_flash_data = { - .width = 4, /* bankwidth in bytes */ -}; - -static struct resource flash_resource = { - .start = IMX_CS0_PHYS, - .end = IMX_CS0_PHYS + SZ_32M - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device flash_device = { - .name = "physmap-flash", - .id = 0, - .resource = &flash_resource, - .num_resources = 1, -}; - -/* - * I2C - */ -static struct pcf857x_platform_data pcf857x_data[] = { - { - .gpio_base = 4 * 32, - }, { - .gpio_base = 4 * 32 + 16, - } -}; - -static struct imxi2c_platform_data mx1ads_i2c_data = { - .bitrate = 100000, -}; - -static struct i2c_board_info mx1ads_i2c_devices[] = { - { - I2C_BOARD_INFO("pcf8575", 0x22), - .platform_data = &pcf857x_data[0], - }, { - I2C_BOARD_INFO("pcf8575", 0x24), - .platform_data = &pcf857x_data[1], - }, -}; - -/* - * Board init - */ -static void __init mx1ads_init(void) -{ - mxc_gpio_setup_multiple_pins(mx1ads_pins, - ARRAY_SIZE(mx1ads_pins), "mx1ads"); - - /* UART */ - mxc_register_device(&imx_uart1_device, &uart_pdata[0]); - mxc_register_device(&imx_uart2_device, &uart_pdata[1]); - - /* Physmap flash */ - mxc_register_device(&flash_device, &mx1ads_flash_data); - - /* I2C */ - i2c_register_board_info(0, mx1ads_i2c_devices, - ARRAY_SIZE(mx1ads_i2c_devices)); - - mxc_register_device(&imx_i2c_device, &mx1ads_i2c_data); -} - -static void __init mx1ads_timer_init(void) -{ - mx1_clocks_init(32000); -} - -struct sys_timer mx1ads_timer = { - .init = mx1ads_timer_init, -}; - -MACHINE_START(MX1ADS, "Freescale MX1ADS") - /* Maintainer: Sascha Hauer, Pengutronix */ - .phys_io = IMX_IO_PHYS, - .io_pg_offst = (IMX_IO_BASE >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx1_map_io, - .init_irq = mx1_init_irq, - .timer = &mx1ads_timer, - .init_machine = mx1ads_init, -MACHINE_END - -MACHINE_START(MXLADS, "Freescale MXLADS") - .phys_io = IMX_IO_PHYS, - .io_pg_offst = (IMX_IO_BASE >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx1_map_io, - .init_irq = mx1_init_irq, - .timer = &mx1ads_timer, - .init_machine = mx1ads_init, -MACHINE_END diff --git a/arch/arm/mach-mx1/scb9328.c b/arch/arm/mach-mx1/scb9328.c deleted file mode 100644 index 325d98d..0000000 --- a/arch/arm/mach-mx1/scb9328.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * linux/arch/arm/mach-mx1/scb9328.c - * - * Copyright (c) 2004 Sascha Hauer - * Copyright (c) 2006-2008 Juergen Beisert - * - * 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 "devices.h" - -/* - * This scb9328 has a 32MiB flash - */ -static struct resource flash_resource = { - .start = IMX_CS0_PHYS, - .end = IMX_CS0_PHYS + (32 * 1024 * 1024) - 1, - .flags = IORESOURCE_MEM, -}; - -static struct physmap_flash_data scb_flash_data = { - .width = 2, -}; - -static struct platform_device scb_flash_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &scb_flash_data, - }, - .resource = &flash_resource, - .num_resources = 1, -}; - -/* - * scb9328 has a DM9000 network controller - * connected to CS5, with 16 bit data path - * and interrupt connected to GPIO 3 - */ - -/* - * internal datapath is fixed 16 bit - */ -static struct dm9000_plat_data dm9000_platdata = { - .flags = DM9000_PLATF_16BITONLY, -}; - -/* - * the DM9000 drivers wants two defined address spaces - * to gain access to address latch registers and the data path. - */ -static struct resource dm9000x_resources[] = { - { - .name = "address area", - .start = IMX_CS5_PHYS, - .end = IMX_CS5_PHYS + 1, - .flags = IORESOURCE_MEM, /* address access */ - }, { - .name = "data area", - .start = IMX_CS5_PHYS + 4, - .end = IMX_CS5_PHYS + 5, - .flags = IORESOURCE_MEM, /* data access */ - }, { - .start = IRQ_GPIOC(3), - .end = IRQ_GPIOC(3), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, - }, -}; - -static struct platform_device dm9000x_device = { - .name = "dm9000", - .id = 0, - .num_resources = ARRAY_SIZE(dm9000x_resources), - .resource = dm9000x_resources, - .dev = { - .platform_data = &dm9000_platdata, - } -}; - -static int mxc_uart1_pins[] = { - PC9_PF_UART1_CTS, - PC10_PF_UART1_RTS, - PC11_PF_UART1_TXD, - PC12_PF_UART1_RXD, -}; - -static int uart1_mxc_init(struct platform_device *pdev) -{ - return mxc_gpio_setup_multiple_pins(mxc_uart1_pins, - ARRAY_SIZE(mxc_uart1_pins), "UART1"); -} - -static int uart1_mxc_exit(struct platform_device *pdev) -{ - mxc_gpio_release_multiple_pins(mxc_uart1_pins, - ARRAY_SIZE(mxc_uart1_pins)); - return 0; -} - -static struct imxuart_platform_data uart_pdata = { - .init = uart1_mxc_init, - .exit = uart1_mxc_exit, - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct platform_device *devices[] __initdata = { - &scb_flash_device, - &dm9000x_device, -}; - -/* - * scb9328_init - Init the CPU card itself - */ -static void __init scb9328_init(void) -{ - mxc_register_device(&imx_uart1_device, &uart_pdata); - - printk(KERN_INFO"Scb9328: Adding devices\n"); - platform_add_devices(devices, ARRAY_SIZE(devices)); -} - -static void __init scb9328_timer_init(void) -{ - mx1_clocks_init(32000); -} - -static struct sys_timer scb9328_timer = { - .init = scb9328_timer_init, -}; - -MACHINE_START(SCB9328, "Synertronixx scb9328") - /* Sascha Hauer */ - .phys_io = 0x00200000, - .io_pg_offst = ((0xe0200000) >> 18) & 0xfffc, - .boot_params = 0x08000100, - .map_io = mx1_map_io, - .init_irq = mx1_init_irq, - .timer = &scb9328_timer, - .init_machine = scb9328_init, -MACHINE_END -- cgit v0.10.2 From bc727390f0a7d7069a39cd97451fa890caa0010e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 10 Dec 2009 10:19:43 +0100 Subject: imx: rename Kconfig symbol for "Eukrea CPUIMX27 module" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not using MACH_CPUIMX27 makes machine_is_cpuimx27 always return 0 independent of building support for this machine. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Jean Delvare Cc: Eric Benard diff --git a/arch/arm/mach-mx2/Kconfig b/arch/arm/mach-mx2/Kconfig index b96c6a3..a8bc337 100644 --- a/arch/arm/mach-mx2/Kconfig +++ b/arch/arm/mach-mx2/Kconfig @@ -55,7 +55,7 @@ config MACH_PCM970_BASEBOARD endchoice -config MACH_EUKREA_CPUIMX27 +config MACH_CPUIMX27 bool "Eukrea CPUIMX27 module" depends on MACH_MX27 help @@ -64,14 +64,14 @@ config MACH_EUKREA_CPUIMX27 config MACH_EUKREA_CPUIMX27_USESDHC2 bool "CPUIMX27 integrates SDHC2 module" - depends on MACH_EUKREA_CPUIMX27 + depends on MACH_CPUIMX27 help This adds support for the internal SDHC2 used on CPUIMX27 used for wifi or eMMC. choice prompt "Baseboard" - depends on MACH_EUKREA_CPUIMX27 + depends on MACH_CPUIMX27 default MACH_EUKREA_MBIMX27_BASEBOARD config MACH_EUKREA_MBIMX27_BASEBOARD diff --git a/arch/arm/mach-mx2/Makefile b/arch/arm/mach-mx2/Makefile index 52aca0a..2ba9997 100644 --- a/arch/arm/mach-mx2/Makefile +++ b/arch/arm/mach-mx2/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_MACH_PCM038) += pcm038.o obj-$(CONFIG_MACH_PCM970_BASEBOARD) += pcm970-baseboard.o obj-$(CONFIG_MACH_MX27_3DS) += mx27pdk.o obj-$(CONFIG_MACH_MX27LITE) += mx27lite.o -obj-$(CONFIG_MACH_EUKREA_CPUIMX27) += eukrea_cpuimx27.o +obj-$(CONFIG_MACH_CPUIMX27) += eukrea_cpuimx27.o obj-$(CONFIG_MACH_EUKREA_MBIMX27_BASEBOARD) += eukrea_mbimx27-baseboard.o obj-$(CONFIG_MACH_PCA100) += pca100.o obj-$(CONFIG_MACH_MXT_TD60) += mxt_td60.o -- cgit v0.10.2 From f747f935431c796749752037b8cf7d3f76e96b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 10 Dec 2009 10:25:07 +0100 Subject: imx: rename Kconfig symbol for "LogicPD MX27 LITEKIT platform" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not using MACH_IMX27LITE makes machine_is_imx27lite always return 0 independent of building support for this machine. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Daniel Schaeffer diff --git a/arch/arm/configs/mx27_defconfig b/arch/arm/configs/mx27_defconfig index edfdd6f..b4c1366 100644 --- a/arch/arm/configs/mx27_defconfig +++ b/arch/arm/configs/mx27_defconfig @@ -200,7 +200,7 @@ CONFIG_MACH_MX27ADS=y CONFIG_MACH_PCM038=y CONFIG_MACH_PCM970_BASEBOARD=y CONFIG_MACH_MX27_3DS=y -CONFIG_MACH_MX27LITE=y +CONFIG_MACH_IMX27LITE=y CONFIG_MXC_IRQ_PRIOR=y CONFIG_MXC_PWM=y diff --git a/arch/arm/mach-mx2/Kconfig b/arch/arm/mach-mx2/Kconfig index a8bc337..7bc797c 100644 --- a/arch/arm/mach-mx2/Kconfig +++ b/arch/arm/mach-mx2/Kconfig @@ -90,7 +90,7 @@ config MACH_MX27_3DS Include support for MX27PDK platform. This includes specific configurations for the board and its peripherals. -config MACH_MX27LITE +config MACH_IMX27LITE bool "LogicPD MX27 LITEKIT platform" depends on MACH_MX27 help diff --git a/arch/arm/mach-mx2/Makefile b/arch/arm/mach-mx2/Makefile index 2ba9997..a340698 100644 --- a/arch/arm/mach-mx2/Makefile +++ b/arch/arm/mach-mx2/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_MACH_MX27ADS) += mx27ads.o obj-$(CONFIG_MACH_PCM038) += pcm038.o obj-$(CONFIG_MACH_PCM970_BASEBOARD) += pcm970-baseboard.o obj-$(CONFIG_MACH_MX27_3DS) += mx27pdk.o -obj-$(CONFIG_MACH_MX27LITE) += mx27lite.o +obj-$(CONFIG_MACH_IMX27LITE) += mx27lite.o obj-$(CONFIG_MACH_CPUIMX27) += eukrea_cpuimx27.o obj-$(CONFIG_MACH_EUKREA_MBIMX27_BASEBOARD) += eukrea_mbimx27-baseboard.o obj-$(CONFIG_MACH_PCA100) += pca100.o -- cgit v0.10.2 From 386c5cf7a9a1d6856f17bde43174f751fac1bc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 10 Dec 2009 10:34:04 +0100 Subject: imx/mx2: rename files defining a machine to mach-$mach.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Eric Benard diff --git a/arch/arm/mach-mx2/Makefile b/arch/arm/mach-mx2/Makefile index a340698..3710893 100644 --- a/arch/arm/mach-mx2/Makefile +++ b/arch/arm/mach-mx2/Makefile @@ -11,14 +11,13 @@ obj-$(CONFIG_MACH_MX21) += clock_imx21.o obj-$(CONFIG_MACH_MX27) += cpu_imx27.o obj-$(CONFIG_MACH_MX27) += clock_imx27.o -obj-$(CONFIG_MACH_MX21ADS) += mx21ads.o -obj-$(CONFIG_MACH_MX27ADS) += mx27ads.o -obj-$(CONFIG_MACH_PCM038) += pcm038.o +obj-$(CONFIG_MACH_MX21ADS) += mach-mx21ads.o +obj-$(CONFIG_MACH_MX27ADS) += mach-mx27ads.o +obj-$(CONFIG_MACH_PCM038) += mach-pcm038.o obj-$(CONFIG_MACH_PCM970_BASEBOARD) += pcm970-baseboard.o -obj-$(CONFIG_MACH_MX27_3DS) += mx27pdk.o -obj-$(CONFIG_MACH_IMX27LITE) += mx27lite.o -obj-$(CONFIG_MACH_CPUIMX27) += eukrea_cpuimx27.o +obj-$(CONFIG_MACH_MX27_3DS) += mach-mx27_3ds.o +obj-$(CONFIG_MACH_IMX27LITE) += mach-imx27lite.o +obj-$(CONFIG_MACH_CPUIMX27) += mach-cpuimx27.o obj-$(CONFIG_MACH_EUKREA_MBIMX27_BASEBOARD) += eukrea_mbimx27-baseboard.o -obj-$(CONFIG_MACH_PCA100) += pca100.o -obj-$(CONFIG_MACH_MXT_TD60) += mxt_td60.o - +obj-$(CONFIG_MACH_PCA100) += mach-pca100.o +obj-$(CONFIG_MACH_MXT_TD60) += mach-mxt_td60.o diff --git a/arch/arm/mach-mx2/eukrea_cpuimx27.c b/arch/arm/mach-mx2/eukrea_cpuimx27.c deleted file mode 100644 index 8e4f3d0..0000000 --- a/arch/arm/mach-mx2/eukrea_cpuimx27.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2009 Eric Benard - eric@eukrea.com - * - * Based on pcm038.c which is : - * Copyright 2007 Robert Schwebel , Pengutronix - * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 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 "devices.h" - -static int eukrea_cpuimx27_pins[] = { - /* UART1 */ - PE12_PF_UART1_TXD, - PE13_PF_UART1_RXD, - PE14_PF_UART1_CTS, - PE15_PF_UART1_RTS, - /* UART4 */ - PB26_AF_UART4_RTS, - PB28_AF_UART4_TXD, - PB29_AF_UART4_CTS, - PB31_AF_UART4_RXD, - /* FEC */ - PD0_AIN_FEC_TXD0, - PD1_AIN_FEC_TXD1, - PD2_AIN_FEC_TXD2, - PD3_AIN_FEC_TXD3, - PD4_AOUT_FEC_RX_ER, - PD5_AOUT_FEC_RXD1, - PD6_AOUT_FEC_RXD2, - PD7_AOUT_FEC_RXD3, - PD8_AF_FEC_MDIO, - PD9_AIN_FEC_MDC, - PD10_AOUT_FEC_CRS, - PD11_AOUT_FEC_TX_CLK, - PD12_AOUT_FEC_RXD0, - PD13_AOUT_FEC_RX_DV, - PD14_AOUT_FEC_RX_CLK, - PD15_AOUT_FEC_COL, - PD16_AIN_FEC_TX_ER, - PF23_AIN_FEC_TX_EN, - /* I2C1 */ - PD17_PF_I2C_DATA, - PD18_PF_I2C_CLK, - /* SDHC2 */ - PB4_PF_SD2_D0, - PB5_PF_SD2_D1, - PB6_PF_SD2_D2, - PB7_PF_SD2_D3, - PB8_PF_SD2_CMD, - PB9_PF_SD2_CLK, -#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) - /* Quad UART's IRQ */ - GPIO_PORTD | 22 | GPIO_GPIO | GPIO_IN, - GPIO_PORTD | 23 | GPIO_GPIO | GPIO_IN, - GPIO_PORTD | 27 | GPIO_GPIO | GPIO_IN, - GPIO_PORTD | 30 | GPIO_GPIO | GPIO_IN, -#endif -}; - -static struct physmap_flash_data eukrea_cpuimx27_flash_data = { - .width = 2, -}; - -static struct resource eukrea_cpuimx27_flash_resource = { - .start = 0xc0000000, - .end = 0xc3ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device eukrea_cpuimx27_nor_mtd_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &eukrea_cpuimx27_flash_data, - }, - .num_resources = 1, - .resource = &eukrea_cpuimx27_flash_resource, -}; - -static struct imxuart_platform_data uart_pdata[] = { - { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, -}; - -static struct mxc_nand_platform_data eukrea_cpuimx27_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -static struct platform_device *platform_devices[] __initdata = { - &eukrea_cpuimx27_nor_mtd_device, - &mxc_fec_device, -}; - -static struct imxi2c_platform_data eukrea_cpuimx27_i2c_1_data = { - .bitrate = 100000, -}; - -static struct i2c_board_info eukrea_cpuimx27_i2c_devices[] = { - { - I2C_BOARD_INFO("pcf8563", 0x51), - }, -}; - -#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) -static struct plat_serial8250_port serial_platform_data[] = { - { - .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x200000), - .irq = IRQ_GPIOB(23), - .uartclk = 14745600, - .regshift = 1, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, - }, { - .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x400000), - .irq = IRQ_GPIOB(22), - .uartclk = 14745600, - .regshift = 1, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, - }, { - .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x800000), - .irq = IRQ_GPIOB(27), - .uartclk = 14745600, - .regshift = 1, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, - }, { - .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x1000000), - .irq = IRQ_GPIOB(30), - .uartclk = 14745600, - .regshift = 1, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, - }, { - } -}; - -static struct platform_device serial_device = { - .name = "serial8250", - .id = 0, - .dev = { - .platform_data = serial_platform_data, - }, -}; -#endif - -static void __init eukrea_cpuimx27_init(void) -{ - mxc_gpio_setup_multiple_pins(eukrea_cpuimx27_pins, - ARRAY_SIZE(eukrea_cpuimx27_pins), "CPUIMX27"); - - mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); - - mxc_register_device(&mxc_nand_device, &eukrea_cpuimx27_nand_board_info); - - i2c_register_board_info(0, eukrea_cpuimx27_i2c_devices, - ARRAY_SIZE(eukrea_cpuimx27_i2c_devices)); - - mxc_register_device(&mxc_i2c_device0, &eukrea_cpuimx27_i2c_1_data); - - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); - -#if defined(CONFIG_MACH_EUKREA_CPUIMX27_USESDHC2) - /* SDHC2 can be used for Wifi */ - mxc_register_device(&mxc_sdhc_device1, NULL); - /* in which case UART4 is also used for Bluetooth */ - mxc_register_device(&mxc_uart_device3, &uart_pdata[1]); -#endif - -#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) - platform_device_register(&serial_device); -#endif - -#ifdef CONFIG_MACH_EUKREA_MBIMX27_BASEBOARD - eukrea_mbimx27_baseboard_init(); -#endif -} - -static void __init eukrea_cpuimx27_timer_init(void) -{ - mx27_clocks_init(26000000); -} - -static struct sys_timer eukrea_cpuimx27_timer = { - .init = eukrea_cpuimx27_timer_init, -}; - -MACHINE_START(CPUIMX27, "EUKREA CPUIMX27") - .phys_io = MX27_AIPI_BASE_ADDR, - .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx27_map_io, - .init_irq = mx27_init_irq, - .init_machine = eukrea_cpuimx27_init, - .timer = &eukrea_cpuimx27_timer, -MACHINE_END diff --git a/arch/arm/mach-mx2/mach-cpuimx27.c b/arch/arm/mach-mx2/mach-cpuimx27.c new file mode 100644 index 0000000..8e4f3d0 --- /dev/null +++ b/arch/arm/mach-mx2/mach-cpuimx27.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2009 Eric Benard - eric@eukrea.com + * + * Based on pcm038.c which is : + * Copyright 2007 Robert Schwebel , Pengutronix + * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 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 "devices.h" + +static int eukrea_cpuimx27_pins[] = { + /* UART1 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + /* UART4 */ + PB26_AF_UART4_RTS, + PB28_AF_UART4_TXD, + PB29_AF_UART4_CTS, + PB31_AF_UART4_RXD, + /* FEC */ + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, + PD16_AIN_FEC_TX_ER, + PF23_AIN_FEC_TX_EN, + /* I2C1 */ + PD17_PF_I2C_DATA, + PD18_PF_I2C_CLK, + /* SDHC2 */ + PB4_PF_SD2_D0, + PB5_PF_SD2_D1, + PB6_PF_SD2_D2, + PB7_PF_SD2_D3, + PB8_PF_SD2_CMD, + PB9_PF_SD2_CLK, +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + /* Quad UART's IRQ */ + GPIO_PORTD | 22 | GPIO_GPIO | GPIO_IN, + GPIO_PORTD | 23 | GPIO_GPIO | GPIO_IN, + GPIO_PORTD | 27 | GPIO_GPIO | GPIO_IN, + GPIO_PORTD | 30 | GPIO_GPIO | GPIO_IN, +#endif +}; + +static struct physmap_flash_data eukrea_cpuimx27_flash_data = { + .width = 2, +}; + +static struct resource eukrea_cpuimx27_flash_resource = { + .start = 0xc0000000, + .end = 0xc3ffffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device eukrea_cpuimx27_nor_mtd_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &eukrea_cpuimx27_flash_data, + }, + .num_resources = 1, + .resource = &eukrea_cpuimx27_flash_resource, +}; + +static struct imxuart_platform_data uart_pdata[] = { + { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, +}; + +static struct mxc_nand_platform_data eukrea_cpuimx27_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +static struct platform_device *platform_devices[] __initdata = { + &eukrea_cpuimx27_nor_mtd_device, + &mxc_fec_device, +}; + +static struct imxi2c_platform_data eukrea_cpuimx27_i2c_1_data = { + .bitrate = 100000, +}; + +static struct i2c_board_info eukrea_cpuimx27_i2c_devices[] = { + { + I2C_BOARD_INFO("pcf8563", 0x51), + }, +}; + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) +static struct plat_serial8250_port serial_platform_data[] = { + { + .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x200000), + .irq = IRQ_GPIOB(23), + .uartclk = 14745600, + .regshift = 1, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, + }, { + .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x400000), + .irq = IRQ_GPIOB(22), + .uartclk = 14745600, + .regshift = 1, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, + }, { + .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x800000), + .irq = IRQ_GPIOB(27), + .uartclk = 14745600, + .regshift = 1, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, + }, { + .mapbase = (unsigned long)(MX27_CS3_BASE_ADDR + 0x1000000), + .irq = IRQ_GPIOB(30), + .uartclk = 14745600, + .regshift = 1, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP, + }, { + } +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = serial_platform_data, + }, +}; +#endif + +static void __init eukrea_cpuimx27_init(void) +{ + mxc_gpio_setup_multiple_pins(eukrea_cpuimx27_pins, + ARRAY_SIZE(eukrea_cpuimx27_pins), "CPUIMX27"); + + mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); + + mxc_register_device(&mxc_nand_device, &eukrea_cpuimx27_nand_board_info); + + i2c_register_board_info(0, eukrea_cpuimx27_i2c_devices, + ARRAY_SIZE(eukrea_cpuimx27_i2c_devices)); + + mxc_register_device(&mxc_i2c_device0, &eukrea_cpuimx27_i2c_1_data); + + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); + +#if defined(CONFIG_MACH_EUKREA_CPUIMX27_USESDHC2) + /* SDHC2 can be used for Wifi */ + mxc_register_device(&mxc_sdhc_device1, NULL); + /* in which case UART4 is also used for Bluetooth */ + mxc_register_device(&mxc_uart_device3, &uart_pdata[1]); +#endif + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + platform_device_register(&serial_device); +#endif + +#ifdef CONFIG_MACH_EUKREA_MBIMX27_BASEBOARD + eukrea_mbimx27_baseboard_init(); +#endif +} + +static void __init eukrea_cpuimx27_timer_init(void) +{ + mx27_clocks_init(26000000); +} + +static struct sys_timer eukrea_cpuimx27_timer = { + .init = eukrea_cpuimx27_timer_init, +}; + +MACHINE_START(CPUIMX27, "EUKREA CPUIMX27") + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx27_map_io, + .init_irq = mx27_init_irq, + .init_machine = eukrea_cpuimx27_init, + .timer = &eukrea_cpuimx27_timer, +MACHINE_END diff --git a/arch/arm/mach-mx2/mach-imx27lite.c b/arch/arm/mach-mx2/mach-imx27lite.c new file mode 100644 index 0000000..ca6ab12 --- /dev/null +++ b/arch/arm/mach-mx2/mach-imx27lite.c @@ -0,0 +1,95 @@ +/* + * Copyright 2007 Robert Schwebel , Pengutronix + * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) + * Copyright 2009 Daniel Schaeffer (daniel.schaeffer@timesys.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +static unsigned int mx27lite_pins[] = { + /* UART1 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + /* FEC */ + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, + PD16_AIN_FEC_TX_ER, + PF23_AIN_FEC_TX_EN, +}; + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct platform_device *platform_devices[] __initdata = { + &mxc_fec_device, +}; + +static void __init mx27lite_init(void) +{ + mxc_gpio_setup_multiple_pins(mx27lite_pins, ARRAY_SIZE(mx27lite_pins), + "imx27lite"); + mxc_register_device(&mxc_uart_device0, &uart_pdata); + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); +} + +static void __init mx27lite_timer_init(void) +{ + mx27_clocks_init(26000000); +} + +static struct sys_timer mx27lite_timer = { + .init = mx27lite_timer_init, +}; + +MACHINE_START(IMX27LITE, "LogicPD i.MX27LITE") + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx27_map_io, + .init_irq = mx27_init_irq, + .init_machine = mx27lite_init, + .timer = &mx27lite_timer, +MACHINE_END diff --git a/arch/arm/mach-mx2/mach-mx21ads.c b/arch/arm/mach-mx2/mach-mx21ads.c new file mode 100644 index 0000000..eb4a6e8 --- /dev/null +++ b/arch/arm/mach-mx2/mach-mx21ads.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2000 Deep Blue Solutions Ltd + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * Copyright 2006-2007 Freescale Semiconductor, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +static unsigned int mx21ads_pins[] = { + + /* CS8900A */ + (GPIO_PORTE | GPIO_GPIO | GPIO_IN | 11), + + /* UART1 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + + /* UART3 (IrDA) - only TXD and RXD */ + PE8_PF_UART3_TXD, + PE9_PF_UART3_RXD, + + /* UART4 */ + PB26_AF_UART4_RTS, + PB28_AF_UART4_TXD, + PB29_AF_UART4_CTS, + PB31_AF_UART4_RXD, + + /* LCDC */ + PA5_PF_LSCLK, + PA6_PF_LD0, + PA7_PF_LD1, + PA8_PF_LD2, + PA9_PF_LD3, + PA10_PF_LD4, + PA11_PF_LD5, + PA12_PF_LD6, + PA13_PF_LD7, + PA14_PF_LD8, + PA15_PF_LD9, + PA16_PF_LD10, + PA17_PF_LD11, + PA18_PF_LD12, + PA19_PF_LD13, + PA20_PF_LD14, + PA21_PF_LD15, + PA22_PF_LD16, + PA24_PF_REV, /* Sharp panel dedicated signal */ + PA25_PF_CLS, /* Sharp panel dedicated signal */ + PA26_PF_PS, /* Sharp panel dedicated signal */ + PA27_PF_SPL_SPR, /* Sharp panel dedicated signal */ + PA28_PF_HSYNC, + PA29_PF_VSYNC, + PA30_PF_CONTRAST, + PA31_PF_OE_ACD, + + /* MMC/SDHC */ + PE18_PF_SD1_D0, + PE19_PF_SD1_D1, + PE20_PF_SD1_D2, + PE21_PF_SD1_D3, + PE22_PF_SD1_CMD, + PE23_PF_SD1_CLK, + + /* NFC */ + PF0_PF_NRFB, + PF1_PF_NFCE, + PF2_PF_NFWP, + PF3_PF_NFCLE, + PF4_PF_NFALE, + PF5_PF_NFRE, + PF6_PF_NFWE, + PF7_PF_NFIO0, + PF8_PF_NFIO1, + PF9_PF_NFIO2, + PF10_PF_NFIO3, + PF11_PF_NFIO4, + PF12_PF_NFIO5, + PF13_PF_NFIO6, + PF14_PF_NFIO7, +}; + +/* ADS's NOR flash: 2x AM29BDS128HE9VKI on 32-bit bus */ +static struct physmap_flash_data mx21ads_flash_data = { + .width = 4, +}; + +static struct resource mx21ads_flash_resource = { + .start = MX21_CS0_BASE_ADDR, + .end = MX21_CS0_BASE_ADDR + 0x02000000 - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device mx21ads_nor_mtd_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &mx21ads_flash_data, + }, + .num_resources = 1, + .resource = &mx21ads_flash_resource, +}; + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct imxuart_platform_data uart_norts_pdata = { +}; + + +static int mx21ads_fb_init(struct platform_device *pdev) +{ + u16 tmp; + + tmp = __raw_readw(MX21ADS_IO_REG); + tmp |= MX21ADS_IO_LCDON; + __raw_writew(tmp, MX21ADS_IO_REG); + return 0; +} + +static void mx21ads_fb_exit(struct platform_device *pdev) +{ + u16 tmp; + + tmp = __raw_readw(MX21ADS_IO_REG); + tmp &= ~MX21ADS_IO_LCDON; + __raw_writew(tmp, MX21ADS_IO_REG); +} + +/* + * Connected is a portrait Sharp-QVGA display + * of type: LQ035Q7DB02 + */ +static struct imx_fb_videomode mx21ads_modes[] = { + { + .mode = { + .name = "Sharp-LQ035Q7", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 188679, /* in ps (5.3MHz) */ + .hsync_len = 2, + .left_margin = 6, + .right_margin = 16, + .vsync_len = 1, + .upper_margin = 8, + .lower_margin = 10, + }, + .pcr = 0xfb108bc7, + .bpp = 16, + }, +}; + +static struct imx_fb_platform_data mx21ads_fb_data = { + .mode = mx21ads_modes, + .num_modes = ARRAY_SIZE(mx21ads_modes), + + .pwmr = 0x00a903ff, + .lscr1 = 0x00120300, + .dmacr = 0x00020008, + + .init = mx21ads_fb_init, + .exit = mx21ads_fb_exit, +}; + +static int mx21ads_sdhc_get_ro(struct device *dev) +{ + return (__raw_readw(MX21ADS_IO_REG) & MX21ADS_IO_SD_WP) ? 1 : 0; +} + +static int mx21ads_sdhc_init(struct device *dev, irq_handler_t detect_irq, + void *data) +{ + int ret; + + ret = request_irq(IRQ_GPIOD(25), detect_irq, + IRQF_TRIGGER_FALLING, "mmc-detect", data); + if (ret) + goto out; + return 0; +out: + return ret; +} + +static void mx21ads_sdhc_exit(struct device *dev, void *data) +{ + free_irq(IRQ_GPIOD(25), data); +} + +static struct imxmmc_platform_data mx21ads_sdhc_pdata = { + .ocr_avail = MMC_VDD_29_30 | MMC_VDD_30_31, /* 3.0V */ + .get_ro = mx21ads_sdhc_get_ro, + .init = mx21ads_sdhc_init, + .exit = mx21ads_sdhc_exit, +}; + +static struct mxc_nand_platform_data mx21ads_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +static struct map_desc mx21ads_io_desc[] __initdata = { + /* + * Memory-mapped I/O on MX21ADS Base board: + * - CS8900A Ethernet controller + * - ST16C2552CJ UART + * - CPU and Base board version + * - Base board I/O register + */ + { + .virtual = MX21ADS_MMIO_BASE_ADDR, + .pfn = __phys_to_pfn(MX21_CS1_BASE_ADDR), + .length = MX21ADS_MMIO_SIZE, + .type = MT_DEVICE, + }, +}; + +static void __init mx21ads_map_io(void) +{ + mx21_map_io(); + iotable_init(mx21ads_io_desc, ARRAY_SIZE(mx21ads_io_desc)); +} + +static struct platform_device *platform_devices[] __initdata = { + &mx21ads_nor_mtd_device, +}; + +static void __init mx21ads_board_init(void) +{ + mxc_gpio_setup_multiple_pins(mx21ads_pins, ARRAY_SIZE(mx21ads_pins), + "mx21ads"); + + mxc_register_device(&mxc_uart_device0, &uart_pdata); + mxc_register_device(&mxc_uart_device2, &uart_norts_pdata); + mxc_register_device(&mxc_uart_device3, &uart_pdata); + mxc_register_device(&mxc_fb_device, &mx21ads_fb_data); + mxc_register_device(&mxc_sdhc_device0, &mx21ads_sdhc_pdata); + mxc_register_device(&mxc_nand_device, &mx21ads_nand_board_info); + + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); +} + +static void __init mx21ads_timer_init(void) +{ + mx21_clocks_init(32768, 26000000); +} + +static struct sys_timer mx21ads_timer = { + .init = mx21ads_timer_init, +}; + +MACHINE_START(MX21ADS, "Freescale i.MX21ADS") + /* maintainer: Freescale Semiconductor, Inc. */ + .phys_io = MX21_AIPI_BASE_ADDR, + .io_pg_offst = ((MX21_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx21ads_map_io, + .init_irq = mx21_init_irq, + .init_machine = mx21ads_board_init, + .timer = &mx21ads_timer, +MACHINE_END diff --git a/arch/arm/mach-mx2/mach-mx27_3ds.c b/arch/arm/mach-mx2/mach-mx27_3ds.c new file mode 100644 index 0000000..595fea4 --- /dev/null +++ b/arch/arm/mach-mx2/mach-mx27_3ds.c @@ -0,0 +1,95 @@ +/* + * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Author: Fabio Estevam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +static unsigned int mx27pdk_pins[] = { + /* UART1 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + /* FEC */ + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, + PD16_AIN_FEC_TX_ER, + PF23_AIN_FEC_TX_EN, +}; + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct platform_device *platform_devices[] __initdata = { + &mxc_fec_device, +}; + +static void __init mx27pdk_init(void) +{ + mxc_gpio_setup_multiple_pins(mx27pdk_pins, ARRAY_SIZE(mx27pdk_pins), + "mx27pdk"); + mxc_register_device(&mxc_uart_device0, &uart_pdata); + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); +} + +static void __init mx27pdk_timer_init(void) +{ + mx27_clocks_init(26000000); +} + +static struct sys_timer mx27pdk_timer = { + .init = mx27pdk_timer_init, +}; + +MACHINE_START(MX27_3DS, "Freescale MX27PDK") + /* maintainer: Freescale Semiconductor, Inc. */ + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx27_map_io, + .init_irq = mx27_init_irq, + .init_machine = mx27pdk_init, + .timer = &mx27pdk_timer, +MACHINE_END diff --git a/arch/arm/mach-mx2/mach-mx27ads.c b/arch/arm/mach-mx2/mach-mx27ads.c new file mode 100644 index 0000000..385fc1c --- /dev/null +++ b/arch/arm/mach-mx2/mach-mx27ads.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2000 Deep Blue Solutions Ltd + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * Copyright 2006-2007 Freescale Semiconductor, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +static unsigned int mx27ads_pins[] = { + /* UART0 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + /* UART1 */ + PE3_PF_UART2_CTS, + PE4_PF_UART2_RTS, + PE6_PF_UART2_TXD, + PE7_PF_UART2_RXD, + /* UART2 */ + PE8_PF_UART3_TXD, + PE9_PF_UART3_RXD, + PE10_PF_UART3_CTS, + PE11_PF_UART3_RTS, + /* UART3 */ + PB26_AF_UART4_RTS, + PB28_AF_UART4_TXD, + PB29_AF_UART4_CTS, + PB31_AF_UART4_RXD, + /* UART4 */ + PB18_AF_UART5_TXD, + PB19_AF_UART5_RXD, + PB20_AF_UART5_CTS, + PB21_AF_UART5_RTS, + /* UART5 */ + PB10_AF_UART6_TXD, + PB12_AF_UART6_CTS, + PB11_AF_UART6_RXD, + PB13_AF_UART6_RTS, + /* FEC */ + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, + PD16_AIN_FEC_TX_ER, + PF23_AIN_FEC_TX_EN, + /* I2C2 */ + PC5_PF_I2C2_SDA, + PC6_PF_I2C2_SCL, + /* FB */ + PA5_PF_LSCLK, + PA6_PF_LD0, + PA7_PF_LD1, + PA8_PF_LD2, + PA9_PF_LD3, + PA10_PF_LD4, + PA11_PF_LD5, + PA12_PF_LD6, + PA13_PF_LD7, + PA14_PF_LD8, + PA15_PF_LD9, + PA16_PF_LD10, + PA17_PF_LD11, + PA18_PF_LD12, + PA19_PF_LD13, + PA20_PF_LD14, + PA21_PF_LD15, + PA22_PF_LD16, + PA23_PF_LD17, + PA24_PF_REV, + PA25_PF_CLS, + PA26_PF_PS, + PA27_PF_SPL_SPR, + PA28_PF_HSYNC, + PA29_PF_VSYNC, + PA30_PF_CONTRAST, + PA31_PF_OE_ACD, + /* OWIRE */ + PE16_AF_OWIRE, + /* SDHC1*/ + PE18_PF_SD1_D0, + PE19_PF_SD1_D1, + PE20_PF_SD1_D2, + PE21_PF_SD1_D3, + PE22_PF_SD1_CMD, + PE23_PF_SD1_CLK, + /* SDHC2*/ + PB4_PF_SD2_D0, + PB5_PF_SD2_D1, + PB6_PF_SD2_D2, + PB7_PF_SD2_D3, + PB8_PF_SD2_CMD, + PB9_PF_SD2_CLK, +}; + +static struct mxc_nand_platform_data mx27ads_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +/* ADS's NOR flash */ +static struct physmap_flash_data mx27ads_flash_data = { + .width = 2, +}; + +static struct resource mx27ads_flash_resource = { + .start = 0xc0000000, + .end = 0xc0000000 + 0x02000000 - 1, + .flags = IORESOURCE_MEM, + +}; + +static struct platform_device mx27ads_nor_mtd_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &mx27ads_flash_data, + }, + .num_resources = 1, + .resource = &mx27ads_flash_resource, +}; + +static struct imxi2c_platform_data mx27ads_i2c_data = { + .bitrate = 100000, +}; + +static struct i2c_board_info mx27ads_i2c_devices[] = { +}; + +void lcd_power(int on) +{ + if (on) + __raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_SET_REG); + else + __raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_CLEAR_REG); +} + +static struct imx_fb_videomode mx27ads_modes[] = { + { + .mode = { + .name = "Sharp-LQ035Q7", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 188679, /* in ps (5.3MHz) */ + .hsync_len = 1, + .left_margin = 9, + .right_margin = 16, + .vsync_len = 1, + .upper_margin = 7, + .lower_margin = 9, + }, + .bpp = 16, + .pcr = 0xFB008BC0, + }, +}; + +static struct imx_fb_platform_data mx27ads_fb_data = { + .mode = mx27ads_modes, + .num_modes = ARRAY_SIZE(mx27ads_modes), + + /* + * - HSYNC active high + * - VSYNC active high + * - clk notenabled while idle + * - clock inverted + * - data not inverted + * - data enable low active + * - enable sharp mode + */ + .pwmr = 0x00A903FF, + .lscr1 = 0x00120300, + .dmacr = 0x00020010, + + .lcd_power = lcd_power, +}; + +static int mx27ads_sdhc1_init(struct device *dev, irq_handler_t detect_irq, + void *data) +{ + return request_irq(IRQ_GPIOE(21), detect_irq, IRQF_TRIGGER_RISING, + "sdhc1-card-detect", data); +} + +static int mx27ads_sdhc2_init(struct device *dev, irq_handler_t detect_irq, + void *data) +{ + return request_irq(IRQ_GPIOB(7), detect_irq, IRQF_TRIGGER_RISING, + "sdhc2-card-detect", data); +} + +static void mx27ads_sdhc1_exit(struct device *dev, void *data) +{ + free_irq(IRQ_GPIOE(21), data); +} + +static void mx27ads_sdhc2_exit(struct device *dev, void *data) +{ + free_irq(IRQ_GPIOB(7), data); +} + +static struct imxmmc_platform_data sdhc1_pdata = { + .init = mx27ads_sdhc1_init, + .exit = mx27ads_sdhc1_exit, +}; + +static struct imxmmc_platform_data sdhc2_pdata = { + .init = mx27ads_sdhc2_init, + .exit = mx27ads_sdhc2_exit, +}; + +static struct platform_device *platform_devices[] __initdata = { + &mx27ads_nor_mtd_device, + &mxc_fec_device, + &mxc_w1_master_device, +}; + +static struct imxuart_platform_data uart_pdata[] = { + { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, +}; + +static void __init mx27ads_board_init(void) +{ + mxc_gpio_setup_multiple_pins(mx27ads_pins, ARRAY_SIZE(mx27ads_pins), + "mx27ads"); + + mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); + mxc_register_device(&mxc_uart_device1, &uart_pdata[1]); + mxc_register_device(&mxc_uart_device2, &uart_pdata[2]); + mxc_register_device(&mxc_uart_device3, &uart_pdata[3]); + mxc_register_device(&mxc_uart_device4, &uart_pdata[4]); + mxc_register_device(&mxc_uart_device5, &uart_pdata[5]); + mxc_register_device(&mxc_nand_device, &mx27ads_nand_board_info); + + /* only the i2c master 1 is used on this CPU card */ + i2c_register_board_info(1, mx27ads_i2c_devices, + ARRAY_SIZE(mx27ads_i2c_devices)); + mxc_register_device(&mxc_i2c_device1, &mx27ads_i2c_data); + mxc_register_device(&mxc_fb_device, &mx27ads_fb_data); + mxc_register_device(&mxc_sdhc_device0, &sdhc1_pdata); + mxc_register_device(&mxc_sdhc_device1, &sdhc2_pdata); + + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); +} + +static void __init mx27ads_timer_init(void) +{ + unsigned long fref = 26000000; + + if ((__raw_readw(PBC_VERSION_REG) & CKIH_27MHZ_BIT_SET) == 0) + fref = 27000000; + + mx27_clocks_init(fref); +} + +static struct sys_timer mx27ads_timer = { + .init = mx27ads_timer_init, +}; + +static struct map_desc mx27ads_io_desc[] __initdata = { + { + .virtual = PBC_BASE_ADDRESS, + .pfn = __phys_to_pfn(MX27_CS4_BASE_ADDR), + .length = SZ_1M, + .type = MT_DEVICE, + }, +}; + +static void __init mx27ads_map_io(void) +{ + mx27_map_io(); + iotable_init(mx27ads_io_desc, ARRAY_SIZE(mx27ads_io_desc)); +} + +MACHINE_START(MX27ADS, "Freescale i.MX27ADS") + /* maintainer: Freescale Semiconductor, Inc. */ + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx27ads_map_io, + .init_irq = mx27_init_irq, + .init_machine = mx27ads_board_init, + .timer = &mx27ads_timer, +MACHINE_END + diff --git a/arch/arm/mach-mx2/mach-mxt_td60.c b/arch/arm/mach-mx2/mach-mxt_td60.c new file mode 100644 index 0000000..9ed4e49 --- /dev/null +++ b/arch/arm/mach-mx2/mach-mxt_td60.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2000 Deep Blue Solutions Ltd + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * Copyright 2006-2007 Freescale Semiconductor, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +static unsigned int mxt_td60_pins[] __initdata = { + /* UART0 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + /* UART1 */ + PE3_PF_UART2_CTS, + PE4_PF_UART2_RTS, + PE6_PF_UART2_TXD, + PE7_PF_UART2_RXD, + /* UART2 */ + PE8_PF_UART3_TXD, + PE9_PF_UART3_RXD, + PE10_PF_UART3_CTS, + PE11_PF_UART3_RTS, + /* FEC */ + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, + PD16_AIN_FEC_TX_ER, + PF23_AIN_FEC_TX_EN, + /* I2C1 */ + PD17_PF_I2C_DATA, + PD18_PF_I2C_CLK, + /* I2C2 */ + PC5_PF_I2C2_SDA, + PC6_PF_I2C2_SCL, + /* FB */ + PA5_PF_LSCLK, + PA6_PF_LD0, + PA7_PF_LD1, + PA8_PF_LD2, + PA9_PF_LD3, + PA10_PF_LD4, + PA11_PF_LD5, + PA12_PF_LD6, + PA13_PF_LD7, + PA14_PF_LD8, + PA15_PF_LD9, + PA16_PF_LD10, + PA17_PF_LD11, + PA18_PF_LD12, + PA19_PF_LD13, + PA20_PF_LD14, + PA21_PF_LD15, + PA22_PF_LD16, + PA23_PF_LD17, + PA25_PF_CLS, + PA27_PF_SPL_SPR, + PA28_PF_HSYNC, + PA29_PF_VSYNC, + PA30_PF_CONTRAST, + PA31_PF_OE_ACD, + /* OWIRE */ + PE16_AF_OWIRE, + /* SDHC1*/ + PE18_PF_SD1_D0, + PE19_PF_SD1_D1, + PE20_PF_SD1_D2, + PE21_PF_SD1_D3, + PE22_PF_SD1_CMD, + PE23_PF_SD1_CLK, + PF8_AF_ATA_IORDY, + /* SDHC2*/ + PB4_PF_SD2_D0, + PB5_PF_SD2_D1, + PB6_PF_SD2_D2, + PB7_PF_SD2_D3, + PB8_PF_SD2_CMD, + PB9_PF_SD2_CLK, +}; + +static struct mxc_nand_platform_data mxt_td60_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +static struct imxi2c_platform_data mxt_td60_i2c_data = { + .bitrate = 100000, +}; + +/* PCA9557 */ +static int mxt_td60_pca9557_setup(struct i2c_client *client, + unsigned gpio_base, unsigned ngpio, + void *context) +{ + static int mxt_td60_gpio_value[] = { + -1, -1, -1, -1, -1, -1, -1, 1 + }; + int n; + + for (n = 0; n < ARRAY_SIZE(mxt_td60_gpio_value); ++n) { + gpio_request(gpio_base + n, "MXT_TD60 GPIO Exp"); + if (mxt_td60_gpio_value[n] < 0) + gpio_direction_input(gpio_base + n); + else + gpio_direction_output(gpio_base + n, + mxt_td60_gpio_value[n]); + gpio_export(gpio_base + n, 0); + } + + return 0; +} + +static struct pca953x_platform_data mxt_td60_pca9557_pdata = { + .gpio_base = 240, /* place PCA9557 after all MX27 gpio pins */ + .invert = 0, /* Do not invert */ + .setup = mxt_td60_pca9557_setup, +}; + +static struct i2c_board_info mxt_td60_i2c_devices[] = { + { + I2C_BOARD_INFO("pca9557", 0x18), + .platform_data = &mxt_td60_pca9557_pdata, + }, +}; + +static struct imxi2c_platform_data mxt_td60_i2c2_data = { + .bitrate = 100000, +}; + +static struct i2c_board_info mxt_td60_i2c2_devices[] = { +}; + +static struct imx_fb_videomode mxt_td60_modes[] = { + { + .mode = { + .name = "Chimei LW700AT9003", + .refresh = 60, + .xres = 800, + .yres = 480, + .pixclock = 30303, + .hsync_len = 64, + .left_margin = 0x67, + .right_margin = 0x68, + .vsync_len = 16, + .upper_margin = 0x0f, + .lower_margin = 0x0f, + }, + .bpp = 16, + .pcr = 0xFA208B83, + }, +}; + +static struct imx_fb_platform_data mxt_td60_fb_data = { + .mode = mxt_td60_modes, + .num_modes = ARRAY_SIZE(mxt_td60_modes), + + /* + * - HSYNC active high + * - VSYNC active high + * - clk notenabled while idle + * - clock inverted + * - data not inverted + * - data enable low active + * - enable sharp mode + */ + .pwmr = 0x00A903FF, + .lscr1 = 0x00120300, + .dmacr = 0x00020010, +}; + +static int mxt_td60_sdhc1_init(struct device *dev, irq_handler_t detect_irq, + void *data) +{ + return request_irq(IRQ_GPIOF(8), detect_irq, IRQF_TRIGGER_FALLING, + "sdhc1-card-detect", data); +} + +static void mxt_td60_sdhc1_exit(struct device *dev, void *data) +{ + free_irq(IRQ_GPIOF(8), data); +} + +static struct imxmmc_platform_data sdhc1_pdata = { + .init = mxt_td60_sdhc1_init, + .exit = mxt_td60_sdhc1_exit, +}; + +static struct platform_device *platform_devices[] __initdata = { + &mxc_fec_device, +}; + +static struct imxuart_platform_data uart_pdata[] = { + { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, +}; + +static void __init mxt_td60_board_init(void) +{ + mxc_gpio_setup_multiple_pins(mxt_td60_pins, ARRAY_SIZE(mxt_td60_pins), + "MXT_TD60"); + + mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); + mxc_register_device(&mxc_uart_device1, &uart_pdata[1]); + mxc_register_device(&mxc_uart_device2, &uart_pdata[2]); + mxc_register_device(&mxc_nand_device, &mxt_td60_nand_board_info); + + i2c_register_board_info(0, mxt_td60_i2c_devices, + ARRAY_SIZE(mxt_td60_i2c_devices)); + + i2c_register_board_info(1, mxt_td60_i2c2_devices, + ARRAY_SIZE(mxt_td60_i2c2_devices)); + + mxc_register_device(&mxc_i2c_device0, &mxt_td60_i2c_data); + mxc_register_device(&mxc_i2c_device1, &mxt_td60_i2c2_data); + mxc_register_device(&mxc_fb_device, &mxt_td60_fb_data); + mxc_register_device(&mxc_sdhc_device0, &sdhc1_pdata); + + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); +} + +static void __init mxt_td60_timer_init(void) +{ + mx27_clocks_init(26000000); +} + +static struct sys_timer mxt_td60_timer = { + .init = mxt_td60_timer_init, +}; + +MACHINE_START(MXT_TD60, "Maxtrack i-MXT TD60") + /* maintainer: Maxtrack Industrial */ + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx27_map_io, + .init_irq = mx27_init_irq, + .init_machine = mxt_td60_board_init, + .timer = &mxt_td60_timer, +MACHINE_END + diff --git a/arch/arm/mach-mx2/mach-pca100.c b/arch/arm/mach-mx2/mach-pca100.c new file mode 100644 index 0000000..55dbf5a --- /dev/null +++ b/arch/arm/mach-mx2/mach-pca100.c @@ -0,0 +1,244 @@ +/* + * Copyright 2007 Robert Schwebel , Pengutronix + * Copyright (C) 2009 Sascha Hauer (kernel@pengutronix.de) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) +#include +#endif +#include +#include +#include +#include + +#include "devices.h" + +static int pca100_pins[] = { + /* UART1 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + /* SDHC */ + PB4_PF_SD2_D0, + PB5_PF_SD2_D1, + PB6_PF_SD2_D2, + PB7_PF_SD2_D3, + PB8_PF_SD2_CMD, + PB9_PF_SD2_CLK, + /* FEC */ + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, + PD16_AIN_FEC_TX_ER, + PF23_AIN_FEC_TX_EN, + /* SSI1 */ + PC20_PF_SSI1_FS, + PC21_PF_SSI1_RXD, + PC22_PF_SSI1_TXD, + PC23_PF_SSI1_CLK, + /* onboard I2C */ + PC5_PF_I2C2_SDA, + PC6_PF_I2C2_SCL, + /* external I2C */ + PD17_PF_I2C_DATA, + PD18_PF_I2C_CLK, + /* SPI1 */ + PD25_PF_CSPI1_RDY, + PD29_PF_CSPI1_SCLK, + PD30_PF_CSPI1_MISO, + PD31_PF_CSPI1_MOSI, +}; + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct mxc_nand_platform_data pca100_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +static struct platform_device *platform_devices[] __initdata = { + &mxc_w1_master_device, + &mxc_fec_device, +}; + +static struct imxi2c_platform_data pca100_i2c_1_data = { + .bitrate = 100000, +}; + +static struct at24_platform_data board_eeprom = { + .byte_len = 4096, + .page_size = 32, + .flags = AT24_FLAG_ADDR16, +}; + +static struct i2c_board_info pca100_i2c_devices[] = { + { + I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */ + .platform_data = &board_eeprom, + }, { + I2C_BOARD_INFO("rtc-pcf8563", 0x51), + .type = "pcf8563" + }, { + I2C_BOARD_INFO("lm75", 0x4a), + .type = "lm75" + } +}; + +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) +static struct spi_eeprom at25320 = { + .name = "at25320an", + .byte_len = 4096, + .page_size = 32, + .flags = EE_ADDR2, +}; + +static struct spi_board_info pca100_spi_board_info[] __initdata = { + { + .modalias = "at25", + .max_speed_hz = 30000, + .bus_num = 0, + .chip_select = 1, + .platform_data = &at25320, + }, +}; + +static int pca100_spi_cs[] = {GPIO_PORTD + 28, GPIO_PORTD + 27}; + +static struct spi_imx_master pca100_spi_0_data = { + .chipselect = pca100_spi_cs, + .num_chipselect = ARRAY_SIZE(pca100_spi_cs), +}; +#endif + +static int pca100_sdhc2_init(struct device *dev, irq_handler_t detect_irq, + void *data) +{ + int ret; + + ret = request_irq(IRQ_GPIOC(29), detect_irq, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "imx-mmc-detect", data); + if (ret) + printk(KERN_ERR + "pca100: Failed to reuest irq for sd/mmc detection\n"); + + return ret; +} + +static void pca100_sdhc2_exit(struct device *dev, void *data) +{ + free_irq(IRQ_GPIOC(29), data); +} + +static struct imxmmc_platform_data sdhc_pdata = { + .init = pca100_sdhc2_init, + .exit = pca100_sdhc2_exit, +}; + +static void __init pca100_init(void) +{ + int ret; + + ret = mxc_gpio_setup_multiple_pins(pca100_pins, + ARRAY_SIZE(pca100_pins), "PCA100"); + if (ret) + printk(KERN_ERR "pca100: Failed to setup pins (%d)\n", ret); + + mxc_register_device(&mxc_uart_device0, &uart_pdata); + + mxc_gpio_mode(GPIO_PORTC | 29 | GPIO_GPIO | GPIO_IN); + mxc_register_device(&mxc_sdhc_device1, &sdhc_pdata); + + mxc_register_device(&mxc_nand_device, &pca100_nand_board_info); + + /* only the i2c master 1 is used on this CPU card */ + i2c_register_board_info(1, pca100_i2c_devices, + ARRAY_SIZE(pca100_i2c_devices)); + + mxc_register_device(&mxc_i2c_device1, &pca100_i2c_1_data); + + mxc_gpio_mode(GPIO_PORTD | 28 | GPIO_GPIO | GPIO_OUT); + mxc_gpio_mode(GPIO_PORTD | 27 | GPIO_GPIO | GPIO_OUT); + + /* GPIO0_IRQ */ + mxc_gpio_mode(GPIO_PORTC | 31 | GPIO_GPIO | GPIO_IN); + /* GPIO1_IRQ */ + mxc_gpio_mode(GPIO_PORTC | 25 | GPIO_GPIO | GPIO_IN); + /* GPIO2_IRQ */ + mxc_gpio_mode(GPIO_PORTE | 5 | GPIO_GPIO | GPIO_IN); + +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) + spi_register_board_info(pca100_spi_board_info, + ARRAY_SIZE(pca100_spi_board_info)); + mxc_register_device(&mxc_spi_device0, &pca100_spi_0_data); +#endif + + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); +} + +static void __init pca100_timer_init(void) +{ + mx27_clocks_init(26000000); +} + +static struct sys_timer pca100_timer = { + .init = pca100_timer_init, +}; + +MACHINE_START(PCA100, "phyCARD-i.MX27") + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx27_map_io, + .init_irq = mx27_init_irq, + .init_machine = pca100_init, + .timer = &pca100_timer, +MACHINE_END + diff --git a/arch/arm/mach-mx2/mach-pcm038.c b/arch/arm/mach-mx2/mach-pcm038.c new file mode 100644 index 0000000..a2e2ea1 --- /dev/null +++ b/arch/arm/mach-mx2/mach-pcm038.c @@ -0,0 +1,338 @@ +/* + * Copyright 2007 Robert Schwebel , Pengutronix + * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 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 + +#include "devices.h" + +static int pcm038_pins[] = { + /* UART1 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + /* UART2 */ + PE3_PF_UART2_CTS, + PE4_PF_UART2_RTS, + PE6_PF_UART2_TXD, + PE7_PF_UART2_RXD, + /* UART3 */ + PE8_PF_UART3_TXD, + PE9_PF_UART3_RXD, + PE10_PF_UART3_CTS, + PE11_PF_UART3_RTS, + /* FEC */ + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, + PD16_AIN_FEC_TX_ER, + PF23_AIN_FEC_TX_EN, + /* I2C2 */ + PC5_PF_I2C2_SDA, + PC6_PF_I2C2_SCL, + /* SPI1 */ + PD25_PF_CSPI1_RDY, + PD29_PF_CSPI1_SCLK, + PD30_PF_CSPI1_MISO, + PD31_PF_CSPI1_MOSI, + /* SSI1 */ + PC20_PF_SSI1_FS, + PC21_PF_SSI1_RXD, + PC22_PF_SSI1_TXD, + PC23_PF_SSI1_CLK, + /* SSI4 */ + PC16_PF_SSI4_FS, + PC17_PF_SSI4_RXD, + PC18_PF_SSI4_TXD, + PC19_PF_SSI4_CLK, +}; + +/* + * Phytec's PCM038 comes with 2MiB battery buffered SRAM, + * 16 bit width + */ + +static struct platdata_mtd_ram pcm038_sram_data = { + .bankwidth = 2, +}; + +static struct resource pcm038_sram_resource = { + .start = MX27_CS1_BASE_ADDR, + .end = MX27_CS1_BASE_ADDR + 512 * 1024 - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device pcm038_sram_mtd_device = { + .name = "mtd-ram", + .id = 0, + .dev = { + .platform_data = &pcm038_sram_data, + }, + .num_resources = 1, + .resource = &pcm038_sram_resource, +}; + +/* + * Phytec's phyCORE-i.MX27 comes with 32MiB flash, + * 16 bit width + */ +static struct physmap_flash_data pcm038_flash_data = { + .width = 2, +}; + +static struct resource pcm038_flash_resource = { + .start = 0xc0000000, + .end = 0xc1ffffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device pcm038_nor_mtd_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &pcm038_flash_data, + }, + .num_resources = 1, + .resource = &pcm038_flash_resource, +}; + +static struct imxuart_platform_data uart_pdata[] = { + { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, { + .flags = IMXUART_HAVE_RTSCTS, + }, +}; + +static struct mxc_nand_platform_data pcm038_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +static struct platform_device *platform_devices[] __initdata = { + &pcm038_nor_mtd_device, + &mxc_w1_master_device, + &mxc_fec_device, + &pcm038_sram_mtd_device, +}; + +/* On pcm038 there's a sram attached to CS1, we enable the chipselect here and + * setup other stuffs to access the sram. */ +static void __init pcm038_init_sram(void) +{ + __raw_writel(0x0000d843, CSCR_U(1)); + __raw_writel(0x22252521, CSCR_L(1)); + __raw_writel(0x22220a00, CSCR_A(1)); +} + +static struct imxi2c_platform_data pcm038_i2c_1_data = { + .bitrate = 100000, +}; + +static struct at24_platform_data board_eeprom = { + .byte_len = 4096, + .page_size = 32, + .flags = AT24_FLAG_ADDR16, +}; + +static struct i2c_board_info pcm038_i2c_devices[] = { + { + I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */ + .platform_data = &board_eeprom, + }, { + I2C_BOARD_INFO("pcf8563", 0x51), + }, { + I2C_BOARD_INFO("lm75", 0x4a), + } +}; + +static int pcm038_spi_cs[] = {GPIO_PORTD + 28}; + +static struct spi_imx_master pcm038_spi_0_data = { + .chipselect = pcm038_spi_cs, + .num_chipselect = ARRAY_SIZE(pcm038_spi_cs), +}; + +static struct regulator_consumer_supply sdhc1_consumers[] = { + { + .dev = &mxc_sdhc_device1.dev, + .supply = "sdhc_vcc", + }, +}; + +static struct regulator_init_data sdhc1_data = { + .constraints = { + .min_uV = 3000000, + .max_uV = 3400000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_FAST, + .always_on = 0, + .boot_on = 0, + }, + .num_consumer_supplies = ARRAY_SIZE(sdhc1_consumers), + .consumer_supplies = sdhc1_consumers, +}; + +static struct regulator_consumer_supply cam_consumers[] = { + { + .dev = NULL, + .supply = "imx_cam_vcc", + }, +}; + +static struct regulator_init_data cam_data = { + .constraints = { + .min_uV = 3000000, + .max_uV = 3400000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_FAST, + .always_on = 0, + .boot_on = 0, + }, + .num_consumer_supplies = ARRAY_SIZE(cam_consumers), + .consumer_supplies = cam_consumers, +}; + +struct mc13783_regulator_init_data pcm038_regulators[] = { + { + .id = MC13783_REGU_VCAM, + .init_data = &cam_data, + }, { + .id = MC13783_REGU_VMMC1, + .init_data = &sdhc1_data, + }, +}; + +static struct mc13783_platform_data pcm038_pmic = { + .regulators = pcm038_regulators, + .num_regulators = ARRAY_SIZE(pcm038_regulators), + .flags = MC13783_USE_ADC | MC13783_USE_REGULATOR | + MC13783_USE_TOUCHSCREEN, +}; + +static struct spi_board_info pcm038_spi_board_info[] __initdata = { + { + .modalias = "mc13783", + .irq = IRQ_GPIOB(23), + .max_speed_hz = 300000, + .bus_num = 0, + .chip_select = 0, + .platform_data = &pcm038_pmic, + .mode = SPI_CS_HIGH, + } +}; + +static void __init pcm038_init(void) +{ + mxc_gpio_setup_multiple_pins(pcm038_pins, ARRAY_SIZE(pcm038_pins), + "PCM038"); + + pcm038_init_sram(); + + mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); + mxc_register_device(&mxc_uart_device1, &uart_pdata[1]); + mxc_register_device(&mxc_uart_device2, &uart_pdata[2]); + + mxc_gpio_mode(PE16_AF_OWIRE); + mxc_register_device(&mxc_nand_device, &pcm038_nand_board_info); + + /* only the i2c master 1 is used on this CPU card */ + i2c_register_board_info(1, pcm038_i2c_devices, + ARRAY_SIZE(pcm038_i2c_devices)); + + mxc_register_device(&mxc_i2c_device1, &pcm038_i2c_1_data); + + /* PE18 for user-LED D40 */ + mxc_gpio_mode(GPIO_PORTE | 18 | GPIO_GPIO | GPIO_OUT); + + mxc_gpio_mode(GPIO_PORTD | 28 | GPIO_GPIO | GPIO_OUT); + + /* MC13783 IRQ */ + mxc_gpio_mode(GPIO_PORTB | 23 | GPIO_GPIO | GPIO_IN); + + mxc_register_device(&mxc_spi_device0, &pcm038_spi_0_data); + spi_register_board_info(pcm038_spi_board_info, + ARRAY_SIZE(pcm038_spi_board_info)); + + platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); + +#ifdef CONFIG_MACH_PCM970_BASEBOARD + pcm970_baseboard_init(); +#endif +} + +static void __init pcm038_timer_init(void) +{ + mx27_clocks_init(26000000); +} + +static struct sys_timer pcm038_timer = { + .init = pcm038_timer_init, +}; + +MACHINE_START(PCM038, "phyCORE-i.MX27") + .phys_io = MX27_AIPI_BASE_ADDR, + .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx27_map_io, + .init_irq = mx27_init_irq, + .init_machine = pcm038_init, + .timer = &pcm038_timer, +MACHINE_END diff --git a/arch/arm/mach-mx2/mx21ads.c b/arch/arm/mach-mx2/mx21ads.c deleted file mode 100644 index eb4a6e8..0000000 --- a/arch/arm/mach-mx2/mx21ads.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (C) 2000 Deep Blue Solutions Ltd - * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * Copyright 2006-2007 Freescale Semiconductor, Inc. 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 as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -static unsigned int mx21ads_pins[] = { - - /* CS8900A */ - (GPIO_PORTE | GPIO_GPIO | GPIO_IN | 11), - - /* UART1 */ - PE12_PF_UART1_TXD, - PE13_PF_UART1_RXD, - PE14_PF_UART1_CTS, - PE15_PF_UART1_RTS, - - /* UART3 (IrDA) - only TXD and RXD */ - PE8_PF_UART3_TXD, - PE9_PF_UART3_RXD, - - /* UART4 */ - PB26_AF_UART4_RTS, - PB28_AF_UART4_TXD, - PB29_AF_UART4_CTS, - PB31_AF_UART4_RXD, - - /* LCDC */ - PA5_PF_LSCLK, - PA6_PF_LD0, - PA7_PF_LD1, - PA8_PF_LD2, - PA9_PF_LD3, - PA10_PF_LD4, - PA11_PF_LD5, - PA12_PF_LD6, - PA13_PF_LD7, - PA14_PF_LD8, - PA15_PF_LD9, - PA16_PF_LD10, - PA17_PF_LD11, - PA18_PF_LD12, - PA19_PF_LD13, - PA20_PF_LD14, - PA21_PF_LD15, - PA22_PF_LD16, - PA24_PF_REV, /* Sharp panel dedicated signal */ - PA25_PF_CLS, /* Sharp panel dedicated signal */ - PA26_PF_PS, /* Sharp panel dedicated signal */ - PA27_PF_SPL_SPR, /* Sharp panel dedicated signal */ - PA28_PF_HSYNC, - PA29_PF_VSYNC, - PA30_PF_CONTRAST, - PA31_PF_OE_ACD, - - /* MMC/SDHC */ - PE18_PF_SD1_D0, - PE19_PF_SD1_D1, - PE20_PF_SD1_D2, - PE21_PF_SD1_D3, - PE22_PF_SD1_CMD, - PE23_PF_SD1_CLK, - - /* NFC */ - PF0_PF_NRFB, - PF1_PF_NFCE, - PF2_PF_NFWP, - PF3_PF_NFCLE, - PF4_PF_NFALE, - PF5_PF_NFRE, - PF6_PF_NFWE, - PF7_PF_NFIO0, - PF8_PF_NFIO1, - PF9_PF_NFIO2, - PF10_PF_NFIO3, - PF11_PF_NFIO4, - PF12_PF_NFIO5, - PF13_PF_NFIO6, - PF14_PF_NFIO7, -}; - -/* ADS's NOR flash: 2x AM29BDS128HE9VKI on 32-bit bus */ -static struct physmap_flash_data mx21ads_flash_data = { - .width = 4, -}; - -static struct resource mx21ads_flash_resource = { - .start = MX21_CS0_BASE_ADDR, - .end = MX21_CS0_BASE_ADDR + 0x02000000 - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device mx21ads_nor_mtd_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &mx21ads_flash_data, - }, - .num_resources = 1, - .resource = &mx21ads_flash_resource, -}; - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct imxuart_platform_data uart_norts_pdata = { -}; - - -static int mx21ads_fb_init(struct platform_device *pdev) -{ - u16 tmp; - - tmp = __raw_readw(MX21ADS_IO_REG); - tmp |= MX21ADS_IO_LCDON; - __raw_writew(tmp, MX21ADS_IO_REG); - return 0; -} - -static void mx21ads_fb_exit(struct platform_device *pdev) -{ - u16 tmp; - - tmp = __raw_readw(MX21ADS_IO_REG); - tmp &= ~MX21ADS_IO_LCDON; - __raw_writew(tmp, MX21ADS_IO_REG); -} - -/* - * Connected is a portrait Sharp-QVGA display - * of type: LQ035Q7DB02 - */ -static struct imx_fb_videomode mx21ads_modes[] = { - { - .mode = { - .name = "Sharp-LQ035Q7", - .refresh = 60, - .xres = 240, - .yres = 320, - .pixclock = 188679, /* in ps (5.3MHz) */ - .hsync_len = 2, - .left_margin = 6, - .right_margin = 16, - .vsync_len = 1, - .upper_margin = 8, - .lower_margin = 10, - }, - .pcr = 0xfb108bc7, - .bpp = 16, - }, -}; - -static struct imx_fb_platform_data mx21ads_fb_data = { - .mode = mx21ads_modes, - .num_modes = ARRAY_SIZE(mx21ads_modes), - - .pwmr = 0x00a903ff, - .lscr1 = 0x00120300, - .dmacr = 0x00020008, - - .init = mx21ads_fb_init, - .exit = mx21ads_fb_exit, -}; - -static int mx21ads_sdhc_get_ro(struct device *dev) -{ - return (__raw_readw(MX21ADS_IO_REG) & MX21ADS_IO_SD_WP) ? 1 : 0; -} - -static int mx21ads_sdhc_init(struct device *dev, irq_handler_t detect_irq, - void *data) -{ - int ret; - - ret = request_irq(IRQ_GPIOD(25), detect_irq, - IRQF_TRIGGER_FALLING, "mmc-detect", data); - if (ret) - goto out; - return 0; -out: - return ret; -} - -static void mx21ads_sdhc_exit(struct device *dev, void *data) -{ - free_irq(IRQ_GPIOD(25), data); -} - -static struct imxmmc_platform_data mx21ads_sdhc_pdata = { - .ocr_avail = MMC_VDD_29_30 | MMC_VDD_30_31, /* 3.0V */ - .get_ro = mx21ads_sdhc_get_ro, - .init = mx21ads_sdhc_init, - .exit = mx21ads_sdhc_exit, -}; - -static struct mxc_nand_platform_data mx21ads_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -static struct map_desc mx21ads_io_desc[] __initdata = { - /* - * Memory-mapped I/O on MX21ADS Base board: - * - CS8900A Ethernet controller - * - ST16C2552CJ UART - * - CPU and Base board version - * - Base board I/O register - */ - { - .virtual = MX21ADS_MMIO_BASE_ADDR, - .pfn = __phys_to_pfn(MX21_CS1_BASE_ADDR), - .length = MX21ADS_MMIO_SIZE, - .type = MT_DEVICE, - }, -}; - -static void __init mx21ads_map_io(void) -{ - mx21_map_io(); - iotable_init(mx21ads_io_desc, ARRAY_SIZE(mx21ads_io_desc)); -} - -static struct platform_device *platform_devices[] __initdata = { - &mx21ads_nor_mtd_device, -}; - -static void __init mx21ads_board_init(void) -{ - mxc_gpio_setup_multiple_pins(mx21ads_pins, ARRAY_SIZE(mx21ads_pins), - "mx21ads"); - - mxc_register_device(&mxc_uart_device0, &uart_pdata); - mxc_register_device(&mxc_uart_device2, &uart_norts_pdata); - mxc_register_device(&mxc_uart_device3, &uart_pdata); - mxc_register_device(&mxc_fb_device, &mx21ads_fb_data); - mxc_register_device(&mxc_sdhc_device0, &mx21ads_sdhc_pdata); - mxc_register_device(&mxc_nand_device, &mx21ads_nand_board_info); - - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); -} - -static void __init mx21ads_timer_init(void) -{ - mx21_clocks_init(32768, 26000000); -} - -static struct sys_timer mx21ads_timer = { - .init = mx21ads_timer_init, -}; - -MACHINE_START(MX21ADS, "Freescale i.MX21ADS") - /* maintainer: Freescale Semiconductor, Inc. */ - .phys_io = MX21_AIPI_BASE_ADDR, - .io_pg_offst = ((MX21_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx21ads_map_io, - .init_irq = mx21_init_irq, - .init_machine = mx21ads_board_init, - .timer = &mx21ads_timer, -MACHINE_END diff --git a/arch/arm/mach-mx2/mx27ads.c b/arch/arm/mach-mx2/mx27ads.c deleted file mode 100644 index 385fc1c..0000000 --- a/arch/arm/mach-mx2/mx27ads.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2000 Deep Blue Solutions Ltd - * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * Copyright 2006-2007 Freescale Semiconductor, Inc. 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 as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -static unsigned int mx27ads_pins[] = { - /* UART0 */ - PE12_PF_UART1_TXD, - PE13_PF_UART1_RXD, - PE14_PF_UART1_CTS, - PE15_PF_UART1_RTS, - /* UART1 */ - PE3_PF_UART2_CTS, - PE4_PF_UART2_RTS, - PE6_PF_UART2_TXD, - PE7_PF_UART2_RXD, - /* UART2 */ - PE8_PF_UART3_TXD, - PE9_PF_UART3_RXD, - PE10_PF_UART3_CTS, - PE11_PF_UART3_RTS, - /* UART3 */ - PB26_AF_UART4_RTS, - PB28_AF_UART4_TXD, - PB29_AF_UART4_CTS, - PB31_AF_UART4_RXD, - /* UART4 */ - PB18_AF_UART5_TXD, - PB19_AF_UART5_RXD, - PB20_AF_UART5_CTS, - PB21_AF_UART5_RTS, - /* UART5 */ - PB10_AF_UART6_TXD, - PB12_AF_UART6_CTS, - PB11_AF_UART6_RXD, - PB13_AF_UART6_RTS, - /* FEC */ - PD0_AIN_FEC_TXD0, - PD1_AIN_FEC_TXD1, - PD2_AIN_FEC_TXD2, - PD3_AIN_FEC_TXD3, - PD4_AOUT_FEC_RX_ER, - PD5_AOUT_FEC_RXD1, - PD6_AOUT_FEC_RXD2, - PD7_AOUT_FEC_RXD3, - PD8_AF_FEC_MDIO, - PD9_AIN_FEC_MDC, - PD10_AOUT_FEC_CRS, - PD11_AOUT_FEC_TX_CLK, - PD12_AOUT_FEC_RXD0, - PD13_AOUT_FEC_RX_DV, - PD14_AOUT_FEC_RX_CLK, - PD15_AOUT_FEC_COL, - PD16_AIN_FEC_TX_ER, - PF23_AIN_FEC_TX_EN, - /* I2C2 */ - PC5_PF_I2C2_SDA, - PC6_PF_I2C2_SCL, - /* FB */ - PA5_PF_LSCLK, - PA6_PF_LD0, - PA7_PF_LD1, - PA8_PF_LD2, - PA9_PF_LD3, - PA10_PF_LD4, - PA11_PF_LD5, - PA12_PF_LD6, - PA13_PF_LD7, - PA14_PF_LD8, - PA15_PF_LD9, - PA16_PF_LD10, - PA17_PF_LD11, - PA18_PF_LD12, - PA19_PF_LD13, - PA20_PF_LD14, - PA21_PF_LD15, - PA22_PF_LD16, - PA23_PF_LD17, - PA24_PF_REV, - PA25_PF_CLS, - PA26_PF_PS, - PA27_PF_SPL_SPR, - PA28_PF_HSYNC, - PA29_PF_VSYNC, - PA30_PF_CONTRAST, - PA31_PF_OE_ACD, - /* OWIRE */ - PE16_AF_OWIRE, - /* SDHC1*/ - PE18_PF_SD1_D0, - PE19_PF_SD1_D1, - PE20_PF_SD1_D2, - PE21_PF_SD1_D3, - PE22_PF_SD1_CMD, - PE23_PF_SD1_CLK, - /* SDHC2*/ - PB4_PF_SD2_D0, - PB5_PF_SD2_D1, - PB6_PF_SD2_D2, - PB7_PF_SD2_D3, - PB8_PF_SD2_CMD, - PB9_PF_SD2_CLK, -}; - -static struct mxc_nand_platform_data mx27ads_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -/* ADS's NOR flash */ -static struct physmap_flash_data mx27ads_flash_data = { - .width = 2, -}; - -static struct resource mx27ads_flash_resource = { - .start = 0xc0000000, - .end = 0xc0000000 + 0x02000000 - 1, - .flags = IORESOURCE_MEM, - -}; - -static struct platform_device mx27ads_nor_mtd_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &mx27ads_flash_data, - }, - .num_resources = 1, - .resource = &mx27ads_flash_resource, -}; - -static struct imxi2c_platform_data mx27ads_i2c_data = { - .bitrate = 100000, -}; - -static struct i2c_board_info mx27ads_i2c_devices[] = { -}; - -void lcd_power(int on) -{ - if (on) - __raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_SET_REG); - else - __raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_CLEAR_REG); -} - -static struct imx_fb_videomode mx27ads_modes[] = { - { - .mode = { - .name = "Sharp-LQ035Q7", - .refresh = 60, - .xres = 240, - .yres = 320, - .pixclock = 188679, /* in ps (5.3MHz) */ - .hsync_len = 1, - .left_margin = 9, - .right_margin = 16, - .vsync_len = 1, - .upper_margin = 7, - .lower_margin = 9, - }, - .bpp = 16, - .pcr = 0xFB008BC0, - }, -}; - -static struct imx_fb_platform_data mx27ads_fb_data = { - .mode = mx27ads_modes, - .num_modes = ARRAY_SIZE(mx27ads_modes), - - /* - * - HSYNC active high - * - VSYNC active high - * - clk notenabled while idle - * - clock inverted - * - data not inverted - * - data enable low active - * - enable sharp mode - */ - .pwmr = 0x00A903FF, - .lscr1 = 0x00120300, - .dmacr = 0x00020010, - - .lcd_power = lcd_power, -}; - -static int mx27ads_sdhc1_init(struct device *dev, irq_handler_t detect_irq, - void *data) -{ - return request_irq(IRQ_GPIOE(21), detect_irq, IRQF_TRIGGER_RISING, - "sdhc1-card-detect", data); -} - -static int mx27ads_sdhc2_init(struct device *dev, irq_handler_t detect_irq, - void *data) -{ - return request_irq(IRQ_GPIOB(7), detect_irq, IRQF_TRIGGER_RISING, - "sdhc2-card-detect", data); -} - -static void mx27ads_sdhc1_exit(struct device *dev, void *data) -{ - free_irq(IRQ_GPIOE(21), data); -} - -static void mx27ads_sdhc2_exit(struct device *dev, void *data) -{ - free_irq(IRQ_GPIOB(7), data); -} - -static struct imxmmc_platform_data sdhc1_pdata = { - .init = mx27ads_sdhc1_init, - .exit = mx27ads_sdhc1_exit, -}; - -static struct imxmmc_platform_data sdhc2_pdata = { - .init = mx27ads_sdhc2_init, - .exit = mx27ads_sdhc2_exit, -}; - -static struct platform_device *platform_devices[] __initdata = { - &mx27ads_nor_mtd_device, - &mxc_fec_device, - &mxc_w1_master_device, -}; - -static struct imxuart_platform_data uart_pdata[] = { - { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, -}; - -static void __init mx27ads_board_init(void) -{ - mxc_gpio_setup_multiple_pins(mx27ads_pins, ARRAY_SIZE(mx27ads_pins), - "mx27ads"); - - mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); - mxc_register_device(&mxc_uart_device1, &uart_pdata[1]); - mxc_register_device(&mxc_uart_device2, &uart_pdata[2]); - mxc_register_device(&mxc_uart_device3, &uart_pdata[3]); - mxc_register_device(&mxc_uart_device4, &uart_pdata[4]); - mxc_register_device(&mxc_uart_device5, &uart_pdata[5]); - mxc_register_device(&mxc_nand_device, &mx27ads_nand_board_info); - - /* only the i2c master 1 is used on this CPU card */ - i2c_register_board_info(1, mx27ads_i2c_devices, - ARRAY_SIZE(mx27ads_i2c_devices)); - mxc_register_device(&mxc_i2c_device1, &mx27ads_i2c_data); - mxc_register_device(&mxc_fb_device, &mx27ads_fb_data); - mxc_register_device(&mxc_sdhc_device0, &sdhc1_pdata); - mxc_register_device(&mxc_sdhc_device1, &sdhc2_pdata); - - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); -} - -static void __init mx27ads_timer_init(void) -{ - unsigned long fref = 26000000; - - if ((__raw_readw(PBC_VERSION_REG) & CKIH_27MHZ_BIT_SET) == 0) - fref = 27000000; - - mx27_clocks_init(fref); -} - -static struct sys_timer mx27ads_timer = { - .init = mx27ads_timer_init, -}; - -static struct map_desc mx27ads_io_desc[] __initdata = { - { - .virtual = PBC_BASE_ADDRESS, - .pfn = __phys_to_pfn(MX27_CS4_BASE_ADDR), - .length = SZ_1M, - .type = MT_DEVICE, - }, -}; - -static void __init mx27ads_map_io(void) -{ - mx27_map_io(); - iotable_init(mx27ads_io_desc, ARRAY_SIZE(mx27ads_io_desc)); -} - -MACHINE_START(MX27ADS, "Freescale i.MX27ADS") - /* maintainer: Freescale Semiconductor, Inc. */ - .phys_io = MX27_AIPI_BASE_ADDR, - .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx27ads_map_io, - .init_irq = mx27_init_irq, - .init_machine = mx27ads_board_init, - .timer = &mx27ads_timer, -MACHINE_END - diff --git a/arch/arm/mach-mx2/mx27lite.c b/arch/arm/mach-mx2/mx27lite.c deleted file mode 100644 index ca6ab12..0000000 --- a/arch/arm/mach-mx2/mx27lite.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2007 Robert Schwebel , Pengutronix - * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) - * Copyright 2009 Daniel Schaeffer (daniel.schaeffer@timesys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -static unsigned int mx27lite_pins[] = { - /* UART1 */ - PE12_PF_UART1_TXD, - PE13_PF_UART1_RXD, - PE14_PF_UART1_CTS, - PE15_PF_UART1_RTS, - /* FEC */ - PD0_AIN_FEC_TXD0, - PD1_AIN_FEC_TXD1, - PD2_AIN_FEC_TXD2, - PD3_AIN_FEC_TXD3, - PD4_AOUT_FEC_RX_ER, - PD5_AOUT_FEC_RXD1, - PD6_AOUT_FEC_RXD2, - PD7_AOUT_FEC_RXD3, - PD8_AF_FEC_MDIO, - PD9_AIN_FEC_MDC, - PD10_AOUT_FEC_CRS, - PD11_AOUT_FEC_TX_CLK, - PD12_AOUT_FEC_RXD0, - PD13_AOUT_FEC_RX_DV, - PD14_AOUT_FEC_RX_CLK, - PD15_AOUT_FEC_COL, - PD16_AIN_FEC_TX_ER, - PF23_AIN_FEC_TX_EN, -}; - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct platform_device *platform_devices[] __initdata = { - &mxc_fec_device, -}; - -static void __init mx27lite_init(void) -{ - mxc_gpio_setup_multiple_pins(mx27lite_pins, ARRAY_SIZE(mx27lite_pins), - "imx27lite"); - mxc_register_device(&mxc_uart_device0, &uart_pdata); - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); -} - -static void __init mx27lite_timer_init(void) -{ - mx27_clocks_init(26000000); -} - -static struct sys_timer mx27lite_timer = { - .init = mx27lite_timer_init, -}; - -MACHINE_START(IMX27LITE, "LogicPD i.MX27LITE") - .phys_io = MX27_AIPI_BASE_ADDR, - .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx27_map_io, - .init_irq = mx27_init_irq, - .init_machine = mx27lite_init, - .timer = &mx27lite_timer, -MACHINE_END diff --git a/arch/arm/mach-mx2/mx27pdk.c b/arch/arm/mach-mx2/mx27pdk.c deleted file mode 100644 index 595fea4..0000000 --- a/arch/arm/mach-mx2/mx27pdk.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. - * - * Author: Fabio Estevam - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -static unsigned int mx27pdk_pins[] = { - /* UART1 */ - PE12_PF_UART1_TXD, - PE13_PF_UART1_RXD, - PE14_PF_UART1_CTS, - PE15_PF_UART1_RTS, - /* FEC */ - PD0_AIN_FEC_TXD0, - PD1_AIN_FEC_TXD1, - PD2_AIN_FEC_TXD2, - PD3_AIN_FEC_TXD3, - PD4_AOUT_FEC_RX_ER, - PD5_AOUT_FEC_RXD1, - PD6_AOUT_FEC_RXD2, - PD7_AOUT_FEC_RXD3, - PD8_AF_FEC_MDIO, - PD9_AIN_FEC_MDC, - PD10_AOUT_FEC_CRS, - PD11_AOUT_FEC_TX_CLK, - PD12_AOUT_FEC_RXD0, - PD13_AOUT_FEC_RX_DV, - PD14_AOUT_FEC_RX_CLK, - PD15_AOUT_FEC_COL, - PD16_AIN_FEC_TX_ER, - PF23_AIN_FEC_TX_EN, -}; - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct platform_device *platform_devices[] __initdata = { - &mxc_fec_device, -}; - -static void __init mx27pdk_init(void) -{ - mxc_gpio_setup_multiple_pins(mx27pdk_pins, ARRAY_SIZE(mx27pdk_pins), - "mx27pdk"); - mxc_register_device(&mxc_uart_device0, &uart_pdata); - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); -} - -static void __init mx27pdk_timer_init(void) -{ - mx27_clocks_init(26000000); -} - -static struct sys_timer mx27pdk_timer = { - .init = mx27pdk_timer_init, -}; - -MACHINE_START(MX27_3DS, "Freescale MX27PDK") - /* maintainer: Freescale Semiconductor, Inc. */ - .phys_io = MX27_AIPI_BASE_ADDR, - .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx27_map_io, - .init_irq = mx27_init_irq, - .init_machine = mx27pdk_init, - .timer = &mx27pdk_timer, -MACHINE_END diff --git a/arch/arm/mach-mx2/mxt_td60.c b/arch/arm/mach-mx2/mxt_td60.c deleted file mode 100644 index 9ed4e49..0000000 --- a/arch/arm/mach-mx2/mxt_td60.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2000 Deep Blue Solutions Ltd - * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * Copyright 2006-2007 Freescale Semiconductor, Inc. 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 as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -static unsigned int mxt_td60_pins[] __initdata = { - /* UART0 */ - PE12_PF_UART1_TXD, - PE13_PF_UART1_RXD, - PE14_PF_UART1_CTS, - PE15_PF_UART1_RTS, - /* UART1 */ - PE3_PF_UART2_CTS, - PE4_PF_UART2_RTS, - PE6_PF_UART2_TXD, - PE7_PF_UART2_RXD, - /* UART2 */ - PE8_PF_UART3_TXD, - PE9_PF_UART3_RXD, - PE10_PF_UART3_CTS, - PE11_PF_UART3_RTS, - /* FEC */ - PD0_AIN_FEC_TXD0, - PD1_AIN_FEC_TXD1, - PD2_AIN_FEC_TXD2, - PD3_AIN_FEC_TXD3, - PD4_AOUT_FEC_RX_ER, - PD5_AOUT_FEC_RXD1, - PD6_AOUT_FEC_RXD2, - PD7_AOUT_FEC_RXD3, - PD8_AF_FEC_MDIO, - PD9_AIN_FEC_MDC, - PD10_AOUT_FEC_CRS, - PD11_AOUT_FEC_TX_CLK, - PD12_AOUT_FEC_RXD0, - PD13_AOUT_FEC_RX_DV, - PD14_AOUT_FEC_RX_CLK, - PD15_AOUT_FEC_COL, - PD16_AIN_FEC_TX_ER, - PF23_AIN_FEC_TX_EN, - /* I2C1 */ - PD17_PF_I2C_DATA, - PD18_PF_I2C_CLK, - /* I2C2 */ - PC5_PF_I2C2_SDA, - PC6_PF_I2C2_SCL, - /* FB */ - PA5_PF_LSCLK, - PA6_PF_LD0, - PA7_PF_LD1, - PA8_PF_LD2, - PA9_PF_LD3, - PA10_PF_LD4, - PA11_PF_LD5, - PA12_PF_LD6, - PA13_PF_LD7, - PA14_PF_LD8, - PA15_PF_LD9, - PA16_PF_LD10, - PA17_PF_LD11, - PA18_PF_LD12, - PA19_PF_LD13, - PA20_PF_LD14, - PA21_PF_LD15, - PA22_PF_LD16, - PA23_PF_LD17, - PA25_PF_CLS, - PA27_PF_SPL_SPR, - PA28_PF_HSYNC, - PA29_PF_VSYNC, - PA30_PF_CONTRAST, - PA31_PF_OE_ACD, - /* OWIRE */ - PE16_AF_OWIRE, - /* SDHC1*/ - PE18_PF_SD1_D0, - PE19_PF_SD1_D1, - PE20_PF_SD1_D2, - PE21_PF_SD1_D3, - PE22_PF_SD1_CMD, - PE23_PF_SD1_CLK, - PF8_AF_ATA_IORDY, - /* SDHC2*/ - PB4_PF_SD2_D0, - PB5_PF_SD2_D1, - PB6_PF_SD2_D2, - PB7_PF_SD2_D3, - PB8_PF_SD2_CMD, - PB9_PF_SD2_CLK, -}; - -static struct mxc_nand_platform_data mxt_td60_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -static struct imxi2c_platform_data mxt_td60_i2c_data = { - .bitrate = 100000, -}; - -/* PCA9557 */ -static int mxt_td60_pca9557_setup(struct i2c_client *client, - unsigned gpio_base, unsigned ngpio, - void *context) -{ - static int mxt_td60_gpio_value[] = { - -1, -1, -1, -1, -1, -1, -1, 1 - }; - int n; - - for (n = 0; n < ARRAY_SIZE(mxt_td60_gpio_value); ++n) { - gpio_request(gpio_base + n, "MXT_TD60 GPIO Exp"); - if (mxt_td60_gpio_value[n] < 0) - gpio_direction_input(gpio_base + n); - else - gpio_direction_output(gpio_base + n, - mxt_td60_gpio_value[n]); - gpio_export(gpio_base + n, 0); - } - - return 0; -} - -static struct pca953x_platform_data mxt_td60_pca9557_pdata = { - .gpio_base = 240, /* place PCA9557 after all MX27 gpio pins */ - .invert = 0, /* Do not invert */ - .setup = mxt_td60_pca9557_setup, -}; - -static struct i2c_board_info mxt_td60_i2c_devices[] = { - { - I2C_BOARD_INFO("pca9557", 0x18), - .platform_data = &mxt_td60_pca9557_pdata, - }, -}; - -static struct imxi2c_platform_data mxt_td60_i2c2_data = { - .bitrate = 100000, -}; - -static struct i2c_board_info mxt_td60_i2c2_devices[] = { -}; - -static struct imx_fb_videomode mxt_td60_modes[] = { - { - .mode = { - .name = "Chimei LW700AT9003", - .refresh = 60, - .xres = 800, - .yres = 480, - .pixclock = 30303, - .hsync_len = 64, - .left_margin = 0x67, - .right_margin = 0x68, - .vsync_len = 16, - .upper_margin = 0x0f, - .lower_margin = 0x0f, - }, - .bpp = 16, - .pcr = 0xFA208B83, - }, -}; - -static struct imx_fb_platform_data mxt_td60_fb_data = { - .mode = mxt_td60_modes, - .num_modes = ARRAY_SIZE(mxt_td60_modes), - - /* - * - HSYNC active high - * - VSYNC active high - * - clk notenabled while idle - * - clock inverted - * - data not inverted - * - data enable low active - * - enable sharp mode - */ - .pwmr = 0x00A903FF, - .lscr1 = 0x00120300, - .dmacr = 0x00020010, -}; - -static int mxt_td60_sdhc1_init(struct device *dev, irq_handler_t detect_irq, - void *data) -{ - return request_irq(IRQ_GPIOF(8), detect_irq, IRQF_TRIGGER_FALLING, - "sdhc1-card-detect", data); -} - -static void mxt_td60_sdhc1_exit(struct device *dev, void *data) -{ - free_irq(IRQ_GPIOF(8), data); -} - -static struct imxmmc_platform_data sdhc1_pdata = { - .init = mxt_td60_sdhc1_init, - .exit = mxt_td60_sdhc1_exit, -}; - -static struct platform_device *platform_devices[] __initdata = { - &mxc_fec_device, -}; - -static struct imxuart_platform_data uart_pdata[] = { - { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, -}; - -static void __init mxt_td60_board_init(void) -{ - mxc_gpio_setup_multiple_pins(mxt_td60_pins, ARRAY_SIZE(mxt_td60_pins), - "MXT_TD60"); - - mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); - mxc_register_device(&mxc_uart_device1, &uart_pdata[1]); - mxc_register_device(&mxc_uart_device2, &uart_pdata[2]); - mxc_register_device(&mxc_nand_device, &mxt_td60_nand_board_info); - - i2c_register_board_info(0, mxt_td60_i2c_devices, - ARRAY_SIZE(mxt_td60_i2c_devices)); - - i2c_register_board_info(1, mxt_td60_i2c2_devices, - ARRAY_SIZE(mxt_td60_i2c2_devices)); - - mxc_register_device(&mxc_i2c_device0, &mxt_td60_i2c_data); - mxc_register_device(&mxc_i2c_device1, &mxt_td60_i2c2_data); - mxc_register_device(&mxc_fb_device, &mxt_td60_fb_data); - mxc_register_device(&mxc_sdhc_device0, &sdhc1_pdata); - - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); -} - -static void __init mxt_td60_timer_init(void) -{ - mx27_clocks_init(26000000); -} - -static struct sys_timer mxt_td60_timer = { - .init = mxt_td60_timer_init, -}; - -MACHINE_START(MXT_TD60, "Maxtrack i-MXT TD60") - /* maintainer: Maxtrack Industrial */ - .phys_io = MX27_AIPI_BASE_ADDR, - .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx27_map_io, - .init_irq = mx27_init_irq, - .init_machine = mxt_td60_board_init, - .timer = &mxt_td60_timer, -MACHINE_END - diff --git a/arch/arm/mach-mx2/pca100.c b/arch/arm/mach-mx2/pca100.c deleted file mode 100644 index 55dbf5a..0000000 --- a/arch/arm/mach-mx2/pca100.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2007 Robert Schwebel , Pengutronix - * Copyright (C) 2009 Sascha Hauer (kernel@pengutronix.de) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) -#include -#endif -#include -#include -#include -#include - -#include "devices.h" - -static int pca100_pins[] = { - /* UART1 */ - PE12_PF_UART1_TXD, - PE13_PF_UART1_RXD, - PE14_PF_UART1_CTS, - PE15_PF_UART1_RTS, - /* SDHC */ - PB4_PF_SD2_D0, - PB5_PF_SD2_D1, - PB6_PF_SD2_D2, - PB7_PF_SD2_D3, - PB8_PF_SD2_CMD, - PB9_PF_SD2_CLK, - /* FEC */ - PD0_AIN_FEC_TXD0, - PD1_AIN_FEC_TXD1, - PD2_AIN_FEC_TXD2, - PD3_AIN_FEC_TXD3, - PD4_AOUT_FEC_RX_ER, - PD5_AOUT_FEC_RXD1, - PD6_AOUT_FEC_RXD2, - PD7_AOUT_FEC_RXD3, - PD8_AF_FEC_MDIO, - PD9_AIN_FEC_MDC, - PD10_AOUT_FEC_CRS, - PD11_AOUT_FEC_TX_CLK, - PD12_AOUT_FEC_RXD0, - PD13_AOUT_FEC_RX_DV, - PD14_AOUT_FEC_RX_CLK, - PD15_AOUT_FEC_COL, - PD16_AIN_FEC_TX_ER, - PF23_AIN_FEC_TX_EN, - /* SSI1 */ - PC20_PF_SSI1_FS, - PC21_PF_SSI1_RXD, - PC22_PF_SSI1_TXD, - PC23_PF_SSI1_CLK, - /* onboard I2C */ - PC5_PF_I2C2_SDA, - PC6_PF_I2C2_SCL, - /* external I2C */ - PD17_PF_I2C_DATA, - PD18_PF_I2C_CLK, - /* SPI1 */ - PD25_PF_CSPI1_RDY, - PD29_PF_CSPI1_SCLK, - PD30_PF_CSPI1_MISO, - PD31_PF_CSPI1_MOSI, -}; - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct mxc_nand_platform_data pca100_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -static struct platform_device *platform_devices[] __initdata = { - &mxc_w1_master_device, - &mxc_fec_device, -}; - -static struct imxi2c_platform_data pca100_i2c_1_data = { - .bitrate = 100000, -}; - -static struct at24_platform_data board_eeprom = { - .byte_len = 4096, - .page_size = 32, - .flags = AT24_FLAG_ADDR16, -}; - -static struct i2c_board_info pca100_i2c_devices[] = { - { - I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */ - .platform_data = &board_eeprom, - }, { - I2C_BOARD_INFO("rtc-pcf8563", 0x51), - .type = "pcf8563" - }, { - I2C_BOARD_INFO("lm75", 0x4a), - .type = "lm75" - } -}; - -#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) -static struct spi_eeprom at25320 = { - .name = "at25320an", - .byte_len = 4096, - .page_size = 32, - .flags = EE_ADDR2, -}; - -static struct spi_board_info pca100_spi_board_info[] __initdata = { - { - .modalias = "at25", - .max_speed_hz = 30000, - .bus_num = 0, - .chip_select = 1, - .platform_data = &at25320, - }, -}; - -static int pca100_spi_cs[] = {GPIO_PORTD + 28, GPIO_PORTD + 27}; - -static struct spi_imx_master pca100_spi_0_data = { - .chipselect = pca100_spi_cs, - .num_chipselect = ARRAY_SIZE(pca100_spi_cs), -}; -#endif - -static int pca100_sdhc2_init(struct device *dev, irq_handler_t detect_irq, - void *data) -{ - int ret; - - ret = request_irq(IRQ_GPIOC(29), detect_irq, - IRQF_DISABLED | IRQF_TRIGGER_FALLING, - "imx-mmc-detect", data); - if (ret) - printk(KERN_ERR - "pca100: Failed to reuest irq for sd/mmc detection\n"); - - return ret; -} - -static void pca100_sdhc2_exit(struct device *dev, void *data) -{ - free_irq(IRQ_GPIOC(29), data); -} - -static struct imxmmc_platform_data sdhc_pdata = { - .init = pca100_sdhc2_init, - .exit = pca100_sdhc2_exit, -}; - -static void __init pca100_init(void) -{ - int ret; - - ret = mxc_gpio_setup_multiple_pins(pca100_pins, - ARRAY_SIZE(pca100_pins), "PCA100"); - if (ret) - printk(KERN_ERR "pca100: Failed to setup pins (%d)\n", ret); - - mxc_register_device(&mxc_uart_device0, &uart_pdata); - - mxc_gpio_mode(GPIO_PORTC | 29 | GPIO_GPIO | GPIO_IN); - mxc_register_device(&mxc_sdhc_device1, &sdhc_pdata); - - mxc_register_device(&mxc_nand_device, &pca100_nand_board_info); - - /* only the i2c master 1 is used on this CPU card */ - i2c_register_board_info(1, pca100_i2c_devices, - ARRAY_SIZE(pca100_i2c_devices)); - - mxc_register_device(&mxc_i2c_device1, &pca100_i2c_1_data); - - mxc_gpio_mode(GPIO_PORTD | 28 | GPIO_GPIO | GPIO_OUT); - mxc_gpio_mode(GPIO_PORTD | 27 | GPIO_GPIO | GPIO_OUT); - - /* GPIO0_IRQ */ - mxc_gpio_mode(GPIO_PORTC | 31 | GPIO_GPIO | GPIO_IN); - /* GPIO1_IRQ */ - mxc_gpio_mode(GPIO_PORTC | 25 | GPIO_GPIO | GPIO_IN); - /* GPIO2_IRQ */ - mxc_gpio_mode(GPIO_PORTE | 5 | GPIO_GPIO | GPIO_IN); - -#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) - spi_register_board_info(pca100_spi_board_info, - ARRAY_SIZE(pca100_spi_board_info)); - mxc_register_device(&mxc_spi_device0, &pca100_spi_0_data); -#endif - - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); -} - -static void __init pca100_timer_init(void) -{ - mx27_clocks_init(26000000); -} - -static struct sys_timer pca100_timer = { - .init = pca100_timer_init, -}; - -MACHINE_START(PCA100, "phyCARD-i.MX27") - .phys_io = MX27_AIPI_BASE_ADDR, - .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx27_map_io, - .init_irq = mx27_init_irq, - .init_machine = pca100_init, - .timer = &pca100_timer, -MACHINE_END - diff --git a/arch/arm/mach-mx2/pcm038.c b/arch/arm/mach-mx2/pcm038.c deleted file mode 100644 index a2e2ea1..0000000 --- a/arch/arm/mach-mx2/pcm038.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright 2007 Robert Schwebel , Pengutronix - * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 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 - -#include "devices.h" - -static int pcm038_pins[] = { - /* UART1 */ - PE12_PF_UART1_TXD, - PE13_PF_UART1_RXD, - PE14_PF_UART1_CTS, - PE15_PF_UART1_RTS, - /* UART2 */ - PE3_PF_UART2_CTS, - PE4_PF_UART2_RTS, - PE6_PF_UART2_TXD, - PE7_PF_UART2_RXD, - /* UART3 */ - PE8_PF_UART3_TXD, - PE9_PF_UART3_RXD, - PE10_PF_UART3_CTS, - PE11_PF_UART3_RTS, - /* FEC */ - PD0_AIN_FEC_TXD0, - PD1_AIN_FEC_TXD1, - PD2_AIN_FEC_TXD2, - PD3_AIN_FEC_TXD3, - PD4_AOUT_FEC_RX_ER, - PD5_AOUT_FEC_RXD1, - PD6_AOUT_FEC_RXD2, - PD7_AOUT_FEC_RXD3, - PD8_AF_FEC_MDIO, - PD9_AIN_FEC_MDC, - PD10_AOUT_FEC_CRS, - PD11_AOUT_FEC_TX_CLK, - PD12_AOUT_FEC_RXD0, - PD13_AOUT_FEC_RX_DV, - PD14_AOUT_FEC_RX_CLK, - PD15_AOUT_FEC_COL, - PD16_AIN_FEC_TX_ER, - PF23_AIN_FEC_TX_EN, - /* I2C2 */ - PC5_PF_I2C2_SDA, - PC6_PF_I2C2_SCL, - /* SPI1 */ - PD25_PF_CSPI1_RDY, - PD29_PF_CSPI1_SCLK, - PD30_PF_CSPI1_MISO, - PD31_PF_CSPI1_MOSI, - /* SSI1 */ - PC20_PF_SSI1_FS, - PC21_PF_SSI1_RXD, - PC22_PF_SSI1_TXD, - PC23_PF_SSI1_CLK, - /* SSI4 */ - PC16_PF_SSI4_FS, - PC17_PF_SSI4_RXD, - PC18_PF_SSI4_TXD, - PC19_PF_SSI4_CLK, -}; - -/* - * Phytec's PCM038 comes with 2MiB battery buffered SRAM, - * 16 bit width - */ - -static struct platdata_mtd_ram pcm038_sram_data = { - .bankwidth = 2, -}; - -static struct resource pcm038_sram_resource = { - .start = MX27_CS1_BASE_ADDR, - .end = MX27_CS1_BASE_ADDR + 512 * 1024 - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device pcm038_sram_mtd_device = { - .name = "mtd-ram", - .id = 0, - .dev = { - .platform_data = &pcm038_sram_data, - }, - .num_resources = 1, - .resource = &pcm038_sram_resource, -}; - -/* - * Phytec's phyCORE-i.MX27 comes with 32MiB flash, - * 16 bit width - */ -static struct physmap_flash_data pcm038_flash_data = { - .width = 2, -}; - -static struct resource pcm038_flash_resource = { - .start = 0xc0000000, - .end = 0xc1ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device pcm038_nor_mtd_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &pcm038_flash_data, - }, - .num_resources = 1, - .resource = &pcm038_flash_resource, -}; - -static struct imxuart_platform_data uart_pdata[] = { - { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, { - .flags = IMXUART_HAVE_RTSCTS, - }, -}; - -static struct mxc_nand_platform_data pcm038_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -static struct platform_device *platform_devices[] __initdata = { - &pcm038_nor_mtd_device, - &mxc_w1_master_device, - &mxc_fec_device, - &pcm038_sram_mtd_device, -}; - -/* On pcm038 there's a sram attached to CS1, we enable the chipselect here and - * setup other stuffs to access the sram. */ -static void __init pcm038_init_sram(void) -{ - __raw_writel(0x0000d843, CSCR_U(1)); - __raw_writel(0x22252521, CSCR_L(1)); - __raw_writel(0x22220a00, CSCR_A(1)); -} - -static struct imxi2c_platform_data pcm038_i2c_1_data = { - .bitrate = 100000, -}; - -static struct at24_platform_data board_eeprom = { - .byte_len = 4096, - .page_size = 32, - .flags = AT24_FLAG_ADDR16, -}; - -static struct i2c_board_info pcm038_i2c_devices[] = { - { - I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */ - .platform_data = &board_eeprom, - }, { - I2C_BOARD_INFO("pcf8563", 0x51), - }, { - I2C_BOARD_INFO("lm75", 0x4a), - } -}; - -static int pcm038_spi_cs[] = {GPIO_PORTD + 28}; - -static struct spi_imx_master pcm038_spi_0_data = { - .chipselect = pcm038_spi_cs, - .num_chipselect = ARRAY_SIZE(pcm038_spi_cs), -}; - -static struct regulator_consumer_supply sdhc1_consumers[] = { - { - .dev = &mxc_sdhc_device1.dev, - .supply = "sdhc_vcc", - }, -}; - -static struct regulator_init_data sdhc1_data = { - .constraints = { - .min_uV = 3000000, - .max_uV = 3400000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_FAST, - .always_on = 0, - .boot_on = 0, - }, - .num_consumer_supplies = ARRAY_SIZE(sdhc1_consumers), - .consumer_supplies = sdhc1_consumers, -}; - -static struct regulator_consumer_supply cam_consumers[] = { - { - .dev = NULL, - .supply = "imx_cam_vcc", - }, -}; - -static struct regulator_init_data cam_data = { - .constraints = { - .min_uV = 3000000, - .max_uV = 3400000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_FAST, - .always_on = 0, - .boot_on = 0, - }, - .num_consumer_supplies = ARRAY_SIZE(cam_consumers), - .consumer_supplies = cam_consumers, -}; - -struct mc13783_regulator_init_data pcm038_regulators[] = { - { - .id = MC13783_REGU_VCAM, - .init_data = &cam_data, - }, { - .id = MC13783_REGU_VMMC1, - .init_data = &sdhc1_data, - }, -}; - -static struct mc13783_platform_data pcm038_pmic = { - .regulators = pcm038_regulators, - .num_regulators = ARRAY_SIZE(pcm038_regulators), - .flags = MC13783_USE_ADC | MC13783_USE_REGULATOR | - MC13783_USE_TOUCHSCREEN, -}; - -static struct spi_board_info pcm038_spi_board_info[] __initdata = { - { - .modalias = "mc13783", - .irq = IRQ_GPIOB(23), - .max_speed_hz = 300000, - .bus_num = 0, - .chip_select = 0, - .platform_data = &pcm038_pmic, - .mode = SPI_CS_HIGH, - } -}; - -static void __init pcm038_init(void) -{ - mxc_gpio_setup_multiple_pins(pcm038_pins, ARRAY_SIZE(pcm038_pins), - "PCM038"); - - pcm038_init_sram(); - - mxc_register_device(&mxc_uart_device0, &uart_pdata[0]); - mxc_register_device(&mxc_uart_device1, &uart_pdata[1]); - mxc_register_device(&mxc_uart_device2, &uart_pdata[2]); - - mxc_gpio_mode(PE16_AF_OWIRE); - mxc_register_device(&mxc_nand_device, &pcm038_nand_board_info); - - /* only the i2c master 1 is used on this CPU card */ - i2c_register_board_info(1, pcm038_i2c_devices, - ARRAY_SIZE(pcm038_i2c_devices)); - - mxc_register_device(&mxc_i2c_device1, &pcm038_i2c_1_data); - - /* PE18 for user-LED D40 */ - mxc_gpio_mode(GPIO_PORTE | 18 | GPIO_GPIO | GPIO_OUT); - - mxc_gpio_mode(GPIO_PORTD | 28 | GPIO_GPIO | GPIO_OUT); - - /* MC13783 IRQ */ - mxc_gpio_mode(GPIO_PORTB | 23 | GPIO_GPIO | GPIO_IN); - - mxc_register_device(&mxc_spi_device0, &pcm038_spi_0_data); - spi_register_board_info(pcm038_spi_board_info, - ARRAY_SIZE(pcm038_spi_board_info)); - - platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); - -#ifdef CONFIG_MACH_PCM970_BASEBOARD - pcm970_baseboard_init(); -#endif -} - -static void __init pcm038_timer_init(void) -{ - mx27_clocks_init(26000000); -} - -static struct sys_timer pcm038_timer = { - .init = pcm038_timer_init, -}; - -MACHINE_START(PCM038, "phyCORE-i.MX27") - .phys_io = MX27_AIPI_BASE_ADDR, - .io_pg_offst = ((MX27_AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx27_map_io, - .init_irq = mx27_init_irq, - .init_machine = pcm038_init, - .timer = &pcm038_timer, -MACHINE_END -- cgit v0.10.2 From edc34a90613bbb393c019882f85b74f24066ca19 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 4 Jan 2010 18:24:49 +0000 Subject: mx31ads: Convert 1133-EV1 to use dev_name to specify consumer devices This means we don't need to go running round working out struct devices at runtime. Signed-off-by: Mark Brown Signed-off-by: Sascha Hauer diff --git a/arch/arm/mach-mx3/mx31ads.c b/arch/arm/mach-mx3/mx31ads.c index 0497c15..cda570b 100644 --- a/arch/arm/mach-mx3/mx31ads.c +++ b/arch/arm/mach-mx3/mx31ads.c @@ -307,12 +307,8 @@ static struct regulator_init_data ldo1_data = { }; static struct regulator_consumer_supply ldo2_consumers[] = { - { - .supply = "AVDD", - }, - { - .supply = "HPVDD", - }, + { .supply = "AVDD", .dev_name = "1-001a" }, + { .supply = "HPVDD", .dev_name = "1-001a" }, }; /* CODEC and SIM */ @@ -382,8 +378,6 @@ static struct wm8350_audio_platform_data imx32ads_wm8350_setup = { static int mx31_wm8350_init(struct wm8350 *wm8350) { - int i; - wm8350_gpio_config(wm8350, 0, WM8350_GPIO_DIR_IN, WM8350_GPIO0_PWR_ON_IN, WM8350_GPIO_ACTIVE_LOW, WM8350_GPIO_PULL_UP, WM8350_GPIO_INVERT_OFF, @@ -419,10 +413,6 @@ static int mx31_wm8350_init(struct wm8350 *wm8350) WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, WM8350_GPIO_DEBOUNCE_OFF); - /* Fix up for our own supplies. */ - for (i = 0; i < ARRAY_SIZE(ldo2_consumers); i++) - ldo2_consumers[i].dev = wm8350->dev; - wm8350_register_regulator(wm8350, WM8350_DCDC_1, &sw1a_data); wm8350_register_regulator(wm8350, WM8350_DCDC_3, &viohi_data); wm8350_register_regulator(wm8350, WM8350_DCDC_4, &violo_data); -- cgit v0.10.2 From becc670a1bda8f240a93e45d9794f9f79713ffdb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 4 Jan 2010 18:24:50 +0000 Subject: mx31ads: Allow enable/disable of switchable supplies They will be automatically powered off at startup so users will need to enable them for use. Signed-off-by: Mark Brown Signed-off-by: Sascha Hauer diff --git a/arch/arm/mach-mx3/mx31ads.c b/arch/arm/mach-mx3/mx31ads.c index cda570b..718fbe6 100644 --- a/arch/arm/mach-mx3/mx31ads.c +++ b/arch/arm/mach-mx3/mx31ads.c @@ -302,6 +302,7 @@ static struct regulator_init_data ldo1_data = { .min_uV = 2800000, .max_uV = 2800000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .apply_uV = 1, }, }; @@ -318,6 +319,7 @@ static struct regulator_init_data ldo2_data = { .min_uV = 3300000, .max_uV = 3300000, .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, .apply_uV = 1, }, .num_consumer_supplies = ARRAY_SIZE(ldo2_consumers), -- cgit v0.10.2 From 6a4ef48103a78a46b80e07fcd8ac4edda0c7128f Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Thu, 31 Dec 2009 12:04:58 -0800 Subject: ceph: fix copy_user_to_page_vector() The function was broken in the case where there was more than one page involved, broke the ceph sync_write case. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil diff --git a/fs/ceph/file.c b/fs/ceph/file.c index fc8aff4..2d88c80 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -350,10 +350,10 @@ static int copy_user_to_page_vector(struct page **pages, return -EFAULT; data += l - bad; left -= l - bad; - if (po) { - po += l - bad; - if (po == PAGE_CACHE_SIZE) - po = 0; + po += l - bad; + if (po == PAGE_CACHE_SIZE) { + po = 0; + i++; } } return len; -- cgit v0.10.2 From 321ed164369bb5e4888eb24d70ebc64c232ffc31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 10 Dec 2009 10:41:26 +0100 Subject: imx/mx3: rename files defining a machine to mach-$mach.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While at it remove some superfluous parenthesis. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Daniel Mack Cc: Fabio Estevam Cc: Valentin Longchamp diff --git a/arch/arm/mach-mx3/Makefile b/arch/arm/mach-mx3/Makefile index 03e25d6..670fbb5 100644 --- a/arch/arm/mach-mx3/Makefile +++ b/arch/arm/mach-mx3/Makefile @@ -7,16 +7,16 @@ obj-y := mm.o devices.o cpu.o obj-$(CONFIG_ARCH_MX31) += clock-imx31.o iomux-imx31.o obj-$(CONFIG_ARCH_MX35) += clock-imx35.o -obj-$(CONFIG_MACH_MX31ADS) += mx31ads.o -obj-$(CONFIG_MACH_MX31LILLY) += mx31lilly.o mx31lilly-db.o -obj-$(CONFIG_MACH_MX31LITE) += mx31lite.o mx31lite-db.o -obj-$(CONFIG_MACH_PCM037) += pcm037.o -obj-$(CONFIG_MACH_PCM037_EET) += pcm037_eet.o -obj-$(CONFIG_MACH_MX31_3DS) += mx31pdk.o -obj-$(CONFIG_MACH_MX31MOBOARD) += mx31moboard.o mx31moboard-devboard.o \ +obj-$(CONFIG_MACH_MX31ADS) += mach-mx31ads.o +obj-$(CONFIG_MACH_MX31LILLY) += mach-mx31lilly.o mx31lilly-db.o +obj-$(CONFIG_MACH_MX31LITE) += mach-mx31lite.o mx31lite-db.o +obj-$(CONFIG_MACH_PCM037) += mach-pcm037.o +obj-$(CONFIG_MACH_PCM037_EET) += mach-pcm037_eet.o +obj-$(CONFIG_MACH_MX31_3DS) += mach-mx31_3ds.o +obj-$(CONFIG_MACH_MX31MOBOARD) += mach-mx31moboard.o mx31moboard-devboard.o \ mx31moboard-marxbot.o -obj-$(CONFIG_MACH_QONG) += qong.o -obj-$(CONFIG_MACH_PCM043) += pcm043.o -obj-$(CONFIG_MACH_ARMADILLO5X0) += armadillo5x0.o -obj-$(CONFIG_MACH_MX35_3DS) += mx35pdk.o -obj-$(CONFIG_MACH_KZM_ARM11_01) += kzmarm11.o +obj-$(CONFIG_MACH_QONG) += mach-qong.o +obj-$(CONFIG_MACH_PCM043) += mach-pcm043.o +obj-$(CONFIG_MACH_ARMADILLO5X0) += mach-armadillo5x0.o +obj-$(CONFIG_MACH_MX35_3DS) += mach-mx35pdk.o +obj-$(CONFIG_MACH_KZM_ARM11_01) += mach-kzm_arm11_01.o diff --git a/arch/arm/mach-mx3/armadillo5x0.c b/arch/arm/mach-mx3/armadillo5x0.c deleted file mode 100644 index aac5f81..0000000 --- a/arch/arm/mach-mx3/armadillo5x0.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * armadillo5x0.c - * - * Copyright 2009 Alberto Panizzo - * updates in http://alberdroid.blogspot.com/ - * - * Based on Atmark Techno, Inc. armadillo 500 BSP 2008 - * Based on mx31ads.c and pcm037.c Great Work! - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, 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 -#include -#include -#include -#include -#include -#include - -#include "devices.h" -#include "crm_regs.h" - -static int armadillo5x0_pins[] = { - /* UART1 */ - MX31_PIN_CTS1__CTS1, - MX31_PIN_RTS1__RTS1, - MX31_PIN_TXD1__TXD1, - MX31_PIN_RXD1__RXD1, - /* UART2 */ - MX31_PIN_CTS2__CTS2, - MX31_PIN_RTS2__RTS2, - MX31_PIN_TXD2__TXD2, - MX31_PIN_RXD2__RXD2, - /* LAN9118_IRQ */ - IOMUX_MODE(MX31_PIN_GPIO1_0, IOMUX_CONFIG_GPIO), - /* SDHC1 */ - MX31_PIN_SD1_DATA3__SD1_DATA3, - MX31_PIN_SD1_DATA2__SD1_DATA2, - MX31_PIN_SD1_DATA1__SD1_DATA1, - MX31_PIN_SD1_DATA0__SD1_DATA0, - MX31_PIN_SD1_CLK__SD1_CLK, - MX31_PIN_SD1_CMD__SD1_CMD, - /* Framebuffer */ - MX31_PIN_LD0__LD0, - MX31_PIN_LD1__LD1, - MX31_PIN_LD2__LD2, - MX31_PIN_LD3__LD3, - MX31_PIN_LD4__LD4, - MX31_PIN_LD5__LD5, - MX31_PIN_LD6__LD6, - MX31_PIN_LD7__LD7, - MX31_PIN_LD8__LD8, - MX31_PIN_LD9__LD9, - MX31_PIN_LD10__LD10, - MX31_PIN_LD11__LD11, - MX31_PIN_LD12__LD12, - MX31_PIN_LD13__LD13, - MX31_PIN_LD14__LD14, - MX31_PIN_LD15__LD15, - MX31_PIN_LD16__LD16, - MX31_PIN_LD17__LD17, - MX31_PIN_VSYNC3__VSYNC3, - MX31_PIN_HSYNC__HSYNC, - MX31_PIN_FPSHIFT__FPSHIFT, - MX31_PIN_DRDY0__DRDY0, - IOMUX_MODE(MX31_PIN_LCS1, IOMUX_CONFIG_GPIO), /*ADV7125_PSAVE*/ - /* I2C2 */ - MX31_PIN_CSPI2_MOSI__SCL, - MX31_PIN_CSPI2_MISO__SDA, -}; - -/* RTC over I2C*/ -#define ARMADILLO5X0_RTC_GPIO IOMUX_TO_GPIO(MX31_PIN_SRXD4) - -static struct i2c_board_info armadillo5x0_i2c_rtc = { - I2C_BOARD_INFO("s35390a", 0x30), -}; - -/* GPIO BUTTONS */ -static struct gpio_keys_button armadillo5x0_buttons[] = { - { - .code = KEY_ENTER, /*28*/ - .gpio = IOMUX_TO_GPIO(MX31_PIN_SCLK0), - .active_low = 1, - .desc = "menu", - .wakeup = 1, - }, { - .code = KEY_BACK, /*158*/ - .gpio = IOMUX_TO_GPIO(MX31_PIN_SRST0), - .active_low = 1, - .desc = "back", - .wakeup = 1, - } -}; - -static struct gpio_keys_platform_data armadillo5x0_button_data = { - .buttons = armadillo5x0_buttons, - .nbuttons = ARRAY_SIZE(armadillo5x0_buttons), -}; - -static struct platform_device armadillo5x0_button_device = { - .name = "gpio-keys", - .id = -1, - .num_resources = 0, - .dev = { - .platform_data = &armadillo5x0_button_data, - } -}; - -/* - * NAND Flash - */ -static struct mxc_nand_platform_data armadillo5x0_nand_flash_pdata = { - .width = 1, - .hw_ecc = 1, -}; - -/* - * MTD NOR Flash - */ -static struct mtd_partition armadillo5x0_nor_flash_partitions[] = { - { - .name = "nor.bootloader", - .offset = 0x00000000, - .size = 4*32*1024, - }, { - .name = "nor.kernel", - .offset = MTDPART_OFS_APPEND, - .size = 16*128*1024, - }, { - .name = "nor.userland", - .offset = MTDPART_OFS_APPEND, - .size = 110*128*1024, - }, { - .name = "nor.config", - .offset = MTDPART_OFS_APPEND, - .size = 1*128*1024, - }, -}; - -static struct physmap_flash_data armadillo5x0_nor_flash_pdata = { - .width = 2, - .parts = armadillo5x0_nor_flash_partitions, - .nr_parts = ARRAY_SIZE(armadillo5x0_nor_flash_partitions), -}; - -static struct resource armadillo5x0_nor_flash_resource = { - .flags = IORESOURCE_MEM, - .start = MX31_CS0_BASE_ADDR, - .end = MX31_CS0_BASE_ADDR + SZ_64M - 1, -}; - -static struct platform_device armadillo5x0_nor_flash = { - .name = "physmap-flash", - .id = -1, - .num_resources = 1, - .resource = &armadillo5x0_nor_flash_resource, -}; - -/* - * FB support - */ -static const struct fb_videomode fb_modedb[] = { - { /* 640x480 @ 60 Hz */ - .name = "CRT-VGA", - .refresh = 60, - .xres = 640, - .yres = 480, - .pixclock = 39721, - .left_margin = 35, - .right_margin = 115, - .upper_margin = 43, - .lower_margin = 1, - .hsync_len = 10, - .vsync_len = 1, - .sync = FB_SYNC_OE_ACT_HIGH, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, {/* 800x600 @ 56 Hz */ - .name = "CRT-SVGA", - .refresh = 56, - .xres = 800, - .yres = 600, - .pixclock = 30000, - .left_margin = 30, - .right_margin = 108, - .upper_margin = 13, - .lower_margin = 10, - .hsync_len = 10, - .vsync_len = 1, - .sync = FB_SYNC_OE_ACT_HIGH | FB_SYNC_HOR_HIGH_ACT | - FB_SYNC_VERT_HIGH_ACT, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, -}; - -static struct ipu_platform_data mx3_ipu_data = { - .irq_base = MXC_IPU_IRQ_START, -}; - -static struct mx3fb_platform_data mx3fb_pdata = { - .dma_dev = &mx3_ipu.dev, - .name = "CRT-VGA", - .mode = fb_modedb, - .num_modes = ARRAY_SIZE(fb_modedb), -}; - -/* - * SDHC 1 - * MMC support - */ -static int armadillo5x0_sdhc1_get_ro(struct device *dev) -{ - return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_ATA_RESET_B)); -} - -static int armadillo5x0_sdhc1_init(struct device *dev, - irq_handler_t detect_irq, void *data) -{ - int ret; - int gpio_det, gpio_wp; - - gpio_det = IOMUX_TO_GPIO(MX31_PIN_ATA_DMACK); - gpio_wp = IOMUX_TO_GPIO(MX31_PIN_ATA_RESET_B); - - ret = gpio_request(gpio_det, "sdhc-card-detect"); - if (ret) - return ret; - - gpio_direction_input(gpio_det); - - ret = gpio_request(gpio_wp, "sdhc-write-protect"); - if (ret) - goto err_gpio_free; - - gpio_direction_input(gpio_wp); - - /* When supported the trigger type have to be BOTH */ - ret = request_irq(IOMUX_TO_IRQ(MX31_PIN_ATA_DMACK), detect_irq, - IRQF_DISABLED | IRQF_TRIGGER_FALLING, - "sdhc-detect", data); - - if (ret) - goto err_gpio_free_2; - - return 0; - -err_gpio_free_2: - gpio_free(gpio_wp); - -err_gpio_free: - gpio_free(gpio_det); - - return ret; - -} - -static void armadillo5x0_sdhc1_exit(struct device *dev, void *data) -{ - free_irq(IOMUX_TO_IRQ(MX31_PIN_ATA_DMACK), data); - gpio_free(IOMUX_TO_GPIO(MX31_PIN_ATA_DMACK)); - gpio_free(IOMUX_TO_GPIO(MX31_PIN_ATA_RESET_B)); -} - -static struct imxmmc_platform_data sdhc_pdata = { - .get_ro = armadillo5x0_sdhc1_get_ro, - .init = armadillo5x0_sdhc1_init, - .exit = armadillo5x0_sdhc1_exit, -}; - -/* - * SMSC 9118 - * Network support - */ -static struct resource armadillo5x0_smc911x_resources[] = { - { - .start = MX31_CS3_BASE_ADDR, - .end = MX31_CS3_BASE_ADDR + SZ_32M - 1, - .flags = IORESOURCE_MEM, - }, { - .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), - .end = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, - }, -}; - -static struct smsc911x_platform_config smsc911x_info = { - .flags = SMSC911X_USE_16BIT, - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, -}; - -static struct platform_device armadillo5x0_smc911x_device = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(armadillo5x0_smc911x_resources), - .resource = armadillo5x0_smc911x_resources, - .dev = { - .platform_data = &smsc911x_info, - }, -}; - -/* UART device data */ -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct platform_device *devices[] __initdata = { - &armadillo5x0_smc911x_device, - &mxc_i2c_device1, - &armadillo5x0_button_device, -}; - -/* - * Perform board specific initializations - */ -static void __init armadillo5x0_init(void) -{ - mxc_iomux_setup_multiple_pins(armadillo5x0_pins, - ARRAY_SIZE(armadillo5x0_pins), "armadillo5x0"); - - platform_add_devices(devices, ARRAY_SIZE(devices)); - - /* Register UART */ - mxc_register_device(&mxc_uart_device0, &uart_pdata); - mxc_register_device(&mxc_uart_device1, &uart_pdata); - - /* SMSC9118 IRQ pin */ - gpio_direction_input(MX31_PIN_GPIO1_0); - - /* Register SDHC */ - mxc_register_device(&mxcsdhc_device0, &sdhc_pdata); - - /* Register FB */ - mxc_register_device(&mx3_ipu, &mx3_ipu_data); - mxc_register_device(&mx3_fb, &mx3fb_pdata); - - /* Register NOR Flash */ - mxc_register_device(&armadillo5x0_nor_flash, - &armadillo5x0_nor_flash_pdata); - - /* Register NAND Flash */ - mxc_register_device(&mxc_nand_device, &armadillo5x0_nand_flash_pdata); - - /* set NAND page size to 2k if not configured via boot mode pins */ - __raw_writel(__raw_readl(MXC_CCM_RCSR) | (1 << 30), MXC_CCM_RCSR); - - /* RTC */ - /* Get RTC IRQ and register the chip */ - if (gpio_request(ARMADILLO5X0_RTC_GPIO, "rtc") == 0) { - if (gpio_direction_input(ARMADILLO5X0_RTC_GPIO) == 0) - armadillo5x0_i2c_rtc.irq = gpio_to_irq(ARMADILLO5X0_RTC_GPIO); - else - gpio_free(ARMADILLO5X0_RTC_GPIO); - } - if (armadillo5x0_i2c_rtc.irq == 0) - pr_warning("armadillo5x0_init: failed to get RTC IRQ\n"); - i2c_register_board_info(1, &armadillo5x0_i2c_rtc, 1); -} - -static void __init armadillo5x0_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -static struct sys_timer armadillo5x0_timer = { - .init = armadillo5x0_timer_init, -}; - -MACHINE_START(ARMADILLO5X0, "Armadillo-500") - /* Maintainer: Alberto Panizzo */ - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x00000100, - .map_io = mx31_map_io, - .init_irq = mx31_init_irq, - .timer = &armadillo5x0_timer, - .init_machine = armadillo5x0_init, -MACHINE_END diff --git a/arch/arm/mach-mx3/kzmarm11.c b/arch/arm/mach-mx3/kzmarm11.c deleted file mode 100644 index 849631e..0000000 --- a/arch/arm/mach-mx3/kzmarm11.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * KZM-ARM11-01 support - * Copyright (C) 2009 Yoichi Yuasa - * - * based on code for MX31ADS, - * Copyright (C) 2000 Deep Blue Solutions Ltd - * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * Copyright 2005-2007 Freescale Semiconductor, Inc. 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 as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) -/* - * KZM-ARM11-01 has an external UART on FPGA - */ -static struct plat_serial8250_port serial_platform_data[] = { - { - .membase = IO_ADDRESS(KZM_ARM11_16550), - .mapbase = KZM_ARM11_16550, - .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_1), - .irqflags = IRQ_TYPE_EDGE_RISING, - .uartclk = 14745600, - .regshift = 0, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | - UPF_BUGGY_UART, - }, - {}, -}; - -static struct resource serial8250_resources[] = { - { - .start = KZM_ARM11_16550, - .end = KZM_ARM11_16550 + 0x10, - .flags = IORESOURCE_MEM, - }, - { - .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_1), - .end = IOMUX_TO_IRQ(MX31_PIN_GPIO1_1), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device serial_device = { - .name = "serial8250", - .id = PLAT8250_DEV_PLATFORM, - .dev = { - .platform_data = serial_platform_data, - }, - .num_resources = ARRAY_SIZE(serial8250_resources), - .resource = serial8250_resources, -}; - -static int __init kzm_init_ext_uart(void) -{ - u8 tmp; - - /* - * GPIO 1-1: external UART interrupt line - */ - mxc_iomux_mode(IOMUX_MODE(MX31_PIN_GPIO1_1, IOMUX_CONFIG_GPIO)); - gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1), "ext-uart-int"); - gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1)); - - /* - * Unmask UART interrupt - */ - tmp = __raw_readb(IO_ADDRESS(KZM_ARM11_CTL1)); - tmp |= 0x2; - __raw_writeb(tmp, IO_ADDRESS(KZM_ARM11_CTL1)); - - return platform_device_register(&serial_device); -} -#else -static inline int kzm_init_ext_uart(void) -{ - return 0; -} -#endif - -/* - * SMSC LAN9118 - */ -#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE) -static struct smsc911x_platform_config kzm_smsc9118_config = { - .phy_interface = PHY_INTERFACE_MODE_MII, - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH, - .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, - .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS, -}; - -static struct resource kzm_smsc9118_resources[] = { - { - .start = MX31_CS5_BASE_ADDR, - .end = MX31_CS5_BASE_ADDR + SZ_128K - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_2), - .end = IOMUX_TO_IRQ(MX31_PIN_GPIO1_2), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, - }, -}; - -static struct platform_device kzm_smsc9118_device = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(kzm_smsc9118_resources), - .resource = kzm_smsc9118_resources, - .dev = { - .platform_data = &kzm_smsc9118_config, - }, -}; - -static int __init kzm_init_smsc9118(void) -{ - /* - * GPIO 1-2: SMSC9118 interrupt line - */ - mxc_iomux_mode(IOMUX_MODE(MX31_PIN_GPIO1_2, IOMUX_CONFIG_GPIO)); - gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2), "smsc9118-int"); - gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2)); - - return platform_device_register(&kzm_smsc9118_device); -} -#else -static inline int kzm_init_smsc9118(void) -{ - return 0; -} -#endif - -#if defined(CONFIG_SERIAL_IMX) || defined(CONFIG_SERIAL_IMX_MODULE) -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static void __init kzm_init_imx_uart(void) -{ - mxc_register_device(&mxc_uart_device0, &uart_pdata); - - mxc_register_device(&mxc_uart_device1, &uart_pdata); -} -#else -static inline void kzm_init_imx_uart(void) -{ -} -#endif - -static int kzm_pins[] __initdata = { - MX31_PIN_CTS1__CTS1, - MX31_PIN_RTS1__RTS1, - MX31_PIN_TXD1__TXD1, - MX31_PIN_RXD1__RXD1, - MX31_PIN_DCD_DCE1__DCD_DCE1, - MX31_PIN_RI_DCE1__RI_DCE1, - MX31_PIN_DSR_DCE1__DSR_DCE1, - MX31_PIN_DTR_DCE1__DTR_DCE1, - MX31_PIN_CTS2__CTS2, - MX31_PIN_RTS2__RTS2, - MX31_PIN_TXD2__TXD2, - MX31_PIN_RXD2__RXD2, - MX31_PIN_DCD_DTE1__DCD_DTE2, - MX31_PIN_RI_DTE1__RI_DTE2, - MX31_PIN_DSR_DTE1__DSR_DTE2, - MX31_PIN_DTR_DTE1__DTR_DTE2, -}; - -/* - * Board specific initialization. - */ -static void __init kzm_board_init(void) -{ - mxc_iomux_setup_multiple_pins(kzm_pins, - ARRAY_SIZE(kzm_pins), "kzm"); - kzm_init_ext_uart(); - kzm_init_smsc9118(); - kzm_init_imx_uart(); - - pr_info("Clock input source is 26MHz\n"); -} - -/* - * This structure defines static mappings for the kzm-arm11-01 board. - */ -static struct map_desc kzm_io_desc[] __initdata = { - { - .virtual = MX31_CS4_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), - .length = MX31_CS4_SIZE, - .type = MT_DEVICE - }, - { - .virtual = MX31_CS5_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(MX31_CS5_BASE_ADDR), - .length = MX31_CS5_SIZE, - .type = MT_DEVICE - }, -}; - -/* - * Set up static virtual mappings. - */ -static void __init kzm_map_io(void) -{ - mx31_map_io(); - iotable_init(kzm_io_desc, ARRAY_SIZE(kzm_io_desc)); -} - -static void __init kzm_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -static struct sys_timer kzm_timer = { - .init = kzm_timer_init, -}; - -/* - * The following uses standard kernel macros define in arch.h in order to - * initialize __mach_desc_KZM_ARM11_01 data structure. - */ -MACHINE_START(KZM_ARM11_01, "Kyoto Microcomputer Co., Ltd. KZM-ARM11-01") - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = kzm_map_io, - .init_irq = mx31_init_irq, - .init_machine = kzm_board_init, - .timer = &kzm_timer, -MACHINE_END diff --git a/arch/arm/mach-mx3/mach-armadillo5x0.c b/arch/arm/mach-mx3/mach-armadillo5x0.c new file mode 100644 index 0000000..1fed146 --- /dev/null +++ b/arch/arm/mach-mx3/mach-armadillo5x0.c @@ -0,0 +1,416 @@ +/* + * armadillo5x0.c + * + * Copyright 2009 Alberto Panizzo + * updates in http://alberdroid.blogspot.com/ + * + * Based on Atmark Techno, Inc. armadillo 500 BSP 2008 + * Based on mx31ads.c and pcm037.c Great Work! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 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 +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "crm_regs.h" + +static int armadillo5x0_pins[] = { + /* UART1 */ + MX31_PIN_CTS1__CTS1, + MX31_PIN_RTS1__RTS1, + MX31_PIN_TXD1__TXD1, + MX31_PIN_RXD1__RXD1, + /* UART2 */ + MX31_PIN_CTS2__CTS2, + MX31_PIN_RTS2__RTS2, + MX31_PIN_TXD2__TXD2, + MX31_PIN_RXD2__RXD2, + /* LAN9118_IRQ */ + IOMUX_MODE(MX31_PIN_GPIO1_0, IOMUX_CONFIG_GPIO), + /* SDHC1 */ + MX31_PIN_SD1_DATA3__SD1_DATA3, + MX31_PIN_SD1_DATA2__SD1_DATA2, + MX31_PIN_SD1_DATA1__SD1_DATA1, + MX31_PIN_SD1_DATA0__SD1_DATA0, + MX31_PIN_SD1_CLK__SD1_CLK, + MX31_PIN_SD1_CMD__SD1_CMD, + /* Framebuffer */ + MX31_PIN_LD0__LD0, + MX31_PIN_LD1__LD1, + MX31_PIN_LD2__LD2, + MX31_PIN_LD3__LD3, + MX31_PIN_LD4__LD4, + MX31_PIN_LD5__LD5, + MX31_PIN_LD6__LD6, + MX31_PIN_LD7__LD7, + MX31_PIN_LD8__LD8, + MX31_PIN_LD9__LD9, + MX31_PIN_LD10__LD10, + MX31_PIN_LD11__LD11, + MX31_PIN_LD12__LD12, + MX31_PIN_LD13__LD13, + MX31_PIN_LD14__LD14, + MX31_PIN_LD15__LD15, + MX31_PIN_LD16__LD16, + MX31_PIN_LD17__LD17, + MX31_PIN_VSYNC3__VSYNC3, + MX31_PIN_HSYNC__HSYNC, + MX31_PIN_FPSHIFT__FPSHIFT, + MX31_PIN_DRDY0__DRDY0, + IOMUX_MODE(MX31_PIN_LCS1, IOMUX_CONFIG_GPIO), /*ADV7125_PSAVE*/ + /* I2C2 */ + MX31_PIN_CSPI2_MOSI__SCL, + MX31_PIN_CSPI2_MISO__SDA, +}; + +/* RTC over I2C*/ +#define ARMADILLO5X0_RTC_GPIO IOMUX_TO_GPIO(MX31_PIN_SRXD4) + +static struct i2c_board_info armadillo5x0_i2c_rtc = { + I2C_BOARD_INFO("s35390a", 0x30), +}; + +/* GPIO BUTTONS */ +static struct gpio_keys_button armadillo5x0_buttons[] = { + { + .code = KEY_ENTER, /*28*/ + .gpio = IOMUX_TO_GPIO(MX31_PIN_SCLK0), + .active_low = 1, + .desc = "menu", + .wakeup = 1, + }, { + .code = KEY_BACK, /*158*/ + .gpio = IOMUX_TO_GPIO(MX31_PIN_SRST0), + .active_low = 1, + .desc = "back", + .wakeup = 1, + } +}; + +static struct gpio_keys_platform_data armadillo5x0_button_data = { + .buttons = armadillo5x0_buttons, + .nbuttons = ARRAY_SIZE(armadillo5x0_buttons), +}; + +static struct platform_device armadillo5x0_button_device = { + .name = "gpio-keys", + .id = -1, + .num_resources = 0, + .dev = { + .platform_data = &armadillo5x0_button_data, + } +}; + +/* + * NAND Flash + */ +static struct mxc_nand_platform_data armadillo5x0_nand_flash_pdata = { + .width = 1, + .hw_ecc = 1, +}; + +/* + * MTD NOR Flash + */ +static struct mtd_partition armadillo5x0_nor_flash_partitions[] = { + { + .name = "nor.bootloader", + .offset = 0x00000000, + .size = 4*32*1024, + }, { + .name = "nor.kernel", + .offset = MTDPART_OFS_APPEND, + .size = 16*128*1024, + }, { + .name = "nor.userland", + .offset = MTDPART_OFS_APPEND, + .size = 110*128*1024, + }, { + .name = "nor.config", + .offset = MTDPART_OFS_APPEND, + .size = 1*128*1024, + }, +}; + +static struct physmap_flash_data armadillo5x0_nor_flash_pdata = { + .width = 2, + .parts = armadillo5x0_nor_flash_partitions, + .nr_parts = ARRAY_SIZE(armadillo5x0_nor_flash_partitions), +}; + +static struct resource armadillo5x0_nor_flash_resource = { + .flags = IORESOURCE_MEM, + .start = MX31_CS0_BASE_ADDR, + .end = MX31_CS0_BASE_ADDR + SZ_64M - 1, +}; + +static struct platform_device armadillo5x0_nor_flash = { + .name = "physmap-flash", + .id = -1, + .num_resources = 1, + .resource = &armadillo5x0_nor_flash_resource, +}; + +/* + * FB support + */ +static const struct fb_videomode fb_modedb[] = { + { /* 640x480 @ 60 Hz */ + .name = "CRT-VGA", + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 39721, + .left_margin = 35, + .right_margin = 115, + .upper_margin = 43, + .lower_margin = 1, + .hsync_len = 10, + .vsync_len = 1, + .sync = FB_SYNC_OE_ACT_HIGH, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, {/* 800x600 @ 56 Hz */ + .name = "CRT-SVGA", + .refresh = 56, + .xres = 800, + .yres = 600, + .pixclock = 30000, + .left_margin = 30, + .right_margin = 108, + .upper_margin = 13, + .lower_margin = 10, + .hsync_len = 10, + .vsync_len = 1, + .sync = FB_SYNC_OE_ACT_HIGH | FB_SYNC_HOR_HIGH_ACT | + FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, +}; + +static struct ipu_platform_data mx3_ipu_data = { + .irq_base = MXC_IPU_IRQ_START, +}; + +static struct mx3fb_platform_data mx3fb_pdata = { + .dma_dev = &mx3_ipu.dev, + .name = "CRT-VGA", + .mode = fb_modedb, + .num_modes = ARRAY_SIZE(fb_modedb), +}; + +/* + * SDHC 1 + * MMC support + */ +static int armadillo5x0_sdhc1_get_ro(struct device *dev) +{ + return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_ATA_RESET_B)); +} + +static int armadillo5x0_sdhc1_init(struct device *dev, + irq_handler_t detect_irq, void *data) +{ + int ret; + int gpio_det, gpio_wp; + + gpio_det = IOMUX_TO_GPIO(MX31_PIN_ATA_DMACK); + gpio_wp = IOMUX_TO_GPIO(MX31_PIN_ATA_RESET_B); + + ret = gpio_request(gpio_det, "sdhc-card-detect"); + if (ret) + return ret; + + gpio_direction_input(gpio_det); + + ret = gpio_request(gpio_wp, "sdhc-write-protect"); + if (ret) + goto err_gpio_free; + + gpio_direction_input(gpio_wp); + + /* When supported the trigger type have to be BOTH */ + ret = request_irq(IOMUX_TO_IRQ(MX31_PIN_ATA_DMACK), detect_irq, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "sdhc-detect", data); + + if (ret) + goto err_gpio_free_2; + + return 0; + +err_gpio_free_2: + gpio_free(gpio_wp); + +err_gpio_free: + gpio_free(gpio_det); + + return ret; + +} + +static void armadillo5x0_sdhc1_exit(struct device *dev, void *data) +{ + free_irq(IOMUX_TO_IRQ(MX31_PIN_ATA_DMACK), data); + gpio_free(IOMUX_TO_GPIO(MX31_PIN_ATA_DMACK)); + gpio_free(IOMUX_TO_GPIO(MX31_PIN_ATA_RESET_B)); +} + +static struct imxmmc_platform_data sdhc_pdata = { + .get_ro = armadillo5x0_sdhc1_get_ro, + .init = armadillo5x0_sdhc1_init, + .exit = armadillo5x0_sdhc1_exit, +}; + +/* + * SMSC 9118 + * Network support + */ +static struct resource armadillo5x0_smc911x_resources[] = { + { + .start = MX31_CS3_BASE_ADDR, + .end = MX31_CS3_BASE_ADDR + SZ_32M - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), + .end = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, + }, +}; + +static struct smsc911x_platform_config smsc911x_info = { + .flags = SMSC911X_USE_16BIT, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, +}; + +static struct platform_device armadillo5x0_smc911x_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(armadillo5x0_smc911x_resources), + .resource = armadillo5x0_smc911x_resources, + .dev = { + .platform_data = &smsc911x_info, + }, +}; + +/* UART device data */ +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct platform_device *devices[] __initdata = { + &armadillo5x0_smc911x_device, + &mxc_i2c_device1, + &armadillo5x0_button_device, +}; + +/* + * Perform board specific initializations + */ +static void __init armadillo5x0_init(void) +{ + mxc_iomux_setup_multiple_pins(armadillo5x0_pins, + ARRAY_SIZE(armadillo5x0_pins), "armadillo5x0"); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + /* Register UART */ + mxc_register_device(&mxc_uart_device0, &uart_pdata); + mxc_register_device(&mxc_uart_device1, &uart_pdata); + + /* SMSC9118 IRQ pin */ + gpio_direction_input(MX31_PIN_GPIO1_0); + + /* Register SDHC */ + mxc_register_device(&mxcsdhc_device0, &sdhc_pdata); + + /* Register FB */ + mxc_register_device(&mx3_ipu, &mx3_ipu_data); + mxc_register_device(&mx3_fb, &mx3fb_pdata); + + /* Register NOR Flash */ + mxc_register_device(&armadillo5x0_nor_flash, + &armadillo5x0_nor_flash_pdata); + + /* Register NAND Flash */ + mxc_register_device(&mxc_nand_device, &armadillo5x0_nand_flash_pdata); + + /* set NAND page size to 2k if not configured via boot mode pins */ + __raw_writel(__raw_readl(MXC_CCM_RCSR) | (1 << 30), MXC_CCM_RCSR); + + /* RTC */ + /* Get RTC IRQ and register the chip */ + if (gpio_request(ARMADILLO5X0_RTC_GPIO, "rtc") == 0) { + if (gpio_direction_input(ARMADILLO5X0_RTC_GPIO) == 0) + armadillo5x0_i2c_rtc.irq = gpio_to_irq(ARMADILLO5X0_RTC_GPIO); + else + gpio_free(ARMADILLO5X0_RTC_GPIO); + } + if (armadillo5x0_i2c_rtc.irq == 0) + pr_warning("armadillo5x0_init: failed to get RTC IRQ\n"); + i2c_register_board_info(1, &armadillo5x0_i2c_rtc, 1); +} + +static void __init armadillo5x0_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +static struct sys_timer armadillo5x0_timer = { + .init = armadillo5x0_timer_init, +}; + +MACHINE_START(ARMADILLO5X0, "Armadillo-500") + /* Maintainer: Alberto Panizzo */ + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x00000100, + .map_io = mx31_map_io, + .init_irq = mx31_init_irq, + .timer = &armadillo5x0_timer, + .init_machine = armadillo5x0_init, +MACHINE_END diff --git a/arch/arm/mach-mx3/mach-kzm_arm11_01.c b/arch/arm/mach-mx3/mach-kzm_arm11_01.c new file mode 100644 index 0000000..2850b0b --- /dev/null +++ b/arch/arm/mach-mx3/mach-kzm_arm11_01.c @@ -0,0 +1,268 @@ +/* + * KZM-ARM11-01 support + * Copyright (C) 2009 Yoichi Yuasa + * + * based on code for MX31ADS, + * Copyright (C) 2000 Deep Blue Solutions Ltd + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * Copyright 2005-2007 Freescale Semiconductor, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) +/* + * KZM-ARM11-01 has an external UART on FPGA + */ +static struct plat_serial8250_port serial_platform_data[] = { + { + .membase = IO_ADDRESS(KZM_ARM11_16550), + .mapbase = KZM_ARM11_16550, + .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_1), + .irqflags = IRQ_TYPE_EDGE_RISING, + .uartclk = 14745600, + .regshift = 0, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | + UPF_BUGGY_UART, + }, + {}, +}; + +static struct resource serial8250_resources[] = { + { + .start = KZM_ARM11_16550, + .end = KZM_ARM11_16550 + 0x10, + .flags = IORESOURCE_MEM, + }, + { + .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_1), + .end = IOMUX_TO_IRQ(MX31_PIN_GPIO1_1), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = PLAT8250_DEV_PLATFORM, + .dev = { + .platform_data = serial_platform_data, + }, + .num_resources = ARRAY_SIZE(serial8250_resources), + .resource = serial8250_resources, +}; + +static int __init kzm_init_ext_uart(void) +{ + u8 tmp; + + /* + * GPIO 1-1: external UART interrupt line + */ + mxc_iomux_mode(IOMUX_MODE(MX31_PIN_GPIO1_1, IOMUX_CONFIG_GPIO)); + gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1), "ext-uart-int"); + gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1)); + + /* + * Unmask UART interrupt + */ + tmp = __raw_readb(IO_ADDRESS(KZM_ARM11_CTL1)); + tmp |= 0x2; + __raw_writeb(tmp, IO_ADDRESS(KZM_ARM11_CTL1)); + + return platform_device_register(&serial_device); +} +#else +static inline int kzm_init_ext_uart(void) +{ + return 0; +} +#endif + +/* + * SMSC LAN9118 + */ +#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE) +static struct smsc911x_platform_config kzm_smsc9118_config = { + .phy_interface = PHY_INTERFACE_MODE_MII, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS, +}; + +static struct resource kzm_smsc9118_resources[] = { + { + .start = MX31_CS5_BASE_ADDR, + .end = MX31_CS5_BASE_ADDR + SZ_128K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_2), + .end = IOMUX_TO_IRQ(MX31_PIN_GPIO1_2), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, + }, +}; + +static struct platform_device kzm_smsc9118_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(kzm_smsc9118_resources), + .resource = kzm_smsc9118_resources, + .dev = { + .platform_data = &kzm_smsc9118_config, + }, +}; + +static int __init kzm_init_smsc9118(void) +{ + /* + * GPIO 1-2: SMSC9118 interrupt line + */ + mxc_iomux_mode(IOMUX_MODE(MX31_PIN_GPIO1_2, IOMUX_CONFIG_GPIO)); + gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2), "smsc9118-int"); + gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2)); + + return platform_device_register(&kzm_smsc9118_device); +} +#else +static inline int kzm_init_smsc9118(void) +{ + return 0; +} +#endif + +#if defined(CONFIG_SERIAL_IMX) || defined(CONFIG_SERIAL_IMX_MODULE) +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static void __init kzm_init_imx_uart(void) +{ + mxc_register_device(&mxc_uart_device0, &uart_pdata); + + mxc_register_device(&mxc_uart_device1, &uart_pdata); +} +#else +static inline void kzm_init_imx_uart(void) +{ +} +#endif + +static int kzm_pins[] __initdata = { + MX31_PIN_CTS1__CTS1, + MX31_PIN_RTS1__RTS1, + MX31_PIN_TXD1__TXD1, + MX31_PIN_RXD1__RXD1, + MX31_PIN_DCD_DCE1__DCD_DCE1, + MX31_PIN_RI_DCE1__RI_DCE1, + MX31_PIN_DSR_DCE1__DSR_DCE1, + MX31_PIN_DTR_DCE1__DTR_DCE1, + MX31_PIN_CTS2__CTS2, + MX31_PIN_RTS2__RTS2, + MX31_PIN_TXD2__TXD2, + MX31_PIN_RXD2__RXD2, + MX31_PIN_DCD_DTE1__DCD_DTE2, + MX31_PIN_RI_DTE1__RI_DTE2, + MX31_PIN_DSR_DTE1__DSR_DTE2, + MX31_PIN_DTR_DTE1__DTR_DTE2, +}; + +/* + * Board specific initialization. + */ +static void __init kzm_board_init(void) +{ + mxc_iomux_setup_multiple_pins(kzm_pins, + ARRAY_SIZE(kzm_pins), "kzm"); + kzm_init_ext_uart(); + kzm_init_smsc9118(); + kzm_init_imx_uart(); + + pr_info("Clock input source is 26MHz\n"); +} + +/* + * This structure defines static mappings for the kzm-arm11-01 board. + */ +static struct map_desc kzm_io_desc[] __initdata = { + { + .virtual = MX31_CS4_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), + .length = MX31_CS4_SIZE, + .type = MT_DEVICE + }, + { + .virtual = MX31_CS5_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS5_BASE_ADDR), + .length = MX31_CS5_SIZE, + .type = MT_DEVICE + }, +}; + +/* + * Set up static virtual mappings. + */ +static void __init kzm_map_io(void) +{ + mx31_map_io(); + iotable_init(kzm_io_desc, ARRAY_SIZE(kzm_io_desc)); +} + +static void __init kzm_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +static struct sys_timer kzm_timer = { + .init = kzm_timer_init, +}; + +/* + * The following uses standard kernel macros define in arch.h in order to + * initialize __mach_desc_KZM_ARM11_01 data structure. + */ +MACHINE_START(KZM_ARM11_01, "Kyoto Microcomputer Co., Ltd. KZM-ARM11-01") + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = kzm_map_io, + .init_irq = mx31_init_irq, + .init_machine = kzm_board_init, + .timer = &kzm_timer, +MACHINE_END diff --git a/arch/arm/mach-mx3/mach-mx31_3ds.c b/arch/arm/mach-mx3/mach-mx31_3ds.c new file mode 100644 index 0000000..88af585 --- /dev/null +++ b/arch/arm/mach-mx3/mach-mx31_3ds.c @@ -0,0 +1,266 @@ +/* + * Copyright 2008 Freescale Semiconductor, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" + +/*! + * @file mx31pdk.c + * + * @brief This file contains the board-specific initialization routines. + * + * @ingroup System + */ + +static int mx31pdk_pins[] = { + /* UART1 */ + MX31_PIN_CTS1__CTS1, + MX31_PIN_RTS1__RTS1, + MX31_PIN_TXD1__TXD1, + MX31_PIN_RXD1__RXD1, + IOMUX_MODE(MX31_PIN_GPIO1_1, IOMUX_CONFIG_GPIO), +}; + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +/* + * Support for the SMSC9217 on the Debug board. + */ + +static struct smsc911x_platform_config smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .flags = SMSC911X_USE_16BIT | SMSC911X_FORCE_INTERNAL_PHY, + .phy_interface = PHY_INTERFACE_MODE_MII, +}; + +static struct resource smsc911x_resources[] = { + { + .start = LAN9217_BASE_ADDR, + .end = LAN9217_BASE_ADDR + 0xff, + .flags = IORESOURCE_MEM, + }, { + .start = EXPIO_INT_ENET, + .end = EXPIO_INT_ENET, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smsc911x_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_config, + }, +}; + +/* + * Routines for the CPLD on the debug board. It contains a CPLD handling + * LEDs, switches, interrupts for Ethernet. + */ + +static void mx31pdk_expio_irq_handler(uint32_t irq, struct irq_desc *desc) +{ + uint32_t imr_val; + uint32_t int_valid; + uint32_t expio_irq; + + imr_val = __raw_readw(CPLD_INT_MASK_REG); + int_valid = __raw_readw(CPLD_INT_STATUS_REG) & ~imr_val; + + expio_irq = MXC_EXP_IO_BASE; + for (; int_valid != 0; int_valid >>= 1, expio_irq++) { + if ((int_valid & 1) == 0) + continue; + generic_handle_irq(expio_irq); + } +} + +/* + * Disable an expio pin's interrupt by setting the bit in the imr. + * @param irq an expio virtual irq number + */ +static void expio_mask_irq(uint32_t irq) +{ + uint16_t reg; + uint32_t expio = MXC_IRQ_TO_EXPIO(irq); + + /* mask the interrupt */ + reg = __raw_readw(CPLD_INT_MASK_REG); + reg |= 1 << expio; + __raw_writew(reg, CPLD_INT_MASK_REG); +} + +/* + * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr. + * @param irq an expanded io virtual irq number + */ +static void expio_ack_irq(uint32_t irq) +{ + uint32_t expio = MXC_IRQ_TO_EXPIO(irq); + + /* clear the interrupt status */ + __raw_writew(1 << expio, CPLD_INT_RESET_REG); + __raw_writew(0, CPLD_INT_RESET_REG); + /* mask the interrupt */ + expio_mask_irq(irq); +} + +/* + * Enable a expio pin's interrupt by clearing the bit in the imr. + * @param irq a expio virtual irq number + */ +static void expio_unmask_irq(uint32_t irq) +{ + uint16_t reg; + uint32_t expio = MXC_IRQ_TO_EXPIO(irq); + + /* unmask the interrupt */ + reg = __raw_readw(CPLD_INT_MASK_REG); + reg &= ~(1 << expio); + __raw_writew(reg, CPLD_INT_MASK_REG); +} + +static struct irq_chip expio_irq_chip = { + .ack = expio_ack_irq, + .mask = expio_mask_irq, + .unmask = expio_unmask_irq, +}; + +static int __init mx31pdk_init_expio(void) +{ + int i; + int ret; + + /* Check if there's a debug board connected */ + if ((__raw_readw(CPLD_MAGIC_NUMBER1_REG) != 0xAAAA) || + (__raw_readw(CPLD_MAGIC_NUMBER2_REG) != 0x5555) || + (__raw_readw(CPLD_MAGIC_NUMBER3_REG) != 0xCAFE)) { + /* No Debug board found */ + return -ENODEV; + } + + pr_info("i.MX31PDK Debug board detected, rev = 0x%04X\n", + __raw_readw(CPLD_CODE_VER_REG)); + + /* + * Configure INT line as GPIO input + */ + ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1), "sms9217-irq"); + if (ret) + pr_warning("could not get LAN irq gpio\n"); + else + gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1)); + + /* Disable the interrupts and clear the status */ + __raw_writew(0, CPLD_INT_MASK_REG); + __raw_writew(0xFFFF, CPLD_INT_RESET_REG); + __raw_writew(0, CPLD_INT_RESET_REG); + __raw_writew(0x1F, CPLD_INT_MASK_REG); + for (i = MXC_EXP_IO_BASE; + i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES); + i++) { + set_irq_chip(i, &expio_irq_chip); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } + set_irq_type(EXPIO_PARENT_INT, IRQ_TYPE_LEVEL_LOW); + set_irq_chained_handler(EXPIO_PARENT_INT, mx31pdk_expio_irq_handler); + + return 0; +} + +/* + * This structure defines the MX31 memory map. + */ +static struct map_desc mx31pdk_io_desc[] __initdata = { + { + .virtual = MX31_CS5_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS5_BASE_ADDR), + .length = MX31_CS5_SIZE, + .type = MT_DEVICE, + }, +}; + +/* + * Set up static virtual mappings. + */ +static void __init mx31pdk_map_io(void) +{ + mx31_map_io(); + iotable_init(mx31pdk_io_desc, ARRAY_SIZE(mx31pdk_io_desc)); +} + +/*! + * Board specific initialization. + */ +static void __init mxc_board_init(void) +{ + mxc_iomux_setup_multiple_pins(mx31pdk_pins, ARRAY_SIZE(mx31pdk_pins), + "mx31pdk"); + + mxc_register_device(&mxc_uart_device0, &uart_pdata); + + if (!mx31pdk_init_expio()) + platform_device_register(&smsc911x_device); +} + +static void __init mx31pdk_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +static struct sys_timer mx31pdk_timer = { + .init = mx31pdk_timer_init, +}; + +/* + * The following uses standard kernel macros defined in arch.h in order to + * initialize __mach_desc_MX31PDK data structure. + */ +MACHINE_START(MX31_3DS, "Freescale MX31PDK (3DS)") + /* Maintainer: Freescale Semiconductor, Inc. */ + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx31pdk_map_io, + .init_irq = mx31_init_irq, + .init_machine = mxc_board_init, + .timer = &mx31pdk_timer, +MACHINE_END diff --git a/arch/arm/mach-mx3/mach-mx31ads.c b/arch/arm/mach-mx3/mach-mx31ads.c new file mode 100644 index 0000000..1bcf226 --- /dev/null +++ b/arch/arm/mach-mx3/mach-mx31ads.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2000 Deep Blue Solutions Ltd + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * Copyright 2005-2007 Freescale Semiconductor, Inc. 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MACH_MX31ADS_WM1133_EV1 +#include +#include +#include +#endif + +#include "devices.h" + +/*! + * @file mx31ads.c + * + * @brief This file contains the board-specific initialization routines. + * + * @ingroup System + */ + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) +/*! + * The serial port definition structure. + */ +static struct plat_serial8250_port serial_platform_data[] = { + { + .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTA), + .mapbase = (unsigned long)(MX31_CS4_BASE_ADDR + PBC_SC16C652_UARTA), + .irq = EXPIO_INT_XUART_INTA, + .uartclk = 14745600, + .regshift = 0, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ, + }, { + .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTB), + .mapbase = (unsigned long)(MX31_CS4_BASE_ADDR + PBC_SC16C652_UARTB), + .irq = EXPIO_INT_XUART_INTB, + .uartclk = 14745600, + .regshift = 0, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ, + }, + {}, +}; + +static struct platform_device serial_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = serial_platform_data, + }, +}; + +static int __init mxc_init_extuart(void) +{ + return platform_device_register(&serial_device); +} +#else +static inline int mxc_init_extuart(void) +{ + return 0; +} +#endif + +#if defined(CONFIG_SERIAL_IMX) || defined(CONFIG_SERIAL_IMX_MODULE) +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static unsigned int uart_pins[] = { + MX31_PIN_CTS1__CTS1, + MX31_PIN_RTS1__RTS1, + MX31_PIN_TXD1__TXD1, + MX31_PIN_RXD1__RXD1 +}; + +static inline void mxc_init_imx_uart(void) +{ + mxc_iomux_setup_multiple_pins(uart_pins, ARRAY_SIZE(uart_pins), "uart-0"); + mxc_register_device(&mxc_uart_device0, &uart_pdata); +} +#else /* !SERIAL_IMX */ +static inline void mxc_init_imx_uart(void) +{ +} +#endif /* !SERIAL_IMX */ + +static void mx31ads_expio_irq_handler(u32 irq, struct irq_desc *desc) +{ + u32 imr_val; + u32 int_valid; + u32 expio_irq; + + imr_val = __raw_readw(PBC_INTMASK_SET_REG); + int_valid = __raw_readw(PBC_INTSTATUS_REG) & imr_val; + + expio_irq = MXC_EXP_IO_BASE; + for (; int_valid != 0; int_valid >>= 1, expio_irq++) { + if ((int_valid & 1) == 0) + continue; + + generic_handle_irq(expio_irq); + } +} + +/* + * Disable an expio pin's interrupt by setting the bit in the imr. + * @param irq an expio virtual irq number + */ +static void expio_mask_irq(u32 irq) +{ + u32 expio = MXC_IRQ_TO_EXPIO(irq); + /* mask the interrupt */ + __raw_writew(1 << expio, PBC_INTMASK_CLEAR_REG); + __raw_readw(PBC_INTMASK_CLEAR_REG); +} + +/* + * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr. + * @param irq an expanded io virtual irq number + */ +static void expio_ack_irq(u32 irq) +{ + u32 expio = MXC_IRQ_TO_EXPIO(irq); + /* clear the interrupt status */ + __raw_writew(1 << expio, PBC_INTSTATUS_REG); +} + +/* + * Enable a expio pin's interrupt by clearing the bit in the imr. + * @param irq a expio virtual irq number + */ +static void expio_unmask_irq(u32 irq) +{ + u32 expio = MXC_IRQ_TO_EXPIO(irq); + /* unmask the interrupt */ + __raw_writew(1 << expio, PBC_INTMASK_SET_REG); +} + +static struct irq_chip expio_irq_chip = { + .ack = expio_ack_irq, + .mask = expio_mask_irq, + .unmask = expio_unmask_irq, +}; + +static void __init mx31ads_init_expio(void) +{ + int i; + + printk(KERN_INFO "MX31ADS EXPIO(CPLD) hardware\n"); + + /* + * Configure INT line as GPIO input + */ + mxc_iomux_alloc_pin(IOMUX_MODE(MX31_PIN_GPIO1_4, IOMUX_CONFIG_GPIO), "expio"); + + /* disable the interrupt and clear the status */ + __raw_writew(0xFFFF, PBC_INTMASK_CLEAR_REG); + __raw_writew(0xFFFF, PBC_INTSTATUS_REG); + for (i = MXC_EXP_IO_BASE; i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES); + i++) { + set_irq_chip(i, &expio_irq_chip); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } + set_irq_type(EXPIO_PARENT_INT, IRQ_TYPE_LEVEL_HIGH); + set_irq_chained_handler(EXPIO_PARENT_INT, mx31ads_expio_irq_handler); +} + +#ifdef CONFIG_MACH_MX31ADS_WM1133_EV1 +/* This section defines setup for the Wolfson Microelectronics + * 1133-EV1 PMU/audio board. When other PMU boards are supported the + * regulator definitions may be shared with them, but for now they can + * only be used with this board so would generate warnings about + * unused statics and some of the configuration is specific to this + * module. + */ + +/* CPU */ +static struct regulator_consumer_supply sw1a_consumers[] = { + { + .supply = "cpu_vcc", + } +}; + +static struct regulator_init_data sw1a_data = { + .constraints = { + .name = "SW1A", + .min_uV = 1275000, + .max_uV = 1600000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_FAST, + .state_mem = { + .uV = 1400000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + .initial_state = PM_SUSPEND_MEM, + .always_on = 1, + .boot_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(sw1a_consumers), + .consumer_supplies = sw1a_consumers, +}; + +/* System IO - High */ +static struct regulator_init_data viohi_data = { + .constraints = { + .name = "VIOHO", + .min_uV = 2800000, + .max_uV = 2800000, + .state_mem = { + .uV = 2800000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + .initial_state = PM_SUSPEND_MEM, + .always_on = 1, + .boot_on = 1, + }, +}; + +/* System IO - Low */ +static struct regulator_init_data violo_data = { + .constraints = { + .name = "VIOLO", + .min_uV = 1800000, + .max_uV = 1800000, + .state_mem = { + .uV = 1800000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + .initial_state = PM_SUSPEND_MEM, + .always_on = 1, + .boot_on = 1, + }, +}; + +/* DDR RAM */ +static struct regulator_init_data sw2a_data = { + .constraints = { + .name = "SW2A", + .min_uV = 1800000, + .max_uV = 1800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .state_mem = { + .uV = 1800000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + .state_disk = { + .mode = REGULATOR_MODE_NORMAL, + .enabled = 0, + }, + .always_on = 1, + .boot_on = 1, + .initial_state = PM_SUSPEND_MEM, + }, +}; + +static struct regulator_init_data ldo1_data = { + .constraints = { + .name = "VCAM/VMMC1/VMMC2", + .min_uV = 2800000, + .max_uV = 2800000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + }, +}; + +static struct regulator_consumer_supply ldo2_consumers[] = { + { + .supply = "AVDD", + }, + { + .supply = "HPVDD", + }, +}; + +/* CODEC and SIM */ +static struct regulator_init_data ldo2_data = { + .constraints = { + .name = "VESIM/VSIM/AVDD", + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo2_consumers), + .consumer_supplies = ldo2_consumers, +}; + +/* General */ +static struct regulator_init_data vdig_data = { + .constraints = { + .name = "VDIG", + .min_uV = 1500000, + .max_uV = 1500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + .always_on = 1, + .boot_on = 1, + }, +}; + +/* Tranceivers */ +static struct regulator_init_data ldo4_data = { + .constraints = { + .name = "VRF1/CVDD_2.775", + .min_uV = 2500000, + .max_uV = 2500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .apply_uV = 1, + .always_on = 1, + .boot_on = 1, + }, +}; + +static struct wm8350_led_platform_data wm8350_led_data = { + .name = "wm8350:white", + .default_trigger = "heartbeat", + .max_uA = 27899, +}; + +static struct wm8350_audio_platform_data imx32ads_wm8350_setup = { + .vmid_discharge_msecs = 1000, + .drain_msecs = 30, + .cap_discharge_msecs = 700, + .vmid_charge_msecs = 700, + .vmid_s_curve = WM8350_S_CURVE_SLOW, + .dis_out4 = WM8350_DISCHARGE_SLOW, + .dis_out3 = WM8350_DISCHARGE_SLOW, + .dis_out2 = WM8350_DISCHARGE_SLOW, + .dis_out1 = WM8350_DISCHARGE_SLOW, + .vroi_out4 = WM8350_TIE_OFF_500R, + .vroi_out3 = WM8350_TIE_OFF_500R, + .vroi_out2 = WM8350_TIE_OFF_500R, + .vroi_out1 = WM8350_TIE_OFF_500R, + .vroi_enable = 0, + .codec_current_on = WM8350_CODEC_ISEL_1_0, + .codec_current_standby = WM8350_CODEC_ISEL_0_5, + .codec_current_charge = WM8350_CODEC_ISEL_1_5, +}; + +static int mx31_wm8350_init(struct wm8350 *wm8350) +{ + int i; + + wm8350_gpio_config(wm8350, 0, WM8350_GPIO_DIR_IN, + WM8350_GPIO0_PWR_ON_IN, WM8350_GPIO_ACTIVE_LOW, + WM8350_GPIO_PULL_UP, WM8350_GPIO_INVERT_OFF, + WM8350_GPIO_DEBOUNCE_ON); + + wm8350_gpio_config(wm8350, 3, WM8350_GPIO_DIR_IN, + WM8350_GPIO3_PWR_OFF_IN, WM8350_GPIO_ACTIVE_HIGH, + WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF, + WM8350_GPIO_DEBOUNCE_ON); + + wm8350_gpio_config(wm8350, 4, WM8350_GPIO_DIR_IN, + WM8350_GPIO4_MR_IN, WM8350_GPIO_ACTIVE_HIGH, + WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF, + WM8350_GPIO_DEBOUNCE_OFF); + + wm8350_gpio_config(wm8350, 7, WM8350_GPIO_DIR_IN, + WM8350_GPIO7_HIBERNATE_IN, WM8350_GPIO_ACTIVE_HIGH, + WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF, + WM8350_GPIO_DEBOUNCE_OFF); + + wm8350_gpio_config(wm8350, 6, WM8350_GPIO_DIR_OUT, + WM8350_GPIO6_SDOUT_OUT, WM8350_GPIO_ACTIVE_HIGH, + WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, + WM8350_GPIO_DEBOUNCE_OFF); + + wm8350_gpio_config(wm8350, 8, WM8350_GPIO_DIR_OUT, + WM8350_GPIO8_VCC_FAULT_OUT, WM8350_GPIO_ACTIVE_LOW, + WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, + WM8350_GPIO_DEBOUNCE_OFF); + + wm8350_gpio_config(wm8350, 9, WM8350_GPIO_DIR_OUT, + WM8350_GPIO9_BATT_FAULT_OUT, WM8350_GPIO_ACTIVE_LOW, + WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, + WM8350_GPIO_DEBOUNCE_OFF); + + /* Fix up for our own supplies. */ + for (i = 0; i < ARRAY_SIZE(ldo2_consumers); i++) + ldo2_consumers[i].dev = wm8350->dev; + + wm8350_register_regulator(wm8350, WM8350_DCDC_1, &sw1a_data); + wm8350_register_regulator(wm8350, WM8350_DCDC_3, &viohi_data); + wm8350_register_regulator(wm8350, WM8350_DCDC_4, &violo_data); + wm8350_register_regulator(wm8350, WM8350_DCDC_6, &sw2a_data); + wm8350_register_regulator(wm8350, WM8350_LDO_1, &ldo1_data); + wm8350_register_regulator(wm8350, WM8350_LDO_2, &ldo2_data); + wm8350_register_regulator(wm8350, WM8350_LDO_3, &vdig_data); + wm8350_register_regulator(wm8350, WM8350_LDO_4, &ldo4_data); + + /* LEDs */ + wm8350_dcdc_set_slot(wm8350, WM8350_DCDC_5, 1, 1, + WM8350_DC5_ERRACT_SHUTDOWN_CONV); + wm8350_isink_set_flash(wm8350, WM8350_ISINK_A, + WM8350_ISINK_FLASH_DISABLE, + WM8350_ISINK_FLASH_TRIG_BIT, + WM8350_ISINK_FLASH_DUR_32MS, + WM8350_ISINK_FLASH_ON_INSTANT, + WM8350_ISINK_FLASH_OFF_INSTANT, + WM8350_ISINK_FLASH_MODE_EN); + wm8350_dcdc25_set_mode(wm8350, WM8350_DCDC_5, + WM8350_ISINK_MODE_BOOST, + WM8350_ISINK_ILIM_NORMAL, + WM8350_DC5_RMP_20V, + WM8350_DC5_FBSRC_ISINKA); + wm8350_register_led(wm8350, 0, WM8350_DCDC_5, WM8350_ISINK_A, + &wm8350_led_data); + + wm8350->codec.platform_data = &imx32ads_wm8350_setup; + + regulator_has_full_constraints(); + + return 0; +} + +static struct wm8350_platform_data __initdata mx31_wm8350_pdata = { + .init = mx31_wm8350_init, +}; +#endif + +#if defined(CONFIG_I2C_IMX) || defined(CONFIG_I2C_IMX_MODULE) +static struct i2c_board_info __initdata mx31ads_i2c1_devices[] = { +#ifdef CONFIG_MACH_MX31ADS_WM1133_EV1 + { + I2C_BOARD_INFO("wm8350", 0x1a), + .platform_data = &mx31_wm8350_pdata, + .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3), + }, +#endif +}; + +static void mxc_init_i2c(void) +{ + i2c_register_board_info(1, mx31ads_i2c1_devices, + ARRAY_SIZE(mx31ads_i2c1_devices)); + + mxc_iomux_mode(IOMUX_MODE(MX31_PIN_CSPI2_MOSI, IOMUX_CONFIG_ALT1)); + mxc_iomux_mode(IOMUX_MODE(MX31_PIN_CSPI2_MISO, IOMUX_CONFIG_ALT1)); + + mxc_register_device(&mxc_i2c_device1, NULL); +} +#else +static void mxc_init_i2c(void) +{ +} +#endif + +/*! + * This structure defines static mappings for the i.MX31ADS board. + */ +static struct map_desc mx31ads_io_desc[] __initdata = { + { + .virtual = MX31_CS4_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), + .length = MX31_CS4_SIZE / 2, + .type = MT_DEVICE + }, +}; + +/*! + * Set up static virtual mappings. + */ +static void __init mx31ads_map_io(void) +{ + mx31_map_io(); + iotable_init(mx31ads_io_desc, ARRAY_SIZE(mx31ads_io_desc)); +} + +static void __init mx31ads_init_irq(void) +{ + mx31_init_irq(); + mx31ads_init_expio(); +} + +/*! + * Board specific initialization. + */ +static void __init mxc_board_init(void) +{ + mxc_init_extuart(); + mxc_init_imx_uart(); + mxc_init_i2c(); +} + +static void __init mx31ads_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +static struct sys_timer mx31ads_timer = { + .init = mx31ads_timer_init, +}; + +/* + * The following uses standard kernel macros defined in arch.h in order to + * initialize __mach_desc_MX31ADS data structure. + */ +MACHINE_START(MX31ADS, "Freescale MX31ADS") + /* Maintainer: Freescale Semiconductor, Inc. */ + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx31ads_map_io, + .init_irq = mx31ads_init_irq, + .init_machine = mxc_board_init, + .timer = &mx31ads_timer, +MACHINE_END diff --git a/arch/arm/mach-mx3/mach-mx31lilly.c b/arch/arm/mach-mx3/mach-mx31lilly.c new file mode 100644 index 0000000..9225cb7 --- /dev/null +++ b/arch/arm/mach-mx3/mach-mx31lilly.c @@ -0,0 +1,206 @@ +/* + * LILLY-1131 module support + * + * Copyright (c) 2009 Daniel Mack + * + * based on code for other MX31 boards, + * + * Copyright 2005-2007 Freescale Semiconductor + * Copyright (c) 2009 Alberto Panizzo + * Copyright (C) 2009 Valentin Longchamp, EPFL Mobots group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "devices.h" + +/* + * This file contains module-specific initialization routines for LILLY-1131. + * Initialization of peripherals found on the baseboard is implemented in the + * appropriate baseboard support code. + */ + +/* SMSC ethernet support */ + +static struct resource smsc91x_resources[] = { + { + .start = MX31_CS4_BASE_ADDR, + .end = MX31_CS4_BASE_ADDR + 0xffff, + .flags = IORESOURCE_MEM, + }, + { + .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), + .end = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_FALLING, + } +}; + +static struct smsc911x_platform_config smsc911x_config = { + .phy_interface = PHY_INTERFACE_MODE_MII, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, + .flags = SMSC911X_USE_32BIT | + SMSC911X_SAVE_MAC_ADDRESS | + SMSC911X_FORCE_INTERNAL_PHY, +}; + +static struct platform_device smsc91x_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smsc91x_resources), + .resource = smsc91x_resources, + .dev = { + .platform_data = &smsc911x_config, + } +}; + +/* NOR flash */ +static struct physmap_flash_data nor_flash_data = { + .width = 2, +}; + +static struct resource nor_flash_resource = { + .start = 0xa0000000, + .end = 0xa1ffffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device physmap_flash_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &nor_flash_data, + }, + .resource = &nor_flash_resource, + .num_resources = 1, +}; + +static struct platform_device *devices[] __initdata = { + &smsc91x_device, + &physmap_flash_device, +}; + +/* SPI */ + +static int spi_internal_chipselect[] = { + MXC_SPI_CS(0), + MXC_SPI_CS(1), + MXC_SPI_CS(2), +}; + +static struct spi_imx_master spi0_pdata = { + .chipselect = spi_internal_chipselect, + .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), +}; + +static struct spi_imx_master spi1_pdata = { + .chipselect = spi_internal_chipselect, + .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), +}; + +static struct mc13783_platform_data mc13783_pdata __initdata = { + .flags = MC13783_USE_RTC | MC13783_USE_TOUCHSCREEN, +}; + +static struct spi_board_info mc13783_dev __initdata = { + .modalias = "mc13783", + .max_speed_hz = 1000000, + .bus_num = 1, + .chip_select = 0, + .platform_data = &mc13783_pdata, +}; + +static int mx31lilly_baseboard; +core_param(mx31lilly_baseboard, mx31lilly_baseboard, int, 0444); + +static void __init mx31lilly_board_init(void) +{ + switch (mx31lilly_baseboard) { + case MX31LILLY_NOBOARD: + break; + case MX31LILLY_DB: + mx31lilly_db_init(); + break; + default: + printk(KERN_ERR "Illegal mx31lilly_baseboard type %d\n", + mx31lilly_baseboard); + } + + mxc_iomux_alloc_pin(MX31_PIN_CS4__CS4, "Ethernet CS"); + + /* SPI */ + mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SCLK__SCLK, "SPI1_CLK"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI1_MOSI__MOSI, "SPI1_TX"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI1_MISO__MISO, "SPI1_RX"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SPI_RDY__SPI_RDY, "SPI1_RDY"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SS0__SS0, "SPI1_SS0"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SS1__SS1, "SPI1_SS1"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SS2__SS2, "SPI1_SS2"); + + mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SCLK__SCLK, "SPI2_CLK"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI2_MOSI__MOSI, "SPI2_TX"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI2_MISO__MISO, "SPI2_RX"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SPI_RDY__SPI_RDY, "SPI2_RDY"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SS0__SS0, "SPI2_SS0"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SS1__SS1, "SPI2_SS1"); + mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SS2__SS2, "SPI2_SS2"); + + mxc_register_device(&mxc_spi_device0, &spi0_pdata); + mxc_register_device(&mxc_spi_device1, &spi1_pdata); + spi_register_board_info(&mc13783_dev, 1); + + platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +static void __init mx31lilly_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +static struct sys_timer mx31lilly_timer = { + .init = mx31lilly_timer_init, +}; + +MACHINE_START(LILLY1131, "INCO startec LILLY-1131") + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx31_map_io, + .init_irq = mx31_init_irq, + .init_machine = mx31lilly_board_init, + .timer = &mx31lilly_timer, +MACHINE_END + diff --git a/arch/arm/mach-mx3/mach-mx31lite.c b/arch/arm/mach-mx3/mach-mx31lite.c new file mode 100644 index 0000000..8589e3d --- /dev/null +++ b/arch/arm/mach-mx3/mach-mx31lite.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2000 Deep Blue Solutions Ltd + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009 Daniel Mack + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +/* + * This file contains the module-specific initialization routines. + */ + +static unsigned int mx31lite_pins[] = { + /* LAN9117 IRQ pin */ + IOMUX_MODE(MX31_PIN_SFS6, IOMUX_CONFIG_GPIO), + /* SPI 1 */ + MX31_PIN_CSPI2_SCLK__SCLK, + MX31_PIN_CSPI2_MOSI__MOSI, + MX31_PIN_CSPI2_MISO__MISO, + MX31_PIN_CSPI2_SPI_RDY__SPI_RDY, + MX31_PIN_CSPI2_SS0__SS0, + MX31_PIN_CSPI2_SS1__SS1, + MX31_PIN_CSPI2_SS2__SS2, +}; + +static struct mxc_nand_platform_data mx31lite_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +static struct smsc911x_platform_config smsc911x_config = { + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, + .flags = SMSC911X_USE_16BIT, +}; + +static struct resource smsc911x_resources[] = { + { + .start = MX31_CS4_BASE_ADDR, + .end = MX31_CS4_BASE_ADDR + 0x100, + .flags = IORESOURCE_MEM, + }, { + .start = IOMUX_TO_IRQ(MX31_PIN_SFS6), + .end = IOMUX_TO_IRQ(MX31_PIN_SFS6), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smsc911x_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_config, + }, +}; + +/* + * SPI + * + * The MC13783 is the only hard-wired SPI device on the module. + */ + +static int spi_internal_chipselect[] = { + MXC_SPI_CS(0), +}; + +static struct spi_imx_master spi1_pdata = { + .chipselect = spi_internal_chipselect, + .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), +}; + +static struct mc13783_platform_data mc13783_pdata __initdata = { + .flags = MC13783_USE_RTC | + MC13783_USE_REGULATOR, +}; + +static struct spi_board_info mc13783_spi_dev __initdata = { + .modalias = "mc13783", + .max_speed_hz = 1000000, + .bus_num = 1, + .chip_select = 0, + .platform_data = &mc13783_pdata, + .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3), +}; + +/* + * USB + */ + +#if defined(CONFIG_USB_ULPI) +#define USB_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST | PAD_CTL_HYS_CMOS | \ + PAD_CTL_ODE_CMOS | PAD_CTL_100K_PU) + +static int usbh2_init(struct platform_device *pdev) +{ + int pins[] = { + MX31_PIN_USBH2_DATA0__USBH2_DATA0, + MX31_PIN_USBH2_DATA1__USBH2_DATA1, + MX31_PIN_USBH2_CLK__USBH2_CLK, + MX31_PIN_USBH2_DIR__USBH2_DIR, + MX31_PIN_USBH2_NXT__USBH2_NXT, + MX31_PIN_USBH2_STP__USBH2_STP, + }; + + mxc_iomux_setup_multiple_pins(pins, ARRAY_SIZE(pins), "USB H2"); + + mxc_iomux_set_pad(MX31_PIN_USBH2_CLK, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_DIR, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_NXT, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_STP, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_DATA0, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_DATA1, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_SRXD6, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_STXD6, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_SFS3, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_SCK3, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_SRXD3, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_STXD3, USB_PAD_CFG); + + mxc_iomux_set_gpr(MUX_PGP_UH2, true); + + /* chip select */ + mxc_iomux_alloc_pin(IOMUX_MODE(MX31_PIN_DTR_DCE1, IOMUX_CONFIG_GPIO), + "USBH2_CS"); + gpio_request(IOMUX_TO_GPIO(MX31_PIN_DTR_DCE1), "USBH2 CS"); + gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_DTR_DCE1), 0); + + return 0; +} + +static struct mxc_usbh_platform_data usbh2_pdata = { + .init = usbh2_init, + .portsc = MXC_EHCI_MODE_ULPI | MXC_EHCI_UTMI_8BIT, + .flags = MXC_EHCI_POWER_PINS_ENABLED, +}; +#endif + +/* + * NOR flash + */ + +static struct physmap_flash_data nor_flash_data = { + .width = 2, +}; + +static struct resource nor_flash_resource = { + .start = 0xa0000000, + .end = 0xa1ffffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device physmap_flash_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &nor_flash_data, + }, + .resource = &nor_flash_resource, + .num_resources = 1, +}; + + + +/* + * This structure defines the MX31 memory map. + */ +static struct map_desc mx31lite_io_desc[] __initdata = { + { + .virtual = MX31_CS4_BASE_ADDR_VIRT, + .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), + .length = MX31_CS4_SIZE, + .type = MT_DEVICE + } +}; + +/* + * Set up static virtual mappings. + */ +void __init mx31lite_map_io(void) +{ + mx31_map_io(); + iotable_init(mx31lite_io_desc, ARRAY_SIZE(mx31lite_io_desc)); +} + +static int mx31lite_baseboard; +core_param(mx31lite_baseboard, mx31lite_baseboard, int, 0444); + +static void __init mxc_board_init(void) +{ + int ret; + + switch (mx31lite_baseboard) { + case MX31LITE_NOBOARD: + break; + case MX31LITE_DB: + mx31lite_db_init(); + break; + default: + printk(KERN_ERR "Illegal mx31lite_baseboard type %d\n", + mx31lite_baseboard); + } + + mxc_iomux_setup_multiple_pins(mx31lite_pins, ARRAY_SIZE(mx31lite_pins), + "mx31lite"); + + /* NOR and NAND flash */ + platform_device_register(&physmap_flash_device); + mxc_register_device(&mxc_nand_device, &mx31lite_nand_board_info); + + mxc_register_device(&mxc_spi_device1, &spi1_pdata); + spi_register_board_info(&mc13783_spi_dev, 1); + +#if defined(CONFIG_USB_ULPI) + /* USB */ + usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, + USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + + mxc_register_device(&mxc_usbh2, &usbh2_pdata); +#endif + + /* SMSC9117 IRQ pin */ + ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_SFS6), "sms9117-irq"); + if (ret) + pr_warning("could not get LAN irq gpio\n"); + else { + gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_SFS6)); + platform_device_register(&smsc911x_device); + } +} + +static void __init mx31lite_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +struct sys_timer mx31lite_timer = { + .init = mx31lite_timer_init, +}; + +MACHINE_START(MX31LITE, "LogicPD i.MX31 SOM") + /* Maintainer: Freescale Semiconductor, Inc. */ + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx31lite_map_io, + .init_irq = mx31_init_irq, + .init_machine = mxc_board_init, + .timer = &mx31lite_timer, +MACHINE_END diff --git a/arch/arm/mach-mx3/mach-mx31moboard.c b/arch/arm/mach-mx3/mach-mx31moboard.c new file mode 100644 index 0000000..63f991f --- /dev/null +++ b/arch/arm/mach-mx3/mach-mx31moboard.c @@ -0,0 +1,580 @@ +/* + * Copyright (C) 2008 Valentin Longchamp, EPFL Mobots group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" + +static unsigned int moboard_pins[] = { + /* UART0 */ + MX31_PIN_TXD1__TXD1, MX31_PIN_RXD1__RXD1, + MX31_PIN_CTS1__GPIO2_7, + /* UART4 */ + MX31_PIN_PC_RST__CTS5, MX31_PIN_PC_VS2__RTS5, + MX31_PIN_PC_BVD2__TXD5, MX31_PIN_PC_BVD1__RXD5, + /* I2C0 */ + MX31_PIN_I2C_DAT__I2C1_SDA, MX31_PIN_I2C_CLK__I2C1_SCL, + /* I2C1 */ + MX31_PIN_DCD_DTE1__I2C2_SDA, MX31_PIN_RI_DTE1__I2C2_SCL, + /* SDHC1 */ + MX31_PIN_SD1_DATA3__SD1_DATA3, MX31_PIN_SD1_DATA2__SD1_DATA2, + MX31_PIN_SD1_DATA1__SD1_DATA1, MX31_PIN_SD1_DATA0__SD1_DATA0, + MX31_PIN_SD1_CLK__SD1_CLK, MX31_PIN_SD1_CMD__SD1_CMD, + MX31_PIN_ATA_CS0__GPIO3_26, MX31_PIN_ATA_CS1__GPIO3_27, + /* USB reset */ + MX31_PIN_GPIO1_0__GPIO1_0, + /* USB OTG */ + MX31_PIN_USBOTG_DATA0__USBOTG_DATA0, + MX31_PIN_USBOTG_DATA1__USBOTG_DATA1, + MX31_PIN_USBOTG_DATA2__USBOTG_DATA2, + MX31_PIN_USBOTG_DATA3__USBOTG_DATA3, + MX31_PIN_USBOTG_DATA4__USBOTG_DATA4, + MX31_PIN_USBOTG_DATA5__USBOTG_DATA5, + MX31_PIN_USBOTG_DATA6__USBOTG_DATA6, + MX31_PIN_USBOTG_DATA7__USBOTG_DATA7, + MX31_PIN_USBOTG_CLK__USBOTG_CLK, MX31_PIN_USBOTG_DIR__USBOTG_DIR, + MX31_PIN_USBOTG_NXT__USBOTG_NXT, MX31_PIN_USBOTG_STP__USBOTG_STP, + MX31_PIN_USB_OC__GPIO1_30, + /* USB H2 */ + MX31_PIN_USBH2_DATA0__USBH2_DATA0, + MX31_PIN_USBH2_DATA1__USBH2_DATA1, + MX31_PIN_STXD3__USBH2_DATA2, MX31_PIN_SRXD3__USBH2_DATA3, + MX31_PIN_SCK3__USBH2_DATA4, MX31_PIN_SFS3__USBH2_DATA5, + MX31_PIN_STXD6__USBH2_DATA6, MX31_PIN_SRXD6__USBH2_DATA7, + MX31_PIN_USBH2_CLK__USBH2_CLK, MX31_PIN_USBH2_DIR__USBH2_DIR, + MX31_PIN_USBH2_NXT__USBH2_NXT, MX31_PIN_USBH2_STP__USBH2_STP, + MX31_PIN_SCK6__GPIO1_25, + /* LEDs */ + MX31_PIN_SVEN0__GPIO2_0, MX31_PIN_STX0__GPIO2_1, + MX31_PIN_SRX0__GPIO2_2, MX31_PIN_SIMPD0__GPIO2_3, + /* SEL */ + MX31_PIN_DTR_DCE1__GPIO2_8, MX31_PIN_DSR_DCE1__GPIO2_9, + MX31_PIN_RI_DCE1__GPIO2_10, MX31_PIN_DCD_DCE1__GPIO2_11, + /* SPI1 */ + MX31_PIN_CSPI2_MOSI__MOSI, MX31_PIN_CSPI2_MISO__MISO, + MX31_PIN_CSPI2_SCLK__SCLK, MX31_PIN_CSPI2_SPI_RDY__SPI_RDY, + MX31_PIN_CSPI2_SS0__SS0, MX31_PIN_CSPI2_SS2__SS2, + /* Atlas IRQ */ + MX31_PIN_GPIO1_3__GPIO1_3, + /* SPI2 */ + MX31_PIN_CSPI3_MOSI__MOSI, MX31_PIN_CSPI3_MISO__MISO, + MX31_PIN_CSPI3_SCLK__SCLK, MX31_PIN_CSPI3_SPI_RDY__SPI_RDY, + MX31_PIN_CSPI2_SS1__CSPI3_SS1, +}; + +static struct physmap_flash_data mx31moboard_flash_data = { + .width = 2, +}; + +static struct resource mx31moboard_flash_resource = { + .start = 0xa0000000, + .end = 0xa1ffffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device mx31moboard_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &mx31moboard_flash_data, + }, + .resource = &mx31moboard_flash_resource, + .num_resources = 1, +}; + +static int moboard_uart0_init(struct platform_device *pdev) +{ + gpio_request(IOMUX_TO_GPIO(MX31_PIN_CTS1), "uart0-cts-hack"); + gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_CTS1), 0); + return 0; +} + +static struct imxuart_platform_data uart0_pdata = { + .init = moboard_uart0_init, +}; + +static struct imxuart_platform_data uart4_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct imxi2c_platform_data moboard_i2c0_pdata = { + .bitrate = 400000, +}; + +static struct imxi2c_platform_data moboard_i2c1_pdata = { + .bitrate = 100000, +}; + +static int moboard_spi1_cs[] = { + MXC_SPI_CS(0), + MXC_SPI_CS(2), +}; + +static struct spi_imx_master moboard_spi1_master = { + .chipselect = moboard_spi1_cs, + .num_chipselect = ARRAY_SIZE(moboard_spi1_cs), +}; + +static struct regulator_consumer_supply sdhc_consumers[] = { + { + .dev = &mxcsdhc_device0.dev, + .supply = "sdhc0_vcc", + }, + { + .dev = &mxcsdhc_device1.dev, + .supply = "sdhc1_vcc", + }, +}; + +static struct regulator_init_data sdhc_vreg_data = { + .constraints = { + .min_uV = 2700000, + .max_uV = 3000000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_FAST, + .always_on = 0, + .boot_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(sdhc_consumers), + .consumer_supplies = sdhc_consumers, +}; + +static struct regulator_consumer_supply cam_consumers[] = { + { + .dev = &mx3_camera.dev, + .supply = "cam_vcc", + }, +}; + +static struct regulator_init_data cam_vreg_data = { + .constraints = { + .min_uV = 2700000, + .max_uV = 3000000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_FAST, + .always_on = 0, + .boot_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(cam_consumers), + .consumer_supplies = cam_consumers, +}; + +static struct mc13783_regulator_init_data moboard_regulators[] = { + { + .id = MC13783_REGU_VMMC1, + .init_data = &sdhc_vreg_data, + }, + { + .id = MC13783_REGU_VCAM, + .init_data = &cam_vreg_data, + }, +}; + +static struct mc13783_platform_data moboard_pmic = { + .regulators = moboard_regulators, + .num_regulators = ARRAY_SIZE(moboard_regulators), + .flags = MC13783_USE_REGULATOR | MC13783_USE_RTC | + MC13783_USE_ADC, +}; + +static struct spi_board_info moboard_spi_board_info[] __initdata = { + { + .modalias = "mc13783", + .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3), + .max_speed_hz = 300000, + .bus_num = 1, + .chip_select = 0, + .platform_data = &moboard_pmic, + .mode = SPI_CS_HIGH, + }, +}; + +static int moboard_spi2_cs[] = { + MXC_SPI_CS(1), +}; + +static struct spi_imx_master moboard_spi2_master = { + .chipselect = moboard_spi2_cs, + .num_chipselect = ARRAY_SIZE(moboard_spi2_cs), +}; + +#define SDHC1_CD IOMUX_TO_GPIO(MX31_PIN_ATA_CS0) +#define SDHC1_WP IOMUX_TO_GPIO(MX31_PIN_ATA_CS1) + +static int moboard_sdhc1_get_ro(struct device *dev) +{ + return !gpio_get_value(SDHC1_WP); +} + +static int moboard_sdhc1_init(struct device *dev, irq_handler_t detect_irq, + void *data) +{ + int ret; + + ret = gpio_request(SDHC1_CD, "sdhc-detect"); + if (ret) + return ret; + + gpio_direction_input(SDHC1_CD); + + ret = gpio_request(SDHC1_WP, "sdhc-wp"); + if (ret) + goto err_gpio_free; + gpio_direction_input(SDHC1_WP); + + ret = request_irq(gpio_to_irq(SDHC1_CD), detect_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "sdhc1-card-detect", data); + if (ret) + goto err_gpio_free_2; + + return 0; + +err_gpio_free_2: + gpio_free(SDHC1_WP); +err_gpio_free: + gpio_free(SDHC1_CD); + + return ret; +} + +static void moboard_sdhc1_exit(struct device *dev, void *data) +{ + free_irq(gpio_to_irq(SDHC1_CD), data); + gpio_free(SDHC1_WP); + gpio_free(SDHC1_CD); +} + +static struct imxmmc_platform_data sdhc1_pdata = { + .get_ro = moboard_sdhc1_get_ro, + .init = moboard_sdhc1_init, + .exit = moboard_sdhc1_exit, +}; + +/* + * this pin is dedicated for all mx31moboard systems, so we do it here + */ +#define USB_RESET_B IOMUX_TO_GPIO(MX31_PIN_GPIO1_0) + +static void usb_xcvr_reset(void) +{ + gpio_request(USB_RESET_B, "usb-reset"); + gpio_direction_output(USB_RESET_B, 0); + mdelay(1); + gpio_set_value(USB_RESET_B, 1); +} + +#define USB_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST | PAD_CTL_HYS_CMOS | \ + PAD_CTL_ODE_CMOS | PAD_CTL_100K_PU) + +#define OTG_EN_B IOMUX_TO_GPIO(MX31_PIN_USB_OC) + +static void moboard_usbotg_init(void) +{ + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBOTG_STP, USB_PAD_CFG); + + gpio_request(OTG_EN_B, "usb-udc-en"); + gpio_direction_output(OTG_EN_B, 0); +} + +static struct fsl_usb2_platform_data usb_pdata = { + .operating_mode = FSL_USB2_DR_DEVICE, + .phy_mode = FSL_USB2_PHY_ULPI, +}; + +#if defined(CONFIG_USB_ULPI) + +#define USBH2_EN_B IOMUX_TO_GPIO(MX31_PIN_SCK6) + +static int moboard_usbh2_hw_init(struct platform_device *pdev) +{ + int ret = gpio_request(USBH2_EN_B, "usbh2-en"); + if (ret) + return ret; + + mxc_iomux_set_gpr(MUX_PGP_UH2, true); + + mxc_iomux_set_pad(MX31_PIN_USBH2_CLK, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_DIR, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_NXT, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_STP, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_DATA0, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_USBH2_DATA1, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_SRXD6, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_STXD6, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_SFS3, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_SCK3, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_SRXD3, USB_PAD_CFG); + mxc_iomux_set_pad(MX31_PIN_STXD3, USB_PAD_CFG); + + gpio_direction_output(USBH2_EN_B, 0); + + return 0; +} + +static int moboard_usbh2_hw_exit(struct platform_device *pdev) +{ + gpio_free(USBH2_EN_B); + return 0; +} + +static struct mxc_usbh_platform_data usbh2_pdata = { + .init = moboard_usbh2_hw_init, + .exit = moboard_usbh2_hw_exit, + .portsc = MXC_EHCI_MODE_ULPI | MXC_EHCI_UTMI_8BIT, + .flags = MXC_EHCI_POWER_PINS_ENABLED, +}; + +static int __init moboard_usbh2_init(void) +{ + usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, + USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + + return mxc_register_device(&mxc_usbh2, &usbh2_pdata); +} +#else +static inline int moboard_usbh2_init(void) { return 0; } +#endif + + +static struct gpio_led mx31moboard_leds[] = { + { + .name = "coreboard-led-0:red:running", + .default_trigger = "heartbeat", + .gpio = IOMUX_TO_GPIO(MX31_PIN_SVEN0), + }, { + .name = "coreboard-led-1:red", + .gpio = IOMUX_TO_GPIO(MX31_PIN_STX0), + }, { + .name = "coreboard-led-2:red", + .gpio = IOMUX_TO_GPIO(MX31_PIN_SRX0), + }, { + .name = "coreboard-led-3:red", + .gpio = IOMUX_TO_GPIO(MX31_PIN_SIMPD0), + }, +}; + +static struct gpio_led_platform_data mx31moboard_led_pdata = { + .num_leds = ARRAY_SIZE(mx31moboard_leds), + .leds = mx31moboard_leds, +}; + +static struct platform_device mx31moboard_leds_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &mx31moboard_led_pdata, + }, +}; + +#define SEL0 IOMUX_TO_GPIO(MX31_PIN_DTR_DCE1) +#define SEL1 IOMUX_TO_GPIO(MX31_PIN_DSR_DCE1) +#define SEL2 IOMUX_TO_GPIO(MX31_PIN_RI_DCE1) +#define SEL3 IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1) + +static void mx31moboard_init_sel_gpios(void) +{ + if (!gpio_request(SEL0, "sel0")) { + gpio_direction_input(SEL0); + gpio_export(SEL0, true); + } + + if (!gpio_request(SEL1, "sel1")) { + gpio_direction_input(SEL1); + gpio_export(SEL1, true); + } + + if (!gpio_request(SEL2, "sel2")) { + gpio_direction_input(SEL2); + gpio_export(SEL2, true); + } + + if (!gpio_request(SEL3, "sel3")) { + gpio_direction_input(SEL3); + gpio_export(SEL3, true); + } +} + +static struct ipu_platform_data mx3_ipu_data = { + .irq_base = MXC_IPU_IRQ_START, +}; + +static struct platform_device *devices[] __initdata = { + &mx31moboard_flash, + &mx31moboard_leds_device, +}; + +static struct mx3_camera_pdata camera_pdata = { + .dma_dev = &mx3_ipu.dev, + .flags = MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10, + .mclk_10khz = 4800, +}; + +#define CAMERA_BUF_SIZE (4*1024*1024) + +static int __init mx31moboard_cam_alloc_dma(const size_t buf_size) +{ + dma_addr_t dma_handle; + void *buf; + int dma; + + if (buf_size < 2 * 1024 * 1024) + return -EINVAL; + + buf = dma_alloc_coherent(NULL, buf_size, &dma_handle, GFP_KERNEL); + if (!buf) { + pr_err("%s: cannot allocate camera buffer-memory\n", __func__); + return -ENOMEM; + } + + memset(buf, 0, buf_size); + + dma = dma_declare_coherent_memory(&mx3_camera.dev, + dma_handle, dma_handle, buf_size, + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); + + /* The way we call dma_declare_coherent_memory only a malloc can fail */ + return dma & DMA_MEMORY_MAP ? 0 : -ENOMEM; +} + +static int mx31moboard_baseboard; +core_param(mx31moboard_baseboard, mx31moboard_baseboard, int, 0444); + +/* + * Board specific initialization. + */ +static void __init mxc_board_init(void) +{ + mxc_iomux_setup_multiple_pins(moboard_pins, ARRAY_SIZE(moboard_pins), + "moboard"); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + mxc_register_device(&mxc_uart_device0, &uart0_pdata); + + mxc_register_device(&mxc_uart_device4, &uart4_pdata); + + mx31moboard_init_sel_gpios(); + + mxc_register_device(&mxc_i2c_device0, &moboard_i2c0_pdata); + mxc_register_device(&mxc_i2c_device1, &moboard_i2c1_pdata); + + mxc_register_device(&mxc_spi_device1, &moboard_spi1_master); + mxc_register_device(&mxc_spi_device2, &moboard_spi2_master); + + gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3), "pmic-irq"); + gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3)); + spi_register_board_info(moboard_spi_board_info, + ARRAY_SIZE(moboard_spi_board_info)); + + mxc_register_device(&mxcsdhc_device0, &sdhc1_pdata); + + mxc_register_device(&mx3_ipu, &mx3_ipu_data); + if (!mx31moboard_cam_alloc_dma(CAMERA_BUF_SIZE)) + mxc_register_device(&mx3_camera, &camera_pdata); + + usb_xcvr_reset(); + + moboard_usbotg_init(); + mxc_register_device(&mxc_otg_udc_device, &usb_pdata); + moboard_usbh2_init(); + + switch (mx31moboard_baseboard) { + case MX31NOBOARD: + break; + case MX31DEVBOARD: + mx31moboard_devboard_init(); + break; + case MX31MARXBOT: + mx31moboard_marxbot_init(); + break; + default: + printk(KERN_ERR "Illegal mx31moboard_baseboard type %d\n", + mx31moboard_baseboard); + } +} + +static void __init mx31moboard_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +struct sys_timer mx31moboard_timer = { + .init = mx31moboard_timer_init, +}; + +MACHINE_START(MX31MOBOARD, "EPFL Mobots mx31moboard") + /* Maintainer: Valentin Longchamp, EPFL Mobots group */ + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx31_map_io, + .init_irq = mx31_init_irq, + .init_machine = mxc_board_init, + .timer = &mx31moboard_timer, +MACHINE_END + diff --git a/arch/arm/mach-mx3/mach-mx35pdk.c b/arch/arm/mach-mx3/mach-mx35pdk.c new file mode 100644 index 0000000..2d11bf0 --- /dev/null +++ b/arch/arm/mach-mx3/mach-mx35pdk.c @@ -0,0 +1,116 @@ +/* + * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Author: Fabio Estevam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "devices.h" + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct platform_device *devices[] __initdata = { + &mxc_fec_device, +}; + +static struct pad_desc mx35pdk_pads[] = { + /* UART1 */ + MX35_PAD_CTS1__UART1_CTS, + MX35_PAD_RTS1__UART1_RTS, + MX35_PAD_TXD1__UART1_TXD_MUX, + MX35_PAD_RXD1__UART1_RXD_MUX, + /* FEC */ + MX35_PAD_FEC_TX_CLK__FEC_TX_CLK, + MX35_PAD_FEC_RX_CLK__FEC_RX_CLK, + MX35_PAD_FEC_RX_DV__FEC_RX_DV, + MX35_PAD_FEC_COL__FEC_COL, + MX35_PAD_FEC_RDATA0__FEC_RDATA_0, + MX35_PAD_FEC_TDATA0__FEC_TDATA_0, + MX35_PAD_FEC_TX_EN__FEC_TX_EN, + MX35_PAD_FEC_MDC__FEC_MDC, + MX35_PAD_FEC_MDIO__FEC_MDIO, + MX35_PAD_FEC_TX_ERR__FEC_TX_ERR, + MX35_PAD_FEC_RX_ERR__FEC_RX_ERR, + MX35_PAD_FEC_CRS__FEC_CRS, + MX35_PAD_FEC_RDATA1__FEC_RDATA_1, + MX35_PAD_FEC_TDATA1__FEC_TDATA_1, + MX35_PAD_FEC_RDATA2__FEC_RDATA_2, + MX35_PAD_FEC_TDATA2__FEC_TDATA_2, + MX35_PAD_FEC_RDATA3__FEC_RDATA_3, + MX35_PAD_FEC_TDATA3__FEC_TDATA_3, + /* USBOTG */ + MX35_PAD_USBOTG_PWR__USB_TOP_USBOTG_PWR, + MX35_PAD_USBOTG_OC__USB_TOP_USBOTG_OC, +}; + +/* OTG config */ +static struct fsl_usb2_platform_data usb_pdata = { + .operating_mode = FSL_USB2_DR_DEVICE, + .phy_mode = FSL_USB2_PHY_UTMI_WIDE, +}; + +/* + * Board specific initialization. + */ +static void __init mxc_board_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mx35pdk_pads, ARRAY_SIZE(mx35pdk_pads)); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + mxc_register_device(&mxc_uart_device0, &uart_pdata); + + mxc_register_device(&mxc_otg_udc_device, &usb_pdata); +} + +static void __init mx35pdk_timer_init(void) +{ + mx35_clocks_init(); +} + +struct sys_timer mx35pdk_timer = { + .init = mx35pdk_timer_init, +}; + +MACHINE_START(MX35_3DS, "Freescale MX35PDK") + /* Maintainer: Freescale Semiconductor, Inc */ + .phys_io = MX35_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX35_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx35_map_io, + .init_irq = mx35_init_irq, + .init_machine = mxc_board_init, + .timer = &mx35pdk_timer, +MACHINE_END diff --git a/arch/arm/mach-mx3/mach-pcm037.c b/arch/arm/mach-mx3/mach-pcm037.c new file mode 100644 index 0000000..d9bd7d2 --- /dev/null +++ b/arch/arm/mach-mx3/mach-pcm037.c @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2008 Sascha Hauer, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devices.h" +#include "pcm037.h" + +static enum pcm037_board_variant pcm037_instance = PCM037_PCM970; + +static int __init pcm037_variant_setup(char *str) +{ + if (!strcmp("eet", str)) + pcm037_instance = PCM037_EET; + else if (strcmp("pcm970", str)) + pr_warning("Unknown pcm037 baseboard variant %s\n", str); + + return 1; +} + +/* Supported values: "pcm970" (default) and "eet" */ +__setup("pcm037_variant=", pcm037_variant_setup); + +enum pcm037_board_variant pcm037_variant(void) +{ + return pcm037_instance; +} + +/* UART1 with RTS/CTS handshake signals */ +static unsigned int pcm037_uart1_handshake_pins[] = { + MX31_PIN_CTS1__CTS1, + MX31_PIN_RTS1__RTS1, + MX31_PIN_TXD1__TXD1, + MX31_PIN_RXD1__RXD1, +}; + +/* UART1 without RTS/CTS handshake signals */ +static unsigned int pcm037_uart1_pins[] = { + MX31_PIN_TXD1__TXD1, + MX31_PIN_RXD1__RXD1, +}; + +static unsigned int pcm037_pins[] = { + /* I2C */ + MX31_PIN_CSPI2_MOSI__SCL, + MX31_PIN_CSPI2_MISO__SDA, + MX31_PIN_CSPI2_SS2__I2C3_SDA, + MX31_PIN_CSPI2_SCLK__I2C3_SCL, + /* SDHC1 */ + MX31_PIN_SD1_DATA3__SD1_DATA3, + MX31_PIN_SD1_DATA2__SD1_DATA2, + MX31_PIN_SD1_DATA1__SD1_DATA1, + MX31_PIN_SD1_DATA0__SD1_DATA0, + MX31_PIN_SD1_CLK__SD1_CLK, + MX31_PIN_SD1_CMD__SD1_CMD, + IOMUX_MODE(MX31_PIN_SCK6, IOMUX_CONFIG_GPIO), /* card detect */ + IOMUX_MODE(MX31_PIN_SFS6, IOMUX_CONFIG_GPIO), /* write protect */ + /* SPI1 */ + MX31_PIN_CSPI1_MOSI__MOSI, + MX31_PIN_CSPI1_MISO__MISO, + MX31_PIN_CSPI1_SCLK__SCLK, + MX31_PIN_CSPI1_SPI_RDY__SPI_RDY, + MX31_PIN_CSPI1_SS0__SS0, + MX31_PIN_CSPI1_SS1__SS1, + MX31_PIN_CSPI1_SS2__SS2, + /* UART2 */ + MX31_PIN_TXD2__TXD2, + MX31_PIN_RXD2__RXD2, + MX31_PIN_CTS2__CTS2, + MX31_PIN_RTS2__RTS2, + /* UART3 */ + MX31_PIN_CSPI3_MOSI__RXD3, + MX31_PIN_CSPI3_MISO__TXD3, + MX31_PIN_CSPI3_SCLK__RTS3, + MX31_PIN_CSPI3_SPI_RDY__CTS3, + /* LAN9217 irq pin */ + IOMUX_MODE(MX31_PIN_GPIO3_1, IOMUX_CONFIG_GPIO), + /* Onewire */ + MX31_PIN_BATT_LINE__OWIRE, + /* Framebuffer */ + MX31_PIN_LD0__LD0, + MX31_PIN_LD1__LD1, + MX31_PIN_LD2__LD2, + MX31_PIN_LD3__LD3, + MX31_PIN_LD4__LD4, + MX31_PIN_LD5__LD5, + MX31_PIN_LD6__LD6, + MX31_PIN_LD7__LD7, + MX31_PIN_LD8__LD8, + MX31_PIN_LD9__LD9, + MX31_PIN_LD10__LD10, + MX31_PIN_LD11__LD11, + MX31_PIN_LD12__LD12, + MX31_PIN_LD13__LD13, + MX31_PIN_LD14__LD14, + MX31_PIN_LD15__LD15, + MX31_PIN_LD16__LD16, + MX31_PIN_LD17__LD17, + MX31_PIN_VSYNC3__VSYNC3, + MX31_PIN_HSYNC__HSYNC, + MX31_PIN_FPSHIFT__FPSHIFT, + MX31_PIN_DRDY0__DRDY0, + MX31_PIN_D3_REV__D3_REV, + MX31_PIN_CONTRAST__CONTRAST, + MX31_PIN_D3_SPL__D3_SPL, + MX31_PIN_D3_CLS__D3_CLS, + MX31_PIN_LCS0__GPI03_23, + /* CSI */ + IOMUX_MODE(MX31_PIN_CSI_D5, IOMUX_CONFIG_GPIO), + MX31_PIN_CSI_D6__CSI_D6, + MX31_PIN_CSI_D7__CSI_D7, + MX31_PIN_CSI_D8__CSI_D8, + MX31_PIN_CSI_D9__CSI_D9, + MX31_PIN_CSI_D10__CSI_D10, + MX31_PIN_CSI_D11__CSI_D11, + MX31_PIN_CSI_D12__CSI_D12, + MX31_PIN_CSI_D13__CSI_D13, + MX31_PIN_CSI_D14__CSI_D14, + MX31_PIN_CSI_D15__CSI_D15, + MX31_PIN_CSI_HSYNC__CSI_HSYNC, + MX31_PIN_CSI_MCLK__CSI_MCLK, + MX31_PIN_CSI_PIXCLK__CSI_PIXCLK, + MX31_PIN_CSI_VSYNC__CSI_VSYNC, + /* GPIO */ + IOMUX_MODE(MX31_PIN_ATA_DMACK, IOMUX_CONFIG_GPIO), +}; + +static struct physmap_flash_data pcm037_flash_data = { + .width = 2, +}; + +static struct resource pcm037_flash_resource = { + .start = 0xa0000000, + .end = 0xa1ffffff, + .flags = IORESOURCE_MEM, +}; + +static int usbotg_pins[] = { + MX31_PIN_USBOTG_DATA0__USBOTG_DATA0, + MX31_PIN_USBOTG_DATA1__USBOTG_DATA1, + MX31_PIN_USBOTG_DATA2__USBOTG_DATA2, + MX31_PIN_USBOTG_DATA3__USBOTG_DATA3, + MX31_PIN_USBOTG_DATA4__USBOTG_DATA4, + MX31_PIN_USBOTG_DATA5__USBOTG_DATA5, + MX31_PIN_USBOTG_DATA6__USBOTG_DATA6, + MX31_PIN_USBOTG_DATA7__USBOTG_DATA7, + MX31_PIN_USBOTG_CLK__USBOTG_CLK, + MX31_PIN_USBOTG_DIR__USBOTG_DIR, + MX31_PIN_USBOTG_NXT__USBOTG_NXT, + MX31_PIN_USBOTG_STP__USBOTG_STP, +}; + +/* USB OTG HS port */ +static int __init gpio_usbotg_hs_activate(void) +{ + int ret = mxc_iomux_setup_multiple_pins(usbotg_pins, + ARRAY_SIZE(usbotg_pins), "usbotg"); + + if (ret < 0) { + printk(KERN_ERR "Cannot set up OTG pins\n"); + return ret; + } + + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + mxc_iomux_set_pad(MX31_PIN_USBOTG_STP, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); + + return 0; +} + +/* OTG config */ +static struct fsl_usb2_platform_data usb_pdata = { + .operating_mode = FSL_USB2_DR_DEVICE, + .phy_mode = FSL_USB2_PHY_ULPI, +}; + +static struct platform_device pcm037_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &pcm037_flash_data, + }, + .resource = &pcm037_flash_resource, + .num_resources = 1, +}; + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static struct resource smsc911x_resources[] = { + { + .start = MX31_CS1_BASE_ADDR + 0x300, + .end = MX31_CS1_BASE_ADDR + 0x300 + SZ_64K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1), + .end = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, + }, +}; + +static struct smsc911x_platform_config smsc911x_info = { + .flags = SMSC911X_USE_32BIT | SMSC911X_FORCE_INTERNAL_PHY | + SMSC911X_SAVE_MAC_ADDRESS, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, + .phy_interface = PHY_INTERFACE_MODE_MII, +}; + +static struct platform_device pcm037_eth = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smsc911x_resources), + .resource = smsc911x_resources, + .dev = { + .platform_data = &smsc911x_info, + }, +}; + +static struct platdata_mtd_ram pcm038_sram_data = { + .bankwidth = 2, +}; + +static struct resource pcm038_sram_resource = { + .start = MX31_CS4_BASE_ADDR, + .end = MX31_CS4_BASE_ADDR + 512 * 1024 - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device pcm037_sram_device = { + .name = "mtd-ram", + .id = 0, + .dev = { + .platform_data = &pcm038_sram_data, + }, + .num_resources = 1, + .resource = &pcm038_sram_resource, +}; + +static struct mxc_nand_platform_data pcm037_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +static struct imxi2c_platform_data pcm037_i2c_1_data = { + .bitrate = 100000, +}; + +static struct imxi2c_platform_data pcm037_i2c_2_data = { + .bitrate = 20000, +}; + +static struct at24_platform_data board_eeprom = { + .byte_len = 4096, + .page_size = 32, + .flags = AT24_FLAG_ADDR16, +}; + +static int pcm037_camera_power(struct device *dev, int on) +{ + /* disable or enable the camera in X7 or X8 PCM970 connector */ + gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), !on); + return 0; +} + +static struct i2c_board_info pcm037_i2c_camera[] = { + { + I2C_BOARD_INFO("mt9t031", 0x5d), + }, { + I2C_BOARD_INFO("mt9v022", 0x48), + }, +}; + +static struct soc_camera_link iclink_mt9v022 = { + .bus_id = 0, /* Must match with the camera ID */ + .board_info = &pcm037_i2c_camera[1], + .i2c_adapter_id = 2, + .module_name = "mt9v022", +}; + +static struct soc_camera_link iclink_mt9t031 = { + .bus_id = 0, /* Must match with the camera ID */ + .power = pcm037_camera_power, + .board_info = &pcm037_i2c_camera[0], + .i2c_adapter_id = 2, + .module_name = "mt9t031", +}; + +static struct i2c_board_info pcm037_i2c_devices[] = { + { + I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */ + .platform_data = &board_eeprom, + }, { + I2C_BOARD_INFO("pcf8563", 0x51), + } +}; + +static struct platform_device pcm037_mt9t031 = { + .name = "soc-camera-pdrv", + .id = 0, + .dev = { + .platform_data = &iclink_mt9t031, + }, +}; + +static struct platform_device pcm037_mt9v022 = { + .name = "soc-camera-pdrv", + .id = 1, + .dev = { + .platform_data = &iclink_mt9v022, + }, +}; + +/* Not connected by default */ +#ifdef PCM970_SDHC_RW_SWITCH +static int pcm970_sdhc1_get_ro(struct device *dev) +{ + return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_SFS6)); +} +#endif + +#define SDHC1_GPIO_WP IOMUX_TO_GPIO(MX31_PIN_SFS6) +#define SDHC1_GPIO_DET IOMUX_TO_GPIO(MX31_PIN_SCK6) + +static int pcm970_sdhc1_init(struct device *dev, irq_handler_t detect_irq, + void *data) +{ + int ret; + + ret = gpio_request(SDHC1_GPIO_DET, "sdhc-detect"); + if (ret) + return ret; + + gpio_direction_input(SDHC1_GPIO_DET); + +#ifdef PCM970_SDHC_RW_SWITCH + ret = gpio_request(SDHC1_GPIO_WP, "sdhc-wp"); + if (ret) + goto err_gpio_free; + gpio_direction_input(SDHC1_GPIO_WP); +#endif + + ret = request_irq(IOMUX_TO_IRQ(MX31_PIN_SCK6), detect_irq, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "sdhc-detect", data); + if (ret) + goto err_gpio_free_2; + + return 0; + +err_gpio_free_2: +#ifdef PCM970_SDHC_RW_SWITCH + gpio_free(SDHC1_GPIO_WP); +err_gpio_free: +#endif + gpio_free(SDHC1_GPIO_DET); + + return ret; +} + +static void pcm970_sdhc1_exit(struct device *dev, void *data) +{ + free_irq(IOMUX_TO_IRQ(MX31_PIN_SCK6), data); + gpio_free(SDHC1_GPIO_DET); + gpio_free(SDHC1_GPIO_WP); +} + +static struct imxmmc_platform_data sdhc_pdata = { +#ifdef PCM970_SDHC_RW_SWITCH + .get_ro = pcm970_sdhc1_get_ro, +#endif + .init = pcm970_sdhc1_init, + .exit = pcm970_sdhc1_exit, +}; + +struct mx3_camera_pdata camera_pdata = { + .dma_dev = &mx3_ipu.dev, + .flags = MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10, + .mclk_10khz = 2000, +}; + +static int __init pcm037_camera_alloc_dma(const size_t buf_size) +{ + dma_addr_t dma_handle; + void *buf; + int dma; + + if (buf_size < 2 * 1024 * 1024) + return -EINVAL; + + buf = dma_alloc_coherent(NULL, buf_size, &dma_handle, GFP_KERNEL); + if (!buf) { + pr_err("%s: cannot allocate camera buffer-memory\n", __func__); + return -ENOMEM; + } + + memset(buf, 0, buf_size); + + dma = dma_declare_coherent_memory(&mx3_camera.dev, + dma_handle, dma_handle, buf_size, + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); + + /* The way we call dma_declare_coherent_memory only a malloc can fail */ + return dma & DMA_MEMORY_MAP ? 0 : -ENOMEM; +} + +static struct platform_device *devices[] __initdata = { + &pcm037_flash, + &pcm037_sram_device, + &pcm037_mt9t031, + &pcm037_mt9v022, +}; + +static struct ipu_platform_data mx3_ipu_data = { + .irq_base = MXC_IPU_IRQ_START, +}; + +static const struct fb_videomode fb_modedb[] = { + { + /* 240x320 @ 60 Hz Sharp */ + .name = "Sharp-LQ035Q7DH06-QVGA", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 185925, + .left_margin = 9, + .right_margin = 16, + .upper_margin = 7, + .lower_margin = 9, + .hsync_len = 1, + .vsync_len = 1, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | + FB_SYNC_CLK_INVERT | FB_SYNC_CLK_IDLE_EN, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, { + /* 240x320 @ 60 Hz */ + .name = "TX090", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 38255, + .left_margin = 144, + .right_margin = 0, + .upper_margin = 7, + .lower_margin = 40, + .hsync_len = 96, + .vsync_len = 1, + .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, { + /* 240x320 @ 60 Hz */ + .name = "CMEL-OLED", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 185925, + .left_margin = 9, + .right_margin = 16, + .upper_margin = 7, + .lower_margin = 9, + .hsync_len = 1, + .vsync_len = 1, + .sync = FB_SYNC_OE_ACT_HIGH | FB_SYNC_CLK_INVERT, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, +}; + +static struct mx3fb_platform_data mx3fb_pdata = { + .dma_dev = &mx3_ipu.dev, + .name = "Sharp-LQ035Q7DH06-QVGA", + .mode = fb_modedb, + .num_modes = ARRAY_SIZE(fb_modedb), +}; + +static struct resource pcm970_sja1000_resources[] = { + { + .start = MX31_CS5_BASE_ADDR, + .end = MX31_CS5_BASE_ADDR + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, { + .start = IOMUX_TO_IRQ(IOMUX_PIN(48, 105)), + .end = IOMUX_TO_IRQ(IOMUX_PIN(48, 105)), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE, + }, +}; + +struct sja1000_platform_data pcm970_sja1000_platform_data = { + .clock = 16000000 / 2, + .ocr = 0x40 | 0x18, + .cdr = 0x40, +}; + +static struct platform_device pcm970_sja1000 = { + .name = "sja1000_platform", + .dev = { + .platform_data = &pcm970_sja1000_platform_data, + }, + .resource = pcm970_sja1000_resources, + .num_resources = ARRAY_SIZE(pcm970_sja1000_resources), +}; + +/* + * Board specific initialization. + */ +static void __init mxc_board_init(void) +{ + int ret; + + mxc_iomux_setup_multiple_pins(pcm037_pins, ARRAY_SIZE(pcm037_pins), + "pcm037"); + + if (pcm037_variant() == PCM037_EET) + mxc_iomux_setup_multiple_pins(pcm037_uart1_pins, + ARRAY_SIZE(pcm037_uart1_pins), "pcm037_uart1"); + else + mxc_iomux_setup_multiple_pins(pcm037_uart1_handshake_pins, + ARRAY_SIZE(pcm037_uart1_handshake_pins), + "pcm037_uart1"); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + mxc_register_device(&mxc_uart_device0, &uart_pdata); + mxc_register_device(&mxc_uart_device1, &uart_pdata); + mxc_register_device(&mxc_uart_device2, &uart_pdata); + + mxc_register_device(&mxc_w1_master_device, NULL); + + /* LAN9217 IRQ pin */ + ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO3_1), "lan9217-irq"); + if (ret) + pr_warning("could not get LAN irq gpio\n"); + else { + gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO3_1)); + platform_device_register(&pcm037_eth); + } + + + /* I2C adapters and devices */ + i2c_register_board_info(1, pcm037_i2c_devices, + ARRAY_SIZE(pcm037_i2c_devices)); + + mxc_register_device(&mxc_i2c_device1, &pcm037_i2c_1_data); + mxc_register_device(&mxc_i2c_device2, &pcm037_i2c_2_data); + + mxc_register_device(&mxc_nand_device, &pcm037_nand_board_info); + mxc_register_device(&mxcsdhc_device0, &sdhc_pdata); + mxc_register_device(&mx3_ipu, &mx3_ipu_data); + mxc_register_device(&mx3_fb, &mx3fb_pdata); + if (!gpio_usbotg_hs_activate()) + mxc_register_device(&mxc_otg_udc_device, &usb_pdata); + + /* CSI */ + /* Camera power: default - off */ + ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), "mt9t031-power"); + if (!ret) + gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), 1); + else + iclink_mt9t031.power = NULL; + + if (!pcm037_camera_alloc_dma(4 * 1024 * 1024)) + mxc_register_device(&mx3_camera, &camera_pdata); + + platform_device_register(&pcm970_sja1000); +} + +static void __init pcm037_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +struct sys_timer pcm037_timer = { + .init = pcm037_timer_init, +}; + +MACHINE_START(PCM037, "Phytec Phycore pcm037") + /* Maintainer: Pengutronix */ + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx31_map_io, + .init_irq = mx31_init_irq, + .init_machine = mxc_board_init, + .timer = &pcm037_timer, +MACHINE_END diff --git a/arch/arm/mach-mx3/mach-pcm037_eet.c b/arch/arm/mach-mx3/mach-pcm037_eet.c new file mode 100644 index 0000000..8d38600 --- /dev/null +++ b/arch/arm/mach-mx3/mach-pcm037_eet.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2009 + * Guennadi Liakhovetski, DENX Software Engineering, + * + * 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 +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) +#include +#endif +#include + +#include + +#include "pcm037.h" +#include "devices.h" + +static unsigned int pcm037_eet_pins[] = { + /* Reserve and hardwire GPIO 57 high - S6E63D6 chipselect */ + IOMUX_MODE(MX31_PIN_KEY_COL7, IOMUX_CONFIG_GPIO), + /* GPIO keys */ + IOMUX_MODE(MX31_PIN_GPIO1_0, IOMUX_CONFIG_GPIO), /* 0 */ + IOMUX_MODE(MX31_PIN_GPIO1_1, IOMUX_CONFIG_GPIO), /* 1 */ + IOMUX_MODE(MX31_PIN_GPIO1_2, IOMUX_CONFIG_GPIO), /* 2 */ + IOMUX_MODE(MX31_PIN_GPIO1_3, IOMUX_CONFIG_GPIO), /* 3 */ + IOMUX_MODE(MX31_PIN_SVEN0, IOMUX_CONFIG_GPIO), /* 32 */ + IOMUX_MODE(MX31_PIN_STX0, IOMUX_CONFIG_GPIO), /* 33 */ + IOMUX_MODE(MX31_PIN_SRX0, IOMUX_CONFIG_GPIO), /* 34 */ + IOMUX_MODE(MX31_PIN_SIMPD0, IOMUX_CONFIG_GPIO), /* 35 */ + IOMUX_MODE(MX31_PIN_RTS1, IOMUX_CONFIG_GPIO), /* 38 */ + IOMUX_MODE(MX31_PIN_CTS1, IOMUX_CONFIG_GPIO), /* 39 */ + IOMUX_MODE(MX31_PIN_KEY_ROW4, IOMUX_CONFIG_GPIO), /* 50 */ + IOMUX_MODE(MX31_PIN_KEY_ROW5, IOMUX_CONFIG_GPIO), /* 51 */ + IOMUX_MODE(MX31_PIN_KEY_ROW6, IOMUX_CONFIG_GPIO), /* 52 */ + IOMUX_MODE(MX31_PIN_KEY_ROW7, IOMUX_CONFIG_GPIO), /* 53 */ + + /* LEDs */ + IOMUX_MODE(MX31_PIN_DTR_DTE1, IOMUX_CONFIG_GPIO), /* 44 */ + IOMUX_MODE(MX31_PIN_DSR_DTE1, IOMUX_CONFIG_GPIO), /* 45 */ + IOMUX_MODE(MX31_PIN_KEY_COL5, IOMUX_CONFIG_GPIO), /* 55 */ + IOMUX_MODE(MX31_PIN_KEY_COL6, IOMUX_CONFIG_GPIO), /* 56 */ +}; + +/* SPI */ +static struct spi_board_info pcm037_spi_dev[] = { + { + .modalias = "dac124s085", + .max_speed_hz = 400000, + .bus_num = 0, + .chip_select = 0, /* Index in pcm037_spi1_cs[] */ + .mode = SPI_CPHA, + }, +}; + +/* Platform Data for MXC CSPI */ +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) +static int pcm037_spi1_cs[] = {MXC_SPI_CS(1), IOMUX_TO_GPIO(MX31_PIN_KEY_COL7)}; + +struct spi_imx_master pcm037_spi1_master = { + .chipselect = pcm037_spi1_cs, + .num_chipselect = ARRAY_SIZE(pcm037_spi1_cs), +}; +#endif + +/* GPIO-keys input device */ +static struct gpio_keys_button pcm037_gpio_keys[] = { + { + .type = EV_KEY, + .code = KEY_L, + .gpio = 0, + .desc = "Wheel Manual", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_A, + .gpio = 1, + .desc = "Wheel AF", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_V, + .gpio = 2, + .desc = "Wheel View", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_M, + .gpio = 3, + .desc = "Wheel Menu", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_UP, + .gpio = 32, + .desc = "Nav Pad Up", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_RIGHT, + .gpio = 33, + .desc = "Nav Pad Right", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_DOWN, + .gpio = 34, + .desc = "Nav Pad Down", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_LEFT, + .gpio = 35, + .desc = "Nav Pad Left", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_ENTER, + .gpio = 38, + .desc = "Nav Pad Ok", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = KEY_O, + .gpio = 39, + .desc = "Wheel Off", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = BTN_FORWARD, + .gpio = 50, + .desc = "Focus Forward", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = BTN_BACK, + .gpio = 51, + .desc = "Focus Backward", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = BTN_MIDDLE, + .gpio = 52, + .desc = "Release Half", + .wakeup = 0, + }, { + .type = EV_KEY, + .code = BTN_EXTRA, + .gpio = 53, + .desc = "Release Full", + .wakeup = 0, + }, +}; + +static struct gpio_keys_platform_data pcm037_gpio_keys_platform_data = { + .buttons = pcm037_gpio_keys, + .nbuttons = ARRAY_SIZE(pcm037_gpio_keys), + .rep = 0, /* No auto-repeat */ +}; + +static struct platform_device pcm037_gpio_keys_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &pcm037_gpio_keys_platform_data, + }, +}; + +static int eet_init_devices(void) +{ + if (!machine_is_pcm037() || pcm037_variant() != PCM037_EET) + return 0; + + mxc_iomux_setup_multiple_pins(pcm037_eet_pins, + ARRAY_SIZE(pcm037_eet_pins), "pcm037_eet"); + + /* SPI */ + spi_register_board_info(pcm037_spi_dev, ARRAY_SIZE(pcm037_spi_dev)); +#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) + mxc_register_device(&mxc_spi_device0, &pcm037_spi1_master); +#endif + + platform_device_register(&pcm037_gpio_keys_device); + + return 0; +} + +late_initcall(eet_init_devices); diff --git a/arch/arm/mach-mx3/mach-pcm043.c b/arch/arm/mach-mx3/mach-pcm043.c new file mode 100644 index 0000000..1212194 --- /dev/null +++ b/arch/arm/mach-mx3/mach-pcm043.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2009 Sascha Hauer, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#if defined CONFIG_I2C_IMX || defined CONFIG_I2C_IMX_MODULE +#include +#endif +#include +#include +#include +#include + +#include "devices.h" + +static const struct fb_videomode fb_modedb[] = { + { + /* 240x320 @ 60 Hz */ + .name = "Sharp-LQ035Q7", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 185925, + .left_margin = 9, + .right_margin = 16, + .upper_margin = 7, + .lower_margin = 9, + .hsync_len = 1, + .vsync_len = 1, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | FB_SYNC_CLK_INVERT | FB_SYNC_CLK_IDLE_EN, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, { + /* 240x320 @ 60 Hz */ + .name = "TX090", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 38255, + .left_margin = 144, + .right_margin = 0, + .upper_margin = 7, + .lower_margin = 40, + .hsync_len = 96, + .vsync_len = 1, + .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, +}; + +static struct ipu_platform_data mx3_ipu_data = { + .irq_base = MXC_IPU_IRQ_START, +}; + +static struct mx3fb_platform_data mx3fb_pdata = { + .dma_dev = &mx3_ipu.dev, + .name = "Sharp-LQ035Q7", + .mode = fb_modedb, + .num_modes = ARRAY_SIZE(fb_modedb), +}; + +static struct physmap_flash_data pcm043_flash_data = { + .width = 2, +}; + +static struct resource pcm043_flash_resource = { + .start = 0xa0000000, + .end = 0xa1ffffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device pcm043_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &pcm043_flash_data, + }, + .resource = &pcm043_flash_resource, + .num_resources = 1, +}; + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +#if defined CONFIG_I2C_IMX || defined CONFIG_I2C_IMX_MODULE +static struct imxi2c_platform_data pcm043_i2c_1_data = { + .bitrate = 50000, +}; + +static struct at24_platform_data board_eeprom = { + .byte_len = 4096, + .page_size = 32, + .flags = AT24_FLAG_ADDR16, +}; + +static struct i2c_board_info pcm043_i2c_devices[] = { + { + I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */ + .platform_data = &board_eeprom, + }, { + I2C_BOARD_INFO("pcf8563", 0x51), + } +}; +#endif + +static struct platform_device *devices[] __initdata = { + &pcm043_flash, + &mxc_fec_device, +}; + +static struct pad_desc pcm043_pads[] = { + /* UART1 */ + MX35_PAD_CTS1__UART1_CTS, + MX35_PAD_RTS1__UART1_RTS, + MX35_PAD_TXD1__UART1_TXD_MUX, + MX35_PAD_RXD1__UART1_RXD_MUX, + /* UART2 */ + MX35_PAD_CTS2__UART2_CTS, + MX35_PAD_RTS2__UART2_RTS, + MX35_PAD_TXD2__UART2_TXD_MUX, + MX35_PAD_RXD2__UART2_RXD_MUX, + /* FEC */ + MX35_PAD_FEC_TX_CLK__FEC_TX_CLK, + MX35_PAD_FEC_RX_CLK__FEC_RX_CLK, + MX35_PAD_FEC_RX_DV__FEC_RX_DV, + MX35_PAD_FEC_COL__FEC_COL, + MX35_PAD_FEC_RDATA0__FEC_RDATA_0, + MX35_PAD_FEC_TDATA0__FEC_TDATA_0, + MX35_PAD_FEC_TX_EN__FEC_TX_EN, + MX35_PAD_FEC_MDC__FEC_MDC, + MX35_PAD_FEC_MDIO__FEC_MDIO, + MX35_PAD_FEC_TX_ERR__FEC_TX_ERR, + MX35_PAD_FEC_RX_ERR__FEC_RX_ERR, + MX35_PAD_FEC_CRS__FEC_CRS, + MX35_PAD_FEC_RDATA1__FEC_RDATA_1, + MX35_PAD_FEC_TDATA1__FEC_TDATA_1, + MX35_PAD_FEC_RDATA2__FEC_RDATA_2, + MX35_PAD_FEC_TDATA2__FEC_TDATA_2, + MX35_PAD_FEC_RDATA3__FEC_RDATA_3, + MX35_PAD_FEC_TDATA3__FEC_TDATA_3, + /* I2C1 */ + MX35_PAD_I2C1_CLK__I2C1_SCL, + MX35_PAD_I2C1_DAT__I2C1_SDA, + /* Display */ + MX35_PAD_LD0__IPU_DISPB_DAT_0, + MX35_PAD_LD1__IPU_DISPB_DAT_1, + MX35_PAD_LD2__IPU_DISPB_DAT_2, + MX35_PAD_LD3__IPU_DISPB_DAT_3, + MX35_PAD_LD4__IPU_DISPB_DAT_4, + MX35_PAD_LD5__IPU_DISPB_DAT_5, + MX35_PAD_LD6__IPU_DISPB_DAT_6, + MX35_PAD_LD7__IPU_DISPB_DAT_7, + MX35_PAD_LD8__IPU_DISPB_DAT_8, + MX35_PAD_LD9__IPU_DISPB_DAT_9, + MX35_PAD_LD10__IPU_DISPB_DAT_10, + MX35_PAD_LD11__IPU_DISPB_DAT_11, + MX35_PAD_LD12__IPU_DISPB_DAT_12, + MX35_PAD_LD13__IPU_DISPB_DAT_13, + MX35_PAD_LD14__IPU_DISPB_DAT_14, + MX35_PAD_LD15__IPU_DISPB_DAT_15, + MX35_PAD_LD16__IPU_DISPB_DAT_16, + MX35_PAD_LD17__IPU_DISPB_DAT_17, + MX35_PAD_D3_HSYNC__IPU_DISPB_D3_HSYNC, + MX35_PAD_D3_FPSHIFT__IPU_DISPB_D3_CLK, + MX35_PAD_D3_DRDY__IPU_DISPB_D3_DRDY, + MX35_PAD_CONTRAST__IPU_DISPB_CONTR, + MX35_PAD_D3_VSYNC__IPU_DISPB_D3_VSYNC, + MX35_PAD_D3_REV__IPU_DISPB_D3_REV, + MX35_PAD_D3_CLS__IPU_DISPB_D3_CLS, + /* gpio */ + MX35_PAD_ATA_CS0__GPIO2_6, +}; + +static struct mxc_nand_platform_data pcm037_nand_board_info = { + .width = 1, + .hw_ecc = 1, +}; + +/* + * Board specific initialization. + */ +static void __init mxc_board_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(pcm043_pads, ARRAY_SIZE(pcm043_pads)); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + mxc_register_device(&mxc_uart_device0, &uart_pdata); + mxc_register_device(&mxc_nand_device, &pcm037_nand_board_info); + + mxc_register_device(&mxc_uart_device1, &uart_pdata); + +#if defined CONFIG_I2C_IMX || defined CONFIG_I2C_IMX_MODULE + i2c_register_board_info(0, pcm043_i2c_devices, + ARRAY_SIZE(pcm043_i2c_devices)); + + mxc_register_device(&mxc_i2c_device0, &pcm043_i2c_1_data); +#endif + + mxc_register_device(&mx3_ipu, &mx3_ipu_data); + mxc_register_device(&mx3_fb, &mx3fb_pdata); +} + +static void __init pcm043_timer_init(void) +{ + mx35_clocks_init(); +} + +struct sys_timer pcm043_timer = { + .init = pcm043_timer_init, +}; + +MACHINE_START(PCM043, "Phytec Phycore pcm043") + /* Maintainer: Pengutronix */ + .phys_io = MX35_AIPS1_BASE_ADDR, + .io_pg_offst = ((MX35_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx35_map_io, + .init_irq = mx35_init_irq, + .init_machine = mxc_board_init, + .timer = &pcm043_timer, +MACHINE_END + diff --git a/arch/arm/mach-mx3/mach-qong.c b/arch/arm/mach-mx3/mach-qong.c new file mode 100644 index 0000000..93991f1 --- /dev/null +++ b/arch/arm/mach-mx3/mach-qong.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2009 Ilya Yanok, Emcraft Systems Ltd, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices.h" + +/* FPGA defines */ +#define QONG_FPGA_VERSION(major, minor, rev) \ + (((major & 0xF) << 12) | ((minor & 0xF) << 8) | (rev & 0xFF)) + +#define QONG_FPGA_BASEADDR MX31_CS1_BASE_ADDR +#define QONG_FPGA_PERIPH_SIZE (1 << 24) + +#define QONG_FPGA_CTRL_BASEADDR QONG_FPGA_BASEADDR +#define QONG_FPGA_CTRL_SIZE 0x10 +/* FPGA control registers */ +#define QONG_FPGA_CTRL_VERSION 0x00 + +#define QONG_DNET_ID 1 +#define QONG_DNET_BASEADDR \ + (QONG_FPGA_BASEADDR + QONG_DNET_ID * QONG_FPGA_PERIPH_SIZE) +#define QONG_DNET_SIZE 0x00001000 + +#define QONG_FPGA_IRQ IOMUX_TO_IRQ(MX31_PIN_DTR_DCE1) + +/* + * This file contains the board-specific initialization routines. + */ + +static struct imxuart_platform_data uart_pdata = { + .flags = IMXUART_HAVE_RTSCTS, +}; + +static int uart_pins[] = { + MX31_PIN_CTS1__CTS1, + MX31_PIN_RTS1__RTS1, + MX31_PIN_TXD1__TXD1, + MX31_PIN_RXD1__RXD1 +}; + +static inline void mxc_init_imx_uart(void) +{ + mxc_iomux_setup_multiple_pins(uart_pins, ARRAY_SIZE(uart_pins), + "uart-0"); + mxc_register_device(&mxc_uart_device0, &uart_pdata); +} + +static struct resource dnet_resources[] = { + { + .name = "dnet-memory", + .start = QONG_DNET_BASEADDR, + .end = QONG_DNET_BASEADDR + QONG_DNET_SIZE - 1, + .flags = IORESOURCE_MEM, + }, { + .start = QONG_FPGA_IRQ, + .end = QONG_FPGA_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device dnet_device = { + .name = "dnet", + .id = -1, + .num_resources = ARRAY_SIZE(dnet_resources), + .resource = dnet_resources, +}; + +static int __init qong_init_dnet(void) +{ + int ret; + + ret = platform_device_register(&dnet_device); + return ret; +} + +/* MTD NOR flash */ + +static struct physmap_flash_data qong_flash_data = { + .width = 2, +}; + +static struct resource qong_flash_resource = { + .start = MX31_CS0_BASE_ADDR, + .end = MX31_CS0_BASE_ADDR + QONG_NOR_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device qong_nor_mtd_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &qong_flash_data, + }, + .resource = &qong_flash_resource, + .num_resources = 1, +}; + +static void qong_init_nor_mtd(void) +{ + (void)platform_device_register(&qong_nor_mtd_device); +} + +/* + * Hardware specific access to control-lines + */ +static void qong_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd->priv; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writeb(cmd, nand_chip->IO_ADDR_W + (1 << 24)); + else + writeb(cmd, nand_chip->IO_ADDR_W + (1 << 23)); +} + +/* + * Read the Device Ready pin. + */ +static int qong_nand_device_ready(struct mtd_info *mtd) +{ + return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_NFRB)); +} + +static void qong_nand_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip >= 0) + gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_NFCE_B), 0); + else + gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_NFCE_B), 1); +} + +static struct platform_nand_data qong_nand_data = { + .chip = { + .chip_delay = 20, + .options = 0, + }, + .ctrl = { + .cmd_ctrl = qong_nand_cmd_ctrl, + .dev_ready = qong_nand_device_ready, + .select_chip = qong_nand_select_chip, + } +}; + +static struct resource qong_nand_resource = { + .start = MX31_CS3_BASE_ADDR, + .end = MX31_CS3_BASE_ADDR + SZ_32M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device qong_nand_device = { + .name = "gen_nand", + .id = -1, + .dev = { + .platform_data = &qong_nand_data, + }, + .num_resources = 1, + .resource = &qong_nand_resource, +}; + +static void __init qong_init_nand_mtd(void) +{ + /* init CS */ + __raw_writel(0x00004f00, CSCR_U(3)); + __raw_writel(0x20013b31, CSCR_L(3)); + __raw_writel(0x00020800, CSCR_A(3)); + mxc_iomux_set_gpr(MUX_SDCTL_CSD1_SEL, true); + + /* enable pin */ + mxc_iomux_mode(IOMUX_MODE(MX31_PIN_NFCE_B, IOMUX_CONFIG_GPIO)); + if (!gpio_request(IOMUX_TO_GPIO(MX31_PIN_NFCE_B), "nand_enable")) + gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_NFCE_B), 0); + + /* ready/busy pin */ + mxc_iomux_mode(IOMUX_MODE(MX31_PIN_NFRB, IOMUX_CONFIG_GPIO)); + if (!gpio_request(IOMUX_TO_GPIO(MX31_PIN_NFRB), "nand_rdy")) + gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_NFRB)); + + /* write protect pin */ + mxc_iomux_mode(IOMUX_MODE(MX31_PIN_NFWP_B, IOMUX_CONFIG_GPIO)); + if (!gpio_request(IOMUX_TO_GPIO(MX31_PIN_NFWP_B), "nand_wp")) + gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_NFWP_B)); + + platform_device_register(&qong_nand_device); +} + +static void __init qong_init_fpga(void) +{ + void __iomem *regs; + u32 fpga_ver; + + regs = ioremap(QONG_FPGA_CTRL_BASEADDR, QONG_FPGA_CTRL_SIZE); + if (!regs) { + printk(KERN_ERR "%s: failed to map registers, aborting.\n", + __func__); + return; + } + + fpga_ver = readl(regs + QONG_FPGA_CTRL_VERSION); + iounmap(regs); + printk(KERN_INFO "Qong FPGA version %d.%d.%d\n", + (fpga_ver & 0xF000) >> 12, + (fpga_ver & 0x0F00) >> 8, fpga_ver & 0x00FF); + if (fpga_ver < QONG_FPGA_VERSION(0, 8, 7)) { + printk(KERN_ERR "qong: Unexpected FPGA version, FPGA-based " + "devices won't be registered!\n"); + return; + } + + /* register FPGA-based devices */ + qong_init_nand_mtd(); + qong_init_dnet(); +} + +/* + * Board specific initialization. + */ +static void __init mxc_board_init(void) +{ + mxc_init_imx_uart(); + qong_init_nor_mtd(); + qong_init_fpga(); +} + +static void __init qong_timer_init(void) +{ + mx31_clocks_init(26000000); +} + +static struct sys_timer qong_timer = { + .init = qong_timer_init, +}; + +/* + * The following uses standard kernel macros defined in arch.h in order to + * initialize __mach_desc_QONG data structure. + */ + +MACHINE_START(QONG, "Dave/DENX QongEVB-LITE") + /* Maintainer: DENX Software Engineering GmbH */ + .phys_io = MX31_AIPS1_BASE_ADDR, + .io_pg_offst = (MX31_AIPS1_BASE_ADDR_VIRT >> 18) & 0xfffc, + .boot_params = PHYS_OFFSET + 0x100, + .map_io = mx31_map_io, + .init_irq = mx31_init_irq, + .init_machine = mxc_board_init, + .timer = &qong_timer, +MACHINE_END diff --git a/arch/arm/mach-mx3/mx31ads.c b/arch/arm/mach-mx3/mx31ads.c deleted file mode 100644 index 6699116..0000000 --- a/arch/arm/mach-mx3/mx31ads.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Copyright (C) 2000 Deep Blue Solutions Ltd - * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * Copyright 2005-2007 Freescale Semiconductor, Inc. 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 as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_MACH_MX31ADS_WM1133_EV1 -#include -#include -#include -#endif - -#include "devices.h" - -/*! - * @file mx31ads.c - * - * @brief This file contains the board-specific initialization routines. - * - * @ingroup System - */ - -#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) -/*! - * The serial port definition structure. - */ -static struct plat_serial8250_port serial_platform_data[] = { - { - .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTA), - .mapbase = (unsigned long)(MX31_CS4_BASE_ADDR + PBC_SC16C652_UARTA), - .irq = EXPIO_INT_XUART_INTA, - .uartclk = 14745600, - .regshift = 0, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ, - }, { - .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTB), - .mapbase = (unsigned long)(MX31_CS4_BASE_ADDR + PBC_SC16C652_UARTB), - .irq = EXPIO_INT_XUART_INTB, - .uartclk = 14745600, - .regshift = 0, - .iotype = UPIO_MEM, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ, - }, - {}, -}; - -static struct platform_device serial_device = { - .name = "serial8250", - .id = 0, - .dev = { - .platform_data = serial_platform_data, - }, -}; - -static int __init mxc_init_extuart(void) -{ - return platform_device_register(&serial_device); -} -#else -static inline int mxc_init_extuart(void) -{ - return 0; -} -#endif - -#if defined(CONFIG_SERIAL_IMX) || defined(CONFIG_SERIAL_IMX_MODULE) -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static unsigned int uart_pins[] = { - MX31_PIN_CTS1__CTS1, - MX31_PIN_RTS1__RTS1, - MX31_PIN_TXD1__TXD1, - MX31_PIN_RXD1__RXD1 -}; - -static inline void mxc_init_imx_uart(void) -{ - mxc_iomux_setup_multiple_pins(uart_pins, ARRAY_SIZE(uart_pins), "uart-0"); - mxc_register_device(&mxc_uart_device0, &uart_pdata); -} -#else /* !SERIAL_IMX */ -static inline void mxc_init_imx_uart(void) -{ -} -#endif /* !SERIAL_IMX */ - -static void mx31ads_expio_irq_handler(u32 irq, struct irq_desc *desc) -{ - u32 imr_val; - u32 int_valid; - u32 expio_irq; - - imr_val = __raw_readw(PBC_INTMASK_SET_REG); - int_valid = __raw_readw(PBC_INTSTATUS_REG) & imr_val; - - expio_irq = MXC_EXP_IO_BASE; - for (; int_valid != 0; int_valid >>= 1, expio_irq++) { - if ((int_valid & 1) == 0) - continue; - - generic_handle_irq(expio_irq); - } -} - -/* - * Disable an expio pin's interrupt by setting the bit in the imr. - * @param irq an expio virtual irq number - */ -static void expio_mask_irq(u32 irq) -{ - u32 expio = MXC_IRQ_TO_EXPIO(irq); - /* mask the interrupt */ - __raw_writew(1 << expio, PBC_INTMASK_CLEAR_REG); - __raw_readw(PBC_INTMASK_CLEAR_REG); -} - -/* - * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr. - * @param irq an expanded io virtual irq number - */ -static void expio_ack_irq(u32 irq) -{ - u32 expio = MXC_IRQ_TO_EXPIO(irq); - /* clear the interrupt status */ - __raw_writew(1 << expio, PBC_INTSTATUS_REG); -} - -/* - * Enable a expio pin's interrupt by clearing the bit in the imr. - * @param irq a expio virtual irq number - */ -static void expio_unmask_irq(u32 irq) -{ - u32 expio = MXC_IRQ_TO_EXPIO(irq); - /* unmask the interrupt */ - __raw_writew(1 << expio, PBC_INTMASK_SET_REG); -} - -static struct irq_chip expio_irq_chip = { - .ack = expio_ack_irq, - .mask = expio_mask_irq, - .unmask = expio_unmask_irq, -}; - -static void __init mx31ads_init_expio(void) -{ - int i; - - printk(KERN_INFO "MX31ADS EXPIO(CPLD) hardware\n"); - - /* - * Configure INT line as GPIO input - */ - mxc_iomux_alloc_pin(IOMUX_MODE(MX31_PIN_GPIO1_4, IOMUX_CONFIG_GPIO), "expio"); - - /* disable the interrupt and clear the status */ - __raw_writew(0xFFFF, PBC_INTMASK_CLEAR_REG); - __raw_writew(0xFFFF, PBC_INTSTATUS_REG); - for (i = MXC_EXP_IO_BASE; i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES); - i++) { - set_irq_chip(i, &expio_irq_chip); - set_irq_handler(i, handle_level_irq); - set_irq_flags(i, IRQF_VALID); - } - set_irq_type(EXPIO_PARENT_INT, IRQ_TYPE_LEVEL_HIGH); - set_irq_chained_handler(EXPIO_PARENT_INT, mx31ads_expio_irq_handler); -} - -#ifdef CONFIG_MACH_MX31ADS_WM1133_EV1 -/* This section defines setup for the Wolfson Microelectronics - * 1133-EV1 PMU/audio board. When other PMU boards are supported the - * regulator definitions may be shared with them, but for now they can - * only be used with this board so would generate warnings about - * unused statics and some of the configuration is specific to this - * module. - */ - -/* CPU */ -static struct regulator_consumer_supply sw1a_consumers[] = { - { - .supply = "cpu_vcc", - } -}; - -static struct regulator_init_data sw1a_data = { - .constraints = { - .name = "SW1A", - .min_uV = 1275000, - .max_uV = 1600000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_FAST, - .state_mem = { - .uV = 1400000, - .mode = REGULATOR_MODE_NORMAL, - .enabled = 1, - }, - .initial_state = PM_SUSPEND_MEM, - .always_on = 1, - .boot_on = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(sw1a_consumers), - .consumer_supplies = sw1a_consumers, -}; - -/* System IO - High */ -static struct regulator_init_data viohi_data = { - .constraints = { - .name = "VIOHO", - .min_uV = 2800000, - .max_uV = 2800000, - .state_mem = { - .uV = 2800000, - .mode = REGULATOR_MODE_NORMAL, - .enabled = 1, - }, - .initial_state = PM_SUSPEND_MEM, - .always_on = 1, - .boot_on = 1, - }, -}; - -/* System IO - Low */ -static struct regulator_init_data violo_data = { - .constraints = { - .name = "VIOLO", - .min_uV = 1800000, - .max_uV = 1800000, - .state_mem = { - .uV = 1800000, - .mode = REGULATOR_MODE_NORMAL, - .enabled = 1, - }, - .initial_state = PM_SUSPEND_MEM, - .always_on = 1, - .boot_on = 1, - }, -}; - -/* DDR RAM */ -static struct regulator_init_data sw2a_data = { - .constraints = { - .name = "SW2A", - .min_uV = 1800000, - .max_uV = 1800000, - .valid_modes_mask = REGULATOR_MODE_NORMAL, - .state_mem = { - .uV = 1800000, - .mode = REGULATOR_MODE_NORMAL, - .enabled = 1, - }, - .state_disk = { - .mode = REGULATOR_MODE_NORMAL, - .enabled = 0, - }, - .always_on = 1, - .boot_on = 1, - .initial_state = PM_SUSPEND_MEM, - }, -}; - -static struct regulator_init_data ldo1_data = { - .constraints = { - .name = "VCAM/VMMC1/VMMC2", - .min_uV = 2800000, - .max_uV = 2800000, - .valid_modes_mask = REGULATOR_MODE_NORMAL, - .apply_uV = 1, - }, -}; - -static struct regulator_consumer_supply ldo2_consumers[] = { - { - .supply = "AVDD", - }, - { - .supply = "HPVDD", - }, -}; - -/* CODEC and SIM */ -static struct regulator_init_data ldo2_data = { - .constraints = { - .name = "VESIM/VSIM/AVDD", - .min_uV = 3300000, - .max_uV = 3300000, - .valid_modes_mask = REGULATOR_MODE_NORMAL, - .apply_uV = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(ldo2_consumers), - .consumer_supplies = ldo2_consumers, -}; - -/* General */ -static struct regulator_init_data vdig_data = { - .constraints = { - .name = "VDIG", - .min_uV = 1500000, - .max_uV = 1500000, - .valid_modes_mask = REGULATOR_MODE_NORMAL, - .apply_uV = 1, - .always_on = 1, - .boot_on = 1, - }, -}; - -/* Tranceivers */ -static struct regulator_init_data ldo4_data = { - .constraints = { - .name = "VRF1/CVDD_2.775", - .min_uV = 2500000, - .max_uV = 2500000, - .valid_modes_mask = REGULATOR_MODE_NORMAL, - .apply_uV = 1, - .always_on = 1, - .boot_on = 1, - }, -}; - -static struct wm8350_led_platform_data wm8350_led_data = { - .name = "wm8350:white", - .default_trigger = "heartbeat", - .max_uA = 27899, -}; - -static struct wm8350_audio_platform_data imx32ads_wm8350_setup = { - .vmid_discharge_msecs = 1000, - .drain_msecs = 30, - .cap_discharge_msecs = 700, - .vmid_charge_msecs = 700, - .vmid_s_curve = WM8350_S_CURVE_SLOW, - .dis_out4 = WM8350_DISCHARGE_SLOW, - .dis_out3 = WM8350_DISCHARGE_SLOW, - .dis_out2 = WM8350_DISCHARGE_SLOW, - .dis_out1 = WM8350_DISCHARGE_SLOW, - .vroi_out4 = WM8350_TIE_OFF_500R, - .vroi_out3 = WM8350_TIE_OFF_500R, - .vroi_out2 = WM8350_TIE_OFF_500R, - .vroi_out1 = WM8350_TIE_OFF_500R, - .vroi_enable = 0, - .codec_current_on = WM8350_CODEC_ISEL_1_0, - .codec_current_standby = WM8350_CODEC_ISEL_0_5, - .codec_current_charge = WM8350_CODEC_ISEL_1_5, -}; - -static int mx31_wm8350_init(struct wm8350 *wm8350) -{ - int i; - - wm8350_gpio_config(wm8350, 0, WM8350_GPIO_DIR_IN, - WM8350_GPIO0_PWR_ON_IN, WM8350_GPIO_ACTIVE_LOW, - WM8350_GPIO_PULL_UP, WM8350_GPIO_INVERT_OFF, - WM8350_GPIO_DEBOUNCE_ON); - - wm8350_gpio_config(wm8350, 3, WM8350_GPIO_DIR_IN, - WM8350_GPIO3_PWR_OFF_IN, WM8350_GPIO_ACTIVE_HIGH, - WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF, - WM8350_GPIO_DEBOUNCE_ON); - - wm8350_gpio_config(wm8350, 4, WM8350_GPIO_DIR_IN, - WM8350_GPIO4_MR_IN, WM8350_GPIO_ACTIVE_HIGH, - WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF, - WM8350_GPIO_DEBOUNCE_OFF); - - wm8350_gpio_config(wm8350, 7, WM8350_GPIO_DIR_IN, - WM8350_GPIO7_HIBERNATE_IN, WM8350_GPIO_ACTIVE_HIGH, - WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF, - WM8350_GPIO_DEBOUNCE_OFF); - - wm8350_gpio_config(wm8350, 6, WM8350_GPIO_DIR_OUT, - WM8350_GPIO6_SDOUT_OUT, WM8350_GPIO_ACTIVE_HIGH, - WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, - WM8350_GPIO_DEBOUNCE_OFF); - - wm8350_gpio_config(wm8350, 8, WM8350_GPIO_DIR_OUT, - WM8350_GPIO8_VCC_FAULT_OUT, WM8350_GPIO_ACTIVE_LOW, - WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, - WM8350_GPIO_DEBOUNCE_OFF); - - wm8350_gpio_config(wm8350, 9, WM8350_GPIO_DIR_OUT, - WM8350_GPIO9_BATT_FAULT_OUT, WM8350_GPIO_ACTIVE_LOW, - WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, - WM8350_GPIO_DEBOUNCE_OFF); - - /* Fix up for our own supplies. */ - for (i = 0; i < ARRAY_SIZE(ldo2_consumers); i++) - ldo2_consumers[i].dev = wm8350->dev; - - wm8350_register_regulator(wm8350, WM8350_DCDC_1, &sw1a_data); - wm8350_register_regulator(wm8350, WM8350_DCDC_3, &viohi_data); - wm8350_register_regulator(wm8350, WM8350_DCDC_4, &violo_data); - wm8350_register_regulator(wm8350, WM8350_DCDC_6, &sw2a_data); - wm8350_register_regulator(wm8350, WM8350_LDO_1, &ldo1_data); - wm8350_register_regulator(wm8350, WM8350_LDO_2, &ldo2_data); - wm8350_register_regulator(wm8350, WM8350_LDO_3, &vdig_data); - wm8350_register_regulator(wm8350, WM8350_LDO_4, &ldo4_data); - - /* LEDs */ - wm8350_dcdc_set_slot(wm8350, WM8350_DCDC_5, 1, 1, - WM8350_DC5_ERRACT_SHUTDOWN_CONV); - wm8350_isink_set_flash(wm8350, WM8350_ISINK_A, - WM8350_ISINK_FLASH_DISABLE, - WM8350_ISINK_FLASH_TRIG_BIT, - WM8350_ISINK_FLASH_DUR_32MS, - WM8350_ISINK_FLASH_ON_INSTANT, - WM8350_ISINK_FLASH_OFF_INSTANT, - WM8350_ISINK_FLASH_MODE_EN); - wm8350_dcdc25_set_mode(wm8350, WM8350_DCDC_5, - WM8350_ISINK_MODE_BOOST, - WM8350_ISINK_ILIM_NORMAL, - WM8350_DC5_RMP_20V, - WM8350_DC5_FBSRC_ISINKA); - wm8350_register_led(wm8350, 0, WM8350_DCDC_5, WM8350_ISINK_A, - &wm8350_led_data); - - wm8350->codec.platform_data = &imx32ads_wm8350_setup; - - regulator_has_full_constraints(); - - return 0; -} - -static struct wm8350_platform_data __initdata mx31_wm8350_pdata = { - .init = mx31_wm8350_init, -}; -#endif - -#if defined(CONFIG_I2C_IMX) || defined(CONFIG_I2C_IMX_MODULE) -static struct i2c_board_info __initdata mx31ads_i2c1_devices[] = { -#ifdef CONFIG_MACH_MX31ADS_WM1133_EV1 - { - I2C_BOARD_INFO("wm8350", 0x1a), - .platform_data = &mx31_wm8350_pdata, - .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3), - }, -#endif -}; - -static void mxc_init_i2c(void) -{ - i2c_register_board_info(1, mx31ads_i2c1_devices, - ARRAY_SIZE(mx31ads_i2c1_devices)); - - mxc_iomux_mode(IOMUX_MODE(MX31_PIN_CSPI2_MOSI, IOMUX_CONFIG_ALT1)); - mxc_iomux_mode(IOMUX_MODE(MX31_PIN_CSPI2_MISO, IOMUX_CONFIG_ALT1)); - - mxc_register_device(&mxc_i2c_device1, NULL); -} -#else -static void mxc_init_i2c(void) -{ -} -#endif - -/*! - * This structure defines static mappings for the i.MX31ADS board. - */ -static struct map_desc mx31ads_io_desc[] __initdata = { - { - .virtual = MX31_CS4_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), - .length = MX31_CS4_SIZE / 2, - .type = MT_DEVICE - }, -}; - -/*! - * Set up static virtual mappings. - */ -static void __init mx31ads_map_io(void) -{ - mx31_map_io(); - iotable_init(mx31ads_io_desc, ARRAY_SIZE(mx31ads_io_desc)); -} - -static void __init mx31ads_init_irq(void) -{ - mx31_init_irq(); - mx31ads_init_expio(); -} - -/*! - * Board specific initialization. - */ -static void __init mxc_board_init(void) -{ - mxc_init_extuart(); - mxc_init_imx_uart(); - mxc_init_i2c(); -} - -static void __init mx31ads_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -static struct sys_timer mx31ads_timer = { - .init = mx31ads_timer_init, -}; - -/* - * The following uses standard kernel macros defined in arch.h in order to - * initialize __mach_desc_MX31ADS data structure. - */ -MACHINE_START(MX31ADS, "Freescale MX31ADS") - /* Maintainer: Freescale Semiconductor, Inc. */ - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx31ads_map_io, - .init_irq = mx31ads_init_irq, - .init_machine = mxc_board_init, - .timer = &mx31ads_timer, -MACHINE_END diff --git a/arch/arm/mach-mx3/mx31lilly.c b/arch/arm/mach-mx3/mx31lilly.c deleted file mode 100644 index ffccea9..0000000 --- a/arch/arm/mach-mx3/mx31lilly.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * LILLY-1131 module support - * - * Copyright (c) 2009 Daniel Mack - * - * based on code for other MX31 boards, - * - * Copyright 2005-2007 Freescale Semiconductor - * Copyright (c) 2009 Alberto Panizzo - * Copyright (C) 2009 Valentin Longchamp, EPFL Mobots group - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "devices.h" - -/* - * This file contains module-specific initialization routines for LILLY-1131. - * Initialization of peripherals found on the baseboard is implemented in the - * appropriate baseboard support code. - */ - -/* SMSC ethernet support */ - -static struct resource smsc91x_resources[] = { - { - .start = MX31_CS4_BASE_ADDR, - .end = MX31_CS4_BASE_ADDR + 0xffff, - .flags = IORESOURCE_MEM, - }, - { - .start = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), - .end = IOMUX_TO_IRQ(MX31_PIN_GPIO1_0), - .flags = IORESOURCE_IRQ | IRQF_TRIGGER_FALLING, - } -}; - -static struct smsc911x_platform_config smsc911x_config = { - .phy_interface = PHY_INTERFACE_MODE_MII, - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, - .flags = SMSC911X_USE_32BIT | - SMSC911X_SAVE_MAC_ADDRESS | - SMSC911X_FORCE_INTERNAL_PHY, -}; - -static struct platform_device smsc91x_device = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(smsc91x_resources), - .resource = smsc91x_resources, - .dev = { - .platform_data = &smsc911x_config, - } -}; - -/* NOR flash */ -static struct physmap_flash_data nor_flash_data = { - .width = 2, -}; - -static struct resource nor_flash_resource = { - .start = 0xa0000000, - .end = 0xa1ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device physmap_flash_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &nor_flash_data, - }, - .resource = &nor_flash_resource, - .num_resources = 1, -}; - -static struct platform_device *devices[] __initdata = { - &smsc91x_device, - &physmap_flash_device, -}; - -/* SPI */ - -static int spi_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), -}; - -static struct spi_imx_master spi0_pdata = { - .chipselect = spi_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), -}; - -static struct spi_imx_master spi1_pdata = { - .chipselect = spi_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), -}; - -static struct mc13783_platform_data mc13783_pdata __initdata = { - .flags = MC13783_USE_RTC | MC13783_USE_TOUCHSCREEN, -}; - -static struct spi_board_info mc13783_dev __initdata = { - .modalias = "mc13783", - .max_speed_hz = 1000000, - .bus_num = 1, - .chip_select = 0, - .platform_data = &mc13783_pdata, -}; - -static int mx31lilly_baseboard; -core_param(mx31lilly_baseboard, mx31lilly_baseboard, int, 0444); - -static void __init mx31lilly_board_init(void) -{ - switch (mx31lilly_baseboard) { - case MX31LILLY_NOBOARD: - break; - case MX31LILLY_DB: - mx31lilly_db_init(); - break; - default: - printk(KERN_ERR "Illegal mx31lilly_baseboard type %d\n", - mx31lilly_baseboard); - } - - mxc_iomux_alloc_pin(MX31_PIN_CS4__CS4, "Ethernet CS"); - - /* SPI */ - mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SCLK__SCLK, "SPI1_CLK"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI1_MOSI__MOSI, "SPI1_TX"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI1_MISO__MISO, "SPI1_RX"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SPI_RDY__SPI_RDY, "SPI1_RDY"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SS0__SS0, "SPI1_SS0"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SS1__SS1, "SPI1_SS1"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI1_SS2__SS2, "SPI1_SS2"); - - mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SCLK__SCLK, "SPI2_CLK"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI2_MOSI__MOSI, "SPI2_TX"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI2_MISO__MISO, "SPI2_RX"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SPI_RDY__SPI_RDY, "SPI2_RDY"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SS0__SS0, "SPI2_SS0"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SS1__SS1, "SPI2_SS1"); - mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SS2__SS2, "SPI2_SS2"); - - mxc_register_device(&mxc_spi_device0, &spi0_pdata); - mxc_register_device(&mxc_spi_device1, &spi1_pdata); - spi_register_board_info(&mc13783_dev, 1); - - platform_add_devices(devices, ARRAY_SIZE(devices)); -} - -static void __init mx31lilly_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -static struct sys_timer mx31lilly_timer = { - .init = mx31lilly_timer_init, -}; - -MACHINE_START(LILLY1131, "INCO startec LILLY-1131") - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx31_map_io, - .init_irq = mx31_init_irq, - .init_machine = mx31lilly_board_init, - .timer = &mx31lilly_timer, -MACHINE_END - diff --git a/arch/arm/mach-mx3/mx31lite.c b/arch/arm/mach-mx3/mx31lite.c deleted file mode 100644 index 1e26397..0000000 --- a/arch/arm/mach-mx3/mx31lite.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2000 Deep Blue Solutions Ltd - * Copyright (C) 2002 Shane Nay (shane@minirl.com) - * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright (C) 2009 Daniel Mack - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -/* - * This file contains the module-specific initialization routines. - */ - -static unsigned int mx31lite_pins[] = { - /* LAN9117 IRQ pin */ - IOMUX_MODE(MX31_PIN_SFS6, IOMUX_CONFIG_GPIO), - /* SPI 1 */ - MX31_PIN_CSPI2_SCLK__SCLK, - MX31_PIN_CSPI2_MOSI__MOSI, - MX31_PIN_CSPI2_MISO__MISO, - MX31_PIN_CSPI2_SPI_RDY__SPI_RDY, - MX31_PIN_CSPI2_SS0__SS0, - MX31_PIN_CSPI2_SS1__SS1, - MX31_PIN_CSPI2_SS2__SS2, -}; - -static struct mxc_nand_platform_data mx31lite_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -static struct smsc911x_platform_config smsc911x_config = { - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, - .flags = SMSC911X_USE_16BIT, -}; - -static struct resource smsc911x_resources[] = { - { - .start = MX31_CS4_BASE_ADDR, - .end = MX31_CS4_BASE_ADDR + 0x100, - .flags = IORESOURCE_MEM, - }, { - .start = IOMUX_TO_IRQ(MX31_PIN_SFS6), - .end = IOMUX_TO_IRQ(MX31_PIN_SFS6), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device smsc911x_device = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(smsc911x_resources), - .resource = smsc911x_resources, - .dev = { - .platform_data = &smsc911x_config, - }, -}; - -/* - * SPI - * - * The MC13783 is the only hard-wired SPI device on the module. - */ - -static int spi_internal_chipselect[] = { - MXC_SPI_CS(0), -}; - -static struct spi_imx_master spi1_pdata = { - .chipselect = spi_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), -}; - -static struct mc13783_platform_data mc13783_pdata __initdata = { - .flags = MC13783_USE_RTC | - MC13783_USE_REGULATOR, -}; - -static struct spi_board_info mc13783_spi_dev __initdata = { - .modalias = "mc13783", - .max_speed_hz = 1000000, - .bus_num = 1, - .chip_select = 0, - .platform_data = &mc13783_pdata, - .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3), -}; - -/* - * USB - */ - -#if defined(CONFIG_USB_ULPI) -#define USB_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST | PAD_CTL_HYS_CMOS | \ - PAD_CTL_ODE_CMOS | PAD_CTL_100K_PU) - -static int usbh2_init(struct platform_device *pdev) -{ - int pins[] = { - MX31_PIN_USBH2_DATA0__USBH2_DATA0, - MX31_PIN_USBH2_DATA1__USBH2_DATA1, - MX31_PIN_USBH2_CLK__USBH2_CLK, - MX31_PIN_USBH2_DIR__USBH2_DIR, - MX31_PIN_USBH2_NXT__USBH2_NXT, - MX31_PIN_USBH2_STP__USBH2_STP, - }; - - mxc_iomux_setup_multiple_pins(pins, ARRAY_SIZE(pins), "USB H2"); - - mxc_iomux_set_pad(MX31_PIN_USBH2_CLK, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_DIR, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_NXT, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_STP, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_DATA0, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_DATA1, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_SRXD6, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_STXD6, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_SFS3, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_SCK3, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_SRXD3, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_STXD3, USB_PAD_CFG); - - mxc_iomux_set_gpr(MUX_PGP_UH2, true); - - /* chip select */ - mxc_iomux_alloc_pin(IOMUX_MODE(MX31_PIN_DTR_DCE1, IOMUX_CONFIG_GPIO), - "USBH2_CS"); - gpio_request(IOMUX_TO_GPIO(MX31_PIN_DTR_DCE1), "USBH2 CS"); - gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_DTR_DCE1), 0); - - return 0; -} - -static struct mxc_usbh_platform_data usbh2_pdata = { - .init = usbh2_init, - .portsc = MXC_EHCI_MODE_ULPI | MXC_EHCI_UTMI_8BIT, - .flags = MXC_EHCI_POWER_PINS_ENABLED, -}; -#endif - -/* - * NOR flash - */ - -static struct physmap_flash_data nor_flash_data = { - .width = 2, -}; - -static struct resource nor_flash_resource = { - .start = 0xa0000000, - .end = 0xa1ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device physmap_flash_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &nor_flash_data, - }, - .resource = &nor_flash_resource, - .num_resources = 1, -}; - - - -/* - * This structure defines the MX31 memory map. - */ -static struct map_desc mx31lite_io_desc[] __initdata = { - { - .virtual = MX31_CS4_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR), - .length = MX31_CS4_SIZE, - .type = MT_DEVICE - } -}; - -/* - * Set up static virtual mappings. - */ -void __init mx31lite_map_io(void) -{ - mx31_map_io(); - iotable_init(mx31lite_io_desc, ARRAY_SIZE(mx31lite_io_desc)); -} - -static int mx31lite_baseboard; -core_param(mx31lite_baseboard, mx31lite_baseboard, int, 0444); - -static void __init mxc_board_init(void) -{ - int ret; - - switch (mx31lite_baseboard) { - case MX31LITE_NOBOARD: - break; - case MX31LITE_DB: - mx31lite_db_init(); - break; - default: - printk(KERN_ERR "Illegal mx31lite_baseboard type %d\n", - mx31lite_baseboard); - } - - mxc_iomux_setup_multiple_pins(mx31lite_pins, ARRAY_SIZE(mx31lite_pins), - "mx31lite"); - - /* NOR and NAND flash */ - platform_device_register(&physmap_flash_device); - mxc_register_device(&mxc_nand_device, &mx31lite_nand_board_info); - - mxc_register_device(&mxc_spi_device1, &spi1_pdata); - spi_register_board_info(&mc13783_spi_dev, 1); - -#if defined(CONFIG_USB_ULPI) - /* USB */ - usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); - - mxc_register_device(&mxc_usbh2, &usbh2_pdata); -#endif - - /* SMSC9117 IRQ pin */ - ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_SFS6), "sms9117-irq"); - if (ret) - pr_warning("could not get LAN irq gpio\n"); - else { - gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_SFS6)); - platform_device_register(&smsc911x_device); - } -} - -static void __init mx31lite_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -struct sys_timer mx31lite_timer = { - .init = mx31lite_timer_init, -}; - -MACHINE_START(MX31LITE, "LogicPD i.MX31 SOM") - /* Maintainer: Freescale Semiconductor, Inc. */ - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx31lite_map_io, - .init_irq = mx31_init_irq, - .init_machine = mxc_board_init, - .timer = &mx31lite_timer, -MACHINE_END diff --git a/arch/arm/mach-mx3/mx31moboard.c b/arch/arm/mach-mx3/mx31moboard.c deleted file mode 100644 index 9428827..0000000 --- a/arch/arm/mach-mx3/mx31moboard.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright (C) 2008 Valentin Longchamp, EPFL Mobots group - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "devices.h" - -static unsigned int moboard_pins[] = { - /* UART0 */ - MX31_PIN_TXD1__TXD1, MX31_PIN_RXD1__RXD1, - MX31_PIN_CTS1__GPIO2_7, - /* UART4 */ - MX31_PIN_PC_RST__CTS5, MX31_PIN_PC_VS2__RTS5, - MX31_PIN_PC_BVD2__TXD5, MX31_PIN_PC_BVD1__RXD5, - /* I2C0 */ - MX31_PIN_I2C_DAT__I2C1_SDA, MX31_PIN_I2C_CLK__I2C1_SCL, - /* I2C1 */ - MX31_PIN_DCD_DTE1__I2C2_SDA, MX31_PIN_RI_DTE1__I2C2_SCL, - /* SDHC1 */ - MX31_PIN_SD1_DATA3__SD1_DATA3, MX31_PIN_SD1_DATA2__SD1_DATA2, - MX31_PIN_SD1_DATA1__SD1_DATA1, MX31_PIN_SD1_DATA0__SD1_DATA0, - MX31_PIN_SD1_CLK__SD1_CLK, MX31_PIN_SD1_CMD__SD1_CMD, - MX31_PIN_ATA_CS0__GPIO3_26, MX31_PIN_ATA_CS1__GPIO3_27, - /* USB reset */ - MX31_PIN_GPIO1_0__GPIO1_0, - /* USB OTG */ - MX31_PIN_USBOTG_DATA0__USBOTG_DATA0, - MX31_PIN_USBOTG_DATA1__USBOTG_DATA1, - MX31_PIN_USBOTG_DATA2__USBOTG_DATA2, - MX31_PIN_USBOTG_DATA3__USBOTG_DATA3, - MX31_PIN_USBOTG_DATA4__USBOTG_DATA4, - MX31_PIN_USBOTG_DATA5__USBOTG_DATA5, - MX31_PIN_USBOTG_DATA6__USBOTG_DATA6, - MX31_PIN_USBOTG_DATA7__USBOTG_DATA7, - MX31_PIN_USBOTG_CLK__USBOTG_CLK, MX31_PIN_USBOTG_DIR__USBOTG_DIR, - MX31_PIN_USBOTG_NXT__USBOTG_NXT, MX31_PIN_USBOTG_STP__USBOTG_STP, - MX31_PIN_USB_OC__GPIO1_30, - /* USB H2 */ - MX31_PIN_USBH2_DATA0__USBH2_DATA0, - MX31_PIN_USBH2_DATA1__USBH2_DATA1, - MX31_PIN_STXD3__USBH2_DATA2, MX31_PIN_SRXD3__USBH2_DATA3, - MX31_PIN_SCK3__USBH2_DATA4, MX31_PIN_SFS3__USBH2_DATA5, - MX31_PIN_STXD6__USBH2_DATA6, MX31_PIN_SRXD6__USBH2_DATA7, - MX31_PIN_USBH2_CLK__USBH2_CLK, MX31_PIN_USBH2_DIR__USBH2_DIR, - MX31_PIN_USBH2_NXT__USBH2_NXT, MX31_PIN_USBH2_STP__USBH2_STP, - MX31_PIN_SCK6__GPIO1_25, - /* LEDs */ - MX31_PIN_SVEN0__GPIO2_0, MX31_PIN_STX0__GPIO2_1, - MX31_PIN_SRX0__GPIO2_2, MX31_PIN_SIMPD0__GPIO2_3, - /* SEL */ - MX31_PIN_DTR_DCE1__GPIO2_8, MX31_PIN_DSR_DCE1__GPIO2_9, - MX31_PIN_RI_DCE1__GPIO2_10, MX31_PIN_DCD_DCE1__GPIO2_11, - /* SPI1 */ - MX31_PIN_CSPI2_MOSI__MOSI, MX31_PIN_CSPI2_MISO__MISO, - MX31_PIN_CSPI2_SCLK__SCLK, MX31_PIN_CSPI2_SPI_RDY__SPI_RDY, - MX31_PIN_CSPI2_SS0__SS0, MX31_PIN_CSPI2_SS2__SS2, - /* Atlas IRQ */ - MX31_PIN_GPIO1_3__GPIO1_3, - /* SPI2 */ - MX31_PIN_CSPI3_MOSI__MOSI, MX31_PIN_CSPI3_MISO__MISO, - MX31_PIN_CSPI3_SCLK__SCLK, MX31_PIN_CSPI3_SPI_RDY__SPI_RDY, - MX31_PIN_CSPI2_SS1__CSPI3_SS1, -}; - -static struct physmap_flash_data mx31moboard_flash_data = { - .width = 2, -}; - -static struct resource mx31moboard_flash_resource = { - .start = 0xa0000000, - .end = 0xa1ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device mx31moboard_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &mx31moboard_flash_data, - }, - .resource = &mx31moboard_flash_resource, - .num_resources = 1, -}; - -static int moboard_uart0_init(struct platform_device *pdev) -{ - gpio_request(IOMUX_TO_GPIO(MX31_PIN_CTS1), "uart0-cts-hack"); - gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_CTS1), 0); - return 0; -} - -static struct imxuart_platform_data uart0_pdata = { - .init = moboard_uart0_init, -}; - -static struct imxuart_platform_data uart4_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct imxi2c_platform_data moboard_i2c0_pdata = { - .bitrate = 400000, -}; - -static struct imxi2c_platform_data moboard_i2c1_pdata = { - .bitrate = 100000, -}; - -static int moboard_spi1_cs[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(2), -}; - -static struct spi_imx_master moboard_spi1_master = { - .chipselect = moboard_spi1_cs, - .num_chipselect = ARRAY_SIZE(moboard_spi1_cs), -}; - -static struct regulator_consumer_supply sdhc_consumers[] = { - { - .dev = &mxcsdhc_device0.dev, - .supply = "sdhc0_vcc", - }, - { - .dev = &mxcsdhc_device1.dev, - .supply = "sdhc1_vcc", - }, -}; - -static struct regulator_init_data sdhc_vreg_data = { - .constraints = { - .min_uV = 2700000, - .max_uV = 3000000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_FAST, - .always_on = 0, - .boot_on = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(sdhc_consumers), - .consumer_supplies = sdhc_consumers, -}; - -static struct regulator_consumer_supply cam_consumers[] = { - { - .dev = &mx3_camera.dev, - .supply = "cam_vcc", - }, -}; - -static struct regulator_init_data cam_vreg_data = { - .constraints = { - .min_uV = 2700000, - .max_uV = 3000000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_FAST, - .always_on = 0, - .boot_on = 1, - }, - .num_consumer_supplies = ARRAY_SIZE(cam_consumers), - .consumer_supplies = cam_consumers, -}; - -static struct mc13783_regulator_init_data moboard_regulators[] = { - { - .id = MC13783_REGU_VMMC1, - .init_data = &sdhc_vreg_data, - }, - { - .id = MC13783_REGU_VCAM, - .init_data = &cam_vreg_data, - }, -}; - -static struct mc13783_platform_data moboard_pmic = { - .regulators = moboard_regulators, - .num_regulators = ARRAY_SIZE(moboard_regulators), - .flags = MC13783_USE_REGULATOR | MC13783_USE_RTC | - MC13783_USE_ADC, -}; - -static struct spi_board_info moboard_spi_board_info[] __initdata = { - { - .modalias = "mc13783", - .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3), - .max_speed_hz = 300000, - .bus_num = 1, - .chip_select = 0, - .platform_data = &moboard_pmic, - .mode = SPI_CS_HIGH, - }, -}; - -static int moboard_spi2_cs[] = { - MXC_SPI_CS(1), -}; - -static struct spi_imx_master moboard_spi2_master = { - .chipselect = moboard_spi2_cs, - .num_chipselect = ARRAY_SIZE(moboard_spi2_cs), -}; - -#define SDHC1_CD IOMUX_TO_GPIO(MX31_PIN_ATA_CS0) -#define SDHC1_WP IOMUX_TO_GPIO(MX31_PIN_ATA_CS1) - -static int moboard_sdhc1_get_ro(struct device *dev) -{ - return !gpio_get_value(SDHC1_WP); -} - -static int moboard_sdhc1_init(struct device *dev, irq_handler_t detect_irq, - void *data) -{ - int ret; - - ret = gpio_request(SDHC1_CD, "sdhc-detect"); - if (ret) - return ret; - - gpio_direction_input(SDHC1_CD); - - ret = gpio_request(SDHC1_WP, "sdhc-wp"); - if (ret) - goto err_gpio_free; - gpio_direction_input(SDHC1_WP); - - ret = request_irq(gpio_to_irq(SDHC1_CD), detect_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "sdhc1-card-detect", data); - if (ret) - goto err_gpio_free_2; - - return 0; - -err_gpio_free_2: - gpio_free(SDHC1_WP); -err_gpio_free: - gpio_free(SDHC1_CD); - - return ret; -} - -static void moboard_sdhc1_exit(struct device *dev, void *data) -{ - free_irq(gpio_to_irq(SDHC1_CD), data); - gpio_free(SDHC1_WP); - gpio_free(SDHC1_CD); -} - -static struct imxmmc_platform_data sdhc1_pdata = { - .get_ro = moboard_sdhc1_get_ro, - .init = moboard_sdhc1_init, - .exit = moboard_sdhc1_exit, -}; - -/* - * this pin is dedicated for all mx31moboard systems, so we do it here - */ -#define USB_RESET_B IOMUX_TO_GPIO(MX31_PIN_GPIO1_0) - -static void usb_xcvr_reset(void) -{ - gpio_request(USB_RESET_B, "usb-reset"); - gpio_direction_output(USB_RESET_B, 0); - mdelay(1); - gpio_set_value(USB_RESET_B, 1); -} - -#define USB_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST | PAD_CTL_HYS_CMOS | \ - PAD_CTL_ODE_CMOS | PAD_CTL_100K_PU) - -#define OTG_EN_B IOMUX_TO_GPIO(MX31_PIN_USB_OC) - -static void moboard_usbotg_init(void) -{ - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBOTG_STP, USB_PAD_CFG); - - gpio_request(OTG_EN_B, "usb-udc-en"); - gpio_direction_output(OTG_EN_B, 0); -} - -static struct fsl_usb2_platform_data usb_pdata = { - .operating_mode = FSL_USB2_DR_DEVICE, - .phy_mode = FSL_USB2_PHY_ULPI, -}; - -#if defined(CONFIG_USB_ULPI) - -#define USBH2_EN_B IOMUX_TO_GPIO(MX31_PIN_SCK6) - -static int moboard_usbh2_hw_init(struct platform_device *pdev) -{ - int ret = gpio_request(USBH2_EN_B, "usbh2-en"); - if (ret) - return ret; - - mxc_iomux_set_gpr(MUX_PGP_UH2, true); - - mxc_iomux_set_pad(MX31_PIN_USBH2_CLK, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_DIR, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_NXT, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_STP, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_DATA0, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_USBH2_DATA1, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_SRXD6, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_STXD6, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_SFS3, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_SCK3, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_SRXD3, USB_PAD_CFG); - mxc_iomux_set_pad(MX31_PIN_STXD3, USB_PAD_CFG); - - gpio_direction_output(USBH2_EN_B, 0); - - return 0; -} - -static int moboard_usbh2_hw_exit(struct platform_device *pdev) -{ - gpio_free(USBH2_EN_B); - return 0; -} - -static struct mxc_usbh_platform_data usbh2_pdata = { - .init = moboard_usbh2_hw_init, - .exit = moboard_usbh2_hw_exit, - .portsc = MXC_EHCI_MODE_ULPI | MXC_EHCI_UTMI_8BIT, - .flags = MXC_EHCI_POWER_PINS_ENABLED, -}; - -static int __init moboard_usbh2_init(void) -{ - usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); - - return mxc_register_device(&mxc_usbh2, &usbh2_pdata); -} -#else -static inline int moboard_usbh2_init(void) { return 0; } -#endif - - -static struct gpio_led mx31moboard_leds[] = { - { - .name = "coreboard-led-0:red:running", - .default_trigger = "heartbeat", - .gpio = IOMUX_TO_GPIO(MX31_PIN_SVEN0), - }, { - .name = "coreboard-led-1:red", - .gpio = IOMUX_TO_GPIO(MX31_PIN_STX0), - }, { - .name = "coreboard-led-2:red", - .gpio = IOMUX_TO_GPIO(MX31_PIN_SRX0), - }, { - .name = "coreboard-led-3:red", - .gpio = IOMUX_TO_GPIO(MX31_PIN_SIMPD0), - }, -}; - -static struct gpio_led_platform_data mx31moboard_led_pdata = { - .num_leds = ARRAY_SIZE(mx31moboard_leds), - .leds = mx31moboard_leds, -}; - -static struct platform_device mx31moboard_leds_device = { - .name = "leds-gpio", - .id = -1, - .dev = { - .platform_data = &mx31moboard_led_pdata, - }, -}; - -#define SEL0 IOMUX_TO_GPIO(MX31_PIN_DTR_DCE1) -#define SEL1 IOMUX_TO_GPIO(MX31_PIN_DSR_DCE1) -#define SEL2 IOMUX_TO_GPIO(MX31_PIN_RI_DCE1) -#define SEL3 IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1) - -static void mx31moboard_init_sel_gpios(void) -{ - if (!gpio_request(SEL0, "sel0")) { - gpio_direction_input(SEL0); - gpio_export(SEL0, true); - } - - if (!gpio_request(SEL1, "sel1")) { - gpio_direction_input(SEL1); - gpio_export(SEL1, true); - } - - if (!gpio_request(SEL2, "sel2")) { - gpio_direction_input(SEL2); - gpio_export(SEL2, true); - } - - if (!gpio_request(SEL3, "sel3")) { - gpio_direction_input(SEL3); - gpio_export(SEL3, true); - } -} - -static struct ipu_platform_data mx3_ipu_data = { - .irq_base = MXC_IPU_IRQ_START, -}; - -static struct platform_device *devices[] __initdata = { - &mx31moboard_flash, - &mx31moboard_leds_device, -}; - -static struct mx3_camera_pdata camera_pdata = { - .dma_dev = &mx3_ipu.dev, - .flags = MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10, - .mclk_10khz = 4800, -}; - -#define CAMERA_BUF_SIZE (4*1024*1024) - -static int __init mx31moboard_cam_alloc_dma(const size_t buf_size) -{ - dma_addr_t dma_handle; - void *buf; - int dma; - - if (buf_size < 2 * 1024 * 1024) - return -EINVAL; - - buf = dma_alloc_coherent(NULL, buf_size, &dma_handle, GFP_KERNEL); - if (!buf) { - pr_err("%s: cannot allocate camera buffer-memory\n", __func__); - return -ENOMEM; - } - - memset(buf, 0, buf_size); - - dma = dma_declare_coherent_memory(&mx3_camera.dev, - dma_handle, dma_handle, buf_size, - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); - - /* The way we call dma_declare_coherent_memory only a malloc can fail */ - return dma & DMA_MEMORY_MAP ? 0 : -ENOMEM; -} - -static int mx31moboard_baseboard; -core_param(mx31moboard_baseboard, mx31moboard_baseboard, int, 0444); - -/* - * Board specific initialization. - */ -static void __init mxc_board_init(void) -{ - mxc_iomux_setup_multiple_pins(moboard_pins, ARRAY_SIZE(moboard_pins), - "moboard"); - - platform_add_devices(devices, ARRAY_SIZE(devices)); - - mxc_register_device(&mxc_uart_device0, &uart0_pdata); - - mxc_register_device(&mxc_uart_device4, &uart4_pdata); - - mx31moboard_init_sel_gpios(); - - mxc_register_device(&mxc_i2c_device0, &moboard_i2c0_pdata); - mxc_register_device(&mxc_i2c_device1, &moboard_i2c1_pdata); - - mxc_register_device(&mxc_spi_device1, &moboard_spi1_master); - mxc_register_device(&mxc_spi_device2, &moboard_spi2_master); - - gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3), "pmic-irq"); - gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3)); - spi_register_board_info(moboard_spi_board_info, - ARRAY_SIZE(moboard_spi_board_info)); - - mxc_register_device(&mxcsdhc_device0, &sdhc1_pdata); - - mxc_register_device(&mx3_ipu, &mx3_ipu_data); - if (!mx31moboard_cam_alloc_dma(CAMERA_BUF_SIZE)) - mxc_register_device(&mx3_camera, &camera_pdata); - - usb_xcvr_reset(); - - moboard_usbotg_init(); - mxc_register_device(&mxc_otg_udc_device, &usb_pdata); - moboard_usbh2_init(); - - switch (mx31moboard_baseboard) { - case MX31NOBOARD: - break; - case MX31DEVBOARD: - mx31moboard_devboard_init(); - break; - case MX31MARXBOT: - mx31moboard_marxbot_init(); - break; - default: - printk(KERN_ERR "Illegal mx31moboard_baseboard type %d\n", - mx31moboard_baseboard); - } -} - -static void __init mx31moboard_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -struct sys_timer mx31moboard_timer = { - .init = mx31moboard_timer_init, -}; - -MACHINE_START(MX31MOBOARD, "EPFL Mobots mx31moboard") - /* Maintainer: Valentin Longchamp, EPFL Mobots group */ - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx31_map_io, - .init_irq = mx31_init_irq, - .init_machine = mxc_board_init, - .timer = &mx31moboard_timer, -MACHINE_END - diff --git a/arch/arm/mach-mx3/mx31pdk.c b/arch/arm/mach-mx3/mx31pdk.c deleted file mode 100644 index e71e3b4..0000000 --- a/arch/arm/mach-mx3/mx31pdk.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2008 Freescale Semiconductor, Inc. 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 as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "devices.h" - -/*! - * @file mx31pdk.c - * - * @brief This file contains the board-specific initialization routines. - * - * @ingroup System - */ - -static int mx31pdk_pins[] = { - /* UART1 */ - MX31_PIN_CTS1__CTS1, - MX31_PIN_RTS1__RTS1, - MX31_PIN_TXD1__TXD1, - MX31_PIN_RXD1__RXD1, - IOMUX_MODE(MX31_PIN_GPIO1_1, IOMUX_CONFIG_GPIO), -}; - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -/* - * Support for the SMSC9217 on the Debug board. - */ - -static struct smsc911x_platform_config smsc911x_config = { - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, - .flags = SMSC911X_USE_16BIT | SMSC911X_FORCE_INTERNAL_PHY, - .phy_interface = PHY_INTERFACE_MODE_MII, -}; - -static struct resource smsc911x_resources[] = { - { - .start = LAN9217_BASE_ADDR, - .end = LAN9217_BASE_ADDR + 0xff, - .flags = IORESOURCE_MEM, - }, { - .start = EXPIO_INT_ENET, - .end = EXPIO_INT_ENET, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device smsc911x_device = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(smsc911x_resources), - .resource = smsc911x_resources, - .dev = { - .platform_data = &smsc911x_config, - }, -}; - -/* - * Routines for the CPLD on the debug board. It contains a CPLD handling - * LEDs, switches, interrupts for Ethernet. - */ - -static void mx31pdk_expio_irq_handler(uint32_t irq, struct irq_desc *desc) -{ - uint32_t imr_val; - uint32_t int_valid; - uint32_t expio_irq; - - imr_val = __raw_readw(CPLD_INT_MASK_REG); - int_valid = __raw_readw(CPLD_INT_STATUS_REG) & ~imr_val; - - expio_irq = MXC_EXP_IO_BASE; - for (; int_valid != 0; int_valid >>= 1, expio_irq++) { - if ((int_valid & 1) == 0) - continue; - generic_handle_irq(expio_irq); - } -} - -/* - * Disable an expio pin's interrupt by setting the bit in the imr. - * @param irq an expio virtual irq number - */ -static void expio_mask_irq(uint32_t irq) -{ - uint16_t reg; - uint32_t expio = MXC_IRQ_TO_EXPIO(irq); - - /* mask the interrupt */ - reg = __raw_readw(CPLD_INT_MASK_REG); - reg |= 1 << expio; - __raw_writew(reg, CPLD_INT_MASK_REG); -} - -/* - * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr. - * @param irq an expanded io virtual irq number - */ -static void expio_ack_irq(uint32_t irq) -{ - uint32_t expio = MXC_IRQ_TO_EXPIO(irq); - - /* clear the interrupt status */ - __raw_writew(1 << expio, CPLD_INT_RESET_REG); - __raw_writew(0, CPLD_INT_RESET_REG); - /* mask the interrupt */ - expio_mask_irq(irq); -} - -/* - * Enable a expio pin's interrupt by clearing the bit in the imr. - * @param irq a expio virtual irq number - */ -static void expio_unmask_irq(uint32_t irq) -{ - uint16_t reg; - uint32_t expio = MXC_IRQ_TO_EXPIO(irq); - - /* unmask the interrupt */ - reg = __raw_readw(CPLD_INT_MASK_REG); - reg &= ~(1 << expio); - __raw_writew(reg, CPLD_INT_MASK_REG); -} - -static struct irq_chip expio_irq_chip = { - .ack = expio_ack_irq, - .mask = expio_mask_irq, - .unmask = expio_unmask_irq, -}; - -static int __init mx31pdk_init_expio(void) -{ - int i; - int ret; - - /* Check if there's a debug board connected */ - if ((__raw_readw(CPLD_MAGIC_NUMBER1_REG) != 0xAAAA) || - (__raw_readw(CPLD_MAGIC_NUMBER2_REG) != 0x5555) || - (__raw_readw(CPLD_MAGIC_NUMBER3_REG) != 0xCAFE)) { - /* No Debug board found */ - return -ENODEV; - } - - pr_info("i.MX31PDK Debug board detected, rev = 0x%04X\n", - __raw_readw(CPLD_CODE_VER_REG)); - - /* - * Configure INT line as GPIO input - */ - ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1), "sms9217-irq"); - if (ret) - pr_warning("could not get LAN irq gpio\n"); - else - gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1)); - - /* Disable the interrupts and clear the status */ - __raw_writew(0, CPLD_INT_MASK_REG); - __raw_writew(0xFFFF, CPLD_INT_RESET_REG); - __raw_writew(0, CPLD_INT_RESET_REG); - __raw_writew(0x1F, CPLD_INT_MASK_REG); - for (i = MXC_EXP_IO_BASE; - i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES); - i++) { - set_irq_chip(i, &expio_irq_chip); - set_irq_handler(i, handle_level_irq); - set_irq_flags(i, IRQF_VALID); - } - set_irq_type(EXPIO_PARENT_INT, IRQ_TYPE_LEVEL_LOW); - set_irq_chained_handler(EXPIO_PARENT_INT, mx31pdk_expio_irq_handler); - - return 0; -} - -/* - * This structure defines the MX31 memory map. - */ -static struct map_desc mx31pdk_io_desc[] __initdata = { - { - .virtual = MX31_CS5_BASE_ADDR_VIRT, - .pfn = __phys_to_pfn(MX31_CS5_BASE_ADDR), - .length = MX31_CS5_SIZE, - .type = MT_DEVICE, - }, -}; - -/* - * Set up static virtual mappings. - */ -static void __init mx31pdk_map_io(void) -{ - mx31_map_io(); - iotable_init(mx31pdk_io_desc, ARRAY_SIZE(mx31pdk_io_desc)); -} - -/*! - * Board specific initialization. - */ -static void __init mxc_board_init(void) -{ - mxc_iomux_setup_multiple_pins(mx31pdk_pins, ARRAY_SIZE(mx31pdk_pins), - "mx31pdk"); - - mxc_register_device(&mxc_uart_device0, &uart_pdata); - - if (!mx31pdk_init_expio()) - platform_device_register(&smsc911x_device); -} - -static void __init mx31pdk_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -static struct sys_timer mx31pdk_timer = { - .init = mx31pdk_timer_init, -}; - -/* - * The following uses standard kernel macros defined in arch.h in order to - * initialize __mach_desc_MX31PDK data structure. - */ -MACHINE_START(MX31_3DS, "Freescale MX31PDK (3DS)") - /* Maintainer: Freescale Semiconductor, Inc. */ - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx31pdk_map_io, - .init_irq = mx31_init_irq, - .init_machine = mxc_board_init, - .timer = &mx31pdk_timer, -MACHINE_END diff --git a/arch/arm/mach-mx3/mx35pdk.c b/arch/arm/mach-mx3/mx35pdk.c deleted file mode 100644 index 2d11bf0..0000000 --- a/arch/arm/mach-mx3/mx35pdk.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved. - * - * Author: Fabio Estevam - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "devices.h" - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct platform_device *devices[] __initdata = { - &mxc_fec_device, -}; - -static struct pad_desc mx35pdk_pads[] = { - /* UART1 */ - MX35_PAD_CTS1__UART1_CTS, - MX35_PAD_RTS1__UART1_RTS, - MX35_PAD_TXD1__UART1_TXD_MUX, - MX35_PAD_RXD1__UART1_RXD_MUX, - /* FEC */ - MX35_PAD_FEC_TX_CLK__FEC_TX_CLK, - MX35_PAD_FEC_RX_CLK__FEC_RX_CLK, - MX35_PAD_FEC_RX_DV__FEC_RX_DV, - MX35_PAD_FEC_COL__FEC_COL, - MX35_PAD_FEC_RDATA0__FEC_RDATA_0, - MX35_PAD_FEC_TDATA0__FEC_TDATA_0, - MX35_PAD_FEC_TX_EN__FEC_TX_EN, - MX35_PAD_FEC_MDC__FEC_MDC, - MX35_PAD_FEC_MDIO__FEC_MDIO, - MX35_PAD_FEC_TX_ERR__FEC_TX_ERR, - MX35_PAD_FEC_RX_ERR__FEC_RX_ERR, - MX35_PAD_FEC_CRS__FEC_CRS, - MX35_PAD_FEC_RDATA1__FEC_RDATA_1, - MX35_PAD_FEC_TDATA1__FEC_TDATA_1, - MX35_PAD_FEC_RDATA2__FEC_RDATA_2, - MX35_PAD_FEC_TDATA2__FEC_TDATA_2, - MX35_PAD_FEC_RDATA3__FEC_RDATA_3, - MX35_PAD_FEC_TDATA3__FEC_TDATA_3, - /* USBOTG */ - MX35_PAD_USBOTG_PWR__USB_TOP_USBOTG_PWR, - MX35_PAD_USBOTG_OC__USB_TOP_USBOTG_OC, -}; - -/* OTG config */ -static struct fsl_usb2_platform_data usb_pdata = { - .operating_mode = FSL_USB2_DR_DEVICE, - .phy_mode = FSL_USB2_PHY_UTMI_WIDE, -}; - -/* - * Board specific initialization. - */ -static void __init mxc_board_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(mx35pdk_pads, ARRAY_SIZE(mx35pdk_pads)); - - platform_add_devices(devices, ARRAY_SIZE(devices)); - - mxc_register_device(&mxc_uart_device0, &uart_pdata); - - mxc_register_device(&mxc_otg_udc_device, &usb_pdata); -} - -static void __init mx35pdk_timer_init(void) -{ - mx35_clocks_init(); -} - -struct sys_timer mx35pdk_timer = { - .init = mx35pdk_timer_init, -}; - -MACHINE_START(MX35_3DS, "Freescale MX35PDK") - /* Maintainer: Freescale Semiconductor, Inc */ - .phys_io = MX35_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX35_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx35_map_io, - .init_irq = mx35_init_irq, - .init_machine = mxc_board_init, - .timer = &mx35pdk_timer, -MACHINE_END diff --git a/arch/arm/mach-mx3/pcm037.c b/arch/arm/mach-mx3/pcm037.c deleted file mode 100644 index 701fac9..0000000 --- a/arch/arm/mach-mx3/pcm037.c +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Copyright (C) 2008 Sascha Hauer, Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "devices.h" -#include "pcm037.h" - -static enum pcm037_board_variant pcm037_instance = PCM037_PCM970; - -static int __init pcm037_variant_setup(char *str) -{ - if (!strcmp("eet", str)) - pcm037_instance = PCM037_EET; - else if (strcmp("pcm970", str)) - pr_warning("Unknown pcm037 baseboard variant %s\n", str); - - return 1; -} - -/* Supported values: "pcm970" (default) and "eet" */ -__setup("pcm037_variant=", pcm037_variant_setup); - -enum pcm037_board_variant pcm037_variant(void) -{ - return pcm037_instance; -} - -/* UART1 with RTS/CTS handshake signals */ -static unsigned int pcm037_uart1_handshake_pins[] = { - MX31_PIN_CTS1__CTS1, - MX31_PIN_RTS1__RTS1, - MX31_PIN_TXD1__TXD1, - MX31_PIN_RXD1__RXD1, -}; - -/* UART1 without RTS/CTS handshake signals */ -static unsigned int pcm037_uart1_pins[] = { - MX31_PIN_TXD1__TXD1, - MX31_PIN_RXD1__RXD1, -}; - -static unsigned int pcm037_pins[] = { - /* I2C */ - MX31_PIN_CSPI2_MOSI__SCL, - MX31_PIN_CSPI2_MISO__SDA, - MX31_PIN_CSPI2_SS2__I2C3_SDA, - MX31_PIN_CSPI2_SCLK__I2C3_SCL, - /* SDHC1 */ - MX31_PIN_SD1_DATA3__SD1_DATA3, - MX31_PIN_SD1_DATA2__SD1_DATA2, - MX31_PIN_SD1_DATA1__SD1_DATA1, - MX31_PIN_SD1_DATA0__SD1_DATA0, - MX31_PIN_SD1_CLK__SD1_CLK, - MX31_PIN_SD1_CMD__SD1_CMD, - IOMUX_MODE(MX31_PIN_SCK6, IOMUX_CONFIG_GPIO), /* card detect */ - IOMUX_MODE(MX31_PIN_SFS6, IOMUX_CONFIG_GPIO), /* write protect */ - /* SPI1 */ - MX31_PIN_CSPI1_MOSI__MOSI, - MX31_PIN_CSPI1_MISO__MISO, - MX31_PIN_CSPI1_SCLK__SCLK, - MX31_PIN_CSPI1_SPI_RDY__SPI_RDY, - MX31_PIN_CSPI1_SS0__SS0, - MX31_PIN_CSPI1_SS1__SS1, - MX31_PIN_CSPI1_SS2__SS2, - /* UART2 */ - MX31_PIN_TXD2__TXD2, - MX31_PIN_RXD2__RXD2, - MX31_PIN_CTS2__CTS2, - MX31_PIN_RTS2__RTS2, - /* UART3 */ - MX31_PIN_CSPI3_MOSI__RXD3, - MX31_PIN_CSPI3_MISO__TXD3, - MX31_PIN_CSPI3_SCLK__RTS3, - MX31_PIN_CSPI3_SPI_RDY__CTS3, - /* LAN9217 irq pin */ - IOMUX_MODE(MX31_PIN_GPIO3_1, IOMUX_CONFIG_GPIO), - /* Onewire */ - MX31_PIN_BATT_LINE__OWIRE, - /* Framebuffer */ - MX31_PIN_LD0__LD0, - MX31_PIN_LD1__LD1, - MX31_PIN_LD2__LD2, - MX31_PIN_LD3__LD3, - MX31_PIN_LD4__LD4, - MX31_PIN_LD5__LD5, - MX31_PIN_LD6__LD6, - MX31_PIN_LD7__LD7, - MX31_PIN_LD8__LD8, - MX31_PIN_LD9__LD9, - MX31_PIN_LD10__LD10, - MX31_PIN_LD11__LD11, - MX31_PIN_LD12__LD12, - MX31_PIN_LD13__LD13, - MX31_PIN_LD14__LD14, - MX31_PIN_LD15__LD15, - MX31_PIN_LD16__LD16, - MX31_PIN_LD17__LD17, - MX31_PIN_VSYNC3__VSYNC3, - MX31_PIN_HSYNC__HSYNC, - MX31_PIN_FPSHIFT__FPSHIFT, - MX31_PIN_DRDY0__DRDY0, - MX31_PIN_D3_REV__D3_REV, - MX31_PIN_CONTRAST__CONTRAST, - MX31_PIN_D3_SPL__D3_SPL, - MX31_PIN_D3_CLS__D3_CLS, - MX31_PIN_LCS0__GPI03_23, - /* CSI */ - IOMUX_MODE(MX31_PIN_CSI_D5, IOMUX_CONFIG_GPIO), - MX31_PIN_CSI_D6__CSI_D6, - MX31_PIN_CSI_D7__CSI_D7, - MX31_PIN_CSI_D8__CSI_D8, - MX31_PIN_CSI_D9__CSI_D9, - MX31_PIN_CSI_D10__CSI_D10, - MX31_PIN_CSI_D11__CSI_D11, - MX31_PIN_CSI_D12__CSI_D12, - MX31_PIN_CSI_D13__CSI_D13, - MX31_PIN_CSI_D14__CSI_D14, - MX31_PIN_CSI_D15__CSI_D15, - MX31_PIN_CSI_HSYNC__CSI_HSYNC, - MX31_PIN_CSI_MCLK__CSI_MCLK, - MX31_PIN_CSI_PIXCLK__CSI_PIXCLK, - MX31_PIN_CSI_VSYNC__CSI_VSYNC, - /* GPIO */ - IOMUX_MODE(MX31_PIN_ATA_DMACK, IOMUX_CONFIG_GPIO), -}; - -static struct physmap_flash_data pcm037_flash_data = { - .width = 2, -}; - -static struct resource pcm037_flash_resource = { - .start = 0xa0000000, - .end = 0xa1ffffff, - .flags = IORESOURCE_MEM, -}; - -static int usbotg_pins[] = { - MX31_PIN_USBOTG_DATA0__USBOTG_DATA0, - MX31_PIN_USBOTG_DATA1__USBOTG_DATA1, - MX31_PIN_USBOTG_DATA2__USBOTG_DATA2, - MX31_PIN_USBOTG_DATA3__USBOTG_DATA3, - MX31_PIN_USBOTG_DATA4__USBOTG_DATA4, - MX31_PIN_USBOTG_DATA5__USBOTG_DATA5, - MX31_PIN_USBOTG_DATA6__USBOTG_DATA6, - MX31_PIN_USBOTG_DATA7__USBOTG_DATA7, - MX31_PIN_USBOTG_CLK__USBOTG_CLK, - MX31_PIN_USBOTG_DIR__USBOTG_DIR, - MX31_PIN_USBOTG_NXT__USBOTG_NXT, - MX31_PIN_USBOTG_STP__USBOTG_STP, -}; - -/* USB OTG HS port */ -static int __init gpio_usbotg_hs_activate(void) -{ - int ret = mxc_iomux_setup_multiple_pins(usbotg_pins, - ARRAY_SIZE(usbotg_pins), "usbotg"); - - if (ret < 0) { - printk(KERN_ERR "Cannot set up OTG pins\n"); - return ret; - } - - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - mxc_iomux_set_pad(MX31_PIN_USBOTG_STP, PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST); - - return 0; -} - -/* OTG config */ -static struct fsl_usb2_platform_data usb_pdata = { - .operating_mode = FSL_USB2_DR_DEVICE, - .phy_mode = FSL_USB2_PHY_ULPI, -}; - -static struct platform_device pcm037_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &pcm037_flash_data, - }, - .resource = &pcm037_flash_resource, - .num_resources = 1, -}; - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static struct resource smsc911x_resources[] = { - { - .start = MX31_CS1_BASE_ADDR + 0x300, - .end = MX31_CS1_BASE_ADDR + 0x300 + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, { - .start = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1), - .end = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, - }, -}; - -static struct smsc911x_platform_config smsc911x_info = { - .flags = SMSC911X_USE_32BIT | SMSC911X_FORCE_INTERNAL_PHY | - SMSC911X_SAVE_MAC_ADDRESS, - .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, - .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, - .phy_interface = PHY_INTERFACE_MODE_MII, -}; - -static struct platform_device pcm037_eth = { - .name = "smsc911x", - .id = -1, - .num_resources = ARRAY_SIZE(smsc911x_resources), - .resource = smsc911x_resources, - .dev = { - .platform_data = &smsc911x_info, - }, -}; - -static struct platdata_mtd_ram pcm038_sram_data = { - .bankwidth = 2, -}; - -static struct resource pcm038_sram_resource = { - .start = MX31_CS4_BASE_ADDR, - .end = MX31_CS4_BASE_ADDR + 512 * 1024 - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device pcm037_sram_device = { - .name = "mtd-ram", - .id = 0, - .dev = { - .platform_data = &pcm038_sram_data, - }, - .num_resources = 1, - .resource = &pcm038_sram_resource, -}; - -static struct mxc_nand_platform_data pcm037_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -static struct imxi2c_platform_data pcm037_i2c_1_data = { - .bitrate = 100000, -}; - -static struct imxi2c_platform_data pcm037_i2c_2_data = { - .bitrate = 20000, -}; - -static struct at24_platform_data board_eeprom = { - .byte_len = 4096, - .page_size = 32, - .flags = AT24_FLAG_ADDR16, -}; - -static int pcm037_camera_power(struct device *dev, int on) -{ - /* disable or enable the camera in X7 or X8 PCM970 connector */ - gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), !on); - return 0; -} - -static struct i2c_board_info pcm037_i2c_camera[] = { - { - I2C_BOARD_INFO("mt9t031", 0x5d), - }, { - I2C_BOARD_INFO("mt9v022", 0x48), - }, -}; - -static struct soc_camera_link iclink_mt9v022 = { - .bus_id = 0, /* Must match with the camera ID */ - .board_info = &pcm037_i2c_camera[1], - .i2c_adapter_id = 2, - .module_name = "mt9v022", -}; - -static struct soc_camera_link iclink_mt9t031 = { - .bus_id = 0, /* Must match with the camera ID */ - .power = pcm037_camera_power, - .board_info = &pcm037_i2c_camera[0], - .i2c_adapter_id = 2, - .module_name = "mt9t031", -}; - -static struct i2c_board_info pcm037_i2c_devices[] = { - { - I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */ - .platform_data = &board_eeprom, - }, { - I2C_BOARD_INFO("pcf8563", 0x51), - } -}; - -static struct platform_device pcm037_mt9t031 = { - .name = "soc-camera-pdrv", - .id = 0, - .dev = { - .platform_data = &iclink_mt9t031, - }, -}; - -static struct platform_device pcm037_mt9v022 = { - .name = "soc-camera-pdrv", - .id = 1, - .dev = { - .platform_data = &iclink_mt9v022, - }, -}; - -/* Not connected by default */ -#ifdef PCM970_SDHC_RW_SWITCH -static int pcm970_sdhc1_get_ro(struct device *dev) -{ - return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_SFS6)); -} -#endif - -#define SDHC1_GPIO_WP IOMUX_TO_GPIO(MX31_PIN_SFS6) -#define SDHC1_GPIO_DET IOMUX_TO_GPIO(MX31_PIN_SCK6) - -static int pcm970_sdhc1_init(struct device *dev, irq_handler_t detect_irq, - void *data) -{ - int ret; - - ret = gpio_request(SDHC1_GPIO_DET, "sdhc-detect"); - if (ret) - return ret; - - gpio_direction_input(SDHC1_GPIO_DET); - -#ifdef PCM970_SDHC_RW_SWITCH - ret = gpio_request(SDHC1_GPIO_WP, "sdhc-wp"); - if (ret) - goto err_gpio_free; - gpio_direction_input(SDHC1_GPIO_WP); -#endif - - ret = request_irq(IOMUX_TO_IRQ(MX31_PIN_SCK6), detect_irq, - IRQF_DISABLED | IRQF_TRIGGER_FALLING, - "sdhc-detect", data); - if (ret) - goto err_gpio_free_2; - - return 0; - -err_gpio_free_2: -#ifdef PCM970_SDHC_RW_SWITCH - gpio_free(SDHC1_GPIO_WP); -err_gpio_free: -#endif - gpio_free(SDHC1_GPIO_DET); - - return ret; -} - -static void pcm970_sdhc1_exit(struct device *dev, void *data) -{ - free_irq(IOMUX_TO_IRQ(MX31_PIN_SCK6), data); - gpio_free(SDHC1_GPIO_DET); - gpio_free(SDHC1_GPIO_WP); -} - -static struct imxmmc_platform_data sdhc_pdata = { -#ifdef PCM970_SDHC_RW_SWITCH - .get_ro = pcm970_sdhc1_get_ro, -#endif - .init = pcm970_sdhc1_init, - .exit = pcm970_sdhc1_exit, -}; - -struct mx3_camera_pdata camera_pdata = { - .dma_dev = &mx3_ipu.dev, - .flags = MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10, - .mclk_10khz = 2000, -}; - -static int __init pcm037_camera_alloc_dma(const size_t buf_size) -{ - dma_addr_t dma_handle; - void *buf; - int dma; - - if (buf_size < 2 * 1024 * 1024) - return -EINVAL; - - buf = dma_alloc_coherent(NULL, buf_size, &dma_handle, GFP_KERNEL); - if (!buf) { - pr_err("%s: cannot allocate camera buffer-memory\n", __func__); - return -ENOMEM; - } - - memset(buf, 0, buf_size); - - dma = dma_declare_coherent_memory(&mx3_camera.dev, - dma_handle, dma_handle, buf_size, - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); - - /* The way we call dma_declare_coherent_memory only a malloc can fail */ - return dma & DMA_MEMORY_MAP ? 0 : -ENOMEM; -} - -static struct platform_device *devices[] __initdata = { - &pcm037_flash, - &pcm037_sram_device, - &pcm037_mt9t031, - &pcm037_mt9v022, -}; - -static struct ipu_platform_data mx3_ipu_data = { - .irq_base = MXC_IPU_IRQ_START, -}; - -static const struct fb_videomode fb_modedb[] = { - { - /* 240x320 @ 60 Hz Sharp */ - .name = "Sharp-LQ035Q7DH06-QVGA", - .refresh = 60, - .xres = 240, - .yres = 320, - .pixclock = 185925, - .left_margin = 9, - .right_margin = 16, - .upper_margin = 7, - .lower_margin = 9, - .hsync_len = 1, - .vsync_len = 1, - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | - FB_SYNC_CLK_INVERT | FB_SYNC_CLK_IDLE_EN, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, { - /* 240x320 @ 60 Hz */ - .name = "TX090", - .refresh = 60, - .xres = 240, - .yres = 320, - .pixclock = 38255, - .left_margin = 144, - .right_margin = 0, - .upper_margin = 7, - .lower_margin = 40, - .hsync_len = 96, - .vsync_len = 1, - .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, { - /* 240x320 @ 60 Hz */ - .name = "CMEL-OLED", - .refresh = 60, - .xres = 240, - .yres = 320, - .pixclock = 185925, - .left_margin = 9, - .right_margin = 16, - .upper_margin = 7, - .lower_margin = 9, - .hsync_len = 1, - .vsync_len = 1, - .sync = FB_SYNC_OE_ACT_HIGH | FB_SYNC_CLK_INVERT, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, -}; - -static struct mx3fb_platform_data mx3fb_pdata = { - .dma_dev = &mx3_ipu.dev, - .name = "Sharp-LQ035Q7DH06-QVGA", - .mode = fb_modedb, - .num_modes = ARRAY_SIZE(fb_modedb), -}; - -static struct resource pcm970_sja1000_resources[] = { - { - .start = MX31_CS5_BASE_ADDR, - .end = MX31_CS5_BASE_ADDR + 0x100 - 1, - .flags = IORESOURCE_MEM, - }, { - .start = IOMUX_TO_IRQ(IOMUX_PIN(48, 105)), - .end = IOMUX_TO_IRQ(IOMUX_PIN(48, 105)), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE, - }, -}; - -struct sja1000_platform_data pcm970_sja1000_platform_data = { - .clock = 16000000 / 2, - .ocr = 0x40 | 0x18, - .cdr = 0x40, -}; - -static struct platform_device pcm970_sja1000 = { - .name = "sja1000_platform", - .dev = { - .platform_data = &pcm970_sja1000_platform_data, - }, - .resource = pcm970_sja1000_resources, - .num_resources = ARRAY_SIZE(pcm970_sja1000_resources), -}; - -/* - * Board specific initialization. - */ -static void __init mxc_board_init(void) -{ - int ret; - - mxc_iomux_setup_multiple_pins(pcm037_pins, ARRAY_SIZE(pcm037_pins), - "pcm037"); - - if (pcm037_variant() == PCM037_EET) - mxc_iomux_setup_multiple_pins(pcm037_uart1_pins, - ARRAY_SIZE(pcm037_uart1_pins), "pcm037_uart1"); - else - mxc_iomux_setup_multiple_pins(pcm037_uart1_handshake_pins, - ARRAY_SIZE(pcm037_uart1_handshake_pins), - "pcm037_uart1"); - - platform_add_devices(devices, ARRAY_SIZE(devices)); - - mxc_register_device(&mxc_uart_device0, &uart_pdata); - mxc_register_device(&mxc_uart_device1, &uart_pdata); - mxc_register_device(&mxc_uart_device2, &uart_pdata); - - mxc_register_device(&mxc_w1_master_device, NULL); - - /* LAN9217 IRQ pin */ - ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO3_1), "lan9217-irq"); - if (ret) - pr_warning("could not get LAN irq gpio\n"); - else { - gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO3_1)); - platform_device_register(&pcm037_eth); - } - - - /* I2C adapters and devices */ - i2c_register_board_info(1, pcm037_i2c_devices, - ARRAY_SIZE(pcm037_i2c_devices)); - - mxc_register_device(&mxc_i2c_device1, &pcm037_i2c_1_data); - mxc_register_device(&mxc_i2c_device2, &pcm037_i2c_2_data); - - mxc_register_device(&mxc_nand_device, &pcm037_nand_board_info); - mxc_register_device(&mxcsdhc_device0, &sdhc_pdata); - mxc_register_device(&mx3_ipu, &mx3_ipu_data); - mxc_register_device(&mx3_fb, &mx3fb_pdata); - if (!gpio_usbotg_hs_activate()) - mxc_register_device(&mxc_otg_udc_device, &usb_pdata); - - /* CSI */ - /* Camera power: default - off */ - ret = gpio_request(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), "mt9t031-power"); - if (!ret) - gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), 1); - else - iclink_mt9t031.power = NULL; - - if (!pcm037_camera_alloc_dma(4 * 1024 * 1024)) - mxc_register_device(&mx3_camera, &camera_pdata); - - platform_device_register(&pcm970_sja1000); -} - -static void __init pcm037_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -struct sys_timer pcm037_timer = { - .init = pcm037_timer_init, -}; - -MACHINE_START(PCM037, "Phytec Phycore pcm037") - /* Maintainer: Pengutronix */ - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx31_map_io, - .init_irq = mx31_init_irq, - .init_machine = mxc_board_init, - .timer = &pcm037_timer, -MACHINE_END diff --git a/arch/arm/mach-mx3/pcm037_eet.c b/arch/arm/mach-mx3/pcm037_eet.c deleted file mode 100644 index 8d38600..0000000 --- a/arch/arm/mach-mx3/pcm037_eet.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2009 - * Guennadi Liakhovetski, DENX Software Engineering, - * - * 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 -#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) -#include -#endif -#include - -#include - -#include "pcm037.h" -#include "devices.h" - -static unsigned int pcm037_eet_pins[] = { - /* Reserve and hardwire GPIO 57 high - S6E63D6 chipselect */ - IOMUX_MODE(MX31_PIN_KEY_COL7, IOMUX_CONFIG_GPIO), - /* GPIO keys */ - IOMUX_MODE(MX31_PIN_GPIO1_0, IOMUX_CONFIG_GPIO), /* 0 */ - IOMUX_MODE(MX31_PIN_GPIO1_1, IOMUX_CONFIG_GPIO), /* 1 */ - IOMUX_MODE(MX31_PIN_GPIO1_2, IOMUX_CONFIG_GPIO), /* 2 */ - IOMUX_MODE(MX31_PIN_GPIO1_3, IOMUX_CONFIG_GPIO), /* 3 */ - IOMUX_MODE(MX31_PIN_SVEN0, IOMUX_CONFIG_GPIO), /* 32 */ - IOMUX_MODE(MX31_PIN_STX0, IOMUX_CONFIG_GPIO), /* 33 */ - IOMUX_MODE(MX31_PIN_SRX0, IOMUX_CONFIG_GPIO), /* 34 */ - IOMUX_MODE(MX31_PIN_SIMPD0, IOMUX_CONFIG_GPIO), /* 35 */ - IOMUX_MODE(MX31_PIN_RTS1, IOMUX_CONFIG_GPIO), /* 38 */ - IOMUX_MODE(MX31_PIN_CTS1, IOMUX_CONFIG_GPIO), /* 39 */ - IOMUX_MODE(MX31_PIN_KEY_ROW4, IOMUX_CONFIG_GPIO), /* 50 */ - IOMUX_MODE(MX31_PIN_KEY_ROW5, IOMUX_CONFIG_GPIO), /* 51 */ - IOMUX_MODE(MX31_PIN_KEY_ROW6, IOMUX_CONFIG_GPIO), /* 52 */ - IOMUX_MODE(MX31_PIN_KEY_ROW7, IOMUX_CONFIG_GPIO), /* 53 */ - - /* LEDs */ - IOMUX_MODE(MX31_PIN_DTR_DTE1, IOMUX_CONFIG_GPIO), /* 44 */ - IOMUX_MODE(MX31_PIN_DSR_DTE1, IOMUX_CONFIG_GPIO), /* 45 */ - IOMUX_MODE(MX31_PIN_KEY_COL5, IOMUX_CONFIG_GPIO), /* 55 */ - IOMUX_MODE(MX31_PIN_KEY_COL6, IOMUX_CONFIG_GPIO), /* 56 */ -}; - -/* SPI */ -static struct spi_board_info pcm037_spi_dev[] = { - { - .modalias = "dac124s085", - .max_speed_hz = 400000, - .bus_num = 0, - .chip_select = 0, /* Index in pcm037_spi1_cs[] */ - .mode = SPI_CPHA, - }, -}; - -/* Platform Data for MXC CSPI */ -#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) -static int pcm037_spi1_cs[] = {MXC_SPI_CS(1), IOMUX_TO_GPIO(MX31_PIN_KEY_COL7)}; - -struct spi_imx_master pcm037_spi1_master = { - .chipselect = pcm037_spi1_cs, - .num_chipselect = ARRAY_SIZE(pcm037_spi1_cs), -}; -#endif - -/* GPIO-keys input device */ -static struct gpio_keys_button pcm037_gpio_keys[] = { - { - .type = EV_KEY, - .code = KEY_L, - .gpio = 0, - .desc = "Wheel Manual", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_A, - .gpio = 1, - .desc = "Wheel AF", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_V, - .gpio = 2, - .desc = "Wheel View", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_M, - .gpio = 3, - .desc = "Wheel Menu", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_UP, - .gpio = 32, - .desc = "Nav Pad Up", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_RIGHT, - .gpio = 33, - .desc = "Nav Pad Right", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_DOWN, - .gpio = 34, - .desc = "Nav Pad Down", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_LEFT, - .gpio = 35, - .desc = "Nav Pad Left", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_ENTER, - .gpio = 38, - .desc = "Nav Pad Ok", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = KEY_O, - .gpio = 39, - .desc = "Wheel Off", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = BTN_FORWARD, - .gpio = 50, - .desc = "Focus Forward", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = BTN_BACK, - .gpio = 51, - .desc = "Focus Backward", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = BTN_MIDDLE, - .gpio = 52, - .desc = "Release Half", - .wakeup = 0, - }, { - .type = EV_KEY, - .code = BTN_EXTRA, - .gpio = 53, - .desc = "Release Full", - .wakeup = 0, - }, -}; - -static struct gpio_keys_platform_data pcm037_gpio_keys_platform_data = { - .buttons = pcm037_gpio_keys, - .nbuttons = ARRAY_SIZE(pcm037_gpio_keys), - .rep = 0, /* No auto-repeat */ -}; - -static struct platform_device pcm037_gpio_keys_device = { - .name = "gpio-keys", - .id = -1, - .dev = { - .platform_data = &pcm037_gpio_keys_platform_data, - }, -}; - -static int eet_init_devices(void) -{ - if (!machine_is_pcm037() || pcm037_variant() != PCM037_EET) - return 0; - - mxc_iomux_setup_multiple_pins(pcm037_eet_pins, - ARRAY_SIZE(pcm037_eet_pins), "pcm037_eet"); - - /* SPI */ - spi_register_board_info(pcm037_spi_dev, ARRAY_SIZE(pcm037_spi_dev)); -#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE) - mxc_register_device(&mxc_spi_device0, &pcm037_spi1_master); -#endif - - platform_device_register(&pcm037_gpio_keys_device); - - return 0; -} - -late_initcall(eet_init_devices); diff --git a/arch/arm/mach-mx3/pcm043.c b/arch/arm/mach-mx3/pcm043.c deleted file mode 100644 index 1212194..0000000 --- a/arch/arm/mach-mx3/pcm043.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2009 Sascha Hauer, Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#if defined CONFIG_I2C_IMX || defined CONFIG_I2C_IMX_MODULE -#include -#endif -#include -#include -#include -#include - -#include "devices.h" - -static const struct fb_videomode fb_modedb[] = { - { - /* 240x320 @ 60 Hz */ - .name = "Sharp-LQ035Q7", - .refresh = 60, - .xres = 240, - .yres = 320, - .pixclock = 185925, - .left_margin = 9, - .right_margin = 16, - .upper_margin = 7, - .lower_margin = 9, - .hsync_len = 1, - .vsync_len = 1, - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | FB_SYNC_CLK_INVERT | FB_SYNC_CLK_IDLE_EN, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, { - /* 240x320 @ 60 Hz */ - .name = "TX090", - .refresh = 60, - .xres = 240, - .yres = 320, - .pixclock = 38255, - .left_margin = 144, - .right_margin = 0, - .upper_margin = 7, - .lower_margin = 40, - .hsync_len = 96, - .vsync_len = 1, - .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, - .vmode = FB_VMODE_NONINTERLACED, - .flag = 0, - }, -}; - -static struct ipu_platform_data mx3_ipu_data = { - .irq_base = MXC_IPU_IRQ_START, -}; - -static struct mx3fb_platform_data mx3fb_pdata = { - .dma_dev = &mx3_ipu.dev, - .name = "Sharp-LQ035Q7", - .mode = fb_modedb, - .num_modes = ARRAY_SIZE(fb_modedb), -}; - -static struct physmap_flash_data pcm043_flash_data = { - .width = 2, -}; - -static struct resource pcm043_flash_resource = { - .start = 0xa0000000, - .end = 0xa1ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device pcm043_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &pcm043_flash_data, - }, - .resource = &pcm043_flash_resource, - .num_resources = 1, -}; - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -#if defined CONFIG_I2C_IMX || defined CONFIG_I2C_IMX_MODULE -static struct imxi2c_platform_data pcm043_i2c_1_data = { - .bitrate = 50000, -}; - -static struct at24_platform_data board_eeprom = { - .byte_len = 4096, - .page_size = 32, - .flags = AT24_FLAG_ADDR16, -}; - -static struct i2c_board_info pcm043_i2c_devices[] = { - { - I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */ - .platform_data = &board_eeprom, - }, { - I2C_BOARD_INFO("pcf8563", 0x51), - } -}; -#endif - -static struct platform_device *devices[] __initdata = { - &pcm043_flash, - &mxc_fec_device, -}; - -static struct pad_desc pcm043_pads[] = { - /* UART1 */ - MX35_PAD_CTS1__UART1_CTS, - MX35_PAD_RTS1__UART1_RTS, - MX35_PAD_TXD1__UART1_TXD_MUX, - MX35_PAD_RXD1__UART1_RXD_MUX, - /* UART2 */ - MX35_PAD_CTS2__UART2_CTS, - MX35_PAD_RTS2__UART2_RTS, - MX35_PAD_TXD2__UART2_TXD_MUX, - MX35_PAD_RXD2__UART2_RXD_MUX, - /* FEC */ - MX35_PAD_FEC_TX_CLK__FEC_TX_CLK, - MX35_PAD_FEC_RX_CLK__FEC_RX_CLK, - MX35_PAD_FEC_RX_DV__FEC_RX_DV, - MX35_PAD_FEC_COL__FEC_COL, - MX35_PAD_FEC_RDATA0__FEC_RDATA_0, - MX35_PAD_FEC_TDATA0__FEC_TDATA_0, - MX35_PAD_FEC_TX_EN__FEC_TX_EN, - MX35_PAD_FEC_MDC__FEC_MDC, - MX35_PAD_FEC_MDIO__FEC_MDIO, - MX35_PAD_FEC_TX_ERR__FEC_TX_ERR, - MX35_PAD_FEC_RX_ERR__FEC_RX_ERR, - MX35_PAD_FEC_CRS__FEC_CRS, - MX35_PAD_FEC_RDATA1__FEC_RDATA_1, - MX35_PAD_FEC_TDATA1__FEC_TDATA_1, - MX35_PAD_FEC_RDATA2__FEC_RDATA_2, - MX35_PAD_FEC_TDATA2__FEC_TDATA_2, - MX35_PAD_FEC_RDATA3__FEC_RDATA_3, - MX35_PAD_FEC_TDATA3__FEC_TDATA_3, - /* I2C1 */ - MX35_PAD_I2C1_CLK__I2C1_SCL, - MX35_PAD_I2C1_DAT__I2C1_SDA, - /* Display */ - MX35_PAD_LD0__IPU_DISPB_DAT_0, - MX35_PAD_LD1__IPU_DISPB_DAT_1, - MX35_PAD_LD2__IPU_DISPB_DAT_2, - MX35_PAD_LD3__IPU_DISPB_DAT_3, - MX35_PAD_LD4__IPU_DISPB_DAT_4, - MX35_PAD_LD5__IPU_DISPB_DAT_5, - MX35_PAD_LD6__IPU_DISPB_DAT_6, - MX35_PAD_LD7__IPU_DISPB_DAT_7, - MX35_PAD_LD8__IPU_DISPB_DAT_8, - MX35_PAD_LD9__IPU_DISPB_DAT_9, - MX35_PAD_LD10__IPU_DISPB_DAT_10, - MX35_PAD_LD11__IPU_DISPB_DAT_11, - MX35_PAD_LD12__IPU_DISPB_DAT_12, - MX35_PAD_LD13__IPU_DISPB_DAT_13, - MX35_PAD_LD14__IPU_DISPB_DAT_14, - MX35_PAD_LD15__IPU_DISPB_DAT_15, - MX35_PAD_LD16__IPU_DISPB_DAT_16, - MX35_PAD_LD17__IPU_DISPB_DAT_17, - MX35_PAD_D3_HSYNC__IPU_DISPB_D3_HSYNC, - MX35_PAD_D3_FPSHIFT__IPU_DISPB_D3_CLK, - MX35_PAD_D3_DRDY__IPU_DISPB_D3_DRDY, - MX35_PAD_CONTRAST__IPU_DISPB_CONTR, - MX35_PAD_D3_VSYNC__IPU_DISPB_D3_VSYNC, - MX35_PAD_D3_REV__IPU_DISPB_D3_REV, - MX35_PAD_D3_CLS__IPU_DISPB_D3_CLS, - /* gpio */ - MX35_PAD_ATA_CS0__GPIO2_6, -}; - -static struct mxc_nand_platform_data pcm037_nand_board_info = { - .width = 1, - .hw_ecc = 1, -}; - -/* - * Board specific initialization. - */ -static void __init mxc_board_init(void) -{ - mxc_iomux_v3_setup_multiple_pads(pcm043_pads, ARRAY_SIZE(pcm043_pads)); - - platform_add_devices(devices, ARRAY_SIZE(devices)); - - mxc_register_device(&mxc_uart_device0, &uart_pdata); - mxc_register_device(&mxc_nand_device, &pcm037_nand_board_info); - - mxc_register_device(&mxc_uart_device1, &uart_pdata); - -#if defined CONFIG_I2C_IMX || defined CONFIG_I2C_IMX_MODULE - i2c_register_board_info(0, pcm043_i2c_devices, - ARRAY_SIZE(pcm043_i2c_devices)); - - mxc_register_device(&mxc_i2c_device0, &pcm043_i2c_1_data); -#endif - - mxc_register_device(&mx3_ipu, &mx3_ipu_data); - mxc_register_device(&mx3_fb, &mx3fb_pdata); -} - -static void __init pcm043_timer_init(void) -{ - mx35_clocks_init(); -} - -struct sys_timer pcm043_timer = { - .init = pcm043_timer_init, -}; - -MACHINE_START(PCM043, "Phytec Phycore pcm043") - /* Maintainer: Pengutronix */ - .phys_io = MX35_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX35_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx35_map_io, - .init_irq = mx35_init_irq, - .init_machine = mxc_board_init, - .timer = &pcm043_timer, -MACHINE_END - diff --git a/arch/arm/mach-mx3/qong.c b/arch/arm/mach-mx3/qong.c deleted file mode 100644 index 3c1e736..0000000 --- a/arch/arm/mach-mx3/qong.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (C) 2009 Ilya Yanok, Emcraft Systems Ltd, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "devices.h" - -/* FPGA defines */ -#define QONG_FPGA_VERSION(major, minor, rev) \ - (((major & 0xF) << 12) | ((minor & 0xF) << 8) | (rev & 0xFF)) - -#define QONG_FPGA_BASEADDR MX31_CS1_BASE_ADDR -#define QONG_FPGA_PERIPH_SIZE (1 << 24) - -#define QONG_FPGA_CTRL_BASEADDR QONG_FPGA_BASEADDR -#define QONG_FPGA_CTRL_SIZE 0x10 -/* FPGA control registers */ -#define QONG_FPGA_CTRL_VERSION 0x00 - -#define QONG_DNET_ID 1 -#define QONG_DNET_BASEADDR \ - (QONG_FPGA_BASEADDR + QONG_DNET_ID * QONG_FPGA_PERIPH_SIZE) -#define QONG_DNET_SIZE 0x00001000 - -#define QONG_FPGA_IRQ IOMUX_TO_IRQ(MX31_PIN_DTR_DCE1) - -/* - * This file contains the board-specific initialization routines. - */ - -static struct imxuart_platform_data uart_pdata = { - .flags = IMXUART_HAVE_RTSCTS, -}; - -static int uart_pins[] = { - MX31_PIN_CTS1__CTS1, - MX31_PIN_RTS1__RTS1, - MX31_PIN_TXD1__TXD1, - MX31_PIN_RXD1__RXD1 -}; - -static inline void mxc_init_imx_uart(void) -{ - mxc_iomux_setup_multiple_pins(uart_pins, ARRAY_SIZE(uart_pins), - "uart-0"); - mxc_register_device(&mxc_uart_device0, &uart_pdata); -} - -static struct resource dnet_resources[] = { - { - .name = "dnet-memory", - .start = QONG_DNET_BASEADDR, - .end = QONG_DNET_BASEADDR + QONG_DNET_SIZE - 1, - .flags = IORESOURCE_MEM, - }, { - .start = QONG_FPGA_IRQ, - .end = QONG_FPGA_IRQ, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device dnet_device = { - .name = "dnet", - .id = -1, - .num_resources = ARRAY_SIZE(dnet_resources), - .resource = dnet_resources, -}; - -static int __init qong_init_dnet(void) -{ - int ret; - - ret = platform_device_register(&dnet_device); - return ret; -} - -/* MTD NOR flash */ - -static struct physmap_flash_data qong_flash_data = { - .width = 2, -}; - -static struct resource qong_flash_resource = { - .start = MX31_CS0_BASE_ADDR, - .end = MX31_CS0_BASE_ADDR + QONG_NOR_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device qong_nor_mtd_device = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &qong_flash_data, - }, - .resource = &qong_flash_resource, - .num_resources = 1, -}; - -static void qong_init_nor_mtd(void) -{ - (void)platform_device_register(&qong_nor_mtd_device); -} - -/* - * Hardware specific access to control-lines - */ -static void qong_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *nand_chip = mtd->priv; - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd, nand_chip->IO_ADDR_W + (1 << 24)); - else - writeb(cmd, nand_chip->IO_ADDR_W + (1 << 23)); -} - -/* - * Read the Device Ready pin. - */ -static int qong_nand_device_ready(struct mtd_info *mtd) -{ - return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_NFRB)); -} - -static void qong_nand_select_chip(struct mtd_info *mtd, int chip) -{ - if (chip >= 0) - gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_NFCE_B), 0); - else - gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_NFCE_B), 1); -} - -static struct platform_nand_data qong_nand_data = { - .chip = { - .chip_delay = 20, - .options = 0, - }, - .ctrl = { - .cmd_ctrl = qong_nand_cmd_ctrl, - .dev_ready = qong_nand_device_ready, - .select_chip = qong_nand_select_chip, - } -}; - -static struct resource qong_nand_resource = { - .start = MX31_CS3_BASE_ADDR, - .end = MX31_CS3_BASE_ADDR + SZ_32M - 1, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device qong_nand_device = { - .name = "gen_nand", - .id = -1, - .dev = { - .platform_data = &qong_nand_data, - }, - .num_resources = 1, - .resource = &qong_nand_resource, -}; - -static void __init qong_init_nand_mtd(void) -{ - /* init CS */ - __raw_writel(0x00004f00, CSCR_U(3)); - __raw_writel(0x20013b31, CSCR_L(3)); - __raw_writel(0x00020800, CSCR_A(3)); - mxc_iomux_set_gpr(MUX_SDCTL_CSD1_SEL, true); - - /* enable pin */ - mxc_iomux_mode(IOMUX_MODE(MX31_PIN_NFCE_B, IOMUX_CONFIG_GPIO)); - if (!gpio_request(IOMUX_TO_GPIO(MX31_PIN_NFCE_B), "nand_enable")) - gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_NFCE_B), 0); - - /* ready/busy pin */ - mxc_iomux_mode(IOMUX_MODE(MX31_PIN_NFRB, IOMUX_CONFIG_GPIO)); - if (!gpio_request(IOMUX_TO_GPIO(MX31_PIN_NFRB), "nand_rdy")) - gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_NFRB)); - - /* write protect pin */ - mxc_iomux_mode(IOMUX_MODE(MX31_PIN_NFWP_B, IOMUX_CONFIG_GPIO)); - if (!gpio_request(IOMUX_TO_GPIO(MX31_PIN_NFWP_B), "nand_wp")) - gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_NFWP_B)); - - platform_device_register(&qong_nand_device); -} - -static void __init qong_init_fpga(void) -{ - void __iomem *regs; - u32 fpga_ver; - - regs = ioremap(QONG_FPGA_CTRL_BASEADDR, QONG_FPGA_CTRL_SIZE); - if (!regs) { - printk(KERN_ERR "%s: failed to map registers, aborting.\n", - __func__); - return; - } - - fpga_ver = readl(regs + QONG_FPGA_CTRL_VERSION); - iounmap(regs); - printk(KERN_INFO "Qong FPGA version %d.%d.%d\n", - (fpga_ver & 0xF000) >> 12, - (fpga_ver & 0x0F00) >> 8, fpga_ver & 0x00FF); - if (fpga_ver < QONG_FPGA_VERSION(0, 8, 7)) { - printk(KERN_ERR "qong: Unexpected FPGA version, FPGA-based " - "devices won't be registered!\n"); - return; - } - - /* register FPGA-based devices */ - qong_init_nand_mtd(); - qong_init_dnet(); -} - -/* - * Board specific initialization. - */ -static void __init mxc_board_init(void) -{ - mxc_init_imx_uart(); - qong_init_nor_mtd(); - qong_init_fpga(); -} - -static void __init qong_timer_init(void) -{ - mx31_clocks_init(26000000); -} - -static struct sys_timer qong_timer = { - .init = qong_timer_init, -}; - -/* - * The following uses standard kernel macros defined in arch.h in order to - * initialize __mach_desc_QONG data structure. - */ - -MACHINE_START(QONG, "Dave/DENX QongEVB-LITE") - /* Maintainer: DENX Software Engineering GmbH */ - .phys_io = MX31_AIPS1_BASE_ADDR, - .io_pg_offst = ((MX31_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc, - .boot_params = PHYS_OFFSET + 0x100, - .map_io = mx31_map_io, - .init_irq = mx31_init_irq, - .init_machine = mxc_board_init, - .timer = &qong_timer, -MACHINE_END -- cgit v0.10.2 From 1f2ddd64da6e53365102d831aa9e0446b78f0300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 16 Dec 2009 19:05:04 +0100 Subject: imx: provide helper macro to define IO_ADDRESS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Holger Schurig Cc: Dmitriy Taychenachev diff --git a/arch/arm/plat-mxc/include/mach/hardware.h b/arch/arm/plat-mxc/include/mach/hardware.h index 78db754..db14c56 100644 --- a/arch/arm/plat-mxc/include/mach/hardware.h +++ b/arch/arm/plat-mxc/include/mach/hardware.h @@ -22,6 +22,11 @@ #include +#define IMX_IO_ADDRESS(addr, module) \ + ((void __force __iomem *) \ + (((unsigned long)((addr) - (module ## _BASE_ADDR)) < module ## _SIZE) ?\ + (addr) - (module ## _BASE_ADDR) + (module ## _BASE_ADDR_VIRT) : 0)) + #ifdef CONFIG_ARCH_MX3 #include #include -- cgit v0.10.2 From a3f5ac7838a93977f71288310336a725eb6e62db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 16 Dec 2009 17:29:39 +0100 Subject: imx21: define and use MX21_IO_ADDRESS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Holger Schurig Cc: Rabin Vincent Cc: Agustín Ferrín Pozuelo diff --git a/arch/arm/mach-mx2/clock_imx21.c b/arch/arm/mach-mx2/clock_imx21.c index 6ab3c72..2367b6c 100644 --- a/arch/arm/mach-mx2/clock_imx21.c +++ b/arch/arm/mach-mx2/clock_imx21.c @@ -28,7 +28,7 @@ #include #include -#define IO_ADDR_CCM(off) (IO_ADDRESS(MX21_CCM_BASE_ADDR) + (off)) +#define IO_ADDR_CCM(off) (MX21_IO_ADDRESS(MX21_CCM_BASE_ADDR) + (off)) /* Register offsets */ #define CCM_CSCR IO_ADDR_CCM(0x0) @@ -1235,7 +1235,7 @@ int __init mx21_clocks_init(unsigned long lref, unsigned long href) clk_enable(&uart_clk[0]); #endif - mxc_timer_init(&gpt_clk[0], IO_ADDRESS(MX21_GPT1_BASE_ADDR), + mxc_timer_init(&gpt_clk[0], MX21_IO_ADDRESS(MX21_GPT1_BASE_ADDR), MX21_INT_GPT1); return 0; } diff --git a/arch/arm/plat-mxc/include/mach/mx21.h b/arch/arm/plat-mxc/include/mach/mx21.h index bb297d8..5b9f062 100644 --- a/arch/arm/plat-mxc/include/mach/mx21.h +++ b/arch/arm/plat-mxc/include/mach/mx21.h @@ -92,6 +92,11 @@ #define MX21_IRAM_BASE_ADDR 0xffffe800 /* internal ram */ +#define MX21_IO_ADDRESS(x) ( \ + IMX_IO_ADDRESS(x, MX21_AIPI) ?: \ + IMX_IO_ADDRESS(x, MX21_SAHB1) ?: \ + IMX_IO_ADDRESS(x, MX21_X_MEMC)) + /* fixed interrupt numbers */ #define MX21_INT_CSPI3 6 #define MX21_INT_GPIO 8 -- cgit v0.10.2 From bc9ea6c7f5352f8857525b054bc3df784be1b8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 16 Dec 2009 17:30:27 +0100 Subject: imx27: define and use MX27_IO_ADDRESS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Rabin Vincent Cc: Javier Martin Cc: Valentin Longchamp Cc: Holger Schurig diff --git a/arch/arm/mach-mx2/clock_imx27.c b/arch/arm/mach-mx2/clock_imx27.c index 6db9856..bb9f603 100644 --- a/arch/arm/mach-mx2/clock_imx27.c +++ b/arch/arm/mach-mx2/clock_imx27.c @@ -29,7 +29,7 @@ #include #include -#define IO_ADDR_CCM(off) (IO_ADDRESS(MX27_CCM_BASE_ADDR) + (off)) +#define IO_ADDR_CCM(off) (MX27_IO_ADDRESS(MX27_CCM_BASE_ADDR) + (off)) /* Register offsets */ #define CCM_CSCR IO_ADDR_CCM(0x0) @@ -757,7 +757,7 @@ int __init mx27_clocks_init(unsigned long fref) clk_enable(&uart1_clk); #endif - mxc_timer_init(&gpt1_clk, IO_ADDRESS(MX27_GPT1_BASE_ADDR), + mxc_timer_init(&gpt1_clk, MX27_IO_ADDRESS(MX27_GPT1_BASE_ADDR), MX27_INT_GPT1); return 0; diff --git a/arch/arm/mach-mx2/cpu_imx27.c b/arch/arm/mach-mx2/cpu_imx27.c index 8c50c5e..d8d3b2d 100644 --- a/arch/arm/mach-mx2/cpu_imx27.c +++ b/arch/arm/mach-mx2/cpu_imx27.c @@ -39,7 +39,8 @@ static void query_silicon_parameter(void) * the silicon revision very early we read it here to * avoid any further hooks */ - val = __raw_readl(IO_ADDRESS(MX27_SYSCTRL_BASE_ADDR) + SYS_CHIP_ID); + val = __raw_readl(MX27_IO_ADDRESS(MX27_SYSCTRL_BASE_ADDR + + SYS_CHIP_ID)); cpu_silicon_rev = (int)(val >> 28); cpu_partnumber = (int)((val >> 12) & 0xFFFF); diff --git a/arch/arm/plat-mxc/include/mach/mx27.h b/arch/arm/plat-mxc/include/mach/mx27.h index e2ae19f..cd292ff 100644 --- a/arch/arm/plat-mxc/include/mach/mx27.h +++ b/arch/arm/plat-mxc/include/mach/mx27.h @@ -114,6 +114,11 @@ /* IRAM */ #define MX27_IRAM_BASE_ADDR 0xffff4c00 /* internal ram */ +#define MX27_IO_ADDRESS(x) ( \ + IMX_IO_ADDRESS(x, MX27_AIPI) ?: \ + IMX_IO_ADDRESS(x, MX27_SAHB1) ?: \ + IMX_IO_ADDRESS(x, MX27_X_MEMC)) + /* fixed interrupt numbers */ #define MX27_INT_I2C2 1 #define MX27_INT_GPT6 2 -- cgit v0.10.2 From 1273e7686f0c122bf58592c57387471c7f4eaa1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 16 Dec 2009 19:06:12 +0100 Subject: imx31: define and use MX31_IO_ADDRESS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Daniel Mack Cc: Greg Kroah-Hartman Cc: Mark Brown Cc: Magnus Lilja diff --git a/arch/arm/mach-mx3/clock-imx31.c b/arch/arm/mach-mx3/clock-imx31.c index cc03a61..d22a66f 100644 --- a/arch/arm/mach-mx3/clock-imx31.c +++ b/arch/arm/mach-mx3/clock-imx31.c @@ -625,7 +625,8 @@ int __init mx31_clocks_init(unsigned long fref) __raw_writel(reg, MXC_CCM_PMCR1); } - mxc_timer_init(&ipg_clk, IO_ADDRESS(MX31_GPT1_BASE_ADDR), MX31_INT_GPT); + mxc_timer_init(&ipg_clk, MX31_IO_ADDRESS(MX31_GPT1_BASE_ADDR), + MX31_INT_GPT); return 0; } diff --git a/arch/arm/mach-mx3/crm_regs.h b/arch/arm/mach-mx3/crm_regs.h index e25cd92..37a8a07 100644 --- a/arch/arm/mach-mx3/crm_regs.h +++ b/arch/arm/mach-mx3/crm_regs.h @@ -24,7 +24,7 @@ #define CKIH_CLK_FREQ_27MHZ 27000000 #define CKIL_CLK_FREQ 32768 -#define MXC_CCM_BASE IO_ADDRESS(MX31_CCM_BASE_ADDR) +#define MXC_CCM_BASE MX31_IO_ADDRESS(MX31_CCM_BASE_ADDR) /* Register addresses */ #define MXC_CCM_CCMR (MXC_CCM_BASE + 0x00) diff --git a/arch/arm/mach-mx3/iomux-imx31.c b/arch/arm/mach-mx3/iomux-imx31.c index 6381e56..a1d7fa5 100644 --- a/arch/arm/mach-mx3/iomux-imx31.c +++ b/arch/arm/mach-mx3/iomux-imx31.c @@ -29,7 +29,7 @@ /* * IOMUX register (base) addresses */ -#define IOMUX_BASE IO_ADDRESS(MX31_IOMUXC_BASE_ADDR) +#define IOMUX_BASE MX31_IO_ADDRESS(MX31_IOMUXC_BASE_ADDR) #define IOMUXINT_OBS1 (IOMUX_BASE + 0x000) #define IOMUXINT_OBS2 (IOMUX_BASE + 0x004) #define IOMUXGPR (IOMUX_BASE + 0x008) diff --git a/arch/arm/plat-mxc/ehci.c b/arch/arm/plat-mxc/ehci.c index 41599be..8df03f3 100644 --- a/arch/arm/plat-mxc/ehci.c +++ b/arch/arm/plat-mxc/ehci.c @@ -43,7 +43,7 @@ int mxc_set_usbcontrol(int port, unsigned int flags) unsigned int v; if (cpu_is_mx31()) { - v = readl(IO_ADDRESS(MX31_OTG_BASE_ADDR + + v = readl(MX31_IO_ADDRESS(MX31_OTG_BASE_ADDR + USBCTRL_OTGBASE_OFFSET)); switch (port) { @@ -79,7 +79,7 @@ int mxc_set_usbcontrol(int port, unsigned int flags) break; } - writel(v, IO_ADDRESS(MX31_OTG_BASE_ADDR + + writel(v, MX31_IO_ADDRESS(MX31_OTG_BASE_ADDR + USBCTRL_OTGBASE_OFFSET)); return 0; } diff --git a/arch/arm/plat-mxc/include/mach/board-mx31ads.h b/arch/arm/plat-mxc/include/mach/board-mx31ads.h index 2cbfa35..095a199 100644 --- a/arch/arm/plat-mxc/include/mach/board-mx31ads.h +++ b/arch/arm/plat-mxc/include/mach/board-mx31ads.h @@ -14,7 +14,7 @@ #include /* Base address of PBC controller */ -#define PBC_BASE_ADDRESS IO_ADDRESS(CS4_BASE_ADDR) +#define PBC_BASE_ADDRESS MX31_CS4_BASE_ADDR_VIRT /* Offsets for the PBC Controller register */ /* PBC Board status register offset */ diff --git a/arch/arm/plat-mxc/include/mach/mx31.h b/arch/arm/plat-mxc/include/mach/mx31.h index b8b47d1..0c005af 100644 --- a/arch/arm/plat-mxc/include/mach/mx31.h +++ b/arch/arm/plat-mxc/include/mach/mx31.h @@ -109,6 +109,13 @@ #define MX31_PCMCIA_MEM_BASE_ADDR 0xbc000000 +#define MX31_IO_ADDRESS(x) ( \ + IMX_IO_ADDRESS(x, MX31_AIPS1) ?: \ + IMX_IO_ADDRESS(x, MX31_AIPS2) ?: \ + IMX_IO_ADDRESS(x, MX31_AVIC) ?: \ + IMX_IO_ADDRESS(x, MX31_X_MEMC) ?: \ + IMX_IO_ADDRESS(x, MX31_SPBA0)) + #define MX31_INT_I2C3 3 #define MX31_INT_I2C2 4 #define MX31_INT_MPEG4_ENCODER 5 -- cgit v0.10.2 From df9375fad4f99b28f5a500d9aa2cbb04e065ac97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 16 Dec 2009 19:07:04 +0100 Subject: imx25: redefine MX25_IO_ADDRESS using IMX_IO_ADDRESS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h index 854e2dc..7a8982d 100644 --- a/arch/arm/plat-mxc/include/mach/mx25.h +++ b/arch/arm/plat-mxc/include/mach/mx25.h @@ -22,21 +22,10 @@ #define MX25_GPIO3_BASE_ADDR_VIRT (MX25_AIPS2_BASE_ADDR_VIRT + 0xa4000) #define MX25_GPIO4_BASE_ADDR_VIRT (MX25_AIPS2_BASE_ADDR_VIRT + 0x9c000) -#define MX25_AIPS1_IO_ADDRESS(x) \ - (((x) - MX25_AIPS1_BASE_ADDR) + MX25_AIPS1_BASE_ADDR_VIRT) -#define MX25_AIPS2_IO_ADDRESS(x) \ - (((x) - MX25_AIPS2_BASE_ADDR) + MX25_AIPS2_BASE_ADDR_VIRT) -#define MX25_AVIC_IO_ADDRESS(x) \ - (((x) - MX25_AVIC_BASE_ADDR) + MX25_AVIC_BASE_ADDR_VIRT) - -#define __in_range(addr, name) ((addr) >= name##_BASE_ADDR && (addr) < name##_BASE_ADDR + name##_SIZE) - -#define MX25_IO_ADDRESS(x) \ - (void __force __iomem *) \ - (__in_range(x, MX25_AIPS1) ? MX25_AIPS1_IO_ADDRESS(x) : \ - __in_range(x, MX25_AIPS2) ? MX25_AIPS2_IO_ADDRESS(x) : \ - __in_range(x, MX25_AVIC) ? MX25_AVIC_IO_ADDRESS(x) : \ - 0xDEADBEEF) +#define MX25_IO_ADDRESS(x) ( \ + IMX_IO_ADDRESS(x, MX25_AIPS1) ?: \ + IMX_IO_ADDRESS(x, MX25_AIPS2) ?: \ + IMX_IO_ADDRESS(x, MX25_AVIC)) #define UART1_BASE_ADDR 0x43f90000 #define UART2_BASE_ADDR 0x43f94000 -- cgit v0.10.2 From 6ef9af6871e3f220c6c7facf90babe6931d972bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 16 Dec 2009 19:07:20 +0100 Subject: imx35: define and use MX35_IO_ADDRESS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Jürgen Beisert Cc: Rabin Vincent Cc: Valentin Longchamp diff --git a/arch/arm/mach-mx3/clock-imx35.c b/arch/arm/mach-mx3/clock-imx35.c index e65e862..07d630e 100644 --- a/arch/arm/mach-mx3/clock-imx35.c +++ b/arch/arm/mach-mx3/clock-imx35.c @@ -28,7 +28,7 @@ #include #include -#define CCM_BASE IO_ADDRESS(MX35_CCM_BASE_ADDR) +#define CCM_BASE MX35_IO_ADDRESS(MX35_CCM_BASE_ADDR) #define CCM_CCMR 0x00 #define CCM_PDR0 0x04 @@ -504,7 +504,8 @@ int __init mx35_clocks_init() __raw_writel((3 << 26) | ll, CCM_BASE + CCM_CGR2); __raw_writel(0, CCM_BASE + CCM_CGR3); - mxc_timer_init(&gpt_clk, IO_ADDRESS(MX35_GPT1_BASE_ADDR), MX35_INT_GPT); + mxc_timer_init(&gpt_clk, + MX35_IO_ADDRESS(MX35_GPT1_BASE_ADDR), MX35_INT_GPT); return 0; } diff --git a/arch/arm/plat-mxc/include/mach/mx35.h b/arch/arm/plat-mxc/include/mach/mx35.h index af871bc..0e453b3 100644 --- a/arch/arm/plat-mxc/include/mach/mx35.h +++ b/arch/arm/plat-mxc/include/mach/mx35.h @@ -104,6 +104,13 @@ #define MX35_NFC_BASE_ADDR 0xbb000000 #define MX35_PCMCIA_MEM_BASE_ADDR 0xbc000000 +#define MX35_IO_ADDRESS(x) ( \ + IMX_IO_ADDRESS(x, MX35_AIPS1) ?: \ + IMX_IO_ADDRESS(x, MX35_AIPS2) ?: \ + IMX_IO_ADDRESS(x, MX35_AVIC) ?: \ + IMX_IO_ADDRESS(x, MX35_X_MEMC) ?: \ + IMX_IO_ADDRESS(x, MX35_SPBA0)) + /* * Interrupt numbers */ -- cgit v0.10.2 From 59e2608169bf4199f9f9e08a8abe80132e9416f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 16 Dec 2009 19:08:09 +0100 Subject: mx3/kzm_arm11_01: define and use board specific IO_ADDRESS macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King diff --git a/arch/arm/mach-mx3/mach-kzm_arm11_01.c b/arch/arm/mach-mx3/mach-kzm_arm11_01.c index 2850b0b..2484ddd 100644 --- a/arch/arm/mach-mx3/mach-kzm_arm11_01.c +++ b/arch/arm/mach-mx3/mach-kzm_arm11_01.c @@ -46,13 +46,18 @@ #include "devices.h" +#define KZM_ARM11_IO_ADDRESS(x) ( \ + IMX_IO_ADDRESS(x, MX31_CS4) ?: \ + IMX_IO_ADDRESS(x, MX31_CS5) ?: \ + MX31_IO_ADDRESS(x)) + #if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) /* * KZM-ARM11-01 has an external UART on FPGA */ static struct plat_serial8250_port serial_platform_data[] = { { - .membase = IO_ADDRESS(KZM_ARM11_16550), + .membase = KZM_ARM11_IO_ADDRESS(KZM_ARM11_16550), .mapbase = KZM_ARM11_16550, .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_1), .irqflags = IRQ_TYPE_EDGE_RISING, @@ -102,9 +107,9 @@ static int __init kzm_init_ext_uart(void) /* * Unmask UART interrupt */ - tmp = __raw_readb(IO_ADDRESS(KZM_ARM11_CTL1)); + tmp = __raw_readb(KZM_ARM11_IO_ADDRESS(KZM_ARM11_CTL1)); tmp |= 0x2; - __raw_writeb(tmp, IO_ADDRESS(KZM_ARM11_CTL1)); + __raw_writeb(tmp, KZM_ARM11_IO_ADDRESS(KZM_ARM11_CTL1)); return platform_device_register(&serial_device); } -- cgit v0.10.2 From a9b7a2dd217bd43e122b604ec99b63e0211c38aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 17 Dec 2009 11:56:43 +0100 Subject: imx: mangle addresses after adding the offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit don't use IO_ADDRESS($base) + $offset but IO_ADDRESS($base + $offset) Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Holger Schurig Cc: Rabin Vincent Cc: "Agustín Ferrín Pozuelo" Cc: Javier Martin Cc: Valentin Longchamp Cc: Daniel Mack Cc: Dmitriy Taychenachev diff --git a/arch/arm/mach-mx2/clock_imx21.c b/arch/arm/mach-mx2/clock_imx21.c index 2367b6c..8974faf 100644 --- a/arch/arm/mach-mx2/clock_imx21.c +++ b/arch/arm/mach-mx2/clock_imx21.c @@ -28,7 +28,7 @@ #include #include -#define IO_ADDR_CCM(off) (MX21_IO_ADDRESS(MX21_CCM_BASE_ADDR) + (off)) +#define IO_ADDR_CCM(off) (MX21_IO_ADDRESS(MX21_CCM_BASE_ADDR + (off))) /* Register offsets */ #define CCM_CSCR IO_ADDR_CCM(0x0) diff --git a/arch/arm/mach-mx2/clock_imx27.c b/arch/arm/mach-mx2/clock_imx27.c index bb9f603..68bf93e 100644 --- a/arch/arm/mach-mx2/clock_imx27.c +++ b/arch/arm/mach-mx2/clock_imx27.c @@ -29,7 +29,7 @@ #include #include -#define IO_ADDR_CCM(off) (MX27_IO_ADDRESS(MX27_CCM_BASE_ADDR) + (off)) +#define IO_ADDR_CCM(off) (MX27_IO_ADDRESS(MX27_CCM_BASE_ADDR + (off))) /* Register offsets */ #define CCM_CSCR IO_ADDR_CCM(0x0) diff --git a/arch/arm/mach-mx3/cpu.c b/arch/arm/mach-mx3/cpu.c index db82880..861afe0 100644 --- a/arch/arm/mach-mx3/cpu.c +++ b/arch/arm/mach-mx3/cpu.c @@ -41,7 +41,7 @@ void __init mx31_read_cpu_rev(void) u32 i, srev; /* read SREV register from IIM module */ - srev = __raw_readl(IO_ADDRESS(IIM_BASE_ADDR) + MXC_IIMSREV); + srev = __raw_readl(IO_ADDRESS(IIM_BASE_ADDR + MXC_IIMSREV)); for (i = 0; i < ARRAY_SIZE(mx31_cpu_type); i++) if (srev == mx31_cpu_type[i].srev) { diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h index 5199053..2b14126 100644 --- a/arch/arm/plat-mxc/include/mach/mxc.h +++ b/arch/arm/plat-mxc/include/mach/mxc.h @@ -121,9 +121,9 @@ extern unsigned int __mxc_cpu_type; #endif #if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) -#define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR) + n * 0x10) -#define CSCR_L(n) (IO_ADDRESS(WEIM_BASE_ADDR) + n * 0x10 + 0x4) -#define CSCR_A(n) (IO_ADDRESS(WEIM_BASE_ADDR) + n * 0x10 + 0x8) +#define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) +#define CSCR_L(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10 + 0x4)) +#define CSCR_A(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10 + 0x8)) #endif #define cpu_is_mx3() (cpu_is_mx31() || cpu_is_mx35() || cpu_is_mxc91231()) -- cgit v0.10.2 From aae7019382896cf1075a93acc564e42601bc44a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 17 Dec 2009 17:17:54 +0100 Subject: imx: only define deprecated symbols conditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define deprecated symbols in an #ifdef IMX_NEEDS_DEPRECATED_SYMBOLS ... All files that still depend on the old definitions get -DIMX_NEEDS_DEPRECATED_SYMBOLS passed to the compiler. When all remaining users are fixed this allows including the soc specific headers unconditionally. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Eric Benard Cc: Fabio Estevam Cc: Alan Carvalho de Assis Cc: Daniel Mack Cc: Alberto Panizzo Cc: Ilya Yanok Cc: Holger Schurig Cc: Jiri Kosina Cc: Magnus Lilja diff --git a/arch/arm/mach-mx2/Makefile b/arch/arm/mach-mx2/Makefile index 3710893..a9c94e3 100644 --- a/arch/arm/mach-mx2/Makefile +++ b/arch/arm/mach-mx2/Makefile @@ -5,6 +5,9 @@ # Object file lists. obj-y := generic.o devices.o serial.o +CFLAGS_generic.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS +CFLAGS_devices.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS +CFLAGS_serial.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS obj-$(CONFIG_MACH_MX21) += clock_imx21.o diff --git a/arch/arm/mach-mx3/Makefile b/arch/arm/mach-mx3/Makefile index 670fbb5..62b6093 100644 --- a/arch/arm/mach-mx3/Makefile +++ b/arch/arm/mach-mx3/Makefile @@ -5,6 +5,9 @@ # Object file lists. obj-y := mm.o devices.o cpu.o +CFLAGS_mm.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS +CFLAGS_devices.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS +CFLAGS_cpu.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS obj-$(CONFIG_ARCH_MX31) += clock-imx31.o iomux-imx31.o obj-$(CONFIG_ARCH_MX35) += clock-imx35.o obj-$(CONFIG_MACH_MX31ADS) += mach-mx31ads.o @@ -13,6 +16,7 @@ obj-$(CONFIG_MACH_MX31LITE) += mach-mx31lite.o mx31lite-db.o obj-$(CONFIG_MACH_PCM037) += mach-pcm037.o obj-$(CONFIG_MACH_PCM037_EET) += mach-pcm037_eet.o obj-$(CONFIG_MACH_MX31_3DS) += mach-mx31_3ds.o +CFLAGS_mach-mx31_3ds.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS obj-$(CONFIG_MACH_MX31MOBOARD) += mach-mx31moboard.o mx31moboard-devboard.o \ mx31moboard-marxbot.o obj-$(CONFIG_MACH_QONG) += mach-qong.o diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile index 996cbac..7322bca 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile @@ -7,9 +7,13 @@ obj-y := irq.o clock.o gpio.o time.o devices.o cpu.o system.o obj-$(CONFIG_ARCH_MX1) += iomux-mx1-mx2.o dma-mx1-mx2.o obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o dma-mx1-mx2.o +CFLAGS_iomux-mx1-mx2.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS +CFLAGS_dma-mx1-mx2.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o obj-$(CONFIG_MXC_PWM) += pwm.o obj-$(CONFIG_USB_EHCI_MXC) += ehci.o obj-$(CONFIG_MXC_ULPI) += ulpi.o obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o +CFLAGS_audmux-v1.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o +CFLAGS_audmux-v2.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS diff --git a/arch/arm/plat-mxc/include/mach/mx21.h b/arch/arm/plat-mxc/include/mach/mx21.h index 5b9f062..5e2f16e 100644 --- a/arch/arm/plat-mxc/include/mach/mx21.h +++ b/arch/arm/plat-mxc/include/mach/mx21.h @@ -184,6 +184,7 @@ #define MX21_DMA_REQ_CSI_STAT 30 #define MX21_DMA_REQ_CSI_RX 31 +#ifdef IMX_NEEDS_DEPRECATED_SYMBOLS /* these should go away */ #define SDRAM_BASE_ADDR MX21_SDRAM_BASE_ADDR #define CSD1_BASE_ADDR MX21_CSD1_BASE_ADDR @@ -216,5 +217,6 @@ #define DMA_REQ_FIRI_RX MX21_DMA_REQ_FIRI_RX #define DMA_REQ_BMI_TX MX21_DMA_REQ_BMI_TX #define DMA_REQ_BMI_RX MX21_DMA_REQ_BMI_RX +#endif #endif /* __ASM_ARCH_MXC_MX21_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx27.h b/arch/arm/plat-mxc/include/mach/mx27.h index cd292ff..62746c0 100644 --- a/arch/arm/plat-mxc/include/mach/mx27.h +++ b/arch/arm/plat-mxc/include/mach/mx27.h @@ -230,6 +230,7 @@ extern int mx27_revision(void); #endif +#ifdef IMX_NEEDS_DEPRECATED_SYMBOLS /* these should go away */ #define MSHC_BASE_ADDR MX27_MSHC_BASE_ADDR #define GPT5_BASE_ADDR MX27_GPT5_BASE_ADDR @@ -297,5 +298,6 @@ extern int mx27_revision(void); #define DMA_REQ_UART6_RX MX27_DMA_REQ_UART6_RX #define DMA_REQ_SDHC3 MX27_DMA_REQ_SDHC3 #define DMA_REQ_NFC MX27_DMA_REQ_NFC +#endif #endif /* __ASM_ARCH_MXC_MX27_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx2x.h b/arch/arm/plat-mxc/include/mach/mx2x.h index f2eaf14..396470e 100644 --- a/arch/arm/plat-mxc/include/mach/mx2x.h +++ b/arch/arm/plat-mxc/include/mach/mx2x.h @@ -176,6 +176,7 @@ #define MX2x_DMA_REQ_CSI_STAT 30 #define MX2x_DMA_REQ_CSI_RX 31 +#ifdef IMX_NEEDS_DEPRECATED_SYMBOLS /* these should go away */ #define AIPI_BASE_ADDR MX2x_AIPI_BASE_ADDR #define AIPI_BASE_ADDR_VIRT MX2x_AIPI_BASE_ADDR_VIRT @@ -287,5 +288,6 @@ #define DMA_REQ_UART1_TX MX2x_DMA_REQ_UART1_TX #define DMA_REQ_CSI_STAT MX2x_DMA_REQ_CSI_STAT #define DMA_REQ_CSI_RX MX2x_DMA_REQ_CSI_RX +#endif #endif /* __ASM_ARCH_MXC_MX2x_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx31.h b/arch/arm/plat-mxc/include/mach/mx31.h index 0c005af..53da4c5 100644 --- a/arch/arm/plat-mxc/include/mach/mx31.h +++ b/arch/arm/plat-mxc/include/mach/mx31.h @@ -193,6 +193,7 @@ #define MX31_SYSTEM_REV_MIN MX31_CHIP_REV_1_0 #define MX31_SYSTEM_REV_NUM 3 +#ifdef IMX_NEEDS_DEPRECATED_SYMBOLS /* these should go away */ #define ATA_BASE_ADDR MX31_ATA_BASE_ADDR #define UART4_BASE_ADDR MX31_UART4_BASE_ADDR @@ -223,3 +224,4 @@ #define MXC_INT_UART5 MX31_INT_UART5 #define MXC_INT_CCM MX31_INT_CCM #define MXC_INT_PCMCIA MX31_INT_PCMCIA +#endif diff --git a/arch/arm/plat-mxc/include/mach/mx35.h b/arch/arm/plat-mxc/include/mach/mx35.h index 0e453b3..ff2d2f0 100644 --- a/arch/arm/plat-mxc/include/mach/mx35.h +++ b/arch/arm/plat-mxc/include/mach/mx35.h @@ -187,6 +187,7 @@ #define MX35_SYSTEM_REV_MIN MX35_CHIP_REV_1_0 #define MX35_SYSTEM_REV_NUM 3 +#ifdef IMX_NEEDS_DEPRECATED_SYMBOLS /* these should go away */ #define MXC_FEC_BASE_ADDR MX35_FEC_BASE_ADDR #define MXC_INT_OWIRE MX35_INT_OWIRE @@ -202,3 +203,4 @@ #define MXC_INT_MLB MX35_INT_MLB #define MXC_INT_SPDIF MX35_INT_SPDIF #define MXC_INT_FEC MX35_INT_FEC +#endif diff --git a/arch/arm/plat-mxc/include/mach/mx3x.h b/arch/arm/plat-mxc/include/mach/mx3x.h index be69272..5ae0b00 100644 --- a/arch/arm/plat-mxc/include/mach/mx3x.h +++ b/arch/arm/plat-mxc/include/mach/mx3x.h @@ -269,6 +269,7 @@ static inline int mx31_revision(void) } #endif +#ifdef IMX_NEEDS_DEPRECATED_SYMBOLS /* these should go away */ #define L2CC_BASE_ADDR MX3x_L2CC_BASE_ADDR #define L2CC_SIZE MX3x_L2CC_SIZE @@ -401,5 +402,6 @@ static inline int mx31_revision(void) #define CHIP_REV_3_2 MX3x_CHIP_REV_3_2 #define SYSTEM_REV_MIN MX3x_SYSTEM_REV_MIN #define SYSTEM_REV_NUM MX3x_SYSTEM_REV_NUM +#endif #endif /* __ASM_ARCH_MXC_MX31_H__ */ -- cgit v0.10.2 From d13ab097ae9671be6765c7a8f1946641db5a7dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 5 Jan 2010 11:27:42 +0100 Subject: imx: remove mx1ads defconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some time now the defconfig for mx1ads produces a kernel for a versatile board. As CONFIG_ARCH_MX1ADS is included in the mx1 defconfig just get rid of mx1ads_defconfig. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer diff --git a/arch/arm/configs/mx1ads_defconfig b/arch/arm/configs/mx1ads_defconfig deleted file mode 100644 index 3cabbb6..0000000 --- a/arch/arm/configs/mx1ads_defconfig +++ /dev/null @@ -1,742 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.12-rc1-bk2 -# Sun Mar 27 02:15:46 2005 -# -CONFIG_ARM=y -CONFIG_MMU=y -CONFIG_UID16=y -CONFIG_RWSEM_GENERIC_SPINLOCK=y -CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_GENERIC_IOMAP=y - -# -# Code maturity level options -# -CONFIG_EXPERIMENTAL=y -CONFIG_CLEAN_COMPILE=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_LOCK_KERNEL=y - -# -# General setup -# -CONFIG_LOCALVERSION="" -CONFIG_SWAP=y -CONFIG_SYSVIPC=y -# CONFIG_POSIX_MQUEUE is not set -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_SYSCTL is not set -# CONFIG_AUDIT is not set -# CONFIG_HOTPLUG is not set -CONFIG_KOBJECT_UEVENT=y -# CONFIG_IKCONFIG is not set -CONFIG_EMBEDDED=y -# CONFIG_KALLSYMS is not set -CONFIG_BASE_FULL=y -CONFIG_FUTEX=y -CONFIG_EPOLL=y -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_SHMEM=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 -# CONFIG_TINY_SHMEM is not set -CONFIG_BASE_SMALL=0 - -# -# Loadable module support -# -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -# CONFIG_MODULE_FORCE_UNLOAD is not set -CONFIG_OBSOLETE_MODPARM=y -# CONFIG_MODVERSIONS is not set -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y - -# -# System Type -# -# CONFIG_ARCH_CLPS7500 is not set -# CONFIG_ARCH_CLPS711X is not set -# CONFIG_ARCH_CO285 is not set -# CONFIG_ARCH_EBSA110 is not set -# CONFIG_ARCH_FOOTBRIDGE is not set -# CONFIG_ARCH_INTEGRATOR is not set -# CONFIG_ARCH_IOP3XX is not set -# CONFIG_ARCH_IXP4XX is not set -# CONFIG_ARCH_IXP2000 is not set -# CONFIG_ARCH_L7200 is not set -# CONFIG_ARCH_PXA is not set -# CONFIG_ARCH_RPC is not set -# CONFIG_ARCH_SA1100 is not set -# CONFIG_ARCH_S3C2410 is not set -# CONFIG_ARCH_SHARK is not set -# CONFIG_ARCH_LH7A40X is not set -# CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_VERSATILE is not set -CONFIG_ARCH_IMX=y -# CONFIG_ARCH_H720X is not set - -# -# IMX Implementations -# -CONFIG_ARCH_MX1ADS=y - -# -# Processor Type -# -CONFIG_CPU_ARM920T=y -CONFIG_CPU_32v4=y -CONFIG_CPU_ABRT_EV4T=y -CONFIG_CPU_CACHE_V4WT=y -CONFIG_CPU_CACHE_VIVT=y -CONFIG_CPU_COPY_V4WB=y -CONFIG_CPU_TLB_V4WBI=y - -# -# Processor Features -# -# CONFIG_ARM_THUMB is not set -# CONFIG_CPU_ICACHE_DISABLE is not set -# CONFIG_CPU_DCACHE_DISABLE is not set -# CONFIG_CPU_DCACHE_WRITETHROUGH is not set - -# -# Bus support -# -CONFIG_ISA=y - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# Kernel Features -# -CONFIG_PREEMPT=y -# CONFIG_LEDS is not set -CONFIG_ALIGNMENT_TRAP=y - -# -# Boot options -# -CONFIG_ZBOOT_ROM_TEXT=0x0 -CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="console=ttySMX0,57600n8 ip=bootp root=/dev/nfs" -# CONFIG_XIP_KERNEL is not set - -# -# Floating point emulation -# - -# -# At least one emulation must be selected -# -CONFIG_FPE_NWFPE=y -CONFIG_FPE_NWFPE_XP=y -CONFIG_FPE_FASTFPE=y - -# -# Userspace binary formats -# -CONFIG_BINFMT_ELF=y -# CONFIG_BINFMT_AOUT is not set -# CONFIG_BINFMT_MISC is not set -# CONFIG_ARTHUR is not set - -# -# Power management options -# -# CONFIG_PM is not set - -# -# Device Drivers -# - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_DEBUG_DRIVER is not set - -# -# Memory Technology Devices (MTD) -# -CONFIG_MTD=y -# CONFIG_MTD_DEBUG is not set -# CONFIG_MTD_CONCAT is not set -CONFIG_MTD_PARTITIONS=y -# CONFIG_MTD_REDBOOT_PARTS is not set -# CONFIG_MTD_CMDLINE_PARTS is not set -# CONFIG_MTD_AFS_PARTS is not set - -# -# User Modules And Translation Layers -# -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=y -# CONFIG_FTL is not set -# CONFIG_NFTL is not set -# CONFIG_INFTL is not set - -# -# RAM/ROM/Flash chip drivers -# -# CONFIG_MTD_CFI is not set -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_MAP_BANK_WIDTH_1=y -CONFIG_MTD_MAP_BANK_WIDTH_2=y -CONFIG_MTD_MAP_BANK_WIDTH_4=y -# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set -CONFIG_MTD_CFI_I1=y -CONFIG_MTD_CFI_I2=y -# CONFIG_MTD_CFI_I4 is not set -# CONFIG_MTD_CFI_I8 is not set -# CONFIG_MTD_RAM is not set -CONFIG_MTD_ROM=y -# CONFIG_MTD_ABSENT is not set - -# -# Mapping drivers for chip access -# -# CONFIG_MTD_COMPLEX_MAPPINGS is not set - -# -# Self-contained MTD device drivers -# -# CONFIG_MTD_SLRAM is not set -# CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLKMTD is not set -# CONFIG_MTD_BLOCK2MTD is not set - -# -# Disk-On-Chip Device Drivers -# -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001 is not set -# CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# -# CONFIG_MTD_NAND is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Plug and Play support -# -# CONFIG_PNP is not set - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -CONFIG_BLK_DEV_LOOP=y -# CONFIG_BLK_DEV_CRYPTOLOOP is not set -# CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_RAM is not set -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y -# CONFIG_ATA_OVER_ETH is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# - -# -# IEEE 1394 (FireWire) support -# - -# -# I2O device support -# - -# -# Networking support -# -CONFIG_NET=y - -# -# Networking options -# -CONFIG_PACKET=m -CONFIG_PACKET_MMAP=y -# CONFIG_NETLINK_DEV is not set -CONFIG_UNIX=y -# CONFIG_NET_KEY is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ADVANCED_ROUTER is not set -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=y -# CONFIG_IP_PNP_RARP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set -# CONFIG_ARPD is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_TUNNEL is not set -CONFIG_IP_TCPDIAG=y -# CONFIG_IP_TCPDIAG_IPV6 is not set -# CONFIG_IPV6 is not set -# CONFIG_NETFILTER is not set - -# -# SCTP Configuration (EXPERIMENTAL) -# -# CONFIG_IP_SCTP is not set -# CONFIG_ATM is not set -# CONFIG_BRIDGE is not set -# CONFIG_VLAN_8021Q is not set -# CONFIG_DECNET is not set -# CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_NET_DIVERT is not set -# CONFIG_ECONET is not set -# CONFIG_WAN_ROUTER is not set - -# -# QoS and/or fair queueing -# -# CONFIG_NET_SCHED is not set -# CONFIG_NET_CLS_ROUTE is not set - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set -# CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set -# CONFIG_BT is not set -CONFIG_NETDEVICES=y -# CONFIG_DUMMY is not set -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -# CONFIG_TUN is not set - -# -# ARCnet devices -# -# CONFIG_ARCNET is not set - -# -# Ethernet (10 or 100Mbit) -# -CONFIG_NET_ETHERNET=y -CONFIG_MII=y -# CONFIG_NET_VENDOR_3COM is not set -# CONFIG_LANCE is not set -# CONFIG_NET_VENDOR_SMC is not set -# CONFIG_SMC91X is not set -# CONFIG_NET_VENDOR_RACAL is not set -# CONFIG_AT1700 is not set -# CONFIG_DEPCA is not set -# CONFIG_HP100 is not set -# CONFIG_NET_ISA is not set -# CONFIG_NET_PCI is not set -# CONFIG_NET_POCKET is not set - -# -# Ethernet (1000 Mbit) -# - -# -# Ethernet (10000 Mbit) -# - -# -# Token Ring devices -# -# CONFIG_TR is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set - -# -# Wan interfaces -# -# CONFIG_WAN is not set -CONFIG_PPP=y -# CONFIG_PPP_MULTILINK is not set -CONFIG_PPP_FILTER=y -CONFIG_PPP_ASYNC=y -# CONFIG_PPP_SYNC_TTY is not set -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_BSDCOMP=y -# CONFIG_PPPOE is not set -# CONFIG_SLIP is not set -# CONFIG_SHAPER is not set -# CONFIG_NETCONSOLE is not set - -# -# ISDN subsystem -# -# CONFIG_ISDN is not set - -# -# Input device support -# -# CONFIG_INPUT is not set - -# -# Hardware I/O ports -# -# CONFIG_SERIO is not set -# CONFIG_GAMEPORT is not set -CONFIG_SOUND_GAMEPORT=y - -# -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -CONFIG_SERIAL_IMX=y -CONFIG_SERIAL_IMX_CONSOLE=y -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -CONFIG_UNIX98_PTYS=y -# CONFIG_LEGACY_PTYS is not set - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set -# CONFIG_NVRAM is not set -CONFIG_RTC=m -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_DRM is not set -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# -# CONFIG_TCG_TPM is not set - -# -# I2C support -# -# CONFIG_I2C is not set - -# -# Misc devices -# - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# -# CONFIG_DVB is not set - -# -# Graphics support -# -# CONFIG_FB is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support -# -CONFIG_USB_ARCH_HAS_HCD=y -# CONFIG_USB_ARCH_HAS_OHCI is not set -# CONFIG_USB is not set - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# MMC/SD Card support -# -# CONFIG_MMC is not set - -# -# File systems -# -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set - -# -# XFS support -# -# CONFIG_XFS_FS is not set -# CONFIG_MINIX_FS is not set -# CONFIG_ROMFS_FS is not set -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y -CONFIG_VFAT_FS=y -CONFIG_FAT_DEFAULT_CODEPAGE=437 -CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_SYSFS=y -CONFIG_DEVFS_FS=y -CONFIG_DEVFS_MOUNT=y -# CONFIG_DEVFS_DEBUG is not set -# CONFIG_DEVPTS_FS_XATTR is not set -CONFIG_TMPFS=y -# CONFIG_TMPFS_XATTR is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y - -# -# Miscellaneous filesystems -# -# CONFIG_ADFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_HFSPLUS_FS is not set -# CONFIG_BEFS_FS is not set -# CONFIG_BFS_FS is not set -# CONFIG_EFS_FS is not set -# CONFIG_JFFS_FS is not set -CONFIG_JFFS2_FS=y -CONFIG_JFFS2_FS_DEBUG=0 -# CONFIG_JFFS2_FS_NAND is not set -# CONFIG_JFFS2_FS_NOR_ECC is not set -# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set -CONFIG_JFFS2_ZLIB=y -CONFIG_JFFS2_RTIME=y -# CONFIG_JFFS2_RUBIN is not set -CONFIG_CRAMFS=y -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Network File Systems -# -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -# CONFIG_NFS_V4 is not set -# CONFIG_NFS_DIRECTIO is not set -# CONFIG_NFSD is not set -CONFIG_ROOT_NFS=y -CONFIG_LOCKD=y -CONFIG_LOCKD_V4=y -CONFIG_SUNRPC=y -# CONFIG_RPCSEC_GSS_KRB5 is not set -# CONFIG_RPCSEC_GSS_SPKM3 is not set -# CONFIG_SMB_FS is not set -# CONFIG_CIFS is not set -# CONFIG_NCP_FS is not set -# CONFIG_CODA_FS is not set -# CONFIG_AFS_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -CONFIG_NLS=y -CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set -# CONFIG_NLS_CODEPAGE_737 is not set -# CONFIG_NLS_CODEPAGE_775 is not set -# CONFIG_NLS_CODEPAGE_850 is not set -# CONFIG_NLS_CODEPAGE_852 is not set -# CONFIG_NLS_CODEPAGE_855 is not set -# CONFIG_NLS_CODEPAGE_857 is not set -# CONFIG_NLS_CODEPAGE_860 is not set -# CONFIG_NLS_CODEPAGE_861 is not set -# CONFIG_NLS_CODEPAGE_862 is not set -# CONFIG_NLS_CODEPAGE_863 is not set -# CONFIG_NLS_CODEPAGE_864 is not set -# CONFIG_NLS_CODEPAGE_865 is not set -# CONFIG_NLS_CODEPAGE_866 is not set -# CONFIG_NLS_CODEPAGE_869 is not set -# CONFIG_NLS_CODEPAGE_936 is not set -# CONFIG_NLS_CODEPAGE_950 is not set -# CONFIG_NLS_CODEPAGE_932 is not set -# CONFIG_NLS_CODEPAGE_949 is not set -# CONFIG_NLS_CODEPAGE_874 is not set -# CONFIG_NLS_ISO8859_8 is not set -# CONFIG_NLS_CODEPAGE_1250 is not set -# CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ASCII is not set -# CONFIG_NLS_ISO8859_1 is not set -# CONFIG_NLS_ISO8859_2 is not set -# CONFIG_NLS_ISO8859_3 is not set -# CONFIG_NLS_ISO8859_4 is not set -# CONFIG_NLS_ISO8859_5 is not set -# CONFIG_NLS_ISO8859_6 is not set -# CONFIG_NLS_ISO8859_7 is not set -# CONFIG_NLS_ISO8859_9 is not set -# CONFIG_NLS_ISO8859_13 is not set -# CONFIG_NLS_ISO8859_14 is not set -# CONFIG_NLS_ISO8859_15 is not set -# CONFIG_NLS_KOI8_R is not set -# CONFIG_NLS_KOI8_U is not set -# CONFIG_NLS_UTF8 is not set - -# -# Profiling support -# -# CONFIG_PROFILING is not set - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_SCHEDSTATS is not set -# CONFIG_DEBUG_SLAB is not set -CONFIG_DEBUG_PREEMPT=y -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_KOBJECT is not set -CONFIG_DEBUG_BUGVERBOSE=y -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_FS is not set -CONFIG_FRAME_POINTER=y -CONFIG_DEBUG_USER=y -CONFIG_DEBUG_ERRORS=y -# CONFIG_DEBUG_LL is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -CONFIG_CRYPTO=y -# CONFIG_CRYPTO_HMAC is not set -# CONFIG_CRYPTO_NULL is not set -# CONFIG_CRYPTO_MD4 is not set -# CONFIG_CRYPTO_MD5 is not set -# CONFIG_CRYPTO_SHA1 is not set -# CONFIG_CRYPTO_SHA256 is not set -# CONFIG_CRYPTO_SHA512 is not set -# CONFIG_CRYPTO_WP512 is not set -# CONFIG_CRYPTO_TGR192 is not set -# CONFIG_CRYPTO_DES is not set -# CONFIG_CRYPTO_BLOWFISH is not set -# CONFIG_CRYPTO_TWOFISH is not set -# CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_AES is not set -# CONFIG_CRYPTO_CAST5 is not set -# CONFIG_CRYPTO_CAST6 is not set -# CONFIG_CRYPTO_TEA is not set -# CONFIG_CRYPTO_ARC4 is not set -# CONFIG_CRYPTO_KHAZAD is not set -# CONFIG_CRYPTO_ANUBIS is not set -# CONFIG_CRYPTO_DEFLATE is not set -# CONFIG_CRYPTO_MICHAEL_MIC is not set -# CONFIG_CRYPTO_CRC32C is not set -# CONFIG_CRYPTO_TEST is not set - -# -# Hardware crypto devices -# - -# -# Library routines -# -CONFIG_CRC_CCITT=y -CONFIG_CRC32=y -# CONFIG_LIBCRC32C is not set -CONFIG_ZLIB_INFLATE=y -CONFIG_ZLIB_DEFLATE=y -- cgit v0.10.2 From 4631166124d928d3e9372a57b1477ce6335332c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 8 Jan 2010 12:29:11 +0100 Subject: imx: remove unneeded include of mach/hardware.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This include isn't needed and hurts when defining static inline functions in mach/hardware.h (or files included by mach/hardware.h). The reason is that the consumers of mach/uncompress.h are compiled using -Dstatic= so the code for all inline function is included in e.g. arch/arm/boot/compressed/misc.o. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Jörg Knobloch Cc: Alan Carvalho de Assis Cc: Daniel Mack Cc: Dmitriy Taychenachev diff --git a/arch/arm/plat-mxc/include/mach/uncompress.h b/arch/arm/plat-mxc/include/mach/uncompress.h index 4d5d395..d189f00 100644 --- a/arch/arm/plat-mxc/include/mach/uncompress.h +++ b/arch/arm/plat-mxc/include/mach/uncompress.h @@ -1,8 +1,6 @@ /* * arch/arm/plat-mxc/include/mach/uncompress.h * - * - * * Copyright (C) 1999 ARM Limited * Copyright (C) Shane Nay (shane@minirl.com) * @@ -25,7 +23,6 @@ #define __MXC_BOOT_UNCOMPRESS -#include #include static unsigned long uart_base; -- cgit v0.10.2 From 3cdd54417d0f821825a353f7273d356399112f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 8 Jan 2010 16:02:30 +0100 Subject: imx: properly protect mach/mx{1,[25][157x]}.h from multiple inclusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some headers have used (now) wrong names or havn't had protection at all. This is needed before adding static inline functions to the headers. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Magnus Lilja Cc: Holger Schurig Cc: Jiri Kosina Cc: Daniel Mack diff --git a/arch/arm/plat-mxc/include/mach/mx1.h b/arch/arm/plat-mxc/include/mach/mx1.h index 1b2890a..b652a9c25 100644 --- a/arch/arm/plat-mxc/include/mach/mx1.h +++ b/arch/arm/plat-mxc/include/mach/mx1.h @@ -9,8 +9,8 @@ * published by the Free Software Foundation. */ -#ifndef __ASM_ARCH_MXC_MX1_H__ -#define __ASM_ARCH_MXC_MX1_H__ +#ifndef __MACH_MX1_H__ +#define __MACH_MX1_H__ #include @@ -161,4 +161,4 @@ #define DMA_REQ_UART1_T 30 #define DMA_REQ_UART1_R 31 -#endif /* __ASM_ARCH_MXC_MX1_H__ */ +#endif /* ifndef __MACH_MX1_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx21.h b/arch/arm/plat-mxc/include/mach/mx21.h index 5e2f16e..ed98b9c 100644 --- a/arch/arm/plat-mxc/include/mach/mx21.h +++ b/arch/arm/plat-mxc/include/mach/mx21.h @@ -22,8 +22,8 @@ * MA 02110-1301, USA. */ -#ifndef __ASM_ARCH_MXC_MX21_H__ -#define __ASM_ARCH_MXC_MX21_H__ +#ifndef __MACH_MX21_H__ +#define __MACH_MX21_H__ #define MX21_AIPI_BASE_ADDR 0x10000000 #define MX21_AIPI_BASE_ADDR_VIRT 0xf4000000 @@ -219,4 +219,4 @@ #define DMA_REQ_BMI_RX MX21_DMA_REQ_BMI_RX #endif -#endif /* __ASM_ARCH_MXC_MX21_H__ */ +#endif /* ifndef __MACH_MX21_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h index 7a8982d..18eb44c 100644 --- a/arch/arm/plat-mxc/include/mach/mx25.h +++ b/arch/arm/plat-mxc/include/mach/mx25.h @@ -34,4 +34,4 @@ #define MX25_INT_FEC 57 -#endif /* __MACH_MX25_H__ */ +#endif /* ifndef __MACH_MX25_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx27.h b/arch/arm/plat-mxc/include/mach/mx27.h index 62746c0..832b580 100644 --- a/arch/arm/plat-mxc/include/mach/mx27.h +++ b/arch/arm/plat-mxc/include/mach/mx27.h @@ -21,8 +21,8 @@ * MA 02110-1301, USA. */ -#ifndef __ASM_ARCH_MXC_MX27_H__ -#define __ASM_ARCH_MXC_MX27_H__ +#ifndef __MACH_MX27_H__ +#define __MACH_MX27_H__ #define MX27_AIPI_BASE_ADDR 0x10000000 #define MX27_AIPI_BASE_ADDR_VIRT 0xf4000000 @@ -300,4 +300,4 @@ extern int mx27_revision(void); #define DMA_REQ_NFC MX27_DMA_REQ_NFC #endif -#endif /* __ASM_ARCH_MXC_MX27_H__ */ +#endif /* ifndef __MACH_MX27_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx2x.h b/arch/arm/plat-mxc/include/mach/mx2x.h index 396470e..afb895a 100644 --- a/arch/arm/plat-mxc/include/mach/mx2x.h +++ b/arch/arm/plat-mxc/include/mach/mx2x.h @@ -20,8 +20,8 @@ * MA 02110-1301, USA. */ -#ifndef __ASM_ARCH_MXC_MX2x_H__ -#define __ASM_ARCH_MXC_MX2x_H__ +#ifndef __MACH_MX2x_H__ +#define __MACH_MX2x_H__ /* The following addresses are common between i.MX21 and i.MX27 */ @@ -290,4 +290,4 @@ #define DMA_REQ_CSI_RX MX2x_DMA_REQ_CSI_RX #endif -#endif /* __ASM_ARCH_MXC_MX2x_H__ */ +#endif /* ifndef __MACH_MX2x_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx31.h b/arch/arm/plat-mxc/include/mach/mx31.h index 53da4c5..62d26da 100644 --- a/arch/arm/plat-mxc/include/mach/mx31.h +++ b/arch/arm/plat-mxc/include/mach/mx31.h @@ -1,3 +1,6 @@ +#ifndef __MACH_MX31_H__ +#define __MACH_MX31_H__ + /* * IRAM */ @@ -225,3 +228,5 @@ #define MXC_INT_CCM MX31_INT_CCM #define MXC_INT_PCMCIA MX31_INT_PCMCIA #endif + +#endif /* ifndef __MACH_MX31_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx35.h b/arch/arm/plat-mxc/include/mach/mx35.h index ff2d2f0..526a558 100644 --- a/arch/arm/plat-mxc/include/mach/mx35.h +++ b/arch/arm/plat-mxc/include/mach/mx35.h @@ -1,3 +1,5 @@ +#ifndef __MACH_MX35_H__ +#define __MACH_MX35_H__ /* * IRAM */ @@ -204,3 +206,5 @@ #define MXC_INT_SPDIF MX35_INT_SPDIF #define MXC_INT_FEC MX35_INT_FEC #endif + +#endif /* ifndef __MACH_MX35_H__ */ diff --git a/arch/arm/plat-mxc/include/mach/mx3x.h b/arch/arm/plat-mxc/include/mach/mx3x.h index 5ae0b00..7a356de3 100644 --- a/arch/arm/plat-mxc/include/mach/mx3x.h +++ b/arch/arm/plat-mxc/include/mach/mx3x.h @@ -8,8 +8,8 @@ * published by the Free Software Foundation. */ -#ifndef __ASM_ARCH_MXC_MX31_H__ -#define __ASM_ARCH_MXC_MX31_H__ +#ifndef __MACH_MX3x_H__ +#define __MACH_MX3x_H__ /* * MX31 memory map: @@ -404,4 +404,4 @@ static inline int mx31_revision(void) #define SYSTEM_REV_NUM MX3x_SYSTEM_REV_NUM #endif -#endif /* __ASM_ARCH_MXC_MX31_H__ */ +#endif /* ifndef __MACH_MX3x_H__ */ -- cgit v0.10.2 From a8dfb6462a033984b99fee4122fe0799a31f5bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 7 Jan 2010 11:27:17 +0100 Subject: imx: define functions to configure chip selects in the WEIM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has the addional effect that the macros CSCR_U, CSCR_L and CSCR_A are not used anymore in mach-pcm038.c and mach-qong.c. These still use the deprecated IO_ADDRESS macro and shouldn't be used in new code. Signed-off-by: Uwe Kleine-König Cc: Sascha Hauer Cc: Russell King Cc: Holger Schurig Cc: Dmitriy Taychenachev diff --git a/arch/arm/mach-mx2/mach-pcm038.c b/arch/arm/mach-mx2/mach-pcm038.c index a2e2ea1..9636bb8 100644 --- a/arch/arm/mach-mx2/mach-pcm038.c +++ b/arch/arm/mach-mx2/mach-pcm038.c @@ -173,9 +173,7 @@ static struct platform_device *platform_devices[] __initdata = { * setup other stuffs to access the sram. */ static void __init pcm038_init_sram(void) { - __raw_writel(0x0000d843, CSCR_U(1)); - __raw_writel(0x22252521, CSCR_L(1)); - __raw_writel(0x22220a00, CSCR_A(1)); + mx27_setup_weimcs(1, 0x0000d843, 0x22252521, 0x22220a00); } static struct imxi2c_platform_data pcm038_i2c_1_data = { diff --git a/arch/arm/mach-mx3/mach-qong.c b/arch/arm/mach-mx3/mach-qong.c index 93991f1..fdb819a 100644 --- a/arch/arm/mach-mx3/mach-qong.c +++ b/arch/arm/mach-mx3/mach-qong.c @@ -198,9 +198,7 @@ static struct platform_device qong_nand_device = { static void __init qong_init_nand_mtd(void) { /* init CS */ - __raw_writel(0x00004f00, CSCR_U(3)); - __raw_writel(0x20013b31, CSCR_L(3)); - __raw_writel(0x00020800, CSCR_A(3)); + mx31_setup_weimcs(3, 0x00004f00, 0x20013b31, 0x00020800); mxc_iomux_set_gpr(MUX_SDCTL_CSD1_SEL, true); /* enable pin */ diff --git a/arch/arm/plat-mxc/include/mach/mx27.h b/arch/arm/plat-mxc/include/mach/mx27.h index 832b580..bae9cd7 100644 --- a/arch/arm/plat-mxc/include/mach/mx27.h +++ b/arch/arm/plat-mxc/include/mach/mx27.h @@ -24,6 +24,10 @@ #ifndef __MACH_MX27_H__ #define __MACH_MX27_H__ +#ifndef __ASSEMBLER__ +#include +#endif + #define MX27_AIPI_BASE_ADDR 0x10000000 #define MX27_AIPI_BASE_ADDR_VIRT 0xf4000000 #define MX27_AIPI_SIZE SZ_1M @@ -109,6 +113,11 @@ #define MX27_M3IF_BASE_ADDR (MX27_X_MEMC_BASE_ADDR + 0x3000) #define MX27_PCMCIA_CTL_BASE_ADDR (MX27_X_MEMC_BASE_ADDR + 0x4000) +#define MX27_WEIM_CSCRx_BASE_ADDR(cs) (MX27_WEIM_BASE_ADDR + (cs) * 0x10) +#define MX27_WEIM_CSCRxU(cs) (MX27_WEIM_CSCRx_BASE_ADDR(cs)) +#define MX27_WEIM_CSCRxL(cs) (MX27_WEIM_CSCRx_BASE_ADDR(cs) + 0x4) +#define MX27_WEIM_CSCRxA(cs) (MX27_WEIM_CSCRx_BASE_ADDR(cs) + 0x8) + #define MX27_PCMCIA_MEM_BASE_ADDR 0xdc000000 /* IRAM */ @@ -119,6 +128,16 @@ IMX_IO_ADDRESS(x, MX27_SAHB1) ?: \ IMX_IO_ADDRESS(x, MX27_X_MEMC)) +#ifndef __ASSEMBLER__ +static inline void mx27_setup_weimcs(size_t cs, + unsigned upper, unsigned lower, unsigned addional) +{ + __raw_writel(upper, MX27_IO_ADDRESS(MX27_WEIM_CSCRxU(cs))); + __raw_writel(lower, MX27_IO_ADDRESS(MX27_WEIM_CSCRxL(cs))); + __raw_writel(addional, MX27_IO_ADDRESS(MX27_WEIM_CSCRxA(cs))); +} +#endif + /* fixed interrupt numbers */ #define MX27_INT_I2C2 1 #define MX27_INT_GPT6 2 diff --git a/arch/arm/plat-mxc/include/mach/mx31.h b/arch/arm/plat-mxc/include/mach/mx31.h index 62d26da..fb90e11 100644 --- a/arch/arm/plat-mxc/include/mach/mx31.h +++ b/arch/arm/plat-mxc/include/mach/mx31.h @@ -1,6 +1,10 @@ #ifndef __MACH_MX31_H__ #define __MACH_MX31_H__ +#ifndef __ASSEMBLER__ +#include +#endif + /* * IRAM */ @@ -110,6 +114,11 @@ #define MX31_EMI_CTL_BASE_ADDR (MX31_X_MEMC_BASE_ADDR + 0x4000) #define MX31_PCMCIA_CTL_BASE_ADDR MX31_EMI_CTL_BASE_ADDR +#define MX31_WEIM_CSCRx_BASE_ADDR(cs) (MX31_WEIM_BASE_ADDR + (cs) * 0x10) +#define MX31_WEIM_CSCRxU(cs) (MX31_WEIM_CSCRx_BASE_ADDR(cs)) +#define MX31_WEIM_CSCRxL(cs) (MX31_WEIM_CSCRx_BASE_ADDR(cs) + 0x4) +#define MX31_WEIM_CSCRxA(cs) (MX31_WEIM_CSCRx_BASE_ADDR(cs) + 0x8) + #define MX31_PCMCIA_MEM_BASE_ADDR 0xbc000000 #define MX31_IO_ADDRESS(x) ( \ @@ -119,6 +128,16 @@ IMX_IO_ADDRESS(x, MX31_X_MEMC) ?: \ IMX_IO_ADDRESS(x, MX31_SPBA0)) +#ifndef __ASSEMBLER__ +static inline void mx31_setup_weimcs(size_t cs, + unsigned upper, unsigned lower, unsigned addional) +{ + __raw_writel(upper, MX31_IO_ADDRESS(MX31_WEIM_CSCRxU(cs))); + __raw_writel(lower, MX31_IO_ADDRESS(MX31_WEIM_CSCRxL(cs))); + __raw_writel(addional, MX31_IO_ADDRESS(MX31_WEIM_CSCRxA(cs))); +} +#endif + #define MX31_INT_I2C3 3 #define MX31_INT_I2C2 4 #define MX31_INT_MPEG4_ENCODER 5 diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h index 2b14126..800ae2a 100644 --- a/arch/arm/plat-mxc/include/mach/mxc.h +++ b/arch/arm/plat-mxc/include/mach/mxc.h @@ -121,6 +121,7 @@ extern unsigned int __mxc_cpu_type; #endif #if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2) +/* These are deprecated, use mx[23][157]_setup_weimcs instead. */ #define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10)) #define CSCR_L(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10 + 0x4)) #define CSCR_A(n) (IO_ADDRESS(WEIM_BASE_ADDR + n * 0x10 + 0x8)) -- cgit v0.10.2 From 145434bee45bd353f9a93e9b411f7aa7cc677c08 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 11 Jan 2010 13:46:31 +0000 Subject: uwb: wlp: refactor wlp_get_() macros Refactor the wlp_get_() macros to call a common function. This save over 4k of space and remove a spurious uninitialized variable warning with some versions of gcc. Signed-off-by: David Vrabel diff --git a/drivers/uwb/wlp/messages.c b/drivers/uwb/wlp/messages.c index aa42fce..7516486 100644 --- a/drivers/uwb/wlp/messages.c +++ b/drivers/uwb/wlp/messages.c @@ -259,6 +259,63 @@ out: } +static ssize_t wlp_get_attribute(struct wlp *wlp, u16 type_code, + struct wlp_attr_hdr *attr_hdr, void *value, ssize_t value_len, + ssize_t buflen) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + ssize_t attr_len = sizeof(*attr_hdr) + value_len; + if (buflen < 0) + return -EINVAL; + if (buflen < attr_len) { + dev_err(dev, "WLP: Not enough space in buffer to parse" + " attribute field. Need %d, received %zu\n", + (int)attr_len, buflen); + return -EIO; + } + if (wlp_check_attr_hdr(wlp, attr_hdr, type_code, value_len) < 0) { + dev_err(dev, "WLP: Header verification failed. \n"); + return -EINVAL; + } + memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), value_len); + return attr_len; +} + +static ssize_t wlp_vget_attribute(struct wlp *wlp, u16 type_code, + struct wlp_attr_hdr *attr_hdr, void *value, ssize_t max_value_len, + ssize_t buflen) +{ + struct device *dev = &wlp->rc->uwb_dev.dev; + size_t len; + if (buflen < 0) + return -EINVAL; + if (buflen < sizeof(*attr_hdr)) { + dev_err(dev, "WLP: Not enough space in buffer to parse" + " header.\n"); + return -EIO; + } + if (le16_to_cpu(attr_hdr->type) != type_code) { + dev_err(dev, "WLP: Unexpected attribute type. Got %u, " + "expected %u.\n", le16_to_cpu(attr_hdr->type), + type_code); + return -EINVAL; + } + len = le16_to_cpu(attr_hdr->length); + if (len > max_value_len) { + dev_err(dev, "WLP: Attribute larger than maximum " + "allowed. Received %zu, max is %d.\n", len, + (int)max_value_len); + return -EFBIG; + } + if (buflen < sizeof(*attr_hdr) + len) { + dev_err(dev, "WLP: Not enough space in buffer to parse " + "variable data.\n"); + return -EIO; + } + memcpy(value, (void *)attr_hdr + sizeof(*attr_hdr), len); + return sizeof(*attr_hdr) + len; +} + /** * Get value of attribute from fixed size attribute field. * @@ -274,22 +331,8 @@ out: ssize_t wlp_get_##name(struct wlp *wlp, struct wlp_attr_##name *attr, \ type *value, ssize_t buflen) \ { \ - struct device *dev = &wlp->rc->uwb_dev.dev; \ - if (buflen < 0) \ - return -EINVAL; \ - if (buflen < sizeof(*attr)) { \ - dev_err(dev, "WLP: Not enough space in buffer to parse" \ - " attribute field. Need %d, received %zu\n", \ - (int)sizeof(*attr), buflen); \ - return -EIO; \ - } \ - if (wlp_check_attr_hdr(wlp, &attr->hdr, type_code, \ - sizeof(attr->name)) < 0) { \ - dev_err(dev, "WLP: Header verification failed. \n"); \ - return -EINVAL; \ - } \ - *value = attr->name; \ - return sizeof(*attr); \ + return wlp_get_attribute(wlp, (type_code), &attr->hdr, \ + value, sizeof(*value), buflen); \ } #define wlp_get_sparse(type, type_code, name) \ @@ -313,35 +356,8 @@ static ssize_t wlp_get_##name(struct wlp *wlp, \ struct wlp_attr_##name *attr, \ type_val *value, ssize_t buflen) \ { \ - struct device *dev = &wlp->rc->uwb_dev.dev; \ - size_t len; \ - if (buflen < 0) \ - return -EINVAL; \ - if (buflen < sizeof(*attr)) { \ - dev_err(dev, "WLP: Not enough space in buffer to parse" \ - " header.\n"); \ - return -EIO; \ - } \ - if (le16_to_cpu(attr->hdr.type) != type_code) { \ - dev_err(dev, "WLP: Unexpected attribute type. Got %u, " \ - "expected %u.\n", le16_to_cpu(attr->hdr.type), \ - type_code); \ - return -EINVAL; \ - } \ - len = le16_to_cpu(attr->hdr.length); \ - if (len > max) { \ - dev_err(dev, "WLP: Attribute larger than maximum " \ - "allowed. Received %zu, max is %d.\n", len, \ - (int)max); \ - return -EFBIG; \ - } \ - if (buflen < sizeof(*attr) + len) { \ - dev_err(dev, "WLP: Not enough space in buffer to parse "\ - "variable data.\n"); \ - return -EIO; \ - } \ - memcpy(value, (void *) attr + sizeof(*attr), len); \ - return sizeof(*attr) + len; \ + return wlp_vget_attribute(wlp, (type_code), &attr->hdr, \ + value, (max), buflen); \ } wlp_get(u8, WLP_ATTR_WLP_VER, version) -- cgit v0.10.2 From 8045e2985012bdb95d832dfbcceae1815880a6ed Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Tue, 12 Jan 2010 03:32:24 +0900 Subject: fat: Fix vfat_lookup() After d_find_alias(), vfat_lookup() checks !(->d_flags & DCACHE_DISCONNECTED) without IS_ROOT(). This means it hits non-anonymous but disconnected dentry. (NOTE: d_splice_alias() doesn't clear DCACHE_DISCONNECTED) But, vfat_lookup() has interest to alias if it was non-anonymous. So, this adds vfat_d_anon_disconn() helper to check it correctly. Another bug is refcnt leak. It needs dput() for uninterested alias. Signed-off-by: OGAWA Hirofumi diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index f565f24..411c192 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -701,6 +701,15 @@ static int vfat_find(struct inode *dir, struct qstr *qname, return fat_search_long(dir, qname->name, len, sinfo); } +/* + * (nfsd's) anonymous disconnected dentry? + * NOTE: !IS_ROOT() is not anonymous (I.e. d_splice_alias() did the job). + */ +static int vfat_d_anon_disconn(struct dentry *dentry) +{ + return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); +} + static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { @@ -729,11 +738,11 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, } alias = d_find_alias(inode); - if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { + if (alias && !vfat_d_anon_disconn(alias)) { /* - * This inode has non DCACHE_DISCONNECTED dentry. This - * means, the user did ->lookup() by an another name - * (longname vs 8.3 alias of it) in past. + * This inode has non anonymous-DCACHE_DISCONNECTED + * dentry. This means, the user did ->lookup() by an + * another name (longname vs 8.3 alias of it) in past. * * Switch to new one for reason of locality if possible. */ @@ -743,7 +752,9 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, iput(inode); unlock_super(sb); return alias; - } + } else + dput(alias); + out: unlock_super(sb); dentry->d_op = sb->s_root->d_op; -- cgit v0.10.2 From b6d0c1bda9038354ff2f0c77da23fd32e0f11a1f Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 14 Jan 2010 11:24:13 +0200 Subject: mtd: mxc_nand: add MX25 to Kconfig Signed-off-by: Baruch Siach Signed-off-by: Sascha Hauer diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 677cd53..4f99274 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -444,7 +444,7 @@ config MTD_NAND_FSL_UPM config MTD_NAND_MXC tristate "MXC NAND support" - depends on ARCH_MX2 || ARCH_MX3 + depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 help This enables the driver for the NAND flash controller on the MXC processors. -- cgit v0.10.2 From 27f590258954ea0d22b43e33dd356bc76a8dfe7a Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 14 Jan 2010 11:24:14 +0200 Subject: mx25: add NAND support Signed-off-by: Baruch Siach Signed-off-by: Sascha Hauer diff --git a/arch/arm/mach-mx25/devices.c b/arch/arm/mach-mx25/devices.c index 9fdeea1..dd40697 100644 --- a/arch/arm/mach-mx25/devices.c +++ b/arch/arm/mach-mx25/devices.c @@ -438,3 +438,23 @@ struct platform_device mx25_fec_device = { .num_resources = ARRAY_SIZE(mx25_fec_resources), .resource = mx25_fec_resources, }; + +static struct resource mxc_nand_resources[] = { + { + .start = MX25_NFC_BASE_ADDR, + .end = MX25_NFC_BASE_ADDR + 0x1fff, + .flags = IORESOURCE_MEM, + }, + { + .start = MX25_INT_NANDFC, + .end = MX25_INT_NANDFC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device mxc_nand_device = { + .name = "mxc_nand", + .id = 0, + .num_resources = ARRAY_SIZE(mxc_nand_resources), + .resource = mxc_nand_resources, +}; diff --git a/arch/arm/mach-mx25/devices.h b/arch/arm/mach-mx25/devices.h index fe5420f..8f55300 100644 --- a/arch/arm/mach-mx25/devices.h +++ b/arch/arm/mach-mx25/devices.h @@ -18,3 +18,4 @@ extern struct platform_device mxc_i2c_device0; extern struct platform_device mxc_i2c_device1; extern struct platform_device mxc_i2c_device2; extern struct platform_device mx25_fec_device; +extern struct platform_device mxc_nand_device; diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h index 18eb44c..021d208 100644 --- a/arch/arm/plat-mxc/include/mach/mx25.h +++ b/arch/arm/plat-mxc/include/mach/mx25.h @@ -31,7 +31,9 @@ #define UART2_BASE_ADDR 0x43f94000 #define MX25_FEC_BASE_ADDR 0x50038000 +#define MX25_NFC_BASE_ADDR 0xbb000000 #define MX25_INT_FEC 57 +#define MX25_INT_NANDFC 33 #endif /* ifndef __MACH_MX25_H__ */ -- cgit v0.10.2 From b97235dfa1aba54786b19fc53ba026e47be86ac6 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 14 Jan 2010 11:24:15 +0200 Subject: mx25pdk: add NAND device support Signed-off-by: Baruch Siach Signed-off-by: Sascha Hauer diff --git a/arch/arm/mach-mx25/mx25pdk.c b/arch/arm/mach-mx25/mx25pdk.c index 921bc99..c8b1d3b 100644 --- a/arch/arm/mach-mx25/mx25pdk.c +++ b/arch/arm/mach-mx25/mx25pdk.c @@ -77,6 +77,12 @@ static void __init mx25pdk_fec_reset(void) gpio_set_value(FEC_RESET_B_GPIO, 1); } +static struct mxc_nand_platform_data mx25pdk_nand_board_info = { + .width = 1, + .hw_ecc = 1, + .flash_bbt = 1, +}; + static void __init mx25pdk_init(void) { mxc_iomux_v3_setup_multiple_pads(mx25pdk_pads, @@ -84,6 +90,7 @@ static void __init mx25pdk_init(void) mxc_register_device(&mxc_uart_device0, &uart_pdata); mxc_register_device(&mxc_usbh2, NULL); + mxc_register_device(&mxc_nand_device, &mx25pdk_nand_board_info); mx25pdk_fec_reset(); mxc_register_device(&mx25_fec_device, &mx25_fec_pdata); -- cgit v0.10.2 From 34446d05dd255b34518c76d2b8760161e63fe0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Tue, 12 Jan 2010 08:49:14 +0100 Subject: uwb: make USB device id table constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The id_table field of the struct usb_device_id is constant in so it is worth to make the initialization data also constant. Signed-off-by: Márton Németh Signed-off-by: David Vrabel diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c index e7eeb63..b409c228f 100644 --- a/drivers/uwb/hwa-rc.c +++ b/drivers/uwb/hwa-rc.c @@ -891,7 +891,7 @@ static int hwarc_post_reset(struct usb_interface *iface) } /** USB device ID's that we handle */ -static struct usb_device_id hwarc_id_table[] = { +static const struct usb_device_id hwarc_id_table[] = { /* D-Link DUB-1210 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3d02, 0xe0, 0x01, 0x02), .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c index 0bb665a..08f9a7b 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/uwb/i1480/dfu/usb.c @@ -430,7 +430,7 @@ error: /** USB device ID's that we handle */ -static struct usb_device_id i1480_usb_id_table[] = { +static const struct usb_device_id i1480_usb_id_table[] = { i1480_USB_DEV(0x8086, 0xdf3b), i1480_USB_DEV(0x15a9, 0x0005), i1480_USB_DEV(0x07d1, 0x3802), -- cgit v0.10.2 From 35fb2a816a06ded2a3ff83d896c34b83c8e1d556 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 13 Jan 2010 23:41:50 +0000 Subject: uwb: declare MODULE_FIRMWARE() in i1480 DFU driver Signed-off-by: Ben Hutchings Signed-off-by: David Vrabel diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c index 08f9a7b..a6a9375 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/uwb/i1480/dfu/usb.c @@ -413,6 +413,10 @@ error: return result; } +MODULE_FIRMWARE("i1480-pre-phy-0.0.bin"); +MODULE_FIRMWARE("i1480-usb-0.0.bin"); +MODULE_FIRMWARE("i1480-phy-0.0.bin"); + #define i1480_USB_DEV(v, p) \ { \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE \ -- cgit v0.10.2 From 4baa75ef0ed29adae03fcbbaa9aca1511a5a8cc9 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Thu, 7 Jan 2010 15:36:32 -0800 Subject: ceph: change dentry offset and position after splice_dentry This fixes a bug, where we had the parent list have dentries with offsets that are not monotonically increasing, which caused the ceph dcache_readdir to skip entries. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 8774b28..518beb6 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -817,6 +817,33 @@ out: } /* + * Set dentry's directory position based on the current dir's max, and + * order it in d_subdirs, so that dcache_readdir behaves. + */ +static void ceph_set_dentry_offset(struct dentry *dn) +{ + struct dentry *dir = dn->d_parent; + struct inode *inode = dn->d_parent->d_inode; + struct ceph_dentry_info *di; + + BUG_ON(!inode); + + di = ceph_dentry(dn); + + spin_lock(&inode->i_lock); + di->offset = ceph_inode(inode)->i_max_offset++; + spin_unlock(&inode->i_lock); + + spin_lock(&dcache_lock); + spin_lock(&dn->d_lock); + list_move_tail(&dir->d_subdirs, &dn->d_u.d_child); + dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset, + dn->d_u.d_child.prev, dn->d_u.d_child.next); + spin_unlock(&dn->d_lock); + spin_unlock(&dcache_lock); +} + +/* * Incorporate results into the local cache. This is either just * one inode, or a directory, dentry, and possibly linked-to inode (e.g., * after a lookup). @@ -987,6 +1014,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, goto done; } req->r_dentry = dn; /* may have spliced */ + ceph_set_dentry_offset(dn); igrab(in); } else if (ceph_ino(in) == vino.ino && ceph_snap(in) == vino.snap) { @@ -1029,6 +1057,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, err = PTR_ERR(dn); goto done; } + ceph_set_dentry_offset(dn); req->r_dentry = dn; /* may have spliced */ igrab(in); rinfo->head->is_dentry = 1; /* fool notrace handlers */ -- cgit v0.10.2 From 103e2d3ae57d38d18aaac1b327266c1407499ac1 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 7 Jan 2010 16:12:36 -0800 Subject: ceph: remove unused erank field The ceph_entity_addr erank field is obsolete; remove it. Get rid of trivial addr comparison helpers while we're at it. Signed-off-by: Sage Weil diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index c1106e8..1360708 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1056,16 +1056,15 @@ static int process_banner(struct ceph_connection *con) * end may not yet know their ip address, so if it's 0.0.0.0, give * them the benefit of the doubt. */ - if (!ceph_entity_addr_is_local(&con->peer_addr, - &con->actual_peer_addr) && + if (memcmp(&con->peer_addr, &con->actual_peer_addr, + sizeof(con->peer_addr)) != 0 && !(addr_is_blank(&con->actual_peer_addr.in_addr) && con->actual_peer_addr.nonce == con->peer_addr.nonce)) { - pr_warning("wrong peer, want %s/%d, " - "got %s/%d\n", - pr_addr(&con->peer_addr.in_addr), - con->peer_addr.nonce, - pr_addr(&con->actual_peer_addr.in_addr), - con->actual_peer_addr.nonce); + pr_warning("wrong peer, want %s/%lld, got %s/%lld\n", + pr_addr(&con->peer_addr.in_addr), + le64_to_cpu(con->peer_addr.nonce), + pr_addr(&con->actual_peer_addr.in_addr), + le64_to_cpu(con->actual_peer_addr.nonce)); con->error_msg = "wrong peer at address"; return -1; } @@ -1934,8 +1933,7 @@ struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr) msgr->inst.addr = *myaddr; /* select a random nonce */ - get_random_bytes(&msgr->inst.addr.nonce, - sizeof(msgr->inst.addr.nonce)); + get_random_bytes(&msgr->inst.addr.nonce, sizeof(msgr->inst.addr.nonce)); encode_my_addr(msgr); dout("messenger_create %p\n", msgr); @@ -1966,7 +1964,6 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) msg->hdr.src.name = con->msgr->inst.name; msg->hdr.src.addr = con->msgr->my_enc_addr; msg->hdr.orig_src = msg->hdr.src; - msg->hdr.dst_erank = con->peer_addr.erank; /* queue */ mutex_lock(&con->mutex); diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index bb94006..223e8bc 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -88,7 +88,7 @@ int ceph_monmap_contains(struct ceph_monmap *m, struct ceph_entity_addr *addr) int i; for (i = 0; i < m->num_mon; i++) - if (ceph_entity_addr_equal(addr, &m->mon_inst[i].addr)) + if (memcmp(addr, &m->mon_inst[i].addr, sizeof(*addr)) == 0) return 1; return 0; } @@ -503,7 +503,6 @@ static int build_initial_monmap(struct ceph_mon_client *monc) return -ENOMEM; for (i = 0; i < num_mon; i++) { monc->monmap->mon_inst[i].addr = mon_addr[i]; - monc->monmap->mon_inst[i].addr.erank = 0; monc->monmap->mon_inst[i].addr.nonce = 0; monc->monmap->mon_inst[i].name.type = CEPH_ENTITY_TYPE_MON; diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h index be83f93..40b6189 100644 --- a/fs/ceph/msgr.h +++ b/fs/ceph/msgr.h @@ -61,24 +61,10 @@ extern const char *ceph_entity_type_name(int type); * entity_addr -- network address */ struct ceph_entity_addr { - __le32 erank; /* entity's rank in process */ - __le32 nonce; /* unique id for process (e.g. pid) */ + __le64 nonce; /* unique id for process (e.g. pid) */ struct sockaddr_storage in_addr; } __attribute__ ((packed)); -static inline bool ceph_entity_addr_is_local(const struct ceph_entity_addr *a, - const struct ceph_entity_addr *b) -{ - return a->nonce == b->nonce && - memcmp(&a->in_addr, &b->in_addr, sizeof(a->in_addr)) == 0; -} - -static inline bool ceph_entity_addr_equal(const struct ceph_entity_addr *a, - const struct ceph_entity_addr *b) -{ - return memcmp(a, b, sizeof(*a)) == 0; -} - struct ceph_entity_inst { struct ceph_entity_name name; struct ceph_entity_addr addr; @@ -147,7 +133,7 @@ struct ceph_msg_header { receiver: mask against ~PAGE_MASK */ struct ceph_entity_inst src, orig_src; - __le32 dst_erank; + __le32 reserved; __le32 crc; /* header crc32c */ } __attribute__ ((packed)); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index a0aac43..80b868f 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -821,9 +821,10 @@ static void kick_requests(struct ceph_osd_client *osdc, n = rb_next(p); if (!ceph_osd_is_up(osdc->osdmap, osd->o_osd) || - !ceph_entity_addr_equal(&osd->o_con.peer_addr, - ceph_osd_addr(osdc->osdmap, - osd->o_osd))) + memcmp(&osd->o_con.peer_addr, + ceph_osd_addr(osdc->osdmap, + osd->o_osd), + sizeof(struct ceph_entity_addr)) != 0) reset_osd(osdc, osd); } } -- cgit v0.10.2 From 7740a42f816790583bd8a9079337772d511af3a3 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 8 Jan 2010 15:58:25 -0800 Subject: ceph: display pgid in debugfs osd request dump Signed-off-by: Sage Weil diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 22d3b47..fba44b2 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -231,8 +231,10 @@ static int osdc_show(struct seq_file *s, void *pp) req = rb_entry(p, struct ceph_osd_request, r_node); - seq_printf(s, "%lld\tosd%d\t", req->r_tid, - req->r_osd ? req->r_osd->o_osd : -1); + seq_printf(s, "%lld\tosd%d\t%d.%x\t", req->r_tid, + req->r_osd ? req->r_osd->o_osd : -1, + le32_to_cpu(req->r_pgid.pool), + le16_to_cpu(req->r_pgid.ps)); head = req->r_request->front.iov_base; op = (void *)(head + 1); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 80b868f..8417e21 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -538,6 +538,8 @@ static int __map_osds(struct ceph_osd_client *osdc, if (err) return err; pgid = reqhead->layout.ol_pgid; + req->r_pgid = pgid; + o = ceph_calc_pg_primary(osdc->osdmap, pgid); if ((req->r_osd && req->r_osd->o_osd == o && diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index 8fef71c..4162c68 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -42,6 +42,7 @@ struct ceph_osd_request { struct rb_node r_node; struct list_head r_osd_item; struct ceph_osd *r_osd; + struct ceph_pg r_pgid; struct ceph_connection *r_con_filling_pages; -- cgit v0.10.2 From 70792bcfad5f4817fc067b7fdb0541d8b13b5502 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 30 Nov 2009 00:40:47 +0000 Subject: ARM: SAMSUNG: Move to plat-samsung Move the header to plat-samsung where it can be used by all the platforms, and readies it for the next round of clock updates where the clock code will be amalgamated. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/include/plat/clock.h b/arch/arm/plat-s3c/include/plat/clock.h deleted file mode 100644 index d86af84..0000000 --- a/arch/arm/plat-s3c/include/plat/clock.h +++ /dev/null @@ -1,89 +0,0 @@ -/* linux/arch/arm/plat-s3c/include/plat/clock.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * Written by Ben Dooks, - * - * 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 - -struct clk { - struct list_head list; - struct module *owner; - struct clk *parent; - const char *name; - int id; - int usage; - unsigned long rate; - unsigned long ctrlbit; - - int (*enable)(struct clk *, int enable); - int (*set_rate)(struct clk *c, unsigned long rate); - unsigned long (*get_rate)(struct clk *c); - unsigned long (*round_rate)(struct clk *c, unsigned long rate); - int (*set_parent)(struct clk *c, struct clk *parent); -}; - -/* other clocks which may be registered by board support */ - -extern struct clk s3c24xx_dclk0; -extern struct clk s3c24xx_dclk1; -extern struct clk s3c24xx_clkout0; -extern struct clk s3c24xx_clkout1; -extern struct clk s3c24xx_uclk; - -extern struct clk clk_usb_bus; - -/* core clock support */ - -extern struct clk clk_f; -extern struct clk clk_h; -extern struct clk clk_p; -extern struct clk clk_mpll; -extern struct clk clk_upll; -extern struct clk clk_epll; -extern struct clk clk_xtal; -extern struct clk clk_ext; - -/* S3C64XX specific clocks */ -extern struct clk clk_h2; -extern struct clk clk_27m; -extern struct clk clk_48m; - -/* exports for arch/arm/mach-s3c2410 - * - * Please DO NOT use these outside of arch/arm/mach-s3c2410 -*/ - -extern spinlock_t clocks_lock; - -extern int s3c2410_clkcon_enable(struct clk *clk, int enable); - -extern int s3c24xx_register_clock(struct clk *clk); -extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks); - -extern int s3c24xx_register_baseclocks(unsigned long xtal); - -extern void s3c64xx_register_clocks(void); - -extern void s3c24xx_setup_clocks(unsigned long fclk, - unsigned long hclk, - unsigned long pclk); - -extern void s3c2410_setup_clocks(void); -extern void s3c2412_setup_clocks(void); -extern void s3c244x_setup_clocks(void); -extern void s3c2443_setup_clocks(void); - -/* S3C64XX specific functions and clocks */ - -extern int s3c64xx_sclk_ctrl(struct clk *clk, int enable); - -/* Init for pwm clock code */ - -extern void s3c_pwmclk_init(void); - diff --git a/arch/arm/plat-samsung/include/plat/clock.h b/arch/arm/plat-samsung/include/plat/clock.h new file mode 100644 index 0000000..d86af84 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/clock.h @@ -0,0 +1,89 @@ +/* linux/arch/arm/plat-s3c/include/plat/clock.h + * + * Copyright (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Written by Ben Dooks, + * + * 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 + +struct clk { + struct list_head list; + struct module *owner; + struct clk *parent; + const char *name; + int id; + int usage; + unsigned long rate; + unsigned long ctrlbit; + + int (*enable)(struct clk *, int enable); + int (*set_rate)(struct clk *c, unsigned long rate); + unsigned long (*get_rate)(struct clk *c); + unsigned long (*round_rate)(struct clk *c, unsigned long rate); + int (*set_parent)(struct clk *c, struct clk *parent); +}; + +/* other clocks which may be registered by board support */ + +extern struct clk s3c24xx_dclk0; +extern struct clk s3c24xx_dclk1; +extern struct clk s3c24xx_clkout0; +extern struct clk s3c24xx_clkout1; +extern struct clk s3c24xx_uclk; + +extern struct clk clk_usb_bus; + +/* core clock support */ + +extern struct clk clk_f; +extern struct clk clk_h; +extern struct clk clk_p; +extern struct clk clk_mpll; +extern struct clk clk_upll; +extern struct clk clk_epll; +extern struct clk clk_xtal; +extern struct clk clk_ext; + +/* S3C64XX specific clocks */ +extern struct clk clk_h2; +extern struct clk clk_27m; +extern struct clk clk_48m; + +/* exports for arch/arm/mach-s3c2410 + * + * Please DO NOT use these outside of arch/arm/mach-s3c2410 +*/ + +extern spinlock_t clocks_lock; + +extern int s3c2410_clkcon_enable(struct clk *clk, int enable); + +extern int s3c24xx_register_clock(struct clk *clk); +extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks); + +extern int s3c24xx_register_baseclocks(unsigned long xtal); + +extern void s3c64xx_register_clocks(void); + +extern void s3c24xx_setup_clocks(unsigned long fclk, + unsigned long hclk, + unsigned long pclk); + +extern void s3c2410_setup_clocks(void); +extern void s3c2412_setup_clocks(void); +extern void s3c244x_setup_clocks(void); +extern void s3c2443_setup_clocks(void); + +/* S3C64XX specific functions and clocks */ + +extern int s3c64xx_sclk_ctrl(struct clk *clk, int enable); + +/* Init for pwm clock code */ + +extern void s3c_pwmclk_init(void); + -- cgit v0.10.2 From f9c4f1e4ddf40103dcf85e23d00230ab8ece2a89 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 30 Nov 2009 01:10:57 +0000 Subject: ARM: S3C64XX: Cleanup common init code in s3c6400-clock.c Remove the four fields from clksrc_clk.clk which are always the same and init them when the clock is registered. This helps remove the amount of repeated code. This is a re-work of Harald Welte's clock changes for the latest kernel. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c index ffd56de..aba08c7 100644 --- a/arch/arm/plat-s3c64xx/s3c6400-clock.c +++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c @@ -360,10 +360,6 @@ static struct clksrc_clk clk_mmc0 = { .id = 0, .ctrlbit = S3C_CLKCON_SCLK_MMC0, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_MMC0_SHIFT, .mask = S3C6400_CLKSRC_MMC0_MASK, @@ -378,10 +374,6 @@ static struct clksrc_clk clk_mmc1 = { .id = 1, .ctrlbit = S3C_CLKCON_SCLK_MMC1, .enable = s3c64xx_sclk_ctrl, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .set_parent = s3c64xx_setparent_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_MMC1_SHIFT, .mask = S3C6400_CLKSRC_MMC1_MASK, @@ -396,10 +388,6 @@ static struct clksrc_clk clk_mmc2 = { .id = 2, .ctrlbit = S3C_CLKCON_SCLK_MMC2, .enable = s3c64xx_sclk_ctrl, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .set_parent = s3c64xx_setparent_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_MMC2_SHIFT, .mask = S3C6400_CLKSRC_MMC2_MASK, @@ -414,10 +402,6 @@ static struct clksrc_clk clk_usbhost = { .id = -1, .ctrlbit = S3C_CLKCON_SCLK_UHOST, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_UHOST_SHIFT, .mask = S3C6400_CLKSRC_UHOST_MASK, @@ -432,10 +416,6 @@ static struct clksrc_clk clk_uart_uclk1 = { .id = -1, .ctrlbit = S3C_CLKCON_SCLK_UART, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_UART_SHIFT, .mask = S3C6400_CLKSRC_UART_MASK, @@ -452,10 +432,6 @@ static struct clksrc_clk clk_spi0 = { .id = 0, .ctrlbit = S3C_CLKCON_SCLK_SPI0, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_SPI0_SHIFT, .mask = S3C6400_CLKSRC_SPI0_MASK, @@ -470,10 +446,6 @@ static struct clksrc_clk clk_spi1 = { .id = 1, .ctrlbit = S3C_CLKCON_SCLK_SPI1, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_SPI1_SHIFT, .mask = S3C6400_CLKSRC_SPI1_MASK, @@ -516,10 +488,6 @@ static struct clksrc_clk clk_audio0 = { .id = 0, .ctrlbit = S3C_CLKCON_SCLK_AUDIO0, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_AUDIO0_SHIFT, .mask = S3C6400_CLKSRC_AUDIO0_MASK, @@ -547,10 +515,6 @@ static struct clksrc_clk clk_audio1 = { .id = 1, .ctrlbit = S3C_CLKCON_SCLK_AUDIO1, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_AUDIO1_SHIFT, .mask = S3C6400_CLKSRC_AUDIO1_MASK, @@ -565,10 +529,6 @@ static struct clksrc_clk clk_irda = { .id = 0, .ctrlbit = S3C_CLKCON_SCLK_IRDA, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = S3C6400_CLKSRC_IRDA_SHIFT, .mask = S3C6400_CLKSRC_IRDA_MASK, @@ -592,10 +552,6 @@ static struct clksrc_clk clk_camif = { .id = -1, .ctrlbit = S3C_CLKCON_SCLK_CAM, .enable = s3c64xx_sclk_ctrl, - .set_parent = s3c64xx_setparent_clksrc, - .get_rate = s3c64xx_getrate_clksrc, - .set_rate = s3c64xx_setrate_clksrc, - .round_rate = s3c64xx_roundrate_clksrc, }, .shift = 0, .mask = 0, @@ -637,6 +593,11 @@ static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk) return; } + clk->clk.get_rate = s3c64xx_getrate_clksrc; + clk->clk.set_rate = s3c64xx_setrate_clksrc; + clk->clk.set_parent = s3c64xx_setparent_clksrc; + clk->clk.round_rate = s3c64xx_roundrate_clksrc; + clk->clk.parent = srcs->sources[clksrc]; printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", -- cgit v0.10.2 From 8360493c4ae4c116339cd1cf4da100c3547f23af Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 30 Nov 2009 01:31:32 +0000 Subject: ARM: S3C64XX: Compress s3c6400-clock.c code The individually named clocks are all static to the code and thus can be compressed into a single array and then the array can be referenced. This removes the need for a seperate array of pointers to clocks. Fix a minor problem of re-initialising the pointers in s3c6400_set_clksrc() as this is also called by the cpufreq code. Move these initialisations to the code that does the registration. Based on Harald Welte's original clock changes patch. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c index aba08c7..6fde910 100644 --- a/arch/arm/plat-s3c64xx/s3c6400-clock.c +++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c @@ -255,7 +255,6 @@ static struct clk_sources clkset_uhost = { .nr_sources = ARRAY_SIZE(clkset_uhost_list), }; - /* The peripheral clocks are all controlled via clocksource followed * by an optional divider and gate stage. We currently roll this into * one clock which hides the intermediate clock from the mux. @@ -354,105 +353,7 @@ static unsigned long s3c64xx_roundrate_clksrc(struct clk *clk, return rate; } -static struct clksrc_clk clk_mmc0 = { - .clk = { - .name = "mmc_bus", - .id = 0, - .ctrlbit = S3C_CLKCON_SCLK_MMC0, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_MMC0_SHIFT, - .mask = S3C6400_CLKSRC_MMC0_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC0_SHIFT, - .reg_divider = S3C_CLK_DIV1, -}; - -static struct clksrc_clk clk_mmc1 = { - .clk = { - .name = "mmc_bus", - .id = 1, - .ctrlbit = S3C_CLKCON_SCLK_MMC1, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_MMC1_SHIFT, - .mask = S3C6400_CLKSRC_MMC1_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC1_SHIFT, - .reg_divider = S3C_CLK_DIV1, -}; - -static struct clksrc_clk clk_mmc2 = { - .clk = { - .name = "mmc_bus", - .id = 2, - .ctrlbit = S3C_CLKCON_SCLK_MMC2, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_MMC2_SHIFT, - .mask = S3C6400_CLKSRC_MMC2_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC2_SHIFT, - .reg_divider = S3C_CLK_DIV1, -}; - -static struct clksrc_clk clk_usbhost = { - .clk = { - .name = "usb-bus-host", - .id = -1, - .ctrlbit = S3C_CLKCON_SCLK_UHOST, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_UHOST_SHIFT, - .mask = S3C6400_CLKSRC_UHOST_MASK, - .sources = &clkset_uhost, - .divider_shift = S3C6400_CLKDIV1_UHOST_SHIFT, - .reg_divider = S3C_CLK_DIV1, -}; - -static struct clksrc_clk clk_uart_uclk1 = { - .clk = { - .name = "uclk1", - .id = -1, - .ctrlbit = S3C_CLKCON_SCLK_UART, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_UART_SHIFT, - .mask = S3C6400_CLKSRC_UART_MASK, - .sources = &clkset_uart, - .divider_shift = S3C6400_CLKDIV2_UART_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - -/* Where does UCLK0 come from? */ - -static struct clksrc_clk clk_spi0 = { - .clk = { - .name = "spi-bus", - .id = 0, - .ctrlbit = S3C_CLKCON_SCLK_SPI0, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_SPI0_SHIFT, - .mask = S3C6400_CLKSRC_SPI0_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV2_SPI0_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - -static struct clksrc_clk clk_spi1 = { - .clk = { - .name = "spi-bus", - .id = 1, - .ctrlbit = S3C_CLKCON_SCLK_SPI1, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_SPI1_SHIFT, - .mask = S3C6400_CLKSRC_SPI1_MASK, - .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV2_SPI1_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; +/* clocks that feed other parts of the clock source tree */ static struct clk clk_iis_cd0 = { .name = "iis_cdclk0", @@ -482,20 +383,6 @@ static struct clk_sources clkset_audio0 = { .nr_sources = ARRAY_SIZE(clkset_audio0_list), }; -static struct clksrc_clk clk_audio0 = { - .clk = { - .name = "audio-bus", - .id = 0, - .ctrlbit = S3C_CLKCON_SCLK_AUDIO0, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_AUDIO0_SHIFT, - .mask = S3C6400_CLKSRC_AUDIO0_MASK, - .sources = &clkset_audio0, - .divider_shift = S3C6400_CLKDIV2_AUDIO0_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - static struct clk *clkset_audio1_list[] = { [0] = &clk_mout_epll.clk, [1] = &clk_dout_mpll, @@ -509,34 +396,6 @@ static struct clk_sources clkset_audio1 = { .nr_sources = ARRAY_SIZE(clkset_audio1_list), }; -static struct clksrc_clk clk_audio1 = { - .clk = { - .name = "audio-bus", - .id = 1, - .ctrlbit = S3C_CLKCON_SCLK_AUDIO1, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_AUDIO1_SHIFT, - .mask = S3C6400_CLKSRC_AUDIO1_MASK, - .sources = &clkset_audio1, - .divider_shift = S3C6400_CLKDIV2_AUDIO1_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - -static struct clksrc_clk clk_irda = { - .clk = { - .name = "irda-bus", - .id = 0, - .ctrlbit = S3C_CLKCON_SCLK_IRDA, - .enable = s3c64xx_sclk_ctrl, - }, - .shift = S3C6400_CLKSRC_IRDA_SHIFT, - .mask = S3C6400_CLKSRC_IRDA_MASK, - .sources = &clkset_irda, - .divider_shift = S3C6400_CLKDIV2_IRDA_SHIFT, - .reg_divider = S3C_CLK_DIV2, -}; - static struct clk *clkset_camif_list[] = { &clk_h2, }; @@ -546,18 +405,141 @@ static struct clk_sources clkset_camif = { .nr_sources = ARRAY_SIZE(clkset_camif_list), }; -static struct clksrc_clk clk_camif = { - .clk = { - .name = "camera", - .id = -1, - .ctrlbit = S3C_CLKCON_SCLK_CAM, - .enable = s3c64xx_sclk_ctrl, +static struct clksrc_clk clksrcs[] = { + { + .clk = { + .name = "mmc_bus", + .id = 0, + .ctrlbit = S3C_CLKCON_SCLK_MMC0, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_MMC0_SHIFT, + .mask = S3C6400_CLKSRC_MMC0_MASK, + .sources = &clkset_spi_mmc, + .divider_shift = S3C6400_CLKDIV1_MMC0_SHIFT, + .reg_divider = S3C_CLK_DIV1, + }, { + .clk = { + .name = "mmc_bus", + .id = 1, + .ctrlbit = S3C_CLKCON_SCLK_MMC1, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_MMC1_SHIFT, + .mask = S3C6400_CLKSRC_MMC1_MASK, + .sources = &clkset_spi_mmc, + .divider_shift = S3C6400_CLKDIV1_MMC1_SHIFT, + .reg_divider = S3C_CLK_DIV1, + }, { + .clk = { + .name = "mmc_bus", + .id = 2, + .ctrlbit = S3C_CLKCON_SCLK_MMC2, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_MMC2_SHIFT, + .mask = S3C6400_CLKSRC_MMC2_MASK, + .sources = &clkset_spi_mmc, + .divider_shift = S3C6400_CLKDIV1_MMC2_SHIFT, + .reg_divider = S3C_CLK_DIV1, + }, { + .clk = { + .name = "usb-bus-host", + .id = -1, + .ctrlbit = S3C_CLKCON_SCLK_UHOST, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_UHOST_SHIFT, + .mask = S3C6400_CLKSRC_UHOST_MASK, + .sources = &clkset_uhost, + .divider_shift = S3C6400_CLKDIV1_UHOST_SHIFT, + .reg_divider = S3C_CLK_DIV1, + }, { + .clk = { + .name = "uclk1", + .id = -1, + .ctrlbit = S3C_CLKCON_SCLK_UART, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_UART_SHIFT, + .mask = S3C6400_CLKSRC_UART_MASK, + .sources = &clkset_uart, + .divider_shift = S3C6400_CLKDIV2_UART_SHIFT, + .reg_divider = S3C_CLK_DIV2, + }, { +/* Where does UCLK0 come from? */ + .clk = { + .name = "spi-bus", + .id = 0, + .ctrlbit = S3C_CLKCON_SCLK_SPI0, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_SPI0_SHIFT, + .mask = S3C6400_CLKSRC_SPI0_MASK, + .sources = &clkset_spi_mmc, + .divider_shift = S3C6400_CLKDIV2_SPI0_SHIFT, + .reg_divider = S3C_CLK_DIV2, + }, { + .clk = { + .name = "spi-bus", + .id = 1, + .ctrlbit = S3C_CLKCON_SCLK_SPI1, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_SPI1_SHIFT, + .mask = S3C6400_CLKSRC_SPI1_MASK, + .sources = &clkset_spi_mmc, + .divider_shift = S3C6400_CLKDIV2_SPI1_SHIFT, + .reg_divider = S3C_CLK_DIV2, + }, { + .clk = { + .name = "audio-bus", + .id = 0, + .ctrlbit = S3C_CLKCON_SCLK_AUDIO0, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_AUDIO0_SHIFT, + .mask = S3C6400_CLKSRC_AUDIO0_MASK, + .sources = &clkset_audio0, + .divider_shift = S3C6400_CLKDIV2_AUDIO0_SHIFT, + .reg_divider = S3C_CLK_DIV2, + }, { + .clk = { + .name = "audio-bus", + .id = 1, + .ctrlbit = S3C_CLKCON_SCLK_AUDIO1, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_AUDIO1_SHIFT, + .mask = S3C6400_CLKSRC_AUDIO1_MASK, + .sources = &clkset_audio1, + .divider_shift = S3C6400_CLKDIV2_AUDIO1_SHIFT, + .reg_divider = S3C_CLK_DIV2, + }, { + .clk = { + .name = "irda-bus", + .id = 0, + .ctrlbit = S3C_CLKCON_SCLK_IRDA, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = S3C6400_CLKSRC_IRDA_SHIFT, + .mask = S3C6400_CLKSRC_IRDA_MASK, + .sources = &clkset_irda, + .divider_shift = S3C6400_CLKDIV2_IRDA_SHIFT, + .reg_divider = S3C_CLK_DIV2, + }, { + .clk = { + .name = "camera", + .id = -1, + .ctrlbit = S3C_CLKCON_SCLK_CAM, + .enable = s3c64xx_sclk_ctrl, + }, + .shift = 0, + .mask = 0, + .sources = &clkset_camif, + .divider_shift = S3C6400_CLKDIV0_CAM_SHIFT, + .reg_divider = S3C_CLK_DIV0, }, - .shift = 0, - .mask = 0, - .sources = &clkset_camif, - .divider_shift = S3C6400_CLKDIV0_CAM_SHIFT, - .reg_divider = S3C_CLK_DIV0, }; /* Clock initialisation code */ @@ -566,17 +548,6 @@ static struct clksrc_clk *init_parents[] = { &clk_mout_apll, &clk_mout_epll, &clk_mout_mpll, - &clk_mmc0, - &clk_mmc1, - &clk_mmc2, - &clk_usbhost, - &clk_uart_uclk1, - &clk_spi0, - &clk_spi1, - &clk_audio0, - &clk_audio1, - &clk_irda, - &clk_camif, }; static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk) @@ -593,11 +564,6 @@ static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk) return; } - clk->clk.get_rate = s3c64xx_getrate_clksrc; - clk->clk.set_rate = s3c64xx_setrate_clksrc; - clk->clk.set_parent = s3c64xx_setparent_clksrc; - clk->clk.round_rate = s3c64xx_roundrate_clksrc; - clk->clk.parent = srcs->sources[clksrc]; printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", @@ -664,6 +630,9 @@ void __init_or_cpufreq s3c6400_setup_clocks(void) for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) s3c6400_set_clksrc(init_parents[ptr]); + + for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) + s3c6400_set_clksrc(&clksrcs[ptr]); } static struct clk *clks[] __initdata = { @@ -674,17 +643,6 @@ static struct clk *clks[] __initdata = { &clk_mout_epll.clk, &clk_mout_mpll.clk, &clk_dout_mpll, - &clk_mmc0.clk, - &clk_mmc1.clk, - &clk_mmc2.clk, - &clk_usbhost.clk, - &clk_uart_uclk1.clk, - &clk_spi0.clk, - &clk_spi1.clk, - &clk_audio0.clk, - &clk_audio1.clk, - &clk_irda.clk, - &clk_camif.clk, &clk_arm, }; @@ -716,4 +674,20 @@ void __init s3c6400_register_clocks(unsigned armclk_divlimit) clkp->name, ret); } } + + for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) { + clkp = &clksrcs[ptr].clk; + + /* all clksrc clocks have these */ + clkp->get_rate = s3c64xx_getrate_clksrc; + clkp->set_rate = s3c64xx_setrate_clksrc; + clkp->set_parent = s3c64xx_setparent_clksrc; + clkp->round_rate = s3c64xx_roundrate_clksrc; + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } } -- cgit v0.10.2 From aa9ad6ad9c16e1daff41792c485f46e601a5af33 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 1 Dec 2009 01:24:34 +0000 Subject: ARM: SAMSUNG: Add core clock implementation for clksrc based clocks Add a core for the clksrc clock implementation, which is found in many of the newer Samsung SoCs into plat-samsung. Signed-off-by: Harald Welte [ben-linux@fluff.org: split from original patch to make change smaller] [ben-linux@fluff.org: split clk and clksrc changes] [ben-linux@fluff.org: moved to plat-samsung from plat-s3c] [ben-linux@fluff.org: re-wrote headers after splits] [ben-linux@fluff.org: added better documentation to headers] Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 486a0d6..e3ae68472 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -13,5 +13,10 @@ config PLAT_SAMSUNG if PLAT_SAMSUNG +config SAMSUNG_CLKSRC + bool + help + Select the clock code for the clksrc implementation + used by newer systems such as the S3C64XX. endif diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 4478b9f..ce736ce 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -9,3 +9,4 @@ obj-m := obj-n := dummy.o obj- := +obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c new file mode 100644 index 0000000..5872f0b --- /dev/null +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -0,0 +1,177 @@ +/* linux/arch/arm/plat-samsung/clock-clksrc.c + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * 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 + +static inline struct clksrc_clk *to_clksrc(struct clk *clk) +{ + return container_of(clk, struct clksrc_clk, clk); +} + +static inline u32 bit_mask(u32 shift, u32 nr_bits) +{ + u32 mask = 0xffffffff >> (32 - nr_bits); + + return mask << shift; +} + +static unsigned long s3c_getrate_clksrc(struct clk *clk) +{ + struct clksrc_clk *sclk = to_clksrc(clk); + unsigned long rate = clk_get_rate(clk->parent); + u32 clkdiv = __raw_readl(sclk->reg_div.reg); + u32 mask = bit_mask(sclk->reg_div.shift, sclk->reg_div.size); + + clkdiv &= mask; + clkdiv >>= sclk->reg_div.shift; + clkdiv++; + + rate /= clkdiv; + return rate; +} + +static int s3c_setrate_clksrc(struct clk *clk, unsigned long rate) +{ + struct clksrc_clk *sclk = to_clksrc(clk); + void __iomem *reg = sclk->reg_div.reg; + unsigned int div; + u32 mask = bit_mask(sclk->reg_div.shift, sclk->reg_div.size); + u32 val; + + rate = clk_round_rate(clk, rate); + div = clk_get_rate(clk->parent) / rate; + if (div > 16) + return -EINVAL; + + val = __raw_readl(reg); + val &= ~mask; + val |= (div - 1) << sclk->reg_div.shift; + __raw_writel(val, reg); + + return 0; +} + +static int s3c_setparent_clksrc(struct clk *clk, struct clk *parent) +{ + struct clksrc_clk *sclk = to_clksrc(clk); + struct clksrc_sources *srcs = sclk->sources; + u32 clksrc = __raw_readl(sclk->reg_src.reg); + u32 mask = bit_mask(sclk->reg_src.shift, sclk->reg_src.size); + int src_nr = -1; + int ptr; + + for (ptr = 0; ptr < srcs->nr_sources; ptr++) + if (srcs->sources[ptr] == parent) { + src_nr = ptr; + break; + } + + if (src_nr >= 0 && sclk->reg_src.reg) { + clk->parent = parent; + + clksrc &= ~mask; + clksrc |= src_nr << sclk->reg_src.shift; + + __raw_writel(clksrc, sclk->reg_src.reg); + return 0; + } + + return -EINVAL; +} + +static unsigned long s3c_roundrate_clksrc(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + int div; + + if (rate >= parent_rate) + rate = parent_rate; + else { + div = parent_rate / rate; + if (parent_rate % rate) + div++; + + if (div == 0) + div = 1; + if (div > 16) + div = 16; + + rate = parent_rate / div; + } + + return rate; +} + +/* Clock initialisation code */ + +void __init_or_cpufreq s3c_set_clksrc(struct clksrc_clk *clk) +{ + struct clksrc_sources *srcs = clk->sources; + u32 mask = bit_mask(clk->reg_src.shift, clk->reg_src.size); + u32 clksrc = 0; + + if (clk->reg_src.reg) + clksrc = __raw_readl(clk->reg_src.reg); + + clksrc &= mask; + clksrc >>= clk->reg_src.shift; + + if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) { + printk(KERN_ERR "%s: bad source %d\n", + clk->clk.name, clksrc); + return; + } + + clk->clk.parent = srcs->sources[clksrc]; + + printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", + clk->clk.name, clk->clk.parent->name, clksrc, + clk_get_rate(&clk->clk)); +} + +void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) +{ + int ret; + + for (; size > 0; size--, clksrc++) { + /* fill in the default functions */ + if (!clksrc->clk.set_parent) + clksrc->clk.set_parent = s3c_setparent_clksrc; + if (!clksrc->clk.get_rate) + clksrc->clk.get_rate = s3c_getrate_clksrc; + if (!clksrc->clk.set_rate) + clksrc->clk.set_rate = s3c_setrate_clksrc; + if (!clksrc->clk.round_rate) + clksrc->clk.round_rate = s3c_roundrate_clksrc; + + s3c_set_clksrc(clksrc); + + ret = s3c24xx_register_clock(&clksrc->clk); + + if (ret < 0) { + printk(KERN_ERR "%s: failed to register %s (%d)\n", + __func__, clksrc->clk.name, ret); + } + } +} diff --git a/arch/arm/plat-samsung/include/plat/clock-clksrc.h b/arch/arm/plat-samsung/include/plat/clock-clksrc.h new file mode 100644 index 0000000..283dfa0 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/clock-clksrc.h @@ -0,0 +1,75 @@ +/* linux/arch/arm/plat-samsung/include/plat/clock-clksrc.h + * + * Parts taken from arch/arm/plat-s3c64xx/clock.c + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * Copyright 2009 Ben Dooks + * Copyright 2009 Harald Welte + * + * 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. +*/ + +/** + * struct clksrc_sources - list of sources for a given clock + * @sources: array of pointers to clocks + * @nr_sources: The size of @sources + */ +struct clksrc_sources { + unsigned int nr_sources; + struct clk **sources; +}; + +/** + * struct clksrc_reg - register definition for clock control bits + * @reg: pointer to the register in virtual memory. + * @shift: the shift in bits to where the bitfield is. + * @size: the size in bits of the bitfield. + * + * This specifies the size and position of the bits we are interested + * in within the register specified by @reg. + */ +struct clksrc_reg { + void __iomem *reg; + unsigned short shift; + unsigned short size; +}; + +/** + * struct clksrc_clk - class of clock for newer style samsung devices. + * @clk: the standard clock representation + * @sources: the sources for this clock + * @reg_src: the register definition for selecting the clock's source + * @reg_div: the register definition for the clock's output divisor + * + * This clock implements the features required by the newer SoCs where + * the standard clock block provides an input mux and a post-mux divisor + * to provide the periperhal's clock. + * + * The array of @sources provides the mapping of mux position to the + * clock, and @reg_src shows the code where to modify to change the mux + * position. The @reg_div defines how to change the divider settings on + * the output. + */ +struct clksrc_clk { + struct clk clk; + struct clksrc_sources *sources; + + struct clksrc_reg reg_src; + struct clksrc_reg reg_div; +}; + +extern void s3c_set_clksrc(struct clksrc_clk *clk); + +/** + * s3c_register_clksrc() register clocks from an array of clksrc clocks + * @srcs: The array of clocks to register + * @size: The size of the @srcs array. + * + * Initialise and register the array of clocks described by @srcs. + */ +extern void s3c_register_clksrc(struct clksrc_clk *srcs, int size); -- cgit v0.10.2 From 399cae747426a6acdba8e347edef241a05a08b09 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 1 Dec 2009 01:24:35 +0000 Subject: ARM: S3C64XX: Use new clock-clksrc.c code for clocks. Move the s3c6400-clock.c implementation over to use the new common plat-samsung based clock-clksrc.c. Note, this does not delete the clocks definitions that are now unused in the regs-clock.h to reduce the quantity of change in this commit. Based on original patches by Harald Welte. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig index e6da87a..bec1224 100644 --- a/arch/arm/plat-s3c64xx/Kconfig +++ b/arch/arm/plat-s3c64xx/Kconfig @@ -13,6 +13,7 @@ config PLAT_S3C64XX select ARM_VIC select NO_IOPORT select ARCH_REQUIRE_GPIOLIB + select SAMSUNG_CLKSRC select S3C_GPIO_TRACK select S3C_GPIO_PULL_UPDOWN select S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c index 6fde910..20af0c2 100644 --- a/arch/arm/plat-s3c64xx/s3c6400-clock.c +++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -48,22 +49,6 @@ static struct clk clk_ext_xtal_mux = { #define clk_fout_mpll clk_mpll #define clk_fout_epll clk_epll -struct clk_sources { - unsigned int nr_sources; - struct clk **sources; -}; - -struct clksrc_clk { - struct clk clk; - unsigned int mask; - unsigned int shift; - - struct clk_sources *sources; - - unsigned int divider_shift; - void __iomem *reg_divider; -}; - static struct clk clk_fout_apll = { .name = "fout_apll", .id = -1, @@ -74,7 +59,7 @@ static struct clk *clk_src_apll_list[] = { [1] = &clk_fout_apll, }; -static struct clk_sources clk_src_apll = { +static struct clksrc_sources clk_src_apll = { .sources = clk_src_apll_list, .nr_sources = ARRAY_SIZE(clk_src_apll_list), }; @@ -84,8 +69,7 @@ static struct clksrc_clk clk_mout_apll = { .name = "mout_apll", .id = -1, }, - .shift = S3C6400_CLKSRC_APLL_MOUT_SHIFT, - .mask = S3C6400_CLKSRC_APLL_MOUT, + .reg_src = { S3C_CLK_SRC, 0, 1 }, .sources = &clk_src_apll, }; @@ -94,7 +78,7 @@ static struct clk *clk_src_epll_list[] = { [1] = &clk_fout_epll, }; -static struct clk_sources clk_src_epll = { +static struct clksrc_sources clk_src_epll = { .sources = clk_src_epll_list, .nr_sources = ARRAY_SIZE(clk_src_epll_list), }; @@ -104,8 +88,7 @@ static struct clksrc_clk clk_mout_epll = { .name = "mout_epll", .id = -1, }, - .shift = S3C6400_CLKSRC_EPLL_MOUT_SHIFT, - .mask = S3C6400_CLKSRC_EPLL_MOUT, + .reg_src = { S3C_CLK_SRC, 2, 1 }, .sources = &clk_src_epll, }; @@ -114,7 +97,7 @@ static struct clk *clk_src_mpll_list[] = { [1] = &clk_fout_mpll, }; -static struct clk_sources clk_src_mpll = { +static struct clksrc_sources clk_src_mpll = { .sources = clk_src_mpll_list, .nr_sources = ARRAY_SIZE(clk_src_mpll_list), }; @@ -124,8 +107,7 @@ static struct clksrc_clk clk_mout_mpll = { .name = "mout_mpll", .id = -1, }, - .shift = S3C6400_CLKSRC_MPLL_MOUT_SHIFT, - .mask = S3C6400_CLKSRC_MPLL_MOUT, + .reg_src = { S3C_CLK_SRC, 1, 1 }, .sources = &clk_src_mpll, }; @@ -214,7 +196,7 @@ static struct clk *clkset_spi_mmc_list[] = { &clk_27m, }; -static struct clk_sources clkset_spi_mmc = { +static struct clksrc_sources clkset_spi_mmc = { .sources = clkset_spi_mmc_list, .nr_sources = ARRAY_SIZE(clkset_spi_mmc_list), }; @@ -226,7 +208,7 @@ static struct clk *clkset_irda_list[] = { &clk_27m, }; -static struct clk_sources clkset_irda = { +static struct clksrc_sources clkset_irda = { .sources = clkset_irda_list, .nr_sources = ARRAY_SIZE(clkset_irda_list), }; @@ -238,7 +220,7 @@ static struct clk *clkset_uart_list[] = { NULL }; -static struct clk_sources clkset_uart = { +static struct clksrc_sources clkset_uart = { .sources = clkset_uart_list, .nr_sources = ARRAY_SIZE(clkset_uart_list), }; @@ -250,7 +232,7 @@ static struct clk *clkset_uhost_list[] = { &clk_fin_epll, }; -static struct clk_sources clkset_uhost = { +static struct clksrc_sources clkset_uhost = { .sources = clkset_uhost_list, .nr_sources = ARRAY_SIZE(clkset_uhost_list), }; @@ -265,94 +247,6 @@ static struct clk_sources clkset_uhost = { * have a common parent divisor so are not included here. */ -static inline struct clksrc_clk *to_clksrc(struct clk *clk) -{ - return container_of(clk, struct clksrc_clk, clk); -} - -static unsigned long s3c64xx_getrate_clksrc(struct clk *clk) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - unsigned long rate = clk_get_rate(clk->parent); - u32 clkdiv = __raw_readl(sclk->reg_divider); - - clkdiv >>= sclk->divider_shift; - clkdiv &= 0xf; - clkdiv++; - - rate /= clkdiv; - return rate; -} - -static int s3c64xx_setrate_clksrc(struct clk *clk, unsigned long rate) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - void __iomem *reg = sclk->reg_divider; - unsigned int div; - u32 val; - - rate = clk_round_rate(clk, rate); - div = clk_get_rate(clk->parent) / rate; - if (div > 16) - return -EINVAL; - - val = __raw_readl(reg); - val &= ~(0xf << sclk->divider_shift); - val |= (div - 1) << sclk->divider_shift; - __raw_writel(val, reg); - - return 0; -} - -static int s3c64xx_setparent_clksrc(struct clk *clk, struct clk *parent) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - struct clk_sources *srcs = sclk->sources; - u32 clksrc = __raw_readl(S3C_CLK_SRC); - int src_nr = -1; - int ptr; - - for (ptr = 0; ptr < srcs->nr_sources; ptr++) - if (srcs->sources[ptr] == parent) { - src_nr = ptr; - break; - } - - if (src_nr >= 0) { - clksrc &= ~sclk->mask; - clksrc |= src_nr << sclk->shift; - - __raw_writel(clksrc, S3C_CLK_SRC); - - clk->parent = parent; - return 0; - } - - return -EINVAL; -} - -static unsigned long s3c64xx_roundrate_clksrc(struct clk *clk, - unsigned long rate) -{ - unsigned long parent_rate = clk_get_rate(clk->parent); - int div; - - if (rate > parent_rate) - rate = parent_rate; - else { - div = parent_rate / rate; - - if (div == 0) - div = 1; - if (div > 16) - div = 16; - - rate = parent_rate / div; - } - - return rate; -} - /* clocks that feed other parts of the clock source tree */ static struct clk clk_iis_cd0 = { @@ -378,7 +272,7 @@ static struct clk *clkset_audio0_list[] = { [4] = &clk_pcm_cd, }; -static struct clk_sources clkset_audio0 = { +static struct clksrc_sources clkset_audio0 = { .sources = clkset_audio0_list, .nr_sources = ARRAY_SIZE(clkset_audio0_list), }; @@ -391,7 +285,7 @@ static struct clk *clkset_audio1_list[] = { [4] = &clk_pcm_cd, }; -static struct clk_sources clkset_audio1 = { +static struct clksrc_sources clkset_audio1 = { .sources = clkset_audio1_list, .nr_sources = ARRAY_SIZE(clkset_audio1_list), }; @@ -400,7 +294,7 @@ static struct clk *clkset_camif_list[] = { &clk_h2, }; -static struct clk_sources clkset_camif = { +static struct clksrc_sources clkset_camif = { .sources = clkset_camif_list, .nr_sources = ARRAY_SIZE(clkset_camif_list), }; @@ -413,11 +307,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_MMC0, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_MMC0_SHIFT, - .mask = S3C6400_CLKSRC_MMC0_MASK, + .reg_src = { S3C_CLK_SRC, 18, 2 }, + .reg_div = { S3C_CLK_DIV1, 0, 4 }, .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC0_SHIFT, - .reg_divider = S3C_CLK_DIV1, }, { .clk = { .name = "mmc_bus", @@ -425,11 +317,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_MMC1, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_MMC1_SHIFT, - .mask = S3C6400_CLKSRC_MMC1_MASK, + .reg_src = { S3C_CLK_SRC, 20, 2 }, + .reg_div = { S3C_CLK_DIV1, 4, 4 }, .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC1_SHIFT, - .reg_divider = S3C_CLK_DIV1, }, { .clk = { .name = "mmc_bus", @@ -437,11 +327,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_MMC2, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_MMC2_SHIFT, - .mask = S3C6400_CLKSRC_MMC2_MASK, + .reg_src = { S3C_CLK_SRC, 22, 2 }, + .reg_div = { S3C_CLK_DIV1, 8, 4 }, .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV1_MMC2_SHIFT, - .reg_divider = S3C_CLK_DIV1, }, { .clk = { .name = "usb-bus-host", @@ -449,11 +337,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_UHOST, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_UHOST_SHIFT, - .mask = S3C6400_CLKSRC_UHOST_MASK, + .reg_src = { S3C_CLK_SRC, 5, 2 }, + .reg_div = { S3C_CLK_DIV1, 20, 4 }, .sources = &clkset_uhost, - .divider_shift = S3C6400_CLKDIV1_UHOST_SHIFT, - .reg_divider = S3C_CLK_DIV1, }, { .clk = { .name = "uclk1", @@ -461,11 +347,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_UART, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_UART_SHIFT, - .mask = S3C6400_CLKSRC_UART_MASK, + .reg_src = { S3C_CLK_SRC, 13, 1 }, + .reg_div = { S3C_CLK_DIV2, 16, 4 }, .sources = &clkset_uart, - .divider_shift = S3C6400_CLKDIV2_UART_SHIFT, - .reg_divider = S3C_CLK_DIV2, }, { /* Where does UCLK0 come from? */ .clk = { @@ -474,11 +358,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_SPI0, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_SPI0_SHIFT, - .mask = S3C6400_CLKSRC_SPI0_MASK, + .reg_src = { S3C_CLK_SRC, 14, 2 }, + .reg_div = { S3C_CLK_DIV2, 0, 4 }, .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV2_SPI0_SHIFT, - .reg_divider = S3C_CLK_DIV2, }, { .clk = { .name = "spi-bus", @@ -486,11 +368,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_SPI1, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_SPI1_SHIFT, - .mask = S3C6400_CLKSRC_SPI1_MASK, + .reg_src = { S3C_CLK_SRC, 16, 2 }, + .reg_div = { S3C_CLK_DIV2, 4, 4 }, .sources = &clkset_spi_mmc, - .divider_shift = S3C6400_CLKDIV2_SPI1_SHIFT, - .reg_divider = S3C_CLK_DIV2, }, { .clk = { .name = "audio-bus", @@ -498,11 +378,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_AUDIO0, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_AUDIO0_SHIFT, - .mask = S3C6400_CLKSRC_AUDIO0_MASK, + .reg_src = { S3C_CLK_SRC, 7, 3 }, + .reg_div = { S3C_CLK_DIV2, 8, 4 }, .sources = &clkset_audio0, - .divider_shift = S3C6400_CLKDIV2_AUDIO0_SHIFT, - .reg_divider = S3C_CLK_DIV2, }, { .clk = { .name = "audio-bus", @@ -510,11 +388,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_AUDIO1, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_AUDIO1_SHIFT, - .mask = S3C6400_CLKSRC_AUDIO1_MASK, + .reg_src = { S3C_CLK_SRC, 10, 3 }, + .reg_div = { S3C_CLK_DIV2, 12, 4 }, .sources = &clkset_audio1, - .divider_shift = S3C6400_CLKDIV2_AUDIO1_SHIFT, - .reg_divider = S3C_CLK_DIV2, }, { .clk = { .name = "irda-bus", @@ -522,11 +398,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_IRDA, .enable = s3c64xx_sclk_ctrl, }, - .shift = S3C6400_CLKSRC_IRDA_SHIFT, - .mask = S3C6400_CLKSRC_IRDA_MASK, + .reg_src = { S3C_CLK_SRC, 24, 2 }, + .reg_div = { S3C_CLK_DIV2, 20, 4 }, .sources = &clkset_irda, - .divider_shift = S3C6400_CLKDIV2_IRDA_SHIFT, - .reg_divider = S3C_CLK_DIV2, }, { .clk = { .name = "camera", @@ -534,11 +408,9 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_CAM, .enable = s3c64xx_sclk_ctrl, }, - .shift = 0, - .mask = 0, + .reg_div = { S3C_CLK_DIV0, 20, 4 }, + .reg_src = { NULL, 0, 0 }, .sources = &clkset_camif, - .divider_shift = S3C6400_CLKDIV0_CAM_SHIFT, - .reg_divider = S3C_CLK_DIV0, }, }; @@ -550,27 +422,6 @@ static struct clksrc_clk *init_parents[] = { &clk_mout_mpll, }; -static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk) -{ - struct clk_sources *srcs = clk->sources; - u32 clksrc = __raw_readl(S3C_CLK_SRC); - - clksrc &= clk->mask; - clksrc >>= clk->shift; - - if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) { - printk(KERN_ERR "%s: bad source %d\n", - clk->clk.name, clksrc); - return; - } - - clk->clk.parent = srcs->sources[clksrc]; - - printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", - clk->clk.name, clk->clk.parent->name, clksrc, - clk_get_rate(&clk->clk)); -} - #define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1) void __init_or_cpufreq s3c6400_setup_clocks(void) @@ -629,10 +480,10 @@ void __init_or_cpufreq s3c6400_setup_clocks(void) clk_f.rate = fclk; for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) - s3c6400_set_clksrc(init_parents[ptr]); + s3c_set_clksrc(init_parents[ptr]); for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) - s3c6400_set_clksrc(&clksrcs[ptr]); + s3c_set_clksrc(&clksrcs[ptr]); } static struct clk *clks[] __initdata = { @@ -675,19 +526,5 @@ void __init s3c6400_register_clocks(unsigned armclk_divlimit) } } - for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) { - clkp = &clksrcs[ptr].clk; - - /* all clksrc clocks have these */ - clkp->get_rate = s3c64xx_getrate_clksrc; - clkp->set_rate = s3c64xx_setrate_clksrc; - clkp->set_parent = s3c64xx_setparent_clksrc; - clkp->round_rate = s3c64xx_roundrate_clksrc; - - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } + s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs)); } -- cgit v0.10.2 From 13bbd88504bfa0d205fa4121322869d8d7e083d0 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 1 Dec 2009 01:24:36 +0000 Subject: ARM: S3C64XX: Remove unused clock definitions from clock header Clean out the definitions we are no longer using after the new clock code updates. Signed-off-by: Harald Welte [ben-linux@fluff.org: split from initial patch provided] Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h index ff46e7f..3ef6274 100644 --- a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h +++ b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h @@ -35,14 +35,6 @@ #define S3C_MEM0_GATE S3C_CLKREG(0x3C) /* CLKDIV0 */ -#define S3C6400_CLKDIV0_MFC_MASK (0xf << 28) -#define S3C6400_CLKDIV0_MFC_SHIFT (28) -#define S3C6400_CLKDIV0_JPEG_MASK (0xf << 24) -#define S3C6400_CLKDIV0_JPEG_SHIFT (24) -#define S3C6400_CLKDIV0_CAM_MASK (0xf << 20) -#define S3C6400_CLKDIV0_CAM_SHIFT (20) -#define S3C6400_CLKDIV0_SECURITY_MASK (0x3 << 18) -#define S3C6400_CLKDIV0_SECURITY_SHIFT (18) #define S3C6400_CLKDIV0_PCLK_MASK (0xf << 12) #define S3C6400_CLKDIV0_PCLK_SHIFT (12) #define S3C6400_CLKDIV0_HCLK2_MASK (0x7 << 9) @@ -51,42 +43,11 @@ #define S3C6400_CLKDIV0_HCLK_SHIFT (8) #define S3C6400_CLKDIV0_MPLL_MASK (0x1 << 4) #define S3C6400_CLKDIV0_MPLL_SHIFT (4) + #define S3C6400_CLKDIV0_ARM_MASK (0x7 << 0) #define S3C6410_CLKDIV0_ARM_MASK (0xf << 0) #define S3C6400_CLKDIV0_ARM_SHIFT (0) -/* CLKDIV1 */ -#define S3C6410_CLKDIV1_FIMC_MASK (0xf << 24) -#define S3C6410_CLKDIV1_FIMC_SHIFT (24) -#define S3C6400_CLKDIV1_UHOST_MASK (0xf << 20) -#define S3C6400_CLKDIV1_UHOST_SHIFT (20) -#define S3C6400_CLKDIV1_SCALER_MASK (0xf << 16) -#define S3C6400_CLKDIV1_SCALER_SHIFT (16) -#define S3C6400_CLKDIV1_LCD_MASK (0xf << 12) -#define S3C6400_CLKDIV1_LCD_SHIFT (12) -#define S3C6400_CLKDIV1_MMC2_MASK (0xf << 8) -#define S3C6400_CLKDIV1_MMC2_SHIFT (8) -#define S3C6400_CLKDIV1_MMC1_MASK (0xf << 4) -#define S3C6400_CLKDIV1_MMC1_SHIFT (4) -#define S3C6400_CLKDIV1_MMC0_MASK (0xf << 0) -#define S3C6400_CLKDIV1_MMC0_SHIFT (0) - -/* CLKDIV2 */ -#define S3C6410_CLKDIV2_AUDIO2_MASK (0xf << 24) -#define S3C6410_CLKDIV2_AUDIO2_SHIFT (24) -#define S3C6400_CLKDIV2_IRDA_MASK (0xf << 20) -#define S3C6400_CLKDIV2_IRDA_SHIFT (20) -#define S3C6400_CLKDIV2_UART_MASK (0xf << 16) -#define S3C6400_CLKDIV2_UART_SHIFT (16) -#define S3C6400_CLKDIV2_AUDIO1_MASK (0xf << 12) -#define S3C6400_CLKDIV2_AUDIO1_SHIFT (12) -#define S3C6400_CLKDIV2_AUDIO0_MASK (0xf << 8) -#define S3C6400_CLKDIV2_AUDIO0_SHIFT (8) -#define S3C6400_CLKDIV2_SPI1_MASK (0xf << 4) -#define S3C6400_CLKDIV2_SPI1_SHIFT (4) -#define S3C6400_CLKDIV2_SPI0_MASK (0xf << 0) -#define S3C6400_CLKDIV2_SPI0_SHIFT (0) - /* HCLK GATE Registers */ #define S3C_CLKCON_HCLK_3DSE (1<<31) #define S3C_CLKCON_HCLK_UHOST (1<<29) @@ -192,34 +153,4 @@ #define S3C6400_CLKSRC_EPLL_MOUT_SHIFT (2) #define S3C6400_CLKSRC_MFC (1 << 4) -#define S3C6410_CLKSRC_TV27_MASK (0x1 << 31) -#define S3C6410_CLKSRC_TV27_SHIFT (31) -#define S3C6410_CLKSRC_DAC27_MASK (0x1 << 30) -#define S3C6410_CLKSRC_DAC27_SHIFT (30) -#define S3C6400_CLKSRC_SCALER_MASK (0x3 << 28) -#define S3C6400_CLKSRC_SCALER_SHIFT (28) -#define S3C6400_CLKSRC_LCD_MASK (0x3 << 26) -#define S3C6400_CLKSRC_LCD_SHIFT (26) -#define S3C6400_CLKSRC_IRDA_MASK (0x3 << 24) -#define S3C6400_CLKSRC_IRDA_SHIFT (24) -#define S3C6400_CLKSRC_MMC2_MASK (0x3 << 22) -#define S3C6400_CLKSRC_MMC2_SHIFT (22) -#define S3C6400_CLKSRC_MMC1_MASK (0x3 << 20) -#define S3C6400_CLKSRC_MMC1_SHIFT (20) -#define S3C6400_CLKSRC_MMC0_MASK (0x3 << 18) -#define S3C6400_CLKSRC_MMC0_SHIFT (18) -#define S3C6400_CLKSRC_SPI1_MASK (0x3 << 16) -#define S3C6400_CLKSRC_SPI1_SHIFT (16) -#define S3C6400_CLKSRC_SPI0_MASK (0x3 << 14) -#define S3C6400_CLKSRC_SPI0_SHIFT (14) -#define S3C6400_CLKSRC_UART_MASK (0x1 << 13) -#define S3C6400_CLKSRC_UART_SHIFT (13) -#define S3C6400_CLKSRC_AUDIO1_MASK (0x7 << 10) -#define S3C6400_CLKSRC_AUDIO1_SHIFT (10) -#define S3C6400_CLKSRC_AUDIO0_MASK (0x7 << 7) -#define S3C6400_CLKSRC_AUDIO0_SHIFT (7) -#define S3C6400_CLKSRC_UHOST_MASK (0x3 << 5) -#define S3C6400_CLKSRC_UHOST_SHIFT (5) - - #endif /* _PLAT_REGS_CLOCK_H */ -- cgit v0.10.2 From b3bf41be06634d69959a68a2b53e1ffc92f0d103 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 1 Dec 2009 01:24:37 +0000 Subject: ARM: SAMSUNG: Reduce size of struct clk. Reduce the size of struct clk by 12 bytes and make defining clocks with common implementation functions easier by moving the set_rate, get_rate, round_rate and set_parent calls into a new structure called 'struct clk_ops' and using that instead. This change does make a few clocks larger as they need their own clk_ops, but this is outweighed by the number of clocks with either no ops or having a common set of ops. Update all the users of this. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c2412/clock.c b/arch/arm/mach-s3c2412/clock.c index a037df5..0c0505b 100644 --- a/arch/arm/mach-s3c2412/clock.c +++ b/arch/arm/mach-s3c2412/clock.c @@ -124,7 +124,9 @@ static struct clk clk_usysclk = { .name = "usysclk", .id = -1, .parent = &clk_xtal, - .set_parent = s3c2412_setparent_usysclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2412_setparent_usysclk, + }, }; static struct clk clk_mrefclk = { @@ -199,10 +201,12 @@ static int s3c2412_setrate_usbsrc(struct clk *clk, unsigned long rate) static struct clk clk_usbsrc = { .name = "usbsrc", .id = -1, - .get_rate = s3c2412_getrate_usbsrc, - .set_rate = s3c2412_setrate_usbsrc, - .round_rate = s3c2412_roundrate_usbsrc, - .set_parent = s3c2412_setparent_usbsrc, + .ops = &(struct clk_ops) { + .get_rate = s3c2412_getrate_usbsrc, + .set_rate = s3c2412_setrate_usbsrc, + .round_rate = s3c2412_roundrate_usbsrc, + .set_parent = s3c2412_setparent_usbsrc, + }, }; static int s3c2412_setparent_msysclk(struct clk *clk, struct clk *parent) @@ -225,7 +229,9 @@ static int s3c2412_setparent_msysclk(struct clk *clk, struct clk *parent) static struct clk clk_msysclk = { .name = "msysclk", .id = -1, - .set_parent = s3c2412_setparent_msysclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2412_setparent_msysclk, + }, }; static int s3c2412_setparent_armclk(struct clk *clk, struct clk *parent) @@ -264,7 +270,9 @@ static struct clk clk_armclk = { .name = "armclk", .id = -1, .parent = &clk_msysclk, - .set_parent = s3c2412_setparent_armclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2412_setparent_armclk, + }, }; /* these next clocks have an divider immediately after them, @@ -337,10 +345,12 @@ static int s3c2412_setrate_uart(struct clk *clk, unsigned long rate) static struct clk clk_uart = { .name = "uartclk", .id = -1, - .get_rate = s3c2412_getrate_uart, - .set_rate = s3c2412_setrate_uart, - .set_parent = s3c2412_setparent_uart, - .round_rate = s3c2412_roundrate_clksrc, + .ops = &(struct clk_ops) { + .get_rate = s3c2412_getrate_uart, + .set_rate = s3c2412_setrate_uart, + .set_parent = s3c2412_setparent_uart, + .round_rate = s3c2412_roundrate_clksrc, + }, }; static int s3c2412_setparent_i2s(struct clk *clk, struct clk *parent) @@ -388,10 +398,12 @@ static int s3c2412_setrate_i2s(struct clk *clk, unsigned long rate) static struct clk clk_i2s = { .name = "i2sclk", .id = -1, - .get_rate = s3c2412_getrate_i2s, - .set_rate = s3c2412_setrate_i2s, - .set_parent = s3c2412_setparent_i2s, - .round_rate = s3c2412_roundrate_clksrc, + .ops = &(struct clk_ops) { + .get_rate = s3c2412_getrate_i2s, + .set_rate = s3c2412_setrate_i2s, + .set_parent = s3c2412_setparent_i2s, + .round_rate = s3c2412_roundrate_clksrc, + }, }; static int s3c2412_setparent_cam(struct clk *clk, struct clk *parent) @@ -438,10 +450,12 @@ static int s3c2412_setrate_cam(struct clk *clk, unsigned long rate) static struct clk clk_cam = { .name = "camif-upll", /* same as 2440 name */ .id = -1, - .get_rate = s3c2412_getrate_cam, - .set_rate = s3c2412_setrate_cam, - .set_parent = s3c2412_setparent_cam, - .round_rate = s3c2412_roundrate_clksrc, + .ops = &(struct clk_ops) { + .get_rate = s3c2412_getrate_cam, + .set_rate = s3c2412_setrate_cam, + .set_parent = s3c2412_setparent_cam, + .round_rate = s3c2412_roundrate_clksrc, + }, }; /* standard clock definitions */ diff --git a/arch/arm/mach-s3c2440/clock.c b/arch/arm/mach-s3c2440/clock.c index d1c29b2..3dc2426 100644 --- a/arch/arm/mach-s3c2440/clock.c +++ b/arch/arm/mach-s3c2440/clock.c @@ -98,8 +98,10 @@ static struct clk s3c2440_clk_cam = { static struct clk s3c2440_clk_cam_upll = { .name = "camif-upll", .id = -1, - .set_rate = s3c2440_camif_upll_setrate, - .round_rate = s3c2440_camif_upll_round, + .ops = &(struct clk_ops) { + .set_rate = s3c2440_camif_upll_setrate, + .round_rate = s3c2440_camif_upll_round, + }, }; static struct clk s3c2440_clk_ac97 = { diff --git a/arch/arm/mach-s3c2442/clock.c b/arch/arm/mach-s3c2442/clock.c index ea1aa1f..d9b692a 100644 --- a/arch/arm/mach-s3c2442/clock.c +++ b/arch/arm/mach-s3c2442/clock.c @@ -109,8 +109,10 @@ static struct clk s3c2442_clk_cam = { static struct clk s3c2442_clk_cam_upll = { .name = "camif-upll", .id = -1, - .set_rate = s3c2442_camif_upll_setrate, - .round_rate = s3c2442_camif_upll_round, + .ops = &(struct clk_ops) { + .set_rate = s3c2442_camif_upll_setrate, + .round_rate = s3c2442_camif_upll_round, + }, }; static int s3c2442_clk_add(struct sys_device *sysdev) diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c index 2785d69..91db4f5 100644 --- a/arch/arm/mach-s3c2443/clock.c +++ b/arch/arm/mach-s3c2443/clock.c @@ -187,7 +187,9 @@ static int s3c2443_setparent_epllref(struct clk *clk, struct clk *parent) static struct clk clk_epllref = { .name = "epllref", .id = -1, - .set_parent = s3c2443_setparent_epllref, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_epllref, + }, }; static unsigned long s3c2443_getrate_mdivclk(struct clk *clk) @@ -205,7 +207,9 @@ static struct clk clk_mdivclk = { .name = "mdivclk", .parent = &clk_mpllref, .id = -1, - .get_rate = s3c2443_getrate_mdivclk, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_mdivclk, + }, }; static int s3c2443_setparent_msysclk(struct clk *clk, struct clk *parent) @@ -232,7 +236,9 @@ static struct clk clk_msysclk = { .name = "msysclk", .parent = &clk_xtal, .id = -1, - .set_parent = s3c2443_setparent_msysclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_msysclk, + }, }; /* armdiv @@ -273,7 +279,9 @@ static int s3c2443_setparent_armclk(struct clk *clk, struct clk *parent) static struct clk clk_arm = { .name = "armclk", .id = -1, - .set_parent = s3c2443_setparent_armclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_armclk, + }, }; /* esysclk @@ -302,7 +310,9 @@ static struct clk clk_esysclk = { .name = "esysclk", .parent = &clk_epll, .id = -1, - .set_parent = s3c2443_setparent_esysclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_esysclk, + }, }; /* uartclk @@ -341,9 +351,11 @@ static struct clk clk_uart = { .name = "uartclk", .id = -1, .parent = &clk_esysclk, - .get_rate = s3c2443_getrate_uart, - .set_rate = s3c2443_setrate_uart, - .round_rate = s3c2443_roundrate_clksrc16, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_uart, + .set_rate = s3c2443_setrate_uart, + .round_rate = s3c2443_roundrate_clksrc16, + }, }; /* hsspi @@ -384,9 +396,11 @@ static struct clk clk_hsspi = { .parent = &clk_esysclk, .ctrlbit = S3C2443_SCLKCON_HSSPICLK, .enable = s3c2443_clkcon_enable_s, - .get_rate = s3c2443_getrate_hsspi, - .set_rate = s3c2443_setrate_hsspi, - .round_rate = s3c2443_roundrate_clksrc4, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_hsspi, + .set_rate = s3c2443_setrate_hsspi, + .round_rate = s3c2443_roundrate_clksrc4, + }, }; /* usbhost @@ -426,9 +440,11 @@ static struct clk clk_usb_bus_host = { .parent = &clk_esysclk, .ctrlbit = S3C2443_SCLKCON_USBHOST, .enable = s3c2443_clkcon_enable_s, - .get_rate = s3c2443_getrate_usbhost, - .set_rate = s3c2443_setrate_usbhost, - .round_rate = s3c2443_roundrate_clksrc4, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_usbhost, + .set_rate = s3c2443_setrate_usbhost, + .round_rate = s3c2443_roundrate_clksrc4, + }, }; /* clk_hsmcc_div @@ -468,9 +484,11 @@ static struct clk clk_hsmmc_div = { .name = "hsmmc-div", .id = -1, .parent = &clk_esysclk, - .get_rate = s3c2443_getrate_hsmmc_div, - .set_rate = s3c2443_setrate_hsmmc_div, - .round_rate = s3c2443_roundrate_clksrc4, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_hsmmc_div, + .set_rate = s3c2443_setrate_hsmmc_div, + .round_rate = s3c2443_roundrate_clksrc4, + }, }; static int s3c2443_setparent_hsmmc(struct clk *clk, struct clk *parent) @@ -505,7 +523,9 @@ static struct clk clk_hsmmc = { .id = -1, .parent = &clk_hsmmc_div, .enable = s3c2443_enable_hsmmc, - .set_parent = s3c2443_setparent_hsmmc, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_hsmmc, + }, }; /* i2s_eplldiv @@ -543,9 +563,11 @@ static struct clk clk_i2s_eplldiv = { .name = "i2s-eplldiv", .id = -1, .parent = &clk_esysclk, - .get_rate = s3c2443_getrate_i2s_eplldiv, - .set_rate = s3c2443_setrate_i2s_eplldiv, - .round_rate = s3c2443_roundrate_clksrc16, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_i2s_eplldiv, + .set_rate = s3c2443_setrate_i2s_eplldiv, + .round_rate = s3c2443_roundrate_clksrc16, + }, }; /* i2s-ref @@ -578,7 +600,9 @@ static struct clk clk_i2s = { .parent = &clk_i2s_eplldiv, .ctrlbit = S3C2443_SCLKCON_I2SCLK, .enable = s3c2443_clkcon_enable_s, - .set_parent = s3c2443_setparent_i2s, + .ops = &(struct clk_ops) { + .set_parent = s3c2443_setparent_i2s, + }, }; /* cam-if @@ -618,9 +642,11 @@ static struct clk clk_cam = { .parent = &clk_esysclk, .ctrlbit = S3C2443_SCLKCON_CAMCLK, .enable = s3c2443_clkcon_enable_s, - .get_rate = s3c2443_getrate_cam, - .set_rate = s3c2443_setrate_cam, - .round_rate = s3c2443_roundrate_clksrc16, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_cam, + .set_rate = s3c2443_setrate_cam, + .round_rate = s3c2443_roundrate_clksrc16, + }, }; /* display-if @@ -660,9 +686,11 @@ static struct clk clk_display = { .parent = &clk_esysclk, .ctrlbit = S3C2443_SCLKCON_DISPCLK, .enable = s3c2443_clkcon_enable_s, - .get_rate = s3c2443_getrate_display, - .set_rate = s3c2443_setrate_display, - .round_rate = s3c2443_roundrate_clksrc256, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_getrate_display, + .set_rate = s3c2443_setrate_display, + .round_rate = s3c2443_roundrate_clksrc256, + }, }; /* prediv @@ -685,7 +713,9 @@ static struct clk clk_prediv = { .name = "prediv", .id = -1, .parent = &clk_msysclk, - .get_rate = s3c2443_prediv_getrate, + .ops = &(struct clk_ops) { + .get_rate = s3c2443_prediv_getrate, + }, }; /* standard clock definitions */ diff --git a/arch/arm/plat-s3c/clock.c b/arch/arm/plat-s3c/clock.c index 619cfa8..fa91125 100644 --- a/arch/arm/plat-s3c/clock.c +++ b/arch/arm/plat-s3c/clock.c @@ -150,8 +150,8 @@ unsigned long clk_get_rate(struct clk *clk) if (clk->rate != 0) return clk->rate; - if (clk->get_rate != NULL) - return (clk->get_rate)(clk); + if (clk->ops != NULL && clk->ops->get_rate != NULL) + return (clk->ops->get_rate)(clk); if (clk->parent != NULL) return clk_get_rate(clk->parent); @@ -161,8 +161,8 @@ unsigned long clk_get_rate(struct clk *clk) long clk_round_rate(struct clk *clk, unsigned long rate) { - if (!IS_ERR(clk) && clk->round_rate) - return (clk->round_rate)(clk, rate); + if (!IS_ERR(clk) && clk->ops && clk->ops->round_rate) + return (clk->ops->round_rate)(clk, rate); return rate; } @@ -178,13 +178,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate) * the clock may have been made this way by choice. */ - WARN_ON(clk->set_rate == NULL); + WARN_ON(clk->ops == NULL); + WARN_ON(clk->ops && clk->ops->set_rate == NULL); - if (clk->set_rate == NULL) + if (clk->ops == NULL || clk->ops->set_rate == NULL) return -EINVAL; spin_lock(&clocks_lock); - ret = (clk->set_rate)(clk, rate); + ret = (clk->ops->set_rate)(clk, rate); spin_unlock(&clocks_lock); return ret; @@ -204,8 +205,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent) spin_lock(&clocks_lock); - if (clk->set_parent) - ret = (clk->set_parent)(clk, parent); + if (clk->ops && clk->ops->set_parent) + ret = (clk->ops->set_parent)(clk, parent); spin_unlock(&clocks_lock); @@ -230,6 +231,10 @@ static int clk_default_setrate(struct clk *clk, unsigned long rate) return 0; } +static struct clk_ops clk_ops_def_setrate = { + .set_rate = clk_default_setrate, +}; + struct clk clk_xtal = { .name = "xtal", .id = -1, @@ -251,7 +256,7 @@ struct clk clk_epll = { struct clk clk_mpll = { .name = "mpll", .id = -1, - .set_rate = clk_default_setrate, + .ops = &clk_ops_def_setrate, }; struct clk clk_upll = { @@ -267,7 +272,6 @@ struct clk clk_f = { .rate = 0, .parent = &clk_mpll, .ctrlbit = 0, - .set_rate = clk_default_setrate, }; struct clk clk_h = { @@ -276,7 +280,7 @@ struct clk clk_h = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .set_rate = clk_default_setrate, + .ops = &clk_ops_def_setrate, }; struct clk clk_p = { @@ -285,7 +289,7 @@ struct clk clk_p = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .set_rate = clk_default_setrate, + .ops = &clk_ops_def_setrate, }; struct clk clk_usb_bus = { @@ -296,7 +300,6 @@ struct clk clk_usb_bus = { }; - struct clk s3c24xx_uclk = { .name = "uclk", .id = -1, diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index a318215..1808fa8 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -130,20 +130,22 @@ static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) return 0; } +static struct clk_ops clk_pwm_scaler_ops = { + .get_rate = clk_pwm_scaler_get_rate, + .set_rate = clk_pwm_scaler_set_rate, + .round_rate = clk_pwm_scaler_round_rate, +}; + static struct clk clk_timer_scaler[] = { [0] = { .name = "pwm-scaler0", .id = -1, - .get_rate = clk_pwm_scaler_get_rate, - .set_rate = clk_pwm_scaler_set_rate, - .round_rate = clk_pwm_scaler_round_rate, + .ops = &clk_pwm_scaler_ops, }, [1] = { .name = "pwm-scaler1", .id = -1, - .get_rate = clk_pwm_scaler_get_rate, - .set_rate = clk_pwm_scaler_set_rate, - .round_rate = clk_pwm_scaler_round_rate, + .ops = &clk_pwm_scaler_ops, }, }; @@ -256,50 +258,46 @@ static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) return 0; } +static struct clk_ops clk_tdiv_ops = { + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, +}; + static struct pwm_tdiv_clk clk_timer_tdiv[] = { [0] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[0], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[0], }, }, [1] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[0], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[0], } }, [2] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], }, }, [3] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], }, }, [4] = { .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], }, }, }; @@ -356,31 +354,35 @@ static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) return 0; } +static struct clk_ops clk_tin_ops = { + .set_parent = clk_pwm_tin_set_parent, +}; + static struct clk clk_tin[] = { [0] = { - .name = "pwm-tin", - .id = 0, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 0, + .ops = &clk_tin_ops, }, [1] = { - .name = "pwm-tin", - .id = 1, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 1, + .ops = &clk_tin_ops, }, [2] = { - .name = "pwm-tin", - .id = 2, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 2, + .ops = &clk_tin_ops, }, [3] = { - .name = "pwm-tin", - .id = 3, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 3, + .ops = &clk_tin_ops, }, [4] = { - .name = "pwm-tin", - .id = 4, - .set_parent = clk_pwm_tin_set_parent, + .name = "pwm-tin", + .id = 4, + .ops = &clk_tin_ops, }, }; diff --git a/arch/arm/plat-s3c24xx/clock-dclk.c b/arch/arm/plat-s3c24xx/clock-dclk.c index ac061a1..cf97caa 100644 --- a/arch/arm/plat-s3c24xx/clock-dclk.c +++ b/arch/arm/plat-s3c24xx/clock-dclk.c @@ -161,14 +161,18 @@ static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) /* external clock definitions */ +static struct clk_ops dclk_ops = { + .set_parent = s3c24xx_dclk_setparent, + .set_rate = s3c24xx_set_dclk_rate, + .round_rate = s3c24xx_round_dclk_rate, +}; + struct clk s3c24xx_dclk0 = { .name = "dclk0", .id = -1, .ctrlbit = S3C2410_DCLKCON_DCLK0EN, .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, - .set_rate = s3c24xx_set_dclk_rate, - .round_rate = s3c24xx_round_dclk_rate, + .ops = &dclk_ops, }; struct clk s3c24xx_dclk1 = { @@ -176,19 +180,21 @@ struct clk s3c24xx_dclk1 = { .id = -1, .ctrlbit = S3C2410_DCLKCON_DCLK1EN, .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, - .set_rate = s3c24xx_set_dclk_rate, - .round_rate = s3c24xx_round_dclk_rate, + .ops = &dclk_ops, +}; + +static struct clk_ops clkout_ops = { + .set_parent = s3c24xx_clkout_setparent, }; struct clk s3c24xx_clkout0 = { .name = "clkout0", .id = -1, - .set_parent = s3c24xx_clkout_setparent, + .ops = &clkout_ops, }; struct clk s3c24xx_clkout1 = { .name = "clkout1", .id = -1, - .set_parent = s3c24xx_clkout_setparent, + .ops = &clkout_ops, }; diff --git a/arch/arm/plat-s3c24xx/s3c244x-clock.c b/arch/arm/plat-s3c24xx/s3c244x-clock.c index 7937109..f8d9613 100644 --- a/arch/arm/plat-s3c24xx/s3c244x-clock.c +++ b/arch/arm/plat-s3c24xx/s3c244x-clock.c @@ -68,7 +68,9 @@ static int s3c2440_setparent_armclk(struct clk *clk, struct clk *parent) static struct clk clk_arm = { .name = "armclk", .id = -1, - .set_parent = s3c2440_setparent_armclk, + .ops = &(struct clk_ops) { + .set_parent = s3c2440_setparent_armclk, + }, }; static int s3c244x_clk_add(struct sys_device *sysdev) diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c index 20af0c2..f85406a 100644 --- a/arch/arm/plat-s3c64xx/s3c6400-clock.c +++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c @@ -165,9 +165,11 @@ static struct clk clk_arm = { .name = "armclk", .id = -1, .parent = &clk_mout_apll.clk, - .get_rate = s3c64xx_clk_arm_get_rate, - .set_rate = s3c64xx_clk_arm_set_rate, - .round_rate = s3c64xx_clk_arm_round_rate, + .ops = &(struct clk_ops) { + .get_rate = s3c64xx_clk_arm_get_rate, + .set_rate = s3c64xx_clk_arm_set_rate, + .round_rate = s3c64xx_clk_arm_round_rate, + }, }; static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk) @@ -182,11 +184,15 @@ static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk) return rate; } +static struct clk_ops clk_dout_ops = { + .get_rate = s3c64xx_clk_doutmpll_get_rate, +}; + static struct clk clk_dout_mpll = { .name = "dout_mpll", .id = -1, .parent = &clk_mout_mpll.clk, - .get_rate = s3c64xx_clk_doutmpll_get_rate, + .ops = &clk_dout_ops, }; static struct clk *clkset_spi_mmc_list[] = { diff --git a/arch/arm/plat-s5pc1xx/clock.c b/arch/arm/plat-s5pc1xx/clock.c index 26c21d8..2f4d8d4 100644 --- a/arch/arm/plat-s5pc1xx/clock.c +++ b/arch/arm/plat-s5pc1xx/clock.c @@ -70,6 +70,10 @@ static int clk_default_setrate(struct clk *clk, unsigned long rate) return 0; } +static struct clk_ops clk_ops_default_setrate = { + .set_rate = clk_default_setrate, +}; + static int clk_dummy_enable(struct clk *clk, int enable) { return 0; @@ -81,8 +85,8 @@ struct clk clk_hd0 = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .set_rate = clk_default_setrate, .enable = clk_dummy_enable, + .ops = &clk_ops_default_setrate, }; struct clk clk_pd0 = { @@ -91,7 +95,7 @@ struct clk clk_pd0 = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .set_rate = clk_default_setrate, + .ops = &clk_ops_default_setrate, .enable = clk_dummy_enable, }; diff --git a/arch/arm/plat-s5pc1xx/s5pc100-clock.c b/arch/arm/plat-s5pc1xx/s5pc100-clock.c index b436d44..16f0b90 100644 --- a/arch/arm/plat-s5pc1xx/s5pc100-clock.c +++ b/arch/arm/plat-s5pc1xx/s5pc100-clock.c @@ -111,7 +111,9 @@ static struct clk clk_dout_apll = { .name = "dout_apll", .id = -1, .parent = &clk_mout_apll.clk, - .get_rate = s5pc100_clk_dout_apll_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_apll_get_rate, + }, }; static unsigned long s5pc100_clk_arm_get_rate(struct clk *clk) @@ -165,9 +167,11 @@ static struct clk clk_arm = { .name = "armclk", .id = -1, .parent = &clk_dout_apll, - .get_rate = s5pc100_clk_arm_get_rate, - .set_rate = s5pc100_clk_arm_set_rate, - .round_rate = s5pc100_clk_arm_round_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_arm_get_rate, + .set_rate = s5pc100_clk_arm_set_rate, + .round_rate = s5pc100_clk_arm_round_rate, + }, }; static unsigned long s5pc100_clk_dout_d0_bus_get_rate(struct clk *clk) @@ -185,7 +189,9 @@ static struct clk clk_dout_d0_bus = { .name = "dout_d0_bus", .id = -1, .parent = &clk_arm, - .get_rate = s5pc100_clk_dout_d0_bus_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_d0_bus_get_rate, + }, }; static unsigned long s5pc100_clk_dout_pclkd0_get_rate(struct clk *clk) @@ -203,7 +209,9 @@ static struct clk clk_dout_pclkd0 = { .name = "dout_pclkd0", .id = -1, .parent = &clk_dout_d0_bus, - .get_rate = s5pc100_clk_dout_pclkd0_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_pclkd0_get_rate, + }, }; static unsigned long s5pc100_clk_dout_apll2_get_rate(struct clk *clk) @@ -221,7 +229,9 @@ static struct clk clk_dout_apll2 = { .name = "dout_apll2", .id = -1, .parent = &clk_mout_apll.clk, - .get_rate = s5pc100_clk_dout_apll2_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_apll2_get_rate, + }, }; /* MPLL */ @@ -284,7 +294,9 @@ static struct clk clk_dout_d1_bus = { .name = "dout_d1_bus", .id = -1, .parent = &clk_mout_am.clk, - .get_rate = s5pc100_clk_dout_d1_bus_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_d1_bus_get_rate, + }, }; static struct clk *clkset_onenand_list[] = { @@ -325,7 +337,9 @@ static struct clk clk_dout_pclkd1 = { .name = "dout_pclkd1", .id = -1, .parent = &clk_dout_d1_bus, - .get_rate = s5pc100_clk_dout_pclkd1_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_pclkd1_get_rate, + }, }; static unsigned long s5pc100_clk_dout_mpll2_get_rate(struct clk *clk) @@ -345,7 +359,9 @@ static struct clk clk_dout_mpll2 = { .name = "dout_mpll2", .id = -1, .parent = &clk_mout_am.clk, - .get_rate = s5pc100_clk_dout_mpll2_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_mpll2_get_rate, + }, }; static unsigned long s5pc100_clk_dout_cam_get_rate(struct clk *clk) @@ -365,7 +381,9 @@ static struct clk clk_dout_cam = { .name = "dout_cam", .id = -1, .parent = &clk_dout_mpll2, - .get_rate = s5pc100_clk_dout_cam_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_cam_get_rate, + }, }; static unsigned long s5pc100_clk_dout_mpll_get_rate(struct clk *clk) @@ -385,7 +403,9 @@ static struct clk clk_dout_mpll = { .name = "dout_mpll", .id = -1, .parent = &clk_mout_am.clk, - .get_rate = s5pc100_clk_dout_mpll_get_rate, + .ops = &(struct clk_ops) { + .get_rate = s5pc100_clk_dout_mpll_get_rate, + }, }; /* EPLL */ @@ -540,6 +560,13 @@ static unsigned long s5pc100_roundrate_clksrc(struct clk *clk, return rate; } +static struct clk_ops s5pc100_clksrc_ops = { + .set_parent = s5pc100_setparent_clksrc, + .get_rate = s5pc100_getrate_clksrc, + .set_rate = s5pc100_setrate_clksrc, + .round_rate = s5pc100_roundrate_clksrc, +}; + static struct clk *clkset_spi_list[] = { &clk_mout_epll.clk, &clk_dout_mpll2, @@ -558,10 +585,7 @@ static struct clksrc_clk clk_spi0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI0, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + }, .shift = S5PC100_CLKSRC1_SPI0_SHIFT, .mask = S5PC100_CLKSRC1_SPI0_MASK, @@ -577,10 +601,7 @@ static struct clksrc_clk clk_spi1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI1, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC1_SPI1_SHIFT, .mask = S5PC100_CLKSRC1_SPI1_MASK, @@ -596,10 +617,7 @@ static struct clksrc_clk clk_spi2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI2, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC1_SPI2_SHIFT, .mask = S5PC100_CLKSRC1_SPI2_MASK, @@ -625,10 +643,7 @@ static struct clksrc_clk clk_uart_uclk1 = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK0_UART, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC1_UART_SHIFT, .mask = S5PC100_CLKSRC1_UART_MASK, @@ -683,10 +698,7 @@ static struct clksrc_clk clk_audio0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO0, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC3_AUDIO0_SHIFT, .mask = S5PC100_CLKSRC3_AUDIO0_MASK, @@ -716,10 +728,7 @@ static struct clksrc_clk clk_audio1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO1, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC3_AUDIO1_SHIFT, .mask = S5PC100_CLKSRC3_AUDIO1_MASK, @@ -748,10 +757,7 @@ static struct clksrc_clk clk_audio2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO2, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC3_AUDIO2_SHIFT, .mask = S5PC100_CLKSRC3_AUDIO2_MASK, @@ -801,10 +807,7 @@ static struct clksrc_clk clk_lcd = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK1_LCD, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_LCD_SHIFT, .mask = S5PC100_CLKSRC2_LCD_MASK, @@ -820,10 +823,7 @@ static struct clksrc_clk clk_fimc0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC0, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_FIMC0_SHIFT, .mask = S5PC100_CLKSRC2_FIMC0_MASK, @@ -839,10 +839,7 @@ static struct clksrc_clk clk_fimc1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC1, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_FIMC1_SHIFT, .mask = S5PC100_CLKSRC2_FIMC1_MASK, @@ -858,10 +855,7 @@ static struct clksrc_clk clk_fimc2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC2, .enable = s5pc100_sclk1_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_FIMC2_SHIFT, .mask = S5PC100_CLKSRC2_FIMC2_MASK, @@ -889,10 +883,7 @@ static struct clksrc_clk clk_mmc0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC0, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_MMC0_SHIFT, .mask = S5PC100_CLKSRC2_MMC0_MASK, @@ -908,10 +899,7 @@ static struct clksrc_clk clk_mmc1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC1, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_MMC1_SHIFT, .mask = S5PC100_CLKSRC2_MMC1_MASK, @@ -927,10 +915,7 @@ static struct clksrc_clk clk_mmc2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC2, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC2_MMC2_SHIFT, .mask = S5PC100_CLKSRC2_MMC2_MASK, @@ -959,10 +944,7 @@ static struct clksrc_clk clk_usbhost = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK0_USBHOST, .enable = s5pc100_sclk0_ctrl, - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, + .ops = &s5pc100_clksrc_ops, }, .shift = S5PC100_CLKSRC1_UHOST_SHIFT, .mask = S5PC100_CLKSRC1_UHOST_MASK, diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c index 5872f0b..ad4e872 100644 --- a/arch/arm/plat-samsung/clock-clksrc.c +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -150,20 +150,21 @@ void __init_or_cpufreq s3c_set_clksrc(struct clksrc_clk *clk) clk_get_rate(&clk->clk)); } +static struct clk_ops clksrc_ops = { + .set_parent = s3c_setparent_clksrc, + .get_rate = s3c_getrate_clksrc, + .set_rate = s3c_setrate_clksrc, + .round_rate = s3c_roundrate_clksrc, +}; + void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) { int ret; for (; size > 0; size--, clksrc++) { /* fill in the default functions */ - if (!clksrc->clk.set_parent) - clksrc->clk.set_parent = s3c_setparent_clksrc; - if (!clksrc->clk.get_rate) - clksrc->clk.get_rate = s3c_getrate_clksrc; - if (!clksrc->clk.set_rate) - clksrc->clk.set_rate = s3c_setrate_clksrc; - if (!clksrc->clk.round_rate) - clksrc->clk.round_rate = s3c_roundrate_clksrc; + if (!clksrc->clk.ops) + clksrc->clk.ops = &clksrc_ops; s3c_set_clksrc(clksrc); diff --git a/arch/arm/plat-samsung/include/plat/clock.h b/arch/arm/plat-samsung/include/plat/clock.h index d86af84..43324af 100644 --- a/arch/arm/plat-samsung/include/plat/clock.h +++ b/arch/arm/plat-samsung/include/plat/clock.h @@ -11,6 +11,30 @@ #include +struct clk; + +/** + * struct clk_ops - standard clock operations + * @set_rate: set the clock rate, see clk_set_rate(). + * @get_rate: get the clock rate, see clk_get_rate(). + * @round_rate: round a given clock rate, see clk_round_rate(). + * @set_parent: set the clock's parent, see clk_set_parent(). + * + * Group the common clock implementations together so that we + * don't have to keep setting the same fiels again. We leave + * enable in struct clk. + * + * Adding an extra layer of indirection into the process should + * not be a problem as it is unlikely these operations are going + * to need to be called quickly. + */ +struct clk_ops { + int (*set_rate)(struct clk *c, unsigned long rate); + unsigned long (*get_rate)(struct clk *c); + unsigned long (*round_rate)(struct clk *c, unsigned long rate); + int (*set_parent)(struct clk *c, struct clk *parent); +}; + struct clk { struct list_head list; struct module *owner; @@ -21,11 +45,8 @@ struct clk { unsigned long rate; unsigned long ctrlbit; + struct clk_ops *ops; int (*enable)(struct clk *, int enable); - int (*set_rate)(struct clk *c, unsigned long rate); - unsigned long (*get_rate)(struct clk *c); - unsigned long (*round_rate)(struct clk *c, unsigned long rate); - int (*set_parent)(struct clk *c, struct clk *parent); }; /* other clocks which may be registered by board support */ -- cgit v0.10.2 From f3e0b724cc70ef5ee2a6e0d9dfafa2328c294ab3 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 8 Dec 2009 01:26:07 +0000 Subject: ARM: S3C64XX: Fixup .reg_src and .reg_div with named initialisers Change these two fields to have named initialisers as per the review comments from Kyungmin Park. sed used: s@\.reg_src\(.*\)=\(.*\){\(.*\),\(.*\),\(.*\)}@.reg_src\1=\2{ .reg =\3, .shift =\4, .size =\5 }@g s@\.reg_div\(.*\)=\(.*\){\(.*\),\(.*\),\(.*\)}@.reg_div\1=\2{ .reg =\3, .shift =\4, .size =\5 }@g Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c index f85406a..555d1aa 100644 --- a/arch/arm/plat-s3c64xx/s3c6400-clock.c +++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c @@ -69,7 +69,7 @@ static struct clksrc_clk clk_mout_apll = { .name = "mout_apll", .id = -1, }, - .reg_src = { S3C_CLK_SRC, 0, 1 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 0, .size = 1 }, .sources = &clk_src_apll, }; @@ -88,7 +88,7 @@ static struct clksrc_clk clk_mout_epll = { .name = "mout_epll", .id = -1, }, - .reg_src = { S3C_CLK_SRC, 2, 1 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 2, .size = 1 }, .sources = &clk_src_epll, }; @@ -107,7 +107,7 @@ static struct clksrc_clk clk_mout_mpll = { .name = "mout_mpll", .id = -1, }, - .reg_src = { S3C_CLK_SRC, 1, 1 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 1, .size = 1 }, .sources = &clk_src_mpll, }; @@ -313,8 +313,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_MMC0, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 18, 2 }, - .reg_div = { S3C_CLK_DIV1, 0, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 18, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV1, .shift = 0, .size = 4 }, .sources = &clkset_spi_mmc, }, { .clk = { @@ -323,8 +323,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_MMC1, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 20, 2 }, - .reg_div = { S3C_CLK_DIV1, 4, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 20, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV1, .shift = 4, .size = 4 }, .sources = &clkset_spi_mmc, }, { .clk = { @@ -333,8 +333,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_MMC2, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 22, 2 }, - .reg_div = { S3C_CLK_DIV1, 8, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 22, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV1, .shift = 8, .size = 4 }, .sources = &clkset_spi_mmc, }, { .clk = { @@ -343,8 +343,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_UHOST, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 5, 2 }, - .reg_div = { S3C_CLK_DIV1, 20, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 5, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV1, .shift = 20, .size = 4 }, .sources = &clkset_uhost, }, { .clk = { @@ -353,8 +353,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_UART, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 13, 1 }, - .reg_div = { S3C_CLK_DIV2, 16, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 13, .size = 1 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 16, .size = 4 }, .sources = &clkset_uart, }, { /* Where does UCLK0 come from? */ @@ -364,8 +364,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_SPI0, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 14, 2 }, - .reg_div = { S3C_CLK_DIV2, 0, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 14, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 0, .size = 4 }, .sources = &clkset_spi_mmc, }, { .clk = { @@ -374,8 +374,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_SPI1, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 16, 2 }, - .reg_div = { S3C_CLK_DIV2, 4, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 16, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 4, .size = 4 }, .sources = &clkset_spi_mmc, }, { .clk = { @@ -384,8 +384,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_AUDIO0, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 7, 3 }, - .reg_div = { S3C_CLK_DIV2, 8, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 7, .size = 3 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 8, .size = 4 }, .sources = &clkset_audio0, }, { .clk = { @@ -394,8 +394,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_AUDIO1, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 10, 3 }, - .reg_div = { S3C_CLK_DIV2, 12, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 10, .size = 3 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 12, .size = 4 }, .sources = &clkset_audio1, }, { .clk = { @@ -404,8 +404,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_IRDA, .enable = s3c64xx_sclk_ctrl, }, - .reg_src = { S3C_CLK_SRC, 24, 2 }, - .reg_div = { S3C_CLK_DIV2, 20, 4 }, + .reg_src = { .reg = S3C_CLK_SRC, .shift = 24, .size = 2 }, + .reg_div = { .reg = S3C_CLK_DIV2, .shift = 20, .size = 4 }, .sources = &clkset_irda, }, { .clk = { @@ -414,8 +414,8 @@ static struct clksrc_clk clksrcs[] = { .ctrlbit = S3C_CLKCON_SCLK_CAM, .enable = s3c64xx_sclk_ctrl, }, - .reg_div = { S3C_CLK_DIV0, 20, 4 }, - .reg_src = { NULL, 0, 0 }, + .reg_div = { .reg = S3C_CLK_DIV0, .shift = 20, .size = 4 }, + .reg_src = { .reg = NULL, .shift = 0, .size = 0 }, .sources = &clkset_camif, }, }; -- cgit v0.10.2 From 682e2b7d45878586ce84f6993da0b8a2981a399d Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 9 Dec 2009 00:08:35 +0000 Subject: ARM: S3C64XX: Avoid announcing clksrc clocks twice The new code calls the clock setup code on registration which can be before the clock system has been fully initialised. The following code re-does this setup at the end of the clock registration and thus we get two printings. Update the calls to only print on the last pass or when doing the necessary resume work. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c index 555d1aa..cb2bf4b 100644 --- a/arch/arm/plat-s3c64xx/s3c6400-clock.c +++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c @@ -486,10 +486,10 @@ void __init_or_cpufreq s3c6400_setup_clocks(void) clk_f.rate = fclk; for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) - s3c_set_clksrc(init_parents[ptr]); + s3c_set_clksrc(init_parents[ptr], true); for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) - s3c_set_clksrc(&clksrcs[ptr]); + s3c_set_clksrc(&clksrcs[ptr], true); } static struct clk *clks[] __initdata = { diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c index ad4e872..12129f8 100644 --- a/arch/arm/plat-samsung/clock-clksrc.c +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -125,7 +125,7 @@ static unsigned long s3c_roundrate_clksrc(struct clk *clk, /* Clock initialisation code */ -void __init_or_cpufreq s3c_set_clksrc(struct clksrc_clk *clk) +void __init_or_cpufreq s3c_set_clksrc(struct clksrc_clk *clk, bool announce) { struct clksrc_sources *srcs = clk->sources; u32 mask = bit_mask(clk->reg_src.shift, clk->reg_src.size); @@ -145,9 +145,10 @@ void __init_or_cpufreq s3c_set_clksrc(struct clksrc_clk *clk) clk->clk.parent = srcs->sources[clksrc]; - printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", - clk->clk.name, clk->clk.parent->name, clksrc, - clk_get_rate(&clk->clk)); + if (announce) + printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n", + clk->clk.name, clk->clk.parent->name, clksrc, + clk_get_rate(&clk->clk)); } static struct clk_ops clksrc_ops = { @@ -166,7 +167,12 @@ void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) if (!clksrc->clk.ops) clksrc->clk.ops = &clksrc_ops; - s3c_set_clksrc(clksrc); + /* setup the clocksource, but do not announce it + * as it may be re-set by the setup routines + * called after the rest of the clocks have been + * registered + */ + s3c_set_clksrc(clksrc, false); ret = s3c24xx_register_clock(&clksrc->clk); diff --git a/arch/arm/plat-samsung/include/plat/clock-clksrc.h b/arch/arm/plat-samsung/include/plat/clock-clksrc.h index 283dfa0..50a8ca7 100644 --- a/arch/arm/plat-samsung/include/plat/clock-clksrc.h +++ b/arch/arm/plat-samsung/include/plat/clock-clksrc.h @@ -63,7 +63,15 @@ struct clksrc_clk { struct clksrc_reg reg_div; }; -extern void s3c_set_clksrc(struct clksrc_clk *clk); +/** + * s3c_set_clksrc() - setup the clock from the register settings + * @clk: The clock to setup. + * @announce: true to announce the setting to printk(). + * + * Setup the clock from the current register settings, for when the + * kernel boots or if it is resuming from a possibly unknown state. + */ +extern void s3c_set_clksrc(struct clksrc_clk *clk, bool announce); /** * s3c_register_clksrc() register clocks from an array of clksrc clocks -- cgit v0.10.2 From 2c2f69b15d5420291a941ace10149e76f0af7ab0 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 23 Dec 2009 18:43:48 +0000 Subject: ARM: SAMSUNG: Move clock.c to arch/arm/plat-samsung This is the core implementation of the clock code for all Samsung based SoCs, so move it to arch/arm/plat-samsung (the clock.h file has already been moved). Since the file is built for every Samsung SoC, no changes are needed to the Kconfig system. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 50444da..99f91ad 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -11,9 +11,8 @@ obj- := # Core support for all Samsung SoCs -obj-y += init.o +obj-y += init.o obj-y += time.o -obj-y += clock.o obj-y += pwm-clock.o obj-y += gpio.o obj-y += gpio-config.o diff --git a/arch/arm/plat-s3c/clock.c b/arch/arm/plat-s3c/clock.c deleted file mode 100644 index fa91125..0000000 --- a/arch/arm/plat-s3c/clock.c +++ /dev/null @@ -1,369 +0,0 @@ -/* linux/arch/arm/plat-s3c24xx/clock.c - * - * Copyright 2004-2005 Simtec Electronics - * Ben Dooks - * - * S3C24XX Core clock control support - * - * Based on, and code from linux/arch/arm/mach-versatile/clock.c - ** - ** Copyright (C) 2004 ARM Limited. - ** Written by Deep Blue Solutions 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include - -/* clock information */ - -static LIST_HEAD(clocks); - -/* We originally used an mutex here, but some contexts (see resume) - * are calling functions such as clk_set_parent() with IRQs disabled - * causing an BUG to be triggered. - */ -DEFINE_SPINLOCK(clocks_lock); - -/* enable and disable calls for use with the clk struct */ - -static int clk_null_enable(struct clk *clk, int enable) -{ - return 0; -} - -/* Clock API calls */ - -struct clk *clk_get(struct device *dev, const char *id) -{ - struct clk *p; - struct clk *clk = ERR_PTR(-ENOENT); - int idno; - - if (dev == NULL || dev->bus != &platform_bus_type) - idno = -1; - else - idno = to_platform_device(dev)->id; - - spin_lock(&clocks_lock); - - list_for_each_entry(p, &clocks, list) { - if (p->id == idno && - strcmp(id, p->name) == 0 && - try_module_get(p->owner)) { - clk = p; - break; - } - } - - /* check for the case where a device was supplied, but the - * clock that was being searched for is not device specific */ - - if (IS_ERR(clk)) { - list_for_each_entry(p, &clocks, list) { - if (p->id == -1 && strcmp(id, p->name) == 0 && - try_module_get(p->owner)) { - clk = p; - break; - } - } - } - - spin_unlock(&clocks_lock); - return clk; -} - -void clk_put(struct clk *clk) -{ - module_put(clk->owner); -} - -int clk_enable(struct clk *clk) -{ - if (IS_ERR(clk) || clk == NULL) - return -EINVAL; - - clk_enable(clk->parent); - - spin_lock(&clocks_lock); - - if ((clk->usage++) == 0) - (clk->enable)(clk, 1); - - spin_unlock(&clocks_lock); - return 0; -} - -void clk_disable(struct clk *clk) -{ - if (IS_ERR(clk) || clk == NULL) - return; - - spin_lock(&clocks_lock); - - if ((--clk->usage) == 0) - (clk->enable)(clk, 0); - - spin_unlock(&clocks_lock); - clk_disable(clk->parent); -} - - -unsigned long clk_get_rate(struct clk *clk) -{ - if (IS_ERR(clk)) - return 0; - - if (clk->rate != 0) - return clk->rate; - - if (clk->ops != NULL && clk->ops->get_rate != NULL) - return (clk->ops->get_rate)(clk); - - if (clk->parent != NULL) - return clk_get_rate(clk->parent); - - return clk->rate; -} - -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - if (!IS_ERR(clk) && clk->ops && clk->ops->round_rate) - return (clk->ops->round_rate)(clk, rate); - - return rate; -} - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - int ret; - - if (IS_ERR(clk)) - return -EINVAL; - - /* We do not default just do a clk->rate = rate as - * the clock may have been made this way by choice. - */ - - WARN_ON(clk->ops == NULL); - WARN_ON(clk->ops && clk->ops->set_rate == NULL); - - if (clk->ops == NULL || clk->ops->set_rate == NULL) - return -EINVAL; - - spin_lock(&clocks_lock); - ret = (clk->ops->set_rate)(clk, rate); - spin_unlock(&clocks_lock); - - return ret; -} - -struct clk *clk_get_parent(struct clk *clk) -{ - return clk->parent; -} - -int clk_set_parent(struct clk *clk, struct clk *parent) -{ - int ret = 0; - - if (IS_ERR(clk)) - return -EINVAL; - - spin_lock(&clocks_lock); - - if (clk->ops && clk->ops->set_parent) - ret = (clk->ops->set_parent)(clk, parent); - - spin_unlock(&clocks_lock); - - return ret; -} - -EXPORT_SYMBOL(clk_get); -EXPORT_SYMBOL(clk_put); -EXPORT_SYMBOL(clk_enable); -EXPORT_SYMBOL(clk_disable); -EXPORT_SYMBOL(clk_get_rate); -EXPORT_SYMBOL(clk_round_rate); -EXPORT_SYMBOL(clk_set_rate); -EXPORT_SYMBOL(clk_get_parent); -EXPORT_SYMBOL(clk_set_parent); - -/* base clocks */ - -static int clk_default_setrate(struct clk *clk, unsigned long rate) -{ - clk->rate = rate; - return 0; -} - -static struct clk_ops clk_ops_def_setrate = { - .set_rate = clk_default_setrate, -}; - -struct clk clk_xtal = { - .name = "xtal", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, -}; - -struct clk clk_ext = { - .name = "ext", - .id = -1, -}; - -struct clk clk_epll = { - .name = "epll", - .id = -1, -}; - -struct clk clk_mpll = { - .name = "mpll", - .id = -1, - .ops = &clk_ops_def_setrate, -}; - -struct clk clk_upll = { - .name = "upll", - .id = -1, - .parent = NULL, - .ctrlbit = 0, -}; - -struct clk clk_f = { - .name = "fclk", - .id = -1, - .rate = 0, - .parent = &clk_mpll, - .ctrlbit = 0, -}; - -struct clk clk_h = { - .name = "hclk", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, - .ops = &clk_ops_def_setrate, -}; - -struct clk clk_p = { - .name = "pclk", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, - .ops = &clk_ops_def_setrate, -}; - -struct clk clk_usb_bus = { - .name = "usb-bus", - .id = -1, - .rate = 0, - .parent = &clk_upll, -}; - - -struct clk s3c24xx_uclk = { - .name = "uclk", - .id = -1, -}; - -/* initialise the clock system */ - -int s3c24xx_register_clock(struct clk *clk) -{ - if (clk->enable == NULL) - clk->enable = clk_null_enable; - - /* add to the list of available clocks */ - - /* Quick check to see if this clock has already been registered. */ - BUG_ON(clk->list.prev != clk->list.next); - - spin_lock(&clocks_lock); - list_add(&clk->list, &clocks); - spin_unlock(&clocks_lock); - - return 0; -} - -int s3c24xx_register_clocks(struct clk **clks, int nr_clks) -{ - int fails = 0; - - for (; nr_clks > 0; nr_clks--, clks++) { - if (s3c24xx_register_clock(*clks) < 0) - fails++; - } - - return fails; -} - -/* initalise all the clocks */ - -int __init s3c24xx_register_baseclocks(unsigned long xtal) -{ - printk(KERN_INFO "S3C24XX Clocks, Copyright 2004 Simtec Electronics\n"); - - clk_xtal.rate = xtal; - - /* register our clocks */ - - if (s3c24xx_register_clock(&clk_xtal) < 0) - printk(KERN_ERR "failed to register master xtal\n"); - - if (s3c24xx_register_clock(&clk_mpll) < 0) - printk(KERN_ERR "failed to register mpll clock\n"); - - if (s3c24xx_register_clock(&clk_upll) < 0) - printk(KERN_ERR "failed to register upll clock\n"); - - if (s3c24xx_register_clock(&clk_f) < 0) - printk(KERN_ERR "failed to register cpu fclk\n"); - - if (s3c24xx_register_clock(&clk_h) < 0) - printk(KERN_ERR "failed to register cpu hclk\n"); - - if (s3c24xx_register_clock(&clk_p) < 0) - printk(KERN_ERR "failed to register cpu pclk\n"); - - return 0; -} - diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index ce736ce..7612198 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -9,4 +9,8 @@ obj-m := obj-n := dummy.o obj- := +# Objects we always build independent of SoC choice + +obj-y += clock.o + obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c new file mode 100644 index 0000000..fa91125 --- /dev/null +++ b/arch/arm/plat-samsung/clock.c @@ -0,0 +1,369 @@ +/* linux/arch/arm/plat-s3c24xx/clock.c + * + * Copyright 2004-2005 Simtec Electronics + * Ben Dooks + * + * S3C24XX Core clock control support + * + * Based on, and code from linux/arch/arm/mach-versatile/clock.c + ** + ** Copyright (C) 2004 ARM Limited. + ** Written by Deep Blue Solutions 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +/* clock information */ + +static LIST_HEAD(clocks); + +/* We originally used an mutex here, but some contexts (see resume) + * are calling functions such as clk_set_parent() with IRQs disabled + * causing an BUG to be triggered. + */ +DEFINE_SPINLOCK(clocks_lock); + +/* enable and disable calls for use with the clk struct */ + +static int clk_null_enable(struct clk *clk, int enable) +{ + return 0; +} + +/* Clock API calls */ + +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p; + struct clk *clk = ERR_PTR(-ENOENT); + int idno; + + if (dev == NULL || dev->bus != &platform_bus_type) + idno = -1; + else + idno = to_platform_device(dev)->id; + + spin_lock(&clocks_lock); + + list_for_each_entry(p, &clocks, list) { + if (p->id == idno && + strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + /* check for the case where a device was supplied, but the + * clock that was being searched for is not device specific */ + + if (IS_ERR(clk)) { + list_for_each_entry(p, &clocks, list) { + if (p->id == -1 && strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + break; + } + } + } + + spin_unlock(&clocks_lock); + return clk; +} + +void clk_put(struct clk *clk) +{ + module_put(clk->owner); +} + +int clk_enable(struct clk *clk) +{ + if (IS_ERR(clk) || clk == NULL) + return -EINVAL; + + clk_enable(clk->parent); + + spin_lock(&clocks_lock); + + if ((clk->usage++) == 0) + (clk->enable)(clk, 1); + + spin_unlock(&clocks_lock); + return 0; +} + +void clk_disable(struct clk *clk) +{ + if (IS_ERR(clk) || clk == NULL) + return; + + spin_lock(&clocks_lock); + + if ((--clk->usage) == 0) + (clk->enable)(clk, 0); + + spin_unlock(&clocks_lock); + clk_disable(clk->parent); +} + + +unsigned long clk_get_rate(struct clk *clk) +{ + if (IS_ERR(clk)) + return 0; + + if (clk->rate != 0) + return clk->rate; + + if (clk->ops != NULL && clk->ops->get_rate != NULL) + return (clk->ops->get_rate)(clk); + + if (clk->parent != NULL) + return clk_get_rate(clk->parent); + + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (!IS_ERR(clk) && clk->ops && clk->ops->round_rate) + return (clk->ops->round_rate)(clk, rate); + + return rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret; + + if (IS_ERR(clk)) + return -EINVAL; + + /* We do not default just do a clk->rate = rate as + * the clock may have been made this way by choice. + */ + + WARN_ON(clk->ops == NULL); + WARN_ON(clk->ops && clk->ops->set_rate == NULL); + + if (clk->ops == NULL || clk->ops->set_rate == NULL) + return -EINVAL; + + spin_lock(&clocks_lock); + ret = (clk->ops->set_rate)(clk, rate); + spin_unlock(&clocks_lock); + + return ret; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = 0; + + if (IS_ERR(clk)) + return -EINVAL; + + spin_lock(&clocks_lock); + + if (clk->ops && clk->ops->set_parent) + ret = (clk->ops->set_parent)(clk, parent); + + spin_unlock(&clocks_lock); + + return ret; +} + +EXPORT_SYMBOL(clk_get); +EXPORT_SYMBOL(clk_put); +EXPORT_SYMBOL(clk_enable); +EXPORT_SYMBOL(clk_disable); +EXPORT_SYMBOL(clk_get_rate); +EXPORT_SYMBOL(clk_round_rate); +EXPORT_SYMBOL(clk_set_rate); +EXPORT_SYMBOL(clk_get_parent); +EXPORT_SYMBOL(clk_set_parent); + +/* base clocks */ + +static int clk_default_setrate(struct clk *clk, unsigned long rate) +{ + clk->rate = rate; + return 0; +} + +static struct clk_ops clk_ops_def_setrate = { + .set_rate = clk_default_setrate, +}; + +struct clk clk_xtal = { + .name = "xtal", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_ext = { + .name = "ext", + .id = -1, +}; + +struct clk clk_epll = { + .name = "epll", + .id = -1, +}; + +struct clk clk_mpll = { + .name = "mpll", + .id = -1, + .ops = &clk_ops_def_setrate, +}; + +struct clk clk_upll = { + .name = "upll", + .id = -1, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_f = { + .name = "fclk", + .id = -1, + .rate = 0, + .parent = &clk_mpll, + .ctrlbit = 0, +}; + +struct clk clk_h = { + .name = "hclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, + .ops = &clk_ops_def_setrate, +}; + +struct clk clk_p = { + .name = "pclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, + .ops = &clk_ops_def_setrate, +}; + +struct clk clk_usb_bus = { + .name = "usb-bus", + .id = -1, + .rate = 0, + .parent = &clk_upll, +}; + + +struct clk s3c24xx_uclk = { + .name = "uclk", + .id = -1, +}; + +/* initialise the clock system */ + +int s3c24xx_register_clock(struct clk *clk) +{ + if (clk->enable == NULL) + clk->enable = clk_null_enable; + + /* add to the list of available clocks */ + + /* Quick check to see if this clock has already been registered. */ + BUG_ON(clk->list.prev != clk->list.next); + + spin_lock(&clocks_lock); + list_add(&clk->list, &clocks); + spin_unlock(&clocks_lock); + + return 0; +} + +int s3c24xx_register_clocks(struct clk **clks, int nr_clks) +{ + int fails = 0; + + for (; nr_clks > 0; nr_clks--, clks++) { + if (s3c24xx_register_clock(*clks) < 0) + fails++; + } + + return fails; +} + +/* initalise all the clocks */ + +int __init s3c24xx_register_baseclocks(unsigned long xtal) +{ + printk(KERN_INFO "S3C24XX Clocks, Copyright 2004 Simtec Electronics\n"); + + clk_xtal.rate = xtal; + + /* register our clocks */ + + if (s3c24xx_register_clock(&clk_xtal) < 0) + printk(KERN_ERR "failed to register master xtal\n"); + + if (s3c24xx_register_clock(&clk_mpll) < 0) + printk(KERN_ERR "failed to register mpll clock\n"); + + if (s3c24xx_register_clock(&clk_upll) < 0) + printk(KERN_ERR "failed to register upll clock\n"); + + if (s3c24xx_register_clock(&clk_f) < 0) + printk(KERN_ERR "failed to register cpu fclk\n"); + + if (s3c24xx_register_clock(&clk_h) < 0) + printk(KERN_ERR "failed to register cpu hclk\n"); + + if (s3c24xx_register_clock(&clk_p) < 0) + printk(KERN_ERR "failed to register cpu pclk\n"); + + return 0; +} + -- cgit v0.10.2 From fb6e76cd2309c76d2120e013ff34ff6ab27aad88 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 6 Jan 2010 01:07:57 +0900 Subject: ARM: SAMSUNG: Do not allow get/set/round rate calls with no divider If the reg_div field is not set, then do not register clk_ops with the get/set/round rate calls as these will fail to work. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c index 12129f8..aaf7d5e 100644 --- a/arch/arm/plat-samsung/clock-clksrc.c +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -158,14 +158,25 @@ static struct clk_ops clksrc_ops = { .round_rate = s3c_roundrate_clksrc, }; +static struct clk_ops clksrc_ops_nodiv = { + .set_parent = s3c_setparent_clksrc, +}; + void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) { int ret; + WARN_ON(!clksrc->reg_div.reg && !clksrc->reg_src.reg); + for (; size > 0; size--, clksrc++) { /* fill in the default functions */ - if (!clksrc->clk.ops) - clksrc->clk.ops = &clksrc_ops; + + if (!clksrc->clk.ops) { + if (!clksrc->reg_div.reg) + clksrc->clk.ops = &clksrc_ops_nodiv; + else + clksrc->clk.ops = &clksrc_ops; + } /* setup the clocksource, but do not announce it * as it may be re-set by the setup routines -- cgit v0.10.2 From 1d9f13c49ed750260f40317629bdd66160a3ac16 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 6 Jan 2010 01:21:38 +0900 Subject: ARM: SAMSUNG: Add call to register array of clocks Add s3c_register_clocks() to register an array of clocks, printing an error message if there is a problem. Replace all points in the code where this could be used. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c index 91db4f5..3eb8b93 100644 --- a/arch/arm/mach-s3c2443/clock.c +++ b/arch/arm/mach-s3c2443/clock.c @@ -1104,14 +1104,7 @@ void __init s3c2443_init_clocks(int xtal) /* register clocks from clock array */ - clkp = init_clocks; - for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } + s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); /* We must be careful disabling the clocks we are not intending to * be using at boot time, as subsystems such as the LCD which do diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index 1808fa8..46c9381 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -430,25 +430,15 @@ __init void s3c_pwmclk_init(void) return; } - for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { + for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) clk_timer_scaler[clk].parent = clk_timers; - ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); - if (ret < 0) { - printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); - return; - } - } - for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { - ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); - if (ret < 0) { - printk(KERN_ERR "error adding pww tclk%d\n", clk); - return; - } - } + s3c_register_clocks(clk_timer_scaler, ARRAY_SIZE(clk_timer_scaler)); + s3c_register_clocks(clk_timer_tclk, ARRAY_SIZE(clk_timer_tclk)); for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { ret = clk_pwm_tdiv_register(clk); + if (ret < 0) { printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); return; diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c index 7a36e89..ae5883c 100644 --- a/arch/arm/plat-s3c64xx/clock.c +++ b/arch/arm/plat-s3c64xx/clock.c @@ -274,15 +274,7 @@ void __init s3c64xx_register_clocks(void) int ptr; s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); - - clkp = init_clocks; - for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } + s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); clkp = init_clocks_disable; for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { diff --git a/arch/arm/plat-s5pc1xx/clock.c b/arch/arm/plat-s5pc1xx/clock.c index 2f4d8d4..0aff16d 100644 --- a/arch/arm/plat-s5pc1xx/clock.c +++ b/arch/arm/plat-s5pc1xx/clock.c @@ -704,16 +704,8 @@ void __init s5pc1xx_register_clocks(void) s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); - clkp = s5pc100_init_clocks; - size = ARRAY_SIZE(s5pc100_init_clocks); - - for (ptr = 0; ptr < size; ptr++, clkp++) { - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } + s3c_register_clocks(s5pc100_init_clocks, + ARRAY_SIZE(s5pc100_init_clocks)); clkp = s5pc100_init_clocks_disable; size = ARRAY_SIZE(s5pc100_init_clocks_disable); diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c index fa91125..9194af9 100644 --- a/arch/arm/plat-samsung/clock.c +++ b/arch/arm/plat-samsung/clock.c @@ -336,6 +336,28 @@ int s3c24xx_register_clocks(struct clk **clks, int nr_clks) return fails; } +/** + * s3c_register_clocks() - register an array of clocks + * @clkp: Pointer to the first clock in the array. + * @nr_clks: Number of clocks to register. + * + * Call s3c24xx_register_clock() on the @clkp array given, printing an + * error if it fails to register the clock (unlikely). + */ +void __initdata s3c_register_clocks(struct clk *clkp, int nr_clks) +{ + int ret; + + for (; nr_clks > 0; nr_clks--, clkp++) { + ret = s3c24xx_register_clock(clkp); + + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } +} + /* initalise all the clocks */ int __init s3c24xx_register_baseclocks(unsigned long xtal) diff --git a/arch/arm/plat-samsung/include/plat/clock.h b/arch/arm/plat-samsung/include/plat/clock.h index 43324af..bd41f82 100644 --- a/arch/arm/plat-samsung/include/plat/clock.h +++ b/arch/arm/plat-samsung/include/plat/clock.h @@ -87,6 +87,8 @@ extern int s3c2410_clkcon_enable(struct clk *clk, int enable); extern int s3c24xx_register_clock(struct clk *clk); extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks); +extern void s3c_register_clocks(struct clk *clk, int nr_clks); + extern int s3c24xx_register_baseclocks(unsigned long xtal); extern void s3c64xx_register_clocks(void); -- cgit v0.10.2 From 14235696d401e62f8f5740ca2fb917ab42b9fd18 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 6 Jan 2010 01:53:25 +0900 Subject: ARM: SAMSUNG: Do not register set_parent call if no source If there is no source register defined, do not register a clksrc clock with a valid .set_parent in the ops. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c index aaf7d5e..33c633a 100644 --- a/arch/arm/plat-samsung/clock-clksrc.c +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -86,7 +86,7 @@ static int s3c_setparent_clksrc(struct clk *clk, struct clk *parent) break; } - if (src_nr >= 0 && sclk->reg_src.reg) { + if (src_nr >= 0) { clk->parent = parent; clksrc &= ~mask; @@ -162,6 +162,12 @@ static struct clk_ops clksrc_ops_nodiv = { .set_parent = s3c_setparent_clksrc, }; +static struct clk_ops clksrc_ops_nosrc = { + .get_rate = s3c_getrate_clksrc, + .set_rate = s3c_setrate_clksrc, + .round_rate = s3c_roundrate_clksrc, +}; + void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) { int ret; @@ -174,6 +180,8 @@ void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) if (!clksrc->clk.ops) { if (!clksrc->reg_div.reg) clksrc->clk.ops = &clksrc_ops_nodiv; + else if (!clksrc->reg_src.reg) + clksrc->clk.ops = &clksrc_ops_nosrc; else clksrc->clk.ops = &clksrc_ops; } -- cgit v0.10.2 From ed276849bc6a8ad5bb0f9fa94fe8305ee67bb6c6 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 12:50:23 +0900 Subject: ARM: SAMSUNG: Make clk_default_setrate and clk_ops_def_setrate visible This patch makes clk_default_setrate and clk_ops_def_setrate available to code outside plat-samsung clock code. Signed-off-by: Thomas Abraham Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s5pc1xx/clock.c b/arch/arm/plat-s5pc1xx/clock.c index 0aff16d..aec0305 100644 --- a/arch/arm/plat-s5pc1xx/clock.c +++ b/arch/arm/plat-s5pc1xx/clock.c @@ -64,16 +64,6 @@ struct clk clk_54m = { .rate = 54000000, }; -static int clk_default_setrate(struct clk *clk, unsigned long rate) -{ - clk->rate = rate; - return 0; -} - -static struct clk_ops clk_ops_default_setrate = { - .set_rate = clk_default_setrate, -}; - static int clk_dummy_enable(struct clk *clk, int enable) { return 0; @@ -86,7 +76,7 @@ struct clk clk_hd0 = { .parent = NULL, .ctrlbit = 0, .enable = clk_dummy_enable, - .ops = &clk_ops_default_setrate, + .ops = &clk_ops_def_setrate, }; struct clk clk_pd0 = { @@ -95,7 +85,7 @@ struct clk clk_pd0 = { .rate = 0, .parent = NULL, .ctrlbit = 0, - .ops = &clk_ops_default_setrate, + .ops = &clk_ops_def_setrate, .enable = clk_dummy_enable, }; diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c index 9194af9..0c746ae 100644 --- a/arch/arm/plat-samsung/clock.c +++ b/arch/arm/plat-samsung/clock.c @@ -225,13 +225,13 @@ EXPORT_SYMBOL(clk_set_parent); /* base clocks */ -static int clk_default_setrate(struct clk *clk, unsigned long rate) +int clk_default_setrate(struct clk *clk, unsigned long rate) { clk->rate = rate; return 0; } -static struct clk_ops clk_ops_def_setrate = { +struct clk_ops clk_ops_def_setrate = { .set_rate = clk_default_setrate, }; diff --git a/arch/arm/plat-samsung/include/plat/clock.h b/arch/arm/plat-samsung/include/plat/clock.h index bd41f82..22e0114 100644 --- a/arch/arm/plat-samsung/include/plat/clock.h +++ b/arch/arm/plat-samsung/include/plat/clock.h @@ -75,6 +75,9 @@ extern struct clk clk_h2; extern struct clk clk_27m; extern struct clk clk_48m; +extern int clk_default_setrate(struct clk *clk, unsigned long rate); +extern struct clk_ops clk_ops_def_setrate; + /* exports for arch/arm/mach-s3c2410 * * Please DO NOT use these outside of arch/arm/mach-s3c2410 -- cgit v0.10.2 From 9745626b65b856bf58e70feb4eee1e5471db699e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 6 Jan 2010 02:39:28 +0900 Subject: ARM: SAMSUNG: Move pwm-clock code into plat-samsung Move the code for the pwm-clock into plat-samsung, as it is common to all Samsung SoCs. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 99f91ad..ef10a91 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -13,7 +13,6 @@ obj- := obj-y += init.o obj-y += time.o -obj-y += pwm-clock.o obj-y += gpio.o obj-y += gpio-config.o diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c deleted file mode 100644 index 46c9381..0000000 --- a/arch/arm/plat-s3c/pwm-clock.c +++ /dev/null @@ -1,455 +0,0 @@ -/* linux/arch/arm/plat-s3c24xx/pwm-clock.c - * - * Copyright (c) 2007 Simtec Electronics - * Copyright (c) 2007, 2008 Ben Dooks - * Ben Dooks - * - * 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. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -/* Each of the timers 0 through 5 go through the following - * clock tree, with the inputs depending on the timers. - * - * pclk ---- [ prescaler 0 ] -+---> timer 0 - * +---> timer 1 - * - * pclk ---- [ prescaler 1 ] -+---> timer 2 - * +---> timer 3 - * \---> timer 4 - * - * Which are fed into the timers as so: - * - * prescaled 0 ---- [ div 2,4,8,16 ] ---\ - * [mux] -> timer 0 - * tclk 0 ------------------------------/ - * - * prescaled 0 ---- [ div 2,4,8,16 ] ---\ - * [mux] -> timer 1 - * tclk 0 ------------------------------/ - * - * - * prescaled 1 ---- [ div 2,4,8,16 ] ---\ - * [mux] -> timer 2 - * tclk 1 ------------------------------/ - * - * prescaled 1 ---- [ div 2,4,8,16 ] ---\ - * [mux] -> timer 3 - * tclk 1 ------------------------------/ - * - * prescaled 1 ---- [ div 2,4,8, 16 ] --\ - * [mux] -> timer 4 - * tclk 1 ------------------------------/ - * - * Since the mux and the divider are tied together in the - * same register space, it is impossible to set the parent - * and the rate at the same time. To avoid this, we add an - * intermediate 'prescaled-and-divided' clock to select - * as the parent for the timer input clock called tdiv. - * - * prescaled clk --> pwm-tdiv ---\ - * [ mux ] --> timer X - * tclk -------------------------/ -*/ - -static struct clk clk_timer_scaler[]; - -static unsigned long clk_pwm_scaler_get_rate(struct clk *clk) -{ - unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); - - if (clk == &clk_timer_scaler[1]) { - tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; - tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; - } else { - tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; - } - - return clk_get_rate(clk->parent) / (tcfg0 + 1); -} - -static unsigned long clk_pwm_scaler_round_rate(struct clk *clk, - unsigned long rate) -{ - unsigned long parent_rate = clk_get_rate(clk->parent); - unsigned long divisor = parent_rate / rate; - - if (divisor > 256) - divisor = 256; - else if (divisor < 2) - divisor = 2; - - return parent_rate / divisor; -} - -static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) -{ - unsigned long round = clk_pwm_scaler_round_rate(clk, rate); - unsigned long tcfg0; - unsigned long divisor; - unsigned long flags; - - divisor = clk_get_rate(clk->parent) / round; - divisor--; - - local_irq_save(flags); - tcfg0 = __raw_readl(S3C2410_TCFG0); - - if (clk == &clk_timer_scaler[1]) { - tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; - tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT; - } else { - tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; - tcfg0 |= divisor; - } - - __raw_writel(tcfg0, S3C2410_TCFG0); - local_irq_restore(flags); - - return 0; -} - -static struct clk_ops clk_pwm_scaler_ops = { - .get_rate = clk_pwm_scaler_get_rate, - .set_rate = clk_pwm_scaler_set_rate, - .round_rate = clk_pwm_scaler_round_rate, -}; - -static struct clk clk_timer_scaler[] = { - [0] = { - .name = "pwm-scaler0", - .id = -1, - .ops = &clk_pwm_scaler_ops, - }, - [1] = { - .name = "pwm-scaler1", - .id = -1, - .ops = &clk_pwm_scaler_ops, - }, -}; - -static struct clk clk_timer_tclk[] = { - [0] = { - .name = "pwm-tclk0", - .id = -1, - }, - [1] = { - .name = "pwm-tclk1", - .id = -1, - }, -}; - -struct pwm_tdiv_clk { - struct clk clk; - unsigned int divisor; -}; - -static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) -{ - return container_of(clk, struct pwm_tdiv_clk, clk); -} - -static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) -{ - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - unsigned int divisor; - - tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); - tcfg1 &= S3C2410_TCFG1_MUX_MASK; - - if (pwm_cfg_src_is_tclk(tcfg1)) - divisor = to_tdiv(clk)->divisor; - else - divisor = tcfg_to_divisor(tcfg1); - - return clk_get_rate(clk->parent) / divisor; -} - -static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, - unsigned long rate) -{ - unsigned long parent_rate; - unsigned long divisor; - - parent_rate = clk_get_rate(clk->parent); - divisor = parent_rate / rate; - - if (divisor <= 1 && pwm_tdiv_has_div1()) - divisor = 1; - else if (divisor <= 2) - divisor = 2; - else if (divisor <= 4) - divisor = 4; - else if (divisor <= 8) - divisor = 8; - else - divisor = 16; - - return parent_rate / divisor; -} - -static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) -{ - return pwm_tdiv_div_bits(divclk->divisor); -} - -static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) -{ - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - unsigned long bits = clk_pwm_tdiv_bits(divclk); - unsigned long flags; - unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); - - local_irq_save(flags); - - tcfg1 = __raw_readl(S3C2410_TCFG1); - tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); - tcfg1 |= bits << shift; - __raw_writel(tcfg1, S3C2410_TCFG1); - - local_irq_restore(flags); -} - -static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) -{ - struct pwm_tdiv_clk *divclk = to_tdiv(clk); - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - unsigned long parent_rate = clk_get_rate(clk->parent); - unsigned long divisor; - - tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); - tcfg1 &= S3C2410_TCFG1_MUX_MASK; - - rate = clk_round_rate(clk, rate); - divisor = parent_rate / rate; - - if (divisor > 16) - return -EINVAL; - - divclk->divisor = divisor; - - /* Update the current MUX settings if we are currently - * selected as the clock source for this clock. */ - - if (!pwm_cfg_src_is_tclk(tcfg1)) - clk_pwm_tdiv_update(divclk); - - return 0; -} - -static struct clk_ops clk_tdiv_ops = { - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, -}; - -static struct pwm_tdiv_clk clk_timer_tdiv[] = { - [0] = { - .clk = { - .name = "pwm-tdiv", - .ops = &clk_tdiv_ops, - .parent = &clk_timer_scaler[0], - }, - }, - [1] = { - .clk = { - .name = "pwm-tdiv", - .ops = &clk_tdiv_ops, - .parent = &clk_timer_scaler[0], - } - }, - [2] = { - .clk = { - .name = "pwm-tdiv", - .ops = &clk_tdiv_ops, - .parent = &clk_timer_scaler[1], - }, - }, - [3] = { - .clk = { - .name = "pwm-tdiv", - .ops = &clk_tdiv_ops, - .parent = &clk_timer_scaler[1], - }, - }, - [4] = { - .clk = { - .name = "pwm-tdiv", - .ops = &clk_tdiv_ops, - .parent = &clk_timer_scaler[1], - }, - }, -}; - -static int __init clk_pwm_tdiv_register(unsigned int id) -{ - struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - - tcfg1 >>= S3C2410_TCFG1_SHIFT(id); - tcfg1 &= S3C2410_TCFG1_MUX_MASK; - - divclk->clk.id = id; - divclk->divisor = tcfg_to_divisor(tcfg1); - - return s3c24xx_register_clock(&divclk->clk); -} - -static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) -{ - return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; -} - -static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) -{ - return &clk_timer_tdiv[id].clk; -} - -static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) -{ - unsigned int id = clk->id; - unsigned long tcfg1; - unsigned long flags; - unsigned long bits; - unsigned long shift = S3C2410_TCFG1_SHIFT(id); - - if (parent == s3c24xx_pwmclk_tclk(id)) - bits = S3C_TCFG1_MUX_TCLK << shift; - else if (parent == s3c24xx_pwmclk_tdiv(id)) - bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; - else - return -EINVAL; - - clk->parent = parent; - - local_irq_save(flags); - - tcfg1 = __raw_readl(S3C2410_TCFG1); - tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); - __raw_writel(tcfg1 | bits, S3C2410_TCFG1); - - local_irq_restore(flags); - - return 0; -} - -static struct clk_ops clk_tin_ops = { - .set_parent = clk_pwm_tin_set_parent, -}; - -static struct clk clk_tin[] = { - [0] = { - .name = "pwm-tin", - .id = 0, - .ops = &clk_tin_ops, - }, - [1] = { - .name = "pwm-tin", - .id = 1, - .ops = &clk_tin_ops, - }, - [2] = { - .name = "pwm-tin", - .id = 2, - .ops = &clk_tin_ops, - }, - [3] = { - .name = "pwm-tin", - .id = 3, - .ops = &clk_tin_ops, - }, - [4] = { - .name = "pwm-tin", - .id = 4, - .ops = &clk_tin_ops, - }, -}; - -static __init int clk_pwm_tin_register(struct clk *pwm) -{ - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - unsigned int id = pwm->id; - - struct clk *parent; - int ret; - - ret = s3c24xx_register_clock(pwm); - if (ret < 0) - return ret; - - tcfg1 >>= S3C2410_TCFG1_SHIFT(id); - tcfg1 &= S3C2410_TCFG1_MUX_MASK; - - if (pwm_cfg_src_is_tclk(tcfg1)) - parent = s3c24xx_pwmclk_tclk(id); - else - parent = s3c24xx_pwmclk_tdiv(id); - - return clk_set_parent(pwm, parent); -} - -/** - * s3c_pwmclk_init() - initialise pwm clocks - * - * Initialise and register the clocks which provide the inputs for the - * pwm timer blocks. - * - * Note, this call is required by the time core, so must be called after - * the base clocks are added and before any of the initcalls are run. - */ -__init void s3c_pwmclk_init(void) -{ - struct clk *clk_timers; - unsigned int clk; - int ret; - - clk_timers = clk_get(NULL, "timers"); - if (IS_ERR(clk_timers)) { - printk(KERN_ERR "%s: no parent clock\n", __func__); - return; - } - - for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) - clk_timer_scaler[clk].parent = clk_timers; - - s3c_register_clocks(clk_timer_scaler, ARRAY_SIZE(clk_timer_scaler)); - s3c_register_clocks(clk_timer_tclk, ARRAY_SIZE(clk_timer_tclk)); - - for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { - ret = clk_pwm_tdiv_register(clk); - - if (ret < 0) { - printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); - return; - } - } - - for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { - ret = clk_pwm_tin_register(&clk_tin[clk]); - if (ret < 0) { - printk(KERN_ERR "error adding pwm%d tin clock\n", clk); - return; - } - } -} diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 7612198..d4d5f94 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -12,5 +12,6 @@ obj- := # Objects we always build independent of SoC choice obj-y += clock.o +obj-y += pwm-clock.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o diff --git a/arch/arm/plat-samsung/pwm-clock.c b/arch/arm/plat-samsung/pwm-clock.c new file mode 100644 index 0000000..46c9381 --- /dev/null +++ b/arch/arm/plat-samsung/pwm-clock.c @@ -0,0 +1,455 @@ +/* linux/arch/arm/plat-s3c24xx/pwm-clock.c + * + * Copyright (c) 2007 Simtec Electronics + * Copyright (c) 2007, 2008 Ben Dooks + * Ben Dooks + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +/* Each of the timers 0 through 5 go through the following + * clock tree, with the inputs depending on the timers. + * + * pclk ---- [ prescaler 0 ] -+---> timer 0 + * +---> timer 1 + * + * pclk ---- [ prescaler 1 ] -+---> timer 2 + * +---> timer 3 + * \---> timer 4 + * + * Which are fed into the timers as so: + * + * prescaled 0 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 0 + * tclk 0 ------------------------------/ + * + * prescaled 0 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 1 + * tclk 0 ------------------------------/ + * + * + * prescaled 1 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 2 + * tclk 1 ------------------------------/ + * + * prescaled 1 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 3 + * tclk 1 ------------------------------/ + * + * prescaled 1 ---- [ div 2,4,8, 16 ] --\ + * [mux] -> timer 4 + * tclk 1 ------------------------------/ + * + * Since the mux and the divider are tied together in the + * same register space, it is impossible to set the parent + * and the rate at the same time. To avoid this, we add an + * intermediate 'prescaled-and-divided' clock to select + * as the parent for the timer input clock called tdiv. + * + * prescaled clk --> pwm-tdiv ---\ + * [ mux ] --> timer X + * tclk -------------------------/ +*/ + +static struct clk clk_timer_scaler[]; + +static unsigned long clk_pwm_scaler_get_rate(struct clk *clk) +{ + unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); + + if (clk == &clk_timer_scaler[1]) { + tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; + } else { + tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; + } + + return clk_get_rate(clk->parent) / (tcfg0 + 1); +} + +static unsigned long clk_pwm_scaler_round_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long divisor = parent_rate / rate; + + if (divisor > 256) + divisor = 256; + else if (divisor < 2) + divisor = 2; + + return parent_rate / divisor; +} + +static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long round = clk_pwm_scaler_round_rate(clk, rate); + unsigned long tcfg0; + unsigned long divisor; + unsigned long flags; + + divisor = clk_get_rate(clk->parent) / round; + divisor--; + + local_irq_save(flags); + tcfg0 = __raw_readl(S3C2410_TCFG0); + + if (clk == &clk_timer_scaler[1]) { + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT; + } else { + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; + tcfg0 |= divisor; + } + + __raw_writel(tcfg0, S3C2410_TCFG0); + local_irq_restore(flags); + + return 0; +} + +static struct clk_ops clk_pwm_scaler_ops = { + .get_rate = clk_pwm_scaler_get_rate, + .set_rate = clk_pwm_scaler_set_rate, + .round_rate = clk_pwm_scaler_round_rate, +}; + +static struct clk clk_timer_scaler[] = { + [0] = { + .name = "pwm-scaler0", + .id = -1, + .ops = &clk_pwm_scaler_ops, + }, + [1] = { + .name = "pwm-scaler1", + .id = -1, + .ops = &clk_pwm_scaler_ops, + }, +}; + +static struct clk clk_timer_tclk[] = { + [0] = { + .name = "pwm-tclk0", + .id = -1, + }, + [1] = { + .name = "pwm-tclk1", + .id = -1, + }, +}; + +struct pwm_tdiv_clk { + struct clk clk; + unsigned int divisor; +}; + +static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) +{ + return container_of(clk, struct pwm_tdiv_clk, clk); +} + +static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned int divisor; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + if (pwm_cfg_src_is_tclk(tcfg1)) + divisor = to_tdiv(clk)->divisor; + else + divisor = tcfg_to_divisor(tcfg1); + + return clk_get_rate(clk->parent) / divisor; +} + +static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate; + unsigned long divisor; + + parent_rate = clk_get_rate(clk->parent); + divisor = parent_rate / rate; + + if (divisor <= 1 && pwm_tdiv_has_div1()) + divisor = 1; + else if (divisor <= 2) + divisor = 2; + else if (divisor <= 4) + divisor = 4; + else if (divisor <= 8) + divisor = 8; + else + divisor = 16; + + return parent_rate / divisor; +} + +static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) +{ + return pwm_tdiv_div_bits(divclk->divisor); +} + +static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned long bits = clk_pwm_tdiv_bits(divclk); + unsigned long flags; + unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); + + local_irq_save(flags); + + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); + tcfg1 |= bits << shift; + __raw_writel(tcfg1, S3C2410_TCFG1); + + local_irq_restore(flags); +} + +static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) +{ + struct pwm_tdiv_clk *divclk = to_tdiv(clk); + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long divisor; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + rate = clk_round_rate(clk, rate); + divisor = parent_rate / rate; + + if (divisor > 16) + return -EINVAL; + + divclk->divisor = divisor; + + /* Update the current MUX settings if we are currently + * selected as the clock source for this clock. */ + + if (!pwm_cfg_src_is_tclk(tcfg1)) + clk_pwm_tdiv_update(divclk); + + return 0; +} + +static struct clk_ops clk_tdiv_ops = { + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, +}; + +static struct pwm_tdiv_clk clk_timer_tdiv[] = { + [0] = { + .clk = { + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[0], + }, + }, + [1] = { + .clk = { + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[0], + } + }, + [2] = { + .clk = { + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], + }, + }, + [3] = { + .clk = { + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], + }, + }, + [4] = { + .clk = { + .name = "pwm-tdiv", + .ops = &clk_tdiv_ops, + .parent = &clk_timer_scaler[1], + }, + }, +}; + +static int __init clk_pwm_tdiv_register(unsigned int id) +{ + struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + + tcfg1 >>= S3C2410_TCFG1_SHIFT(id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + divclk->clk.id = id; + divclk->divisor = tcfg_to_divisor(tcfg1); + + return s3c24xx_register_clock(&divclk->clk); +} + +static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) +{ + return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; +} + +static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) +{ + return &clk_timer_tdiv[id].clk; +} + +static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) +{ + unsigned int id = clk->id; + unsigned long tcfg1; + unsigned long flags; + unsigned long bits; + unsigned long shift = S3C2410_TCFG1_SHIFT(id); + + if (parent == s3c24xx_pwmclk_tclk(id)) + bits = S3C_TCFG1_MUX_TCLK << shift; + else if (parent == s3c24xx_pwmclk_tdiv(id)) + bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; + else + return -EINVAL; + + clk->parent = parent; + + local_irq_save(flags); + + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); + __raw_writel(tcfg1 | bits, S3C2410_TCFG1); + + local_irq_restore(flags); + + return 0; +} + +static struct clk_ops clk_tin_ops = { + .set_parent = clk_pwm_tin_set_parent, +}; + +static struct clk clk_tin[] = { + [0] = { + .name = "pwm-tin", + .id = 0, + .ops = &clk_tin_ops, + }, + [1] = { + .name = "pwm-tin", + .id = 1, + .ops = &clk_tin_ops, + }, + [2] = { + .name = "pwm-tin", + .id = 2, + .ops = &clk_tin_ops, + }, + [3] = { + .name = "pwm-tin", + .id = 3, + .ops = &clk_tin_ops, + }, + [4] = { + .name = "pwm-tin", + .id = 4, + .ops = &clk_tin_ops, + }, +}; + +static __init int clk_pwm_tin_register(struct clk *pwm) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned int id = pwm->id; + + struct clk *parent; + int ret; + + ret = s3c24xx_register_clock(pwm); + if (ret < 0) + return ret; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + if (pwm_cfg_src_is_tclk(tcfg1)) + parent = s3c24xx_pwmclk_tclk(id); + else + parent = s3c24xx_pwmclk_tdiv(id); + + return clk_set_parent(pwm, parent); +} + +/** + * s3c_pwmclk_init() - initialise pwm clocks + * + * Initialise and register the clocks which provide the inputs for the + * pwm timer blocks. + * + * Note, this call is required by the time core, so must be called after + * the base clocks are added and before any of the initcalls are run. + */ +__init void s3c_pwmclk_init(void) +{ + struct clk *clk_timers; + unsigned int clk; + int ret; + + clk_timers = clk_get(NULL, "timers"); + if (IS_ERR(clk_timers)) { + printk(KERN_ERR "%s: no parent clock\n", __func__); + return; + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) + clk_timer_scaler[clk].parent = clk_timers; + + s3c_register_clocks(clk_timer_scaler, ARRAY_SIZE(clk_timer_scaler)); + s3c_register_clocks(clk_timer_tclk, ARRAY_SIZE(clk_timer_tclk)); + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { + ret = clk_pwm_tdiv_register(clk); + + if (ret < 0) { + printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); + return; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { + ret = clk_pwm_tin_register(&clk_tin[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pwm%d tin clock\n", clk); + return; + } + } +} -- cgit v0.10.2 From b6a604137bb978d9f65b1228cf0bb691ece45cba Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 6 Jan 2010 02:45:09 +0900 Subject: ARM: SAMSUNG: Move device definitions in plat-samsung Move all the platform device definitions from plat-s3c into plat-samsung Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig index 9e9d028..cd979b1 100644 --- a/arch/arm/plat-s3c/Kconfig +++ b/arch/arm/plat-s3c/Kconfig @@ -170,46 +170,4 @@ config S3C_DMA help Internal configuration for S3C DMA core -# device definitions to compile in - -config S3C_DEV_HSMMC - bool - help - Compile in platform device definitions for HSMMC code - -config S3C_DEV_HSMMC1 - bool - help - Compile in platform device definitions for HSMMC channel 1 - -config S3C_DEV_HSMMC2 - bool - help - Compile in platform device definitions for HSMMC channel 2 - -config S3C_DEV_I2C1 - bool - help - Compile in platform device definitions for I2C channel 1 - -config S3C_DEV_FB - bool - help - Compile in platform device definition for framebuffer - -config S3C_DEV_USB_HOST - bool - help - Compile in platform device definition for USB host. - -config S3C_DEV_USB_HSOTG - bool - help - Compile in platform device definition for USB high-speed OtG - -config S3C_DEV_NAND - bool - help - Compile in platform device definition for NAND controller - endif diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index ef10a91..d862995 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -29,15 +29,3 @@ obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o # PWM support obj-$(CONFIG_HAVE_PWM) += pwm.o - -# devices - -obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o -obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmmc1.o -obj-$(CONFIG_S3C_DEV_HSMMC2) += dev-hsmmc2.o -obj-y += dev-i2c0.o -obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o -obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o -obj-$(CONFIG_S3C_DEV_USB_HOST) += dev-usb.o -obj-$(CONFIG_S3C_DEV_USB_HSOTG) += dev-usb-hsotg.o -obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o diff --git a/arch/arm/plat-s3c/dev-fb.c b/arch/arm/plat-s3c/dev-fb.c deleted file mode 100644 index a90198f..0000000 --- a/arch/arm/plat-s3c/dev-fb.c +++ /dev/null @@ -1,73 +0,0 @@ -/* linux/arch/arm/plat-s3c/dev-fb.c - * - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series device definition for framebuffer device - * - * 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 - -static struct resource s3c_fb_resource[] = { - [0] = { - .start = S3C_PA_FB, - .end = S3C_PA_FB + SZ_16K - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_LCD_VSYNC, - .end = IRQ_LCD_VSYNC, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_LCD_FIFO, - .end = IRQ_LCD_FIFO, - .flags = IORESOURCE_IRQ, - }, - [3] = { - .start = IRQ_LCD_SYSTEM, - .end = IRQ_LCD_SYSTEM, - .flags = IORESOURCE_IRQ, - }, -}; - -struct platform_device s3c_device_fb = { - .name = "s3c-fb", - .id = -1, - .num_resources = ARRAY_SIZE(s3c_fb_resource), - .resource = s3c_fb_resource, - .dev.dma_mask = &s3c_device_fb.dev.coherent_dma_mask, - .dev.coherent_dma_mask = 0xffffffffUL, -}; - -void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd) -{ - struct s3c_fb_platdata *npd; - - if (!pd) { - printk(KERN_ERR "%s: no platform data\n", __func__); - return; - } - - npd = kmemdup(pd, sizeof(struct s3c_fb_platdata), GFP_KERNEL); - if (!npd) - printk(KERN_ERR "%s: no memory for platform data\n", __func__); - - s3c_device_fb.dev.platform_data = npd; -} diff --git a/arch/arm/plat-s3c/dev-hsmmc.c b/arch/arm/plat-s3c/dev-hsmmc.c deleted file mode 100644 index 4c05b39..0000000 --- a/arch/arm/plat-s3c/dev-hsmmc.c +++ /dev/null @@ -1,68 +0,0 @@ -/* linux/arch/arm/plat-s3c/dev-hsmmc.c - * - * Copyright (c) 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series device definition for hsmmc devices - * - * 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 - -#define S3C_SZ_HSMMC (0x1000) - -static struct resource s3c_hsmmc_resource[] = { - [0] = { - .start = S3C_PA_HSMMC0, - .end = S3C_PA_HSMMC0 + S3C_SZ_HSMMC - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_HSMMC0, - .end = IRQ_HSMMC0, - .flags = IORESOURCE_IRQ, - } -}; - -static u64 s3c_device_hsmmc_dmamask = 0xffffffffUL; - -struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata = { - .max_width = 4, - .host_caps = (MMC_CAP_4_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), -}; - -struct platform_device s3c_device_hsmmc0 = { - .name = "s3c-sdhci", - .id = 0, - .num_resources = ARRAY_SIZE(s3c_hsmmc_resource), - .resource = s3c_hsmmc_resource, - .dev = { - .dma_mask = &s3c_device_hsmmc_dmamask, - .coherent_dma_mask = 0xffffffffUL, - .platform_data = &s3c_hsmmc0_def_platdata, - }, -}; - -void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd) -{ - struct s3c_sdhci_platdata *set = &s3c_hsmmc0_def_platdata; - - set->max_width = pd->max_width; - - if (pd->cfg_gpio) - set->cfg_gpio = pd->cfg_gpio; - if (pd->cfg_card) - set->cfg_card = pd->cfg_card; -} diff --git a/arch/arm/plat-s3c/dev-hsmmc1.c b/arch/arm/plat-s3c/dev-hsmmc1.c deleted file mode 100644 index e49bc4c..0000000 --- a/arch/arm/plat-s3c/dev-hsmmc1.c +++ /dev/null @@ -1,68 +0,0 @@ -/* linux/arch/arm/plat-s3c/dev-hsmmc1.c - * - * Copyright (c) 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series device definition for hsmmc device 1 - * - * 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 - -#define S3C_SZ_HSMMC (0x1000) - -static struct resource s3c_hsmmc1_resource[] = { - [0] = { - .start = S3C_PA_HSMMC1, - .end = S3C_PA_HSMMC1 + S3C_SZ_HSMMC - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_HSMMC1, - .end = IRQ_HSMMC1, - .flags = IORESOURCE_IRQ, - } -}; - -static u64 s3c_device_hsmmc1_dmamask = 0xffffffffUL; - -struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata = { - .max_width = 4, - .host_caps = (MMC_CAP_4_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), -}; - -struct platform_device s3c_device_hsmmc1 = { - .name = "s3c-sdhci", - .id = 1, - .num_resources = ARRAY_SIZE(s3c_hsmmc1_resource), - .resource = s3c_hsmmc1_resource, - .dev = { - .dma_mask = &s3c_device_hsmmc1_dmamask, - .coherent_dma_mask = 0xffffffffUL, - .platform_data = &s3c_hsmmc1_def_platdata, - }, -}; - -void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd) -{ - struct s3c_sdhci_platdata *set = &s3c_hsmmc1_def_platdata; - - set->max_width = pd->max_width; - - if (pd->cfg_gpio) - set->cfg_gpio = pd->cfg_gpio; - if (pd->cfg_card) - set->cfg_card = pd->cfg_card; -} diff --git a/arch/arm/plat-s3c/dev-hsmmc2.c b/arch/arm/plat-s3c/dev-hsmmc2.c deleted file mode 100644 index 824580b..0000000 --- a/arch/arm/plat-s3c/dev-hsmmc2.c +++ /dev/null @@ -1,69 +0,0 @@ -/* linux/arch/arm/plat-s3c/dev-hsmmc2.c - * - * Copyright (c) 2009 Samsung Electronics - * Copyright (c) 2009 Maurus Cuelenaere - * - * Based on arch/arm/plat-s3c/dev-hsmmc1.c - * original file Copyright (c) 2008 Simtec Electronics - * - * S3C series device definition for hsmmc device 2 - * - * 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 - -#define S3C_SZ_HSMMC (0x1000) - -static struct resource s3c_hsmmc2_resource[] = { - [0] = { - .start = S3C_PA_HSMMC2, - .end = S3C_PA_HSMMC2 + S3C_SZ_HSMMC - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_HSMMC2, - .end = IRQ_HSMMC2, - .flags = IORESOURCE_IRQ, - } -}; - -static u64 s3c_device_hsmmc2_dmamask = 0xffffffffUL; - -struct s3c_sdhci_platdata s3c_hsmmc2_def_platdata = { - .max_width = 4, - .host_caps = (MMC_CAP_4_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), -}; - -struct platform_device s3c_device_hsmmc2 = { - .name = "s3c-sdhci", - .id = 2, - .num_resources = ARRAY_SIZE(s3c_hsmmc2_resource), - .resource = s3c_hsmmc2_resource, - .dev = { - .dma_mask = &s3c_device_hsmmc2_dmamask, - .coherent_dma_mask = 0xffffffffUL, - .platform_data = &s3c_hsmmc2_def_platdata, - }, -}; - -void s3c_sdhci2_set_platdata(struct s3c_sdhci_platdata *pd) -{ - struct s3c_sdhci_platdata *set = &s3c_hsmmc2_def_platdata; - - set->max_width = pd->max_width; - - if (pd->cfg_gpio) - set->cfg_gpio = pd->cfg_gpio; - if (pd->cfg_card) - set->cfg_card = pd->cfg_card; -} diff --git a/arch/arm/plat-s3c/dev-i2c0.c b/arch/arm/plat-s3c/dev-i2c0.c deleted file mode 100644 index 4c76152..0000000 --- a/arch/arm/plat-s3c/dev-i2c0.c +++ /dev/null @@ -1,71 +0,0 @@ -/* linux/arch/arm/plat-s3c/dev-i2c0.c - * - * Copyright 2008-2009 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series device definition for i2c device 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. -*/ - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -static struct resource s3c_i2c_resource[] = { - [0] = { - .start = S3C_PA_IIC, - .end = S3C_PA_IIC + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_IIC, - .end = IRQ_IIC, - .flags = IORESOURCE_IRQ, - }, -}; - -struct platform_device s3c_device_i2c0 = { - .name = "s3c2410-i2c", -#ifdef CONFIG_S3C_DEV_I2C1 - .id = 0, -#else - .id = -1, -#endif - .num_resources = ARRAY_SIZE(s3c_i2c_resource), - .resource = s3c_i2c_resource, -}; - -static struct s3c2410_platform_i2c default_i2c_data0 __initdata = { - .flags = 0, - .slave_addr = 0x10, - .frequency = 100*1000, - .sda_delay = 100, -}; - -void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) -{ - struct s3c2410_platform_i2c *npd; - - if (!pd) - pd = &default_i2c_data0; - - npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); - if (!npd) - printk(KERN_ERR "%s: no memory for platform data\n", __func__); - else if (!npd->cfg_gpio) - npd->cfg_gpio = s3c_i2c0_cfg_gpio; - - s3c_device_i2c0.dev.platform_data = npd; -} diff --git a/arch/arm/plat-s3c/dev-i2c1.c b/arch/arm/plat-s3c/dev-i2c1.c deleted file mode 100644 index d44f791..0000000 --- a/arch/arm/plat-s3c/dev-i2c1.c +++ /dev/null @@ -1,68 +0,0 @@ -/* linux/arch/arm/plat-s3c/dev-i2c1.c - * - * Copyright 2008-2009 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series device definition for i2c device 1 - * - * 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 - -static struct resource s3c_i2c_resource[] = { - [0] = { - .start = S3C_PA_IIC1, - .end = S3C_PA_IIC1 + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_IIC1, - .end = IRQ_IIC1, - .flags = IORESOURCE_IRQ, - }, -}; - -struct platform_device s3c_device_i2c1 = { - .name = "s3c2410-i2c", - .id = 1, - .num_resources = ARRAY_SIZE(s3c_i2c_resource), - .resource = s3c_i2c_resource, -}; - -static struct s3c2410_platform_i2c default_i2c_data1 __initdata = { - .flags = 0, - .bus_num = 1, - .slave_addr = 0x10, - .frequency = 100*1000, - .sda_delay = 100, -}; - -void __init s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *pd) -{ - struct s3c2410_platform_i2c *npd; - - if (!pd) - pd = &default_i2c_data1; - - npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); - if (!npd) - printk(KERN_ERR "%s: no memory for platform data\n", __func__); - else if (!npd->cfg_gpio) - npd->cfg_gpio = s3c_i2c1_cfg_gpio; - - s3c_device_i2c1.dev.platform_data = npd; -} diff --git a/arch/arm/plat-s3c/dev-nand.c b/arch/arm/plat-s3c/dev-nand.c deleted file mode 100644 index a52fb6c..0000000 --- a/arch/arm/plat-s3c/dev-nand.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * S3C series device definition for nand device - * - * 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 - -static struct resource s3c_nand_resource[] = { - [0] = { - .start = S3C_PA_NAND, - .end = S3C_PA_NAND + SZ_1M, - .flags = IORESOURCE_MEM, - } -}; - -struct platform_device s3c_device_nand = { - .name = "s3c2410-nand", - .id = -1, - .num_resources = ARRAY_SIZE(s3c_nand_resource), - .resource = s3c_nand_resource, -}; - -EXPORT_SYMBOL(s3c_device_nand); - -/** - * s3c_nand_copy_set() - copy nand set data - * @set: The new structure, directly copied from the old. - * - * Copy all the fields from the NAND set field from what is probably __initdata - * to new kernel memory. The code returns 0 if the copy happened correctly or - * an error code for the calling function to display. - * - * Note, we currently do not try and look to see if we've already copied the - * data in a previous set. - */ -static int __init s3c_nand_copy_set(struct s3c2410_nand_set *set) -{ - void *ptr; - int size; - - size = sizeof(struct mtd_partition) * set->nr_partitions; - if (size) { - ptr = kmemdup(set->partitions, size, GFP_KERNEL); - set->partitions = ptr; - - if (!ptr) - return -ENOMEM; - } - - if (set->nr_map && set->nr_chips) { - size = sizeof(int) * set->nr_chips; - ptr = kmemdup(set->nr_map, size, GFP_KERNEL); - set->nr_map = ptr; - - if (!ptr) - return -ENOMEM; - } - - if (set->ecc_layout) { - ptr = kmemdup(set->ecc_layout, - sizeof(struct nand_ecclayout), GFP_KERNEL); - set->ecc_layout = ptr; - - if (!ptr) - return -ENOMEM; - } - - return 0; -} - -void __init s3c_nand_set_platdata(struct s3c2410_platform_nand *nand) -{ - struct s3c2410_platform_nand *npd; - int size; - int ret; - - /* note, if we get a failure in allocation, we simply drop out of the - * function. If there is so little memory available at initialisation - * time then there is little chance the system is going to run. - */ - - npd = kmemdup(nand, sizeof(struct s3c2410_platform_nand), GFP_KERNEL); - if (!npd) { - printk(KERN_ERR "%s: failed copying platform data\n", __func__); - return; - } - - /* now see if we need to copy any of the nand set data */ - - size = sizeof(struct s3c2410_nand_set) * npd->nr_sets; - if (size) { - struct s3c2410_nand_set *from = npd->sets; - struct s3c2410_nand_set *to; - int i; - - to = kmemdup(from, size, GFP_KERNEL); - npd->sets = to; /* set, even if we failed */ - - if (!to) { - printk(KERN_ERR "%s: no memory for sets\n", __func__); - return; - } - - for (i = 0; i < npd->nr_sets; i++) { - ret = s3c_nand_copy_set(to); - if (ret) { - printk(KERN_ERR "%s: failed to copy set %d\n", - __func__, i); - return; - } - to++; - } - } - - s3c_device_nand.dev.platform_data = npd; -} - -EXPORT_SYMBOL_GPL(s3c_nand_set_platdata); diff --git a/arch/arm/plat-s3c/dev-usb-hsotg.c b/arch/arm/plat-s3c/dev-usb-hsotg.c deleted file mode 100644 index e2f604b..0000000 --- a/arch/arm/plat-s3c/dev-usb-hsotg.c +++ /dev/null @@ -1,41 +0,0 @@ -/* linux/arch/arm/plat-s3c/dev-usb-hsotg.c - * - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series device definition for USB high-speed UDC/OtG block - * - * 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 - -static struct resource s3c_usb_hsotg_resources[] = { - [0] = { - .start = S3C_PA_USB_HSOTG, - .end = S3C_PA_USB_HSOTG + 0x10000 - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_OTG, - .end = IRQ_OTG, - .flags = IORESOURCE_IRQ, - }, -}; - -struct platform_device s3c_device_usb_hsotg = { - .name = "s3c-hsotg", - .id = -1, - .num_resources = ARRAY_SIZE(s3c_usb_hsotg_resources), - .resource = s3c_usb_hsotg_resources, -}; diff --git a/arch/arm/plat-s3c/dev-usb.c b/arch/arm/plat-s3c/dev-usb.c deleted file mode 100644 index 2ee85ab..0000000 --- a/arch/arm/plat-s3c/dev-usb.c +++ /dev/null @@ -1,50 +0,0 @@ -/* linux/arch/arm/plat-s3c/dev-usb.c - * - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series device definition for USB host - * - * 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 - - -static struct resource s3c_usb_resource[] = { - [0] = { - .start = S3C_PA_USBHOST, - .end = S3C_PA_USBHOST + 0x100 - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_USBH, - .end = IRQ_USBH, - .flags = IORESOURCE_IRQ, - } -}; - -static u64 s3c_device_usb_dmamask = 0xffffffffUL; - -struct platform_device s3c_device_usb = { - .name = "s3c2410-ohci", - .id = -1, - .num_resources = ARRAY_SIZE(s3c_usb_resource), - .resource = s3c_usb_resource, - .dev = { - .dma_mask = &s3c_device_usb_dmamask, - .coherent_dma_mask = 0xffffffffUL - } -}; - -EXPORT_SYMBOL(s3c_device_usb); diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index e3ae68472..5a72a52 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -19,4 +19,46 @@ config SAMSUNG_CLKSRC Select the clock code for the clksrc implementation used by newer systems such as the S3C64XX. +# device definitions to compile in + +config S3C_DEV_HSMMC + bool + help + Compile in platform device definitions for HSMMC code + +config S3C_DEV_HSMMC1 + bool + help + Compile in platform device definitions for HSMMC channel 1 + +config S3C_DEV_HSMMC2 + bool + help + Compile in platform device definitions for HSMMC channel 2 + +config S3C_DEV_I2C1 + bool + help + Compile in platform device definitions for I2C channel 1 + +config S3C_DEV_FB + bool + help + Compile in platform device definition for framebuffer + +config S3C_DEV_USB_HOST + bool + help + Compile in platform device definition for USB host. + +config S3C_DEV_USB_HSOTG + bool + help + Compile in platform device definition for USB high-speed OtG + +config S3C_DEV_NAND + bool + help + Compile in platform device definition for NAND controller + endif diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index d4d5f94..32f03e5 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -15,3 +15,15 @@ obj-y += clock.o obj-y += pwm-clock.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o + +# devices + +obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o +obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmmc1.o +obj-$(CONFIG_S3C_DEV_HSMMC2) += dev-hsmmc2.o +obj-y += dev-i2c0.o +obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o +obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o +obj-$(CONFIG_S3C_DEV_USB_HOST) += dev-usb.o +obj-$(CONFIG_S3C_DEV_USB_HSOTG) += dev-usb-hsotg.o +obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o diff --git a/arch/arm/plat-samsung/dev-fb.c b/arch/arm/plat-samsung/dev-fb.c new file mode 100644 index 0000000..a90198f --- /dev/null +++ b/arch/arm/plat-samsung/dev-fb.c @@ -0,0 +1,73 @@ +/* linux/arch/arm/plat-s3c/dev-fb.c + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series device definition for framebuffer device + * + * 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 + +static struct resource s3c_fb_resource[] = { + [0] = { + .start = S3C_PA_FB, + .end = S3C_PA_FB + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LCD_VSYNC, + .end = IRQ_LCD_VSYNC, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_LCD_FIFO, + .end = IRQ_LCD_FIFO, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = IRQ_LCD_SYSTEM, + .end = IRQ_LCD_SYSTEM, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device s3c_device_fb = { + .name = "s3c-fb", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_fb_resource), + .resource = s3c_fb_resource, + .dev.dma_mask = &s3c_device_fb.dev.coherent_dma_mask, + .dev.coherent_dma_mask = 0xffffffffUL, +}; + +void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd) +{ + struct s3c_fb_platdata *npd; + + if (!pd) { + printk(KERN_ERR "%s: no platform data\n", __func__); + return; + } + + npd = kmemdup(pd, sizeof(struct s3c_fb_platdata), GFP_KERNEL); + if (!npd) + printk(KERN_ERR "%s: no memory for platform data\n", __func__); + + s3c_device_fb.dev.platform_data = npd; +} diff --git a/arch/arm/plat-samsung/dev-hsmmc.c b/arch/arm/plat-samsung/dev-hsmmc.c new file mode 100644 index 0000000..4c05b39 --- /dev/null +++ b/arch/arm/plat-samsung/dev-hsmmc.c @@ -0,0 +1,68 @@ +/* linux/arch/arm/plat-s3c/dev-hsmmc.c + * + * Copyright (c) 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series device definition for hsmmc devices + * + * 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 + +#define S3C_SZ_HSMMC (0x1000) + +static struct resource s3c_hsmmc_resource[] = { + [0] = { + .start = S3C_PA_HSMMC0, + .end = S3C_PA_HSMMC0 + S3C_SZ_HSMMC - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_HSMMC0, + .end = IRQ_HSMMC0, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_device_hsmmc_dmamask = 0xffffffffUL; + +struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata = { + .max_width = 4, + .host_caps = (MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), +}; + +struct platform_device s3c_device_hsmmc0 = { + .name = "s3c-sdhci", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_hsmmc_resource), + .resource = s3c_hsmmc_resource, + .dev = { + .dma_mask = &s3c_device_hsmmc_dmamask, + .coherent_dma_mask = 0xffffffffUL, + .platform_data = &s3c_hsmmc0_def_platdata, + }, +}; + +void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd) +{ + struct s3c_sdhci_platdata *set = &s3c_hsmmc0_def_platdata; + + set->max_width = pd->max_width; + + if (pd->cfg_gpio) + set->cfg_gpio = pd->cfg_gpio; + if (pd->cfg_card) + set->cfg_card = pd->cfg_card; +} diff --git a/arch/arm/plat-samsung/dev-hsmmc1.c b/arch/arm/plat-samsung/dev-hsmmc1.c new file mode 100644 index 0000000..e49bc4c --- /dev/null +++ b/arch/arm/plat-samsung/dev-hsmmc1.c @@ -0,0 +1,68 @@ +/* linux/arch/arm/plat-s3c/dev-hsmmc1.c + * + * Copyright (c) 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series device definition for hsmmc device 1 + * + * 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 + +#define S3C_SZ_HSMMC (0x1000) + +static struct resource s3c_hsmmc1_resource[] = { + [0] = { + .start = S3C_PA_HSMMC1, + .end = S3C_PA_HSMMC1 + S3C_SZ_HSMMC - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_HSMMC1, + .end = IRQ_HSMMC1, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_device_hsmmc1_dmamask = 0xffffffffUL; + +struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata = { + .max_width = 4, + .host_caps = (MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), +}; + +struct platform_device s3c_device_hsmmc1 = { + .name = "s3c-sdhci", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_hsmmc1_resource), + .resource = s3c_hsmmc1_resource, + .dev = { + .dma_mask = &s3c_device_hsmmc1_dmamask, + .coherent_dma_mask = 0xffffffffUL, + .platform_data = &s3c_hsmmc1_def_platdata, + }, +}; + +void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd) +{ + struct s3c_sdhci_platdata *set = &s3c_hsmmc1_def_platdata; + + set->max_width = pd->max_width; + + if (pd->cfg_gpio) + set->cfg_gpio = pd->cfg_gpio; + if (pd->cfg_card) + set->cfg_card = pd->cfg_card; +} diff --git a/arch/arm/plat-samsung/dev-hsmmc2.c b/arch/arm/plat-samsung/dev-hsmmc2.c new file mode 100644 index 0000000..824580b --- /dev/null +++ b/arch/arm/plat-samsung/dev-hsmmc2.c @@ -0,0 +1,69 @@ +/* linux/arch/arm/plat-s3c/dev-hsmmc2.c + * + * Copyright (c) 2009 Samsung Electronics + * Copyright (c) 2009 Maurus Cuelenaere + * + * Based on arch/arm/plat-s3c/dev-hsmmc1.c + * original file Copyright (c) 2008 Simtec Electronics + * + * S3C series device definition for hsmmc device 2 + * + * 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 + +#define S3C_SZ_HSMMC (0x1000) + +static struct resource s3c_hsmmc2_resource[] = { + [0] = { + .start = S3C_PA_HSMMC2, + .end = S3C_PA_HSMMC2 + S3C_SZ_HSMMC - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_HSMMC2, + .end = IRQ_HSMMC2, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_device_hsmmc2_dmamask = 0xffffffffUL; + +struct s3c_sdhci_platdata s3c_hsmmc2_def_platdata = { + .max_width = 4, + .host_caps = (MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), +}; + +struct platform_device s3c_device_hsmmc2 = { + .name = "s3c-sdhci", + .id = 2, + .num_resources = ARRAY_SIZE(s3c_hsmmc2_resource), + .resource = s3c_hsmmc2_resource, + .dev = { + .dma_mask = &s3c_device_hsmmc2_dmamask, + .coherent_dma_mask = 0xffffffffUL, + .platform_data = &s3c_hsmmc2_def_platdata, + }, +}; + +void s3c_sdhci2_set_platdata(struct s3c_sdhci_platdata *pd) +{ + struct s3c_sdhci_platdata *set = &s3c_hsmmc2_def_platdata; + + set->max_width = pd->max_width; + + if (pd->cfg_gpio) + set->cfg_gpio = pd->cfg_gpio; + if (pd->cfg_card) + set->cfg_card = pd->cfg_card; +} diff --git a/arch/arm/plat-samsung/dev-i2c0.c b/arch/arm/plat-samsung/dev-i2c0.c new file mode 100644 index 0000000..4c76152 --- /dev/null +++ b/arch/arm/plat-samsung/dev-i2c0.c @@ -0,0 +1,71 @@ +/* linux/arch/arm/plat-s3c/dev-i2c0.c + * + * Copyright 2008-2009 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series device definition for i2c device 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. +*/ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +static struct resource s3c_i2c_resource[] = { + [0] = { + .start = S3C_PA_IIC, + .end = S3C_PA_IIC + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_IIC, + .end = IRQ_IIC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device s3c_device_i2c0 = { + .name = "s3c2410-i2c", +#ifdef CONFIG_S3C_DEV_I2C1 + .id = 0, +#else + .id = -1, +#endif + .num_resources = ARRAY_SIZE(s3c_i2c_resource), + .resource = s3c_i2c_resource, +}; + +static struct s3c2410_platform_i2c default_i2c_data0 __initdata = { + .flags = 0, + .slave_addr = 0x10, + .frequency = 100*1000, + .sda_delay = 100, +}; + +void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) +{ + struct s3c2410_platform_i2c *npd; + + if (!pd) + pd = &default_i2c_data0; + + npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); + if (!npd) + printk(KERN_ERR "%s: no memory for platform data\n", __func__); + else if (!npd->cfg_gpio) + npd->cfg_gpio = s3c_i2c0_cfg_gpio; + + s3c_device_i2c0.dev.platform_data = npd; +} diff --git a/arch/arm/plat-samsung/dev-i2c1.c b/arch/arm/plat-samsung/dev-i2c1.c new file mode 100644 index 0000000..d44f791 --- /dev/null +++ b/arch/arm/plat-samsung/dev-i2c1.c @@ -0,0 +1,68 @@ +/* linux/arch/arm/plat-s3c/dev-i2c1.c + * + * Copyright 2008-2009 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series device definition for i2c device 1 + * + * 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 + +static struct resource s3c_i2c_resource[] = { + [0] = { + .start = S3C_PA_IIC1, + .end = S3C_PA_IIC1 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_IIC1, + .end = IRQ_IIC1, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device s3c_device_i2c1 = { + .name = "s3c2410-i2c", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_i2c_resource), + .resource = s3c_i2c_resource, +}; + +static struct s3c2410_platform_i2c default_i2c_data1 __initdata = { + .flags = 0, + .bus_num = 1, + .slave_addr = 0x10, + .frequency = 100*1000, + .sda_delay = 100, +}; + +void __init s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *pd) +{ + struct s3c2410_platform_i2c *npd; + + if (!pd) + pd = &default_i2c_data1; + + npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); + if (!npd) + printk(KERN_ERR "%s: no memory for platform data\n", __func__); + else if (!npd->cfg_gpio) + npd->cfg_gpio = s3c_i2c1_cfg_gpio; + + s3c_device_i2c1.dev.platform_data = npd; +} diff --git a/arch/arm/plat-samsung/dev-nand.c b/arch/arm/plat-samsung/dev-nand.c new file mode 100644 index 0000000..a52fb6c --- /dev/null +++ b/arch/arm/plat-samsung/dev-nand.c @@ -0,0 +1,129 @@ +/* + * S3C series device definition for nand device + * + * 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 + +static struct resource s3c_nand_resource[] = { + [0] = { + .start = S3C_PA_NAND, + .end = S3C_PA_NAND + SZ_1M, + .flags = IORESOURCE_MEM, + } +}; + +struct platform_device s3c_device_nand = { + .name = "s3c2410-nand", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_nand_resource), + .resource = s3c_nand_resource, +}; + +EXPORT_SYMBOL(s3c_device_nand); + +/** + * s3c_nand_copy_set() - copy nand set data + * @set: The new structure, directly copied from the old. + * + * Copy all the fields from the NAND set field from what is probably __initdata + * to new kernel memory. The code returns 0 if the copy happened correctly or + * an error code for the calling function to display. + * + * Note, we currently do not try and look to see if we've already copied the + * data in a previous set. + */ +static int __init s3c_nand_copy_set(struct s3c2410_nand_set *set) +{ + void *ptr; + int size; + + size = sizeof(struct mtd_partition) * set->nr_partitions; + if (size) { + ptr = kmemdup(set->partitions, size, GFP_KERNEL); + set->partitions = ptr; + + if (!ptr) + return -ENOMEM; + } + + if (set->nr_map && set->nr_chips) { + size = sizeof(int) * set->nr_chips; + ptr = kmemdup(set->nr_map, size, GFP_KERNEL); + set->nr_map = ptr; + + if (!ptr) + return -ENOMEM; + } + + if (set->ecc_layout) { + ptr = kmemdup(set->ecc_layout, + sizeof(struct nand_ecclayout), GFP_KERNEL); + set->ecc_layout = ptr; + + if (!ptr) + return -ENOMEM; + } + + return 0; +} + +void __init s3c_nand_set_platdata(struct s3c2410_platform_nand *nand) +{ + struct s3c2410_platform_nand *npd; + int size; + int ret; + + /* note, if we get a failure in allocation, we simply drop out of the + * function. If there is so little memory available at initialisation + * time then there is little chance the system is going to run. + */ + + npd = kmemdup(nand, sizeof(struct s3c2410_platform_nand), GFP_KERNEL); + if (!npd) { + printk(KERN_ERR "%s: failed copying platform data\n", __func__); + return; + } + + /* now see if we need to copy any of the nand set data */ + + size = sizeof(struct s3c2410_nand_set) * npd->nr_sets; + if (size) { + struct s3c2410_nand_set *from = npd->sets; + struct s3c2410_nand_set *to; + int i; + + to = kmemdup(from, size, GFP_KERNEL); + npd->sets = to; /* set, even if we failed */ + + if (!to) { + printk(KERN_ERR "%s: no memory for sets\n", __func__); + return; + } + + for (i = 0; i < npd->nr_sets; i++) { + ret = s3c_nand_copy_set(to); + if (ret) { + printk(KERN_ERR "%s: failed to copy set %d\n", + __func__, i); + return; + } + to++; + } + } + + s3c_device_nand.dev.platform_data = npd; +} + +EXPORT_SYMBOL_GPL(s3c_nand_set_platdata); diff --git a/arch/arm/plat-samsung/dev-usb-hsotg.c b/arch/arm/plat-samsung/dev-usb-hsotg.c new file mode 100644 index 0000000..e2f604b --- /dev/null +++ b/arch/arm/plat-samsung/dev-usb-hsotg.c @@ -0,0 +1,41 @@ +/* linux/arch/arm/plat-s3c/dev-usb-hsotg.c + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series device definition for USB high-speed UDC/OtG block + * + * 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 + +static struct resource s3c_usb_hsotg_resources[] = { + [0] = { + .start = S3C_PA_USB_HSOTG, + .end = S3C_PA_USB_HSOTG + 0x10000 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_OTG, + .end = IRQ_OTG, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device s3c_device_usb_hsotg = { + .name = "s3c-hsotg", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usb_hsotg_resources), + .resource = s3c_usb_hsotg_resources, +}; diff --git a/arch/arm/plat-samsung/dev-usb.c b/arch/arm/plat-samsung/dev-usb.c new file mode 100644 index 0000000..2ee85ab --- /dev/null +++ b/arch/arm/plat-samsung/dev-usb.c @@ -0,0 +1,50 @@ +/* linux/arch/arm/plat-s3c/dev-usb.c + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series device definition for USB host + * + * 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 + + +static struct resource s3c_usb_resource[] = { + [0] = { + .start = S3C_PA_USBHOST, + .end = S3C_PA_USBHOST + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBH, + .end = IRQ_USBH, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_device_usb_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_usb = { + .name = "s3c2410-ohci", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usb_resource), + .resource = s3c_usb_resource, + .dev = { + .dma_mask = &s3c_device_usb_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_usb); -- cgit v0.10.2 From 4f830db9629e413e7c5523085ab009b0de5ae6d0 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 6 Jan 2010 02:28:20 +0900 Subject: ARM: SAMSUNG: Move gpio-config.c into plat-samsung The arch/arm/plat-s3c/gpio-config.c file is common to pretty much all the Samsung SoCs, so move it to arch/arm/plat-samsung Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig index cd979b1..454cc39 100644 --- a/arch/arm/plat-s3c/Kconfig +++ b/arch/arm/plat-s3c/Kconfig @@ -130,39 +130,6 @@ config S3C_GPIO_TRACK Internal configuration option to enable the s3c specific gpio chip tracking if the platform requires it. -config S3C_GPIO_PULL_UPDOWN - bool - help - Internal configuration to enable the correct GPIO pull helper - -config S3C_GPIO_PULL_DOWN - bool - help - Internal configuration to enable the correct GPIO pull helper - -config S3C_GPIO_PULL_UP - bool - help - Internal configuration to enable the correct GPIO pull helper - -config S3C_GPIO_CFG_S3C24XX - bool - help - Internal configuration to enable S3C24XX style GPIO configuration - functions. - -config S3C_GPIO_CFG_S3C64XX - bool - help - Internal configuration to enable S3C64XX style GPIO configuration - functions. - -config S5P_GPIO_CFG_S5PC1XX - bool - help - Internal configuration to enable S5PC1XX style GPIO configuration - functions. - # DMA config S3C_DMA diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index d862995..ea4a001 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -14,7 +14,6 @@ obj- := obj-y += init.o obj-y += time.o obj-y += gpio.o -obj-y += gpio-config.o # DMA support diff --git a/arch/arm/plat-s3c/gpio-config.c b/arch/arm/plat-s3c/gpio-config.c deleted file mode 100644 index 456969b..0000000 --- a/arch/arm/plat-s3c/gpio-config.c +++ /dev/null @@ -1,166 +0,0 @@ -/* linux/arch/arm/plat-s3c/gpio-config.c - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series GPIO configuration core - * - * 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 - -int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long flags; - int offset; - int ret; - - if (!chip) - return -EINVAL; - - offset = pin - chip->chip.base; - - local_irq_save(flags); - ret = s3c_gpio_do_setcfg(chip, offset, config); - local_irq_restore(flags); - - return ret; -} -EXPORT_SYMBOL(s3c_gpio_cfgpin); - -int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long flags; - int offset, ret; - - if (!chip) - return -EINVAL; - - offset = pin - chip->chip.base; - - local_irq_save(flags); - ret = s3c_gpio_do_setpull(chip, offset, pull); - local_irq_restore(flags); - - return ret; -} -EXPORT_SYMBOL(s3c_gpio_setpull); - -#ifdef CONFIG_S3C_GPIO_CFG_S3C24XX -int s3c_gpio_setcfg_s3c24xx_banka(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg) -{ - void __iomem *reg = chip->base; - unsigned int shift = off; - u32 con; - - if (s3c_gpio_is_cfg_special(cfg)) { - cfg &= 0xf; - - /* Map output to 0, and SFN2 to 1 */ - cfg -= 1; - if (cfg > 1) - return -EINVAL; - - cfg <<= shift; - } - - con = __raw_readl(reg); - con &= ~(0x1 << shift); - con |= cfg; - __raw_writel(con, reg); - - return 0; -} - -int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg) -{ - void __iomem *reg = chip->base; - unsigned int shift = off * 2; - u32 con; - - if (s3c_gpio_is_cfg_special(cfg)) { - cfg &= 0xf; - if (cfg > 3) - return -EINVAL; - - cfg <<= shift; - } - - con = __raw_readl(reg); - con &= ~(0x3 << shift); - con |= cfg; - __raw_writel(con, reg); - - return 0; -} -#endif - -#ifdef CONFIG_S3C_GPIO_CFG_S3C64XX -int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg) -{ - void __iomem *reg = chip->base; - unsigned int shift = (off & 7) * 4; - u32 con; - - if (off < 8 && chip->chip.ngpio > 8) - reg -= 4; - - if (s3c_gpio_is_cfg_special(cfg)) { - cfg &= 0xf; - cfg <<= shift; - } - - con = __raw_readl(reg); - con &= ~(0xf << shift); - con |= cfg; - __raw_writel(con, reg); - - return 0; -} -#endif /* CONFIG_S3C_GPIO_CFG_S3C64XX */ - -#ifdef CONFIG_S3C_GPIO_PULL_UPDOWN -int s3c_gpio_setpull_updown(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) -{ - void __iomem *reg = chip->base + 0x08; - int shift = off * 2; - u32 pup; - - pup = __raw_readl(reg); - pup &= ~(3 << shift); - pup |= pull << shift; - __raw_writel(pup, reg); - - return 0; -} - -s3c_gpio_pull_t s3c_gpio_getpull_updown(struct s3c_gpio_chip *chip, - unsigned int off) -{ - void __iomem *reg = chip->base + 0x08; - int shift = off * 2; - u32 pup = __raw_readl(reg); - - pup >>= shift; - pup &= 0x3; - return (__force s3c_gpio_pull_t)pup; -} -#endif diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 5a72a52..9e7daf2 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -19,6 +19,41 @@ config SAMSUNG_CLKSRC Select the clock code for the clksrc implementation used by newer systems such as the S3C64XX. +# options for gpio configuration support + +config S3C_GPIO_CFG_S3C24XX + bool + help + Internal configuration to enable S3C24XX style GPIO configuration + functions. + +config S3C_GPIO_CFG_S3C64XX + bool + help + Internal configuration to enable S3C64XX style GPIO configuration + functions. + +config S5P_GPIO_CFG_S5PC1XX + bool + help + Internal configuration to enable S5PC1XX style GPIO configuration + functions. + +config S3C_GPIO_PULL_UPDOWN + bool + help + Internal configuration to enable the correct GPIO pull helper + +config S3C_GPIO_PULL_DOWN + bool + help + Internal configuration to enable the correct GPIO pull helper + +config S3C_GPIO_PULL_UP + bool + help + Internal configuration to enable the correct GPIO pull helper + # device definitions to compile in config S3C_DEV_HSMMC diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 32f03e5..2c01437 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -13,6 +13,7 @@ obj- := obj-y += clock.o obj-y += pwm-clock.o +obj-y += gpio-config.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o diff --git a/arch/arm/plat-samsung/gpio-config.c b/arch/arm/plat-samsung/gpio-config.c new file mode 100644 index 0000000..456969b --- /dev/null +++ b/arch/arm/plat-samsung/gpio-config.c @@ -0,0 +1,166 @@ +/* linux/arch/arm/plat-s3c/gpio-config.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series GPIO configuration core + * + * 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 + +int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) +{ + struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); + unsigned long flags; + int offset; + int ret; + + if (!chip) + return -EINVAL; + + offset = pin - chip->chip.base; + + local_irq_save(flags); + ret = s3c_gpio_do_setcfg(chip, offset, config); + local_irq_restore(flags); + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_cfgpin); + +int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull) +{ + struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); + unsigned long flags; + int offset, ret; + + if (!chip) + return -EINVAL; + + offset = pin - chip->chip.base; + + local_irq_save(flags); + ret = s3c_gpio_do_setpull(chip, offset, pull); + local_irq_restore(flags); + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_setpull); + +#ifdef CONFIG_S3C_GPIO_CFG_S3C24XX +int s3c_gpio_setcfg_s3c24xx_banka(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = off; + u32 con; + + if (s3c_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + + /* Map output to 0, and SFN2 to 1 */ + cfg -= 1; + if (cfg > 1) + return -EINVAL; + + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0x1 << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = off * 2; + u32 con; + + if (s3c_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + if (cfg > 3) + return -EINVAL; + + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0x3 << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} +#endif + +#ifdef CONFIG_S3C_GPIO_CFG_S3C64XX +int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = (off & 7) * 4; + u32 con; + + if (off < 8 && chip->chip.ngpio > 8) + reg -= 4; + + if (s3c_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0xf << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} +#endif /* CONFIG_S3C_GPIO_CFG_S3C64XX */ + +#ifdef CONFIG_S3C_GPIO_PULL_UPDOWN +int s3c_gpio_setpull_updown(struct s3c_gpio_chip *chip, + unsigned int off, s3c_gpio_pull_t pull) +{ + void __iomem *reg = chip->base + 0x08; + int shift = off * 2; + u32 pup; + + pup = __raw_readl(reg); + pup &= ~(3 << shift); + pup |= pull << shift; + __raw_writel(pup, reg); + + return 0; +} + +s3c_gpio_pull_t s3c_gpio_getpull_updown(struct s3c_gpio_chip *chip, + unsigned int off) +{ + void __iomem *reg = chip->base + 0x08; + int shift = off * 2; + u32 pup = __raw_readl(reg); + + pup >>= shift; + pup &= 0x3; + return (__force s3c_gpio_pull_t)pup; +} +#endif -- cgit v0.10.2 From 7162ba03729e0a47aaab44448ce2453f07a9664d Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 6 Jan 2010 10:14:51 +0900 Subject: ARM: SAMSUNG: Move IRQ VIC timer handling out to common header files Move the VIC based timer interrupt handling out of plat-s3c64xx and into plat-samsung to be re-used for other systems. This also reduces the code size as we now have a common init routine and use the irq_desc to store the interrupt number of the timer. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig index bec1224..02d716f 100644 --- a/arch/arm/plat-s3c64xx/Kconfig +++ b/arch/arm/plat-s3c64xx/Kconfig @@ -14,6 +14,7 @@ config PLAT_S3C64XX select NO_IOPORT select ARCH_REQUIRE_GPIOLIB select SAMSUNG_CLKSRC + select SAMSUNG_IRQ_VIC_TIMER select S3C_GPIO_TRACK select S3C_GPIO_PULL_UPDOWN select S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/plat-s3c64xx/irq.c b/arch/arm/plat-s3c64xx/irq.c index 8dc5b6d..8b69bca 100644 --- a/arch/arm/plat-s3c64xx/irq.c +++ b/arch/arm/plat-s3c64xx/irq.c @@ -21,78 +21,10 @@ #include #include +#include #include -#include #include -/* Timer interrupt handling */ - -static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq) -{ - generic_handle_irq(sub_irq); -} - -static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER0); -} - -static void s3c_irq_demux_timer1(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER1); -} - -static void s3c_irq_demux_timer2(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER2); -} - -static void s3c_irq_demux_timer3(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER3); -} - -static void s3c_irq_demux_timer4(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER4); -} - -/* We assume the IRQ_TIMER0..IRQ_TIMER4 range is continuous. */ - -static void s3c_irq_timer_mask(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; /* mask out pending interrupts */ - reg &= ~(1 << (irq - IRQ_TIMER0)); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static void s3c_irq_timer_unmask(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; /* mask out pending interrupts */ - reg |= 1 << (irq - IRQ_TIMER0); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static void s3c_irq_timer_ack(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; - reg |= (1 << 5) << (irq - IRQ_TIMER0); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static struct irq_chip s3c_irq_timer = { - .name = "s3c-timer", - .mask = s3c_irq_timer_mask, - .unmask = s3c_irq_timer_unmask, - .ack = s3c_irq_timer_ack, -}; - struct uart_irq { void __iomem *regs; unsigned int base_irq; @@ -227,7 +159,7 @@ static void __init s3c64xx_uart_irq(struct uart_irq *uirq) void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) { - int uart, irq; + int uart; printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); @@ -237,20 +169,12 @@ void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) /* add the timer sub-irqs */ - set_irq_chained_handler(IRQ_TIMER0_VIC, s3c_irq_demux_timer0); - set_irq_chained_handler(IRQ_TIMER1_VIC, s3c_irq_demux_timer1); - set_irq_chained_handler(IRQ_TIMER2_VIC, s3c_irq_demux_timer2); - set_irq_chained_handler(IRQ_TIMER3_VIC, s3c_irq_demux_timer3); - set_irq_chained_handler(IRQ_TIMER4_VIC, s3c_irq_demux_timer4); - - for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) { - set_irq_chip(irq, &s3c_irq_timer); - set_irq_handler(irq, handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } + s3c_init_vic_timer_irq(IRQ_TIMER0_VIC, IRQ_TIMER0); + s3c_init_vic_timer_irq(IRQ_TIMER1_VIC, IRQ_TIMER1); + s3c_init_vic_timer_irq(IRQ_TIMER2_VIC, IRQ_TIMER2); + s3c_init_vic_timer_irq(IRQ_TIMER3_VIC, IRQ_TIMER3); + s3c_init_vic_timer_irq(IRQ_TIMER4_VIC, IRQ_TIMER4); for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++) s3c64xx_uart_irq(&uart_irqs[uart]); } - - diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 9e7daf2..cedecd8 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -19,6 +19,13 @@ config SAMSUNG_CLKSRC Select the clock code for the clksrc implementation used by newer systems such as the S3C64XX. +# options for IRQ support + +config SAMSUNG_IRQ_VIC_TIMER + bool + help + Internal configuration to build the VIC timer interrupt code. + # options for gpio configuration support config S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 2c01437..aa048c7 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -17,6 +17,8 @@ obj-y += gpio-config.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o +obj-$(CONFIG_SAMSUNG_IRQ_VIC_TIMER) += irq-vic-timer.o + # devices obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o diff --git a/arch/arm/plat-samsung/include/plat/irq-vic-timer.h b/arch/arm/plat-samsung/include/plat/irq-vic-timer.h new file mode 100644 index 0000000..a90b534 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/irq-vic-timer.h @@ -0,0 +1,13 @@ +/* arch/arm/plat-samsung/include/plat/irq-vic-timer.h + * + * Copyright (c) 2010 Simtec Electronics + * Ben Dooks + * + * Header file for Samsung SoC IRQ VIC timer + * + * 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. +*/ + +extern void s3c_init_vic_timer_irq(unsigned int vic, unsigned int timer); diff --git a/arch/arm/plat-samsung/irq-vic-timer.c b/arch/arm/plat-samsung/irq-vic-timer.c new file mode 100644 index 0000000..0270519 --- /dev/null +++ b/arch/arm/plat-samsung/irq-vic-timer.c @@ -0,0 +1,86 @@ +/* arch/arm/plat-samsung/irq-vic-timer.c + * originally part of arch/arm/plat-s3c64xx/irq.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C64XX - Interrupt handling + * + * 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 + +static void s3c_irq_demux_vic_timer(unsigned int irq, struct irq_desc *desc) +{ + generic_handle_irq((int)desc->handler_data); +} + +/* We assume the IRQ_TIMER0..IRQ_TIMER4 range is continuous. */ + +static void s3c_irq_timer_mask(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; /* mask out pending interrupts */ + reg &= ~(1 << (irq - IRQ_TIMER0)); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static void s3c_irq_timer_unmask(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; /* mask out pending interrupts */ + reg |= 1 << (irq - IRQ_TIMER0); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static void s3c_irq_timer_ack(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; + reg |= (1 << 5) << (irq - IRQ_TIMER0); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static struct irq_chip s3c_irq_timer = { + .name = "s3c-timer", + .mask = s3c_irq_timer_mask, + .unmask = s3c_irq_timer_unmask, + .ack = s3c_irq_timer_ack, +}; + +/** + * s3c_init_vic_timer_irq() - initialise timer irq chanined off VIC.\ + * @parent_irq: The parent IRQ on the VIC for the timer. + * @timer_irq: The IRQ to be used for the timer. + * + * Register the necessary IRQ chaining and support for the timer IRQs + * chained of the VIC. + */ +void __init s3c_init_vic_timer_irq(unsigned int parent_irq, + unsigned int timer_irq) +{ + struct irq_desc *desc = irq_to_desc(parent_irq); + + set_irq_chained_handler(parent_irq, s3c_irq_demux_vic_timer); + + set_irq_chip(timer_irq, &s3c_irq_timer); + set_irq_handler(timer_irq, handle_level_irq); + set_irq_flags(timer_irq, IRQF_VALID); + + desc->handler_data = (void *)timer_irq; +} -- cgit v0.10.2 From 51022cf6591ae2945960d034788bdeffa28cde13 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 6 Jan 2010 11:18:44 +0900 Subject: ARM: SAMSUNG: Move IRQ UART handling for newer devices to plat-samsung Move the handling for the UART interrupts out of the s3c64xx specific code and into plat-samsung so that it can be used by all implementations that need it. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig index 02d716f..0fba1f9 100644 --- a/arch/arm/plat-s3c64xx/Kconfig +++ b/arch/arm/plat-s3c64xx/Kconfig @@ -15,6 +15,7 @@ config PLAT_S3C64XX select ARCH_REQUIRE_GPIOLIB select SAMSUNG_CLKSRC select SAMSUNG_IRQ_VIC_TIMER + select SAMSUNG_IRQ_UART select S3C_GPIO_TRACK select S3C_GPIO_PULL_UPDOWN select S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/plat-s3c64xx/irq.c b/arch/arm/plat-s3c64xx/irq.c index 8b69bca..b98451e 100644 --- a/arch/arm/plat-s3c64xx/irq.c +++ b/arch/arm/plat-s3c64xx/irq.c @@ -22,19 +22,10 @@ #include #include -#include +#include #include -struct uart_irq { - void __iomem *regs; - unsigned int base_irq; - unsigned int parent_irq; -}; - -/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] - * are consecutive when looking up the interrupt in the demux routines. - */ -static struct uart_irq uart_irqs[] = { +static struct s3c_uart_irq uart_irqs[] = { [0] = { .regs = S3C_VA_UART0, .base_irq = IRQ_S3CUART_BASE0, @@ -57,110 +48,9 @@ static struct uart_irq uart_irqs[] = { }, }; -static inline void __iomem *s3c_irq_uart_base(unsigned int irq) -{ - struct uart_irq *uirq = get_irq_chip_data(irq); - return uirq->regs; -} - -static inline unsigned int s3c_irq_uart_bit(unsigned int irq) -{ - return irq & 3; -} - -/* UART interrupt registers, not worth adding to seperate include header */ - -static void s3c_irq_uart_mask(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg |= (1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); -} - -static void s3c_irq_uart_maskack(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg |= (1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); - __raw_writel(1 << bit, regs + S3C64XX_UINTP); -} - -static void s3c_irq_uart_unmask(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg &= ~(1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); -} - -static void s3c_irq_uart_ack(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - - __raw_writel(1 << bit, regs + S3C64XX_UINTP); -} - -static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc) -{ - struct uart_irq *uirq = &uart_irqs[irq - IRQ_UART0]; - u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP); - int base = uirq->base_irq; - - if (pend & (1 << 0)) - generic_handle_irq(base); - if (pend & (1 << 1)) - generic_handle_irq(base + 1); - if (pend & (1 << 2)) - generic_handle_irq(base + 2); - if (pend & (1 << 3)) - generic_handle_irq(base + 3); -} - -static struct irq_chip s3c_irq_uart = { - .name = "s3c-uart", - .mask = s3c_irq_uart_mask, - .unmask = s3c_irq_uart_unmask, - .mask_ack = s3c_irq_uart_maskack, - .ack = s3c_irq_uart_ack, -}; - -static void __init s3c64xx_uart_irq(struct uart_irq *uirq) -{ - void __iomem *reg_base = uirq->regs; - unsigned int irq; - int offs; - - /* mask all interrupts at the start. */ - __raw_writel(0xf, reg_base + S3C64XX_UINTM); - - for (offs = 0; offs < 3; offs++) { - irq = uirq->base_irq + offs; - - set_irq_chip(irq, &s3c_irq_uart); - set_irq_chip_data(irq, uirq); - set_irq_handler(irq, handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } - - set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart); -} void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) { - int uart; - printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); /* initialise the pair of VICs */ @@ -175,6 +65,5 @@ void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) s3c_init_vic_timer_irq(IRQ_TIMER3_VIC, IRQ_TIMER3); s3c_init_vic_timer_irq(IRQ_TIMER4_VIC, IRQ_TIMER4); - for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++) - s3c64xx_uart_irq(&uart_irqs[uart]); + s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); } diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index cedecd8..900b463 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -26,6 +26,11 @@ config SAMSUNG_IRQ_VIC_TIMER help Internal configuration to build the VIC timer interrupt code. +config SAMSUNG_IRQ_UART + bool + help + Internal configuration to build the IRQ UART demux code. + # options for gpio configuration support config S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index aa048c7..aeb7e12 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -17,6 +17,7 @@ obj-y += gpio-config.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o +obj-$(CONFIG_SAMSUNG_IRQ_UART) += irq-uart.o obj-$(CONFIG_SAMSUNG_IRQ_VIC_TIMER) += irq-vic-timer.o # devices diff --git a/arch/arm/plat-samsung/include/plat/irq-uart.h b/arch/arm/plat-samsung/include/plat/irq-uart.h new file mode 100644 index 0000000..a9331e4 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/irq-uart.h @@ -0,0 +1,20 @@ +/* arch/arm/plat-samsung/include/plat/irq-uart.h + * + * Copyright (c) 2010 Simtec Electronics + * Ben Dooks + * + * Header file for Samsung SoC UART IRQ demux for S3C64XX and later + * + * 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. +*/ + +struct s3c_uart_irq { + void __iomem *regs; + unsigned int base_irq; + unsigned int parent_irq; +}; + +extern void s3c_init_uart_irqs(struct s3c_uart_irq *irq, unsigned int nr_irqs); + diff --git a/arch/arm/plat-samsung/irq-uart.c b/arch/arm/plat-samsung/irq-uart.c new file mode 100644 index 0000000..4f8c102 --- /dev/null +++ b/arch/arm/plat-samsung/irq-uart.c @@ -0,0 +1,143 @@ +/* arch/arm/plat-samsung/irq-uart.c + * originally part of arch/arm/plat-s3c64xx/irq.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * Samsung- UART Interrupt handling + * + * 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 + +/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] + * are consecutive when looking up the interrupt in the demux routines. + */ + +static inline void __iomem *s3c_irq_uart_base(unsigned int irq) +{ + struct s3c_uart_irq *uirq = get_irq_chip_data(irq); + return uirq->regs; +} + +static inline unsigned int s3c_irq_uart_bit(unsigned int irq) +{ + return irq & 3; +} + +static void s3c_irq_uart_mask(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg |= (1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); +} + +static void s3c_irq_uart_maskack(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg |= (1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); + __raw_writel(1 << bit, regs + S3C64XX_UINTP); +} + +static void s3c_irq_uart_unmask(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg &= ~(1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); +} + +static void s3c_irq_uart_ack(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + + __raw_writel(1 << bit, regs + S3C64XX_UINTP); +} + +static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc) +{ + struct s3c_uart_irq *uirq = desc->handler_data; + u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP); + int base = uirq->base_irq; + + if (pend & (1 << 0)) + generic_handle_irq(base); + if (pend & (1 << 1)) + generic_handle_irq(base + 1); + if (pend & (1 << 2)) + generic_handle_irq(base + 2); + if (pend & (1 << 3)) + generic_handle_irq(base + 3); +} + +static struct irq_chip s3c_irq_uart = { + .name = "s3c-uart", + .mask = s3c_irq_uart_mask, + .unmask = s3c_irq_uart_unmask, + .mask_ack = s3c_irq_uart_maskack, + .ack = s3c_irq_uart_ack, +}; + +static void __init s3c_init_uart_irq(struct s3c_uart_irq *uirq) +{ + struct irq_desc *desc = irq_to_desc(uirq->parent_irq); + void __iomem *reg_base = uirq->regs; + unsigned int irq; + int offs; + + /* mask all interrupts at the start. */ + __raw_writel(0xf, reg_base + S3C64XX_UINTM); + + for (offs = 0; offs < 3; offs++) { + irq = uirq->base_irq + offs; + + set_irq_chip(irq, &s3c_irq_uart); + set_irq_chip_data(irq, uirq); + set_irq_handler(irq, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + } + + desc->handler_data = uirq; + set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart); +} + +/** + * s3c_init_uart_irqs() - initialise UART IRQs and the necessary demuxing + * @irq: The interrupt data for registering + * @nr_irqs: The number of interrupt descriptions in @irq. + * + * Register the UART interrupts specified by @irq including the demuxing + * routines. This supports the S3C6400 and newer style of devices. + */ +void __init s3c_init_uart_irqs(struct s3c_uart_irq *irq, unsigned int nr_irqs) +{ + for (; nr_irqs > 0; nr_irqs--, irq++) + s3c_init_uart_irq(irq); +} -- cgit v0.10.2 From 5b39be4637bb795b2133dbee0eadbcc08bdd4134 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Thu, 7 Jan 2010 08:59:26 +0900 Subject: ARM: Add common entry code for system with two VICs Add a common entry-macro-vic2.S for systems where there are two VICs so that the machine or platform directories just need to setup the correct information before including into their own entry-macro.S file. Since this code is from the S3C64XX project, we update the S3C64XX machine entry code to use this new header. Signed-off-by: Ben Dooks diff --git a/arch/arm/include/asm/entry-macro-vic2.S b/arch/arm/include/asm/entry-macro-vic2.S new file mode 100644 index 0000000..3ceb85e --- /dev/null +++ b/arch/arm/include/asm/entry-macro-vic2.S @@ -0,0 +1,57 @@ +/* arch/arm/include/asm/entry-macro-vic2.S + * + * Originally arch/arm/mach-s3c6400/include/mach/entry-macro.S + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * Low-level IRQ helper macros for a device with two VICs + * + * 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. +*/ + +/* This should be included from with the necessary + * defines for virtual addresses and IRQ bases for the two vics. + * + * The code needs the following defined: + * IRQ_VIC0_BASE IRQ number of VIC0's first IRQ + * IRQ_VIC1_BASE IRQ number of VIC1's first IRQ + * VA_VIC0 Virtual address of VIC0 + * VA_VIC1 Virtual address of VIC1 + * + * Note, code assumes VIC0's virtual address is an ARM immediate constant + * away from VIC1. +*/ + +#include + + .macro disable_fiq + .endm + + .macro get_irqnr_preamble, base, tmp + ldr \base, =VA_VIC0 + .endm + + .macro arch_ret_to_user, tmp1, tmp2 + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + + @ check the vic0 + mov \irqnr, #IRQ_VIC0_BASE + 31 + ldr \irqstat, [ \base, # VIC_IRQ_STATUS ] + teq \irqstat, #0 + + @ otherwise try vic1 + addeq \tmp, \base, #(VA_VIC1 - VA_VIC0) + addeq \irqnr, \irqnr, #(IRQ_VIC1_BASE - IRQ_VIC0_BASE) + ldreq \irqstat, [ \tmp, # VIC_IRQ_STATUS ] + teqeq \irqstat, #0 + + clzne \irqstat, \irqstat + subne \irqnr, \irqnr, \irqstat + .endm diff --git a/arch/arm/mach-s3c6400/include/mach/entry-macro.S b/arch/arm/mach-s3c6400/include/mach/entry-macro.S index fbd90d2..33a8fe2 100644 --- a/arch/arm/mach-s3c6400/include/mach/entry-macro.S +++ b/arch/arm/mach-s3c6400/include/mach/entry-macro.S @@ -12,33 +12,7 @@ * warranty of any kind, whether express or implied. */ -#include #include #include - .macro disable_fiq - .endm - - .macro get_irqnr_preamble, base, tmp - ldr \base, =S3C_VA_VIC0 - .endm - - .macro arch_ret_to_user, tmp1, tmp2 - .endm - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - - @ check the vic0 - mov \irqnr, # S3C_IRQ_OFFSET + 31 - ldr \irqstat, [ \base, # VIC_IRQ_STATUS ] - teq \irqstat, #0 - - @ otherwise try vic1 - addeq \tmp, \base, #(S3C_VA_VIC1 - S3C_VA_VIC0) - addeq \irqnr, \irqnr, #32 - ldreq \irqstat, [ \tmp, # VIC_IRQ_STATUS ] - teqeq \irqstat, #0 - - clzne \irqstat, \irqstat - subne \irqnr, \irqnr, \irqstat - .endm +#include diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index 106ee13..d4cd3ab 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -70,8 +70,8 @@ #define S3C64XX_VA_USB_HSPHY S3C_ADDR_CPU(0x00200000) /* place VICs close together */ -#define S3C_VA_VIC0 (S3C_VA_IRQ + 0x00) -#define S3C_VA_VIC1 (S3C_VA_IRQ + 0x10000) +#define VA_VIC0 (S3C_VA_IRQ + 0x00) +#define VA_VIC1 (S3C_VA_IRQ + 0x10000) /* compatibiltiy defines. */ #define S3C_PA_TIMER S3C64XX_PA_TIMER diff --git a/arch/arm/mach-s3c6400/include/mach/tick.h b/arch/arm/mach-s3c6400/include/mach/tick.h index d9c0dc7..ebe18a9 100644 --- a/arch/arm/mach-s3c6400/include/mach/tick.h +++ b/arch/arm/mach-s3c6400/include/mach/tick.h @@ -20,7 +20,7 @@ */ static inline u32 s3c24xx_ostimer_pending(void) { - u32 pend = __raw_readl(S3C_VA_VIC0 + VIC_RAW_STATUS); + u32 pend = __raw_readl(VA_VIC0 + VIC_RAW_STATUS); return pend & 1 << (IRQ_TIMER4_VIC - S3C64XX_IRQ_VIC0(0)); } diff --git a/arch/arm/plat-s3c64xx/cpu.c b/arch/arm/plat-s3c64xx/cpu.c index 49796d2..c0e6f2a 100644 --- a/arch/arm/plat-s3c64xx/cpu.c +++ b/arch/arm/plat-s3c64xx/cpu.c @@ -78,12 +78,12 @@ static struct map_desc s3c_iodesc[] __initdata = { .length = SZ_4K, .type = MT_DEVICE, }, { - .virtual = (unsigned long)S3C_VA_VIC0, + .virtual = (unsigned long)VA_VIC0, .pfn = __phys_to_pfn(S3C64XX_PA_VIC0), .length = SZ_16K, .type = MT_DEVICE, }, { - .virtual = (unsigned long)S3C_VA_VIC1, + .virtual = (unsigned long)VA_VIC1, .pfn = __phys_to_pfn(S3C64XX_PA_VIC1), .length = SZ_16K, .type = MT_DEVICE, diff --git a/arch/arm/plat-s3c64xx/include/plat/irqs.h b/arch/arm/plat-s3c64xx/include/plat/irqs.h index 7956fd3..176fe15 100644 --- a/arch/arm/plat-s3c64xx/include/plat/irqs.h +++ b/arch/arm/plat-s3c64xx/include/plat/irqs.h @@ -24,8 +24,8 @@ #define S3C_IRQ(x) ((x) + S3C_IRQ_OFFSET) -#define S3C_VIC0_BASE S3C_IRQ(0) -#define S3C_VIC1_BASE S3C_IRQ(32) +#define IRQ_VIC0_BASE S3C_IRQ(0) +#define IRQ_VIC1_BASE S3C_IRQ(32) /* UART interrupts, each UART has 4 intterupts per channel so * use the space between the ISA and S3C main interrupts. Note, these @@ -59,8 +59,8 @@ /* VIC based IRQs */ -#define S3C64XX_IRQ_VIC0(x) (S3C_VIC0_BASE + (x)) -#define S3C64XX_IRQ_VIC1(x) (S3C_VIC1_BASE + (x)) +#define S3C64XX_IRQ_VIC0(x) (IRQ_VIC0_BASE + (x)) +#define S3C64XX_IRQ_VIC1(x) (IRQ_VIC1_BASE + (x)) /* VIC0 */ diff --git a/arch/arm/plat-s3c64xx/irq.c b/arch/arm/plat-s3c64xx/irq.c index b98451e..67a145d 100644 --- a/arch/arm/plat-s3c64xx/irq.c +++ b/arch/arm/plat-s3c64xx/irq.c @@ -54,8 +54,8 @@ void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); /* initialise the pair of VICs */ - vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid, 0); - vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid, 0); + vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, 0); + vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, 0); /* add the timer sub-irqs */ -- cgit v0.10.2 From 85841bcedd6048dd37cab58a504e5317192324c6 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Mon, 11 Jan 2010 12:21:51 +0900 Subject: ARM: SAMSUNG: Add config option for number of additional GPIO pins. This patch adds a configuration option for specifing the number of additional GPIO pins to be used in addition to the GPIO pins supported onchip. Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 900b463..faec4b8 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -66,6 +66,14 @@ config S3C_GPIO_PULL_UP help Internal configuration to enable the correct GPIO pull helper +config SAMSUNG_GPIO_EXTRA + int "Number of additional GPIO pins" + default 0 + help + Use additional GPIO space in addition to the GPIO's the SOC + provides. This allows expanding the GPIO space for use with + GPIO expanders. + # device definitions to compile in config S3C_DEV_HSMMC -- cgit v0.10.2 From 45426468148d77e8c5eaece21d2a16e063066567 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 12 Jan 2010 12:19:28 +0900 Subject: ARM: S5PC1XX: Move to using generic clksrc_clk for clock code Remove the copy of the old s3c64xx struct clksrc_clk and use the new one in plat-samsung. This eliminates a bug in the set_parent() call where it failed to set the clk->parent after sucesfully updating the clock. The script that was used to automate much of the process will be supplied seperately. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s5pc1xx/Kconfig b/arch/arm/plat-s5pc1xx/Kconfig index b7b9e91..5d97b1c 100644 --- a/arch/arm/plat-s5pc1xx/Kconfig +++ b/arch/arm/plat-s5pc1xx/Kconfig @@ -11,6 +11,7 @@ config PLAT_S5PC1XX select ARM_VIC select NO_IOPORT select ARCH_REQUIRE_GPIOLIB + select SAMSUNG_CLKSRC select S3C_GPIO_TRACK select S3C_GPIO_PULL_UPDOWN select S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/plat-s5pc1xx/s5pc100-clock.c b/arch/arm/plat-s5pc1xx/s5pc100-clock.c index 16f0b90..52c032c 100644 --- a/arch/arm/plat-s5pc1xx/s5pc100-clock.c +++ b/arch/arm/plat-s5pc1xx/s5pc100-clock.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -51,23 +52,6 @@ static struct clk clk_ext_xtal_mux = { #define clk_fout_mpll clk_mpll #define clk_vclk_54m clk_54m -struct clk_sources { - unsigned int nr_sources; - struct clk **sources; -}; - -struct clksrc_clk { - struct clk clk; - unsigned int mask; - unsigned int shift; - - struct clk_sources *sources; - - unsigned int divider_shift; - void __iomem *reg_divider; - void __iomem *reg_source; -}; - /* APLL */ static struct clk clk_fout_apll = { .name = "fout_apll", @@ -80,7 +64,7 @@ static struct clk *clk_src_apll_list[] = { [1] = &clk_fout_apll, }; -static struct clk_sources clk_src_apll = { +static struct clksrc_sources clk_src_apll = { .sources = clk_src_apll_list, .nr_sources = ARRAY_SIZE(clk_src_apll_list), }; @@ -90,10 +74,8 @@ static struct clksrc_clk clk_mout_apll = { .name = "mout_apll", .id = -1, }, - .shift = S5PC100_CLKSRC0_APLL_SHIFT, - .mask = S5PC100_CLKSRC0_APLL_MASK, .sources = &clk_src_apll, - .reg_source = S5PC100_CLKSRC0, + .reg_src = { .reg = S5PC100_CLKSRC0, .shift = 0, .size = 1, }, }; static unsigned long s5pc100_clk_dout_apll_get_rate(struct clk *clk) @@ -240,7 +222,7 @@ static struct clk *clk_src_mpll_list[] = { [1] = &clk_fout_mpll, }; -static struct clk_sources clk_src_mpll = { +static struct clksrc_sources clk_src_mpll = { .sources = clk_src_mpll_list, .nr_sources = ARRAY_SIZE(clk_src_mpll_list), }; @@ -250,10 +232,8 @@ static struct clksrc_clk clk_mout_mpll = { .name = "mout_mpll", .id = -1, }, - .shift = S5PC100_CLKSRC0_MPLL_SHIFT, - .mask = S5PC100_CLKSRC0_MPLL_MASK, .sources = &clk_src_mpll, - .reg_source = S5PC100_CLKSRC0, + .reg_src = { .reg = S5PC100_CLKSRC0, .shift = 4, .size = 1, }, }; static struct clk *clkset_am_list[] = { @@ -261,7 +241,7 @@ static struct clk *clkset_am_list[] = { [1] = &clk_dout_apll2, }; -static struct clk_sources clk_src_am = { +static struct clksrc_sources clk_src_am = { .sources = clkset_am_list, .nr_sources = ARRAY_SIZE(clkset_am_list), }; @@ -271,10 +251,8 @@ static struct clksrc_clk clk_mout_am = { .name = "mout_am", .id = -1, }, - .shift = S5PC100_CLKSRC0_AMMUX_SHIFT, - .mask = S5PC100_CLKSRC0_AMMUX_MASK, .sources = &clk_src_am, - .reg_source = S5PC100_CLKSRC0, + .reg_src = { .reg = S5PC100_CLKSRC0, .shift = 16, .size = 1, }, }; static unsigned long s5pc100_clk_dout_d1_bus_get_rate(struct clk *clk) @@ -304,7 +282,7 @@ static struct clk *clkset_onenand_list[] = { [1] = &clk_dout_d1_bus, }; -static struct clk_sources clk_src_onenand = { +static struct clksrc_sources clk_src_onenand = { .sources = clkset_onenand_list, .nr_sources = ARRAY_SIZE(clkset_onenand_list), }; @@ -314,10 +292,8 @@ static struct clksrc_clk clk_mout_onenand = { .name = "mout_onenand", .id = -1, }, - .shift = S5PC100_CLKSRC0_ONENAND_SHIFT, - .mask = S5PC100_CLKSRC0_ONENAND_MASK, .sources = &clk_src_onenand, - .reg_source = S5PC100_CLKSRC0, + .reg_src = { .reg = S5PC100_CLKSRC0, .shift = 24, .size = 1, }, }; static unsigned long s5pc100_clk_dout_pclkd1_get_rate(struct clk *clk) @@ -419,7 +395,7 @@ static struct clk *clk_src_epll_list[] = { [1] = &clk_fout_epll, }; -static struct clk_sources clk_src_epll = { +static struct clksrc_sources clk_src_epll = { .sources = clk_src_epll_list, .nr_sources = ARRAY_SIZE(clk_src_epll_list), }; @@ -429,10 +405,8 @@ static struct clksrc_clk clk_mout_epll = { .name = "mout_epll", .id = -1, }, - .shift = S5PC100_CLKSRC0_EPLL_SHIFT, - .mask = S5PC100_CLKSRC0_EPLL_MASK, - .sources = &clk_src_epll, - .reg_source = S5PC100_CLKSRC0, + .sources = &clk_src_epll, + .reg_src = { .reg = S5PC100_CLKSRC0, .shift = 8, .size = 1, }, }; /* HPLL */ @@ -446,7 +420,7 @@ static struct clk *clk_src_hpll_list[] = { [1] = &clk_fout_hpll, }; -static struct clk_sources clk_src_hpll = { +static struct clksrc_sources clk_src_hpll = { .sources = clk_src_hpll_list, .nr_sources = ARRAY_SIZE(clk_src_hpll_list), }; @@ -456,10 +430,8 @@ static struct clksrc_clk clk_mout_hpll = { .name = "mout_hpll", .id = -1, }, - .shift = S5PC100_CLKSRC0_HPLL_SHIFT, - .mask = S5PC100_CLKSRC0_HPLL_MASK, - .sources = &clk_src_hpll, - .reg_source = S5PC100_CLKSRC0, + .sources = &clk_src_hpll, + .reg_src = { .reg = S5PC100_CLKSRC0, .shift = 12, .size = 1, }, }; /* Peripherals */ @@ -474,99 +446,6 @@ static struct clksrc_clk clk_mout_hpll = { * have a common parent divisor so are not included here. */ -static inline struct clksrc_clk *to_clksrc(struct clk *clk) -{ - return container_of(clk, struct clksrc_clk, clk); -} - -static unsigned long s5pc100_getrate_clksrc(struct clk *clk) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - unsigned long rate = clk_get_rate(clk->parent); - u32 clkdiv = __raw_readl(sclk->reg_divider); - - clkdiv >>= sclk->divider_shift; - clkdiv &= 0xf; - clkdiv++; - - rate /= clkdiv; - return rate; -} - -static int s5pc100_setrate_clksrc(struct clk *clk, unsigned long rate) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - void __iomem *reg = sclk->reg_divider; - unsigned int div; - u32 val; - - rate = clk_round_rate(clk, rate); - div = clk_get_rate(clk->parent) / rate; - if (div > 16) - return -EINVAL; - - val = __raw_readl(reg); - val &= ~(0xf << sclk->divider_shift); - val |= (div - 1) << sclk->divider_shift; - __raw_writel(val, reg); - - return 0; -} - -static int s5pc100_setparent_clksrc(struct clk *clk, struct clk *parent) -{ - struct clksrc_clk *sclk = to_clksrc(clk); - struct clk_sources *srcs = sclk->sources; - u32 clksrc = __raw_readl(sclk->reg_source); - int src_nr = -1; - int ptr; - - for (ptr = 0; ptr < srcs->nr_sources; ptr++) - if (srcs->sources[ptr] == parent) { - src_nr = ptr; - break; - } - - if (src_nr >= 0) { - clksrc &= ~sclk->mask; - clksrc |= src_nr << sclk->shift; - - __raw_writel(clksrc, sclk->reg_source); - return 0; - } - - return -EINVAL; -} - -static unsigned long s5pc100_roundrate_clksrc(struct clk *clk, - unsigned long rate) -{ - unsigned long parent_rate = clk_get_rate(clk->parent); - int div; - - if (rate > parent_rate) - rate = parent_rate; - else { - div = rate / parent_rate; - - if (div == 0) - div = 1; - if (div > 16) - div = 16; - - rate = parent_rate / div; - } - - return rate; -} - -static struct clk_ops s5pc100_clksrc_ops = { - .set_parent = s5pc100_setparent_clksrc, - .get_rate = s5pc100_getrate_clksrc, - .set_rate = s5pc100_setrate_clksrc, - .round_rate = s5pc100_roundrate_clksrc, -}; - static struct clk *clkset_spi_list[] = { &clk_mout_epll.clk, &clk_dout_mpll2, @@ -574,7 +453,7 @@ static struct clk *clkset_spi_list[] = { &clk_mout_hpll.clk, }; -static struct clk_sources clkset_spi = { +static struct clksrc_sources clkset_spi = { .sources = clkset_spi_list, .nr_sources = ARRAY_SIZE(clkset_spi_list), }; @@ -587,12 +466,9 @@ static struct clksrc_clk clk_spi0 = { .enable = s5pc100_sclk0_ctrl, }, - .shift = S5PC100_CLKSRC1_SPI0_SHIFT, - .mask = S5PC100_CLKSRC1_SPI0_MASK, - .sources = &clkset_spi, - .divider_shift = S5PC100_CLKDIV2_SPI0_SHIFT, - .reg_divider = S5PC100_CLKDIV2, - .reg_source = S5PC100_CLKSRC1, + .sources = &clkset_spi, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 4, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 4, .size = 2, }, }; static struct clksrc_clk clk_spi1 = { @@ -601,14 +477,10 @@ static struct clksrc_clk clk_spi1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI1, .enable = s5pc100_sclk0_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC1_SPI1_SHIFT, - .mask = S5PC100_CLKSRC1_SPI1_MASK, - .sources = &clkset_spi, - .divider_shift = S5PC100_CLKDIV2_SPI1_SHIFT, - .reg_divider = S5PC100_CLKDIV2, - .reg_source = S5PC100_CLKSRC1, + .sources = &clkset_spi, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 8, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 8, .size = 2, }, }; static struct clksrc_clk clk_spi2 = { @@ -617,14 +489,10 @@ static struct clksrc_clk clk_spi2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI2, .enable = s5pc100_sclk0_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC1_SPI2_SHIFT, - .mask = S5PC100_CLKSRC1_SPI2_MASK, - .sources = &clkset_spi, - .divider_shift = S5PC100_CLKDIV2_SPI2_SHIFT, - .reg_divider = S5PC100_CLKDIV2, - .reg_source = S5PC100_CLKSRC1, + .sources = &clkset_spi, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 12, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 12, .size = 2, }, }; static struct clk *clkset_uart_list[] = { @@ -632,7 +500,7 @@ static struct clk *clkset_uart_list[] = { &clk_dout_mpll, }; -static struct clk_sources clkset_uart = { +static struct clksrc_sources clkset_uart = { .sources = clkset_uart_list, .nr_sources = ARRAY_SIZE(clkset_uart_list), }; @@ -643,14 +511,10 @@ static struct clksrc_clk clk_uart_uclk1 = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK0_UART, .enable = s5pc100_sclk0_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC1_UART_SHIFT, - .mask = S5PC100_CLKSRC1_UART_MASK, - .sources = &clkset_uart, - .divider_shift = S5PC100_CLKDIV2_UART_SHIFT, - .reg_divider = S5PC100_CLKDIV2, - .reg_source = S5PC100_CLKSRC1, + .sources = &clkset_uart, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 0, .size = 3, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 0, .size = 1, }, }; static struct clk clk_iis_cd0 = { @@ -687,7 +551,7 @@ static struct clk *clkset_audio0_list[] = { &clk_mout_hpll.clk, }; -static struct clk_sources clkset_audio0 = { +static struct clksrc_sources clkset_audio0 = { .sources = clkset_audio0_list, .nr_sources = ARRAY_SIZE(clkset_audio0_list), }; @@ -698,14 +562,10 @@ static struct clksrc_clk clk_audio0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO0, .enable = s5pc100_sclk1_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC3_AUDIO0_SHIFT, - .mask = S5PC100_CLKSRC3_AUDIO0_MASK, - .sources = &clkset_audio0, - .divider_shift = S5PC100_CLKDIV4_AUDIO0_SHIFT, - .reg_divider = S5PC100_CLKDIV4, - .reg_source = S5PC100_CLKSRC3, + .sources = &clkset_audio0, + .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 12, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 12, .size = 3, }, }; static struct clk *clkset_audio1_list[] = { @@ -717,7 +577,7 @@ static struct clk *clkset_audio1_list[] = { &clk_mout_hpll.clk, }; -static struct clk_sources clkset_audio1 = { +static struct clksrc_sources clkset_audio1 = { .sources = clkset_audio1_list, .nr_sources = ARRAY_SIZE(clkset_audio1_list), }; @@ -728,14 +588,10 @@ static struct clksrc_clk clk_audio1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO1, .enable = s5pc100_sclk1_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC3_AUDIO1_SHIFT, - .mask = S5PC100_CLKSRC3_AUDIO1_MASK, - .sources = &clkset_audio1, - .divider_shift = S5PC100_CLKDIV4_AUDIO1_SHIFT, - .reg_divider = S5PC100_CLKDIV4, - .reg_source = S5PC100_CLKSRC3, + .sources = &clkset_audio1, + .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 16, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 16, .size = 3, }, }; static struct clk *clkset_audio2_list[] = { @@ -746,7 +602,7 @@ static struct clk *clkset_audio2_list[] = { &clk_mout_hpll.clk, }; -static struct clk_sources clkset_audio2 = { +static struct clksrc_sources clkset_audio2 = { .sources = clkset_audio2_list, .nr_sources = ARRAY_SIZE(clkset_audio2_list), }; @@ -757,14 +613,10 @@ static struct clksrc_clk clk_audio2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO2, .enable = s5pc100_sclk1_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC3_AUDIO2_SHIFT, - .mask = S5PC100_CLKSRC3_AUDIO2_MASK, - .sources = &clkset_audio2, - .divider_shift = S5PC100_CLKDIV4_AUDIO2_SHIFT, - .reg_divider = S5PC100_CLKDIV4, - .reg_source = S5PC100_CLKSRC3, + .sources = &clkset_audio2, + .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 20, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 20, .size = 3, }, }; static struct clk *clkset_spdif_list[] = { @@ -773,7 +625,7 @@ static struct clk *clkset_spdif_list[] = { &clk_audio2.clk, }; -static struct clk_sources clkset_spdif = { +static struct clksrc_sources clkset_spdif = { .sources = clkset_spdif_list, .nr_sources = ARRAY_SIZE(clkset_spdif_list), }; @@ -783,10 +635,8 @@ static struct clksrc_clk clk_spdif = { .name = "spdif", .id = -1, }, - .shift = S5PC100_CLKSRC3_SPDIF_SHIFT, - .mask = S5PC100_CLKSRC3_SPDIF_MASK, .sources = &clkset_spdif, - .reg_source = S5PC100_CLKSRC3, + .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 24, .size = 2, }, }; static struct clk *clkset_lcd_fimc_list[] = { @@ -796,7 +646,7 @@ static struct clk *clkset_lcd_fimc_list[] = { &clk_vclk_54m, }; -static struct clk_sources clkset_lcd_fimc = { +static struct clksrc_sources clkset_lcd_fimc = { .sources = clkset_lcd_fimc_list, .nr_sources = ARRAY_SIZE(clkset_lcd_fimc_list), }; @@ -807,14 +657,10 @@ static struct clksrc_clk clk_lcd = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK1_LCD, .enable = s5pc100_sclk1_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC2_LCD_SHIFT, - .mask = S5PC100_CLKSRC2_LCD_MASK, - .sources = &clkset_lcd_fimc, - .divider_shift = S5PC100_CLKDIV3_LCD_SHIFT, - .reg_divider = S5PC100_CLKDIV3, - .reg_source = S5PC100_CLKSRC2, + .sources = &clkset_lcd_fimc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 12, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 12, .size = 2, }, }; static struct clksrc_clk clk_fimc0 = { @@ -823,14 +669,10 @@ static struct clksrc_clk clk_fimc0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC0, .enable = s5pc100_sclk1_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC2_FIMC0_SHIFT, - .mask = S5PC100_CLKSRC2_FIMC0_MASK, - .sources = &clkset_lcd_fimc, - .divider_shift = S5PC100_CLKDIV3_FIMC0_SHIFT, - .reg_divider = S5PC100_CLKDIV3, - .reg_source = S5PC100_CLKSRC2, + .sources = &clkset_lcd_fimc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 16, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 16, .size = 2, }, }; static struct clksrc_clk clk_fimc1 = { @@ -839,14 +681,10 @@ static struct clksrc_clk clk_fimc1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC1, .enable = s5pc100_sclk1_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC2_FIMC1_SHIFT, - .mask = S5PC100_CLKSRC2_FIMC1_MASK, .sources = &clkset_lcd_fimc, - .divider_shift = S5PC100_CLKDIV3_FIMC1_SHIFT, - .reg_divider = S5PC100_CLKDIV3, - .reg_source = S5PC100_CLKSRC2, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 20, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 20, .size = 2, }, }; static struct clksrc_clk clk_fimc2 = { @@ -855,14 +693,10 @@ static struct clksrc_clk clk_fimc2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC2, .enable = s5pc100_sclk1_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC2_FIMC2_SHIFT, - .mask = S5PC100_CLKSRC2_FIMC2_MASK, - .sources = &clkset_lcd_fimc, - .divider_shift = S5PC100_CLKDIV3_FIMC2_SHIFT, - .reg_divider = S5PC100_CLKDIV3, - .reg_source = S5PC100_CLKSRC2, + .sources = &clkset_lcd_fimc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 24, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 24, .size = 2, }, }; static struct clk *clkset_mmc_list[] = { @@ -872,7 +706,7 @@ static struct clk *clkset_mmc_list[] = { &clk_mout_hpll.clk , }; -static struct clk_sources clkset_mmc = { +static struct clksrc_sources clkset_mmc = { .sources = clkset_mmc_list, .nr_sources = ARRAY_SIZE(clkset_mmc_list), }; @@ -883,14 +717,10 @@ static struct clksrc_clk clk_mmc0 = { .id = 0, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC0, .enable = s5pc100_sclk0_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC2_MMC0_SHIFT, - .mask = S5PC100_CLKSRC2_MMC0_MASK, - .sources = &clkset_mmc, - .divider_shift = S5PC100_CLKDIV3_MMC0_SHIFT, - .reg_divider = S5PC100_CLKDIV3, - .reg_source = S5PC100_CLKSRC2, + .sources = &clkset_mmc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 0, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 0, .size = 2, }, }; static struct clksrc_clk clk_mmc1 = { @@ -899,14 +729,10 @@ static struct clksrc_clk clk_mmc1 = { .id = 1, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC1, .enable = s5pc100_sclk0_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC2_MMC1_SHIFT, - .mask = S5PC100_CLKSRC2_MMC1_MASK, - .sources = &clkset_mmc, - .divider_shift = S5PC100_CLKDIV3_MMC1_SHIFT, - .reg_divider = S5PC100_CLKDIV3, - .reg_source = S5PC100_CLKSRC2, + .sources = &clkset_mmc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 4, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 4, .size = 2, }, }; static struct clksrc_clk clk_mmc2 = { @@ -915,14 +741,10 @@ static struct clksrc_clk clk_mmc2 = { .id = 2, .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC2, .enable = s5pc100_sclk0_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC2_MMC2_SHIFT, - .mask = S5PC100_CLKSRC2_MMC2_MASK, .sources = &clkset_mmc, - .divider_shift = S5PC100_CLKDIV3_MMC2_SHIFT, - .reg_divider = S5PC100_CLKDIV3, - .reg_source = S5PC100_CLKSRC2, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 8, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 8, .size = 2, }, }; @@ -933,7 +755,7 @@ static struct clk *clkset_usbhost_list[] = { &clk_48m, }; -static struct clk_sources clkset_usbhost = { +static struct clksrc_sources clkset_usbhost = { .sources = clkset_usbhost_list, .nr_sources = ARRAY_SIZE(clkset_usbhost_list), }; @@ -944,14 +766,10 @@ static struct clksrc_clk clk_usbhost = { .id = -1, .ctrlbit = S5PC100_CLKGATE_SCLK0_USBHOST, .enable = s5pc100_sclk0_ctrl, - .ops = &s5pc100_clksrc_ops, }, - .shift = S5PC100_CLKSRC1_UHOST_SHIFT, - .mask = S5PC100_CLKSRC1_UHOST_MASK, - .sources = &clkset_usbhost, - .divider_shift = S5PC100_CLKDIV2_UHOST_SHIFT, - .reg_divider = S5PC100_CLKDIV2, - .reg_source = S5PC100_CLKSRC1, + .sources = &clkset_usbhost, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 20, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 20, .size = 2, }, }; /* Clock initialisation code */ @@ -981,27 +799,6 @@ static struct clksrc_clk *init_parents[] = { &clk_usbhost, }; -static void __init_or_cpufreq s5pc100_set_clksrc(struct clksrc_clk *clk) -{ - struct clk_sources *srcs = clk->sources; - u32 clksrc = __raw_readl(clk->reg_source); - - clksrc &= clk->mask; - clksrc >>= clk->shift; - - if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) { - printk(KERN_ERR "%s: bad source %d\n", - clk->clk.name, clksrc); - return; - } - - clk->clk.parent = srcs->sources[clksrc]; - - printk(KERN_INFO "%s: source is %s (%d), rate is %ld.%03ld MHz\n", - clk->clk.name, clk->clk.parent->name, clksrc, - print_mhz(clk_get_rate(&clk->clk))); -} - #define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1) void __init_or_cpufreq s5pc100_setup_clocks(void) @@ -1065,50 +862,54 @@ void __init_or_cpufreq s5pc100_setup_clocks(void) clk_f.rate = armclk; for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) - s5pc100_set_clksrc(init_parents[ptr]); + s3c_set_clksrc(init_parents[ptr], true); } static struct clk *clks[] __initdata = { &clk_ext_xtal_mux, - &clk_mout_apll.clk, &clk_dout_apll, &clk_dout_d0_bus, &clk_dout_pclkd0, &clk_dout_apll2, - &clk_mout_mpll.clk, &clk_mout_am.clk, &clk_dout_d1_bus, - &clk_mout_onenand.clk, &clk_dout_pclkd1, &clk_dout_mpll2, &clk_dout_cam, &clk_dout_mpll, - &clk_mout_epll.clk, &clk_fout_epll, &clk_iis_cd0, &clk_iis_cd1, &clk_iis_cd2, &clk_pcm_cd0, &clk_pcm_cd1, - &clk_spi0.clk, - &clk_spi1.clk, - &clk_spi2.clk, - &clk_uart_uclk1.clk, - &clk_audio0.clk, - &clk_audio1.clk, - &clk_audio2.clk, - &clk_spdif.clk, - &clk_lcd.clk, - &clk_fimc0.clk, - &clk_fimc1.clk, - &clk_fimc2.clk, - &clk_mmc0.clk, - &clk_mmc1.clk, - &clk_mmc2.clk, - &clk_usbhost.clk, &clk_arm, }; +/* simplest change - will aggregate clocks later */ +static struct clksrc_clk *clks_src[] = { + &clk_mout_apll, + &clk_mout_mpll, + &clk_mout_onenand, + &clk_mout_epll, + &clk_spi0, + &clk_spi1, + &clk_spi2, + &clk_uart_uclk1, + &clk_audio0, + &clk_audio1, + &clk_audio2, + &clk_spdif, + &clk_lcd, + &clk_fimc0, + &clk_fimc1, + &clk_fimc2, + &clk_mmc0, + &clk_mmc1, + &clk_mmc2, + &clk_usbhost, +}; + void __init s5pc100_register_clocks(void) { struct clk *clkp; @@ -1123,4 +924,7 @@ void __init s5pc100_register_clocks(void) clkp->name, ret); } } + + for (ptr = 0; ptr < ARRAY_SIZE(clks_src); ptr++) + s3c_register_clksrc(clks_src[ptr], 1); } -- cgit v0.10.2 From 1d026d9b1671bcb05e7b00f545f1d62476e9063c Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 12 Jan 2010 12:39:58 +0900 Subject: ARM: S5PC1XX: Move clock definitions around ready to turn into clksrc arrays Move the clock definitions around ready to turn the clocks into an array of clocks and register them in one go. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s5pc1xx/s5pc100-clock.c b/arch/arm/plat-s5pc1xx/s5pc100-clock.c index 52c032c..a9e3730 100644 --- a/arch/arm/plat-s5pc1xx/s5pc100-clock.c +++ b/arch/arm/plat-s5pc1xx/s5pc100-clock.c @@ -446,6 +446,45 @@ static struct clksrc_clk clk_mout_hpll = { * have a common parent divisor so are not included here. */ +static struct clk clk_iis_cd0 = { + .name = "iis_cdclk0", + .id = -1, +}; + +static struct clk clk_iis_cd1 = { + .name = "iis_cdclk1", + .id = -1, +}; + +static struct clk clk_iis_cd2 = { + .name = "iis_cdclk2", + .id = -1, +}; + +static struct clk clk_pcm_cd0 = { + .name = "pcm_cdclk0", + .id = -1, +}; + +static struct clk clk_pcm_cd1 = { + .name = "pcm_cdclk1", + .id = -1, +}; + +static struct clk *clkset_audio0_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll, + &clk_fin_epll, + &clk_iis_cd0, + &clk_pcm_cd0, + &clk_mout_hpll.clk, +}; + +static struct clksrc_sources clkset_audio0 = { + .sources = clkset_audio0_list, + .nr_sources = ARRAY_SIZE(clkset_audio0_list), +}; + static struct clk *clkset_spi_list[] = { &clk_mout_epll.clk, &clk_dout_mpll2, @@ -458,6 +497,94 @@ static struct clksrc_sources clkset_spi = { .nr_sources = ARRAY_SIZE(clkset_spi_list), }; +static struct clk *clkset_uart_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll, +}; + +static struct clksrc_sources clkset_uart = { + .sources = clkset_uart_list, + .nr_sources = ARRAY_SIZE(clkset_uart_list), +}; + +static struct clk *clkset_audio1_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll, + &clk_fin_epll, + &clk_iis_cd1, + &clk_pcm_cd1, + &clk_mout_hpll.clk, +}; + +static struct clksrc_sources clkset_audio1 = { + .sources = clkset_audio1_list, + .nr_sources = ARRAY_SIZE(clkset_audio1_list), +}; + +static struct clk *clkset_audio2_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll, + &clk_fin_epll, + &clk_iis_cd2, + &clk_mout_hpll.clk, +}; + +static struct clksrc_sources clkset_audio2 = { + .sources = clkset_audio2_list, + .nr_sources = ARRAY_SIZE(clkset_audio2_list), +}; + +static struct clksrc_clk clk_audio0; +static struct clksrc_clk clk_audio1; +static struct clksrc_clk clk_audio2; + +static struct clk *clkset_spdif_list[] = { + &clk_audio0.clk, + &clk_audio1.clk, + &clk_audio2.clk, +}; + +static struct clksrc_sources clkset_spdif = { + .sources = clkset_spdif_list, + .nr_sources = ARRAY_SIZE(clkset_spdif_list), +}; + +static struct clk *clkset_lcd_fimc_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll, + &clk_mout_hpll.clk, + &clk_vclk_54m, +}; + +static struct clksrc_sources clkset_lcd_fimc = { + .sources = clkset_lcd_fimc_list, + .nr_sources = ARRAY_SIZE(clkset_lcd_fimc_list), +}; + +static struct clk *clkset_mmc_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll, + &clk_fin_epll, + &clk_mout_hpll.clk , +}; + +static struct clksrc_sources clkset_mmc = { + .sources = clkset_mmc_list, + .nr_sources = ARRAY_SIZE(clkset_mmc_list), +}; + +static struct clk *clkset_usbhost_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll, + &clk_mout_hpll.clk, + &clk_48m, +}; + +static struct clksrc_sources clkset_usbhost = { + .sources = clkset_usbhost_list, + .nr_sources = ARRAY_SIZE(clkset_usbhost_list), +}; + static struct clksrc_clk clk_spi0 = { .clk = { .name = "spi_bus", @@ -495,16 +622,6 @@ static struct clksrc_clk clk_spi2 = { .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 12, .size = 2, }, }; -static struct clk *clkset_uart_list[] = { - &clk_mout_epll.clk, - &clk_dout_mpll, -}; - -static struct clksrc_sources clkset_uart = { - .sources = clkset_uart_list, - .nr_sources = ARRAY_SIZE(clkset_uart_list), -}; - static struct clksrc_clk clk_uart_uclk1 = { .clk = { .name = "uclk1", @@ -517,45 +634,6 @@ static struct clksrc_clk clk_uart_uclk1 = { .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 0, .size = 1, }, }; -static struct clk clk_iis_cd0 = { - .name = "iis_cdclk0", - .id = -1, -}; - -static struct clk clk_iis_cd1 = { - .name = "iis_cdclk1", - .id = -1, -}; - -static struct clk clk_iis_cd2 = { - .name = "iis_cdclk2", - .id = -1, -}; - -static struct clk clk_pcm_cd0 = { - .name = "pcm_cdclk0", - .id = -1, -}; - -static struct clk clk_pcm_cd1 = { - .name = "pcm_cdclk1", - .id = -1, -}; - -static struct clk *clkset_audio0_list[] = { - &clk_mout_epll.clk, - &clk_dout_mpll, - &clk_fin_epll, - &clk_iis_cd0, - &clk_pcm_cd0, - &clk_mout_hpll.clk, -}; - -static struct clksrc_sources clkset_audio0 = { - .sources = clkset_audio0_list, - .nr_sources = ARRAY_SIZE(clkset_audio0_list), -}; - static struct clksrc_clk clk_audio0 = { .clk = { .name = "audio-bus", @@ -568,20 +646,6 @@ static struct clksrc_clk clk_audio0 = { .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 12, .size = 3, }, }; -static struct clk *clkset_audio1_list[] = { - &clk_mout_epll.clk, - &clk_dout_mpll, - &clk_fin_epll, - &clk_iis_cd1, - &clk_pcm_cd1, - &clk_mout_hpll.clk, -}; - -static struct clksrc_sources clkset_audio1 = { - .sources = clkset_audio1_list, - .nr_sources = ARRAY_SIZE(clkset_audio1_list), -}; - static struct clksrc_clk clk_audio1 = { .clk = { .name = "audio-bus", @@ -594,18 +658,6 @@ static struct clksrc_clk clk_audio1 = { .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 16, .size = 3, }, }; -static struct clk *clkset_audio2_list[] = { - &clk_mout_epll.clk, - &clk_dout_mpll, - &clk_fin_epll, - &clk_iis_cd2, - &clk_mout_hpll.clk, -}; - -static struct clksrc_sources clkset_audio2 = { - .sources = clkset_audio2_list, - .nr_sources = ARRAY_SIZE(clkset_audio2_list), -}; static struct clksrc_clk clk_audio2 = { .clk = { @@ -619,17 +671,6 @@ static struct clksrc_clk clk_audio2 = { .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 20, .size = 3, }, }; -static struct clk *clkset_spdif_list[] = { - &clk_audio0.clk, - &clk_audio1.clk, - &clk_audio2.clk, -}; - -static struct clksrc_sources clkset_spdif = { - .sources = clkset_spdif_list, - .nr_sources = ARRAY_SIZE(clkset_spdif_list), -}; - static struct clksrc_clk clk_spdif = { .clk = { .name = "spdif", @@ -639,18 +680,6 @@ static struct clksrc_clk clk_spdif = { .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 24, .size = 2, }, }; -static struct clk *clkset_lcd_fimc_list[] = { - &clk_mout_epll.clk, - &clk_dout_mpll, - &clk_mout_hpll.clk, - &clk_vclk_54m, -}; - -static struct clksrc_sources clkset_lcd_fimc = { - .sources = clkset_lcd_fimc_list, - .nr_sources = ARRAY_SIZE(clkset_lcd_fimc_list), -}; - static struct clksrc_clk clk_lcd = { .clk = { .name = "lcd", @@ -699,18 +728,6 @@ static struct clksrc_clk clk_fimc2 = { .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 24, .size = 2, }, }; -static struct clk *clkset_mmc_list[] = { - &clk_mout_epll.clk, - &clk_dout_mpll, - &clk_fin_epll, - &clk_mout_hpll.clk , -}; - -static struct clksrc_sources clkset_mmc = { - .sources = clkset_mmc_list, - .nr_sources = ARRAY_SIZE(clkset_mmc_list), -}; - static struct clksrc_clk clk_mmc0 = { .clk = { .name = "mmc_bus", @@ -747,19 +764,6 @@ static struct clksrc_clk clk_mmc2 = { .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 8, .size = 2, }, }; - -static struct clk *clkset_usbhost_list[] = { - &clk_mout_epll.clk, - &clk_dout_mpll, - &clk_mout_hpll.clk, - &clk_48m, -}; - -static struct clksrc_sources clkset_usbhost = { - .sources = clkset_usbhost_list, - .nr_sources = ARRAY_SIZE(clkset_usbhost_list), -}; - static struct clksrc_clk clk_usbhost = { .clk = { .name = "usbhost", -- cgit v0.10.2 From c837e88cf9e18c311332cc1c8c488993a201d568 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 12 Jan 2010 12:49:17 +0900 Subject: ARM: S5PC1XX: Change clksrc_clks to use array of clocks Remove the individual 'struct clksrc_clks' and place them into an array so that we can simply use s3c_register_clksrcs to register tham all in one go. Since the spdif clock relies on the audio clock, move the audio clocks into their own arrary. Thanks to Marek Szyprowski for testing and pointing out the four clocks what where missed from the clock list. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s5pc1xx/s5pc100-clock.c b/arch/arm/plat-s5pc1xx/s5pc100-clock.c index a9e3730..2bf6c57 100644 --- a/arch/arm/plat-s5pc1xx/s5pc100-clock.c +++ b/arch/arm/plat-s5pc1xx/s5pc100-clock.c @@ -534,14 +534,44 @@ static struct clksrc_sources clkset_audio2 = { .nr_sources = ARRAY_SIZE(clkset_audio2_list), }; -static struct clksrc_clk clk_audio0; -static struct clksrc_clk clk_audio1; -static struct clksrc_clk clk_audio2; +static struct clksrc_clk clksrc_audio[] = { + { + .clk = { + .name = "audio-bus", + .id = 0, + .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO0, + .enable = s5pc100_sclk1_ctrl, + }, + .sources = &clkset_audio0, + .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 12, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 12, .size = 3, }, + }, { + .clk = { + .name = "audio-bus", + .id = 1, + .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO1, + .enable = s5pc100_sclk1_ctrl, + }, + .sources = &clkset_audio1, + .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 16, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 16, .size = 3, }, + }, { + .clk = { + .name = "audio-bus", + .id = 2, + .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO2, + .enable = s5pc100_sclk1_ctrl, + }, + .sources = &clkset_audio2, + .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 20, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 20, .size = 3, }, + }, +}; static struct clk *clkset_spdif_list[] = { - &clk_audio0.clk, - &clk_audio1.clk, - &clk_audio2.clk, + &clksrc_audio[0].clk, + &clksrc_audio[1].clk, + &clksrc_audio[2].clk, }; static struct clksrc_sources clkset_spdif = { @@ -585,195 +615,136 @@ static struct clksrc_sources clkset_usbhost = { .nr_sources = ARRAY_SIZE(clkset_usbhost_list), }; -static struct clksrc_clk clk_spi0 = { - .clk = { - .name = "spi_bus", - .id = 0, - .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI0, - .enable = s5pc100_sclk0_ctrl, - - }, - .sources = &clkset_spi, - .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 4, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 4, .size = 2, }, -}; - -static struct clksrc_clk clk_spi1 = { - .clk = { - .name = "spi_bus", - .id = 1, - .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI1, - .enable = s5pc100_sclk0_ctrl, - }, - .sources = &clkset_spi, - .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 8, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 8, .size = 2, }, -}; - -static struct clksrc_clk clk_spi2 = { - .clk = { - .name = "spi_bus", - .id = 2, - .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI2, - .enable = s5pc100_sclk0_ctrl, - }, - .sources = &clkset_spi, - .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 12, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 12, .size = 2, }, -}; - -static struct clksrc_clk clk_uart_uclk1 = { - .clk = { - .name = "uclk1", - .id = -1, - .ctrlbit = S5PC100_CLKGATE_SCLK0_UART, - .enable = s5pc100_sclk0_ctrl, - }, - .sources = &clkset_uart, - .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 0, .size = 3, }, - .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 0, .size = 1, }, -}; - -static struct clksrc_clk clk_audio0 = { - .clk = { - .name = "audio-bus", - .id = 0, - .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO0, - .enable = s5pc100_sclk1_ctrl, - }, - .sources = &clkset_audio0, - .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 12, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 12, .size = 3, }, -}; - -static struct clksrc_clk clk_audio1 = { - .clk = { - .name = "audio-bus", - .id = 1, - .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO1, - .enable = s5pc100_sclk1_ctrl, - }, - .sources = &clkset_audio1, - .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 16, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 16, .size = 3, }, -}; - - -static struct clksrc_clk clk_audio2 = { - .clk = { - .name = "audio-bus", - .id = 2, - .ctrlbit = S5PC100_CLKGATE_SCLK1_AUDIO2, - .enable = s5pc100_sclk1_ctrl, - }, - .sources = &clkset_audio2, - .reg_div = { .reg = S5PC100_CLKDIV4, .shift = 20, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 20, .size = 3, }, -}; - -static struct clksrc_clk clk_spdif = { - .clk = { - .name = "spdif", - .id = -1, - }, - .sources = &clkset_spdif, - .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 24, .size = 2, }, -}; - -static struct clksrc_clk clk_lcd = { - .clk = { - .name = "lcd", - .id = -1, - .ctrlbit = S5PC100_CLKGATE_SCLK1_LCD, - .enable = s5pc100_sclk1_ctrl, - }, - .sources = &clkset_lcd_fimc, - .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 12, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 12, .size = 2, }, -}; - -static struct clksrc_clk clk_fimc0 = { - .clk = { - .name = "fimc", - .id = 0, - .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC0, - .enable = s5pc100_sclk1_ctrl, - }, - .sources = &clkset_lcd_fimc, - .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 16, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 16, .size = 2, }, -}; - -static struct clksrc_clk clk_fimc1 = { - .clk = { - .name = "fimc", - .id = 1, - .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC1, - .enable = s5pc100_sclk1_ctrl, - }, - .sources = &clkset_lcd_fimc, - .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 20, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 20, .size = 2, }, -}; - -static struct clksrc_clk clk_fimc2 = { - .clk = { - .name = "fimc", - .id = 2, - .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC2, - .enable = s5pc100_sclk1_ctrl, - }, - .sources = &clkset_lcd_fimc, - .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 24, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 24, .size = 2, }, -}; - -static struct clksrc_clk clk_mmc0 = { - .clk = { - .name = "mmc_bus", - .id = 0, - .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC0, - .enable = s5pc100_sclk0_ctrl, - }, - .sources = &clkset_mmc, - .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 0, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 0, .size = 2, }, -}; - -static struct clksrc_clk clk_mmc1 = { - .clk = { - .name = "mmc_bus", - .id = 1, - .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC1, - .enable = s5pc100_sclk0_ctrl, - }, - .sources = &clkset_mmc, - .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 4, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 4, .size = 2, }, -}; - -static struct clksrc_clk clk_mmc2 = { - .clk = { - .name = "mmc_bus", - .id = 2, - .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC2, - .enable = s5pc100_sclk0_ctrl, - }, - .sources = &clkset_mmc, - .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 8, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 8, .size = 2, }, -}; - -static struct clksrc_clk clk_usbhost = { - .clk = { - .name = "usbhost", - .id = -1, - .ctrlbit = S5PC100_CLKGATE_SCLK0_USBHOST, - .enable = s5pc100_sclk0_ctrl, - }, - .sources = &clkset_usbhost, - .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 20, .size = 4, }, - .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 20, .size = 2, }, +static struct clksrc_clk clksrc_clks[] = { + { + .clk = { + .name = "spi_bus", + .id = 0, + .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI0, + .enable = s5pc100_sclk0_ctrl, + + }, + .sources = &clkset_spi, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 4, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 4, .size = 2, }, + }, { + .clk = { + .name = "spi_bus", + .id = 1, + .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI1, + .enable = s5pc100_sclk0_ctrl, + }, + .sources = &clkset_spi, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 8, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 8, .size = 2, }, + }, { + .clk = { + .name = "spi_bus", + .id = 2, + .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI2, + .enable = s5pc100_sclk0_ctrl, + }, + .sources = &clkset_spi, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 12, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 12, .size = 2, }, + }, { + .clk = { + .name = "uclk1", + .id = -1, + .ctrlbit = S5PC100_CLKGATE_SCLK0_UART, + .enable = s5pc100_sclk0_ctrl, + }, + .sources = &clkset_uart, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 0, .size = 3, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 0, .size = 1, }, + }, { + .clk = { + .name = "spdif", + .id = -1, + }, + .sources = &clkset_spdif, + .reg_src = { .reg = S5PC100_CLKSRC3, .shift = 24, .size = 2, }, + }, { + .clk = { + .name = "lcd", + .id = -1, + .ctrlbit = S5PC100_CLKGATE_SCLK1_LCD, + .enable = s5pc100_sclk1_ctrl, + }, + .sources = &clkset_lcd_fimc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 12, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 12, .size = 2, }, + }, { + .clk = { + .name = "fimc", + .id = 0, + .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC0, + .enable = s5pc100_sclk1_ctrl, + }, + .sources = &clkset_lcd_fimc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 16, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 16, .size = 2, }, + }, { + .clk = { + .name = "fimc", + .id = 1, + .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC1, + .enable = s5pc100_sclk1_ctrl, + }, + .sources = &clkset_lcd_fimc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 20, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 20, .size = 2, }, + }, { + .clk = { + .name = "fimc", + .id = 2, + .ctrlbit = S5PC100_CLKGATE_SCLK1_FIMC2, + .enable = s5pc100_sclk1_ctrl, + }, + .sources = &clkset_lcd_fimc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 24, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 24, .size = 2, }, + }, { + .clk = { + .name = "mmc_bus", + .id = 0, + .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC0, + .enable = s5pc100_sclk0_ctrl, + }, + .sources = &clkset_mmc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 0, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 0, .size = 2, }, + }, { + .clk = { + .name = "mmc_bus", + .id = 1, + .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC1, + .enable = s5pc100_sclk0_ctrl, + }, + .sources = &clkset_mmc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 4, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 4, .size = 2, }, + }, { + .clk = { + .name = "mmc_bus", + .id = 2, + .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC2, + .enable = s5pc100_sclk0_ctrl, + }, + .sources = &clkset_mmc, + .reg_div = { .reg = S5PC100_CLKDIV3, .shift = 8, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC2, .shift = 8, .size = 2, }, + }, { + .clk = { + .name = "usbhost", + .id = -1, + .ctrlbit = S5PC100_CLKGATE_SCLK0_USBHOST, + .enable = s5pc100_sclk0_ctrl, + }, + .sources = &clkset_usbhost, + .reg_div = { .reg = S5PC100_CLKDIV2, .shift = 20, .size = 4, }, + .reg_src = { .reg = S5PC100_CLKSRC1, .shift = 20, .size = 2, }, + } }; /* Clock initialisation code */ @@ -785,22 +756,6 @@ static struct clksrc_clk *init_parents[] = { &clk_mout_onenand, &clk_mout_epll, &clk_mout_hpll, - &clk_spi0, - &clk_spi1, - &clk_spi2, - &clk_uart_uclk1, - &clk_audio0, - &clk_audio1, - &clk_audio2, - &clk_spdif, - &clk_lcd, - &clk_fimc0, - &clk_fimc1, - &clk_fimc2, - &clk_mmc0, - &clk_mmc1, - &clk_mmc2, - &clk_usbhost, }; #define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1) @@ -867,6 +822,12 @@ void __init_or_cpufreq s5pc100_setup_clocks(void) for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) s3c_set_clksrc(init_parents[ptr], true); + + for (ptr = 0; ptr < ARRAY_SIZE(clksrc_audio); ptr++) + s3c_set_clksrc(clksrc_audio + ptr, true); + + for (ptr = 0; ptr < ARRAY_SIZE(clksrc_clks); ptr++) + s3c_set_clksrc(clksrc_clks + ptr, true); } static struct clk *clks[] __initdata = { @@ -875,8 +836,13 @@ static struct clk *clks[] __initdata = { &clk_dout_d0_bus, &clk_dout_pclkd0, &clk_dout_apll2, + &clk_mout_apll.clk, + &clk_mout_mpll.clk, + &clk_mout_epll.clk, + &clk_mout_hpll.clk, &clk_mout_am.clk, &clk_dout_d1_bus, + &clk_mout_onenand.clk, &clk_dout_pclkd1, &clk_dout_mpll2, &clk_dout_cam, @@ -890,30 +856,6 @@ static struct clk *clks[] __initdata = { &clk_arm, }; -/* simplest change - will aggregate clocks later */ -static struct clksrc_clk *clks_src[] = { - &clk_mout_apll, - &clk_mout_mpll, - &clk_mout_onenand, - &clk_mout_epll, - &clk_spi0, - &clk_spi1, - &clk_spi2, - &clk_uart_uclk1, - &clk_audio0, - &clk_audio1, - &clk_audio2, - &clk_spdif, - &clk_lcd, - &clk_fimc0, - &clk_fimc1, - &clk_fimc2, - &clk_mmc0, - &clk_mmc1, - &clk_mmc2, - &clk_usbhost, -}; - void __init s5pc100_register_clocks(void) { struct clk *clkp; @@ -929,6 +871,6 @@ void __init s5pc100_register_clocks(void) } } - for (ptr = 0; ptr < ARRAY_SIZE(clks_src); ptr++) - s3c_register_clksrc(clks_src[ptr], 1); + s3c_register_clksrc(clksrc_audio, ARRAY_SIZE(clksrc_audio)); + s3c_register_clksrc(clksrc_clks, ARRAY_SIZE(clksrc_clks)); } -- cgit v0.10.2 From 37872bb9efddf9518cc77c43f9fa8fc6bfd3d727 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 12 Jan 2010 12:52:43 +0900 Subject: ARM: SAMSUNG: Add script to change old clksrc_clk to new register defs This script is used to change the old style clksrc_clk as originally found in plat-s3c64xx to the new style. It is here for reference if needed for future code merges. Signed-off-by: Ben Dooks diff --git a/Documentation/arm/Samsung/clksrc-change-registers.awk b/Documentation/arm/Samsung/clksrc-change-registers.awk new file mode 100755 index 0000000..0c50220 --- /dev/null +++ b/Documentation/arm/Samsung/clksrc-change-registers.awk @@ -0,0 +1,167 @@ +#!/usr/bin/awk -f +# +# Copyright 2010 Ben Dooks +# +# Released under GPLv2 + +# example usage +# ./clksrc-change-registers.awk arch/arm/plat-s5pc1xx/include/plat/regs-clock.h < src > dst + +function extract_value(s) +{ + eqat = index(s, "=") + comat = index(s, ",") + return substr(s, eqat+2, (comat-eqat)-2) +} + +function remove_brackets(b) +{ + return substr(b, 2, length(b)-2) +} + +function splitdefine(l, p) +{ + r = split(l, tp) + + p[0] = tp[2] + p[1] = remove_brackets(tp[3]) +} + +function find_length(f) +{ + if (0) + printf "find_length " f "\n" > "/dev/stderr" + + if (f ~ /0x1/) + return 1 + else if (f ~ /0x3/) + return 2 + else if (f ~ /0x7/) + return 3 + else if (f ~ /0xf/) + return 4 + + printf "unknown legnth " f "\n" > "/dev/stderr" + exit +} + +function find_shift(s) +{ + id = index(s, "<") + if (id <= 0) { + printf "cannot find shift " s "\n" > "/dev/stderr" + exit + } + + return substr(s, id+2) +} + + +BEGIN { + if (ARGC < 2) { + print "too few arguments" > "/dev/stderr" + exit + } + +# read the header file and find the mask values that we will need +# to replace and create an associative array of values + + while (getline line < ARGV[1] > 0) { + if (line ~ /\#define.*_MASK/ && + !(line ~ /S5PC100_EPLL_MASK/) && + !(line ~ /USB_SIG_MASK/)) { + splitdefine(line, fields) + name = fields[0] + if (0) + printf "MASK " line "\n" > "/dev/stderr" + dmask[name,0] = find_length(fields[1]) + dmask[name,1] = find_shift(fields[1]) + if (0) + printf "=> '" name "' LENGTH=" dmask[name,0] " SHIFT=" dmask[name,1] "\n" > "/dev/stderr" + } else { + } + } + + delete ARGV[1] +} + +/clksrc_clk.*=.*{/ { + shift="" + mask="" + divshift="" + reg_div="" + reg_src="" + indent=1 + + print $0 + + for(; indent >= 1;) { + if ((getline line) <= 0) { + printf "unexpected end of file" > "/dev/stderr" + exit 1; + } + + if (line ~ /\.shift/) { + shift = extract_value(line) + } else if (line ~ /\.mask/) { + mask = extract_value(line) + } else if (line ~ /\.reg_divider/) { + reg_div = extract_value(line) + } else if (line ~ /\.reg_source/) { + reg_src = extract_value(line) + } else if (line ~ /\.divider_shift/) { + divshift = extract_value(line) + } else if (line ~ /{/) { + indent++ + print line + } else if (line ~ /}/) { + indent-- + + if (indent == 0) { + if (0) { + printf "shift '" shift "' ='" dmask[shift,0] "'\n" > "/dev/stderr" + printf "mask '" mask "'\n" > "/dev/stderr" + printf "dshft '" divshift "'\n" > "/dev/stderr" + printf "rdiv '" reg_div "'\n" > "/dev/stderr" + printf "rsrc '" reg_src "'\n" > "/dev/stderr" + } + + generated = mask + sub(reg_src, reg_div, generated) + + if (0) { + printf "/* rsrc " reg_src " */\n" + printf "/* rdiv " reg_div " */\n" + printf "/* shift " shift " */\n" + printf "/* mask " mask " */\n" + printf "/* generated " generated " */\n" + } + + if (reg_div != "") { + printf "\t.reg_div = { " + printf ".reg = " reg_div ", " + printf ".shift = " dmask[generated,1] ", " + printf ".size = " dmask[generated,0] ", " + printf "},\n" + } + + printf "\t.reg_src = { " + printf ".reg = " reg_src ", " + printf ".shift = " dmask[mask,1] ", " + printf ".size = " dmask[mask,0] ", " + + printf "},\n" + + } + + print line + } else { + print line + } + + if (0) + printf indent ":" line "\n" > "/dev/stderr" + } +} + +// && ! /clksrc_clk.*=.*{/ { print $0 } -- cgit v0.10.2 From 35accd2f6639a9245488f2f389e3c6372c7641e4 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 12 Jan 2010 13:10:59 +0900 Subject: ARM: S5PC1XX: Remove definitions deleted by previous clksrc changes Remove the definitions we've deleted in the previous updates to the clksrc_clk for arch/arm/plat-s5pc1xx/include/plat/regs-clock.h. Added comments about the removal to the clock header since we only need these defines in one place (and they've now been removed there) we get rid of them from the header. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s5pc1xx/include/plat/regs-clock.h b/arch/arm/plat-s5pc1xx/include/plat/regs-clock.h index c5cc86e..24dec4e 100644 --- a/arch/arm/plat-s5pc1xx/include/plat/regs-clock.h +++ b/arch/arm/plat-s5pc1xx/include/plat/regs-clock.h @@ -61,73 +61,10 @@ #define S5PC100_EPLL_MASK 0xffffffff #define S5PC100_EPLLVAL(_m, _p, _s) ((_m) << 16 | ((_p) << 8) | ((_s))) -/* CLKSRC0 */ -#define S5PC100_CLKSRC0_APLL_MASK (0x1<<0) -#define S5PC100_CLKSRC0_APLL_SHIFT (0) -#define S5PC100_CLKSRC0_MPLL_MASK (0x1<<4) -#define S5PC100_CLKSRC0_MPLL_SHIFT (4) -#define S5PC100_CLKSRC0_EPLL_MASK (0x1<<8) -#define S5PC100_CLKSRC0_EPLL_SHIFT (8) -#define S5PC100_CLKSRC0_HPLL_MASK (0x1<<12) -#define S5PC100_CLKSRC0_HPLL_SHIFT (12) -#define S5PC100_CLKSRC0_AMMUX_MASK (0x1<<16) -#define S5PC100_CLKSRC0_AMMUX_SHIFT (16) -#define S5PC100_CLKSRC0_HREF_MASK (0x1<<20) -#define S5PC100_CLKSRC0_HREF_SHIFT (20) -#define S5PC100_CLKSRC0_ONENAND_MASK (0x1<<24) -#define S5PC100_CLKSRC0_ONENAND_SHIFT (24) - - -/* CLKSRC1 */ -#define S5PC100_CLKSRC1_UART_MASK (0x1<<0) -#define S5PC100_CLKSRC1_UART_SHIFT (0) -#define S5PC100_CLKSRC1_SPI0_MASK (0x3<<4) -#define S5PC100_CLKSRC1_SPI0_SHIFT (4) -#define S5PC100_CLKSRC1_SPI1_MASK (0x3<<8) -#define S5PC100_CLKSRC1_SPI1_SHIFT (8) -#define S5PC100_CLKSRC1_SPI2_MASK (0x3<<12) -#define S5PC100_CLKSRC1_SPI2_SHIFT (12) -#define S5PC100_CLKSRC1_IRDA_MASK (0x3<<16) -#define S5PC100_CLKSRC1_IRDA_SHIFT (16) -#define S5PC100_CLKSRC1_UHOST_MASK (0x3<<20) -#define S5PC100_CLKSRC1_UHOST_SHIFT (20) -#define S5PC100_CLKSRC1_CLK48M_MASK (0x1<<24) +/* CLKSRC0..CLKSRC3 -> mostly removed due to clksrc updates */ +#define S5PC100_CLKSRC1_CLK48M_MASK (0x1<<24) #define S5PC100_CLKSRC1_CLK48M_SHIFT (24) -/* CLKSRC2 */ -#define S5PC100_CLKSRC2_MMC0_MASK (0x3<<0) -#define S5PC100_CLKSRC2_MMC0_SHIFT (0) -#define S5PC100_CLKSRC2_MMC1_MASK (0x3<<4) -#define S5PC100_CLKSRC2_MMC1_SHIFT (4) -#define S5PC100_CLKSRC2_MMC2_MASK (0x3<<8) -#define S5PC100_CLKSRC2_MMC2_SHIFT (8) -#define S5PC100_CLKSRC2_LCD_MASK (0x3<<12) -#define S5PC100_CLKSRC2_LCD_SHIFT (12) -#define S5PC100_CLKSRC2_FIMC0_MASK (0x3<<16) -#define S5PC100_CLKSRC2_FIMC0_SHIFT (16) -#define S5PC100_CLKSRC2_FIMC1_MASK (0x3<<20) -#define S5PC100_CLKSRC2_FIMC1_SHIFT (20) -#define S5PC100_CLKSRC2_FIMC2_MASK (0x3<<24) -#define S5PC100_CLKSRC2_FIMC2_SHIFT (24) -#define S5PC100_CLKSRC2_MIXER_MASK (0x3<<28) -#define S5PC100_CLKSRC2_MIXER_SHIFT (28) - -/* CLKSRC3 */ -#define S5PC100_CLKSRC3_PWI_MASK (0x3<<0) -#define S5PC100_CLKSRC3_PWI_SHIFT (0) -#define S5PC100_CLKSRC3_HCLKD2_MASK (0x1<<4) -#define S5PC100_CLKSRC3_HCLKD2_SHIFT (4) -#define S5PC100_CLKSRC3_I2SD2_MASK (0x3<<8) -#define S5PC100_CLKSRC3_I2SD2_SHIFT (8) -#define S5PC100_CLKSRC3_AUDIO0_MASK (0x7<<12) -#define S5PC100_CLKSRC3_AUDIO0_SHIFT (12) -#define S5PC100_CLKSRC3_AUDIO1_MASK (0x7<<16) -#define S5PC100_CLKSRC3_AUDIO1_SHIFT (16) -#define S5PC100_CLKSRC3_AUDIO2_MASK (0x7<<20) -#define S5PC100_CLKSRC3_AUDIO2_SHIFT (20) -#define S5PC100_CLKSRC3_SPDIF_MASK (0x3<<24) -#define S5PC100_CLKSRC3_SPDIF_SHIFT (24) - /* CLKDIV0 */ #define S5PC100_CLKDIV0_APLL_MASK (0x1<<0) #define S5PC100_CLKDIV0_APLL_SHIFT (0) @@ -140,7 +77,7 @@ #define S5PC100_CLKDIV0_SECSS_MASK (0x7<<16) #define S5PC100_CLKDIV0_SECSS_SHIFT (16) -/* CLKDIV1 */ +/* CLKDIV1 (OneNAND clock only used in one place, removed) */ #define S5PC100_CLKDIV1_APLL2_MASK (0x7<<0) #define S5PC100_CLKDIV1_APLL2_SHIFT (0) #define S5PC100_CLKDIV1_MPLL_MASK (0x3<<4) @@ -151,56 +88,12 @@ #define S5PC100_CLKDIV1_D1_SHIFT (12) #define S5PC100_CLKDIV1_PCLKD1_MASK (0x7<<16) #define S5PC100_CLKDIV1_PCLKD1_SHIFT (16) -#define S5PC100_CLKDIV1_ONENAND_MASK (0x3<<20) -#define S5PC100_CLKDIV1_ONENAND_SHIFT (20) #define S5PC100_CLKDIV1_CAM_MASK (0x1F<<24) #define S5PC100_CLKDIV1_CAM_SHIFT (24) -/* CLKDIV2 */ -#define S5PC100_CLKDIV2_UART_MASK (0x7<<0) -#define S5PC100_CLKDIV2_UART_SHIFT (0) -#define S5PC100_CLKDIV2_SPI0_MASK (0xf<<4) -#define S5PC100_CLKDIV2_SPI0_SHIFT (4) -#define S5PC100_CLKDIV2_SPI1_MASK (0xf<<8) -#define S5PC100_CLKDIV2_SPI1_SHIFT (8) -#define S5PC100_CLKDIV2_SPI2_MASK (0xf<<12) -#define S5PC100_CLKDIV2_SPI2_SHIFT (12) -#define S5PC100_CLKDIV2_IRDA_MASK (0xf<<16) -#define S5PC100_CLKDIV2_IRDA_SHIFT (16) -#define S5PC100_CLKDIV2_UHOST_MASK (0xf<<20) -#define S5PC100_CLKDIV2_UHOST_SHIFT (20) - -/* CLKDIV3 */ -#define S5PC100_CLKDIV3_MMC0_MASK (0xf<<0) -#define S5PC100_CLKDIV3_MMC0_SHIFT (0) -#define S5PC100_CLKDIV3_MMC1_MASK (0xf<<4) -#define S5PC100_CLKDIV3_MMC1_SHIFT (4) -#define S5PC100_CLKDIV3_MMC2_MASK (0xf<<8) -#define S5PC100_CLKDIV3_MMC2_SHIFT (8) -#define S5PC100_CLKDIV3_LCD_MASK (0xf<<12) -#define S5PC100_CLKDIV3_LCD_SHIFT (12) -#define S5PC100_CLKDIV3_FIMC0_MASK (0xf<<16) -#define S5PC100_CLKDIV3_FIMC0_SHIFT (16) -#define S5PC100_CLKDIV3_FIMC1_MASK (0xf<<20) -#define S5PC100_CLKDIV3_FIMC1_SHIFT (20) -#define S5PC100_CLKDIV3_FIMC2_MASK (0xf<<24) -#define S5PC100_CLKDIV3_FIMC2_SHIFT (24) -#define S5PC100_CLKDIV3_HDMI_MASK (0xf<<28) -#define S5PC100_CLKDIV3_HDMI_SHIFT (28) - -/* CLKDIV4 */ -#define S5PC100_CLKDIV4_PWI_MASK (0x7<<0) -#define S5PC100_CLKDIV4_PWI_SHIFT (0) -#define S5PC100_CLKDIV4_HCLKD2_MASK (0x7<<4) -#define S5PC100_CLKDIV4_HCLKD2_SHIFT (4) -#define S5PC100_CLKDIV4_I2SD2_MASK (0xf<<8) -#define S5PC100_CLKDIV4_I2SD2_SHIFT (8) -#define S5PC100_CLKDIV4_AUDIO0_MASK (0xf<<12) -#define S5PC100_CLKDIV4_AUDIO0_SHIFT (12) -#define S5PC100_CLKDIV4_AUDIO1_MASK (0xf<<16) -#define S5PC100_CLKDIV4_AUDIO1_SHIFT (16) -#define S5PC100_CLKDIV4_AUDIO2_MASK (0xf<<20) -#define S5PC100_CLKDIV4_AUDIO2_SHIFT (20) +/* CLKDIV2 => removed in clksrc update */ +/* CLKDIV3 => removed in clksrc update, or not needed */ +/* CLKDIV4 => removed in clksrc update, or not needed */ /* HCLKD0/PCLKD0 Clock Gate 0 Registers */ #define S5PC100_CLKGATE_D00_INTC (1<<0) -- cgit v0.10.2 From 47101ec73901183520de724fb5f9062c014236bb Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Thu, 7 Jan 2010 14:41:38 +0900 Subject: ARM: S5PC1XX: Move to using standard timer IRQ handling code Move to using the standard VIC/Timer IRQ handling code added previously to avoid duplicating code. Thanks to Marek Szyprowski for pointing out dual Kconfig change. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5pc100/include/mach/tick.h b/arch/arm/mach-s5pc100/include/mach/tick.h index d3de0f3..f338c9e 100644 --- a/arch/arm/mach-s5pc100/include/mach/tick.h +++ b/arch/arm/mach-s5pc100/include/mach/tick.h @@ -21,7 +21,7 @@ static inline u32 s3c24xx_ostimer_pending(void) { u32 pend = __raw_readl(S3C_VA_VIC0 + VIC_RAW_STATUS); - return pend & 1 << (IRQ_TIMER4 - S5PC1XX_IRQ_VIC0(0)); + return pend & 1 << (IRQ_TIMER4_VIC - S5PC1XX_IRQ_VIC0(0)); } #define TICK_MAX (0xffffffff) diff --git a/arch/arm/plat-s5pc1xx/Kconfig b/arch/arm/plat-s5pc1xx/Kconfig index 5d97b1c..256f0f9 100644 --- a/arch/arm/plat-s5pc1xx/Kconfig +++ b/arch/arm/plat-s5pc1xx/Kconfig @@ -12,6 +12,7 @@ config PLAT_S5PC1XX select NO_IOPORT select ARCH_REQUIRE_GPIOLIB select SAMSUNG_CLKSRC + select SAMSUNG_IRQ_VIC_TIMER select S3C_GPIO_TRACK select S3C_GPIO_PULL_UPDOWN select S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/plat-s5pc1xx/include/plat/irqs.h b/arch/arm/plat-s5pc1xx/include/plat/irqs.h index ef87363..409c804 100644 --- a/arch/arm/plat-s5pc1xx/include/plat/irqs.h +++ b/arch/arm/plat-s5pc1xx/include/plat/irqs.h @@ -88,11 +88,11 @@ #define IRQ_MDMA S5PC1XX_IRQ_VIC0(18) #define IRQ_PDMA0 S5PC1XX_IRQ_VIC0(19) #define IRQ_PDMA1 S5PC1XX_IRQ_VIC0(20) -#define IRQ_TIMER0 S5PC1XX_IRQ_VIC0(21) -#define IRQ_TIMER1 S5PC1XX_IRQ_VIC0(22) -#define IRQ_TIMER2 S5PC1XX_IRQ_VIC0(23) -#define IRQ_TIMER3 S5PC1XX_IRQ_VIC0(24) -#define IRQ_TIMER4 S5PC1XX_IRQ_VIC0(25) +#define IRQ_TIMER0_VIC S5PC1XX_IRQ_VIC0(21) +#define IRQ_TIMER1_VIC S5PC1XX_IRQ_VIC0(22) +#define IRQ_TIMER2_VIC S5PC1XX_IRQ_VIC0(23) +#define IRQ_TIMER3_VIC S5PC1XX_IRQ_VIC0(24) +#define IRQ_TIMER4_VIC S5PC1XX_IRQ_VIC0(25) #define IRQ_SYSTIMER S5PC1XX_IRQ_VIC0(26) #define IRQ_WDT S5PC1XX_IRQ_VIC0(27) #define IRQ_RTC_ALARM S5PC1XX_IRQ_VIC0(28) @@ -171,8 +171,15 @@ #define IRQ_SDMIRQ S5PC1XX_IRQ_VIC2(30) #define IRQ_SDMFIQ S5PC1XX_IRQ_VIC2(31) +#define IRQ_TIMER(x) (IRQ_SDMFIQ + 1 + (x)) +#define IRQ_TIMER0 IRQ_TIMER(0) +#define IRQ_TIMER1 IRQ_TIMER(1) +#define IRQ_TIMER2 IRQ_TIMER(2) +#define IRQ_TIMER3 IRQ_TIMER(3) +#define IRQ_TIMER4 IRQ_TIMER(4) + /* External interrupt */ -#define S3C_IRQ_EINT_BASE (IRQ_SDMFIQ + 1) +#define S3C_IRQ_EINT_BASE (IRQ_SDMFIQ + 6) #define S3C_EINT(x) (S3C_IRQ_EINT_BASE + (x - 16)) #define IRQ_EINT(x) (x < 16 ? IRQ_EINT0 + x : S3C_EINT(x)) diff --git a/arch/arm/plat-s5pc1xx/irq.c b/arch/arm/plat-s5pc1xx/irq.c index e44fd04..ae233bd 100644 --- a/arch/arm/plat-s5pc1xx/irq.c +++ b/arch/arm/plat-s5pc1xx/irq.c @@ -20,77 +20,9 @@ #include #include -#include +#include #include -/* Timer interrupt handling */ - -static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq) -{ - generic_handle_irq(sub_irq); -} - -static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER0); -} - -static void s3c_irq_demux_timer1(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER1); -} - -static void s3c_irq_demux_timer2(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER2); -} - -static void s3c_irq_demux_timer3(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER3); -} - -static void s3c_irq_demux_timer4(unsigned int irq, struct irq_desc *desc) -{ - s3c_irq_demux_timer(irq, IRQ_TIMER4); -} - -/* We assume the IRQ_TIMER0..IRQ_TIMER4 range is continuous. */ - -static void s3c_irq_timer_mask(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; /* mask out pending interrupts */ - reg &= ~(1 << (irq - IRQ_TIMER0)); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static void s3c_irq_timer_unmask(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; /* mask out pending interrupts */ - reg |= 1 << (irq - IRQ_TIMER0); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static void s3c_irq_timer_ack(unsigned int irq) -{ - u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); - - reg &= 0x1f; /* mask out pending interrupts */ - reg |= (1 << 5) << (irq - IRQ_TIMER0); - __raw_writel(reg, S3C64XX_TINT_CSTAT); -} - -static struct irq_chip s3c_irq_timer = { - .name = "s3c-timer", - .mask = s3c_irq_timer_mask, - .unmask = s3c_irq_timer_unmask, - .ack = s3c_irq_timer_ack, -}; - struct uart_irq { void __iomem *regs; unsigned int base_irq; @@ -229,7 +161,7 @@ static void __init s5pc1xx_uart_irq(struct uart_irq *uirq) void __init s5pc1xx_init_irq(u32 *vic_valid, int num) { int i; - int uart, irq; + int uart; printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); @@ -240,17 +172,11 @@ void __init s5pc1xx_init_irq(u32 *vic_valid, int num) /* add the timer sub-irqs */ - set_irq_chained_handler(IRQ_TIMER0, s3c_irq_demux_timer0); - set_irq_chained_handler(IRQ_TIMER1, s3c_irq_demux_timer1); - set_irq_chained_handler(IRQ_TIMER2, s3c_irq_demux_timer2); - set_irq_chained_handler(IRQ_TIMER3, s3c_irq_demux_timer3); - set_irq_chained_handler(IRQ_TIMER4, s3c_irq_demux_timer4); - - for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) { - set_irq_chip(irq, &s3c_irq_timer); - set_irq_handler(irq, handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } + s3c_init_vic_timer_irq(IRQ_TIMER0_VIC, IRQ_TIMER0); + s3c_init_vic_timer_irq(IRQ_TIMER1_VIC, IRQ_TIMER1); + s3c_init_vic_timer_irq(IRQ_TIMER2_VIC, IRQ_TIMER2); + s3c_init_vic_timer_irq(IRQ_TIMER3_VIC, IRQ_TIMER3); + s3c_init_vic_timer_irq(IRQ_TIMER4_VIC, IRQ_TIMER4); for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++) s5pc1xx_uart_irq(&uart_irqs[uart]); -- cgit v0.10.2 From be97162d47b2e067f9d21097650f2f0405dacc9f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 12 Jan 2010 14:13:58 +0900 Subject: ARM: S5PC1XX: Use common UART IRQ handling code Use the common UART IRQ handling code for the S5PC100 system. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s5pc1xx/Kconfig b/arch/arm/plat-s5pc1xx/Kconfig index 256f0f9..c7ccdf2 100644 --- a/arch/arm/plat-s5pc1xx/Kconfig +++ b/arch/arm/plat-s5pc1xx/Kconfig @@ -12,6 +12,7 @@ config PLAT_S5PC1XX select NO_IOPORT select ARCH_REQUIRE_GPIOLIB select SAMSUNG_CLKSRC + select SAMSUNG_IRQ_UART select SAMSUNG_IRQ_VIC_TIMER select S3C_GPIO_TRACK select S3C_GPIO_PULL_UPDOWN diff --git a/arch/arm/plat-s5pc1xx/irq.c b/arch/arm/plat-s5pc1xx/irq.c index ae233bd..bfc5248 100644 --- a/arch/arm/plat-s5pc1xx/irq.c +++ b/arch/arm/plat-s5pc1xx/irq.c @@ -21,18 +21,13 @@ #include #include +#include #include -struct uart_irq { - void __iomem *regs; - unsigned int base_irq; - unsigned int parent_irq; -}; - /* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] * are consecutive when looking up the interrupt in the demux routines. */ -static struct uart_irq uart_irqs[] = { +static struct s3c_uart_irq uart_irqs[] = { [0] = { .regs = (void *)S3C_VA_UART0, .base_irq = IRQ_S3CUART_BASE0, @@ -55,113 +50,9 @@ static struct uart_irq uart_irqs[] = { }, }; -static inline void __iomem *s3c_irq_uart_base(unsigned int irq) -{ - struct uart_irq *uirq = get_irq_chip_data(irq); - return uirq->regs; -} - -static inline unsigned int s3c_irq_uart_bit(unsigned int irq) -{ - return irq & 3; -} - -/* UART interrupt registers, not worth adding to seperate include header */ -#define S3C64XX_UINTP 0x30 -#define S3C64XX_UINTSP 0x34 -#define S3C64XX_UINTM 0x38 - -static void s3c_irq_uart_mask(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg |= (1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); -} - -static void s3c_irq_uart_maskack(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg |= (1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); - __raw_writel(1 << bit, regs + S3C64XX_UINTP); -} - -static void s3c_irq_uart_unmask(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - u32 reg; - - reg = __raw_readl(regs + S3C64XX_UINTM); - reg &= ~(1 << bit); - __raw_writel(reg, regs + S3C64XX_UINTM); -} - -static void s3c_irq_uart_ack(unsigned int irq) -{ - void __iomem *regs = s3c_irq_uart_base(irq); - unsigned int bit = s3c_irq_uart_bit(irq); - - __raw_writel(1 << bit, regs + S3C64XX_UINTP); -} - -static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc) -{ - struct uart_irq *uirq = &uart_irqs[irq - IRQ_UART0]; - u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP); - int base = uirq->base_irq; - - if (pend & (1 << 0)) - generic_handle_irq(base); - if (pend & (1 << 1)) - generic_handle_irq(base + 1); - if (pend & (1 << 2)) - generic_handle_irq(base + 2); - if (pend & (1 << 3)) - generic_handle_irq(base + 3); -} - -static struct irq_chip s3c_irq_uart = { - .name = "s3c-uart", - .mask = s3c_irq_uart_mask, - .unmask = s3c_irq_uart_unmask, - .mask_ack = s3c_irq_uart_maskack, - .ack = s3c_irq_uart_ack, -}; - -static void __init s5pc1xx_uart_irq(struct uart_irq *uirq) -{ - void __iomem *reg_base = uirq->regs; - unsigned int irq; - int offs; - - /* mask all interrupts at the start. */ - __raw_writel(0xf, reg_base + S3C64XX_UINTM); - - for (offs = 0; offs < 3; offs++) { - irq = uirq->base_irq + offs; - - set_irq_chip(irq, &s3c_irq_uart); - set_irq_chip_data(irq, uirq); - set_irq_handler(irq, handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } - - set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart); -} - void __init s5pc1xx_init_irq(u32 *vic_valid, int num) { int i; - int uart; printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); @@ -178,8 +69,7 @@ void __init s5pc1xx_init_irq(u32 *vic_valid, int num) s3c_init_vic_timer_irq(IRQ_TIMER3_VIC, IRQ_TIMER3); s3c_init_vic_timer_irq(IRQ_TIMER4_VIC, IRQ_TIMER4); - for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++) - s5pc1xx_uart_irq(&uart_irqs[uart]); + s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); } -- cgit v0.10.2 From 209fecd1b8e65b8046efbbc8314d449e53c4c6b6 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 15:29:17 +0900 Subject: ARM: S5P6440: Add new CPU initialization support This patch adds Samsung's S5P6440 CPU support. Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5p6440/cpu.c b/arch/arm/mach-s5p6440/cpu.c new file mode 100644 index 0000000..1794131a --- /dev/null +++ b/arch/arm/mach-s5p6440/cpu.c @@ -0,0 +1,114 @@ +/* linux/arch/arm/mach-s5p6440/cpu.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +static void s5p6440_idle(void) +{ + unsigned long val; + + if (!need_resched()) { + val = __raw_readl(S5P_PWR_CFG); + val &= ~(0x3<<5); + val |= (0x1<<5); + __raw_writel(val, S5P_PWR_CFG); + + cpu_do_idle(); + } + local_irq_enable(); +} + +/* s5p6440_map_io + * + * register the standard cpu IO areas +*/ + +void __init s5p6440_map_io(void) +{ + /* initialize any device information early */ +} + +void __init s5p6440_init_clocks(int xtal) +{ + printk(KERN_DEBUG "%s: initializing clocks\n", __func__); + + s3c24xx_register_baseclocks(xtal); + s5p_register_clocks(xtal); + s5p6440_register_clocks(); + s5p6440_setup_clocks(); +} + +void __init s5p6440_init_irq(void) +{ + /* S5P6440 supports only 2 VIC */ + u32 vic[2]; + + /* + * VIC0 is missing IRQ_VIC0[3, 4, 8, 10, (12-22)] + * VIC1 is missing IRQ VIC1[1, 3, 4, 10, 11, 12, 14, 15, 22] + */ + vic[0] = 0xff800ae7; + vic[1] = 0xffbf23e5; + + s5p_init_irq(vic, ARRAY_SIZE(vic)); +} + +static struct sysdev_class s5p6440_sysclass = { + .name = "s5p6440-core", +}; + +static struct sys_device s5p6440_sysdev = { + .cls = &s5p6440_sysclass, +}; + +static int __init s5p6440_core_init(void) +{ + return sysdev_class_register(&s5p6440_sysclass); +} + +core_initcall(s5p6440_core_init); + +int __init s5p6440_init(void) +{ + printk(KERN_INFO "S5P6440: Initializing architecture\n"); + + /* set idle function */ + pm_idle = s5p6440_idle; + + return sysdev_register(&s5p6440_sysdev); +} diff --git a/arch/arm/mach-s5p6440/include/mach/debug-macro.S b/arch/arm/mach-s5p6440/include/mach/debug-macro.S new file mode 100644 index 0000000..f3a5d16 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/debug-macro.S @@ -0,0 +1,37 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/debug-macro.S + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +/* pull in the relevant register and map files. */ + +#include +#include + + /* note, for the boot process to work we have to keep the UART + * virtual address aligned to an 1MiB boundary for the L1 + * mapping the head code makes. We keep the UART virtual address + * aligned and add in the offset when we load the value here. + */ + + .macro addruart, rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 + ldreq \rx, = S5P_PA_UART + ldrne \rx, = (S5P_VA_UART + S5P_PA_UART & 0xfffff) +#if CONFIG_DEBUG_S3C_UART != 0 + add \rx, \rx, #(0x400 * CONFIG_DEBUG_S3C_UART) +#endif + .endm + +/* include the reset of the code which will do the work, we're only + * compiling for a single cpu processor type so the default of s3c2440 + * will be fine with us. + */ + +#include diff --git a/arch/arm/mach-s5p6440/include/mach/entry-macro.S b/arch/arm/mach-s5p6440/include/mach/entry-macro.S new file mode 100644 index 0000000..e65f1b9 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/entry-macro.S @@ -0,0 +1,16 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/entry-macro.S + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Low-level IRQ helper macros for the Samsung S5P6440 + * + * 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 diff --git a/arch/arm/mach-s5p6440/include/mach/gpio-core.h b/arch/arm/mach-s5p6440/include/mach/gpio-core.h new file mode 100644 index 0000000..ff7fb30 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/gpio-core.h @@ -0,0 +1,19 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/gpio-core.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - GPIO core 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 __ASM_ARCH_GPIO_CORE_H +#define __ASM_ARCH_GPIO_CORE_H __FILE__ + +/* currently we just include the platform support */ +#include + +#endif /* __ASM_ARCH_GPIO_CORE_H */ diff --git a/arch/arm/mach-s5p6440/include/mach/gpio.h b/arch/arm/mach-s5p6440/include/mach/gpio.h new file mode 100644 index 0000000..2178383 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/gpio.h @@ -0,0 +1,80 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/gpio.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - GPIO lib 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 __ASM_ARCH_GPIO_H +#define __ASM_ARCH_GPIO_H __FILE__ + +#define gpio_get_value __gpio_get_value +#define gpio_set_value __gpio_set_value +#define gpio_cansleep __gpio_cansleep +#define gpio_to_irq __gpio_to_irq + +/* GPIO bank sizes */ +#define S5P6440_GPIO_A_NR (6) +#define S5P6440_GPIO_B_NR (7) +#define S5P6440_GPIO_C_NR (8) +#define S5P6440_GPIO_F_NR (2) +#define S5P6440_GPIO_G_NR (7) +#define S5P6440_GPIO_H_NR (10) +#define S5P6440_GPIO_I_NR (16) +#define S5P6440_GPIO_J_NR (12) +#define S5P6440_GPIO_N_NR (16) +#define S5P6440_GPIO_P_NR (8) +#define S5P6440_GPIO_R_NR (15) + +/* GPIO bank numbers */ + +/* CONFIG_S3C_GPIO_SPACE allows the user to select extra + * space for debugging purposes so that any accidental + * change from one gpio bank to another can be caught. +*/ +#define S5P6440_GPIO_NEXT(__gpio) \ + ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1) + +enum s5p_gpio_number { + S5P6440_GPIO_A_START = 0, + S5P6440_GPIO_B_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_A), + S5P6440_GPIO_C_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_B), + S5P6440_GPIO_F_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_C), + S5P6440_GPIO_G_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_F), + S5P6440_GPIO_H_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_G), + S5P6440_GPIO_I_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_H), + S5P6440_GPIO_J_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_I), + S5P6440_GPIO_N_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_J), + S5P6440_GPIO_P_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_N), + S5P6440_GPIO_R_START = S5P6440_GPIO_NEXT(S5P6440_GPIO_P), +}; + +/* S5P6440 GPIO number definitions. */ +#define S5P6440_GPA(_nr) (S5P6440_GPIO_A_START + (_nr)) +#define S5P6440_GPB(_nr) (S5P6440_GPIO_B_START + (_nr)) +#define S5P6440_GPC(_nr) (S5P6440_GPIO_C_START + (_nr)) +#define S5P6440_GPF(_nr) (S5P6440_GPIO_F_START + (_nr)) +#define S5P6440_GPG(_nr) (S5P6440_GPIO_G_START + (_nr)) +#define S5P6440_GPH(_nr) (S5P6440_GPIO_H_START + (_nr)) +#define S5P6440_GPI(_nr) (S5P6440_GPIO_I_START + (_nr)) +#define S5P6440_GPJ(_nr) (S5P6440_GPIO_J_START + (_nr)) +#define S5P6440_GPN(_nr) (S5P6440_GPIO_N_START + (_nr)) +#define S5P6440_GPP(_nr) (S5P6440_GPIO_P_START + (_nr)) +#define S5P6440_GPR(_nr) (S5P6440_GPIO_R_START + (_nr)) + +/* the end of the S5P6440 specific gpios */ +#define S5P6440_GPIO_END (S5P6440_GPR(S5P6440_GPIO_R_NR) + 1) +#define S3C_GPIO_END S5P6440_GPIO_END + +/* define the number of gpios we need to the one after the GPR() range */ +#define ARCH_NR_GPIOS (S5P6440_GPR(S5P6440_GPIO_R_NR) + \ + CONFIG_SAMSUNG_GPIO_EXTRA + 1) + +#include + +#endif /* __ASM_ARCH_GPIO_H */ diff --git a/arch/arm/mach-s5p6440/include/mach/hardware.h b/arch/arm/mach-s5p6440/include/mach/hardware.h new file mode 100644 index 0000000..be8b26e --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/hardware.h @@ -0,0 +1,18 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/hardware.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - Hardware 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 __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H __FILE__ + +/* currently nothing here, placeholder */ + +#endif /* __ASM_ARCH_HARDWARE_H */ diff --git a/arch/arm/mach-s5p6440/include/mach/map.h b/arch/arm/mach-s5p6440/include/mach/map.h new file mode 100644 index 0000000..b370329 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/map.h @@ -0,0 +1,94 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/map.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - Memory map definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_MAP_H +#define __ASM_ARCH_MAP_H __FILE__ + +#include + +/* SYSCON */ +#define S5P6440_PA_SYSCON (0xE0100000) +#define S5P_PA_SYSCON S5P6440_PA_SYSCON +#define S5P_VA_SYSCON S3C_VA_SYS + +#define S5P6440_PA_CLK (S5P6440_PA_SYSCON + 0x0) +#define S5P_PA_CLK S5P6440_PA_CLK +#define S5P_VA_CLK (S5P_VA_SYSCON + 0x0) + +/* GPIO */ +#define S5P6440_PA_GPIO (0xE0308000) +#define S5P_PA_GPIO S5P6440_PA_GPIO +#define S5P_VA_GPIO S3C_ADDR(0x00500000) + +/* VIC0 */ +#define S5P6440_PA_VIC0 (0xE4000000) +#define S5P_PA_VIC0 S5P6440_PA_VIC0 +#define S5P_VA_VIC0 (S3C_VA_IRQ + 0x0) +#define VA_VIC0 S5P_VA_VIC0 + +/* VIC1 */ +#define S5P6440_PA_VIC1 (0xE4100000) +#define S5P_PA_VIC1 S5P6440_PA_VIC1 +#define S5P_VA_VIC1 (S3C_VA_IRQ + 0x10000) +#define VA_VIC1 S5P_VA_VIC1 + +/* Timer */ +#define S5P6440_PA_TIMER (0xEA000000) +#define S5P_PA_TIMER S5P6440_PA_TIMER +#define S5P_VA_TIMER S3C_VA_TIMER + +/* RTC */ +#define S5P6440_PA_RTC (0xEA100000) +#define S5P_PA_RTC S5P6440_PA_RTC +#define S5P_VA_RTC S3C_ADDR(0x00600000) + +/* WDT */ +#define S5P6440_PA_WDT (0xEA200000) +#define S5P_PA_WDT S5P6440_PA_WDT +#define S5p_VA_WDT S3C_VA_WATCHDOG + +/* UART */ +#define S5P6440_PA_UART (0xEC000000) +#define S5P_PA_UART S5P6440_PA_UART +#define S5P_VA_UART S3C_VA_UART + +#define S5P_PA_UART0 (S5P_PA_UART + 0x0) +#define S5P_PA_UART1 (S5P_PA_UART + 0x400) +#define S5P_PA_UART2 (S5P_PA_UART + 0x800) +#define S5P_PA_UART3 (S5P_PA_UART + 0xC00) +#define S5P_UART_OFFSET (0x400) + +#define S5P_VA_UARTx(x) (S5P_VA_UART + (S5P_PA_UART & 0xfffff) \ + + ((x) * S5P_UART_OFFSET)) + +#define S5P_VA_UART0 S5P_VA_UARTx(0) +#define S5P_VA_UART1 S5P_VA_UARTx(1) +#define S5P_VA_UART2 S5P_VA_UARTx(2) +#define S5P_VA_UART3 S5P_VA_UARTx(3) +#define S5P_SZ_UART SZ_256 + +/* I2C */ +#define S5P6440_PA_IIC0 (0xEC104000) +#define S5P_PA_IIC0 S5P6440_PA_IIC0 +#define S5p_VA_IIC0 S3C_ADDR(0x00700000) + +/* SDRAM */ +#define S5P6440_PA_SDRAM (0x20000000) +#define S5P_PA_SDRAM S5P6440_PA_SDRAM + +/* compatibiltiy defines. */ +#define S3C_PA_UART S5P_PA_UART +#define S3C_UART_OFFSET S5P_UART_OFFSET +#define S3C_PA_TIMER S5P_PA_TIMER +#define S3C_PA_IIC S5P_PA_IIC0 + +#endif /* __ASM_ARCH_MAP_H */ diff --git a/arch/arm/mach-s5p6440/include/mach/memory.h b/arch/arm/mach-s5p6440/include/mach/memory.h new file mode 100644 index 0000000..d62910c --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/memory.h @@ -0,0 +1,19 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/memory.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - Memory definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +#define PHYS_OFFSET UL(0x20000000) +#define CONSISTENT_DMA_SIZE SZ_8M + +#endif /* __ASM_ARCH_MEMORY_H */ diff --git a/arch/arm/mach-s5p6440/include/mach/system.h b/arch/arm/mach-s5p6440/include/mach/system.h new file mode 100644 index 0000000..d2dd817 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/system.h @@ -0,0 +1,26 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/system.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - system support header + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H __FILE__ + +static void arch_idle(void) +{ + /* nothing here yet */ +} + +static void arch_reset(char mode, const char *cmd) +{ + /* nothing here yet */ +} + +#endif /* __ASM_ARCH_SYSTEM_H */ diff --git a/arch/arm/mach-s5p6440/include/mach/uncompress.h b/arch/arm/mach-s5p6440/include/mach/uncompress.h new file mode 100644 index 0000000..7c1f600 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/uncompress.h @@ -0,0 +1,24 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/uncompress.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - uncompress code + * + * 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 __ASM_ARCH_UNCOMPRESS_H +#define __ASM_ARCH_UNCOMPRESS_H + +#include +#include + +static void arch_detect_cpu(void) +{ + /* we do not need to do any cpu detection here at the moment. */ +} + +#endif /* __ASM_ARCH_UNCOMPRESS_H */ diff --git a/arch/arm/plat-s3c/include/plat/cpu.h b/arch/arm/plat-s3c/include/plat/cpu.h index d1131ca..676db94 100644 --- a/arch/arm/plat-s3c/include/plat/cpu.h +++ b/arch/arm/plat-s3c/include/plat/cpu.h @@ -48,9 +48,12 @@ extern void s3c_init_cpu(unsigned long idcode, extern void s3c24xx_init_irq(void); extern void s3c64xx_init_irq(u32 vic0, u32 vic1); +extern void s5p_init_irq(u32 *vic, u32 num_vic); extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); extern void s3c64xx_init_io(struct map_desc *mach_desc, int size); +extern void s5p_init_io(struct map_desc *mach_desc, + int size, void __iomem *cpuid_addr); extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no); diff --git a/arch/arm/plat-s5p/cpu.c b/arch/arm/plat-s5p/cpu.c new file mode 100644 index 0000000..0895a77 --- /dev/null +++ b/arch/arm/plat-s5p/cpu.c @@ -0,0 +1,90 @@ +/* linux/arch/arm/plat-s5p/cpu.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P CPU 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* table of supported CPUs */ + +static const char name_s5p6440[] = "S5P6440"; + +static struct cpu_table cpu_ids[] __initdata = { + { + .idcode = 0x56440100, + .idmask = 0xffffff00, + .map_io = s5p6440_map_io, + .init_clocks = s5p6440_init_clocks, + .init_uarts = s5p6440_init_uarts, + .init = s5p6440_init, + .name = name_s5p6440, + }, +}; + +/* minimal IO mapping */ + +#define UART_OFFS (S5P_PA_UART & 0xfffff) + +static struct map_desc s5p_iodesc[] __initdata = { + { + .virtual = (unsigned long)S5P_VA_SYSCON, + .pfn = __phys_to_pfn(S5P_PA_SYSCON), + .length = SZ_64K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)(S5P_VA_UART + UART_OFFS), + .pfn = __phys_to_pfn(S5P_PA_UART), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_VIC0, + .pfn = __phys_to_pfn(S5P_PA_VIC0), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_VIC1, + .pfn = __phys_to_pfn(S5P_PA_VIC1), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_TIMER, + .pfn = __phys_to_pfn(S5P_PA_TIMER), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_GPIO, + .pfn = __phys_to_pfn(S5P_PA_GPIO), + .length = SZ_4K, + .type = MT_DEVICE, + }, +}; + +/* read cpu identification code */ + +void __init s5p_init_io(struct map_desc *mach_desc, + int size, void __iomem *cpuid_addr) +{ + unsigned long idcode; + + /* initialize the io descriptors we need for initialization */ + iotable_init(s5p_iodesc, ARRAY_SIZE(s5p_iodesc)); + if (mach_desc) + iotable_init(mach_desc, size); + + idcode = __raw_readl(cpuid_addr); + s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); +} diff --git a/arch/arm/plat-s5p/include/plat/s5p6440.h b/arch/arm/plat-s5p/include/plat/s5p6440.h new file mode 100644 index 0000000..a4cd75a --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/s5p6440.h @@ -0,0 +1,37 @@ +/* arch/arm/plat-s5p/include/plat/s5p6440.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Header file for s5p6440 cpu 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. +*/ + + /* Common init code for S5P6440 related SoCs */ + +extern void s5p6440_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); +extern void s5p6440_register_clocks(void); +extern void s5p6440_setup_clocks(void); + +#ifdef CONFIG_CPU_S5P6440 + +extern int s5p6440_init(void); +extern void s5p6440_init_irq(void); +extern void s5p6440_map_io(void); +extern void s5p6440_init_clocks(int xtal); + +#define s5p6440_init_uarts s5p6440_common_init_uarts + +#else +#define s5p6440_init_clocks NULL +#define s5p6440_init_uarts NULL +#define s5p6440_map_io NULL +#define s5p6440_init NULL +#endif + +/* S5P6440 timer */ + +extern struct sys_timer s5p6440_timer; diff --git a/arch/arm/plat-s5p/s5p6440-init.c b/arch/arm/plat-s5p/s5p6440-init.c new file mode 100644 index 0000000..9017825 --- /dev/null +++ b/arch/arm/plat-s5p/s5p6440-init.c @@ -0,0 +1,50 @@ +/* linux/arch/arm/plat-s5p/s5p6440-init.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct s3c24xx_uart_clksrc s5p6440_serial_clocks[] = { + [0] = { + .name = "pclk_low", + .divisor = 1, + .min_baud = 0, + .max_baud = 0, + }, + [1] = { + .name = "uclk1", + .divisor = 1, + .min_baud = 0, + .max_baud = 0, + }, +}; + +/* uart registration process */ +void __init s5p6440_common_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + struct s3c2410_uartcfg *tcfg = cfg; + u32 ucnt; + + for (ucnt = 0; ucnt < no; ucnt++, tcfg++) { + if (!tcfg->clocks) { + tcfg->clocks = s5p6440_serial_clocks; + tcfg->clocks_size = ARRAY_SIZE(s5p6440_serial_clocks); + } + } + + s3c24xx_init_uartdevs("s3c6400-uart", s5p_uart_resources, cfg, no); +} diff --git a/arch/arm/plat-s5p/setup-i2c0.c b/arch/arm/plat-s5p/setup-i2c0.c new file mode 100644 index 0000000..67a66e0 --- /dev/null +++ b/arch/arm/plat-s5p/setup-i2c0.c @@ -0,0 +1,25 @@ +/* linux/arch/arm/plat-s5p/setup-i2c0.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * I2C0 GPIO configuration. + * + * Based on plat-s3c64xx/setup-i2c0.c + * + * 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 + +struct platform_device; /* don't need the contents */ + +#include + +void s3c_i2c0_cfg_gpio(struct platform_device *dev) +{ + /* Will be populated later */ +} -- cgit v0.10.2 From 1a0e8a52ad56075663d8e120e2468fc96fb6fa6c Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 08:13:37 +0900 Subject: ARM: S5P6440: Add Clock and PLL support This patch adds clock and pll support for S5P6440. This patch are based on Harald Welte's patches and Ben's plat-samsung. Signed-off-by: Thomas Abraham Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5p6440/include/mach/regs-clock.h b/arch/arm/mach-s5p6440/include/mach/regs-clock.h new file mode 100644 index 0000000..b7af283 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/regs-clock.h @@ -0,0 +1,130 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/regs-clock.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - Clock register definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_REGS_CLOCK_H +#define __ASM_ARCH_REGS_CLOCK_H __FILE__ + +#include + +#define S5P_CLKREG(x) (S5P_VA_CLK + (x)) + +#define S5P_APLL_LOCK S5P_CLKREG(0x00) +#define S5P_MPLL_LOCK S5P_CLKREG(0x04) +#define S5P_EPLL_LOCK S5P_CLKREG(0x08) +#define S5P_APLL_CON S5P_CLKREG(0x0C) +#define S5P_MPLL_CON S5P_CLKREG(0x10) +#define S5P_EPLL_CON S5P_CLKREG(0x14) +#define S5P_EPLL_CON_K S5P_CLKREG(0x18) +#define S5P_CLK_SRC0 S5P_CLKREG(0x1C) +#define S5P_CLK_DIV0 S5P_CLKREG(0x20) +#define S5P_CLK_DIV1 S5P_CLKREG(0x24) +#define S5P_CLK_DIV2 S5P_CLKREG(0x28) +#define S5P_CLK_OUT S5P_CLKREG(0x2C) +#define S5P_CLK_GATE_HCLK0 S5P_CLKREG(0x30) +#define S5P_CLK_GATE_PCLK S5P_CLKREG(0x34) +#define S5P_CLK_GATE_SCLK0 S5P_CLKREG(0x38) +#define S5P_CLK_GATE_MEM0 S5P_CLKREG(0x3C) +#define S5P_CLK_DIV3 S5P_CLKREG(0x40) +#define S5P_CLK_GATE_HCLK1 S5P_CLKREG(0x44) +#define S5P_CLK_GATE_SCLK1 S5P_CLKREG(0x48) +#define S5P_AHB_CON0 S5P_CLKREG(0x100) +#define S5P_CLK_SRC1 S5P_CLKREG(0x10C) +#define S5P_SWRESET S5P_CLKREG(0x114) +#define S5P_SYS_ID S5P_CLKREG(0x118) +#define S5P_SYS_OTHERS S5P_CLKREG(0x11C) +#define S5P_MEM_CFG_STAT S5P_CLKREG(0x12C) +#define S5P_PWR_CFG S5P_CLKREG(0x804) +#define S5P_EINT_WAKEUP_MASK S5P_CLKREG(0x808) +#define S5P_NORMAL_CFG S5P_CLKREG(0x810) +#define S5P_STOP_CFG S5P_CLKREG(0x814) +#define S5P_SLEEP_CFG S5P_CLKREG(0x818) +#define S5P_OSC_FREQ S5P_CLKREG(0x820) +#define S5P_OSC_STABLE S5P_CLKREG(0x824) +#define S5P_PWR_STABLE S5P_CLKREG(0x828) +#define S5P_MTC_STABLE S5P_CLKREG(0x830) +#define S5P_OTHERS S5P_CLKREG(0x900) +#define S5P_RST_STAT S5P_CLKREG(0x904) +#define S5P_WAKEUP_STAT S5P_CLKREG(0x908) +#define S5P_SLPEN S5P_CLKREG(0x930) +#define S5P_INFORM0 S5P_CLKREG(0xA00) +#define S5P_INFORM1 S5P_CLKREG(0xA04) +#define S5P_INFORM2 S5P_CLKREG(0xA08) +#define S5P_INFORM3 S5P_CLKREG(0xA0C) + +/* CLKDIV0 */ +#define S5P_CLKDIV0_PCLK_MASK (0xf << 12) +#define S5P_CLKDIV0_PCLK_SHIFT (12) +#define S5P_CLKDIV0_HCLK_MASK (0xf << 8) +#define S5P_CLKDIV0_HCLK_SHIFT (8) +#define S5P_CLKDIV0_MPLL_MASK (0x1 << 4) +#define S5P_CLKDIV0_ARM_MASK (0xf << 0) +#define S5P_CLKDIV0_ARM_SHIFT (0) + +/* CLKDIV3 */ +#define S5P_CLKDIV3_PCLK_LOW_MASK (0xf << 12) +#define S5P_CLKDIV3_PCLK_LOW_SHIFT (12) +#define S5P_CLKDIV3_HCLK_LOW_MASK (0xf << 8) +#define S5P_CLKDIV3_HCLK_LOW_SHIFT (8) + +/* HCLK0 GATE Registers */ +#define S5P_CLKCON_HCLK0_USB (1<<20) +#define S5P_CLKCON_HCLK0_HSMMC2 (1<<19) +#define S5P_CLKCON_HCLK0_HSMMC1 (1<<18) +#define S5P_CLKCON_HCLK0_HSMMC0 (1<<17) +#define S5P_CLKCON_HCLK0_POST0 (1<<5) + +/* HCLK1 GATE Registers */ +#define S5P_CLKCON_HCLK1_DISPCON (1<<1) + +/* PCLK GATE Registers */ +#define S5P_CLKCON_PCLK_IIS2 (1<<26) +#define S5P_CLKCON_PCLK_SPI1 (1<<22) +#define S5P_CLKCON_PCLK_SPI0 (1<<21) +#define S5P_CLKCON_PCLK_GPIO (1<<18) +#define S5P_CLKCON_PCLK_IIC0 (1<<17) +#define S5P_CLKCON_PCLK_TSADC (1<<12) +#define S5P_CLKCON_PCLK_PWM (1<<7) +#define S5P_CLKCON_PCLK_RTC (1<<6) +#define S5P_CLKCON_PCLK_WDT (1<<5) +#define S5P_CLKCON_PCLK_UART3 (1<<4) +#define S5P_CLKCON_PCLK_UART2 (1<<3) +#define S5P_CLKCON_PCLK_UART1 (1<<2) +#define S5P_CLKCON_PCLK_UART0 (1<<1) + +/* SCLK0 GATE Registers */ +#define S5P_CLKCON_SCLK0_MMC2_48 (1<<29) +#define S5P_CLKCON_SCLK0_MMC1_48 (1<<28) +#define S5P_CLKCON_SCLK0_MMC0_48 (1<<27) +#define S5P_CLKCON_SCLK0_MMC2 (1<<26) +#define S5P_CLKCON_SCLK0_MMC1 (1<<25) +#define S5P_CLKCON_SCLK0_MMC0 (1<<24) +#define S5P_CLKCON_SCLK0_SPI1_48 (1<<23) +#define S5P_CLKCON_SCLK0_SPI0_48 (1<<22) +#define S5P_CLKCON_SCLK0_SPI1 (1<<21) +#define S5P_CLKCON_SCLK0_SPI0 (1<<20) +#define S5P_CLKCON_SCLK0_UART (1<<5) + +/* SCLK1 GATE Registers */ + +/* MEM0 GATE Registers */ +#define S5P_CLKCON_MEM0_HCLK_NFCON (1<<2) + +/*OTHERS Resgister */ +#define S5P_OTHERS_USB_SIG_MASK (1<<16) +#define S5P_OTHERS_HCLK_LOW_SEL_MPLL (1<<6) + +/* Compatibility defines */ +#define ARM_CLK_DIV S5P_CLK_DIV0 +#define ARM_DIV_RATIO_SHIFT 0 +#define ARM_DIV_MASK (0xf << ARM_DIV_RATIO_SHIFT) + +#endif /* __ASM_ARCH_REGS_CLOCK_H */ diff --git a/arch/arm/plat-s5p/clock.c b/arch/arm/plat-s5p/clock.c new file mode 100644 index 0000000..3d3c0f1 --- /dev/null +++ b/arch/arm/plat-s5p/clock.c @@ -0,0 +1,136 @@ +/* linux/arch/arm/plat-s5p/clock.c + * + * Copyright 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P - Common clock 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* fin_apll, fin_mpll and fin_epll are all the same clock, which we call + * clk_ext_xtal_mux. +*/ +struct clk clk_ext_xtal_mux = { + .name = "ext_xtal", + .id = -1, +}; + +/* 48MHz USB Phy clock output */ +struct clk clk_48m = { + .name = "clk_48m", + .id = -1, + .rate = 48000000, +}; + +/* APLL clock output + * No need .ctrlbit, this is always on +*/ +struct clk clk_fout_apll = { + .name = "fout_apll", + .id = -1, +}; + +/* MPLL clock output + * No need .ctrlbit, this is always on +*/ +struct clk clk_fout_mpll = { + .name = "fout_mpll", + .id = -1, +}; + +/* EPLL clock output */ +struct clk clk_fout_epll = { + .name = "fout_epll", + .id = -1, + .ctrlbit = (1 << 31), +}; + +/* ARM clock */ +struct clk clk_arm = { + .name = "armclk", + .id = -1, + .rate = 0, + .ctrlbit = 0, +}; + +/* Possible clock sources for APLL Mux */ +static struct clk *clk_src_apll_list[] = { + [0] = &clk_fin_apll, + [1] = &clk_fout_apll, +}; + +struct clksrc_sources clk_src_apll = { + .sources = clk_src_apll_list, + .nr_sources = ARRAY_SIZE(clk_src_apll_list), +}; + +/* Possible clock sources for MPLL Mux */ +static struct clk *clk_src_mpll_list[] = { + [0] = &clk_fin_mpll, + [1] = &clk_fout_mpll, +}; + +struct clksrc_sources clk_src_mpll = { + .sources = clk_src_mpll_list, + .nr_sources = ARRAY_SIZE(clk_src_mpll_list), +}; + +/* Possible clock sources for EPLL Mux */ +static struct clk *clk_src_epll_list[] = { + [0] = &clk_fin_epll, + [1] = &clk_fout_epll, +}; + +struct clksrc_sources clk_src_epll = { + .sources = clk_src_epll_list, + .nr_sources = ARRAY_SIZE(clk_src_epll_list), +}; + +int s5p_gatectrl(void __iomem *reg, struct clk *clk, int enable) +{ + unsigned int ctrlbit = clk->ctrlbit; + u32 con; + + con = __raw_readl(reg); + con = enable ? (con | ctrlbit) : (con & ~ctrlbit); + __raw_writel(con, reg); + return 0; +} + +static struct clk *s5p_clks[] __initdata = { + &clk_ext_xtal_mux, + &clk_48m, + &clk_fout_apll, + &clk_fout_mpll, + &clk_fout_epll, + &clk_arm, +}; + +void __init s5p_register_clocks(unsigned long xtal_freq) +{ + int ret; + + clk_ext_xtal_mux.rate = xtal_freq; + + ret = s3c24xx_register_clocks(s5p_clks, ARRAY_SIZE(s5p_clks)); + if (ret > 0) + printk(KERN_ERR "Failed to register s5p clocks\n"); +} diff --git a/arch/arm/plat-s5p/include/plat/pll.h b/arch/arm/plat-s5p/include/plat/pll.h new file mode 100644 index 0000000..d48325b --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/pll.h @@ -0,0 +1,83 @@ +/* arch/arm/plat-s5p/include/plat/pll.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P PLL code + * + * Based on arch/arm/plat-s3c64xx/include/plat/pll.h + * + * 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. +*/ + +#define PLL45XX_MDIV_MASK (0x3FF) +#define PLL45XX_PDIV_MASK (0x3F) +#define PLL45XX_SDIV_MASK (0x7) +#define PLL45XX_MDIV_SHIFT (16) +#define PLL45XX_PDIV_SHIFT (8) +#define PLL45XX_SDIV_SHIFT (0) + +#include + +enum pll45xx_type_t { + pll_4500, + pll_4502, + pll_4508 +}; + +static inline unsigned long s5p_get_pll45xx(unsigned long baseclk, u32 pll_con, + enum pll45xx_type_t pll_type) +{ + u32 mdiv, pdiv, sdiv; + u64 fvco = baseclk; + + mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK; + pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK; + sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK; + + if (pll_type == pll_4508) + sdiv = sdiv - 1; + + fvco *= mdiv; + do_div(fvco, (pdiv << sdiv)); + + return (unsigned long)fvco; +} + +#define PLL90XX_MDIV_MASK (0xFF) +#define PLL90XX_PDIV_MASK (0x3F) +#define PLL90XX_SDIV_MASK (0x7) +#define PLL90XX_KDIV_MASK (0xffff) +#define PLL90XX_MDIV_SHIFT (16) +#define PLL90XX_PDIV_SHIFT (8) +#define PLL90XX_SDIV_SHIFT (0) +#define PLL90XX_KDIV_SHIFT (0) + +static inline unsigned long s5p_get_pll90xx(unsigned long baseclk, + u32 pll_con, u32 pll_conk) +{ + unsigned long result; + u32 mdiv, pdiv, sdiv, kdiv; + u64 tmp; + + mdiv = (pll_con >> PLL90XX_MDIV_SHIFT) & PLL90XX_MDIV_MASK; + pdiv = (pll_con >> PLL90XX_PDIV_SHIFT) & PLL90XX_PDIV_MASK; + sdiv = (pll_con >> PLL90XX_SDIV_SHIFT) & PLL90XX_SDIV_MASK; + kdiv = pll_conk & PLL90XX_KDIV_MASK; + + /* We need to multiple baseclk by mdiv (the integer part) and kdiv + * which is in 2^16ths, so shift mdiv up (does not overflow) and + * add kdiv before multiplying. The use of tmp is to avoid any + * overflows before shifting bac down into result when multipling + * by the mdiv and kdiv pair. + */ + + tmp = baseclk; + tmp *= (mdiv << 16) + kdiv; + do_div(tmp, (pdiv << sdiv)); + result = tmp >> 16; + + return result; +} diff --git a/arch/arm/plat-s5p/include/plat/s5p-clock.h b/arch/arm/plat-s5p/include/plat/s5p-clock.h new file mode 100644 index 0000000..e1a7444 --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/s5p-clock.h @@ -0,0 +1,38 @@ +/* linux/arch/arm/plat-s5p/include/plat/s5p-clock.h + * + * Copyright 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Header file for s5p clock 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 __ASM_PLAT_S5P_CLOCK_H +#define __ASM_PLAT_S5P_CLOCK_H __FILE__ + +#include + +#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1) + +#define clk_fin_apll clk_ext_xtal_mux +#define clk_fin_mpll clk_ext_xtal_mux +#define clk_fin_epll clk_ext_xtal_mux + +extern struct clk clk_ext_xtal_mux; +extern struct clk clk_48m; +extern struct clk clk_fout_apll; +extern struct clk clk_fout_mpll; +extern struct clk clk_fout_epll; +extern struct clk clk_arm; + +extern struct clksrc_sources clk_src_apll; +extern struct clksrc_sources clk_src_mpll; +extern struct clksrc_sources clk_src_epll; + +extern int s5p6440_clk48m_ctrl(struct clk *clk, int enable); +extern int s5p_gatectrl(void __iomem *reg, struct clk *clk, int enable); + +#endif /* __ASM_PLAT_S5P_CLOCK_H */ diff --git a/arch/arm/plat-s5p/s5p6440-clock.c b/arch/arm/plat-s5p/s5p6440-clock.c new file mode 100644 index 0000000..2f412f8 --- /dev/null +++ b/arch/arm/plat-s5p/s5p6440-clock.c @@ -0,0 +1,698 @@ +/* linux/arch/arm/plat-s5p/s5p6440-clock.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - Clock 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* APLL Mux output clock */ +static struct clksrc_clk clk_mout_apll = { + .clk = { + .name = "mout_apll", + .id = -1, + }, + .sources = &clk_src_apll, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 0, .size = 1 }, +}; + +static int s5p6440_epll_enable(struct clk *clk, int enable) +{ + unsigned int ctrlbit = clk->ctrlbit; + unsigned int epll_con = __raw_readl(S5P_EPLL_CON) & ~ctrlbit; + + if (enable) + __raw_writel(epll_con | ctrlbit, S5P_EPLL_CON); + else + __raw_writel(epll_con, S5P_EPLL_CON); + + return 0; +} + +static unsigned long s5p6440_epll_get_rate(struct clk *clk) +{ + return clk->rate; +} + +static u32 epll_div[][5] = { + { 36000000, 0, 48, 1, 4 }, + { 48000000, 0, 32, 1, 3 }, + { 60000000, 0, 40, 1, 3 }, + { 72000000, 0, 48, 1, 3 }, + { 84000000, 0, 28, 1, 2 }, + { 96000000, 0, 32, 1, 2 }, + { 32768000, 45264, 43, 1, 4 }, + { 45158000, 6903, 30, 1, 3 }, + { 49152000, 50332, 32, 1, 3 }, + { 67738000, 10398, 45, 1, 3 }, + { 73728000, 9961, 49, 1, 3 } +}; + +static int s5p6440_epll_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned int epll_con, epll_con_k; + unsigned int i; + + if (clk->rate == rate) /* Return if nothing changed */ + return 0; + + epll_con = __raw_readl(S5P_EPLL_CON); + epll_con_k = __raw_readl(S5P_EPLL_CON_K); + + epll_con_k &= ~(PLL90XX_KDIV_MASK); + epll_con &= ~(PLL90XX_MDIV_MASK | PLL90XX_PDIV_MASK | PLL90XX_SDIV_MASK); + + for (i = 0; i < ARRAY_SIZE(epll_div); i++) { + if (epll_div[i][0] == rate) { + epll_con_k |= (epll_div[i][1] << PLL90XX_KDIV_SHIFT); + epll_con |= (epll_div[i][2] << PLL90XX_MDIV_SHIFT) | + (epll_div[i][3] << PLL90XX_PDIV_SHIFT) | + (epll_div[i][4] << PLL90XX_SDIV_SHIFT); + break; + } + } + + if (i == ARRAY_SIZE(epll_div)) { + printk(KERN_ERR "%s: Invalid Clock EPLL Frequency\n", __func__); + return -EINVAL; + } + + __raw_writel(epll_con, S5P_EPLL_CON); + __raw_writel(epll_con_k, S5P_EPLL_CON_K); + + clk->rate = rate; + + return 0; +} + +static struct clk_ops s5p6440_epll_ops = { + .get_rate = s5p6440_epll_get_rate, + .set_rate = s5p6440_epll_set_rate, +}; + +static struct clksrc_clk clk_mout_epll = { + .clk = { + .name = "mout_epll", + .id = -1, + }, + .sources = &clk_src_epll, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 2, .size = 1 }, +}; + +static struct clksrc_clk clk_mout_mpll = { + .clk = { + .name = "mout_mpll", + .id = -1, + }, + .sources = &clk_src_mpll, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 1, .size = 1 }, +}; + +static struct clk clk_h_low = { + .name = "hclk_low", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, + .ops = &clk_ops_def_setrate, +}; + +static struct clk clk_p_low = { + .name = "pclk_low", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, + .ops = &clk_ops_def_setrate, +}; + +enum perf_level { + L0 = 532*1000, + L1 = 266*1000, + L2 = 133*1000, +}; + +static const u32 clock_table[][3] = { + /*{ARM_CLK, DIVarm, DIVhclk}*/ + {L0 * 1000, (0 << ARM_DIV_RATIO_SHIFT), (3 << S5P_CLKDIV0_HCLK_SHIFT)}, + {L1 * 1000, (1 << ARM_DIV_RATIO_SHIFT), (1 << S5P_CLKDIV0_HCLK_SHIFT)}, + {L2 * 1000, (3 << ARM_DIV_RATIO_SHIFT), (0 << S5P_CLKDIV0_HCLK_SHIFT)}, +}; + +static unsigned long s5p6440_armclk_get_rate(struct clk *clk) +{ + unsigned long rate = clk_get_rate(clk->parent); + u32 clkdiv; + + /* divisor mask starts at bit0, so no need to shift */ + clkdiv = __raw_readl(ARM_CLK_DIV) & ARM_DIV_MASK; + + return rate / (clkdiv + 1); +} + +static unsigned long s5p6440_armclk_round_rate(struct clk *clk, + unsigned long rate) +{ + u32 iter; + + for (iter = 1 ; iter < ARRAY_SIZE(clock_table) ; iter++) { + if (rate > clock_table[iter][0]) + return clock_table[iter-1][0]; + } + + return clock_table[ARRAY_SIZE(clock_table) - 1][0]; +} + +static int s5p6440_armclk_set_rate(struct clk *clk, unsigned long rate) +{ + u32 round_tmp; + u32 iter; + u32 clk_div0_tmp; + u32 cur_rate = clk->ops->get_rate(clk); + unsigned long flags; + + round_tmp = clk->ops->round_rate(clk, rate); + if (round_tmp == cur_rate) + return 0; + + + for (iter = 0 ; iter < ARRAY_SIZE(clock_table) ; iter++) { + if (round_tmp == clock_table[iter][0]) + break; + } + + if (iter >= ARRAY_SIZE(clock_table)) + iter = ARRAY_SIZE(clock_table) - 1; + + local_irq_save(flags); + if (cur_rate > round_tmp) { + /* Frequency Down */ + clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & ~(ARM_DIV_MASK); + clk_div0_tmp |= clock_table[iter][1]; + __raw_writel(clk_div0_tmp, ARM_CLK_DIV); + + clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & + ~(S5P_CLKDIV0_HCLK_MASK); + clk_div0_tmp |= clock_table[iter][2]; + __raw_writel(clk_div0_tmp, ARM_CLK_DIV); + + + } else { + /* Frequency Up */ + clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & + ~(S5P_CLKDIV0_HCLK_MASK); + clk_div0_tmp |= clock_table[iter][2]; + __raw_writel(clk_div0_tmp, ARM_CLK_DIV); + + clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & ~(ARM_DIV_MASK); + clk_div0_tmp |= clock_table[iter][1]; + __raw_writel(clk_div0_tmp, ARM_CLK_DIV); + } + local_irq_restore(flags); + + clk->rate = clock_table[iter][0]; + + return 0; +} + +static struct clk_ops s5p6440_clkarm_ops = { + .get_rate = s5p6440_armclk_get_rate, + .set_rate = s5p6440_armclk_set_rate, + .round_rate = s5p6440_armclk_round_rate, +}; + +static unsigned long s5p6440_clk_doutmpll_get_rate(struct clk *clk) +{ + unsigned long rate = clk_get_rate(clk->parent); + + if (__raw_readl(S5P_CLK_DIV0) & S5P_CLKDIV0_MPLL_MASK) + rate /= 2; + + return rate; +} + +static struct clk clk_dout_mpll = { + .name = "dout_mpll", + .id = -1, + .parent = &clk_mout_mpll.clk, + .ops = &(struct clk_ops) { + .get_rate = s5p6440_clk_doutmpll_get_rate, + }, +}; + +int s5p6440_clk48m_ctrl(struct clk *clk, int enable) +{ + unsigned long flags; + u32 val; + + /* can't rely on clock lock, this register has other usages */ + local_irq_save(flags); + + val = __raw_readl(S5P_OTHERS); + if (enable) + val |= S5P_OTHERS_USB_SIG_MASK; + else + val &= ~S5P_OTHERS_USB_SIG_MASK; + + __raw_writel(val, S5P_OTHERS); + + local_irq_restore(flags); + + return 0; +} + +static int s5p6440_pclk_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(S5P_CLK_GATE_PCLK, clk, enable); +} + +static int s5p6440_hclk0_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(S5P_CLK_GATE_HCLK0, clk, enable); +} + +static int s5p6440_hclk1_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(S5P_CLK_GATE_HCLK1, clk, enable); +} + +static int s5p6440_sclk_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(S5P_CLK_GATE_SCLK0, clk, enable); +} + +static int s5p6440_mem_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(S5P_CLK_GATE_MEM0, clk, enable); +} + +/* + * The following clocks will be disabled during clock initialization. It is + * recommended to keep the following clocks disabled until the driver requests + * for enabling the clock. + */ +static struct clk init_clocks_disable[] = { + { + .name = "nand", + .id = -1, + .parent = &clk_h, + .enable = s5p6440_mem_ctrl, + .ctrlbit = S5P_CLKCON_MEM0_HCLK_NFCON, + }, { + .name = "adc", + .id = -1, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_TSADC, + }, { + .name = "i2c", + .id = -1, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_IIC0, + }, { + .name = "i2s_v40", + .id = 0, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_IIS2, + }, { + .name = "spi", + .id = 0, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_SPI0, + }, { + .name = "spi", + .id = 1, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_SPI1, + }, { + .name = "sclk_spi_48", + .id = 0, + .parent = &clk_48m, + .enable = s5p6440_sclk_ctrl, + .ctrlbit = S5P_CLKCON_SCLK0_SPI0_48, + }, { + .name = "sclk_spi_48", + .id = 1, + .parent = &clk_48m, + .enable = s5p6440_sclk_ctrl, + .ctrlbit = S5P_CLKCON_SCLK0_SPI1_48, + }, { + .name = "mmc_48m", + .id = 0, + .parent = &clk_48m, + .enable = s5p6440_sclk_ctrl, + .ctrlbit = S5P_CLKCON_SCLK0_MMC0_48, + }, { + .name = "mmc_48m", + .id = 1, + .parent = &clk_48m, + .enable = s5p6440_sclk_ctrl, + .ctrlbit = S5P_CLKCON_SCLK0_MMC1_48, + }, { + .name = "mmc_48m", + .id = 2, + .parent = &clk_48m, + .enable = s5p6440_sclk_ctrl, + .ctrlbit = S5P_CLKCON_SCLK0_MMC2_48, + }, { + .name = "otg", + .id = -1, + .parent = &clk_h_low, + .enable = s5p6440_hclk0_ctrl, + .ctrlbit = S5P_CLKCON_HCLK0_USB + }, { + .name = "post", + .id = -1, + .parent = &clk_h_low, + .enable = s5p6440_hclk0_ctrl, + .ctrlbit = S5P_CLKCON_HCLK0_POST0 + }, { + .name = "lcd", + .id = -1, + .parent = &clk_h_low, + .enable = s5p6440_hclk1_ctrl, + .ctrlbit = S5P_CLKCON_HCLK1_DISPCON, + }, { + .name = "hsmmc", + .id = 0, + .parent = &clk_h_low, + .enable = s5p6440_hclk0_ctrl, + .ctrlbit = S5P_CLKCON_HCLK0_HSMMC0, + }, { + .name = "hsmmc", + .id = 1, + .parent = &clk_h_low, + .enable = s5p6440_hclk0_ctrl, + .ctrlbit = S5P_CLKCON_HCLK0_HSMMC1, + }, { + .name = "hsmmc", + .id = 2, + .parent = &clk_h_low, + .enable = s5p6440_hclk0_ctrl, + .ctrlbit = S5P_CLKCON_HCLK0_HSMMC2, + }, { + .name = "rtc", + .id = -1, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_RTC, + }, { + .name = "watchdog", + .id = -1, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_WDT, + }, { + .name = "timers", + .id = -1, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_PWM, + } +}; + +/* + * The following clocks will be enabled during clock initialization. + */ +static struct clk init_clocks[] = { + { + .name = "gpio", + .id = -1, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_GPIO, + }, { + .name = "uart", + .id = 0, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_UART0, + }, { + .name = "uart", + .id = 1, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_UART1, + }, { + .name = "uart", + .id = 2, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_UART2, + }, { + .name = "uart", + .id = 3, + .parent = &clk_p_low, + .enable = s5p6440_pclk_ctrl, + .ctrlbit = S5P_CLKCON_PCLK_UART3, + } +}; + +static struct clk clk_iis_cd_v40 = { + .name = "iis_cdclk_v40", + .id = -1, +}; + +static struct clk clk_pcm_cd = { + .name = "pcm_cdclk", + .id = -1, +}; + +static struct clk *clkset_spi_mmc_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll, + &clk_fin_epll, +}; + +static struct clksrc_sources clkset_spi_mmc = { + .sources = clkset_spi_mmc_list, + .nr_sources = ARRAY_SIZE(clkset_spi_mmc_list), +}; + +static struct clk *clkset_uart_list[] = { + &clk_mout_epll.clk, + &clk_dout_mpll +}; + +static struct clksrc_sources clkset_uart = { + .sources = clkset_uart_list, + .nr_sources = ARRAY_SIZE(clkset_uart_list), +}; + +static struct clksrc_clk clksrcs[] = { + { + .clk = { + .name = "mmc_bus", + .id = 0, + .ctrlbit = S5P_CLKCON_SCLK0_MMC0, + .enable = s5p6440_sclk_ctrl, + }, + .sources = &clkset_spi_mmc, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 18, .size = 2 }, + .reg_div = { .reg = S5P_CLK_DIV1, .shift = 0, .size = 4 }, + }, { + .clk = { + .name = "mmc_bus", + .id = 1, + .ctrlbit = S5P_CLKCON_SCLK0_MMC1, + .enable = s5p6440_sclk_ctrl, + }, + .sources = &clkset_spi_mmc, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 20, .size = 2 }, + .reg_div = { .reg = S5P_CLK_DIV1, .shift = 4, .size = 4 }, + }, { + .clk = { + .name = "mmc_bus", + .id = 2, + .ctrlbit = S5P_CLKCON_SCLK0_MMC2, + .enable = s5p6440_sclk_ctrl, + }, + .sources = &clkset_spi_mmc, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 22, .size = 2 }, + .reg_div = { .reg = S5P_CLK_DIV1, .shift = 8, .size = 4 }, + }, { + .clk = { + .name = "uclk1", + .id = -1, + .ctrlbit = S5P_CLKCON_SCLK0_UART, + .enable = s5p6440_sclk_ctrl, + }, + .sources = &clkset_uart, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 13, .size = 1 }, + .reg_div = { .reg = S5P_CLK_DIV2, .shift = 16, .size = 4 }, + }, { + .clk = { + .name = "spi_epll", + .id = 0, + .ctrlbit = S5P_CLKCON_SCLK0_SPI0, + .enable = s5p6440_sclk_ctrl, + }, + .sources = &clkset_spi_mmc, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 14, .size = 2 }, + .reg_div = { .reg = S5P_CLK_DIV2, .shift = 0, .size = 4 }, + }, { + .clk = { + .name = "spi_epll", + .id = 1, + .ctrlbit = S5P_CLKCON_SCLK0_SPI1, + .enable = s5p6440_sclk_ctrl, + }, + .sources = &clkset_spi_mmc, + .reg_src = { .reg = S5P_CLK_SRC0, .shift = 16, .size = 2 }, + .reg_div = { .reg = S5P_CLK_DIV2, .shift = 4, .size = 4 }, + } +}; + +/* Clock initialisation code */ +static struct clksrc_clk *init_parents[] = { + &clk_mout_apll, + &clk_mout_epll, + &clk_mout_mpll, +}; + +void __init_or_cpufreq s5p6440_setup_clocks(void) +{ + struct clk *xtal_clk; + unsigned long xtal; + unsigned long fclk; + unsigned long hclk; + unsigned long hclk_low; + unsigned long pclk; + unsigned long pclk_low; + unsigned long epll; + unsigned long apll; + unsigned long mpll; + unsigned int ptr; + u32 clkdiv0; + u32 clkdiv3; + + /* Set S5P6440 functions for clk_fout_epll */ + clk_fout_epll.enable = s5p6440_epll_enable; + clk_fout_epll.ops = &s5p6440_epll_ops; + + /* Set S5P6440 functions for arm clock */ + clk_arm.parent = &clk_mout_apll.clk; + clk_arm.ops = &s5p6440_clkarm_ops; + clk_48m.enable = s5p6440_clk48m_ctrl; + + clkdiv0 = __raw_readl(S5P_CLK_DIV0); + clkdiv3 = __raw_readl(S5P_CLK_DIV3); + + xtal_clk = clk_get(NULL, "ext_xtal"); + BUG_ON(IS_ERR(xtal_clk)); + + xtal = clk_get_rate(xtal_clk); + clk_put(xtal_clk); + + epll = s5p_get_pll90xx(xtal, __raw_readl(S5P_EPLL_CON), + __raw_readl(S5P_EPLL_CON_K)); + mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON), pll_4502); + apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON), pll_4502); + + printk(KERN_INFO "S5P6440: PLL settings, A=%ld.%ldMHz, M=%ld.%ldMHz," \ + " E=%ld.%ldMHz\n", + print_mhz(apll), print_mhz(mpll), print_mhz(epll)); + + fclk = apll / GET_DIV(clkdiv0, S5P_CLKDIV0_ARM); + hclk = fclk / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK); + pclk = hclk / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK); + + if (__raw_readl(S5P_OTHERS) & S5P_OTHERS_HCLK_LOW_SEL_MPLL) { + /* Asynchronous mode */ + hclk_low = mpll / GET_DIV(clkdiv3, S5P_CLKDIV3_HCLK_LOW); + } else { + /* Synchronous mode */ + hclk_low = apll / GET_DIV(clkdiv3, S5P_CLKDIV3_HCLK_LOW); + } + + pclk_low = hclk_low / GET_DIV(clkdiv3, S5P_CLKDIV3_PCLK_LOW); + + printk(KERN_INFO "S5P6440: HCLK=%ld.%ldMHz, HCLK_LOW=%ld.%ldMHz," \ + " PCLK=%ld.%ldMHz, PCLK_LOW=%ld.%ldMHz\n", + print_mhz(hclk), print_mhz(hclk_low), + print_mhz(pclk), print_mhz(pclk_low)); + + clk_fout_mpll.rate = mpll; + clk_fout_epll.rate = epll; + clk_fout_apll.rate = apll; + + clk_f.rate = fclk; + clk_h.rate = hclk; + clk_p.rate = pclk; + clk_h_low.rate = hclk_low; + clk_p_low.rate = pclk_low; + + for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) + s3c_set_clksrc(init_parents[ptr], true); + + for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) + s3c_set_clksrc(&clksrcs[ptr], true); +} + +static struct clk *clks[] __initdata = { + &clk_ext, + &clk_mout_epll.clk, + &clk_mout_mpll.clk, + &clk_dout_mpll, + &clk_iis_cd_v40, + &clk_pcm_cd, + &clk_p_low, + &clk_h_low, +}; + +void __init s5p6440_register_clocks(void) +{ + struct clk *clkp; + int ret; + int ptr; + + ret = s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); + if (ret > 0) + printk(KERN_ERR "Failed to register %u clocks\n", ret); + + s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs)); + s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); + + clkp = init_clocks_disable; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + (clkp->enable)(clkp, 0); + } + + s3c_pwmclk_init(); +} diff --git a/arch/arm/plat-samsung/include/plat/clock.h b/arch/arm/plat-samsung/include/plat/clock.h index 22e0114..ba9a1cd 100644 --- a/arch/arm/plat-samsung/include/plat/clock.h +++ b/arch/arm/plat-samsung/include/plat/clock.h @@ -95,6 +95,7 @@ extern void s3c_register_clocks(struct clk *clk, int nr_clks); extern int s3c24xx_register_baseclocks(unsigned long xtal); extern void s3c64xx_register_clocks(void); +extern void s5p_register_clocks(unsigned long xtal_freq); extern void s3c24xx_setup_clocks(unsigned long fclk, unsigned long hclk, -- cgit v0.10.2 From b7db51be43bc8f9943324a00b80e5d034de0a733 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 08:14:40 +0900 Subject: ARM: S5P6440: Add IRQ support This patch adds IRQ support for S5P6440 CPU. Signed-off-by: Adityapratap Sharma Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5p6440/include/mach/irqs.h b/arch/arm/mach-s5p6440/include/mach/irqs.h new file mode 100644 index 0000000..a4b9b40 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/irqs.h @@ -0,0 +1,111 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/irqs.h + * + * Copyright 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - IRQ definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_S5P_IRQS_H +#define __ASM_ARCH_S5P_IRQS_H __FILE__ + +#include + +/* VIC0 */ + +#define IRQ_EINT0_3 S5P_IRQ_VIC0(0) +#define IRQ_EINT4_11 S5P_IRQ_VIC0(1) +#define IRQ_RTC_TIC S5P_IRQ_VIC0(2) +#define IRQ_IIC1 S5P_IRQ_VIC0(5) +#define IRQ_I2SV40 S5P_IRQ_VIC0(6) +#define IRQ_GPS S5P_IRQ_VIC0(7) +#define IRQ_POST0 S5P_IRQ_VIC0(9) +#define IRQ_2D S5P_IRQ_VIC0(11) +#define IRQ_TIMER0_VIC S5P_IRQ_VIC0(23) +#define IRQ_TIMER1_VIC S5P_IRQ_VIC0(24) +#define IRQ_TIMER2_VIC S5P_IRQ_VIC0(25) +#define IRQ_WDT S5P_IRQ_VIC0(26) +#define IRQ_TIMER3_VIC S5P_IRQ_VIC0(27) +#define IRQ_TIMER4_VIC S5P_IRQ_VIC0(28) +#define IRQ_DISPCON0 S5P_IRQ_VIC0(29) +#define IRQ_DISPCON1 S5P_IRQ_VIC0(30) +#define IRQ_DISPCON2 S5P_IRQ_VIC0(31) + +/* VIC1 */ + +#define IRQ_EINT12_15 S5P_IRQ_VIC1(0) +#define IRQ_PCM0 S5P_IRQ_VIC1(2) +#define IRQ_UART0 S5P_IRQ_VIC1(5) +#define IRQ_UART1 S5P_IRQ_VIC1(6) +#define IRQ_UART2 S5P_IRQ_VIC1(7) +#define IRQ_UART3 S5P_IRQ_VIC1(8) +#define IRQ_DMA0 S5P_IRQ_VIC1(9) +#define IRQ_NFC S5P_IRQ_VIC1(13) +#define IRQ_SPI0 S5P_IRQ_VIC1(16) +#define IRQ_SPI1 S5P_IRQ_VIC1(17) +#define IRQ_IIC S5P_IRQ_VIC1(18) +#define IRQ_DISPCON3 S5P_IRQ_VIC1(19) +#define IRQ_FIMGVG S5P_IRQ_VIC1(20) +#define IRQ_EINT_GROUPS S5P_IRQ_VIC1(21) +#define IRQ_PMUIRQ S5P_IRQ_VIC1(23) +#define IRQ_HSMMC0 S5P_IRQ_VIC1(24) +#define IRQ_HSMMC1 S5P_IRQ_VIC1(25) +#define IRQ_HSMMC2 IRQ_SPI1 /* shared with SPI1 */ +#define IRQ_OTG S5P_IRQ_VIC1(26) +#define IRQ_DSI S5P_IRQ_VIC1(27) +#define IRQ_RTC_ALARM S5P_IRQ_VIC1(28) +#define IRQ_TSI S5P_IRQ_VIC1(29) +#define IRQ_PENDN S5P_IRQ_VIC1(30) +#define IRQ_TC IRQ_PENDN +#define IRQ_ADC S5P_IRQ_VIC1(31) + +/* + * Since the IRQ_EINT(x) are a linear mapping on s5p6440 we just defined + * them as an IRQ_EINT(x) macro from S5P_IRQ_EINT_BASE which we place + * after the pair of VICs. + */ + +#define S5P_IRQ_EINT_BASE (S5P_IRQ_VIC1(31) + 6) + +#define S5P_EINT(x) ((x) + S5P_IRQ_EINT_BASE) +#define IRQ_EINT(x) S5P_EINT(x) + +/* + * Next the external interrupt groups. These are similar to the IRQ_EINT(x) + * that they are sourced from the GPIO pins but with a different scheme for + * priority and source indication. + * + * The IRQ_EINT(x) can be thought of as 'group 0' of the available GPIO + * interrupts, but for historical reasons they are kept apart from these + * next interrupts. + * + * Use IRQ_EINT_GROUP(group, offset) to get the number for use in the + * machine specific support files. + */ + +/* Actually, #6 and #7 are missing in the EINT_GROUP1 */ +#define IRQ_EINT_GROUP1_NR (15) +#define IRQ_EINT_GROUP2_NR (8) +#define IRQ_EINT_GROUP5_NR (7) +#define IRQ_EINT_GROUP6_NR (10) +/* Actually, #0, #1 and #2 are missing in the EINT_GROUP8 */ +#define IRQ_EINT_GROUP8_NR (11) + +#define IRQ_EINT_GROUP_BASE S5P_EINT(16) +#define IRQ_EINT_GROUP1_BASE (IRQ_EINT_GROUP_BASE + 0) +#define IRQ_EINT_GROUP2_BASE (IRQ_EINT_GROUP1_BASE + IRQ_EINT_GROUP1_NR) +#define IRQ_EINT_GROUP5_BASE (IRQ_EINT_GROUP2_BASE + IRQ_EINT_GROUP2_NR) +#define IRQ_EINT_GROUP6_BASE (IRQ_EINT_GROUP5_BASE + IRQ_EINT_GROUP5_NR) +#define IRQ_EINT_GROUP8_BASE (IRQ_EINT_GROUP6_BASE + IRQ_EINT_GROUP6_NR) + +#define IRQ_EINT_GROUP(grp, x) (IRQ_EINT_GROUP##grp##_BASE + (x)) + +/* Set the default NR_IRQS */ + +#define NR_IRQS (IRQ_EINT_GROUP8_BASE + IRQ_EINT_GROUP8_NR + 1) + +#endif /* __ASM_ARCH_S5P_IRQS_H */ diff --git a/arch/arm/mach-s5p6440/include/mach/regs-irq.h b/arch/arm/mach-s5p6440/include/mach/regs-irq.h new file mode 100644 index 0000000..a961f4b --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/regs-irq.h @@ -0,0 +1,19 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/regs-irq.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - IRQ register definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_REGS_IRQ_H +#define __ASM_ARCH_REGS_IRQ_H __FILE__ + +#include +#include + +#endif /* __ASM_ARCH_REGS_IRQ_H */ diff --git a/arch/arm/plat-s5p/include/plat/irqs.h b/arch/arm/plat-s5p/include/plat/irqs.h new file mode 100644 index 0000000..5d7937d --- /dev/null +++ b/arch/arm/plat-s5p/include/plat/irqs.h @@ -0,0 +1,83 @@ +/* linux/arch/arm/plat-s5p/include/plat/irqs.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P Common IRQ 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 __ASM_PLAT_S5P_IRQS_H +#define __ASM_PLAT_S5P_IRQS_H __FILE__ + +/* we keep the first set of CPU IRQs out of the range of + * the ISA space, so that the PC104 has them to itself + * and we don't end up having to do horrible things to the + * standard ISA drivers.... + * + * note, since we're using the VICs, our start must be a + * mulitple of 32 to allow the common code to work + */ + +#define S5P_IRQ_OFFSET (32) + +#define S5P_IRQ(x) ((x) + S5P_IRQ_OFFSET) + +#define S5P_VIC0_BASE S5P_IRQ(0) +#define S5P_VIC1_BASE S5P_IRQ(32) + +#define IRQ_VIC0_BASE S5P_VIC0_BASE +#define IRQ_VIC1_BASE S5P_VIC1_BASE + +/* UART interrupts, each UART has 4 intterupts per channel so + * use the space between the ISA and S3C main interrupts. Note, these + * are not in the same order as the S3C24XX series! */ + +#define IRQ_S5P_UART_BASE0 (16) +#define IRQ_S5P_UART_BASE1 (20) +#define IRQ_S5P_UART_BASE2 (24) +#define IRQ_S5P_UART_BASE3 (28) + +#define UART_IRQ_RXD (0) +#define UART_IRQ_ERR (1) +#define UART_IRQ_TXD (2) + +#define IRQ_S5P_UART_RX0 (IRQ_S5P_UART_BASE0 + UART_IRQ_RXD) +#define IRQ_S5P_UART_TX0 (IRQ_S5P_UART_BASE0 + UART_IRQ_TXD) +#define IRQ_S5P_UART_ERR0 (IRQ_S5P_UART_BASE0 + UART_IRQ_ERR) + +#define IRQ_S5P_UART_RX1 (IRQ_S5P_UART_BASE1 + UART_IRQ_RXD) +#define IRQ_S5P_UART_TX1 (IRQ_S5P_UART_BASE1 + UART_IRQ_TXD) +#define IRQ_S5P_UART_ERR1 (IRQ_S5P_UART_BASE1 + UART_IRQ_ERR) + +#define IRQ_S5P_UART_RX2 (IRQ_S5P_UART_BASE2 + UART_IRQ_RXD) +#define IRQ_S5P_UART_TX2 (IRQ_S5P_UART_BASE2 + UART_IRQ_TXD) +#define IRQ_S5P_UART_ERR2 (IRQ_S5P_UART_BASE2 + UART_IRQ_ERR) + +#define IRQ_S5P_UART_RX3 (IRQ_S5P_UART_BASE3 + UART_IRQ_RXD) +#define IRQ_S5P_UART_TX3 (IRQ_S5P_UART_BASE3 + UART_IRQ_TXD) +#define IRQ_S5P_UART_ERR3 (IRQ_S5P_UART_BASE3 + UART_IRQ_ERR) + +/* S3C compatibilty defines */ +#define IRQ_S3CUART_RX0 IRQ_S5P_UART_RX0 +#define IRQ_S3CUART_RX1 IRQ_S5P_UART_RX1 +#define IRQ_S3CUART_RX2 IRQ_S5P_UART_RX2 +#define IRQ_S3CUART_RX3 IRQ_S5P_UART_RX3 + +/* VIC based IRQs */ + +#define S5P_IRQ_VIC0(x) (S5P_VIC0_BASE + (x)) +#define S5P_IRQ_VIC1(x) (S5P_VIC1_BASE + (x)) + +#define S5P_TIMER_IRQ(x) S5P_IRQ(64 + (x)) + +#define IRQ_TIMER0 S5P_TIMER_IRQ(0) +#define IRQ_TIMER1 S5P_TIMER_IRQ(1) +#define IRQ_TIMER2 S5P_TIMER_IRQ(2) +#define IRQ_TIMER3 S5P_TIMER_IRQ(3) +#define IRQ_TIMER4 S5P_TIMER_IRQ(4) + +#endif /* __ASM_PLAT_S5P_IRQS_H */ diff --git a/arch/arm/plat-s5p/irq.c b/arch/arm/plat-s5p/irq.c new file mode 100644 index 0000000..eada40d --- /dev/null +++ b/arch/arm/plat-s5p/irq.c @@ -0,0 +1,73 @@ +/* arch/arm/plat-s5p/irq.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P - Interrupt handling + * + * 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 + +#define VIC_VAADDR(no) (S5P_VA_VIC0 + ((no)*0x10000)) +#define VIC_BASE(no) (S5P_VIC0_BASE + ((no)*32)) + +/* + * Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] + * are consecutive when looking up the interrupt in the demux routines. + */ +static struct s3c_uart_irq uart_irqs[] = { + [0] = { + .regs = S5P_VA_UART0, + .base_irq = IRQ_S5P_UART_BASE0, + .parent_irq = IRQ_UART0, + }, + [1] = { + .regs = S5P_VA_UART1, + .base_irq = IRQ_S5P_UART_BASE1, + .parent_irq = IRQ_UART1, + }, + [2] = { + .regs = S5P_VA_UART2, + .base_irq = IRQ_S5P_UART_BASE2, + .parent_irq = IRQ_UART2, + }, + [3] = { + .regs = S5P_VA_UART3, + .base_irq = IRQ_S5P_UART_BASE3, + .parent_irq = IRQ_UART3, + }, +}; + +void __init s5p_init_irq(u32 *vic, u32 num_vic) +{ + int irq; + + /* initialize the VICs */ + for (irq = 0; irq < num_vic; irq++) + vic_init(VIC_VAADDR(irq), VIC_BASE(irq), vic[irq], 0); + + s3c_init_vic_timer_irq(IRQ_TIMER0_VIC, IRQ_TIMER0); + s3c_init_vic_timer_irq(IRQ_TIMER1_VIC, IRQ_TIMER1); + s3c_init_vic_timer_irq(IRQ_TIMER2_VIC, IRQ_TIMER2); + s3c_init_vic_timer_irq(IRQ_TIMER3_VIC, IRQ_TIMER3); + s3c_init_vic_timer_irq(IRQ_TIMER4_VIC, IRQ_TIMER4); + + s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); +} -- cgit v0.10.2 From 5f3545f64a44b2b2dac34141dba23d18cdf40a92 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 08:15:39 +0900 Subject: ARM: S5P6440: Add Timer support This patch adds timer support for S5P6440 CPU. Signed-off-by: Atul Dahiya Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5p6440/include/mach/pwm-clock.h b/arch/arm/mach-s5p6440/include/mach/pwm-clock.h new file mode 100644 index 0000000..c4bb7c5 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/pwm-clock.h @@ -0,0 +1,62 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/pwm-clock.h + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * Copyright 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - pwm clock and timer 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. +*/ + +/** + * pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk + * @cfg: The timer TCFG1 register bits shifted down to 0. + * + * Return true if the given configuration from TCFG1 is a TCLK instead + * any of the TDIV clocks. + */ +static inline int pwm_cfg_src_is_tclk(unsigned long tcfg) +{ + return tcfg == S3C2410_TCFG1_MUX_TCLK; +} + +/** + * tcfg_to_divisor() - convert tcfg1 setting to a divisor + * @tcfg1: The tcfg1 setting, shifted down. + * + * Get the divisor value for the given tcfg1 setting. We assume the + * caller has already checked to see if this is not a TCLK source. + */ +static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) +{ + return 1 << (1 + tcfg1); +} + +/** + * pwm_tdiv_has_div1() - does the tdiv setting have a /1 + * + * Return true if we have a /1 in the tdiv setting. + */ +static inline unsigned int pwm_tdiv_has_div1(void) +{ + return 0; +} + +/** + * pwm_tdiv_div_bits() - calculate TCFG1 divisor value. + * @div: The divisor to calculate the bit information for. + * + * Turn a divisor into the necessary bit field for TCFG1. + */ +static inline unsigned long pwm_tdiv_div_bits(unsigned int div) +{ + return ilog2(div) - 1; +} + +#define S3C_TCFG1_MUX_TCLK S3C2410_TCFG1_MUX_TCLK diff --git a/arch/arm/mach-s5p6440/include/mach/tick.h b/arch/arm/mach-s5p6440/include/mach/tick.h new file mode 100644 index 0000000..0815aeb --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/tick.h @@ -0,0 +1,24 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/tick.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - Timer tick support definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_TICK_H +#define __ASM_ARCH_TICK_H __FILE__ + +static inline u32 s3c24xx_ostimer_pending(void) +{ + u32 pend = __raw_readl(S5P_VA_VIC0 + VIC_RAW_STATUS); + return pend & (1 << (IRQ_TIMER4_VIC - S5P_IRQ_VIC0(0))); +} + +#define TICK_MAX (0xffffffff) + +#endif /* __ASM_ARCH_TICK_H */ -- cgit v0.10.2 From c4ffccddd54b669cc9683f2c69e31035e916a1a0 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 08:19:36 +0900 Subject: ARM: S5P6440: Add new Kconfig and Makefiles This patch adds the Kconfig and Makefile for the new S5P6440 machine and platform directories. It also updates arch/arm Kconfig and Makefiles to include the support for the new S5P6440 CPU. Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c2238cd..3bc5169 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -633,6 +633,14 @@ config ARCH_S3C64XX help Samsung S3C64XX series based systems +config ARCH_S5P6440 + bool "Samsung S5P6440" + select CPU_V6 + select GENERIC_GPIO + select HAVE_CLK + help + Samsung S5P6440 CPU based systems + config ARCH_S5PC1XX bool "Samsung S5PC1XX" select GENERIC_GPIO @@ -778,6 +786,7 @@ source "arch/arm/plat-samsung/Kconfig" source "arch/arm/plat-s3c24xx/Kconfig" source "arch/arm/plat-s3c64xx/Kconfig" source "arch/arm/plat-s3c/Kconfig" +source "arch/arm/plat-s5p/Kconfig" source "arch/arm/plat-s5pc1xx/Kconfig" if ARCH_S3C2410 @@ -794,6 +803,8 @@ source "arch/arm/mach-s3c6400/Kconfig" source "arch/arm/mach-s3c6410/Kconfig" endif +source "arch/arm/mach-s5p6440/Kconfig" + source "arch/arm/plat-stmp3xxx/Kconfig" if ARCH_S5PC1XX @@ -1074,7 +1085,7 @@ source kernel/Kconfig.preempt config HZ int default 128 if ARCH_L7200 - default 200 if ARCH_EBSA110 || ARCH_S3C2410 + default 200 if ARCH_EBSA110 || ARCH_S3C2410 || ARCH_S5P6440 default OMAP_32K_TIMER_HZ if ARCH_OMAP && OMAP_32K_TIMER default AT91_TIMER_HZ if ARCH_AT91 default 100 diff --git a/arch/arm/Makefile b/arch/arm/Makefile index e9da084..bbcd512 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -161,6 +161,7 @@ machine-$(CONFIG_ARCH_RPC) := rpc machine-$(CONFIG_ARCH_S3C2410) := s3c2410 s3c2400 s3c2412 s3c2440 s3c2442 s3c2443 machine-$(CONFIG_ARCH_S3C24A0) := s3c24a0 machine-$(CONFIG_ARCH_S3C64XX) := s3c6400 s3c6410 +machine-$(CONFIG_ARCH_S5P6440) := s5p6440 machine-$(CONFIG_ARCH_S5PC1XX) := s5pc100 machine-$(CONFIG_ARCH_SA1100) := sa1100 machine-$(CONFIG_ARCH_SHARK) := shark @@ -184,6 +185,7 @@ plat-$(CONFIG_PLAT_PXA) := pxa plat-$(CONFIG_PLAT_S3C24XX) := s3c24xx s3c samsung plat-$(CONFIG_PLAT_S3C64XX) := s3c64xx s3c samsung plat-$(CONFIG_PLAT_S5PC1XX) := s5pc1xx s3c samsung +plat-$(CONFIG_PLAT_S5P) := s5p samsung s3c plat-$(CONFIG_ARCH_STMP3XXX) := stmp3xxx ifeq ($(CONFIG_ARCH_EBSA110),y) diff --git a/arch/arm/mach-s5p6440/Kconfig b/arch/arm/mach-s5p6440/Kconfig new file mode 100644 index 0000000..3aa2462 --- /dev/null +++ b/arch/arm/mach-s5p6440/Kconfig @@ -0,0 +1,23 @@ +# arch/arm/mach-s5p6440/Kconfig +# +# Copyright (c) 2009 Samsung Electronics Co., Ltd. +# http://www.samsung.com/ +# +# Licensed under GPLv2 + +if ARCH_S5P6440 + +config CPU_S5P6440 + bool + select CPU_S5P6440_INIT + select CPU_S5P6440_CLOCK + help + Enable S5P6440 CPU support + +config MACH_SMDK6440 + bool "SMDK6440" + select CPU_S5P6440 + help + Machine support for the Samsung SMDK6440 + +endif diff --git a/arch/arm/mach-s5p6440/Makefile b/arch/arm/mach-s5p6440/Makefile new file mode 100644 index 0000000..a3ffda9 --- /dev/null +++ b/arch/arm/mach-s5p6440/Makefile @@ -0,0 +1,19 @@ +# arch/arm/mach-s5p6440/Makefile +# +# Copyright (c) 2009 Samsung Electronics Co., Ltd. +# http://www.samsung.com/ +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := +obj- := + +# Core support for S5P6440 system + +obj-$(CONFIG_CPU_S5P6440) += cpu.o + +# machine support + +obj-$(CONFIG_MACH_SMDK6440) += mach-smdk6440.o diff --git a/arch/arm/mach-s5p6440/Makefile.boot b/arch/arm/mach-s5p6440/Makefile.boot new file mode 100644 index 0000000..ff90aa1 --- /dev/null +++ b/arch/arm/mach-s5p6440/Makefile.boot @@ -0,0 +1,2 @@ + zreladdr-y := 0x20008000 +params_phys-y := 0x20000100 diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig new file mode 100644 index 0000000..7e08b40 --- /dev/null +++ b/arch/arm/plat-s5p/Kconfig @@ -0,0 +1,38 @@ +# arch/arm/plat-s5p/Kconfig +# +# Copyright (c) 2009 Samsung Electronics Co., Ltd. +# http://www.samsung.com/ +# +# Licensed under GPLv2 + +config PLAT_S5P + bool + depends on ARCH_S5P6440 + default y + select PLAT_S3C + select ARM_VIC + select NO_IOPORT + select ARCH_REQUIRE_GPIOLIB + select S3C_GPIO_TRACK + select PLAT_SAMSUNG + select SAMSUNG_CLKSRC + select SAMSUNG_IRQ_VIC_TIMER + select SAMSUNG_IRQ_UART + help + Base platform code for Samsung's S5P series SoC. + +if (PLAT_S5P && ARCH_S5P6440) + +# Configuration options shared by all S5P64XX implementations + +config CPU_S5P6440_INIT + bool + help + Initialisation code for the S5P6440. + +config CPU_S5P6440_CLOCK + bool + help + Clock support code for the S5P6440. + +endif diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile new file mode 100644 index 0000000..92b6474 --- /dev/null +++ b/arch/arm/plat-s5p/Makefile @@ -0,0 +1,24 @@ +# arch/arm/plat-s5p/Makefile +# +# Copyright (c) 2009 Samsung Electronics Co., Ltd. +# http://www.samsung.com/ +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := dummy.o +obj- := + +# Core files + +obj-y += dev-uart.o +obj-y += cpu.o +obj-y += clock.o +obj-y += irq.o +obj-y += setup-i2c0.o + +# CPU support + +obj-$(CONFIG_CPU_S5P6440_INIT) += s5p6440-init.o +obj-$(CONFIG_CPU_S5P6440_CLOCK) += s5p6440-clock.o -- cgit v0.10.2 From d25168ea9308e19c7000f0c997eedec87c5d6298 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 08:22:00 +0900 Subject: ARM: S5P6440: Add Board support file This patch adds Samsung's SMDK6440 board support file. Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5p6440/mach-smdk6440.c b/arch/arm/mach-s5p6440/mach-smdk6440.c new file mode 100644 index 0000000..760ea54 --- /dev/null +++ b/arch/arm/mach-s5p6440/mach-smdk6440.c @@ -0,0 +1,111 @@ +/* linux/arch/arm/mach-s5p6440/mach-smdk6440.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define S5P6440_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \ + S3C2410_UCON_RXILEVEL | \ + S3C2410_UCON_TXIRQMODE | \ + S3C2410_UCON_RXIRQMODE | \ + S3C2410_UCON_RXFIFO_TOI | \ + S3C2443_UCON_RXERR_IRQEN) + +#define S5P6440_ULCON_DEFAULT S3C2410_LCON_CS8 + +#define S5P6440_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \ + S3C2440_UFCON_TXTRIG16 | \ + S3C2410_UFCON_RXTRIG8) + +static struct s3c2410_uartcfg smdk6440_uartcfgs[] __initdata = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = S5P6440_UCON_DEFAULT, + .ulcon = S5P6440_ULCON_DEFAULT, + .ufcon = S5P6440_UFCON_DEFAULT, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = S5P6440_UCON_DEFAULT, + .ulcon = S5P6440_ULCON_DEFAULT, + .ufcon = S5P6440_UFCON_DEFAULT, + }, + [2] = { + .hwport = 2, + .flags = 0, + .ucon = S5P6440_UCON_DEFAULT, + .ulcon = S5P6440_ULCON_DEFAULT, + .ufcon = S5P6440_UFCON_DEFAULT, + }, + [3] = { + .hwport = 3, + .flags = 0, + .ucon = S5P6440_UCON_DEFAULT, + .ulcon = S5P6440_ULCON_DEFAULT, + .ufcon = S5P6440_UFCON_DEFAULT, + }, +}; + +static struct platform_device *smdk6440_devices[] __initdata = { +}; + +static void __init smdk6440_map_io(void) +{ + s5p_init_io(NULL, 0, S5P_SYS_ID); + s3c24xx_init_clocks(12000000); + s3c24xx_init_uarts(smdk6440_uartcfgs, ARRAY_SIZE(smdk6440_uartcfgs)); +} + +static void __init smdk6440_machine_init(void) +{ + platform_add_devices(smdk6440_devices, ARRAY_SIZE(smdk6440_devices)); +} + +MACHINE_START(SMDK6440, "SMDK6440") + /* Maintainer: Kukjin Kim */ + .phys_io = S5P_PA_UART & 0xfff00000, + .io_pg_offst = (((u32)S5P_VA_UART) >> 18) & 0xfffc, + .boot_params = S5P_PA_SDRAM + 0x100, + + .init_irq = s5p6440_init_irq, + .map_io = smdk6440_map_io, + .init_machine = smdk6440_machine_init, + .timer = &s3c24xx_timer, +MACHINE_END -- cgit v0.10.2 From 62a6156e88b2e89eafed30aefc97ff4979393c1c Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 08:23:04 +0900 Subject: ARM: S5P6440: Add configuration file This patch adds Samsung's S5P6440 default configuration file. Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/configs/s5p6440_defconfig b/arch/arm/configs/s5p6440_defconfig new file mode 100644 index 0000000..279a15e --- /dev/null +++ b/arch/arm/configs/s5p6440_defconfig @@ -0,0 +1,969 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.33-rc2 +# Sat Jan 9 16:33:55 2010 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_NO_IOPORT=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +# CONFIG_SYSVIPC is not set +# CONFIG_BSD_PROCESS_ACCT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +# CONFIG_GROUP_SCHED is not set +# CONFIG_CGROUPS is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +# CONFIG_RELAY is not set +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +# CONFIG_EMBEDDED is not set +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y + +# +# Kernel Performance Events And Counters +# +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_FREEZER is not set + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +CONFIG_ARCH_S5P6440=y +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_U8500 is not set +CONFIG_PLAT_SAMSUNG=y +CONFIG_SAMSUNG_CLKSRC=y +CONFIG_SAMSUNG_IRQ_VIC_TIMER=y +CONFIG_SAMSUNG_IRQ_UART=y +CONFIG_SAMSUNG_GPIO_EXTRA=0 +CONFIG_PLAT_S3C=y + +# +# Boot options +# +CONFIG_S3C_BOOT_ERROR_RESET=y +CONFIG_S3C_BOOT_UART_FORCE_FIFO=y + +# +# Power management +# +CONFIG_S3C_LOWLEVEL_UART_PORT=1 +CONFIG_S3C_GPIO_SPACE=0 +CONFIG_S3C_GPIO_TRACK=y +CONFIG_PLAT_S5P=y +CONFIG_CPU_S5P6440_INIT=y +CONFIG_CPU_S5P6440_CLOCK=y +CONFIG_CPU_S5P6440=y +CONFIG_MACH_SMDK6440=y + +# +# Processor Type +# +CONFIG_CPU_V6=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v6=y +CONFIG_CPU_ABRT_EV6=y +CONFIG_CPU_PABRT_V6=y +CONFIG_CPU_CACHE_V6=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V6=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +# CONFIG_ARM_ERRATA_411920 is not set +CONFIG_ARM_VIC=y +CONFIG_ARM_VIC_NR=2 + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=200 +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x20800000,8M console=ttySAC1,115200 init=/linuxrc" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +# CONFIG_PM is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_MG_DISK is not set +# CONFIG_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_CONSOLE is not set +CONFIG_SERIAL_8250_NR_UARTS=3 +CONFIG_SERIAL_8250_RUNTIME_UARTS=3 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_UARTS=4 +# CONFIG_SERIAL_SAMSUNG_DEBUG is not set +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_SERIAL_S5P6440=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_I2C is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +CONFIG_DISPLAY_SUPPORT=y + +# +# Display hardware drivers +# + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +CONFIG_ROMFS_FS=y +CONFIG_ROMFS_BACKED_BY_BLOCK=y +# CONFIG_ROMFS_BACKED_BY_MTD is not set +# CONFIG_ROMFS_BACKED_BY_BOTH is not set +CONFIG_ROMFS_ON_BLOCK=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_PI_LIST=y +# CONFIG_RT_MUTEX_TESTER is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +CONFIG_DEBUG_SPINLOCK_SLEEP=y +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +CONFIG_SYSCTL_SYSCALL_CHECK=y +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_EARLY_PRINTK is not set +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_OC_ETM is not set +CONFIG_DEBUG_S3C_UART=1 + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_DMA=y -- cgit v0.10.2 From 501dae90b3ae4dd3d8efdacfcb072c3d65eb5a33 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Thu, 14 Jan 2010 08:23:53 +0900 Subject: ARM: S5P6440: Add serial port support This patch adds UART serial port support for S5P6440 CPU. Most of the serial support of Samsung's 6400 CPU is reused for 6440 CPU. Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h index c1c20b0..8a4153b 100644 --- a/arch/arm/plat-s3c/include/plat/devs.h +++ b/arch/arm/plat-s3c/include/plat/devs.h @@ -18,6 +18,7 @@ struct s3c24xx_uart_resources { extern struct s3c24xx_uart_resources s3c2410_uart_resources[]; extern struct s3c24xx_uart_resources s3c64xx_uart_resources[]; +extern struct s3c24xx_uart_resources s5p_uart_resources[]; extern struct platform_device *s3c24xx_uart_devs[]; extern struct platform_device *s3c24xx_uart_src[]; diff --git a/arch/arm/plat-s5p/dev-uart.c b/arch/arm/plat-s5p/dev-uart.c new file mode 100644 index 0000000..af726f4 --- /dev/null +++ b/arch/arm/plat-s5p/dev-uart.c @@ -0,0 +1,165 @@ +/* linux/arch/arm/plat-s5p/dev-uart.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Base S5P UART resource and device definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + /* Serial port registrations */ + +static struct resource s5p_uart0_resource[] = { + [0] = { + .start = S5P_PA_UART0, + .end = S5P_PA_UART0 + S5P_SZ_UART, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S5P_UART_RX0, + .end = IRQ_S5P_UART_RX0, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_S5P_UART_TX0, + .end = IRQ_S5P_UART_TX0, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = IRQ_S5P_UART_ERR0, + .end = IRQ_S5P_UART_ERR0, + .flags = IORESOURCE_IRQ, + } +}; + +static struct resource s5p_uart1_resource[] = { + [0] = { + .start = S5P_PA_UART1, + .end = S5P_PA_UART1 + S5P_SZ_UART, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S5P_UART_RX1, + .end = IRQ_S5P_UART_RX1, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_S5P_UART_TX1, + .end = IRQ_S5P_UART_TX1, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = IRQ_S5P_UART_ERR1, + .end = IRQ_S5P_UART_ERR1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource s5p_uart2_resource[] = { + [0] = { + .start = S5P_PA_UART2, + .end = S5P_PA_UART2 + S5P_SZ_UART, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S5P_UART_RX2, + .end = IRQ_S5P_UART_RX2, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_S5P_UART_TX2, + .end = IRQ_S5P_UART_TX2, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = IRQ_S5P_UART_ERR2, + .end = IRQ_S5P_UART_ERR2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource s5p_uart3_resource[] = { + [0] = { + .start = S5P_PA_UART3, + .end = S5P_PA_UART3 + S5P_SZ_UART, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S5P_UART_RX3, + .end = IRQ_S5P_UART_RX3, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_S5P_UART_TX3, + .end = IRQ_S5P_UART_TX3, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = IRQ_S5P_UART_ERR3, + .end = IRQ_S5P_UART_ERR3, + .flags = IORESOURCE_IRQ, + }, +}; + +struct s3c24xx_uart_resources s5p_uart_resources[] __initdata = { + [0] = { + .resources = s5p_uart0_resource, + .nr_resources = ARRAY_SIZE(s5p_uart0_resource), + }, + [1] = { + .resources = s5p_uart1_resource, + .nr_resources = ARRAY_SIZE(s5p_uart1_resource), + }, + [2] = { + .resources = s5p_uart2_resource, + .nr_resources = ARRAY_SIZE(s5p_uart2_resource), + }, + [3] = { + .resources = s5p_uart3_resource, + .nr_resources = ARRAY_SIZE(s5p_uart3_resource), + }, +}; + +/* uart devices */ + +static struct platform_device s5p_uart_device0 = { + .id = 0, +}; + +static struct platform_device s5p_uart_device1 = { + .id = 1, +}; + +static struct platform_device s5p_uart_device2 = { + .id = 2, +}; + +static struct platform_device s5p_uart_device3 = { + .id = 3, +}; + +struct platform_device *s3c24xx_uart_src[4] = { + &s5p_uart_device0, + &s5p_uart_device1, + &s5p_uart_device2, + &s5p_uart_device3, +}; + +struct platform_device *s3c24xx_uart_devs[4] = { +}; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 9ff47db0..d7d687f 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -459,7 +459,7 @@ config SERIAL_SAMSUNG_UARTS int depends on ARM && PLAT_S3C default 2 if ARCH_S3C2400 - default 4 if ARCH_S5PC1XX || ARCH_S3C64XX || CPU_S3C2443 + default 4 if ARCH_S5P6440 || ARCH_S5PC1XX || ARCH_S3C64XX || CPU_S3C2443 default 3 help Select the number of available UART ports for the Samsung S3C @@ -526,11 +526,11 @@ config SERIAL_S3C24A0 Serial port support for the Samsung S3C24A0 SoC config SERIAL_S3C6400 - tristate "Samsung S3C6400/S3C6410 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410) + tristate "Samsung S3C6400/S3C6410/S5P6440 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440) default y help - Serial port support for the Samsung S3C6400 and S3C6410 + Serial port support for the Samsung S3C6400, S3C6410 and S5P6440 SoCs config SERIAL_S5PC100 -- cgit v0.10.2 From b813248c614b07642adbf1d7b577d0466003b339 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 23 Nov 2009 00:13:39 +0000 Subject: ARM: S3C: Rename s3c_device_usb to s3c_device_ohci Prepare for the forthcoming device changes by renaming s3c_device_usb to s3c_device_ohci as this is what the device represents. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c2410/mach-amlm5900.c b/arch/arm/mach-s3c2410/mach-amlm5900.c index 06a84ad..7047317 100644 --- a/arch/arm/mach-s3c2410/mach-amlm5900.c +++ b/arch/arm/mach-s3c2410/mach-amlm5900.c @@ -153,7 +153,7 @@ static struct platform_device *amlm5900_devices[] __initdata = { &s3c_device_adc, &s3c_device_wdt, &s3c_device_i2c0, - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_rtc, &s3c_device_usbgadget, &s3c_device_sdi, diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c index 97162fd..02b1b622 100644 --- a/arch/arm/mach-s3c2410/mach-bast.c +++ b/arch/arm/mach-s3c2410/mach-bast.c @@ -584,7 +584,7 @@ static struct s3c_hwmon_pdata bast_hwmon_info = { // cat /sys/devices/platform/s3c24xx-adc/s3c-hwmon/in_0 static struct platform_device *bast_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 1e34abe..3f9217d 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -272,7 +272,7 @@ static struct platform_device h1940_lcd_powerdev = { static struct platform_device *h1940_devices[] __initdata = { &s3c_device_ts, - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2410/mach-n30.c b/arch/arm/mach-s3c2410/mach-n30.c index 0405712..684710f 100644 --- a/arch/arm/mach-s3c2410/mach-n30.c +++ b/arch/arm/mach-s3c2410/mach-n30.c @@ -322,7 +322,7 @@ static struct platform_device *n30_devices[] __initdata = { &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_usbgadget, &n30_button_device, &n30_blue_led, diff --git a/arch/arm/mach-s3c2410/mach-otom.c b/arch/arm/mach-s3c2410/mach-otom.c index f6c7261..d8c7f2e 100644 --- a/arch/arm/mach-s3c2410/mach-otom.c +++ b/arch/arm/mach-s3c2410/mach-otom.c @@ -92,7 +92,7 @@ static struct platform_device otom_device_nor = { /* Standard OTOM devices */ static struct platform_device *otom11_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2410/mach-qt2410.c b/arch/arm/mach-s3c2410/mach-qt2410.c index ab092bc..92a4ec3 100644 --- a/arch/arm/mach-s3c2410/mach-qt2410.c +++ b/arch/arm/mach-s3c2410/mach-qt2410.c @@ -246,7 +246,7 @@ static struct platform_device qt2410_spi = { /* Board devices */ static struct platform_device *qt2410_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c index c49126c..4522230 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2410.c +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c @@ -87,7 +87,7 @@ static struct s3c2410_uartcfg smdk2410_uartcfgs[] __initdata = { }; static struct platform_device *smdk2410_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2410/mach-tct_hammer.c b/arch/arm/mach-s3c2410/mach-tct_hammer.c index 8fdb043..929164a 100644 --- a/arch/arm/mach-s3c2410/mach-tct_hammer.c +++ b/arch/arm/mach-s3c2410/mach-tct_hammer.c @@ -129,7 +129,7 @@ static struct platform_device *tct_hammer_devices[] __initdata = { &s3c_device_adc, &s3c_device_wdt, &s3c_device_i2c0, - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_rtc, &s3c_device_usbgadget, &s3c_device_sdi, diff --git a/arch/arm/mach-s3c2410/mach-vr1000.c b/arch/arm/mach-s3c2410/mach-vr1000.c index 0d61fb5..9051f0d 100644 --- a/arch/arm/mach-s3c2410/mach-vr1000.c +++ b/arch/arm/mach-s3c2410/mach-vr1000.c @@ -334,7 +334,7 @@ static struct i2c_board_info vr1000_i2c_devs[] __initdata = { /* devices for this board */ static struct platform_device *vr1000_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2410/usb-simtec.c b/arch/arm/mach-s3c2410/usb-simtec.c index 6b9d0d8..199bb4f 100644 --- a/arch/arm/mach-s3c2410/usb-simtec.c +++ b/arch/arm/mach-s3c2410/usb-simtec.c @@ -127,6 +127,6 @@ int usb_simtec_init(void) gpio_direction_output(S3C2410_GPB(4), 1); gpio_direction_input(S3C2410_GPG(10)); - s3c_device_usb.dev.platform_data = &usb_simtec_info; + s3c_device_ohci.dev.platform_data = &usb_simtec_info; return 0; } diff --git a/arch/arm/mach-s3c2412/mach-jive.c b/arch/arm/mach-s3c2412/mach-jive.c index c9fa3fc..14f4798 100644 --- a/arch/arm/mach-s3c2412/mach-jive.c +++ b/arch/arm/mach-s3c2412/mach-jive.c @@ -468,7 +468,7 @@ static struct i2c_board_info jive_i2c_devs[] __initdata = { /* The platform devices being used. */ static struct platform_device *jive_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_rtc, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2412/mach-smdk2413.c b/arch/arm/mach-s3c2412/mach-smdk2413.c index 9a5e434..437c087 100644 --- a/arch/arm/mach-s3c2412/mach-smdk2413.c +++ b/arch/arm/mach-s3c2412/mach-smdk2413.c @@ -104,7 +104,7 @@ static struct s3c2410_udc_mach_info smdk2413_udc_cfg __initdata = { static struct platform_device *smdk2413_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, //&s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2412/mach-vstms.c b/arch/arm/mach-s3c2412/mach-vstms.c index a6ba591..3ca9265 100644 --- a/arch/arm/mach-s3c2412/mach-vstms.c +++ b/arch/arm/mach-s3c2412/mach-vstms.c @@ -121,7 +121,7 @@ static struct s3c2410_platform_nand __initdata vstms_nand_info = { }; static struct platform_device *vstms_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, diff --git a/arch/arm/mach-s3c2440/mach-anubis.c b/arch/arm/mach-s3c2440/mach-anubis.c index 62a4c3e..b73f78a 100644 --- a/arch/arm/mach-s3c2440/mach-anubis.c +++ b/arch/arm/mach-s3c2440/mach-anubis.c @@ -409,7 +409,7 @@ static struct platform_device anubis_device_sm501 = { /* Standard Anubis devices */ static struct platform_device *anubis_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_wdt, &s3c_device_adc, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/mach-at2440evb.c b/arch/arm/mach-s3c2440/mach-at2440evb.c index aa69290..0defde6 100644 --- a/arch/arm/mach-s3c2440/mach-at2440evb.c +++ b/arch/arm/mach-s3c2440/mach-at2440evb.c @@ -203,7 +203,7 @@ static struct s3c2410fb_mach_info at2440evb_fb_info __initdata = { }; static struct platform_device *at2440evb_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_wdt, &s3c_device_adc, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/mach-mini2440.c b/arch/arm/mach-s3c2440/mach-mini2440.c index 2068e90..347eb6d 100644 --- a/arch/arm/mach-s3c2440/mach-mini2440.c +++ b/arch/arm/mach-s3c2440/mach-mini2440.c @@ -506,7 +506,7 @@ static struct i2c_board_info mini2440_i2c_devs[] __initdata = { }; static struct platform_device *mini2440_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_wdt, /* &s3c_device_adc,*/ /* ADC doesn't like living with touchscreen ! */ &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/mach-nexcoder.c b/arch/arm/mach-s3c2440/mach-nexcoder.c index d43eded..86a243b 100644 --- a/arch/arm/mach-s3c2440/mach-nexcoder.c +++ b/arch/arm/mach-s3c2440/mach-nexcoder.c @@ -106,7 +106,7 @@ static struct platform_device nexcoder_device_nor = { /* Standard Nexcoder devices */ static struct platform_device *nexcoder_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/mach-rx3715.c b/arch/arm/mach-s3c2440/mach-rx3715.c index a952a13..1e836e5 100644 --- a/arch/arm/mach-s3c2440/mach-rx3715.c +++ b/arch/arm/mach-s3c2440/mach-rx3715.c @@ -176,7 +176,7 @@ static struct s3c2410_platform_nand __initdata rx3715_nand_info = { }; static struct platform_device *rx3715_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2440/mach-smdk2440.c b/arch/arm/mach-s3c2440/mach-smdk2440.c index ec13e74..df3e9a3 100644 --- a/arch/arm/mach-s3c2440/mach-smdk2440.c +++ b/arch/arm/mach-s3c2440/mach-smdk2440.c @@ -150,7 +150,7 @@ static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = { }; static struct platform_device *smdk2440_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index 0b4a3a0..825800b 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -565,7 +565,7 @@ static void __init gta02_map_io(void) /* These are the guys that don't need to be children of PMU. */ static struct platform_device *gta02_devices[] __initdata = { - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_wdt, &s3c_device_sdi, &s3c_device_usbgadget, @@ -623,7 +623,7 @@ static void __init gta02_machine_init(void) INIT_DELAYED_WORK(>a02_charger_work, gta02_charger_worker); #endif - s3c_device_usb.dev.platform_data = >a02_usb_info; + s3c_device_ohci.dev.platform_data = >a02_usb_info; s3c24xx_udc_set_platdata(>a02_udc_cfg); s3c_nand_set_platdata(>a02_nand_info); diff --git a/arch/arm/mach-s3c6410/mach-hmt.c b/arch/arm/mach-s3c6410/mach-hmt.c index cdd4b53..284886c 100644 --- a/arch/arm/mach-s3c6410/mach-hmt.c +++ b/arch/arm/mach-s3c6410/mach-hmt.c @@ -233,7 +233,7 @@ static struct platform_device *hmt_devices[] __initdata = { &s3c_device_i2c0, &s3c_device_nand, &s3c_device_fb, - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_timer[1], &hmt_backlight_device, &hmt_leds_device, diff --git a/arch/arm/mach-s3c6410/mach-smdk6410.c b/arch/arm/mach-s3c6410/mach-smdk6410.c index 8969fe7..e2811fc 100644 --- a/arch/arm/mach-s3c6410/mach-smdk6410.c +++ b/arch/arm/mach-s3c6410/mach-smdk6410.c @@ -235,7 +235,7 @@ static struct platform_device *smdk6410_devices[] __initdata = { &s3c_device_i2c0, &s3c_device_i2c1, &s3c_device_fb, - &s3c_device_usb, + &s3c_device_ohci, &s3c_device_usb_hsotg, #ifdef CONFIG_REGULATOR diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h index 8a4153b..543a351 100644 --- a/arch/arm/plat-s3c/include/plat/devs.h +++ b/arch/arm/plat-s3c/include/plat/devs.h @@ -34,7 +34,7 @@ extern struct platform_device s3c64xx_device_pcm1; extern struct platform_device s3c_device_ts; extern struct platform_device s3c_device_fb; -extern struct platform_device s3c_device_usb; +extern struct platform_device s3c_device_ohci; extern struct platform_device s3c_device_lcd; extern struct platform_device s3c_device_wdt; extern struct platform_device s3c_device_i2c0; diff --git a/arch/arm/plat-samsung/dev-usb.c b/arch/arm/plat-samsung/dev-usb.c index 2ee85ab..431ae5d 100644 --- a/arch/arm/plat-samsung/dev-usb.c +++ b/arch/arm/plat-samsung/dev-usb.c @@ -36,7 +36,7 @@ static struct resource s3c_usb_resource[] = { static u64 s3c_device_usb_dmamask = 0xffffffffUL; -struct platform_device s3c_device_usb = { +struct platform_device s3c_device_ohci = { .name = "s3c2410-ohci", .id = -1, .num_resources = ARRAY_SIZE(s3c_usb_resource), @@ -47,4 +47,4 @@ struct platform_device s3c_device_usb = { } }; -EXPORT_SYMBOL(s3c_device_usb); +EXPORT_SYMBOL(s3c_device_ohci); -- cgit v0.10.2 From 0f61628e37e121b7786bff2653fc25a321e82ec4 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sun, 29 Nov 2009 14:34:56 +0000 Subject: ARM: S3C2412: Fixup commented out device in SMDK2413 The SMDK2413 has a commented out device that does not have any setup data for it, so remove it for now. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c2412/mach-smdk2413.c b/arch/arm/mach-s3c2412/mach-smdk2413.c index 437c087..0392065 100644 --- a/arch/arm/mach-s3c2412/mach-smdk2413.c +++ b/arch/arm/mach-s3c2412/mach-smdk2413.c @@ -105,7 +105,6 @@ static struct s3c2410_udc_mach_info smdk2413_udc_cfg __initdata = { static struct platform_device *smdk2413_devices[] __initdata = { &s3c_device_ohci, - //&s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, -- cgit v0.10.2 From 7b3bcc4a1a7cd2d53b403ca29d06ceb5fa617eb7 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 15 Oct 2009 14:31:24 +0400 Subject: ACPI: Battery: Add bit flags Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index cada73f..b2b48f8 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -88,10 +88,13 @@ static const struct acpi_device_id battery_device_ids[] = { MODULE_DEVICE_TABLE(acpi, battery_device_ids); -/* For buggy DSDTs that report negative 16-bit values for either charging - * or discharging current and/or report 0 as 65536 due to bad math. - */ -#define QUIRK_SIGNED16_CURRENT 0x0001 +enum { + ACPI_BATTERY_ALARM_PRESENT, + /* For buggy DSDTs that report negative 16-bit values for either charging + * or discharging current and/or report 0 as 65536 due to bad math. + */ + ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, +}; struct acpi_battery { struct mutex lock; @@ -118,8 +121,7 @@ struct acpi_battery { char oem_info[32]; int state; int power_unit; - u8 alarm_present; - long quirks; + unsigned long flags; }; #define to_acpi_battery(x) container_of(x, struct acpi_battery, bat); @@ -399,7 +401,7 @@ static int acpi_battery_get_state(struct acpi_battery *battery) battery->update_time = jiffies; kfree(buffer.pointer); - if ((battery->quirks & QUIRK_SIGNED16_CURRENT) && + if (test_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags) && battery->rate_now != -1) battery->rate_now = abs((s16)battery->rate_now); @@ -412,7 +414,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) union acpi_object arg0 = { .type = ACPI_TYPE_INTEGER }; struct acpi_object_list arg_list = { 1, &arg0 }; - if (!acpi_battery_present(battery)|| !battery->alarm_present) + if (!acpi_battery_present(battery)|| + !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) return -ENODEV; arg0.integer.value = battery->alarm; @@ -437,10 +440,10 @@ static int acpi_battery_init_alarm(struct acpi_battery *battery) /* See if alarms are supported, and if so, set default */ status = acpi_get_handle(battery->device->handle, "_BTP", &handle); if (ACPI_FAILURE(status)) { - battery->alarm_present = 0; + clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); return 0; } - battery->alarm_present = 1; + set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); if (!battery->alarm) battery->alarm = battery->design_capacity_warning; return acpi_battery_set_alarm(battery); @@ -510,9 +513,8 @@ static void sysfs_remove_battery(struct acpi_battery *battery) static void acpi_battery_quirks(struct acpi_battery *battery) { - battery->quirks = 0; if (dmi_name_in_vendors("Acer") && battery->power_unit) { - battery->quirks |= QUIRK_SIGNED16_CURRENT; + set_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags); } } -- cgit v0.10.2 From c955fe8e0bdd7be7a6bc2d49245d570a816f7cc5 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 15 Oct 2009 14:31:30 +0400 Subject: POWER: Add support for cycle_count Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index c790e0c..ff05e61 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -99,6 +99,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(present), POWER_SUPPLY_ATTR(online), POWER_SUPPLY_ATTR(technology), + POWER_SUPPLY_ATTR(cycle_count), POWER_SUPPLY_ATTR(voltage_max), POWER_SUPPLY_ATTR(voltage_min), POWER_SUPPLY_ATTR(voltage_max_design), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index b5d096d..ebd2b8f 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -82,6 +82,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, -- cgit v0.10.2 From 16698857fba1b10af4890055272975adf5686e83 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 15 Oct 2009 14:31:37 +0400 Subject: ACPI: SBS: Export cycle_count Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 52b9db8..38412ec 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -217,6 +217,9 @@ static int acpi_sbs_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = acpi_battery_technology(battery); break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = battery->cycle_count; + break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: val->intval = battery->design_voltage * acpi_battery_vscale(battery) * 1000; @@ -276,6 +279,7 @@ static enum power_supply_property sbs_charge_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, @@ -560,6 +564,7 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset) battery->design_voltage * acpi_battery_vscale(battery)); seq_printf(seq, "design capacity warning: unknown\n"); seq_printf(seq, "design capacity low: unknown\n"); + seq_printf(seq, "cycle count: %i\n", battery->cycle_count); seq_printf(seq, "capacity granularity 1: unknown\n"); seq_printf(seq, "capacity granularity 2: unknown\n"); seq_printf(seq, "model number: %s\n", battery->device_name); -- cgit v0.10.2 From c67fcd670b55e89e0c129fbf7fae854bd1f8bfa6 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Thu, 15 Oct 2009 14:31:44 +0400 Subject: ACPI: Battery: Add support for _BIX extended info method Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index b2b48f8..1ca0ea7 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -54,6 +54,7 @@ #define ACPI_BATTERY_DEVICE_NAME "Battery" #define ACPI_BATTERY_NOTIFY_STATUS 0x80 #define ACPI_BATTERY_NOTIFY_INFO 0x81 +#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82 #define _COMPONENT ACPI_BATTERY_COMPONENT @@ -90,9 +91,11 @@ MODULE_DEVICE_TABLE(acpi, battery_device_ids); enum { ACPI_BATTERY_ALARM_PRESENT, - /* For buggy DSDTs that report negative 16-bit values for either charging - * or discharging current and/or report 0 as 65536 due to bad math. - */ + ACPI_BATTERY_XINFO_PRESENT, + /* For buggy DSDTs that report negative 16-bit values for either + * charging or discharging current and/or report 0 as 65536 + * due to bad math. + */ ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, }; @@ -112,6 +115,12 @@ struct acpi_battery { int design_voltage; int design_capacity_warning; int design_capacity_low; + int cycle_count; + int measurement_accuracy; + int max_sampling_time; + int min_sampling_time; + int max_averaging_interval; + int min_averaging_interval; int capacity_granularity_1; int capacity_granularity_2; int alarm; @@ -200,6 +209,9 @@ static int acpi_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = acpi_battery_technology(battery); break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = battery->cycle_count; + break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: val->intval = battery->design_voltage * 1000; break; @@ -241,6 +253,7 @@ static enum power_supply_property charge_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, @@ -256,6 +269,7 @@ static enum power_supply_property energy_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, @@ -307,6 +321,28 @@ static struct acpi_offsets info_offsets[] = { {offsetof(struct acpi_battery, oem_info), 1}, }; +static struct acpi_offsets extended_info_offsets[] = { + {offsetof(struct acpi_battery, power_unit), 0}, + {offsetof(struct acpi_battery, design_capacity), 0}, + {offsetof(struct acpi_battery, full_charge_capacity), 0}, + {offsetof(struct acpi_battery, technology), 0}, + {offsetof(struct acpi_battery, design_voltage), 0}, + {offsetof(struct acpi_battery, design_capacity_warning), 0}, + {offsetof(struct acpi_battery, design_capacity_low), 0}, + {offsetof(struct acpi_battery, cycle_count), 0}, + {offsetof(struct acpi_battery, measurement_accuracy), 0}, + {offsetof(struct acpi_battery, max_sampling_time), 0}, + {offsetof(struct acpi_battery, min_sampling_time), 0}, + {offsetof(struct acpi_battery, max_averaging_interval), 0}, + {offsetof(struct acpi_battery, min_averaging_interval), 0}, + {offsetof(struct acpi_battery, capacity_granularity_1), 0}, + {offsetof(struct acpi_battery, capacity_granularity_2), 0}, + {offsetof(struct acpi_battery, model_number), 1}, + {offsetof(struct acpi_battery, serial_number), 1}, + {offsetof(struct acpi_battery, type), 1}, + {offsetof(struct acpi_battery, oem_info), 1}, +}; + static int extract_package(struct acpi_battery *battery, union acpi_object *package, struct acpi_offsets *offsets, int num) @@ -352,22 +388,29 @@ static int acpi_battery_get_info(struct acpi_battery *battery) { int result = -EFAULT; acpi_status status = 0; + char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags)? + "_BIX" : "_BIF"; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; if (!acpi_battery_present(battery)) return 0; mutex_lock(&battery->lock); - status = acpi_evaluate_object(battery->device->handle, "_BIF", - NULL, &buffer); + status = acpi_evaluate_object(battery->device->handle, name, + NULL, &buffer); mutex_unlock(&battery->lock); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF")); + ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name)); return -ENODEV; } - - result = extract_package(battery, buffer.pointer, - info_offsets, ARRAY_SIZE(info_offsets)); + if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags)) + result = extract_package(battery, buffer.pointer, + extended_info_offsets, + ARRAY_SIZE(extended_info_offsets)); + else + result = extract_package(battery, buffer.pointer, + info_offsets, ARRAY_SIZE(info_offsets)); kfree(buffer.pointer); return result; } @@ -414,7 +457,7 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) union acpi_object arg0 = { .type = ACPI_TYPE_INTEGER }; struct acpi_object_list arg_list = { 1, &arg0 }; - if (!acpi_battery_present(battery)|| + if (!acpi_battery_present(battery) || !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) return -ENODEV; @@ -592,6 +635,7 @@ static int acpi_battery_print_info(struct seq_file *seq, int result) seq_printf(seq, "design capacity low: %d %sh\n", battery->design_capacity_low, acpi_battery_units(battery)); + seq_printf(seq, "cycle count: %i\n", battery->cycle_count); seq_printf(seq, "capacity granularity 1: %d %sh\n", battery->capacity_granularity_1, acpi_battery_units(battery)); @@ -843,6 +887,7 @@ static int acpi_battery_add(struct acpi_device *device) { int result = 0; struct acpi_battery *battery = NULL; + acpi_handle handle; if (!device) return -EINVAL; battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL); @@ -853,6 +898,9 @@ static int acpi_battery_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); device->driver_data = battery; mutex_init(&battery->lock); + if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle, + "_BIX", &handle))) + set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); acpi_battery_update(battery); #ifdef CONFIG_ACPI_PROCFS_POWER result = acpi_battery_add_fs(device); -- cgit v0.10.2 From 206090913d0d6ad3506e3e2693a696fc1626993e Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Tue, 12 Jan 2010 01:40:14 +0100 Subject: ARM: S3C64XX: Add S3C64XX RTC platform driver Add S3C64XX RTC platform driver Signed-off-by: Maurus Cuelenaere Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index d4cd3ab..0552716 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -42,6 +42,7 @@ #define S3C64XX_PA_FB (0x77100000) #define S3C64XX_PA_USB_HSOTG (0x7C000000) #define S3C64XX_PA_WATCHDOG (0x7E004000) +#define S3C64XX_PA_RTC (0x7E005000) #define S3C64XX_PA_SYSCON (0x7E00F000) #define S3C64XX_PA_AC97 (0x7F001000) #define S3C64XX_PA_IIS0 (0x7F002000) diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile index b85b435..e66dbd7 100644 --- a/arch/arm/plat-s3c64xx/Makefile +++ b/arch/arm/plat-s3c64xx/Makefile @@ -13,6 +13,7 @@ obj- := # Core files obj-y += dev-uart.o +obj-y += dev-rtc.o obj-y += cpu.o obj-y += irq.o obj-y += irq-eint.o diff --git a/arch/arm/plat-s3c64xx/dev-rtc.c b/arch/arm/plat-s3c64xx/dev-rtc.c new file mode 100644 index 0000000..b9e7a05 --- /dev/null +++ b/arch/arm/plat-s3c64xx/dev-rtc.c @@ -0,0 +1,43 @@ +/* linux/arch/arm/plat-s3c64xx/dev-rtc.c + * + * Copyright 2009 by Maurus Cuelenaere + * + * 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 + +static struct resource s3c_rtc_resource[] = { + [0] = { + .start = S3C64XX_PA_RTC, + .end = S3C64XX_PA_RTC + 0xff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_RTC_ALARM, + .end = IRQ_RTC_ALARM, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_RTC_TIC, + .end = IRQ_RTC_TIC, + .flags = IORESOURCE_IRQ + } +}; + +struct platform_device s3c_device_rtc = { + .name = "s3c64xx-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_rtc_resource), + .resource = s3c_rtc_resource, +}; +EXPORT_SYMBOL(s3c_device_rtc); -- cgit v0.10.2 From 71269364a8d5ea42ecc6cbc7b687e5d45cbabe0e Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Wed, 6 Jan 2010 15:33:44 +0900 Subject: ARM: S3C64XX: Add I2S resources in platform code Define resources needed by the I2S drivers in platform code. Audio cfg_gpio callback and IORESOURCE_DMA are added. Signed-off-by: Jassi Brar Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c index a21a88f..cdba3a7 100644 --- a/arch/arm/plat-s3c64xx/dev-audio.c +++ b/arch/arm/plat-s3c64xx/dev-audio.c @@ -3,7 +3,6 @@ * Copyright 2009 Wolfson Microelectronics * Mark Brown * - * 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. @@ -20,16 +19,69 @@ #include #include +#include #include #include +#include #include +static int s3c64xx_i2sv3_cfg_gpio(struct platform_device *pdev) +{ + switch (pdev->id) { + case 0: + s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_I2S0_CLK); + s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_I2S0_CDCLK); + s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_I2S0_LRCLK); + s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_I2S0_DI); + s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_I2S0_D0); + break; + case 1: + s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_I2S1_CLK); + s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_I2S1_CDCLK); + s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_I2S1_LRCLK); + s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_I2S1_DI); + s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_I2S1_D0); + default: + printk(KERN_DEBUG "Invalid I2S Controller number!"); + return -EINVAL; + } + + return 0; +} + +static int s3c64xx_i2sv4_cfg_gpio(struct platform_device *pdev) +{ + s3c_gpio_cfgpin(S3C64XX_GPC(4), S3C64XX_GPC4_I2S0_V40_DO); + s3c_gpio_cfgpin(S3C64XX_GPC(5), S3C64XX_GPC5_I2S1_V40_DO); + s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C64XX_GPC7_I2S2_V40_DO); + s3c_gpio_cfgpin(S3C64XX_GPH(6), S3C64XX_GPH6_I2S_V40_BCLK); + s3c_gpio_cfgpin(S3C64XX_GPH(7), S3C64XX_GPH7_I2S_V40_CDCLK); + s3c_gpio_cfgpin(S3C64XX_GPH(8), S3C64XX_GPH8_I2S_V40_LRCLK); + s3c_gpio_cfgpin(S3C64XX_GPH(9), S3C64XX_GPH9_I2S_V40_DI); + + return 0; +} + static struct resource s3c64xx_iis0_resource[] = { [0] = { .start = S3C64XX_PA_IIS0, .end = S3C64XX_PA_IIS0 + 0x100 - 1, .flags = IORESOURCE_MEM, }, + [1] = { + .start = DMACH_I2S0_OUT, + .end = DMACH_I2S0_OUT, + .flags = IORESOURCE_DMA, + }, + [2] = { + .start = DMACH_I2S0_IN, + .end = DMACH_I2S0_IN, + .flags = IORESOURCE_DMA, + }, +}; + +struct s3c_audio_pdata s3c_i2s0_pdata = { + .cfg_gpio = s3c64xx_i2sv3_cfg_gpio, }; struct platform_device s3c64xx_device_iis0 = { @@ -37,6 +89,9 @@ struct platform_device s3c64xx_device_iis0 = { .id = 0, .num_resources = ARRAY_SIZE(s3c64xx_iis0_resource), .resource = s3c64xx_iis0_resource, + .dev = { + .platform_data = &s3c_i2s0_pdata, + }, }; EXPORT_SYMBOL(s3c64xx_device_iis0); @@ -46,6 +101,20 @@ static struct resource s3c64xx_iis1_resource[] = { .end = S3C64XX_PA_IIS1 + 0x100 - 1, .flags = IORESOURCE_MEM, }, + [1] = { + .start = DMACH_I2S1_OUT, + .end = DMACH_I2S1_OUT, + .flags = IORESOURCE_DMA, + }, + [2] = { + .start = DMACH_I2S1_IN, + .end = DMACH_I2S1_IN, + .flags = IORESOURCE_DMA, + }, +}; + +struct s3c_audio_pdata s3c_i2s1_pdata = { + .cfg_gpio = s3c64xx_i2sv3_cfg_gpio, }; struct platform_device s3c64xx_device_iis1 = { @@ -53,6 +122,9 @@ struct platform_device s3c64xx_device_iis1 = { .id = 1, .num_resources = ARRAY_SIZE(s3c64xx_iis1_resource), .resource = s3c64xx_iis1_resource, + .dev = { + .platform_data = &s3c_i2s1_pdata, + }, }; EXPORT_SYMBOL(s3c64xx_device_iis1); @@ -62,6 +134,20 @@ static struct resource s3c64xx_iisv4_resource[] = { .end = S3C64XX_PA_IISV4 + 0x100 - 1, .flags = IORESOURCE_MEM, }, + [1] = { + .start = DMACH_HSI_I2SV40_TX, + .end = DMACH_HSI_I2SV40_TX, + .flags = IORESOURCE_DMA, + }, + [2] = { + .start = DMACH_HSI_I2SV40_RX, + .end = DMACH_HSI_I2SV40_RX, + .flags = IORESOURCE_DMA, + }, +}; + +struct s3c_audio_pdata s3c_i2sv4_pdata = { + .cfg_gpio = s3c64xx_i2sv4_cfg_gpio, }; struct platform_device s3c64xx_device_iisv4 = { @@ -69,6 +155,9 @@ struct platform_device s3c64xx_device_iisv4 = { .id = -1, .num_resources = ARRAY_SIZE(s3c64xx_iisv4_resource), .resource = s3c64xx_iisv4_resource, + .dev = { + .platform_data = &s3c_i2sv4_pdata, + }, }; EXPORT_SYMBOL(s3c64xx_device_iisv4); -- cgit v0.10.2 From 3929e1e76d9116856a4c7a00fcce0539dd8507a0 Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Thu, 14 Jan 2010 00:30:31 +0100 Subject: ARM: SAMSUNG: Move S3C24XX ADC driver to plat-samsung Move S3C24XX ADC driver to plat-samsung Signed-off-by: Maurus Cuelenaere Signed-off-by: Ben Dooks diff --git a/arch/arm/configs/mini2440_defconfig b/arch/arm/configs/mini2440_defconfig index d2a90eb..4c46644 100644 --- a/arch/arm/configs/mini2440_defconfig +++ b/arch/arm/configs/mini2440_defconfig @@ -184,7 +184,7 @@ CONFIG_S3C24XX_PWM=y CONFIG_S3C24XX_GPIO_EXTRA=0 CONFIG_S3C2410_DMA=y # CONFIG_S3C2410_DMA_DEBUG is not set -CONFIG_S3C24XX_ADC=y +CONFIG_S3C_ADC=y CONFIG_PLAT_S3C=y CONFIG_CPU_LLSERIAL_S3C2440_ONLY=y CONFIG_CPU_LLSERIAL_S3C2440=y diff --git a/arch/arm/configs/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig index 2f10dae..9697e97 100644 --- a/arch/arm/configs/s3c2410_defconfig +++ b/arch/arm/configs/s3c2410_defconfig @@ -187,7 +187,7 @@ CONFIG_S3C24XX_GPIO_EXTRA128=y CONFIG_PM_SIMTEC=y CONFIG_S3C2410_DMA=y # CONFIG_S3C2410_DMA_DEBUG is not set -CONFIG_S3C24XX_ADC=y +CONFIG_S3C_ADC=y CONFIG_MACH_SMDK=y CONFIG_PLAT_S3C=y CONFIG_CPU_LLSERIAL_S3C2410=y diff --git a/arch/arm/plat-s3c/include/plat/adc.h b/arch/arm/plat-s3c/include/plat/adc.h deleted file mode 100644 index 5f3b1cd..0000000 --- a/arch/arm/plat-s3c/include/plat/adc.h +++ /dev/null @@ -1,35 +0,0 @@ -/* arch/arm/plat-s3c/include/plat/adc.h - * - * Copyright (c) 2008 Simtec Electronics - * http://armlinux.simnte.co.uk/ - * Ben Dooks - * - * S3C24XX ADC driver information - * - * 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 __ASM_PLAT_ADC_H -#define __ASM_PLAT_ADC_H __FILE__ - -struct s3c_adc_client; - -extern int s3c_adc_start(struct s3c_adc_client *client, - unsigned int channel, unsigned int nr_samples); - -extern int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch); - -extern struct s3c_adc_client * - s3c_adc_register(struct platform_device *pdev, - void (*select)(struct s3c_adc_client *client, - unsigned selected), - void (*conv)(struct s3c_adc_client *client, - unsigned d0, unsigned d1, - unsigned *samples_left), - unsigned int is_ts); - -extern void s3c_adc_release(struct s3c_adc_client *client); - -#endif /* __ASM_PLAT_ADC_H */ diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index 342647e..a806f35 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig @@ -117,13 +117,6 @@ config S3C2410_DMA_DEBUG Enable debugging output for the DMA code. This option sends info to the kernel log, at priority KERN_DEBUG. -config S3C24XX_ADC - bool "ADC common driver support" - help - Core support for the ADC block found in the S3C24XX SoC systems - for drivers such as the touchscreen and hwmon to use to share - this resource. - # SPI default pin configuration code config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 5dee8c1..e010026 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -38,7 +38,6 @@ obj-$(CONFIG_PM) += irq-pm.o obj-$(CONFIG_PM) += sleep.o obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o obj-$(CONFIG_S3C2410_DMA) += dma.o -obj-$(CONFIG_S3C24XX_ADC) += adc.o obj-$(CONFIG_S3C2410_IOTIMING) += s3c2410-iotiming.o obj-$(CONFIG_S3C2412_IOTIMING) += s3c2412-iotiming.o obj-$(CONFIG_S3C2410_CPUFREQ_UTILS) += s3c2410-cpufreq-utils.o diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c deleted file mode 100644 index ce47627..0000000 --- a/arch/arm/plat-s3c24xx/adc.c +++ /dev/null @@ -1,435 +0,0 @@ -/* arch/arm/plat-s3c24xx/adc.c - * - * Copyright (c) 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks , - * - * S3C24XX ADC device core - * - * 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. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* This driver is designed to control the usage of the ADC block between - * the touchscreen and any other drivers that may need to use it, such as - * the hwmon driver. - * - * Priority will be given to the touchscreen driver, but as this itself is - * rate limited it should not starve other requests which are processed in - * order that they are received. - * - * Each user registers to get a client block which uniquely identifies it - * and stores information such as the necessary functions to callback when - * action is required. - */ - -struct s3c_adc_client { - struct platform_device *pdev; - struct list_head pend; - wait_queue_head_t *wait; - - unsigned int nr_samples; - int result; - unsigned char is_ts; - unsigned char channel; - - void (*select_cb)(struct s3c_adc_client *c, unsigned selected); - void (*convert_cb)(struct s3c_adc_client *c, - unsigned val1, unsigned val2, - unsigned *samples_left); -}; - -struct adc_device { - struct platform_device *pdev; - struct platform_device *owner; - struct clk *clk; - struct s3c_adc_client *cur; - struct s3c_adc_client *ts_pend; - void __iomem *regs; - - unsigned int prescale; - - int irq; -}; - -static struct adc_device *adc_dev; - -static LIST_HEAD(adc_pending); - -#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg) - -static inline void s3c_adc_convert(struct adc_device *adc) -{ - unsigned con = readl(adc->regs + S3C2410_ADCCON); - - con |= S3C2410_ADCCON_ENABLE_START; - writel(con, adc->regs + S3C2410_ADCCON); -} - -static inline void s3c_adc_select(struct adc_device *adc, - struct s3c_adc_client *client) -{ - unsigned con = readl(adc->regs + S3C2410_ADCCON); - - client->select_cb(client, 1); - - con &= ~S3C2410_ADCCON_MUXMASK; - con &= ~S3C2410_ADCCON_STDBM; - con &= ~S3C2410_ADCCON_STARTMASK; - - if (!client->is_ts) - con |= S3C2410_ADCCON_SELMUX(client->channel); - - writel(con, adc->regs + S3C2410_ADCCON); -} - -static void s3c_adc_dbgshow(struct adc_device *adc) -{ - adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n", - readl(adc->regs + S3C2410_ADCCON), - readl(adc->regs + S3C2410_ADCTSC), - readl(adc->regs + S3C2410_ADCDLY)); -} - -static void s3c_adc_try(struct adc_device *adc) -{ - struct s3c_adc_client *next = adc->ts_pend; - - if (!next && !list_empty(&adc_pending)) { - next = list_first_entry(&adc_pending, - struct s3c_adc_client, pend); - list_del(&next->pend); - } else - adc->ts_pend = NULL; - - if (next) { - adc_dbg(adc, "new client is %p\n", next); - adc->cur = next; - s3c_adc_select(adc, next); - s3c_adc_convert(adc); - s3c_adc_dbgshow(adc); - } -} - -int s3c_adc_start(struct s3c_adc_client *client, - unsigned int channel, unsigned int nr_samples) -{ - struct adc_device *adc = adc_dev; - unsigned long flags; - - if (!adc) { - printk(KERN_ERR "%s: failed to find adc\n", __func__); - return -EINVAL; - } - - if (client->is_ts && adc->ts_pend) - return -EAGAIN; - - local_irq_save(flags); - - client->channel = channel; - client->nr_samples = nr_samples; - - if (client->is_ts) - adc->ts_pend = client; - else - list_add_tail(&client->pend, &adc_pending); - - if (!adc->cur) - s3c_adc_try(adc); - local_irq_restore(flags); - - return 0; -} -EXPORT_SYMBOL_GPL(s3c_adc_start); - -static void s3c_convert_done(struct s3c_adc_client *client, - unsigned v, unsigned u, unsigned *left) -{ - client->result = v; - wake_up(client->wait); -} - -int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch) -{ - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - int ret; - - client->convert_cb = s3c_convert_done; - client->wait = &wake; - client->result = -1; - - ret = s3c_adc_start(client, ch, 1); - if (ret < 0) - goto err; - - ret = wait_event_timeout(wake, client->result >= 0, HZ / 2); - if (client->result < 0) { - ret = -ETIMEDOUT; - goto err; - } - - client->convert_cb = NULL; - return client->result; - -err: - return ret; -} -EXPORT_SYMBOL_GPL(s3c_adc_read); - -static void s3c_adc_default_select(struct s3c_adc_client *client, - unsigned select) -{ -} - -struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, - void (*select)(struct s3c_adc_client *client, - unsigned int selected), - void (*conv)(struct s3c_adc_client *client, - unsigned d0, unsigned d1, - unsigned *samples_left), - unsigned int is_ts) -{ - struct s3c_adc_client *client; - - WARN_ON(!pdev); - - if (!select) - select = s3c_adc_default_select; - - if (!pdev) - return ERR_PTR(-EINVAL); - - client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL); - if (!client) { - dev_err(&pdev->dev, "no memory for adc client\n"); - return ERR_PTR(-ENOMEM); - } - - client->pdev = pdev; - client->is_ts = is_ts; - client->select_cb = select; - client->convert_cb = conv; - - return client; -} -EXPORT_SYMBOL_GPL(s3c_adc_register); - -void s3c_adc_release(struct s3c_adc_client *client) -{ - /* We should really check that nothing is in progress. */ - if (adc_dev->cur == client) - adc_dev->cur = NULL; - if (adc_dev->ts_pend == client) - adc_dev->ts_pend = NULL; - else { - struct list_head *p, *n; - struct s3c_adc_client *tmp; - - list_for_each_safe(p, n, &adc_pending) { - tmp = list_entry(p, struct s3c_adc_client, pend); - if (tmp == client) - list_del(&tmp->pend); - } - } - - if (adc_dev->cur == NULL) - s3c_adc_try(adc_dev); - kfree(client); -} -EXPORT_SYMBOL_GPL(s3c_adc_release); - -static irqreturn_t s3c_adc_irq(int irq, void *pw) -{ - struct adc_device *adc = pw; - struct s3c_adc_client *client = adc->cur; - unsigned long flags; - unsigned data0, data1; - - if (!client) { - dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__); - return IRQ_HANDLED; - } - - data0 = readl(adc->regs + S3C2410_ADCDAT0); - data1 = readl(adc->regs + S3C2410_ADCDAT1); - adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1); - - client->nr_samples--; - - if (client->convert_cb) - (client->convert_cb)(client, data0 & 0x3ff, data1 & 0x3ff, - &client->nr_samples); - - if (client->nr_samples > 0) { - /* fire another conversion for this */ - - client->select_cb(client, 1); - s3c_adc_convert(adc); - } else { - local_irq_save(flags); - (client->select_cb)(client, 0); - adc->cur = NULL; - - s3c_adc_try(adc); - local_irq_restore(flags); - } - - return IRQ_HANDLED; -} - -static int s3c_adc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct adc_device *adc; - struct resource *regs; - int ret; - - adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL); - if (adc == NULL) { - dev_err(dev, "failed to allocate adc_device\n"); - return -ENOMEM; - } - - adc->pdev = pdev; - adc->prescale = S3C2410_ADCCON_PRSCVL(49); - - adc->irq = platform_get_irq(pdev, 1); - if (adc->irq <= 0) { - dev_err(dev, "failed to get adc irq\n"); - ret = -ENOENT; - goto err_alloc; - } - - ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); - if (ret < 0) { - dev_err(dev, "failed to attach adc irq\n"); - goto err_alloc; - } - - adc->clk = clk_get(dev, "adc"); - if (IS_ERR(adc->clk)) { - dev_err(dev, "failed to get adc clock\n"); - ret = PTR_ERR(adc->clk); - goto err_irq; - } - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_err(dev, "failed to find registers\n"); - ret = -ENXIO; - goto err_clk; - } - - adc->regs = ioremap(regs->start, resource_size(regs)); - if (!adc->regs) { - dev_err(dev, "failed to map registers\n"); - ret = -ENXIO; - goto err_clk; - } - - clk_enable(adc->clk); - - writel(adc->prescale | S3C2410_ADCCON_PRSCEN, - adc->regs + S3C2410_ADCCON); - - dev_info(dev, "attached adc driver\n"); - - platform_set_drvdata(pdev, adc); - adc_dev = adc; - - return 0; - - err_clk: - clk_put(adc->clk); - - err_irq: - free_irq(adc->irq, adc); - - err_alloc: - kfree(adc); - return ret; -} - -static int __devexit s3c_adc_remove(struct platform_device *pdev) -{ - struct adc_device *adc = platform_get_drvdata(pdev); - - iounmap(adc->regs); - free_irq(adc->irq, adc); - clk_disable(adc->clk); - clk_put(adc->clk); - kfree(adc); - - return 0; -} - -#ifdef CONFIG_PM -static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct adc_device *adc = platform_get_drvdata(pdev); - u32 con; - - con = readl(adc->regs + S3C2410_ADCCON); - con |= S3C2410_ADCCON_STDBM; - writel(con, adc->regs + S3C2410_ADCCON); - - clk_disable(adc->clk); - - return 0; -} - -static int s3c_adc_resume(struct platform_device *pdev) -{ - struct adc_device *adc = platform_get_drvdata(pdev); - - clk_enable(adc->clk); - - writel(adc->prescale | S3C2410_ADCCON_PRSCEN, - adc->regs + S3C2410_ADCCON); - - return 0; -} - -#else -#define s3c_adc_suspend NULL -#define s3c_adc_resume NULL -#endif - -static struct platform_driver s3c_adc_driver = { - .driver = { - .name = "s3c24xx-adc", - .owner = THIS_MODULE, - }, - .probe = s3c_adc_probe, - .remove = __devexit_p(s3c_adc_remove), - .suspend = s3c_adc_suspend, - .resume = s3c_adc_resume, -}; - -static int __init adc_init(void) -{ - int ret; - - ret = platform_driver_register(&s3c_adc_driver); - if (ret) - printk(KERN_ERR "%s: failed to add adc driver\n", __func__); - - return ret; -} - -arch_initcall(adc_init); diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index faec4b8..e6c1229 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -74,6 +74,15 @@ config SAMSUNG_GPIO_EXTRA provides. This allows expanding the GPIO space for use with GPIO expanders. +# ADC driver + +config S3C_ADC + bool "ADC common driver support" + help + Core support for the ADC block found in the Samsung SoC systems + for drivers such as the touchscreen and hwmon to use to share + this resource. + # device definitions to compile in config S3C_DEV_HSMMC diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index aeb7e12..ee31038 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -20,6 +20,10 @@ obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o obj-$(CONFIG_SAMSUNG_IRQ_UART) += irq-uart.o obj-$(CONFIG_SAMSUNG_IRQ_VIC_TIMER) += irq-vic-timer.o +# ADC + +obj-$(CONFIG_S3C_ADC) += adc.o + # devices obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c new file mode 100644 index 0000000..a8843dd --- /dev/null +++ b/arch/arm/plat-samsung/adc.c @@ -0,0 +1,435 @@ +/* arch/arm/plat-samsung/adc.c + * + * Copyright (c) 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks , + * + * Samsung ADC device core + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* This driver is designed to control the usage of the ADC block between + * the touchscreen and any other drivers that may need to use it, such as + * the hwmon driver. + * + * Priority will be given to the touchscreen driver, but as this itself is + * rate limited it should not starve other requests which are processed in + * order that they are received. + * + * Each user registers to get a client block which uniquely identifies it + * and stores information such as the necessary functions to callback when + * action is required. + */ + +struct s3c_adc_client { + struct platform_device *pdev; + struct list_head pend; + wait_queue_head_t *wait; + + unsigned int nr_samples; + int result; + unsigned char is_ts; + unsigned char channel; + + void (*select_cb)(struct s3c_adc_client *c, unsigned selected); + void (*convert_cb)(struct s3c_adc_client *c, + unsigned val1, unsigned val2, + unsigned *samples_left); +}; + +struct adc_device { + struct platform_device *pdev; + struct platform_device *owner; + struct clk *clk; + struct s3c_adc_client *cur; + struct s3c_adc_client *ts_pend; + void __iomem *regs; + + unsigned int prescale; + + int irq; +}; + +static struct adc_device *adc_dev; + +static LIST_HEAD(adc_pending); + +#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg) + +static inline void s3c_adc_convert(struct adc_device *adc) +{ + unsigned con = readl(adc->regs + S3C2410_ADCCON); + + con |= S3C2410_ADCCON_ENABLE_START; + writel(con, adc->regs + S3C2410_ADCCON); +} + +static inline void s3c_adc_select(struct adc_device *adc, + struct s3c_adc_client *client) +{ + unsigned con = readl(adc->regs + S3C2410_ADCCON); + + client->select_cb(client, 1); + + con &= ~S3C2410_ADCCON_MUXMASK; + con &= ~S3C2410_ADCCON_STDBM; + con &= ~S3C2410_ADCCON_STARTMASK; + + if (!client->is_ts) + con |= S3C2410_ADCCON_SELMUX(client->channel); + + writel(con, adc->regs + S3C2410_ADCCON); +} + +static void s3c_adc_dbgshow(struct adc_device *adc) +{ + adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n", + readl(adc->regs + S3C2410_ADCCON), + readl(adc->regs + S3C2410_ADCTSC), + readl(adc->regs + S3C2410_ADCDLY)); +} + +static void s3c_adc_try(struct adc_device *adc) +{ + struct s3c_adc_client *next = adc->ts_pend; + + if (!next && !list_empty(&adc_pending)) { + next = list_first_entry(&adc_pending, + struct s3c_adc_client, pend); + list_del(&next->pend); + } else + adc->ts_pend = NULL; + + if (next) { + adc_dbg(adc, "new client is %p\n", next); + adc->cur = next; + s3c_adc_select(adc, next); + s3c_adc_convert(adc); + s3c_adc_dbgshow(adc); + } +} + +int s3c_adc_start(struct s3c_adc_client *client, + unsigned int channel, unsigned int nr_samples) +{ + struct adc_device *adc = adc_dev; + unsigned long flags; + + if (!adc) { + printk(KERN_ERR "%s: failed to find adc\n", __func__); + return -EINVAL; + } + + if (client->is_ts && adc->ts_pend) + return -EAGAIN; + + local_irq_save(flags); + + client->channel = channel; + client->nr_samples = nr_samples; + + if (client->is_ts) + adc->ts_pend = client; + else + list_add_tail(&client->pend, &adc_pending); + + if (!adc->cur) + s3c_adc_try(adc); + local_irq_restore(flags); + + return 0; +} +EXPORT_SYMBOL_GPL(s3c_adc_start); + +static void s3c_convert_done(struct s3c_adc_client *client, + unsigned v, unsigned u, unsigned *left) +{ + client->result = v; + wake_up(client->wait); +} + +int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + int ret; + + client->convert_cb = s3c_convert_done; + client->wait = &wake; + client->result = -1; + + ret = s3c_adc_start(client, ch, 1); + if (ret < 0) + goto err; + + ret = wait_event_timeout(wake, client->result >= 0, HZ / 2); + if (client->result < 0) { + ret = -ETIMEDOUT; + goto err; + } + + client->convert_cb = NULL; + return client->result; + +err: + return ret; +} +EXPORT_SYMBOL_GPL(s3c_adc_read); + +static void s3c_adc_default_select(struct s3c_adc_client *client, + unsigned select) +{ +} + +struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, + void (*select)(struct s3c_adc_client *client, + unsigned int selected), + void (*conv)(struct s3c_adc_client *client, + unsigned d0, unsigned d1, + unsigned *samples_left), + unsigned int is_ts) +{ + struct s3c_adc_client *client; + + WARN_ON(!pdev); + + if (!select) + select = s3c_adc_default_select; + + if (!pdev) + return ERR_PTR(-EINVAL); + + client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL); + if (!client) { + dev_err(&pdev->dev, "no memory for adc client\n"); + return ERR_PTR(-ENOMEM); + } + + client->pdev = pdev; + client->is_ts = is_ts; + client->select_cb = select; + client->convert_cb = conv; + + return client; +} +EXPORT_SYMBOL_GPL(s3c_adc_register); + +void s3c_adc_release(struct s3c_adc_client *client) +{ + /* We should really check that nothing is in progress. */ + if (adc_dev->cur == client) + adc_dev->cur = NULL; + if (adc_dev->ts_pend == client) + adc_dev->ts_pend = NULL; + else { + struct list_head *p, *n; + struct s3c_adc_client *tmp; + + list_for_each_safe(p, n, &adc_pending) { + tmp = list_entry(p, struct s3c_adc_client, pend); + if (tmp == client) + list_del(&tmp->pend); + } + } + + if (adc_dev->cur == NULL) + s3c_adc_try(adc_dev); + kfree(client); +} +EXPORT_SYMBOL_GPL(s3c_adc_release); + +static irqreturn_t s3c_adc_irq(int irq, void *pw) +{ + struct adc_device *adc = pw; + struct s3c_adc_client *client = adc->cur; + unsigned long flags; + unsigned data0, data1; + + if (!client) { + dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__); + return IRQ_HANDLED; + } + + data0 = readl(adc->regs + S3C2410_ADCDAT0); + data1 = readl(adc->regs + S3C2410_ADCDAT1); + adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1); + + client->nr_samples--; + + if (client->convert_cb) + (client->convert_cb)(client, data0 & 0x3ff, data1 & 0x3ff, + &client->nr_samples); + + if (client->nr_samples > 0) { + /* fire another conversion for this */ + + client->select_cb(client, 1); + s3c_adc_convert(adc); + } else { + local_irq_save(flags); + (client->select_cb)(client, 0); + adc->cur = NULL; + + s3c_adc_try(adc); + local_irq_restore(flags); + } + + return IRQ_HANDLED; +} + +static int s3c_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adc_device *adc; + struct resource *regs; + int ret; + + adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL); + if (adc == NULL) { + dev_err(dev, "failed to allocate adc_device\n"); + return -ENOMEM; + } + + adc->pdev = pdev; + adc->prescale = S3C2410_ADCCON_PRSCVL(49); + + adc->irq = platform_get_irq(pdev, 1); + if (adc->irq <= 0) { + dev_err(dev, "failed to get adc irq\n"); + ret = -ENOENT; + goto err_alloc; + } + + ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); + if (ret < 0) { + dev_err(dev, "failed to attach adc irq\n"); + goto err_alloc; + } + + adc->clk = clk_get(dev, "adc"); + if (IS_ERR(adc->clk)) { + dev_err(dev, "failed to get adc clock\n"); + ret = PTR_ERR(adc->clk); + goto err_irq; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "failed to find registers\n"); + ret = -ENXIO; + goto err_clk; + } + + adc->regs = ioremap(regs->start, resource_size(regs)); + if (!adc->regs) { + dev_err(dev, "failed to map registers\n"); + ret = -ENXIO; + goto err_clk; + } + + clk_enable(adc->clk); + + writel(adc->prescale | S3C2410_ADCCON_PRSCEN, + adc->regs + S3C2410_ADCCON); + + dev_info(dev, "attached adc driver\n"); + + platform_set_drvdata(pdev, adc); + adc_dev = adc; + + return 0; + + err_clk: + clk_put(adc->clk); + + err_irq: + free_irq(adc->irq, adc); + + err_alloc: + kfree(adc); + return ret; +} + +static int __devexit s3c_adc_remove(struct platform_device *pdev) +{ + struct adc_device *adc = platform_get_drvdata(pdev); + + iounmap(adc->regs); + free_irq(adc->irq, adc); + clk_disable(adc->clk); + clk_put(adc->clk); + kfree(adc); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct adc_device *adc = platform_get_drvdata(pdev); + u32 con; + + con = readl(adc->regs + S3C2410_ADCCON); + con |= S3C2410_ADCCON_STDBM; + writel(con, adc->regs + S3C2410_ADCCON); + + clk_disable(adc->clk); + + return 0; +} + +static int s3c_adc_resume(struct platform_device *pdev) +{ + struct adc_device *adc = platform_get_drvdata(pdev); + + clk_enable(adc->clk); + + writel(adc->prescale | S3C2410_ADCCON_PRSCEN, + adc->regs + S3C2410_ADCCON); + + return 0; +} + +#else +#define s3c_adc_suspend NULL +#define s3c_adc_resume NULL +#endif + +static struct platform_driver s3c_adc_driver = { + .driver = { + .name = "s3c24xx-adc", + .owner = THIS_MODULE, + }, + .probe = s3c_adc_probe, + .remove = __devexit_p(s3c_adc_remove), + .suspend = s3c_adc_suspend, + .resume = s3c_adc_resume, +}; + +static int __init adc_init(void) +{ + int ret; + + ret = platform_driver_register(&s3c_adc_driver); + if (ret) + printk(KERN_ERR "%s: failed to add adc driver\n", __func__); + + return ret; +} + +arch_initcall(adc_init); diff --git a/arch/arm/plat-samsung/include/plat/adc.h b/arch/arm/plat-samsung/include/plat/adc.h new file mode 100644 index 0000000..e8382c7 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/adc.h @@ -0,0 +1,35 @@ +/* arch/arm/plat-samsung/include/plat/adc.h + * + * Copyright (c) 2008 Simtec Electronics + * http://armlinux.simnte.co.uk/ + * Ben Dooks + * + * S3C ADC driver information + * + * 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 __ASM_PLAT_ADC_H +#define __ASM_PLAT_ADC_H __FILE__ + +struct s3c_adc_client; + +extern int s3c_adc_start(struct s3c_adc_client *client, + unsigned int channel, unsigned int nr_samples); + +extern int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch); + +extern struct s3c_adc_client * + s3c_adc_register(struct platform_device *pdev, + void (*select)(struct s3c_adc_client *client, + unsigned selected), + void (*conv)(struct s3c_adc_client *client, + unsigned d0, unsigned d1, + unsigned *samples_left), + unsigned int is_ts); + +extern void s3c_adc_release(struct s3c_adc_client *client); + +#endif /* __ASM_PLAT_ADC_H */ -- cgit v0.10.2 From bcedfa98d97e02e95bb023ab93675dbe63c508ef Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Thu, 14 Jan 2010 00:30:34 +0100 Subject: ARM: S3C64XX: Add S3C64XX support to the generic Samsung ADC driver Add S3C64XX support to the generic Samsung ADC driver Signed-off-by: Maurus Cuelenaere Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index d4cd3ab..5a35f24 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -42,6 +42,7 @@ #define S3C64XX_PA_FB (0x77100000) #define S3C64XX_PA_USB_HSOTG (0x7C000000) #define S3C64XX_PA_WATCHDOG (0x7E004000) +#define S3C64XX_PA_ADC (0x7E00B000) #define S3C64XX_PA_SYSCON (0x7E00F000) #define S3C64XX_PA_AC97 (0x7F001000) #define S3C64XX_PA_IIS0 (0x7F002000) diff --git a/arch/arm/plat-s3c/include/plat/regs-adc.h b/arch/arm/plat-s3c/include/plat/regs-adc.h index 4323ccc..f43c8da 100644 --- a/arch/arm/plat-s3c/include/plat/regs-adc.h +++ b/arch/arm/plat-s3c/include/plat/regs-adc.h @@ -19,6 +19,9 @@ #define S3C2410_ADCDLY S3C2410_ADCREG(0x08) #define S3C2410_ADCDAT0 S3C2410_ADCREG(0x0C) #define S3C2410_ADCDAT1 S3C2410_ADCREG(0x10) +#define S3C64XX_ADCUPDN S3C2410_ADCREG(0x14) +#define S3C64XX_ADCCLRINT S3C2410_ADCREG(0x18) +#define S3C64XX_ADCCLRINTPNDNUP S3C2410_ADCREG(0x20) /* ADCCON Register Bits */ diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile index b85b435..8060017 100644 --- a/arch/arm/plat-s3c64xx/Makefile +++ b/arch/arm/plat-s3c64xx/Makefile @@ -35,6 +35,10 @@ obj-$(CONFIG_PM) += irq-pm.o obj-$(CONFIG_S3C64XX_DMA) += dma.o +# ADC support + +obj-$(CONFIG_S3C_ADC) += dev-adc.o + # Device setup obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o diff --git a/arch/arm/plat-s3c64xx/dev-adc.c b/arch/arm/plat-s3c64xx/dev-adc.c new file mode 100644 index 0000000..fafef9b --- /dev/null +++ b/arch/arm/plat-s3c64xx/dev-adc.c @@ -0,0 +1,46 @@ +/* linux/arch/arm/plat-s3c64xx/dev-adc.c + * + * Copyright 2010 Maurus Cuelenaere + * + * S3C64xx series device definition for ADC device + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +static struct resource s3c_adc_resource[] = { + [0] = { + .start = S3C64XX_PA_ADC, + .end = S3C64XX_PA_ADC + SZ_256 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TC, + .end = IRQ_TC, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_ADC, + .end = IRQ_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device s3c_device_adc = { + .name = "s3c64xx-adc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_adc_resource), + .resource = s3c_adc_resource, +}; diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c index a8843dd..120b790 100644 --- a/arch/arm/plat-samsung/adc.c +++ b/arch/arm/plat-samsung/adc.c @@ -37,6 +37,11 @@ * action is required. */ +enum s3c_cpu_type { + TYPE_S3C24XX, + TYPE_S3C64XX +}; + struct s3c_adc_client { struct platform_device *pdev; struct list_head pend; @@ -262,7 +267,7 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) if (!client) { dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__); - return IRQ_HANDLED; + goto exit; } data0 = readl(adc->regs + S3C2410_ADCDAT0); @@ -289,6 +294,11 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) local_irq_restore(flags); } +exit: + if (platform_get_device_id(client->pdev)->driver_data == TYPE_S3C64XX) { + /* Clear ADC interrupt */ + writel(0, adc->regs + S3C64XX_ADCCLRINT); + } return IRQ_HANDLED; } @@ -410,9 +420,22 @@ static int s3c_adc_resume(struct platform_device *pdev) #define s3c_adc_resume NULL #endif +static struct platform_device_id s3c_adc_driver_ids[] = { + { + .name = "s3c24xx-adc", + .driver_data = TYPE_S3C24XX, + }, { + .name = "s3c64xx-adc", + .driver_data = TYPE_S3C64XX, + }, + { } +}; +MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids); + static struct platform_driver s3c_adc_driver = { + .id_table = s3c_adc_driver_ids, .driver = { - .name = "s3c24xx-adc", + .name = "s3c-adc", .owner = THIS_MODULE, }, .probe = s3c_adc_probe, -- cgit v0.10.2 From a7c9194cdc9a858bbf5a4cdae289e28dea149aeb Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 29 Dec 2009 14:40:23 +0000 Subject: ARM: S3C64XX: Add SROM register definitions This introduces a platform file for s3c64xx that defines the SROM unit registers that control chip select characteristics. Signed-off-by: Andy Green Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index c33810e..0d60440 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -17,6 +17,18 @@ #include +/* + * Post-mux Chip Select Regions Xm0CSn_ + * These may be used by SROM, NAND or CF depending on settings + */ + +#define S3C64XX_PA_XM0CSN0 (0x10000000) +#define S3C64XX_PA_XM0CSN1 (0x18000000) +#define S3C64XX_PA_XM0CSN2 (0x20000000) +#define S3C64XX_PA_XM0CSN3 (0x28000000) +#define S3C64XX_PA_XM0CSN4 (0x30000000) +#define S3C64XX_PA_XM0CSN5 (0x38000000) + /* HSMMC units */ #define S3C64XX_PA_HSMMC(x) (0x7C200000 + ((x) * 0x100000)) #define S3C64XX_PA_HSMMC0 S3C64XX_PA_HSMMC(0) diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-srom.h b/arch/arm/plat-s3c64xx/include/plat/regs-srom.h new file mode 100644 index 0000000..756731b --- /dev/null +++ b/arch/arm/plat-s3c64xx/include/plat/regs-srom.h @@ -0,0 +1,59 @@ +/* arch/arm/plat-s3c64xx/include/plat/regs-srom.h + * + * Copyright 2009 Andy Green + * + * S3C64XX SROM definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __PLAT_REGS_SROM_H +#define __PLAT_REGS_SROM_H __FILE__ + +#define S3C64XX_SROMREG(x) (S3C_VA_MEM + (x)) + +#define S3C64XX_SROM_BW S3C64XX_SROMREG(0) +#define S3C64XX_SROM_BC0 S3C64XX_SROMREG(4) +#define S3C64XX_SROM_BC1 S3C64XX_SROMREG(8) +#define S3C64XX_SROM_BC2 S3C64XX_SROMREG(0xc) +#define S3C64XX_SROM_BC3 S3C64XX_SROMREG(0x10) +#define S3C64XX_SROM_BC4 S3C64XX_SROMREG(0x14) +#define S3C64XX_SROM_BC5 S3C64XX_SROMREG(0x18) + +/* + * one register BW holds 5 x 4-bit packed settings for NCS0 - NCS4 + */ + +#define S3C64XX_SROM_BW__DATAWIDTH__SHIFT 0 +#define S3C64XX_SROM_BW__WAITENABLE__SHIFT 2 +#define S3C64XX_SROM_BW__BYTEENABLE__SHIFT 3 +#define S3C64XX_SROM_BW__CS_MASK 0xf + +#define S3C64XX_SROM_BW__NCS0__SHIFT 0 +#define S3C64XX_SROM_BW__NCS1__SHIFT 4 +#define S3C64XX_SROM_BW__NCS2__SHIFT 8 +#define S3C64XX_SROM_BW__NCS3__SHIFT 0xc +#define S3C64XX_SROM_BW__NCS4__SHIFT 0x10 + +/* + * applies to same to BCS0 - BCS4 + */ + +#define S3C64XX_SROM_BCX__PMC__SHIFT 0 +#define S3C64XX_SROM_BCX__PMC__MASK 3 +#define S3C64XX_SROM_BCX__TACP__SHIFT 4 +#define S3C64XX_SROM_BCX__TACP__MASK 0xf +#define S3C64XX_SROM_BCX__TCAH__SHIFT 8 +#define S3C64XX_SROM_BCX__TCAH__MASK 0xf +#define S3C64XX_SROM_BCX__TCOH__SHIFT 12 +#define S3C64XX_SROM_BCX__TCOH__MASK 0xf +#define S3C64XX_SROM_BCX__TACC__SHIFT 16 +#define S3C64XX_SROM_BCX__TACC__MASK 0x1f +#define S3C64XX_SROM_BCX__TCOS__SHIFT 24 +#define S3C64XX_SROM_BCX__TCOS__MASK 0xf +#define S3C64XX_SROM_BCX__TACS__SHIFT 28 +#define S3C64XX_SROM_BCX__TACS__MASK 0xf + +#endif /* _PLAT_REGS_SROM_H */ -- cgit v0.10.2 From f01fdac005d44e6c36af5b5eb841c4804570f0ad Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 29 Dec 2009 14:40:36 +0000 Subject: ARM: SMDK6410: Add nC1 chipselect SROM setup Since the mach-smdk6410.c file claims it can start up the smsc9115 ethernet device on the board, it should take care about configuring the necessary chip select nCS1 so it can see the chip. The select defaults to 8-bit mode so without config the Ethernet doesn't work. This patch uses the new SROM definitions to set nCS1 to the state found in the Samsung U-Boot port for 6410. It may be more conservative that it needs to be since those settings were marked as for CS8900A also using this chip select. But this change is enough to get the ethernet working when booted with Qi. Signed-off-by: Andy Green Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c6410/mach-smdk6410.c b/arch/arm/mach-s3c6410/mach-smdk6410.c index 8969fe7..e7bae53 100644 --- a/arch/arm/mach-s3c6410/mach-smdk6410.c +++ b/arch/arm/mach-s3c6410/mach-smdk6410.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -156,8 +157,8 @@ static struct s3c_fb_platdata smdk6410_lcd_pdata __initdata = { static struct resource smdk6410_smsc911x_resources[] = { [0] = { - .start = 0x18000000, - .end = 0x18000000 + SZ_64K - 1, + .start = S3C64XX_PA_XM0CSN1, + .end = S3C64XX_PA_XM0CSN1 + SZ_64K - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -430,10 +431,32 @@ static void __init smdk6410_map_io(void) static void __init smdk6410_machine_init(void) { + u32 cs1; + s3c_i2c0_set_platdata(NULL); s3c_i2c1_set_platdata(NULL); s3c_fb_set_platdata(&smdk6410_lcd_pdata); + /* configure nCS1 width to 16 bits */ + + cs1 = __raw_readl(S3C64XX_SROM_BW) & + ~(S3C64XX_SROM_BW__CS_MASK << S3C64XX_SROM_BW__NCS1__SHIFT); + cs1 |= ((1 << S3C64XX_SROM_BW__DATAWIDTH__SHIFT) | + (1 << S3C64XX_SROM_BW__WAITENABLE__SHIFT) | + (1 << S3C64XX_SROM_BW__BYTEENABLE__SHIFT)) << + S3C64XX_SROM_BW__NCS1__SHIFT; + __raw_writel(cs1, S3C64XX_SROM_BW); + + /* set timing for nCS1 suitable for ethernet chip */ + + __raw_writel((0 << S3C64XX_SROM_BCX__PMC__SHIFT) | + (6 << S3C64XX_SROM_BCX__TACP__SHIFT) | + (4 << S3C64XX_SROM_BCX__TCAH__SHIFT) | + (1 << S3C64XX_SROM_BCX__TCOH__SHIFT) | + (0xe << S3C64XX_SROM_BCX__TACC__SHIFT) | + (4 << S3C64XX_SROM_BCX__TCOS__SHIFT) | + (0 << S3C64XX_SROM_BCX__TACS__SHIFT), S3C64XX_SROM_BC1); + gpio_request(S3C64XX_GPN(5), "LCD power"); gpio_request(S3C64XX_GPF(13), "LCD power"); gpio_request(S3C64XX_GPF(15), "LCD power"); -- cgit v0.10.2 From 810f613c03a1614c71a2b27859e91ae155bd659f Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 29 Dec 2009 14:40:30 +0000 Subject: ARM: S3C64XX: Add virtual mapping for S3C64XX SROM area This adds the physical address of the SROM unit, and applies a virtual mapping for it to the S3C_VA_MEM slot. Signed-off-by: Andy Green Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index 0d60440..3e17adf 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -50,6 +50,8 @@ #define S3C_VA_UART2 S3C_VA_UARTx(2) #define S3C_VA_UART3 S3C_VA_UARTx(3) +#define S3C64XX_PA_SROM (0x70000000) + #define S3C64XX_PA_NAND (0x70200000) #define S3C64XX_PA_FB (0x77100000) #define S3C64XX_PA_USB_HSOTG (0x7C000000) diff --git a/arch/arm/plat-s3c64xx/cpu.c b/arch/arm/plat-s3c64xx/cpu.c index c0e6f2a..bc7ca18 100644 --- a/arch/arm/plat-s3c64xx/cpu.c +++ b/arch/arm/plat-s3c64xx/cpu.c @@ -73,6 +73,11 @@ static struct map_desc s3c_iodesc[] __initdata = { .length = SZ_4K, .type = MT_DEVICE, }, { + .virtual = (unsigned long)S3C_VA_MEM, + .pfn = __phys_to_pfn(S3C64XX_PA_SROM), + .length = SZ_4K, + .type = MT_DEVICE, + }, { .virtual = (unsigned long)(S3C_VA_UART + UART_OFFS), .pfn = __phys_to_pfn(S3C_PA_UART), .length = SZ_4K, -- cgit v0.10.2 From a4e946947b966d018f3f712d5c7ca2932c2aadd2 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 29 Dec 2009 14:40:43 +0000 Subject: ARM: SMDK6410: Add comments on ethernet connectivity LAN9115 Ethernet only works on SMDK6410 if certain switches are set in specific ways. Document the requirement. Signed-off-by: Andy Green Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c6410/mach-smdk6410.c b/arch/arm/mach-s3c6410/mach-smdk6410.c index e7bae53..eb5de98 100644 --- a/arch/arm/mach-s3c6410/mach-smdk6410.c +++ b/arch/arm/mach-s3c6410/mach-smdk6410.c @@ -155,6 +155,16 @@ static struct s3c_fb_platdata smdk6410_lcd_pdata __initdata = { .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, }; +/* + * Configuring Ethernet on SMDK6410 + * + * Both CS8900A and LAN9115 chips share one chip select mediated by CFG6. + * The constant address below corresponds to nCS1 + * + * 1) Set CFGB2 p3 ON others off, no other CFGB selects "ethernet" + * 2) CFG6 needs to be switched to "LAN9115" side + */ + static struct resource smdk6410_smsc911x_resources[] = { [0] = { .start = S3C64XX_PA_XM0CSN1, -- cgit v0.10.2 From a0de298a0eee7f9004a8e512c12c7b40d66c9934 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 18 Jan 2010 13:24:01 +0900 Subject: ARM: S5PC1XX: Register pd0 and hd0 clocks, remove unecessary dummy_enable The clk_hd0 and clk_pd0 where given a local dummy_enable call due to not being registered with the clock code, which they really should have been. Register these two clocks and remove the clk_dummy_enable as this is done by the default clock registration code. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s5pc1xx/clock.c b/arch/arm/plat-s5pc1xx/clock.c index aec0305..387f231 100644 --- a/arch/arm/plat-s5pc1xx/clock.c +++ b/arch/arm/plat-s5pc1xx/clock.c @@ -64,18 +64,12 @@ struct clk clk_54m = { .rate = 54000000, }; -static int clk_dummy_enable(struct clk *clk, int enable) -{ - return 0; -} - struct clk clk_hd0 = { .name = "hclkd0", .id = -1, .rate = 0, .parent = NULL, .ctrlbit = 0, - .enable = clk_dummy_enable, .ops = &clk_ops_def_setrate, }; @@ -86,7 +80,6 @@ struct clk clk_pd0 = { .parent = NULL, .ctrlbit = 0, .ops = &clk_ops_def_setrate, - .enable = clk_dummy_enable, }; static int s5pc1xx_clk_gate(void __iomem *reg, struct clk *clk, int enable) @@ -680,6 +673,8 @@ static struct clk s5pc100_init_clocks[] = { static struct clk *clks[] __initdata = { &clk_ext, &clk_epll, + &clk_pd0, + &clk_hd0, &clk_27m, &clk_48m, &clk_54m, -- cgit v0.10.2 From b8792dbff6ed93c5a77f71917d4a0c5efa12eee1 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Mon, 18 Jan 2010 14:30:16 +0900 Subject: ARM: SAMSUNG: Add check to ensure that clksrc_clk does have multiple clock sources Adds a additional check in s3c_set_clksrc function to ensure that the clksrc_clk does have a choice of multiple clock sources. In addition to this, a check is added to ensure that a parent is assigned to the clksrc_clk in case it does not have a choice of parent clocks. Signed-off-by: Thomas Abraham Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c index 33c633a..98c6b88 100644 --- a/arch/arm/plat-samsung/clock-clksrc.c +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -129,11 +129,16 @@ void __init_or_cpufreq s3c_set_clksrc(struct clksrc_clk *clk, bool announce) { struct clksrc_sources *srcs = clk->sources; u32 mask = bit_mask(clk->reg_src.shift, clk->reg_src.size); - u32 clksrc = 0; + u32 clksrc; - if (clk->reg_src.reg) - clksrc = __raw_readl(clk->reg_src.reg); + if (!clk->reg_src.reg) { + if (!clk->clk.parent) + printk(KERN_ERR "%s: no parent clock specified\n", + clk->clk.name); + return; + } + clksrc = __raw_readl(clk->reg_src.reg); clksrc &= mask; clksrc >>= clk->reg_src.shift; -- cgit v0.10.2 From f3b464cca94c4a8f54fbc11ec5af8b143fd1750b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 18 Jan 2010 15:37:25 +0900 Subject: ARM: SAMSUNG: Fix clksrc-clk's checks for bad register settings The WARN_ON() was only checking the first clock in the array, instead of being executed for each clksrc clock being registered. Since this is an array of clocks, WARN_ON() does not provide a lot of useful information about the problem, so change to using printk(KERN_ERR) to report the problem to the console. As a note, we still try and register the clock even if these problems are present just in case and to avoid changing the behaviour of the registration process. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c index 98c6b88..656c70e 100644 --- a/arch/arm/plat-samsung/clock-clksrc.c +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -177,9 +177,11 @@ void __init s3c_register_clksrc(struct clksrc_clk *clksrc, int size) { int ret; - WARN_ON(!clksrc->reg_div.reg && !clksrc->reg_src.reg); - for (; size > 0; size--, clksrc++) { + if (!clksrc->reg_div.reg && !clksrc->reg_src.reg) + printk(KERN_ERR "%s: clock %s has no registers set\n", + __func__, clksrc->clk.name); + /* fill in the default functions */ if (!clksrc->clk.ops) { -- cgit v0.10.2 From f9e011b6b305d38445bbd4a1e7a8814e056de37b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 18 Jan 2010 15:57:42 +0900 Subject: ARM: SAMSUNG: Fix bug in clksrc-clk round_rate call. The call has been assuming all clksrc-clks' divider size is 4 bits, but this may not be the case anymore. Use the reg_div.size parameter to calculate the maximum value it can take and check against that. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c index 656c70e..ae8b850 100644 --- a/arch/arm/plat-samsung/clock-clksrc.c +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -60,7 +60,7 @@ static int s3c_setrate_clksrc(struct clk *clk, unsigned long rate) rate = clk_round_rate(clk, rate); div = clk_get_rate(clk->parent) / rate; - if (div > 16) + if (div > (1 << sclk->reg_div.size)) return -EINVAL; val = __raw_readl(reg); @@ -102,7 +102,9 @@ static int s3c_setparent_clksrc(struct clk *clk, struct clk *parent) static unsigned long s3c_roundrate_clksrc(struct clk *clk, unsigned long rate) { + struct clksrc_clk *sclk = to_clksrc(clk); unsigned long parent_rate = clk_get_rate(clk->parent); + int max_div = 1 << sclk->reg_div.size; int div; if (rate >= parent_rate) @@ -114,8 +116,8 @@ static unsigned long s3c_roundrate_clksrc(struct clk *clk, if (div == 0) div = 1; - if (div > 16) - div = 16; + if (div > max_div) + div = max_div; rate = parent_rate / div; } -- cgit v0.10.2 From 10f9f743f661d200a7a0006e65688cf28c2f01d9 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Mon, 18 Jan 2010 16:15:07 +0900 Subject: ARM: S3C64XX: SPI: Add SPI controller register base Added SPI controller register base. Signed-off-by: Jassi Brar Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h index 3e17adf..801c1c0 100644 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ b/arch/arm/mach-s3c6400/include/mach/map.h @@ -64,6 +64,8 @@ #define S3C64XX_PA_IIS1 (0x7F003000) #define S3C64XX_PA_TIMER (0x7F006000) #define S3C64XX_PA_IIC0 (0x7F004000) +#define S3C64XX_PA_SPI0 (0x7F00B000) +#define S3C64XX_PA_SPI1 (0x7F00C000) #define S3C64XX_PA_PCM0 (0x7F009000) #define S3C64XX_PA_PCM1 (0x7F00A000) #define S3C64XX_PA_IISV4 (0x7F00D000) -- cgit v0.10.2 From 87315a802a8aab9d986858fcf2230726c1b5c598 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Mon, 18 Jan 2010 16:15:08 +0900 Subject: ARM: S3C64XX: SPI: Define SPI-48M clock sources Defined special 48MHz clock sources for SPI-0,1. Signed-off-by: Jassi Brar Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c index ae5883c..2989c3a 100644 --- a/arch/arm/plat-s3c64xx/clock.c +++ b/arch/arm/plat-s3c64xx/clock.c @@ -141,6 +141,18 @@ static struct clk init_clocks_disable[] = { .enable = s3c64xx_pclk_ctrl, .ctrlbit = S3C_CLKCON_PCLK_SPI1, }, { + .name = "spi_48m", + .id = 0, + .parent = &clk_48m, + .enable = s3c64xx_sclk_ctrl, + .ctrlbit = S3C_CLKCON_SCLK_SPI0_48, + }, { + .name = "spi_48m", + .id = 1, + .parent = &clk_48m, + .enable = s3c64xx_sclk_ctrl, + .ctrlbit = S3C_CLKCON_SCLK_SPI1_48, + }, { .name = "48m", .id = 0, .parent = &clk_48m, -- cgit v0.10.2 From 398ccccb770e6ed3e138925404f0746cbe57c5a1 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Mon, 18 Jan 2010 17:45:52 +0900 Subject: ARM: S3C64XX: SPI: Header for passing platform data We need a way to pass controller specific information to the SPI driver. For that purpose new headers are made. SPI Controller is assumed 'type-s3c64xx' and can be defined for newer SoCs. Hence, that part is placed under plat-samsung to be shared across newer SoCs. SoC specific part - spi source clocks, will be placed under plat-/include/plat/ Signed-off-by: Jassi Brar Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/include/plat/spi-clocks.h b/arch/arm/plat-s3c64xx/include/plat/spi-clocks.h new file mode 100644 index 0000000..524bdae --- /dev/null +++ b/arch/arm/plat-s3c64xx/include/plat/spi-clocks.h @@ -0,0 +1,18 @@ +/* linux/arch/arm/plat-s3c64xx/include/plat/spi-clocks.h + * + * Copyright (C) 2009 Samsung Electronics Ltd. + * Jaswinder Singh + * + * 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 __S3C64XX_PLAT_SPI_CLKS_H +#define __S3C64XX_PLAT_SPI_CLKS_H __FILE__ + +#define S3C64XX_SPI_SRCCLK_PCLK 0 +#define S3C64XX_SPI_SRCCLK_SPIBUS 1 +#define S3C64XX_SPI_SRCCLK_48M 2 + +#endif /* __S3C64XX_PLAT_SPI_CLKS_H */ diff --git a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h new file mode 100644 index 0000000..d177241 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h @@ -0,0 +1,67 @@ +/* linux/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h + * + * Copyright (C) 2009 Samsung Electronics Ltd. + * Jaswinder Singh + * + * 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 __S3C64XX_PLAT_SPI_H +#define __S3C64XX_PLAT_SPI_H + +/** + * struct s3c64xx_spi_csinfo - ChipSelect description + * @fb_delay: Slave specific feedback delay. + * Refer to FB_CLK_SEL register definition in SPI chapter. + * @line: Custom 'identity' of the CS line. + * @set_level: CS line control. + * + * This is per SPI-Slave Chipselect information. + * Allocate and initialize one in machine init code and make the + * spi_board_info.controller_data point to it. + */ +struct s3c64xx_spi_csinfo { + u8 fb_delay; + unsigned line; + void (*set_level)(unsigned line_id, int lvl); +}; + +/** + * struct s3c64xx_spi_info - SPI Controller defining structure + * @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field. + * @src_clk_name: Platform name of the corresponding clock. + * @num_cs: Number of CS this controller emulates. + * @cfg_gpio: Configure pins for this SPI controller. + * @fifo_lvl_mask: All tx fifo_lvl fields start at offset-6 + * @rx_lvl_offset: Depends on tx fifo_lvl field and bus number + * @high_speed: If the controller supports HIGH_SPEED_EN bit + */ +struct s3c64xx_spi_info { + int src_clk_nr; + char *src_clk_name; + + int num_cs; + + int (*cfg_gpio)(struct platform_device *pdev); + + /* Following two fields are for future compatibility */ + int fifo_lvl_mask; + int rx_lvl_offset; + int high_speed; +}; + +/** + * s3c64xx_spi_set_info - SPI Controller configure callback by the board + * initialization code. + * @cntrlr: SPI controller number the configuration is for. + * @src_clk_nr: Clock the SPI controller is to use to generate SPI clocks. + * @num_cs: Number of elements in the 'cs' array. + * + * Call this from machine init code for each SPI Controller that + * has some chips attached to it. + */ +extern void s3c64xx_spi_set_info(int cntrlr, int src_clk_nr, int num_cs); + +#endif /* __S3C64XX_PLAT_SPI_H */ -- cgit v0.10.2 From 6a2b4111643127a28944d673995810a82582c5a1 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Mon, 18 Jan 2010 17:32:02 +0900 Subject: ARM: S3C64XX: SPI: Define SPI controller devices Platform devices for SPI Controller of S3C64XX are defined and exported for machines to include. Also, controller setup helper functions are defined for machine code to set runtime configuration of the controller and the bus. Signed-off-by: Jassi Brar Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h index 8a4153b..f4a7e57 100644 --- a/arch/arm/plat-s3c/include/plat/devs.h +++ b/arch/arm/plat-s3c/include/plat/devs.h @@ -29,6 +29,9 @@ extern struct platform_device s3c64xx_device_iis0; extern struct platform_device s3c64xx_device_iis1; extern struct platform_device s3c64xx_device_iisv4; +extern struct platform_device s3c64xx_device_spi0; +extern struct platform_device s3c64xx_device_spi1; + extern struct platform_device s3c64xx_device_pcm0; extern struct platform_device s3c64xx_device_pcm1; diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile index b241f36..80255a5 100644 --- a/arch/arm/plat-s3c64xx/Makefile +++ b/arch/arm/plat-s3c64xx/Makefile @@ -47,3 +47,4 @@ obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o obj-$(CONFIG_S3C64XX_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o obj-$(CONFIG_SND_S3C24XX_SOC) += dev-audio.o +obj-$(CONFIG_SPI_S3C64XX) += dev-spi.o diff --git a/arch/arm/plat-s3c64xx/dev-spi.c b/arch/arm/plat-s3c64xx/dev-spi.c new file mode 100644 index 0000000..6b6d7af --- /dev/null +++ b/arch/arm/plat-s3c64xx/dev-spi.c @@ -0,0 +1,180 @@ +/* linux/arch/arm/plat-s3c64xx/dev-spi.c + * + * Copyright (C) 2009 Samsung Electronics Ltd. + * Jaswinder Singh + * + * 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 + +static char *spi_src_clks[] = { + [S3C64XX_SPI_SRCCLK_PCLK] = "pclk", + [S3C64XX_SPI_SRCCLK_SPIBUS] = "spi-bus", + [S3C64XX_SPI_SRCCLK_48M] = "spi_48m", +}; + +/* SPI Controller platform_devices */ + +/* Since we emulate multi-cs capability, we do not touch the GPC-3,7. + * The emulated CS is toggled by board specific mechanism, as it can + * be either some immediate GPIO or some signal out of some other + * chip in between ... or some yet another way. + * We simply do not assume anything about CS. + */ +static int s3c64xx_spi_cfg_gpio(struct platform_device *pdev) +{ + switch (pdev->id) { + case 0: + s3c_gpio_cfgpin(S3C64XX_GPC(0), S3C64XX_GPC0_SPI_MISO0); + s3c_gpio_cfgpin(S3C64XX_GPC(1), S3C64XX_GPC1_SPI_CLKO); + s3c_gpio_cfgpin(S3C64XX_GPC(2), S3C64XX_GPC2_SPI_MOSIO); + s3c_gpio_setpull(S3C64XX_GPC(0), S3C_GPIO_PULL_UP); + s3c_gpio_setpull(S3C64XX_GPC(1), S3C_GPIO_PULL_UP); + s3c_gpio_setpull(S3C64XX_GPC(2), S3C_GPIO_PULL_UP); + break; + + case 1: + s3c_gpio_cfgpin(S3C64XX_GPC(4), S3C64XX_GPC4_SPI_MISO1); + s3c_gpio_cfgpin(S3C64XX_GPC(5), S3C64XX_GPC5_SPI_CLK1); + s3c_gpio_cfgpin(S3C64XX_GPC(6), S3C64XX_GPC6_SPI_MOSI1); + s3c_gpio_setpull(S3C64XX_GPC(4), S3C_GPIO_PULL_UP); + s3c_gpio_setpull(S3C64XX_GPC(5), S3C_GPIO_PULL_UP); + s3c_gpio_setpull(S3C64XX_GPC(6), S3C_GPIO_PULL_UP); + break; + + default: + dev_err(&pdev->dev, "Invalid SPI Controller number!"); + return -EINVAL; + } + + return 0; +} + +static struct resource s3c64xx_spi0_resource[] = { + [0] = { + .start = S3C64XX_PA_SPI0, + .end = S3C64XX_PA_SPI0 + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = DMACH_SPI0_TX, + .end = DMACH_SPI0_TX, + .flags = IORESOURCE_DMA, + }, + [2] = { + .start = DMACH_SPI0_RX, + .end = DMACH_SPI0_RX, + .flags = IORESOURCE_DMA, + }, + [3] = { + .start = IRQ_SPI0, + .end = IRQ_SPI0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct s3c64xx_spi_info s3c64xx_spi0_pdata = { + .cfg_gpio = s3c64xx_spi_cfg_gpio, + .fifo_lvl_mask = 0x7f, + .rx_lvl_offset = 13, +}; + +static u64 spi_dmamask = DMA_BIT_MASK(32); + +struct platform_device s3c64xx_device_spi0 = { + .name = "s3c64xx-spi", + .id = 0, + .num_resources = ARRAY_SIZE(s3c64xx_spi0_resource), + .resource = s3c64xx_spi0_resource, + .dev = { + .dma_mask = &spi_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &s3c64xx_spi0_pdata, + }, +}; +EXPORT_SYMBOL(s3c64xx_device_spi0); + +static struct resource s3c64xx_spi1_resource[] = { + [0] = { + .start = S3C64XX_PA_SPI1, + .end = S3C64XX_PA_SPI1 + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = DMACH_SPI1_TX, + .end = DMACH_SPI1_TX, + .flags = IORESOURCE_DMA, + }, + [2] = { + .start = DMACH_SPI1_RX, + .end = DMACH_SPI1_RX, + .flags = IORESOURCE_DMA, + }, + [3] = { + .start = IRQ_SPI1, + .end = IRQ_SPI1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct s3c64xx_spi_info s3c64xx_spi1_pdata = { + .cfg_gpio = s3c64xx_spi_cfg_gpio, + .fifo_lvl_mask = 0x7f, + .rx_lvl_offset = 13, +}; + +struct platform_device s3c64xx_device_spi1 = { + .name = "s3c64xx-spi", + .id = 1, + .num_resources = ARRAY_SIZE(s3c64xx_spi1_resource), + .resource = s3c64xx_spi1_resource, + .dev = { + .dma_mask = &spi_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &s3c64xx_spi1_pdata, + }, +}; +EXPORT_SYMBOL(s3c64xx_device_spi1); + +void __init s3c64xx_spi_set_info(int cntrlr, int src_clk_nr, int num_cs) +{ + /* Reject invalid configuration */ + if (!num_cs || src_clk_nr < 0 + || src_clk_nr > S3C64XX_SPI_SRCCLK_48M) { + printk(KERN_ERR "%s: Invalid SPI configuration\n", __func__); + return; + } + + switch (cntrlr) { + case 0: + s3c64xx_spi0_pdata.num_cs = num_cs; + s3c64xx_spi0_pdata.src_clk_nr = src_clk_nr; + s3c64xx_spi0_pdata.src_clk_name = spi_src_clks[src_clk_nr]; + break; + case 1: + s3c64xx_spi1_pdata.num_cs = num_cs; + s3c64xx_spi1_pdata.src_clk_nr = src_clk_nr; + s3c64xx_spi1_pdata.src_clk_name = spi_src_clks[src_clk_nr]; + break; + default: + printk(KERN_ERR "%s: Invalid SPI controller(%d)\n", + __func__, cntrlr); + return; + } +} -- cgit v0.10.2 From 0a6361316d6493b9dcbdda9a6a4586c3ccbfd008 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 12 Jan 2010 15:15:28 +0900 Subject: ARM: SAMSUNG: Make UART device code common Move s3c24xx_uart_devs, s3c24xx_uart_src and the platform devices to a common entry in plat-samsung since they are the same in all the current implementations. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index 7f686a3..38c358c 100644 --- a/arch/arm/plat-s3c24xx/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c @@ -112,34 +112,6 @@ struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = { }, }; -/* yart devices */ - -static struct platform_device s3c24xx_uart_device0 = { - .id = 0, -}; - -static struct platform_device s3c24xx_uart_device1 = { - .id = 1, -}; - -static struct platform_device s3c24xx_uart_device2 = { - .id = 2, -}; - -static struct platform_device s3c24xx_uart_device3 = { - .id = 3, -}; - -struct platform_device *s3c24xx_uart_src[4] = { - &s3c24xx_uart_device0, - &s3c24xx_uart_device1, - &s3c24xx_uart_device2, - &s3c24xx_uart_device3, -}; - -struct platform_device *s3c24xx_uart_devs[4] = { -}; - /* LCD Controller */ static struct resource s3c_lcd_resource[] = { diff --git a/arch/arm/plat-s3c64xx/dev-uart.c b/arch/arm/plat-s3c64xx/dev-uart.c index 62c11a6..f797f74 100644 --- a/arch/arm/plat-s3c64xx/dev-uart.c +++ b/arch/arm/plat-s3c64xx/dev-uart.c @@ -145,32 +145,3 @@ struct s3c24xx_uart_resources s3c64xx_uart_resources[] __initdata = { .nr_resources = ARRAY_SIZE(s3c64xx_uart3_resource), }, }; - -/* uart devices */ - -static struct platform_device s3c24xx_uart_device0 = { - .id = 0, -}; - -static struct platform_device s3c24xx_uart_device1 = { - .id = 1, -}; - -static struct platform_device s3c24xx_uart_device2 = { - .id = 2, -}; - -static struct platform_device s3c24xx_uart_device3 = { - .id = 3, -}; - -struct platform_device *s3c24xx_uart_src[4] = { - &s3c24xx_uart_device0, - &s3c24xx_uart_device1, - &s3c24xx_uart_device2, - &s3c24xx_uart_device3, -}; - -struct platform_device *s3c24xx_uart_devs[4] = { -}; - diff --git a/arch/arm/plat-s5p/dev-uart.c b/arch/arm/plat-s5p/dev-uart.c index af726f4..23c7531 100644 --- a/arch/arm/plat-s5p/dev-uart.c +++ b/arch/arm/plat-s5p/dev-uart.c @@ -135,31 +135,3 @@ struct s3c24xx_uart_resources s5p_uart_resources[] __initdata = { .nr_resources = ARRAY_SIZE(s5p_uart3_resource), }, }; - -/* uart devices */ - -static struct platform_device s5p_uart_device0 = { - .id = 0, -}; - -static struct platform_device s5p_uart_device1 = { - .id = 1, -}; - -static struct platform_device s5p_uart_device2 = { - .id = 2, -}; - -static struct platform_device s5p_uart_device3 = { - .id = 3, -}; - -struct platform_device *s3c24xx_uart_src[4] = { - &s5p_uart_device0, - &s5p_uart_device1, - &s5p_uart_device2, - &s5p_uart_device3, -}; - -struct platform_device *s3c24xx_uart_devs[4] = { -}; diff --git a/arch/arm/plat-s5pc1xx/dev-uart.c b/arch/arm/plat-s5pc1xx/dev-uart.c index f749bc5..586c95c 100644 --- a/arch/arm/plat-s5pc1xx/dev-uart.c +++ b/arch/arm/plat-s5pc1xx/dev-uart.c @@ -143,32 +143,3 @@ struct s3c24xx_uart_resources s5pc1xx_uart_resources[] __initdata = { .nr_resources = ARRAY_SIZE(s5pc1xx_uart3_resource), }, }; - -/* uart devices */ - -static struct platform_device s3c24xx_uart_device0 = { - .id = 0, -}; - -static struct platform_device s3c24xx_uart_device1 = { - .id = 1, -}; - -static struct platform_device s3c24xx_uart_device2 = { - .id = 2, -}; - -static struct platform_device s3c24xx_uart_device3 = { - .id = 3, -}; - -struct platform_device *s3c24xx_uart_src[4] = { - &s3c24xx_uart_device0, - &s3c24xx_uart_device1, - &s3c24xx_uart_device2, - &s3c24xx_uart_device3, -}; - -struct platform_device *s3c24xx_uart_devs[4] = { -}; - diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index aeb7e12..f834e42 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_S3C_DEV_HSMMC2) += dev-hsmmc2.o obj-y += dev-i2c0.o obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o +obj-y += dev-uart.o obj-$(CONFIG_S3C_DEV_USB_HOST) += dev-usb.o obj-$(CONFIG_S3C_DEV_USB_HSOTG) += dev-usb-hsotg.o obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o diff --git a/arch/arm/plat-samsung/dev-uart.c b/arch/arm/plat-samsung/dev-uart.c new file mode 100644 index 0000000..3776cd9 --- /dev/null +++ b/arch/arm/plat-samsung/dev-uart.c @@ -0,0 +1,44 @@ +/* linux/arch/arm/plat-samsung/dev-uart.c + * originally from arch/arm/plat-s3c24xx/devs.c + *x + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * Base S3C24XX platform device definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * +*/ + +#include +#include + +/* uart devices */ + +static struct platform_device s3c24xx_uart_device0 = { + .id = 0, +}; + +static struct platform_device s3c24xx_uart_device1 = { + .id = 1, +}; + +static struct platform_device s3c24xx_uart_device2 = { + .id = 2, +}; + +static struct platform_device s3c24xx_uart_device3 = { + .id = 3, +}; + +struct platform_device *s3c24xx_uart_src[4] = { + &s3c24xx_uart_device0, + &s3c24xx_uart_device1, + &s3c24xx_uart_device2, + &s3c24xx_uart_device3, +}; + +struct platform_device *s3c24xx_uart_devs[4] = { +}; -- cgit v0.10.2 From f126752043c67003de6d34b14160f87ba0323c98 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 13 Jan 2010 14:59:46 +0900 Subject: ARM: SAMSUNG: Add platform data registration for OHCI Add a platform data helper for the OHCI device Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c2410/usb-simtec.c b/arch/arm/mach-s3c2410/usb-simtec.c index 199bb4f..29bd3d9 100644 --- a/arch/arm/mach-s3c2410/usb-simtec.c +++ b/arch/arm/mach-s3c2410/usb-simtec.c @@ -91,7 +91,7 @@ static void usb_simtec_enableoc(struct s3c2410_hcd_info *info, int on) } } -static struct s3c2410_hcd_info usb_simtec_info = { +static struct s3c2410_hcd_info usb_simtec_info __initdata = { .port[0] = { .flags = S3C_HCDFLG_USED }, @@ -127,6 +127,6 @@ int usb_simtec_init(void) gpio_direction_output(S3C2410_GPB(4), 1); gpio_direction_input(S3C2410_GPG(10)); - s3c_device_ohci.dev.platform_data = &usb_simtec_info; + s3c_ohci_set_platdata(&usb_simtec_info); return 0; } diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c index 825800b..45799c6 100644 --- a/arch/arm/mach-s3c2442/mach-gta02.c +++ b/arch/arm/mach-s3c2442/mach-gta02.c @@ -544,7 +544,7 @@ static struct platform_device gta02_bl_dev = { /* USB */ -static struct s3c2410_hcd_info gta02_usb_info = { +static struct s3c2410_hcd_info gta02_usb_info __initdata = { .port[0] = { .flags = S3C_HCDFLG_USED, }, @@ -623,9 +623,8 @@ static void __init gta02_machine_init(void) INIT_DELAYED_WORK(>a02_charger_work, gta02_charger_worker); #endif - s3c_device_ohci.dev.platform_data = >a02_usb_info; - s3c24xx_udc_set_platdata(>a02_udc_cfg); + s3c_ohci_set_platdata(>a02_usb_info); s3c_nand_set_platdata(>a02_nand_info); s3c_i2c0_set_platdata(NULL); diff --git a/arch/arm/plat-s3c/include/plat/usb-control.h b/arch/arm/plat-s3c/include/plat/usb-control.h index 822c87f..a6a57bf 100644 --- a/arch/arm/plat-s3c/include/plat/usb-control.h +++ b/arch/arm/plat-s3c/include/plat/usb-control.h @@ -38,4 +38,6 @@ static void inline s3c2410_usb_report_oc(struct s3c2410_hcd_info *info, int port } } +extern void s3c_ohci_set_platdata(struct s3c2410_hcd_info *info); + #endif /*__ASM_ARCH_USBCONTROL_H */ diff --git a/arch/arm/plat-samsung/dev-usb.c b/arch/arm/plat-samsung/dev-usb.c index 431ae5d..8816565 100644 --- a/arch/arm/plat-samsung/dev-usb.c +++ b/arch/arm/plat-samsung/dev-usb.c @@ -19,7 +19,7 @@ #include #include - +#include static struct resource s3c_usb_resource[] = { [0] = { @@ -48,3 +48,22 @@ struct platform_device s3c_device_ohci = { }; EXPORT_SYMBOL(s3c_device_ohci); + +/** + * s3c_ohci_set_platdata - initialise OHCI device platform data + * @info: The platform data. + * + * This call copies the @info passed in and sets the device .platform_data + * field to that copy. The @info is copied so that the original can be marked + * __initdata. + */ +void __init s3c_ohci_set_platdata(struct s3c2410_hcd_info *info) +{ + struct s3c2410_hcd_info *npd; + + npd = kmemdup(info, sizeof(struct s3c2410_hcd_info), GFP_KERNEL); + if (!npd) + printk(KERN_ERR "%s: no memory for platform data\n", __func__); + + s3c_device_ohci.dev.platform_data = npd; +} -- cgit v0.10.2 From 3cd2fff59fc924752611301f4ca7e8484f5744a5 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 15 Jan 2010 15:09:20 +0900 Subject: ARM: MINI2440: Remove commented out devices from device array There's no point in having these in until there is proper support for them, so remove as they only confuse the device changing script. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c2440/mach-mini2440.c b/arch/arm/mach-s3c2440/mach-mini2440.c index 347eb6d..65ea3b5 100644 --- a/arch/arm/mach-s3c2440/mach-mini2440.c +++ b/arch/arm/mach-s3c2440/mach-mini2440.c @@ -508,7 +508,6 @@ static struct i2c_board_info mini2440_i2c_devs[] __initdata = { static struct platform_device *mini2440_devices[] __initdata = { &s3c_device_ohci, &s3c_device_wdt, -/* &s3c_device_adc,*/ /* ADC doesn't like living with touchscreen ! */ &s3c_device_i2c0, &s3c_device_rtc, &s3c_device_usbgadget, @@ -522,8 +521,6 @@ static struct platform_device *mini2440_devices[] __initdata = { &s3c_device_sdi, &s3c_device_iis, &mini2440_audio, -/* &s3c_device_timer[0],*/ /* buzzer pwm, no API for it */ - /* remaining devices are optional */ }; static void __init mini2440_map_io(void) -- cgit v0.10.2 From 22c810ab3e25d16e6f128384d107b933b6fd8fff Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 18 Jan 2010 16:24:22 +0900 Subject: ARM: S3C24XX: Add mci platform data set call s3c24xx_mci_set_platdata(). Add a s3c24xx_mci_set_platdata() call for all the machine files that have platform data for the MCI driver. This brings the MCI device into line with the other devices with __initdata and a specific call to ensure the right structure type is being passed. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 3f9217d..fbedd07 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -196,7 +196,7 @@ static struct platform_device h1940_device_bluetooth = { .id = -1, }; -static struct s3c24xx_mci_pdata h1940_mmc_cfg = { +static struct s3c24xx_mci_pdata h1940_mmc_cfg __initdata = { .gpio_detect = S3C2410_GPF(5), .gpio_wprotect = S3C2410_GPH(8), .set_power = NULL, @@ -311,12 +311,11 @@ static void __init h1940_init(void) u32 tmp; s3c24xx_fb_set_platdata(&h1940_fb_info); + s3c24xx_mci_set_platdata(&h1940_mmc_cfg); s3c24xx_udc_set_platdata(&h1940_udc_cfg); s3c24xx_ts_set_platdata(&h1940_ts_cfg); s3c_i2c0_set_platdata(NULL); - s3c_device_sdi.dev.platform_data = &h1940_mmc_cfg; - /* Turn off suspend on both USB ports, and switch the * selectable USB port to USB device mode. */ diff --git a/arch/arm/mach-s3c2440/mach-at2440evb.c b/arch/arm/mach-s3c2440/mach-at2440evb.c index 0defde6..8472579 100644 --- a/arch/arm/mach-s3c2440/mach-at2440evb.c +++ b/arch/arm/mach-s3c2440/mach-at2440evb.c @@ -165,7 +165,7 @@ static struct platform_device at2440evb_device_eth = { }, }; -static struct s3c24xx_mci_pdata at2440evb_mci_pdata = { +static struct s3c24xx_mci_pdata at2440evb_mci_pdata __initdata = { .gpio_detect = S3C2410_GPG(10), }; @@ -216,8 +216,6 @@ static struct platform_device *at2440evb_devices[] __initdata = { static void __init at2440evb_map_io(void) { - s3c_device_sdi.dev.platform_data = &at2440evb_mci_pdata; - s3c24xx_init_io(at2440evb_iodesc, ARRAY_SIZE(at2440evb_iodesc)); s3c24xx_init_clocks(16934400); s3c24xx_init_uarts(at2440evb_uartcfgs, ARRAY_SIZE(at2440evb_uartcfgs)); @@ -226,6 +224,7 @@ static void __init at2440evb_map_io(void) static void __init at2440evb_init(void) { s3c24xx_fb_set_platdata(&at2440evb_fb_info); + s3c24xx_mci_set_platdata(&at2440evb_mci_pdata); s3c_nand_set_platdata(&at2440evb_nand_info); s3c_i2c0_set_platdata(NULL); diff --git a/arch/arm/mach-s3c2440/mach-mini2440.c b/arch/arm/mach-s3c2440/mach-mini2440.c index 65ea3b5..571b176 100644 --- a/arch/arm/mach-s3c2440/mach-mini2440.c +++ b/arch/arm/mach-s3c2440/mach-mini2440.c @@ -528,8 +528,6 @@ static void __init mini2440_map_io(void) s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc)); s3c24xx_init_clocks(12000000); s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs)); - - s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg; } /* @@ -675,6 +673,7 @@ static void __init mini2440_init(void) } s3c24xx_udc_set_platdata(&mini2440_udc_cfg); + s3c24xx_mci_set_platdata(&mini2440_mmc_cfg); s3c_nand_set_platdata(&mini2440_nand_info); s3c_i2c0_set_platdata(NULL); diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index 38c358c..986d4e5 100644 --- a/arch/arm/plat-s3c24xx/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -351,6 +352,18 @@ struct platform_device s3c_device_sdi = { EXPORT_SYMBOL(s3c_device_sdi); +void s3c24xx_mci_set_platdata(struct s3c24xx_mci_pdata *pdata) +{ + struct s3c24xx_mci_pdata *npd; + + npd = kmemdup(pdata, sizeof(struct s3c24xx_mci_pdata), GFP_KERNEL); + if (!npd) + printk(KERN_ERR "%s: no memory to copy pdata", __func__); + + s3c_device_sdi.dev.platform_data = npd; +} + + /* SPI (0) */ static struct resource s3c_spi0_resource[] = { diff --git a/arch/arm/plat-s3c24xx/include/plat/mci.h b/arch/arm/plat-s3c24xx/include/plat/mci.h index 36aaa10..2ac2b21 100644 --- a/arch/arm/plat-s3c24xx/include/plat/mci.h +++ b/arch/arm/plat-s3c24xx/include/plat/mci.h @@ -40,4 +40,13 @@ struct s3c24xx_mci_pdata { unsigned short vdd); }; +/** + * s3c24xx_mci_set_platdata - set platform data for mmc/sdi device + * @pdata: The platform data + * + * Copy the platform data supplied by @pdata so that this can be marked + * __initdata. + */ +extern void s3c24xx_mci_set_platdata(struct s3c24xx_mci_pdata *pdata); + #endif /* _ARCH_NCI_H */ -- cgit v0.10.2 From 51fe52229e3765dc56b8d2dd5df656fbf8a20500 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Thu, 14 Jan 2010 15:05:38 +0900 Subject: ARM: SAMSUNG: serial: Modify console initialization. This patch modifies s3c24xx_serial_initconsole function to accept multiple platform UART information structures. This is required on platforms that have differences among the instances of UART ports. As an example, the FIFO sizes could be different for each UART instance and hence multiple platform UART information structures would be needed. This patch also modifies the s3c24xx_console_init macro since it wraps the call to the s3c24xx_serial_initconsole function. Signed-off-by: Thomas Abraham Signed-off-by: Ben Dooks diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index 52e3df1..6982243 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -1374,7 +1374,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, * data. */ -static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info) +static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info) { struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports; struct platform_device **platdev_ptr; @@ -1385,7 +1385,7 @@ static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info) platdev_ptr = s3c24xx_uart_devs; for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) { - s3c24xx_serial_init_port(ptr, info, *platdev_ptr); + s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr); } return 0; @@ -1451,7 +1451,7 @@ static struct console s3c24xx_serial_console = { }; int s3c24xx_serial_initconsole(struct platform_driver *drv, - struct s3c24xx_uart_info *info) + struct s3c24xx_uart_info **info) { struct platform_device *dev = s3c24xx_uart_devs[0]; diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h index 1fb2234..0ac06a0 100644 --- a/drivers/serial/samsung.h +++ b/drivers/serial/samsung.h @@ -75,19 +75,24 @@ extern int s3c24xx_serial_probe(struct platform_device *dev, extern int __devexit s3c24xx_serial_remove(struct platform_device *dev); extern int s3c24xx_serial_initconsole(struct platform_driver *drv, - struct s3c24xx_uart_info *uart); + struct s3c24xx_uart_info **uart); extern int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info); #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE -#define s3c24xx_console_init(__drv, __inf) \ -static int __init s3c_serial_console_init(void) \ -{ \ - return s3c24xx_serial_initconsole(__drv, __inf); \ -} \ - \ +#define s3c24xx_console_init(__drv, __inf) \ +static int __init s3c_serial_console_init(void) \ +{ \ + struct s3c24xx_uart_info *uinfo[CONFIG_SERIAL_SAMSUNG_UARTS]; \ + int i; \ + \ + for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) \ + uinfo[i] = __inf; \ + return s3c24xx_serial_initconsole(__drv, uinfo); \ +} \ + \ console_initcall(s3c_serial_console_init) #else -- cgit v0.10.2 From 1f323cfda5feee4e9c1fc09ededaee849f906468 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 19 Jan 2010 15:30:54 +0900 Subject: ARM: SAMSUNG: Move GPIO common functions to plat-samsung This patch moves GPIO common functions (from plat-s3c64xx) into plat-samsung. and adds the config option to build the plat-samsung/gpiolib for Samsung SoCs. Signed-off-by: Adityapratap Sharma Signed-off-by: Atul Dahiya Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/include/plat/gpio-core.h b/arch/arm/plat-s3c/include/plat/gpio-core.h index 32af612..94fed58 100644 --- a/arch/arm/plat-s3c/include/plat/gpio-core.h +++ b/arch/arm/plat-s3c/include/plat/gpio-core.h @@ -11,6 +11,11 @@ * published by the Free Software Foundation. */ +#define GPIOCON_OFF (0x00) +#define GPIODAT_OFF (0x04) + +#define con_4bit_shift(__off) ((__off) * 4) + /* Define the core gpiolib support functions that the s3c platforms may * need to extend or change depending on the hardware and the s3c chip * selected at build or found at run time. @@ -80,6 +85,29 @@ extern void s3c_gpiolib_add(struct s3c_gpio_chip *chip); * and any other necessary functions. */ +/** + * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @nr_chips: The no of chips (gpio ports) for the GPIO being configured. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependant on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a seperate set of function + * (samsung_gpiolib_add_4bit2_chips)for each case. + */ +extern void samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, + int nr_chips); +extern void samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip, + int nr_chips); + +extern void samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip); +extern void samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip); + #ifdef CONFIG_S3C_GPIO_TRACK extern struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig index 0fba1f9..37b4519 100644 --- a/arch/arm/plat-s3c64xx/Kconfig +++ b/arch/arm/plat-s3c64xx/Kconfig @@ -22,6 +22,7 @@ config PLAT_S3C64XX select S3C_GPIO_CFG_S3C64XX select S3C_DEV_NAND select USB_ARCH_HAS_OHCI + select SAMSUNG_GPIOLIB_4BIT help Base platform code for any Samsung S3C64XX device diff --git a/arch/arm/plat-s3c64xx/gpiolib.c b/arch/arm/plat-s3c64xx/gpiolib.c index 7785604..265e23b 100644 --- a/arch/arm/plat-s3c64xx/gpiolib.c +++ b/arch/arm/plat-s3c64xx/gpiolib.c @@ -49,150 +49,6 @@ * [2] BANK has two control registers, GPxCON0 and GPxCON1 */ -#define OFF_GPCON (0x00) -#define OFF_GPDAT (0x04) - -#define con_4bit_shift(__off) ((__off) * 4) - -#if 1 -#define gpio_dbg(x...) do { } while(0) -#else -#define gpio_dbg(x...) printk(KERN_DEBUG x) -#endif - -/* The s3c64xx_gpiolib_4bit routines are to control the gpio banks where - * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the - * following example: - * - * base + 0x00: Control register, 4 bits per gpio - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x04: Data register, 1 bit per gpio - * bit n: data bit n - * - * Note, since the data register is one bit per gpio and is at base + 0x4 - * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of - * the output. -*/ - -static int s3c64xx_gpiolib_4bit_input(struct gpio_chip *chip, unsigned offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long con; - - con = __raw_readl(base + OFF_GPCON); - con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, base + OFF_GPCON); - - gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); - - return 0; -} - -static int s3c64xx_gpiolib_4bit_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long con; - unsigned long dat; - - con = __raw_readl(base + OFF_GPCON); - con &= ~(0xf << con_4bit_shift(offset)); - con |= 0x1 << con_4bit_shift(offset); - - dat = __raw_readl(base + OFF_GPDAT); - if (value) - dat |= 1 << offset; - else - dat &= ~(1 << offset); - - __raw_writel(dat, base + OFF_GPDAT); - __raw_writel(con, base + OFF_GPCON); - __raw_writel(dat, base + OFF_GPDAT); - - gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); - - return 0; -} - -/* The next set of routines are for the case where the GPIO configuration - * registers are 4 bits per GPIO but there is more than one register (the - * bank has more than 8 GPIOs. - * - * This case is the similar to the 4 bit case, but the registers are as - * follows: - * - * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x08: Data register, 1 bit per gpio - * bit n: data bit n - * - * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we - * store the 'base + 0x4' address so that these routines see the data - * register at ourchip->base + 0x04. -*/ - -static int s3c64xx_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - - if (offset > 7) - offset -= 8; - else - regcon -= 4; - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, regcon); - - gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); - - return 0; - -} - -static int s3c64xx_gpiolib_4bit2_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - unsigned long dat; - - if (offset > 7) - offset -= 8; - else - regcon -= 4; - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(offset)); - con |= 0x1 << con_4bit_shift(offset); - - dat = __raw_readl(base + OFF_GPDAT); - if (value) - dat |= 1 << offset; - else - dat &= ~(1 << offset); - - __raw_writel(dat, base + OFF_GPDAT); - __raw_writel(con, regcon); - __raw_writel(dat, base + OFF_GPDAT); - - gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); - - return 0; -} - static struct s3c_gpio_cfg gpio_4bit_cfg_noint = { .set_config = s3c_gpio_setcfg_s3c64xx_4bit, .set_pull = s3c_gpio_setpull_updown, @@ -399,20 +255,6 @@ static struct s3c_gpio_chip gpio_2bit[] = { }, }; -static __init void s3c64xx_gpiolib_add_4bit(struct s3c_gpio_chip *chip) -{ - chip->chip.direction_input = s3c64xx_gpiolib_4bit_input; - chip->chip.direction_output = s3c64xx_gpiolib_4bit_output; - chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); -} - -static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) -{ - chip->chip.direction_input = s3c64xx_gpiolib_4bit2_input; - chip->chip.direction_output = s3c64xx_gpiolib_4bit2_output; - chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); -} - static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip) { chip->pm = __gpio_pm(&s3c_gpio_pm_2bit); @@ -432,10 +274,10 @@ static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, static __init int s3c64xx_gpiolib_init(void) { s3c64xx_gpiolib_add(gpio_4bit, ARRAY_SIZE(gpio_4bit), - s3c64xx_gpiolib_add_4bit); + samsung_gpiolib_add_4bit); s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2), - s3c64xx_gpiolib_add_4bit2); + samsung_gpiolib_add_4bit2); s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), s3c64xx_gpiolib_add_2bit); diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index e6c1229..6484b5b 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -33,6 +33,13 @@ config SAMSUNG_IRQ_UART # options for gpio configuration support +config SAMSUNG_GPIOLIB_4BIT + bool + help + GPIOlib file contains the 4 bit modification functions for gpio + configuration. GPIOlib shall be compiled only for S3C64XX and S5P + series of processors. + config S3C_GPIO_CFG_S3C24XX bool help diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index ceac416..1fc8d47 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -15,6 +15,7 @@ obj-y += clock.o obj-y += pwm-clock.o obj-y += gpio-config.o +obj-$(CONFIG_SAMSUNG_GPIOLIB_4BIT) += gpiolib.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o obj-$(CONFIG_SAMSUNG_IRQ_UART) += irq-uart.o diff --git a/arch/arm/plat-samsung/gpiolib.c b/arch/arm/plat-samsung/gpiolib.c new file mode 100644 index 0000000..3419b67 --- /dev/null +++ b/arch/arm/plat-samsung/gpiolib.c @@ -0,0 +1,197 @@ +/* arch/arm/plat-samsung/gpiolib.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * SAMSUNG - GPIOlib 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef DEBUG_GPIO +#define gpio_dbg(x...) do { } while (0) +#else +#define gpio_dbg(x...) printk(KERN_DEBUG x) +#endif + +/* The samsung_gpiolib_4bit routines are to control the gpio banks where + * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the + * following example: + * + * base + 0x00: Control register, 4 bits per gpio + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n + * + * Note, since the data register is one bit per gpio and is at base + 0x4 + * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of + * the output. +*/ + +int samsung_gpiolib_4bit_input(struct gpio_chip *chip, unsigned int offset) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + + con = __raw_readl(base + GPIOCON_OFF); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, base + GPIOCON_OFF); + + gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); + + return 0; +} + +int samsung_gpiolib_4bit_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + unsigned long dat; + + con = __raw_readl(base + GPIOCON_OFF); + con &= ~(0xf << con_4bit_shift(offset)); + con |= 0x1 << con_4bit_shift(offset); + + dat = __raw_readl(base + GPIODAT_OFF); + + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + GPIODAT_OFF); + __raw_writel(con, base + GPIOCON_OFF); + __raw_writel(dat, base + GPIODAT_OFF); + + gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +/* The next set of routines are for the case where the GPIO configuration + * registers are 4 bits per GPIO but there is more than one register (the + * bank has more than 8 GPIOs. + * + * This case is the similar to the 4 bit case, but the registers are as + * follows: + * + * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x08: Data register, 1 bit per gpio + * bit n: data bit n + * + * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we + * store the 'base + 0x4' address so that these routines see the data + * register at ourchip->base + 0x04. +*/ + +int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned int offset) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + + if (offset > 7) + offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); + + return 0; +} + +int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + unsigned con_offset = offset; + + if (con_offset > 7) + con_offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(con_offset)); + con |= 0x1 << con_4bit_shift(con_offset); + + dat = __raw_readl(base + GPIODAT_OFF); + + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + GPIODAT_OFF); + __raw_writel(con, regcon); + __raw_writel(dat, base + GPIODAT_OFF); + + gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip) +{ + chip->chip.direction_input = samsung_gpiolib_4bit_input; + chip->chip.direction_output = samsung_gpiolib_4bit_output; + chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); +} + +void __init samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) +{ + chip->chip.direction_input = samsung_gpiolib_4bit2_input; + chip->chip.direction_output = samsung_gpiolib_4bit2_output; + chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); +} + +void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + samsung_gpiolib_add_4bit(chip); + s3c_gpiolib_add(chip); + } +} + +void __init samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + samsung_gpiolib_add_4bit2(chip); + s3c_gpiolib_add(chip); + } +} -- cgit v0.10.2 From 42d828d46f526605c84ebdb5a987fd25fe09a3c9 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 19 Jan 2010 15:31:17 +0900 Subject: ARM: S5P6440: Add S5P6440 GPIO support This patch adds Samsung's S5P6440 GPIO support. Signed-off-by: Adityapratap Sharma Signed-off-by: Atul Dahiya Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5p6440/Makefile b/arch/arm/mach-s5p6440/Makefile index a3ffda9..a79b130 100644 --- a/arch/arm/mach-s5p6440/Makefile +++ b/arch/arm/mach-s5p6440/Makefile @@ -12,7 +12,7 @@ obj- := # Core support for S5P6440 system -obj-$(CONFIG_CPU_S5P6440) += cpu.o +obj-$(CONFIG_CPU_S5P6440) += cpu.o s5p6440-gpio.o # machine support diff --git a/arch/arm/mach-s5p6440/include/mach/regs-gpio.h b/arch/arm/mach-s5p6440/include/mach/regs-gpio.h new file mode 100644 index 0000000..82ff753 --- /dev/null +++ b/arch/arm/mach-s5p6440/include/mach/regs-gpio.h @@ -0,0 +1,54 @@ +/* linux/arch/arm/mach-s5p6440/include/mach/regs-gpio.h + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - GPIO register definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARCH_REGS_GPIO_H +#define __ASM_ARCH_REGS_GPIO_H __FILE__ + +#include + +/* Base addresses for each of the banks */ +#define S5P6440_GPA_BASE (S5P_VA_GPIO + 0x0000) +#define S5P6440_GPB_BASE (S5P_VA_GPIO + 0x0020) +#define S5P6440_GPC_BASE (S5P_VA_GPIO + 0x0040) +#define S5P6440_GPF_BASE (S5P_VA_GPIO + 0x00A0) +#define S5P6440_GPG_BASE (S5P_VA_GPIO + 0x00C0) +#define S5P6440_GPH_BASE (S5P_VA_GPIO + 0x00E0) +#define S5P6440_GPI_BASE (S5P_VA_GPIO + 0x0100) +#define S5P6440_GPJ_BASE (S5P_VA_GPIO + 0x0120) +#define S5P6440_GPN_BASE (S5P_VA_GPIO + 0x0830) +#define S5P6440_GPP_BASE (S5P_VA_GPIO + 0x0160) +#define S5P6440_GPR_BASE (S5P_VA_GPIO + 0x0290) +#define S5P6440_EINT0CON0 (S5P_VA_GPIO + 0x900) +#define S5P6440_EINT0FLTCON0 (S5P_VA_GPIO + 0x910) +#define S5P6440_EINT0FLTCON1 (S5P_VA_GPIO + 0x914) +#define S5P6440_EINT0MASK (S5P_VA_GPIO + 0x920) +#define S5P6440_EINT0PEND (S5P_VA_GPIO + 0x924) + +/* for LCD */ +#define S5P6440_SPCON_LCD_SEL_RGB (1 << 0) +#define S5P6440_SPCON_LCD_SEL_MASK (3 << 0) + +/* These set of macros are not really useful for the + * GPF/GPI/GPJ/GPN/GPP, + * useful for others set of GPIO's (4 bit) + */ +#define S5P6440_GPIO_CONMASK(__gpio) (0xf << ((__gpio) * 4)) +#define S5P6440_GPIO_INPUT(__gpio) (0x0 << ((__gpio) * 4)) +#define S5P6440_GPIO_OUTPUT(__gpio) (0x1 << ((__gpio) * 4)) + +/* Use these macros for GPF/GPI/GPJ/GPN/GPP set of GPIO (2 bit) + * */ +#define S5P6440_GPIO2_CONMASK(__gpio) (0x3 << ((__gpio) * 2)) +#define S5P6440_GPIO2_INPUT(__gpio) (0x0 << ((__gpio) * 2)) +#define S5P6440_GPIO2_OUTPUT(__gpio) (0x1 << ((__gpio) * 2)) + +#endif /* __ASM_ARCH_REGS_GPIO_H */ diff --git a/arch/arm/mach-s5p6440/s5p6440-gpio.c b/arch/arm/mach-s5p6440/s5p6440-gpio.c new file mode 100644 index 0000000..742264c --- /dev/null +++ b/arch/arm/mach-s5p6440/s5p6440-gpio.c @@ -0,0 +1,322 @@ +/* arch/arm/mach-s5p6440/s5p6440-gpio.c + * + * Copyright (c) 2009 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S5P6440 - GPIOlib 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* GPIO bank summary: +* +* Bank GPIOs Style SlpCon ExtInt Group +* A 6 4Bit Yes 1 +* B 7 4Bit Yes 1 +* C 8 4Bit Yes 2 +* F 2 2Bit Yes 4 [1] +* G 7 4Bit Yes 5 +* H 10 4Bit[2] Yes 6 +* I 16 2Bit Yes None +* J 12 2Bit Yes None +* N 16 2Bit No IRQ_EINT +* P 8 2Bit Yes 8 +* R 15 4Bit[2] Yes 8 +* +* [1] BANKF pins 14,15 do not form part of the external interrupt sources +* [2] BANK has two control registers, GPxCON0 and GPxCON1 +*/ + +static int s5p6440_gpiolib_rbank_4bit2_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + + switch (offset) { + case 6: + offset += 1; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + regcon -= 4; + break; + default: + offset -= 7; + break; + } + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + return 0; +} + +static int s5p6440_gpiolib_rbank_4bit2_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + unsigned con_offset = offset; + + switch (con_offset) { + case 6: + con_offset += 1; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + regcon -= 4; + break; + default: + con_offset -= 7; + break; + } + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(con_offset)); + con |= 0x1 << con_4bit_shift(con_offset); + + dat = __raw_readl(base + GPIODAT_OFF); + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(con, regcon); + __raw_writel(dat, base + GPIODAT_OFF); + + return 0; +} + +int s5p6440_gpio_setcfg_4bit_rbank(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift; + u32 con; + + switch (off) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + shift = (off & 7) * 4; + reg -= 4; + break; + case 6: + shift = ((off + 1) & 7) * 4; + reg -= 4; + default: + shift = ((off + 1) & 7) * 4; + break; + } + + if (s3c_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0xf << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +static struct s3c_gpio_cfg s5p6440_gpio_cfgs[] = { + { + .cfg_eint = 0, + }, { + .cfg_eint = 7, + }, { + .cfg_eint = 3, + .set_config = s5p6440_gpio_setcfg_4bit_rbank, + }, { + .cfg_eint = 0, + .set_config = s3c_gpio_setcfg_s3c24xx, + }, { + .cfg_eint = 2, + .set_config = s3c_gpio_setcfg_s3c24xx, + }, { + .cfg_eint = 3, + .set_config = s3c_gpio_setcfg_s3c24xx, + }, +}; + +static struct s3c_gpio_chip s5p6440_gpio_4bit[] = { + { + .base = S5P6440_GPA_BASE, + .config = &s5p6440_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPA(0), + .ngpio = S5P6440_GPIO_A_NR, + .label = "GPA", + }, + }, { + .base = S5P6440_GPB_BASE, + .config = &s5p6440_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPB(0), + .ngpio = S5P6440_GPIO_B_NR, + .label = "GPB", + }, + }, { + .base = S5P6440_GPC_BASE, + .config = &s5p6440_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPC(0), + .ngpio = S5P6440_GPIO_C_NR, + .label = "GPC", + }, + }, { + .base = S5P6440_GPG_BASE, + .config = &s5p6440_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPG(0), + .ngpio = S5P6440_GPIO_G_NR, + .label = "GPG", + }, + }, +}; + +static struct s3c_gpio_chip s5p6440_gpio_4bit2[] = { + { + .base = S5P6440_GPH_BASE + 0x4, + .config = &s5p6440_gpio_cfgs[1], + .chip = { + .base = S5P6440_GPH(0), + .ngpio = S5P6440_GPIO_H_NR, + .label = "GPH", + }, + }, +}; + +static struct s3c_gpio_chip gpio_rbank_4bit2[] = { + { + .base = S5P6440_GPR_BASE + 0x4, + .config = &s5p6440_gpio_cfgs[2], + .chip = { + .base = S5P6440_GPR(0), + .ngpio = S5P6440_GPIO_R_NR, + .label = "GPR", + }, + }, +}; + +static struct s3c_gpio_chip s5p6440_gpio_2bit[] = { + { + .base = S5P6440_GPF_BASE, + .config = &s5p6440_gpio_cfgs[5], + .chip = { + .base = S5P6440_GPF(0), + .ngpio = S5P6440_GPIO_F_NR, + .label = "GPF", + }, + }, { + .base = S5P6440_GPI_BASE, + .config = &s5p6440_gpio_cfgs[3], + .chip = { + .base = S5P6440_GPI(0), + .ngpio = S5P6440_GPIO_I_NR, + .label = "GPI", + }, + }, { + .base = S5P6440_GPJ_BASE, + .config = &s5p6440_gpio_cfgs[3], + .chip = { + .base = S5P6440_GPJ(0), + .ngpio = S5P6440_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .base = S5P6440_GPN_BASE, + .config = &s5p6440_gpio_cfgs[4], + .chip = { + .base = S5P6440_GPN(0), + .ngpio = S5P6440_GPIO_N_NR, + .label = "GPN", + }, + }, { + .base = S5P6440_GPP_BASE, + .config = &s5p6440_gpio_cfgs[5], + .chip = { + .base = S5P6440_GPP(0), + .ngpio = S5P6440_GPIO_P_NR, + .label = "GPP", + }, + }, +}; + +void __init s5p6440_gpiolib_set_cfg(struct s3c_gpio_cfg *chipcfg, int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chipcfg++) { + if (!chipcfg->set_config) + chipcfg->set_config = s3c_gpio_setcfg_s3c64xx_4bit; + if (!chipcfg->set_pull) + chipcfg->set_pull = s3c_gpio_setpull_updown; + if (!chipcfg->get_pull) + chipcfg->get_pull = s3c_gpio_getpull_updown; + } +} + +static void __init s5p6440_gpio_add_rbank_4bit2(struct s3c_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + chip->chip.direction_input = s5p6440_gpiolib_rbank_4bit2_input; + chip->chip.direction_output = + s5p6440_gpiolib_rbank_4bit2_output; + s3c_gpiolib_add(chip); + } +} + +static int __init s5p6440_gpiolib_init(void) +{ + struct s3c_gpio_chip *chips = s5p6440_gpio_2bit; + int nr_chips = ARRAY_SIZE(s5p6440_gpio_2bit); + + s5p6440_gpiolib_set_cfg(s5p6440_gpio_cfgs, + ARRAY_SIZE(s5p6440_gpio_cfgs)); + + for (; nr_chips > 0; nr_chips--, chips++) + s3c_gpiolib_add(chips); + + samsung_gpiolib_add_4bit_chips(s5p6440_gpio_4bit, + ARRAY_SIZE(s5p6440_gpio_4bit)); + + samsung_gpiolib_add_4bit2_chips(s5p6440_gpio_4bit2, + ARRAY_SIZE(s5p6440_gpio_4bit2)); + + s5p6440_gpio_add_rbank_4bit2(gpio_rbank_4bit2, + ARRAY_SIZE(gpio_rbank_4bit2)); + + return 0; +} +arch_initcall(s5p6440_gpiolib_init); diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 7e08b40..e7c31e7 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -14,6 +14,10 @@ config PLAT_S5P select NO_IOPORT select ARCH_REQUIRE_GPIOLIB select S3C_GPIO_TRACK + select SAMSUNG_GPIOLIB_4BIT + select S3C_GPIO_CFG_S3C64XX + select S3C_GPIO_PULL_UPDOWN + select S3C_GPIO_CFG_S3C24XX select PLAT_SAMSUNG select SAMSUNG_CLKSRC select SAMSUNG_IRQ_VIC_TIMER -- cgit v0.10.2 From 67866fb38680d85cbd13c7bb468fb89f16e026d1 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 18 Jan 2010 10:47:45 +0900 Subject: ARM: SAMSUNG: Move pm-gpio into plat-samsung Move the pm-gpio code into plat-samsung. Note, this should be changed later to properly compile the 4bit code if the 4bit settings are enabled. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index ea4a001..ac12ba5 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -22,7 +22,6 @@ obj-$(CONFIG_S3C_DMA) += dma.o # PM support obj-$(CONFIG_PM) += pm.o -obj-$(CONFIG_PM) += pm-gpio.o obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o # PWM support diff --git a/arch/arm/plat-s3c/pm-gpio.c b/arch/arm/plat-s3c/pm-gpio.c deleted file mode 100644 index cfd326a..0000000 --- a/arch/arm/plat-s3c/pm-gpio.c +++ /dev/null @@ -1,380 +0,0 @@ - -/* linux/arch/arm/plat-s3c/pm-gpio.c - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series GPIO PM code - * - * 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 - -/* PM GPIO helpers */ - -#define OFFS_CON (0x00) -#define OFFS_DAT (0x04) -#define OFFS_UP (0x08) - -static void s3c_gpio_pm_1bit_save(struct s3c_gpio_chip *chip) -{ - chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON); - chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT); -} - -static void s3c_gpio_pm_1bit_resume(struct s3c_gpio_chip *chip) -{ - void __iomem *base = chip->base; - u32 old_gpcon = __raw_readl(base + OFFS_CON); - u32 old_gpdat = __raw_readl(base + OFFS_DAT); - u32 gps_gpcon = chip->pm_save[0]; - u32 gps_gpdat = chip->pm_save[1]; - u32 gpcon; - - /* GPACON only has one bit per control / data and no PULLUPs. - * GPACON[x] = 0 => Output, 1 => SFN */ - - /* first set all SFN bits to SFN */ - - gpcon = old_gpcon | gps_gpcon; - __raw_writel(gpcon, base + OFFS_CON); - - /* now set all the other bits */ - - __raw_writel(gps_gpdat, base + OFFS_DAT); - __raw_writel(gps_gpcon, base + OFFS_CON); - - S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n", - chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); -} - -struct s3c_gpio_pm s3c_gpio_pm_1bit = { - .save = s3c_gpio_pm_1bit_save, - .resume = s3c_gpio_pm_1bit_resume, -}; - -static void s3c_gpio_pm_2bit_save(struct s3c_gpio_chip *chip) -{ - chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON); - chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT); - chip->pm_save[2] = __raw_readl(chip->base + OFFS_UP); -} - -/* Test whether the given masked+shifted bits of an GPIO configuration - * are one of the SFN (special function) modes. */ - -static inline int is_sfn(unsigned long con) -{ - return con >= 2; -} - -/* Test if the given masked+shifted GPIO configuration is an input */ - -static inline int is_in(unsigned long con) -{ - return con == 0; -} - -/* Test if the given masked+shifted GPIO configuration is an output */ - -static inline int is_out(unsigned long con) -{ - return con == 1; -} - -/** - * s3c_gpio_pm_2bit_resume() - restore the given GPIO bank - * @chip: The chip information to resume. - * - * Restore one of the GPIO banks that was saved during suspend. This is - * not as simple as once thought, due to the possibility of glitches - * from the order that the CON and DAT registers are set in. - * - * The three states the pin can be are {IN,OUT,SFN} which gives us 9 - * combinations of changes to check. Three of these, if the pin stays - * in the same configuration can be discounted. This leaves us with - * the following: - * - * { IN => OUT } Change DAT first - * { IN => SFN } Change CON first - * { OUT => SFN } Change CON first, so new data will not glitch - * { OUT => IN } Change CON first, so new data will not glitch - * { SFN => IN } Change CON first - * { SFN => OUT } Change DAT first, so new data will not glitch [1] - * - * We do not currently deal with the UP registers as these control - * weak resistors, so a small delay in change should not need to bring - * these into the calculations. - * - * [1] this assumes that writing to a pin DAT whilst in SFN will set the - * state for when it is next output. - */ -static void s3c_gpio_pm_2bit_resume(struct s3c_gpio_chip *chip) -{ - void __iomem *base = chip->base; - u32 old_gpcon = __raw_readl(base + OFFS_CON); - u32 old_gpdat = __raw_readl(base + OFFS_DAT); - u32 gps_gpcon = chip->pm_save[0]; - u32 gps_gpdat = chip->pm_save[1]; - u32 gpcon, old, new, mask; - u32 change_mask = 0x0; - int nr; - - /* restore GPIO pull-up settings */ - __raw_writel(chip->pm_save[2], base + OFFS_UP); - - /* Create a change_mask of all the items that need to have - * their CON value changed before their DAT value, so that - * we minimise the work between the two settings. - */ - - for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) { - old = (old_gpcon & mask) >> nr; - new = (gps_gpcon & mask) >> nr; - - /* If there is no change, then skip */ - - if (old == new) - continue; - - /* If both are special function, then skip */ - - if (is_sfn(old) && is_sfn(new)) - continue; - - /* Change is IN => OUT, do not change now */ - - if (is_in(old) && is_out(new)) - continue; - - /* Change is SFN => OUT, do not change now */ - - if (is_sfn(old) && is_out(new)) - continue; - - /* We should now be at the case of IN=>SFN, - * OUT=>SFN, OUT=>IN, SFN=>IN. */ - - change_mask |= mask; - } - - - /* Write the new CON settings */ - - gpcon = old_gpcon & ~change_mask; - gpcon |= gps_gpcon & change_mask; - - __raw_writel(gpcon, base + OFFS_CON); - - /* Now change any items that require DAT,CON */ - - __raw_writel(gps_gpdat, base + OFFS_DAT); - __raw_writel(gps_gpcon, base + OFFS_CON); - - S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n", - chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); -} - -struct s3c_gpio_pm s3c_gpio_pm_2bit = { - .save = s3c_gpio_pm_2bit_save, - .resume = s3c_gpio_pm_2bit_resume, -}; - -#ifdef CONFIG_ARCH_S3C64XX -static void s3c_gpio_pm_4bit_save(struct s3c_gpio_chip *chip) -{ - chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON); - chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT); - chip->pm_save[3] = __raw_readl(chip->base + OFFS_UP); - - if (chip->chip.ngpio > 8) - chip->pm_save[0] = __raw_readl(chip->base - 4); -} - -static u32 s3c_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon) -{ - u32 old, new, mask; - u32 change_mask = 0x0; - int nr; - - for (nr = 0, mask = 0x0f; nr < 16; nr += 4, mask <<= 4) { - old = (old_gpcon & mask) >> nr; - new = (gps_gpcon & mask) >> nr; - - /* If there is no change, then skip */ - - if (old == new) - continue; - - /* If both are special function, then skip */ - - if (is_sfn(old) && is_sfn(new)) - continue; - - /* Change is IN => OUT, do not change now */ - - if (is_in(old) && is_out(new)) - continue; - - /* Change is SFN => OUT, do not change now */ - - if (is_sfn(old) && is_out(new)) - continue; - - /* We should now be at the case of IN=>SFN, - * OUT=>SFN, OUT=>IN, SFN=>IN. */ - - change_mask |= mask; - } - - return change_mask; -} - -static void s3c_gpio_pm_4bit_con(struct s3c_gpio_chip *chip, int index) -{ - void __iomem *con = chip->base + (index * 4); - u32 old_gpcon = __raw_readl(con); - u32 gps_gpcon = chip->pm_save[index + 1]; - u32 gpcon, mask; - - mask = s3c_gpio_pm_4bit_mask(old_gpcon, gps_gpcon); - - gpcon = old_gpcon & ~mask; - gpcon |= gps_gpcon & mask; - - __raw_writel(gpcon, con); -} - -static void s3c_gpio_pm_4bit_resume(struct s3c_gpio_chip *chip) -{ - void __iomem *base = chip->base; - u32 old_gpcon[2]; - u32 old_gpdat = __raw_readl(base + OFFS_DAT); - u32 gps_gpdat = chip->pm_save[2]; - - /* First, modify the CON settings */ - - old_gpcon[0] = 0; - old_gpcon[1] = __raw_readl(base + OFFS_CON); - - s3c_gpio_pm_4bit_con(chip, 0); - if (chip->chip.ngpio > 8) { - old_gpcon[0] = __raw_readl(base - 4); - s3c_gpio_pm_4bit_con(chip, -1); - } - - /* Now change the configurations that require DAT,CON */ - - __raw_writel(chip->pm_save[2], base + OFFS_DAT); - __raw_writel(chip->pm_save[1], base + OFFS_CON); - if (chip->chip.ngpio > 8) - __raw_writel(chip->pm_save[0], base - 4); - - __raw_writel(chip->pm_save[2], base + OFFS_DAT); - __raw_writel(chip->pm_save[3], base + OFFS_UP); - - if (chip->chip.ngpio > 8) { - S3C_PMDBG("%s: CON4 %08x,%08x => %08x,%08x, DAT %08x => %08x\n", - chip->chip.label, old_gpcon[0], old_gpcon[1], - __raw_readl(base - 4), - __raw_readl(base + OFFS_CON), - old_gpdat, gps_gpdat); - } else - S3C_PMDBG("%s: CON4 %08x => %08x, DAT %08x => %08x\n", - chip->chip.label, old_gpcon[1], - __raw_readl(base + OFFS_CON), - old_gpdat, gps_gpdat); -} - -struct s3c_gpio_pm s3c_gpio_pm_4bit = { - .save = s3c_gpio_pm_4bit_save, - .resume = s3c_gpio_pm_4bit_resume, -}; -#endif /* CONFIG_ARCH_S3C64XX */ - -/** - * s3c_pm_save_gpio() - save gpio chip data for suspend - * @ourchip: The chip for suspend. - */ -static void s3c_pm_save_gpio(struct s3c_gpio_chip *ourchip) -{ - struct s3c_gpio_pm *pm = ourchip->pm; - - if (pm == NULL || pm->save == NULL) - S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label); - else - pm->save(ourchip); -} - -/** - * s3c_pm_save_gpios() - Save the state of the GPIO banks. - * - * For all the GPIO banks, save the state of each one ready for going - * into a suspend mode. - */ -void s3c_pm_save_gpios(void) -{ - struct s3c_gpio_chip *ourchip; - unsigned int gpio_nr; - - for (gpio_nr = 0; gpio_nr < S3C_GPIO_END; gpio_nr++) { - ourchip = s3c_gpiolib_getchip(gpio_nr); - if (!ourchip) - continue; - - s3c_pm_save_gpio(ourchip); - - S3C_PMDBG("%s: save %08x,%08x,%08x,%08x\n", - ourchip->chip.label, - ourchip->pm_save[0], - ourchip->pm_save[1], - ourchip->pm_save[2], - ourchip->pm_save[3]); - - gpio_nr += ourchip->chip.ngpio; - gpio_nr += CONFIG_S3C_GPIO_SPACE; - } -} - -/** - * s3c_pm_resume_gpio() - restore gpio chip data after suspend - * @ourchip: The suspended chip. - */ -static void s3c_pm_resume_gpio(struct s3c_gpio_chip *ourchip) -{ - struct s3c_gpio_pm *pm = ourchip->pm; - - if (pm == NULL || pm->resume == NULL) - S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label); - else - pm->resume(ourchip); -} - -void s3c_pm_restore_gpios(void) -{ - struct s3c_gpio_chip *ourchip; - unsigned int gpio_nr; - - for (gpio_nr = 0; gpio_nr < S3C_GPIO_END; gpio_nr++) { - ourchip = s3c_gpiolib_getchip(gpio_nr); - if (!ourchip) - continue; - - s3c_pm_resume_gpio(ourchip); - - gpio_nr += ourchip->chip.ngpio; - gpio_nr += CONFIG_S3C_GPIO_SPACE; - } -} diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 1fc8d47..94fe6ea3 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -37,3 +37,7 @@ obj-y += dev-uart.o obj-$(CONFIG_S3C_DEV_USB_HOST) += dev-usb.o obj-$(CONFIG_S3C_DEV_USB_HSOTG) += dev-usb-hsotg.o obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o + +# PM support + +obj-$(CONFIG_PM) += pm-gpio.o diff --git a/arch/arm/plat-samsung/pm-gpio.c b/arch/arm/plat-samsung/pm-gpio.c new file mode 100644 index 0000000..cfd326a --- /dev/null +++ b/arch/arm/plat-samsung/pm-gpio.c @@ -0,0 +1,380 @@ + +/* linux/arch/arm/plat-s3c/pm-gpio.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series GPIO PM code + * + * 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 + +/* PM GPIO helpers */ + +#define OFFS_CON (0x00) +#define OFFS_DAT (0x04) +#define OFFS_UP (0x08) + +static void s3c_gpio_pm_1bit_save(struct s3c_gpio_chip *chip) +{ + chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON); + chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT); +} + +static void s3c_gpio_pm_1bit_resume(struct s3c_gpio_chip *chip) +{ + void __iomem *base = chip->base; + u32 old_gpcon = __raw_readl(base + OFFS_CON); + u32 old_gpdat = __raw_readl(base + OFFS_DAT); + u32 gps_gpcon = chip->pm_save[0]; + u32 gps_gpdat = chip->pm_save[1]; + u32 gpcon; + + /* GPACON only has one bit per control / data and no PULLUPs. + * GPACON[x] = 0 => Output, 1 => SFN */ + + /* first set all SFN bits to SFN */ + + gpcon = old_gpcon | gps_gpcon; + __raw_writel(gpcon, base + OFFS_CON); + + /* now set all the other bits */ + + __raw_writel(gps_gpdat, base + OFFS_DAT); + __raw_writel(gps_gpcon, base + OFFS_CON); + + S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n", + chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); +} + +struct s3c_gpio_pm s3c_gpio_pm_1bit = { + .save = s3c_gpio_pm_1bit_save, + .resume = s3c_gpio_pm_1bit_resume, +}; + +static void s3c_gpio_pm_2bit_save(struct s3c_gpio_chip *chip) +{ + chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON); + chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT); + chip->pm_save[2] = __raw_readl(chip->base + OFFS_UP); +} + +/* Test whether the given masked+shifted bits of an GPIO configuration + * are one of the SFN (special function) modes. */ + +static inline int is_sfn(unsigned long con) +{ + return con >= 2; +} + +/* Test if the given masked+shifted GPIO configuration is an input */ + +static inline int is_in(unsigned long con) +{ + return con == 0; +} + +/* Test if the given masked+shifted GPIO configuration is an output */ + +static inline int is_out(unsigned long con) +{ + return con == 1; +} + +/** + * s3c_gpio_pm_2bit_resume() - restore the given GPIO bank + * @chip: The chip information to resume. + * + * Restore one of the GPIO banks that was saved during suspend. This is + * not as simple as once thought, due to the possibility of glitches + * from the order that the CON and DAT registers are set in. + * + * The three states the pin can be are {IN,OUT,SFN} which gives us 9 + * combinations of changes to check. Three of these, if the pin stays + * in the same configuration can be discounted. This leaves us with + * the following: + * + * { IN => OUT } Change DAT first + * { IN => SFN } Change CON first + * { OUT => SFN } Change CON first, so new data will not glitch + * { OUT => IN } Change CON first, so new data will not glitch + * { SFN => IN } Change CON first + * { SFN => OUT } Change DAT first, so new data will not glitch [1] + * + * We do not currently deal with the UP registers as these control + * weak resistors, so a small delay in change should not need to bring + * these into the calculations. + * + * [1] this assumes that writing to a pin DAT whilst in SFN will set the + * state for when it is next output. + */ +static void s3c_gpio_pm_2bit_resume(struct s3c_gpio_chip *chip) +{ + void __iomem *base = chip->base; + u32 old_gpcon = __raw_readl(base + OFFS_CON); + u32 old_gpdat = __raw_readl(base + OFFS_DAT); + u32 gps_gpcon = chip->pm_save[0]; + u32 gps_gpdat = chip->pm_save[1]; + u32 gpcon, old, new, mask; + u32 change_mask = 0x0; + int nr; + + /* restore GPIO pull-up settings */ + __raw_writel(chip->pm_save[2], base + OFFS_UP); + + /* Create a change_mask of all the items that need to have + * their CON value changed before their DAT value, so that + * we minimise the work between the two settings. + */ + + for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) { + old = (old_gpcon & mask) >> nr; + new = (gps_gpcon & mask) >> nr; + + /* If there is no change, then skip */ + + if (old == new) + continue; + + /* If both are special function, then skip */ + + if (is_sfn(old) && is_sfn(new)) + continue; + + /* Change is IN => OUT, do not change now */ + + if (is_in(old) && is_out(new)) + continue; + + /* Change is SFN => OUT, do not change now */ + + if (is_sfn(old) && is_out(new)) + continue; + + /* We should now be at the case of IN=>SFN, + * OUT=>SFN, OUT=>IN, SFN=>IN. */ + + change_mask |= mask; + } + + + /* Write the new CON settings */ + + gpcon = old_gpcon & ~change_mask; + gpcon |= gps_gpcon & change_mask; + + __raw_writel(gpcon, base + OFFS_CON); + + /* Now change any items that require DAT,CON */ + + __raw_writel(gps_gpdat, base + OFFS_DAT); + __raw_writel(gps_gpcon, base + OFFS_CON); + + S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n", + chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); +} + +struct s3c_gpio_pm s3c_gpio_pm_2bit = { + .save = s3c_gpio_pm_2bit_save, + .resume = s3c_gpio_pm_2bit_resume, +}; + +#ifdef CONFIG_ARCH_S3C64XX +static void s3c_gpio_pm_4bit_save(struct s3c_gpio_chip *chip) +{ + chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON); + chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT); + chip->pm_save[3] = __raw_readl(chip->base + OFFS_UP); + + if (chip->chip.ngpio > 8) + chip->pm_save[0] = __raw_readl(chip->base - 4); +} + +static u32 s3c_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon) +{ + u32 old, new, mask; + u32 change_mask = 0x0; + int nr; + + for (nr = 0, mask = 0x0f; nr < 16; nr += 4, mask <<= 4) { + old = (old_gpcon & mask) >> nr; + new = (gps_gpcon & mask) >> nr; + + /* If there is no change, then skip */ + + if (old == new) + continue; + + /* If both are special function, then skip */ + + if (is_sfn(old) && is_sfn(new)) + continue; + + /* Change is IN => OUT, do not change now */ + + if (is_in(old) && is_out(new)) + continue; + + /* Change is SFN => OUT, do not change now */ + + if (is_sfn(old) && is_out(new)) + continue; + + /* We should now be at the case of IN=>SFN, + * OUT=>SFN, OUT=>IN, SFN=>IN. */ + + change_mask |= mask; + } + + return change_mask; +} + +static void s3c_gpio_pm_4bit_con(struct s3c_gpio_chip *chip, int index) +{ + void __iomem *con = chip->base + (index * 4); + u32 old_gpcon = __raw_readl(con); + u32 gps_gpcon = chip->pm_save[index + 1]; + u32 gpcon, mask; + + mask = s3c_gpio_pm_4bit_mask(old_gpcon, gps_gpcon); + + gpcon = old_gpcon & ~mask; + gpcon |= gps_gpcon & mask; + + __raw_writel(gpcon, con); +} + +static void s3c_gpio_pm_4bit_resume(struct s3c_gpio_chip *chip) +{ + void __iomem *base = chip->base; + u32 old_gpcon[2]; + u32 old_gpdat = __raw_readl(base + OFFS_DAT); + u32 gps_gpdat = chip->pm_save[2]; + + /* First, modify the CON settings */ + + old_gpcon[0] = 0; + old_gpcon[1] = __raw_readl(base + OFFS_CON); + + s3c_gpio_pm_4bit_con(chip, 0); + if (chip->chip.ngpio > 8) { + old_gpcon[0] = __raw_readl(base - 4); + s3c_gpio_pm_4bit_con(chip, -1); + } + + /* Now change the configurations that require DAT,CON */ + + __raw_writel(chip->pm_save[2], base + OFFS_DAT); + __raw_writel(chip->pm_save[1], base + OFFS_CON); + if (chip->chip.ngpio > 8) + __raw_writel(chip->pm_save[0], base - 4); + + __raw_writel(chip->pm_save[2], base + OFFS_DAT); + __raw_writel(chip->pm_save[3], base + OFFS_UP); + + if (chip->chip.ngpio > 8) { + S3C_PMDBG("%s: CON4 %08x,%08x => %08x,%08x, DAT %08x => %08x\n", + chip->chip.label, old_gpcon[0], old_gpcon[1], + __raw_readl(base - 4), + __raw_readl(base + OFFS_CON), + old_gpdat, gps_gpdat); + } else + S3C_PMDBG("%s: CON4 %08x => %08x, DAT %08x => %08x\n", + chip->chip.label, old_gpcon[1], + __raw_readl(base + OFFS_CON), + old_gpdat, gps_gpdat); +} + +struct s3c_gpio_pm s3c_gpio_pm_4bit = { + .save = s3c_gpio_pm_4bit_save, + .resume = s3c_gpio_pm_4bit_resume, +}; +#endif /* CONFIG_ARCH_S3C64XX */ + +/** + * s3c_pm_save_gpio() - save gpio chip data for suspend + * @ourchip: The chip for suspend. + */ +static void s3c_pm_save_gpio(struct s3c_gpio_chip *ourchip) +{ + struct s3c_gpio_pm *pm = ourchip->pm; + + if (pm == NULL || pm->save == NULL) + S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label); + else + pm->save(ourchip); +} + +/** + * s3c_pm_save_gpios() - Save the state of the GPIO banks. + * + * For all the GPIO banks, save the state of each one ready for going + * into a suspend mode. + */ +void s3c_pm_save_gpios(void) +{ + struct s3c_gpio_chip *ourchip; + unsigned int gpio_nr; + + for (gpio_nr = 0; gpio_nr < S3C_GPIO_END; gpio_nr++) { + ourchip = s3c_gpiolib_getchip(gpio_nr); + if (!ourchip) + continue; + + s3c_pm_save_gpio(ourchip); + + S3C_PMDBG("%s: save %08x,%08x,%08x,%08x\n", + ourchip->chip.label, + ourchip->pm_save[0], + ourchip->pm_save[1], + ourchip->pm_save[2], + ourchip->pm_save[3]); + + gpio_nr += ourchip->chip.ngpio; + gpio_nr += CONFIG_S3C_GPIO_SPACE; + } +} + +/** + * s3c_pm_resume_gpio() - restore gpio chip data after suspend + * @ourchip: The suspended chip. + */ +static void s3c_pm_resume_gpio(struct s3c_gpio_chip *ourchip) +{ + struct s3c_gpio_pm *pm = ourchip->pm; + + if (pm == NULL || pm->resume == NULL) + S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label); + else + pm->resume(ourchip); +} + +void s3c_pm_restore_gpios(void) +{ + struct s3c_gpio_chip *ourchip; + unsigned int gpio_nr; + + for (gpio_nr = 0; gpio_nr < S3C_GPIO_END; gpio_nr++) { + ourchip = s3c_gpiolib_getchip(gpio_nr); + if (!ourchip) + continue; + + s3c_pm_resume_gpio(ourchip); + + gpio_nr += ourchip->chip.ngpio; + gpio_nr += CONFIG_S3C_GPIO_SPACE; + } +} -- cgit v0.10.2 From 4f2b617aff8147338faa0d5ed7b57636076671bf Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 18 Jan 2010 11:22:38 +0900 Subject: ARM: SAMSUNG: Move gpiolib support in gpio.c to plat-samsung Move the gpio.c code containing the core gpiolib and GPIO support to plat-samsung from plat-s3c as it is used by all current Samsung SoCs. Note, we didn't move this to gpiolib.c as it contains code that is not strictly for gpiolib support and the 4bit code is already called gpiolib.c so make the change easier by not renaming both files in one go. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index ac12ba5..20cb335 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -13,7 +13,6 @@ obj- := obj-y += init.o obj-y += time.o -obj-y += gpio.o # DMA support diff --git a/arch/arm/plat-s3c/gpio.c b/arch/arm/plat-s3c/gpio.c deleted file mode 100644 index 5ff24e0..0000000 --- a/arch/arm/plat-s3c/gpio.c +++ /dev/null @@ -1,156 +0,0 @@ -/* linux/arch/arm/plat-s3c/gpio.c - * - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series GPIO core - * - * 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_S3C_GPIO_TRACK -struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; - -static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip) -{ - unsigned int gpn; - int i; - - gpn = chip->chip.base; - for (i = 0; i < chip->chip.ngpio; i++, gpn++) { - BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios)); - s3c_gpios[gpn] = chip; - } -} -#endif /* CONFIG_S3C_GPIO_TRACK */ - -/* Default routines for controlling GPIO, based on the original S3C24XX - * GPIO functions which deal with the case where each gpio bank of the - * chip is as following: - * - * base + 0x00: Control register, 2 bits per gpio - * gpio n: 2 bits starting at (2*n) - * 00 = input, 01 = output, others mean special-function - * base + 0x04: Data register, 1 bit per gpio - * bit n: data bit n -*/ - -static int s3c_gpiolib_input(struct gpio_chip *chip, unsigned offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long flags; - unsigned long con; - - local_irq_save(flags); - - con = __raw_readl(base + 0x00); - con &= ~(3 << (offset * 2)); - - __raw_writel(con, base + 0x00); - - local_irq_restore(flags); - return 0; -} - -static int s3c_gpiolib_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long flags; - unsigned long dat; - unsigned long con; - - local_irq_save(flags); - - dat = __raw_readl(base + 0x04); - dat &= ~(1 << offset); - if (value) - dat |= 1 << offset; - __raw_writel(dat, base + 0x04); - - con = __raw_readl(base + 0x00); - con &= ~(3 << (offset * 2)); - con |= 1 << (offset * 2); - - __raw_writel(con, base + 0x00); - __raw_writel(dat, base + 0x04); - - local_irq_restore(flags); - return 0; -} - -static void s3c_gpiolib_set(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long flags; - unsigned long dat; - - local_irq_save(flags); - - dat = __raw_readl(base + 0x04); - dat &= ~(1 << offset); - if (value) - dat |= 1 << offset; - __raw_writel(dat, base + 0x04); - - local_irq_restore(flags); -} - -static int s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - unsigned long val; - - val = __raw_readl(ourchip->base + 0x04); - val >>= offset; - val &= 1; - - return val; -} - -__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip) -{ - struct gpio_chip *gc = &chip->chip; - int ret; - - BUG_ON(!chip->base); - BUG_ON(!gc->label); - BUG_ON(!gc->ngpio); - - if (!gc->direction_input) - gc->direction_input = s3c_gpiolib_input; - if (!gc->direction_output) - gc->direction_output = s3c_gpiolib_output; - if (!gc->set) - gc->set = s3c_gpiolib_set; - if (!gc->get) - gc->get = s3c_gpiolib_get; - -#ifdef CONFIG_PM - if (chip->pm != NULL) { - if (!chip->pm->save || !chip->pm->resume) - printk(KERN_ERR "gpio: %s has missing PM functions\n", - gc->label); - } else - printk(KERN_ERR "gpio: %s has no PM function\n", gc->label); -#endif - - /* gpiochip_add() prints own failure message on error. */ - ret = gpiochip_add(gc); - if (ret >= 0) - s3c_gpiolib_track(chip); -} diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 94fe6ea3..27d5bfb 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -13,6 +13,7 @@ obj- := obj-y += clock.o obj-y += pwm-clock.o +obj-y += gpio.o obj-y += gpio-config.o obj-$(CONFIG_SAMSUNG_GPIOLIB_4BIT) += gpiolib.o diff --git a/arch/arm/plat-samsung/gpio.c b/arch/arm/plat-samsung/gpio.c new file mode 100644 index 0000000..5ff24e0 --- /dev/null +++ b/arch/arm/plat-samsung/gpio.c @@ -0,0 +1,156 @@ +/* linux/arch/arm/plat-s3c/gpio.c + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series GPIO core + * + * 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_S3C_GPIO_TRACK +struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; + +static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip) +{ + unsigned int gpn; + int i; + + gpn = chip->chip.base; + for (i = 0; i < chip->chip.ngpio; i++, gpn++) { + BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios)); + s3c_gpios[gpn] = chip; + } +} +#endif /* CONFIG_S3C_GPIO_TRACK */ + +/* Default routines for controlling GPIO, based on the original S3C24XX + * GPIO functions which deal with the case where each gpio bank of the + * chip is as following: + * + * base + 0x00: Control register, 2 bits per gpio + * gpio n: 2 bits starting at (2*n) + * 00 = input, 01 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n +*/ + +static int s3c_gpiolib_input(struct gpio_chip *chip, unsigned offset) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long con; + + local_irq_save(flags); + + con = __raw_readl(base + 0x00); + con &= ~(3 << (offset * 2)); + + __raw_writel(con, base + 0x00); + + local_irq_restore(flags); + return 0; +} + +static int s3c_gpiolib_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + unsigned long con; + + local_irq_save(flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + __raw_writel(dat, base + 0x04); + + con = __raw_readl(base + 0x00); + con &= ~(3 << (offset * 2)); + con |= 1 << (offset * 2); + + __raw_writel(con, base + 0x00); + __raw_writel(dat, base + 0x04); + + local_irq_restore(flags); + return 0; +} + +static void s3c_gpiolib_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + + local_irq_save(flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + __raw_writel(dat, base + 0x04); + + local_irq_restore(flags); +} + +static int s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + unsigned long val; + + val = __raw_readl(ourchip->base + 0x04); + val >>= offset; + val &= 1; + + return val; +} + +__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip) +{ + struct gpio_chip *gc = &chip->chip; + int ret; + + BUG_ON(!chip->base); + BUG_ON(!gc->label); + BUG_ON(!gc->ngpio); + + if (!gc->direction_input) + gc->direction_input = s3c_gpiolib_input; + if (!gc->direction_output) + gc->direction_output = s3c_gpiolib_output; + if (!gc->set) + gc->set = s3c_gpiolib_set; + if (!gc->get) + gc->get = s3c_gpiolib_get; + +#ifdef CONFIG_PM + if (chip->pm != NULL) { + if (!chip->pm->save || !chip->pm->resume) + printk(KERN_ERR "gpio: %s has missing PM functions\n", + gc->label); + } else + printk(KERN_ERR "gpio: %s has no PM function\n", gc->label); +#endif + + /* gpiochip_add() prints own failure message on error. */ + ret = gpiochip_add(gc); + if (ret >= 0) + s3c_gpiolib_track(chip); +} -- cgit v0.10.2 From 16e2c7c59f0a4debcb110648025bef11958aa3cd Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 18 Jan 2010 18:00:55 +0900 Subject: ARM: SAMSUNG: Move GPIO headers to plat-samsung Move the GPIO and GPIO configuration headers into arch/arm/plat-samsung as they are common to all the Samsung SoCs. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/include/plat/gpio-cfg-helpers.h b/arch/arm/plat-s3c/include/plat/gpio-cfg-helpers.h deleted file mode 100644 index 652e2bb..0000000 --- a/arch/arm/plat-s3c/include/plat/gpio-cfg-helpers.h +++ /dev/null @@ -1,176 +0,0 @@ -/* linux/arch/arm/plat-s3c/include/plat/gpio-cfg-helper.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C Platform - GPIO pin configuration helper definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -/* This is meant for core cpu support, machine or other driver files - * should not be including this header. - */ - -#ifndef __PLAT_GPIO_CFG_HELPERS_H -#define __PLAT_GPIO_CFG_HELPERS_H __FILE__ - -/* As a note, all gpio configuration functions are entered exclusively, either - * with the relevant lock held or the system prevented from doing anything else - * by disabling interrupts. -*/ - -static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int config) -{ - return (chip->config->set_config)(chip, off, config); -} - -static inline int s3c_gpio_do_setpull(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) -{ - return (chip->config->set_pull)(chip, off, pull); -} - -/** - * s3c_gpio_setcfg_s3c24xx - S3C24XX style GPIO configuration. - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @cfg: The configuration value to set. - * - * This helper deal with the GPIO cases where the control register - * has two bits of configuration per gpio, which have the following - * functions: - * 00 = input - * 01 = output - * 1x = special function -*/ -extern int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg); - -/** - * s3c_gpio_setcfg_s3c24xx_a - S3C24XX style GPIO configuration (Bank A) - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @cfg: The configuration value to set. - * - * This helper deal with the GPIO cases where the control register - * has one bit of configuration for the gpio, where setting the bit - * means the pin is in special function mode and unset means output. -*/ -extern int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg); - -/** - * s3c_gpio_setcfg_s3c64xx_4bit - S3C64XX 4bit single register GPIO config. - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @cfg: The configuration value to set. - * - * This helper deal with the GPIO cases where the control register has 4 bits - * of control per GPIO, generally in the form of: - * 0000 = Input - * 0001 = Output - * others = Special functions (dependant on bank) - * - * Note, since the code to deal with the case where there are two control - * registers instead of one, we do not have a seperate set of functions for - * each case. -*/ -extern int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg); - - -/* Pull-{up,down} resistor controls. - * - * S3C2410,S3C2440,S3C24A0 = Pull-UP, - * S3C2412,S3C2413 = Pull-Down - * S3C6400,S3C6410 = Pull-Both [None,Down,Up,Undef] - * S3C2443 = Pull-Both [not same as S3C6400] - */ - -/** - * s3c_gpio_setpull_1up() - Pull configuration for choice of up or none. - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @param: pull: The pull mode being requested. - * - * This is a helper function for the case where we have GPIOs with one - * bit configuring the presence of a pull-up resistor. - */ -extern int s3c_gpio_setpull_1up(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull); - -/** - * s3c_gpio_setpull_1down() - Pull configuration for choice of down or none - * @chip: The gpio chip that is being configured - * @off: The offset for the GPIO being configured - * @param: pull: The pull mode being requested - * - * This is a helper function for the case where we have GPIOs with one - * bit configuring the presence of a pull-down resistor. - */ -extern int s3c_gpio_setpull_1down(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull); - -/** - * s3c_gpio_setpull_upown() - Pull configuration for choice of up, down or none - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @param: pull: The pull mode being requested. - * - * This is a helper function for the case where we have GPIOs with two - * bits configuring the presence of a pull resistor, in the following - * order: - * 00 = No pull resistor connected - * 01 = Pull-up resistor connected - * 10 = Pull-down resistor connected - */ -extern int s3c_gpio_setpull_updown(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull); - - -/** - * s3c_gpio_getpull_updown() - Get configuration for choice of up, down or none - * @chip: The gpio chip that the GPIO pin belongs to - * @off: The offset to the pin to get the configuration of. - * - * This helper function reads the state of the pull-{up,down} resistor for the - * given GPIO in the same case as s3c_gpio_setpull_upown. -*/ -extern s3c_gpio_pull_t s3c_gpio_getpull_updown(struct s3c_gpio_chip *chip, - unsigned int off); - -/** - * s3c_gpio_setpull_s3c2443() - Pull configuration for s3c2443. - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @param: pull: The pull mode being requested. - * - * This is a helper function for the case where we have GPIOs with two - * bits configuring the presence of a pull resistor, in the following - * order: - * 00 = Pull-up resistor connected - * 10 = Pull-down resistor connected - * x1 = No pull up resistor - */ -extern int s3c_gpio_setpull_s3c2443(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull); - -/** - * s3c_gpio_getpull_s3c2443() - Get configuration for s3c2443 pull resistors - * @chip: The gpio chip that the GPIO pin belongs to. - * @off: The offset to the pin to get the configuration of. - * - * This helper function reads the state of the pull-{up,down} resistor for the - * given GPIO in the same case as s3c_gpio_setpull_upown. -*/ -extern s3c_gpio_pull_t s3c_gpio_getpull_s3c24xx(struct s3c_gpio_chip *chip, - unsigned int off); - -#endif /* __PLAT_GPIO_CFG_HELPERS_H */ - diff --git a/arch/arm/plat-s3c/include/plat/gpio-cfg.h b/arch/arm/plat-s3c/include/plat/gpio-cfg.h deleted file mode 100644 index 29cd6a8..0000000 --- a/arch/arm/plat-s3c/include/plat/gpio-cfg.h +++ /dev/null @@ -1,110 +0,0 @@ -/* linux/arch/arm/plat-s3c/include/plat/gpio-cfg.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C Platform - GPIO pin configuration - * - * 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 file contains the necessary definitions to get the basic gpio - * pin configuration done such as setting a pin to input or output or - * changing the pull-{up,down} configurations. - */ - -/* Note, this interface is being added to the s3c64xx arch first and will - * be added to the s3c24xx systems later. - */ - -#ifndef __PLAT_GPIO_CFG_H -#define __PLAT_GPIO_CFG_H __FILE__ - -typedef unsigned int __bitwise__ s3c_gpio_pull_t; - -/* forward declaration if gpio-core.h hasn't been included */ -struct s3c_gpio_chip; - -/** - * struct s3c_gpio_cfg GPIO configuration - * @cfg_eint: Configuration setting when used for external interrupt source - * @get_pull: Read the current pull configuration for the GPIO - * @set_pull: Set the current pull configuraiton for the GPIO - * @set_config: Set the current configuration for the GPIO - * @get_config: Read the current configuration for the GPIO - * - * Each chip can have more than one type of GPIO bank available and some - * have different capabilites even when they have the same control register - * layouts. Provide an point to vector control routine and provide any - * per-bank configuration information that other systems such as the - * external interrupt code will need. - */ -struct s3c_gpio_cfg { - unsigned int cfg_eint; - - s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs); - int (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs, - s3c_gpio_pull_t pull); - - unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs); - int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs, - unsigned config); -}; - -#define S3C_GPIO_SPECIAL_MARK (0xfffffff0) -#define S3C_GPIO_SPECIAL(x) (S3C_GPIO_SPECIAL_MARK | (x)) - -/* Defines for generic pin configurations */ -#define S3C_GPIO_INPUT (S3C_GPIO_SPECIAL(0)) -#define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1)) -#define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x)) - -#define s3c_gpio_is_cfg_special(_cfg) \ - (((_cfg) & S3C_GPIO_SPECIAL_MARK) == S3C_GPIO_SPECIAL_MARK) - -/** - * s3c_gpio_cfgpin() - Change the GPIO function of a pin. - * @pin pin The pin number to configure. - * @pin to The configuration for the pin's function. - * - * Configure which function is actually connected to the external - * pin, such as an gpio input, output or some form of special function - * connected to an internal peripheral block. - */ -extern int s3c_gpio_cfgpin(unsigned int pin, unsigned int to); - -/* Define values for the pull-{up,down} available for each gpio pin. - * - * These values control the state of the weak pull-{up,down} resistors - * available on most pins on the S3C series. Not all chips support both - * up or down settings, and it may be dependant on the chip that is being - * used to whether the particular mode is available. - */ -#define S3C_GPIO_PULL_NONE ((__force s3c_gpio_pull_t)0x00) -#define S3C_GPIO_PULL_DOWN ((__force s3c_gpio_pull_t)0x01) -#define S3C_GPIO_PULL_UP ((__force s3c_gpio_pull_t)0x02) - -/** - * s3c_gpio_setpull() - set the state of a gpio pin pull resistor - * @pin: The pin number to configure the pull resistor. - * @pull: The configuration for the pull resistor. - * - * This function sets the state of the pull-{up,down} resistor for the - * specified pin. It will return 0 if successfull, or a negative error - * code if the pin cannot support the requested pull setting. -*/ -extern int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull); - -/** - * s3c_gpio_getpull() - get the pull resistor state of a gpio pin - * @pin: The pin number to get the settings for - * - * Read the pull resistor value for the specified pin. -*/ -extern s3c_gpio_pull_t s3c_gpio_getpull(unsigned int pin); - -#endif /* __PLAT_GPIO_CFG_H */ diff --git a/arch/arm/plat-s3c/include/plat/gpio-core.h b/arch/arm/plat-s3c/include/plat/gpio-core.h deleted file mode 100644 index 94fed58..0000000 --- a/arch/arm/plat-s3c/include/plat/gpio-core.h +++ /dev/null @@ -1,135 +0,0 @@ -/* linux/arch/arm/plat-s3c/include/plat/gpio-core.h - * - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C Platform - GPIO core - * - * 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. -*/ - -#define GPIOCON_OFF (0x00) -#define GPIODAT_OFF (0x04) - -#define con_4bit_shift(__off) ((__off) * 4) - -/* Define the core gpiolib support functions that the s3c platforms may - * need to extend or change depending on the hardware and the s3c chip - * selected at build or found at run time. - * - * These definitions are not intended for driver inclusion, there is - * nothing here that should not live outside the platform and core - * specific code. -*/ - -struct s3c_gpio_chip; - -/** - * struct s3c_gpio_pm - power management (suspend/resume) information - * @save: Routine to save the state of the GPIO block - * @resume: Routine to resume the GPIO block. - */ -struct s3c_gpio_pm { - void (*save)(struct s3c_gpio_chip *chip); - void (*resume)(struct s3c_gpio_chip *chip); -}; - -struct s3c_gpio_cfg; - -/** - * struct s3c_gpio_chip - wrapper for specific implementation of gpio - * @chip: The chip structure to be exported via gpiolib. - * @base: The base pointer to the gpio configuration registers. - * @config: special function and pull-resistor control information. - * @pm_save: Save information for suspend/resume support. - * - * This wrapper provides the necessary information for the Samsung - * specific gpios being registered with gpiolib. - */ -struct s3c_gpio_chip { - struct gpio_chip chip; - struct s3c_gpio_cfg *config; - struct s3c_gpio_pm *pm; - void __iomem *base; -#ifdef CONFIG_PM - u32 pm_save[4]; -#endif -}; - -static inline struct s3c_gpio_chip *to_s3c_gpio(struct gpio_chip *gpc) -{ - return container_of(gpc, struct s3c_gpio_chip, chip); -} - -/** s3c_gpiolib_add() - add the s3c specific version of a gpio_chip. - * @chip: The chip to register - * - * This is a wrapper to gpiochip_add() that takes our specific gpio chip - * information and makes the necessary alterations for the platform and - * notes the information for use with the configuration systems and any - * other parts of the system. - */ -extern void s3c_gpiolib_add(struct s3c_gpio_chip *chip); - -/* CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios - * for use with the configuration calls, and other parts of the s3c gpiolib - * support code. - * - * Not all s3c support code will need this, as some configurations of cpu - * may only support one or two different configuration options and have an - * easy gpio to s3c_gpio_chip mapping function. If this is the case, then - * the machine support file should provide its own s3c_gpiolib_getchip() - * and any other necessary functions. - */ - -/** - * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config. - * @chip: The gpio chip that is being configured. - * @nr_chips: The no of chips (gpio ports) for the GPIO being configured. - * - * This helper deal with the GPIO cases where the control register has 4 bits - * of control per GPIO, generally in the form of: - * 0000 = Input - * 0001 = Output - * others = Special functions (dependant on bank) - * - * Note, since the code to deal with the case where there are two control - * registers instead of one, we do not have a seperate set of function - * (samsung_gpiolib_add_4bit2_chips)for each case. - */ -extern void samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, - int nr_chips); -extern void samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip, - int nr_chips); - -extern void samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip); -extern void samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip); - -#ifdef CONFIG_S3C_GPIO_TRACK -extern struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; - -static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int chip) -{ - return (chip < S3C_GPIO_END) ? s3c_gpios[chip] : NULL; -} -#else -/* machine specific code should provide s3c_gpiolib_getchip */ - -static inline void s3c_gpiolib_track(struct s3c_gpio_chip *chip) { } -#endif - -#ifdef CONFIG_PM -extern struct s3c_gpio_pm s3c_gpio_pm_1bit; -extern struct s3c_gpio_pm s3c_gpio_pm_2bit; -extern struct s3c_gpio_pm s3c_gpio_pm_4bit; -#define __gpio_pm(x) x -#else -#define s3c_gpio_pm_1bit NULL -#define s3c_gpio_pm_2bit NULL -#define s3c_gpio_pm_4bit NULL -#define __gpio_pm(x) NULL - -#endif /* CONFIG_PM */ diff --git a/arch/arm/plat-samsung/include/plat/gpio-cfg-helpers.h b/arch/arm/plat-samsung/include/plat/gpio-cfg-helpers.h new file mode 100644 index 0000000..652e2bb --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/gpio-cfg-helpers.h @@ -0,0 +1,176 @@ +/* linux/arch/arm/plat-s3c/include/plat/gpio-cfg-helper.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C Platform - GPIO pin configuration helper definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +/* This is meant for core cpu support, machine or other driver files + * should not be including this header. + */ + +#ifndef __PLAT_GPIO_CFG_HELPERS_H +#define __PLAT_GPIO_CFG_HELPERS_H __FILE__ + +/* As a note, all gpio configuration functions are entered exclusively, either + * with the relevant lock held or the system prevented from doing anything else + * by disabling interrupts. +*/ + +static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int config) +{ + return (chip->config->set_config)(chip, off, config); +} + +static inline int s3c_gpio_do_setpull(struct s3c_gpio_chip *chip, + unsigned int off, s3c_gpio_pull_t pull) +{ + return (chip->config->set_pull)(chip, off, pull); +} + +/** + * s3c_gpio_setcfg_s3c24xx - S3C24XX style GPIO configuration. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register + * has two bits of configuration per gpio, which have the following + * functions: + * 00 = input + * 01 = output + * 1x = special function +*/ +extern int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int cfg); + +/** + * s3c_gpio_setcfg_s3c24xx_a - S3C24XX style GPIO configuration (Bank A) + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register + * has one bit of configuration for the gpio, where setting the bit + * means the pin is in special function mode and unset means output. +*/ +extern int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int cfg); + +/** + * s3c_gpio_setcfg_s3c64xx_4bit - S3C64XX 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependant on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a seperate set of functions for + * each case. +*/ +extern int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, + unsigned int off, unsigned int cfg); + + +/* Pull-{up,down} resistor controls. + * + * S3C2410,S3C2440,S3C24A0 = Pull-UP, + * S3C2412,S3C2413 = Pull-Down + * S3C6400,S3C6410 = Pull-Both [None,Down,Up,Undef] + * S3C2443 = Pull-Both [not same as S3C6400] + */ + +/** + * s3c_gpio_setpull_1up() - Pull configuration for choice of up or none. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @param: pull: The pull mode being requested. + * + * This is a helper function for the case where we have GPIOs with one + * bit configuring the presence of a pull-up resistor. + */ +extern int s3c_gpio_setpull_1up(struct s3c_gpio_chip *chip, + unsigned int off, s3c_gpio_pull_t pull); + +/** + * s3c_gpio_setpull_1down() - Pull configuration for choice of down or none + * @chip: The gpio chip that is being configured + * @off: The offset for the GPIO being configured + * @param: pull: The pull mode being requested + * + * This is a helper function for the case where we have GPIOs with one + * bit configuring the presence of a pull-down resistor. + */ +extern int s3c_gpio_setpull_1down(struct s3c_gpio_chip *chip, + unsigned int off, s3c_gpio_pull_t pull); + +/** + * s3c_gpio_setpull_upown() - Pull configuration for choice of up, down or none + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @param: pull: The pull mode being requested. + * + * This is a helper function for the case where we have GPIOs with two + * bits configuring the presence of a pull resistor, in the following + * order: + * 00 = No pull resistor connected + * 01 = Pull-up resistor connected + * 10 = Pull-down resistor connected + */ +extern int s3c_gpio_setpull_updown(struct s3c_gpio_chip *chip, + unsigned int off, s3c_gpio_pull_t pull); + + +/** + * s3c_gpio_getpull_updown() - Get configuration for choice of up, down or none + * @chip: The gpio chip that the GPIO pin belongs to + * @off: The offset to the pin to get the configuration of. + * + * This helper function reads the state of the pull-{up,down} resistor for the + * given GPIO in the same case as s3c_gpio_setpull_upown. +*/ +extern s3c_gpio_pull_t s3c_gpio_getpull_updown(struct s3c_gpio_chip *chip, + unsigned int off); + +/** + * s3c_gpio_setpull_s3c2443() - Pull configuration for s3c2443. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @param: pull: The pull mode being requested. + * + * This is a helper function for the case where we have GPIOs with two + * bits configuring the presence of a pull resistor, in the following + * order: + * 00 = Pull-up resistor connected + * 10 = Pull-down resistor connected + * x1 = No pull up resistor + */ +extern int s3c_gpio_setpull_s3c2443(struct s3c_gpio_chip *chip, + unsigned int off, s3c_gpio_pull_t pull); + +/** + * s3c_gpio_getpull_s3c2443() - Get configuration for s3c2443 pull resistors + * @chip: The gpio chip that the GPIO pin belongs to. + * @off: The offset to the pin to get the configuration of. + * + * This helper function reads the state of the pull-{up,down} resistor for the + * given GPIO in the same case as s3c_gpio_setpull_upown. +*/ +extern s3c_gpio_pull_t s3c_gpio_getpull_s3c24xx(struct s3c_gpio_chip *chip, + unsigned int off); + +#endif /* __PLAT_GPIO_CFG_HELPERS_H */ + diff --git a/arch/arm/plat-samsung/include/plat/gpio-cfg.h b/arch/arm/plat-samsung/include/plat/gpio-cfg.h new file mode 100644 index 0000000..29cd6a8 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/gpio-cfg.h @@ -0,0 +1,110 @@ +/* linux/arch/arm/plat-s3c/include/plat/gpio-cfg.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C Platform - GPIO pin configuration + * + * 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 file contains the necessary definitions to get the basic gpio + * pin configuration done such as setting a pin to input or output or + * changing the pull-{up,down} configurations. + */ + +/* Note, this interface is being added to the s3c64xx arch first and will + * be added to the s3c24xx systems later. + */ + +#ifndef __PLAT_GPIO_CFG_H +#define __PLAT_GPIO_CFG_H __FILE__ + +typedef unsigned int __bitwise__ s3c_gpio_pull_t; + +/* forward declaration if gpio-core.h hasn't been included */ +struct s3c_gpio_chip; + +/** + * struct s3c_gpio_cfg GPIO configuration + * @cfg_eint: Configuration setting when used for external interrupt source + * @get_pull: Read the current pull configuration for the GPIO + * @set_pull: Set the current pull configuraiton for the GPIO + * @set_config: Set the current configuration for the GPIO + * @get_config: Read the current configuration for the GPIO + * + * Each chip can have more than one type of GPIO bank available and some + * have different capabilites even when they have the same control register + * layouts. Provide an point to vector control routine and provide any + * per-bank configuration information that other systems such as the + * external interrupt code will need. + */ +struct s3c_gpio_cfg { + unsigned int cfg_eint; + + s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs); + int (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs, + s3c_gpio_pull_t pull); + + unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs); + int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs, + unsigned config); +}; + +#define S3C_GPIO_SPECIAL_MARK (0xfffffff0) +#define S3C_GPIO_SPECIAL(x) (S3C_GPIO_SPECIAL_MARK | (x)) + +/* Defines for generic pin configurations */ +#define S3C_GPIO_INPUT (S3C_GPIO_SPECIAL(0)) +#define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1)) +#define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x)) + +#define s3c_gpio_is_cfg_special(_cfg) \ + (((_cfg) & S3C_GPIO_SPECIAL_MARK) == S3C_GPIO_SPECIAL_MARK) + +/** + * s3c_gpio_cfgpin() - Change the GPIO function of a pin. + * @pin pin The pin number to configure. + * @pin to The configuration for the pin's function. + * + * Configure which function is actually connected to the external + * pin, such as an gpio input, output or some form of special function + * connected to an internal peripheral block. + */ +extern int s3c_gpio_cfgpin(unsigned int pin, unsigned int to); + +/* Define values for the pull-{up,down} available for each gpio pin. + * + * These values control the state of the weak pull-{up,down} resistors + * available on most pins on the S3C series. Not all chips support both + * up or down settings, and it may be dependant on the chip that is being + * used to whether the particular mode is available. + */ +#define S3C_GPIO_PULL_NONE ((__force s3c_gpio_pull_t)0x00) +#define S3C_GPIO_PULL_DOWN ((__force s3c_gpio_pull_t)0x01) +#define S3C_GPIO_PULL_UP ((__force s3c_gpio_pull_t)0x02) + +/** + * s3c_gpio_setpull() - set the state of a gpio pin pull resistor + * @pin: The pin number to configure the pull resistor. + * @pull: The configuration for the pull resistor. + * + * This function sets the state of the pull-{up,down} resistor for the + * specified pin. It will return 0 if successfull, or a negative error + * code if the pin cannot support the requested pull setting. +*/ +extern int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull); + +/** + * s3c_gpio_getpull() - get the pull resistor state of a gpio pin + * @pin: The pin number to get the settings for + * + * Read the pull resistor value for the specified pin. +*/ +extern s3c_gpio_pull_t s3c_gpio_getpull(unsigned int pin); + +#endif /* __PLAT_GPIO_CFG_H */ diff --git a/arch/arm/plat-samsung/include/plat/gpio-core.h b/arch/arm/plat-samsung/include/plat/gpio-core.h new file mode 100644 index 0000000..94fed58 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/gpio-core.h @@ -0,0 +1,135 @@ +/* linux/arch/arm/plat-s3c/include/plat/gpio-core.h + * + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C Platform - GPIO core + * + * 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. +*/ + +#define GPIOCON_OFF (0x00) +#define GPIODAT_OFF (0x04) + +#define con_4bit_shift(__off) ((__off) * 4) + +/* Define the core gpiolib support functions that the s3c platforms may + * need to extend or change depending on the hardware and the s3c chip + * selected at build or found at run time. + * + * These definitions are not intended for driver inclusion, there is + * nothing here that should not live outside the platform and core + * specific code. +*/ + +struct s3c_gpio_chip; + +/** + * struct s3c_gpio_pm - power management (suspend/resume) information + * @save: Routine to save the state of the GPIO block + * @resume: Routine to resume the GPIO block. + */ +struct s3c_gpio_pm { + void (*save)(struct s3c_gpio_chip *chip); + void (*resume)(struct s3c_gpio_chip *chip); +}; + +struct s3c_gpio_cfg; + +/** + * struct s3c_gpio_chip - wrapper for specific implementation of gpio + * @chip: The chip structure to be exported via gpiolib. + * @base: The base pointer to the gpio configuration registers. + * @config: special function and pull-resistor control information. + * @pm_save: Save information for suspend/resume support. + * + * This wrapper provides the necessary information for the Samsung + * specific gpios being registered with gpiolib. + */ +struct s3c_gpio_chip { + struct gpio_chip chip; + struct s3c_gpio_cfg *config; + struct s3c_gpio_pm *pm; + void __iomem *base; +#ifdef CONFIG_PM + u32 pm_save[4]; +#endif +}; + +static inline struct s3c_gpio_chip *to_s3c_gpio(struct gpio_chip *gpc) +{ + return container_of(gpc, struct s3c_gpio_chip, chip); +} + +/** s3c_gpiolib_add() - add the s3c specific version of a gpio_chip. + * @chip: The chip to register + * + * This is a wrapper to gpiochip_add() that takes our specific gpio chip + * information and makes the necessary alterations for the platform and + * notes the information for use with the configuration systems and any + * other parts of the system. + */ +extern void s3c_gpiolib_add(struct s3c_gpio_chip *chip); + +/* CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios + * for use with the configuration calls, and other parts of the s3c gpiolib + * support code. + * + * Not all s3c support code will need this, as some configurations of cpu + * may only support one or two different configuration options and have an + * easy gpio to s3c_gpio_chip mapping function. If this is the case, then + * the machine support file should provide its own s3c_gpiolib_getchip() + * and any other necessary functions. + */ + +/** + * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @nr_chips: The no of chips (gpio ports) for the GPIO being configured. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependant on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a seperate set of function + * (samsung_gpiolib_add_4bit2_chips)for each case. + */ +extern void samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, + int nr_chips); +extern void samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip, + int nr_chips); + +extern void samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip); +extern void samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip); + +#ifdef CONFIG_S3C_GPIO_TRACK +extern struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; + +static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int chip) +{ + return (chip < S3C_GPIO_END) ? s3c_gpios[chip] : NULL; +} +#else +/* machine specific code should provide s3c_gpiolib_getchip */ + +static inline void s3c_gpiolib_track(struct s3c_gpio_chip *chip) { } +#endif + +#ifdef CONFIG_PM +extern struct s3c_gpio_pm s3c_gpio_pm_1bit; +extern struct s3c_gpio_pm s3c_gpio_pm_2bit; +extern struct s3c_gpio_pm s3c_gpio_pm_4bit; +#define __gpio_pm(x) x +#else +#define s3c_gpio_pm_1bit NULL +#define s3c_gpio_pm_2bit NULL +#define s3c_gpio_pm_4bit NULL +#define __gpio_pm(x) NULL + +#endif /* CONFIG_PM */ -- cgit v0.10.2 From e856bb1f8dfcd924a973102a244b81920752648e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 19 Jan 2010 17:14:46 +0900 Subject: ARM: SAMSUNG: Remove empty gpio-core.h and use central plat/gpio-core.h Rename mach-s3c2410/include/mach/gpio-core.h to mach/gpio-track.h so that it can be included by when needed. Eliminate all other empty gpio-core.h files and just include the as necessary. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c2410/include/mach/gpio-core.h b/arch/arm/mach-s3c2410/include/mach/gpio-core.h deleted file mode 100644 index f8b879a..0000000 --- a/arch/arm/mach-s3c2410/include/mach/gpio-core.h +++ /dev/null @@ -1,34 +0,0 @@ -/* arch/arm/mach-s3c24100/include/mach/gpio-core.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C2410 - GPIO core 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 __ASM_ARCH_GPIO_CORE_H -#define __ASM_ARCH_GPIO_CORE_H __FILE__ - -#include -#include - -extern struct s3c_gpio_chip s3c24xx_gpios[]; - -static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin) -{ - struct s3c_gpio_chip *chip; - - if (pin > S3C2410_GPG(10)) - return NULL; - - chip = &s3c24xx_gpios[pin/32]; - return (S3C2410_GPIO_OFFSET(pin) < chip->chip.ngpio) ? chip : NULL; -} - -#endif /* __ASM_ARCH_GPIO_CORE_H */ diff --git a/arch/arm/mach-s3c2410/include/mach/gpio-track.h b/arch/arm/mach-s3c2410/include/mach/gpio-track.h new file mode 100644 index 0000000..acb2591 --- /dev/null +++ b/arch/arm/mach-s3c2410/include/mach/gpio-track.h @@ -0,0 +1,33 @@ +/* arch/arm/mach-s3c24100/include/mach/gpio-core.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C2410 - GPIO core 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 __ASM_ARCH_GPIO_CORE_H +#define __ASM_ARCH_GPIO_CORE_H __FILE__ + +#include + +extern struct s3c_gpio_chip s3c24xx_gpios[]; + +static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin) +{ + struct s3c_gpio_chip *chip; + + if (pin > S3C2410_GPG(10)) + return NULL; + + chip = &s3c24xx_gpios[pin/32]; + return (S3C2410_GPIO_OFFSET(pin) < chip->chip.ngpio) ? chip : NULL; +} + +#endif /* __ASM_ARCH_GPIO_CORE_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/gpio-core.h b/arch/arm/mach-s3c6400/include/mach/gpio-core.h deleted file mode 100644 index d89aae6..0000000 --- a/arch/arm/mach-s3c6400/include/mach/gpio-core.h +++ /dev/null @@ -1,21 +0,0 @@ -/* arch/arm/mach-s3c6400/include/mach/gpio-core.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C64XX - GPIO core 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 __ASM_ARCH_GPIO_CORE_H -#define __ASM_ARCH_GPIO_CORE_H __FILE__ - -/* currently we just include the platform support */ -#include - -#endif /* __ASM_ARCH_GPIO_CORE_H */ diff --git a/arch/arm/mach-s5p6440/include/mach/gpio-core.h b/arch/arm/mach-s5p6440/include/mach/gpio-core.h deleted file mode 100644 index ff7fb30..0000000 --- a/arch/arm/mach-s5p6440/include/mach/gpio-core.h +++ /dev/null @@ -1,19 +0,0 @@ -/* linux/arch/arm/mach-s5p6440/include/mach/gpio-core.h - * - * Copyright (c) 2009 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * S5P6440 - GPIO core 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 __ASM_ARCH_GPIO_CORE_H -#define __ASM_ARCH_GPIO_CORE_H __FILE__ - -/* currently we just include the platform support */ -#include - -#endif /* __ASM_ARCH_GPIO_CORE_H */ diff --git a/arch/arm/mach-s5pc100/include/mach/gpio-core.h b/arch/arm/mach-s5pc100/include/mach/gpio-core.h deleted file mode 100644 index ad28d8e..0000000 --- a/arch/arm/mach-s5pc100/include/mach/gpio-core.h +++ /dev/null @@ -1,21 +0,0 @@ -/* arch/arm/mach-s5pc100/include/mach/gpio-core.h - * - * Copyright 2009 Samsung Electronics Co. - * Byungho Min - * - * S5PC100 - GPIO core support - * - * Based on mach-s3c6400/include/mach/gpio-core.h - * - * 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 __ASM_ARCH_GPIO_CORE_H -#define __ASM_ARCH_GPIO_CORE_H __FILE__ - -/* currently we just include the platform support */ -#include - -#endif /* __ASM_ARCH_GPIO_CORE_H */ diff --git a/arch/arm/plat-s3c24xx/gpiolib.c b/arch/arm/plat-s3c24xx/gpiolib.c index 6d7a961..4f0f11a 100644 --- a/arch/arm/plat-s3c24xx/gpiolib.c +++ b/arch/arm/plat-s3c24xx/gpiolib.c @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/plat-s3c64xx/gpiolib.c b/arch/arm/plat-s3c64xx/gpiolib.c index 265e23b..b6e3f55 100644 --- a/arch/arm/plat-s3c64xx/gpiolib.c +++ b/arch/arm/plat-s3c64xx/gpiolib.c @@ -18,8 +18,8 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/plat-s5pc1xx/gpio-config.c b/arch/arm/plat-s5pc1xx/gpio-config.c index bba675d..a4f67e8 100644 --- a/arch/arm/plat-s5pc1xx/gpio-config.c +++ b/arch/arm/plat-s5pc1xx/gpio-config.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin, unsigned int off) diff --git a/arch/arm/plat-s5pc1xx/gpiolib.c b/arch/arm/plat-s5pc1xx/gpiolib.c index facb410..1ffc57a 100644 --- a/arch/arm/plat-s5pc1xx/gpiolib.c +++ b/arch/arm/plat-s5pc1xx/gpiolib.c @@ -17,8 +17,8 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/plat-samsung/gpio-config.c b/arch/arm/plat-samsung/gpio-config.c index 456969b..44a84e8 100644 --- a/arch/arm/plat-samsung/gpio-config.c +++ b/arch/arm/plat-samsung/gpio-config.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/plat-samsung/gpio.c b/arch/arm/plat-samsung/gpio.c index 5ff24e0..28d2ab8 100644 --- a/arch/arm/plat-samsung/gpio.c +++ b/arch/arm/plat-samsung/gpio.c @@ -16,7 +16,7 @@ #include #include -#include +#include #ifdef CONFIG_S3C_GPIO_TRACK struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; diff --git a/arch/arm/plat-samsung/include/plat/gpio-core.h b/arch/arm/plat-samsung/include/plat/gpio-core.h index 94fed58..49ff406 100644 --- a/arch/arm/plat-samsung/include/plat/gpio-core.h +++ b/arch/arm/plat-samsung/include/plat/gpio-core.h @@ -118,6 +118,8 @@ static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int chip) #else /* machine specific code should provide s3c_gpiolib_getchip */ +#include + static inline void s3c_gpiolib_track(struct s3c_gpio_chip *chip) { } #endif diff --git a/arch/arm/plat-samsung/pm-gpio.c b/arch/arm/plat-samsung/pm-gpio.c index cfd326a..69a4c7f 100644 --- a/arch/arm/plat-samsung/pm-gpio.c +++ b/arch/arm/plat-samsung/pm-gpio.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include /* PM GPIO helpers */ -- cgit v0.10.2 From af1519408a0d40f312e8f103504cd292cc49d927 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 19 Jan 2010 11:21:39 +0900 Subject: ARM: S5P6440: Add IO descriptor for ChipID block. This patch adds the IO descriptor for ChipID block in S5P6440. Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5p6440/include/mach/map.h b/arch/arm/mach-s5p6440/include/mach/map.h index b370329..dc922d6 100644 --- a/arch/arm/mach-s5p6440/include/mach/map.h +++ b/arch/arm/mach-s5p6440/include/mach/map.h @@ -15,6 +15,11 @@ #include +/* Chip ID */ +#define S5P6440_PA_CHIPID (0xE0000000) +#define S5P_PA_CHIPID S5P6440_PA_CHIPID +#define S5P_VA_CHIPID S3C_ADDR(0x00700000) + /* SYSCON */ #define S5P6440_PA_SYSCON (0xE0100000) #define S5P_PA_SYSCON S5P6440_PA_SYSCON -- cgit v0.10.2 From a0c814afcfc2c840698372bd402b669b659ee1c5 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 23 Nov 2009 23:32:31 +0000 Subject: ARM: SAMSUNG: Add initial documentation directory and overview Start Documentation/arm/Samsung and add an initial overview file which whilst is not complete, is better than nothing. Signed-off-by: Ben Dooks diff --git a/Documentation/arm/Samsung/Overview.txt b/Documentation/arm/Samsung/Overview.txt new file mode 100644 index 0000000..7cced1f --- /dev/null +++ b/Documentation/arm/Samsung/Overview.txt @@ -0,0 +1,86 @@ + Samsung ARM Linux Overview + ========================== + +Introduction +------------ + + The Samsung range of ARM SoCs spans many similar devices, from the initial + ARM9 through to the newest ARM cores. This document shows an overview of + the current kernel support, how to use it and where to find the code + that supports this. + + The currently supported SoCs are: + + - S3C24XX: See Documentation/arm/Samsung-S3C24XX/Overview.txt for full list + - S3C64XX: S3C6400 and S3C6410 + - S5PC6440 + + S5PC100 and S5PC110 support is currently being merged + + +S3C24XX Systems +--------------- + + There is still documentation in Documnetation/arm/Samsung-S3C24XX/ which + deals with the architecture and drivers specific to these devices. + + See Documentation/arm/Samsung-S3C24XX/Overview.txt for more information + on the implementation details and specific support. + + +Configuration +------------- + + A number of configurations are supplied, as there is no current way of + unifying all the SoCs into one kernel. + + s5p6440_defconfig - S5P6440 specific default configuration + s5pc100_defconfig - S5PC100 specific default configuration + + +Layout +------ + + The directory layout is currently being restructured, and consists of + several platform directories and then the machine specific directories + of the CPUs being built for. + + plat-samsung provides the base for all the implementations, and is the + last in the line of include directories that are processed for the build + specific information. It contains the base clock, GPIO and device definitions + to get the system running. + + plat-s3c is the s3c24xx/s3c64xx platform directory, although it is currently + involved in other builds this will be phased out once the relevant code is + moved elsewhere. + + plat-s3c24xx is for s3c24xx specific builds, see the S3C24XX docs. + + plat-s3c64xx is for the s3c64xx specific bits, see the S3C24XX docs. + + plat-s5p is for s5p specific builds, more to be added. + + + [ to finish ] + + +Port Contributors +----------------- + + Ben Dooks (BJD) + Vincent Sanders + Herbert Potzl + Arnaud Patard (RTP) + Roc Wu + Klaus Fetscher + Dimitry Andric + Shannon Holland + Guillaume Gourat (NexVision) + Christer Weinigel (wingel) (Acer N30) + Lucas Correia Villa Real (S3C2400 port) + + +Document Author +--------------- + +Copyright 2009-2010 Ben Dooks -- cgit v0.10.2 From 5b8a2fb34f5670b1f07483bfa40de9ce539dbdb2 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sun, 17 Jan 2010 20:25:36 +0100 Subject: msm_sdcc.c: missing brackets in msmsdcc_resume() Since the curly brackets were missing the test and enable_irq(), if true, occurred twice. Signed-off-by: Roel Kluin diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b31946e..4c068e5 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1250,9 +1250,7 @@ msmsdcc_resume(struct platform_device *dev) if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) mmc_resume_host(mmc); - if (host->stat_irq) - enable_irq(host->stat_irq); - else if (host->stat_irq) + if (host->stat_irq) enable_irq(host->stat_irq); } return 0; -- cgit v0.10.2 From 6890556c140469622497bea98cf49bf93f92f922 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 20 Jan 2010 11:09:28 +0900 Subject: ARM: SAMSUNG: Move more support into plat-samsung Move header files which are not likely to be touched in any further support addition out of plat-s3c's include directory into plat-samsung. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/include/plat/dma-core.h b/arch/arm/plat-s3c/include/plat/dma-core.h deleted file mode 100644 index 32ff2a9..0000000 --- a/arch/arm/plat-s3c/include/plat/dma-core.h +++ /dev/null @@ -1,22 +0,0 @@ -/* arch/arm/plat-s3c/include/plat/dma.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * Samsung S3C DMA core 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. -*/ - -extern struct s3c2410_dma_chan *s3c_dma_lookup_channel(unsigned int channel); - -extern struct s3c2410_dma_chan *s3c_dma_chan_map[]; - -/* the currently allocated channel information */ -extern struct s3c2410_dma_chan s3c2410_chans[]; - - diff --git a/arch/arm/plat-s3c/include/plat/hwmon.h b/arch/arm/plat-s3c/include/plat/hwmon.h deleted file mode 100644 index 1ba88ea..0000000 --- a/arch/arm/plat-s3c/include/plat/hwmon.h +++ /dev/null @@ -1,41 +0,0 @@ -/* linux/arch/arm/plat-s3c/include/plat/hwmon.h - * - * Copyright 2005 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C - HWMon interface for ADC - * - * 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 __ASM_ARCH_ADC_HWMON_H -#define __ASM_ARCH_ADC_HWMON_H __FILE__ - -/** - * s3c_hwmon_chcfg - channel configuration - * @name: The name to give this channel. - * @mult: Multiply the ADC value read by this. - * @div: Divide the value from the ADC by this. - * - * The value read from the ADC is converted to a value that - * hwmon expects (mV) by result = (value_read * @mult) / @div. - */ -struct s3c_hwmon_chcfg { - const char *name; - unsigned int mult; - unsigned int div; -}; - -/** - * s3c_hwmon_pdata - HWMON platform data - * @in: One configuration for each possible channel used. - */ -struct s3c_hwmon_pdata { - struct s3c_hwmon_chcfg *in[8]; -}; - -#endif /* __ASM_ARCH_ADC_HWMON_H */ - diff --git a/arch/arm/plat-s3c/include/plat/iic-core.h b/arch/arm/plat-s3c/include/plat/iic-core.h deleted file mode 100644 index 36397ca..0000000 --- a/arch/arm/plat-s3c/include/plat/iic-core.h +++ /dev/null @@ -1,35 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/iic-core.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * - * S3C - I2C Controller core functions - * - * 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 __ASM_ARCH_IIC_CORE_H -#define __ASM_ARCH_IIC_CORE_H __FILE__ - -/* These functions are only for use with the core support code, such as - * the cpu specific initialisation code - */ - -/* re-define device name depending on support. */ -static inline void s3c_i2c0_setname(char *name) -{ - /* currently this device is always compiled in */ - s3c_device_i2c0.name = name; -} - -static inline void s3c_i2c1_setname(char *name) -{ -#ifdef CONFIG_S3C_DEV_I2C1 - s3c_device_i2c1.name = name; -#endif -} - -#endif /* __ASM_ARCH_IIC_H */ diff --git a/arch/arm/plat-s3c/include/plat/iic.h b/arch/arm/plat-s3c/include/plat/iic.h deleted file mode 100644 index 3083df0..0000000 --- a/arch/arm/plat-s3c/include/plat/iic.h +++ /dev/null @@ -1,62 +0,0 @@ -/* arch/arm/plat-s3c/include/plat/iic.h - * - * Copyright 2004-2009 Simtec Electronics - * Ben Dooks - * - * S3C - I2C Controller platform_device info - * - * 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 __ASM_ARCH_IIC_H -#define __ASM_ARCH_IIC_H __FILE__ - -#define S3C_IICFLG_FILTER (1<<0) /* enable s3c2440 filter */ - -/** - * struct s3c2410_platform_i2c - Platform data for s3c I2C. - * @bus_num: The bus number to use (if possible). - * @flags: Any flags for the I2C bus (E.g. S3C_IICFLK_FILTER). - * @slave_addr: The I2C address for the slave device (if enabled). - * @frequency: The desired frequency in Hz of the bus. This is - * guaranteed to not be exceeded. If the caller does - * not care, use zero and the driver will select a - * useful default. - * @sda_delay: The delay (in ns) applied to SDA edges. - * @cfg_gpio: A callback to configure the pins for I2C operation. - */ -struct s3c2410_platform_i2c { - int bus_num; - unsigned int flags; - unsigned int slave_addr; - unsigned long frequency; - unsigned int sda_delay; - - void (*cfg_gpio)(struct platform_device *dev); -}; - -/** - * s3c_i2c0_set_platdata - set platform data for i2c0 device - * @i2c: The platform data to set, or NULL for default data. - * - * Register the given platform data for use with the i2c0 device. This - * call copies the platform data, so the caller can use __initdata for - * their copy. - * - * This call will set cfg_gpio if is null to the default platform - * implementation. - * - * Any user of s3c_device_i2c0 should call this, even if it is with - * NULL to ensure that the device is given the default platform data - * as the driver will no longer carry defaults. - */ -extern void s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *i2c); -extern void s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *i2c); - -/* defined by architecture to configure gpio */ -extern void s3c_i2c0_cfg_gpio(struct platform_device *dev); -extern void s3c_i2c1_cfg_gpio(struct platform_device *dev); - -#endif /* __ASM_ARCH_IIC_H */ diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h deleted file mode 100644 index 226147b..0000000 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ /dev/null @@ -1,67 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/nand.h - * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks - * - * S3C2410 - NAND device controller platfrom_device info - * - * 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. -*/ - -/** - * struct s3c2410_nand_set - define a set of one or more nand chips - * @disable_ecc: Entirely disable ECC - Dangerous - * @flash_bbt: Openmoko u-boot can create a Bad Block Table - * Setting this flag will allow the kernel to - * look for it at boot time and also skip the NAND - * scan. - * @options: Default value to set into 'struct nand_chip' options. - * @nr_chips: Number of chips in this set - * @nr_partitions: Number of partitions pointed to by @partitions - * @name: Name of set (optional) - * @nr_map: Map for low-layer logical to physical chip numbers (option) - * @partitions: The mtd partition list - * - * define a set of one or more nand chips registered with an unique mtd. Also - * allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger - * a warning at boot time. - */ -struct s3c2410_nand_set { - unsigned int disable_ecc:1; - unsigned int flash_bbt:1; - - unsigned int options; - int nr_chips; - int nr_partitions; - char *name; - int *nr_map; - struct mtd_partition *partitions; - struct nand_ecclayout *ecc_layout; -}; - -struct s3c2410_platform_nand { - /* timing information for controller, all times in nanoseconds */ - - int tacls; /* time for active CLE/ALE to nWE/nOE */ - int twrph0; /* active time for nWE/nOE */ - int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ - - unsigned int ignore_unset_ecc:1; - - int nr_sets; - struct s3c2410_nand_set *sets; - - void (*select_chip)(struct s3c2410_nand_set *, - int chip); -}; - -/** - * s3c_nand_set_platdata() - register NAND platform data. - * @nand: The NAND platform data to register with s3c_device_nand. - * - * This function copies the given NAND platform data, @nand and registers - * it with the s3c_device_nand. This allows @nand to be __initdata. -*/ -extern void s3c_nand_set_platdata(struct s3c2410_platform_nand *nand); diff --git a/arch/arm/plat-s3c/include/plat/regs-ac97.h b/arch/arm/plat-s3c/include/plat/regs-ac97.h deleted file mode 100644 index c3878f7..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-ac97.h +++ /dev/null @@ -1,67 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/regs-ac97.h - * - * Copyright (c) 2006 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2440 AC97 Controller -*/ - -#ifndef __ASM_ARCH_REGS_AC97_H -#define __ASM_ARCH_REGS_AC97_H __FILE__ - -#define S3C_AC97_GLBCTRL (0x00) - -#define S3C_AC97_GLBCTRL_CODECREADYIE (1<<22) -#define S3C_AC97_GLBCTRL_PCMOUTURIE (1<<21) -#define S3C_AC97_GLBCTRL_PCMINORIE (1<<20) -#define S3C_AC97_GLBCTRL_MICINORIE (1<<19) -#define S3C_AC97_GLBCTRL_PCMOUTTIE (1<<18) -#define S3C_AC97_GLBCTRL_PCMINTIE (1<<17) -#define S3C_AC97_GLBCTRL_MICINTIE (1<<16) -#define S3C_AC97_GLBCTRL_PCMOUTTM_OFF (0<<12) -#define S3C_AC97_GLBCTRL_PCMOUTTM_PIO (1<<12) -#define S3C_AC97_GLBCTRL_PCMOUTTM_DMA (2<<12) -#define S3C_AC97_GLBCTRL_PCMOUTTM_MASK (3<<12) -#define S3C_AC97_GLBCTRL_PCMINTM_OFF (0<<10) -#define S3C_AC97_GLBCTRL_PCMINTM_PIO (1<<10) -#define S3C_AC97_GLBCTRL_PCMINTM_DMA (2<<10) -#define S3C_AC97_GLBCTRL_PCMINTM_MASK (3<<10) -#define S3C_AC97_GLBCTRL_MICINTM_OFF (0<<8) -#define S3C_AC97_GLBCTRL_MICINTM_PIO (1<<8) -#define S3C_AC97_GLBCTRL_MICINTM_DMA (2<<8) -#define S3C_AC97_GLBCTRL_MICINTM_MASK (3<<8) -#define S3C_AC97_GLBCTRL_TRANSFERDATAENABLE (1<<3) -#define S3C_AC97_GLBCTRL_ACLINKON (1<<2) -#define S3C_AC97_GLBCTRL_WARMRESET (1<<1) -#define S3C_AC97_GLBCTRL_COLDRESET (1<<0) - -#define S3C_AC97_GLBSTAT (0x04) - -#define S3C_AC97_GLBSTAT_CODECREADY (1<<22) -#define S3C_AC97_GLBSTAT_PCMOUTUR (1<<21) -#define S3C_AC97_GLBSTAT_PCMINORI (1<<20) -#define S3C_AC97_GLBSTAT_MICINORI (1<<19) -#define S3C_AC97_GLBSTAT_PCMOUTTI (1<<18) -#define S3C_AC97_GLBSTAT_PCMINTI (1<<17) -#define S3C_AC97_GLBSTAT_MICINTI (1<<16) -#define S3C_AC97_GLBSTAT_MAINSTATE_IDLE (0<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_INIT (1<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_READY (2<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE (3<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_LP (4<<0) -#define S3C_AC97_GLBSTAT_MAINSTATE_WARM (5<<0) - -#define S3C_AC97_CODEC_CMD (0x08) - -#define S3C_AC97_CODEC_CMD_READ (1<<23) - -#define S3C_AC97_STAT (0x0c) -#define S3C_AC97_PCM_ADDR (0x10) -#define S3C_AC97_PCM_DATA (0x18) -#define S3C_AC97_MIC_DATA (0x1C) - -#endif /* __ASM_ARCH_REGS_AC97_H */ diff --git a/arch/arm/plat-s3c/include/plat/regs-adc.h b/arch/arm/plat-s3c/include/plat/regs-adc.h deleted file mode 100644 index f43c8da..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-adc.h +++ /dev/null @@ -1,63 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/regs-adc.h - * - * Copyright (c) 2004 Shannon Holland - * - * This program is free software; yosu 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. - * - * S3C2410 ADC registers -*/ - -#ifndef __ASM_ARCH_REGS_ADC_H -#define __ASM_ARCH_REGS_ADC_H "regs-adc.h" - -#define S3C2410_ADCREG(x) (x) - -#define S3C2410_ADCCON S3C2410_ADCREG(0x00) -#define S3C2410_ADCTSC S3C2410_ADCREG(0x04) -#define S3C2410_ADCDLY S3C2410_ADCREG(0x08) -#define S3C2410_ADCDAT0 S3C2410_ADCREG(0x0C) -#define S3C2410_ADCDAT1 S3C2410_ADCREG(0x10) -#define S3C64XX_ADCUPDN S3C2410_ADCREG(0x14) -#define S3C64XX_ADCCLRINT S3C2410_ADCREG(0x18) -#define S3C64XX_ADCCLRINTPNDNUP S3C2410_ADCREG(0x20) - - -/* ADCCON Register Bits */ -#define S3C2410_ADCCON_ECFLG (1<<15) -#define S3C2410_ADCCON_PRSCEN (1<<14) -#define S3C2410_ADCCON_PRSCVL(x) (((x)&0xFF)<<6) -#define S3C2410_ADCCON_PRSCVLMASK (0xFF<<6) -#define S3C2410_ADCCON_SELMUX(x) (((x)&0x7)<<3) -#define S3C2410_ADCCON_MUXMASK (0x7<<3) -#define S3C2410_ADCCON_STDBM (1<<2) -#define S3C2410_ADCCON_READ_START (1<<1) -#define S3C2410_ADCCON_ENABLE_START (1<<0) -#define S3C2410_ADCCON_STARTMASK (0x3<<0) - - -/* ADCTSC Register Bits */ -#define S3C2410_ADCTSC_YM_SEN (1<<7) -#define S3C2410_ADCTSC_YP_SEN (1<<6) -#define S3C2410_ADCTSC_XM_SEN (1<<5) -#define S3C2410_ADCTSC_XP_SEN (1<<4) -#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3) -#define S3C2410_ADCTSC_AUTO_PST (1<<2) -#define S3C2410_ADCTSC_XY_PST(x) (((x)&0x3)<<0) - -/* ADCDAT0 Bits */ -#define S3C2410_ADCDAT0_UPDOWN (1<<15) -#define S3C2410_ADCDAT0_AUTO_PST (1<<14) -#define S3C2410_ADCDAT0_XY_PST (0x3<<12) -#define S3C2410_ADCDAT0_XPDATA_MASK (0x03FF) - -/* ADCDAT1 Bits */ -#define S3C2410_ADCDAT1_UPDOWN (1<<15) -#define S3C2410_ADCDAT1_AUTO_PST (1<<14) -#define S3C2410_ADCDAT1_XY_PST (0x3<<12) -#define S3C2410_ADCDAT1_YPDATA_MASK (0x03FF) - -#endif /* __ASM_ARCH_REGS_ADC_H */ - - diff --git a/arch/arm/plat-s3c/include/plat/regs-iic.h b/arch/arm/plat-s3c/include/plat/regs-iic.h deleted file mode 100644 index 2f7c17d..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-iic.h +++ /dev/null @@ -1,56 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/regs-iic.h - * - * Copyright (c) 2004 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2410 I2C Controller -*/ - -#ifndef __ASM_ARCH_REGS_IIC_H -#define __ASM_ARCH_REGS_IIC_H __FILE__ - -/* see s3c2410x user guide, v1.1, section 9 (p447) for more info */ - -#define S3C2410_IICREG(x) (x) - -#define S3C2410_IICCON S3C2410_IICREG(0x00) -#define S3C2410_IICSTAT S3C2410_IICREG(0x04) -#define S3C2410_IICADD S3C2410_IICREG(0x08) -#define S3C2410_IICDS S3C2410_IICREG(0x0C) -#define S3C2440_IICLC S3C2410_IICREG(0x10) - -#define S3C2410_IICCON_ACKEN (1<<7) -#define S3C2410_IICCON_TXDIV_16 (0<<6) -#define S3C2410_IICCON_TXDIV_512 (1<<6) -#define S3C2410_IICCON_IRQEN (1<<5) -#define S3C2410_IICCON_IRQPEND (1<<4) -#define S3C2410_IICCON_SCALE(x) ((x)&15) -#define S3C2410_IICCON_SCALEMASK (0xf) - -#define S3C2410_IICSTAT_MASTER_RX (2<<6) -#define S3C2410_IICSTAT_MASTER_TX (3<<6) -#define S3C2410_IICSTAT_SLAVE_RX (0<<6) -#define S3C2410_IICSTAT_SLAVE_TX (1<<6) -#define S3C2410_IICSTAT_MODEMASK (3<<6) - -#define S3C2410_IICSTAT_START (1<<5) -#define S3C2410_IICSTAT_BUSBUSY (1<<5) -#define S3C2410_IICSTAT_TXRXEN (1<<4) -#define S3C2410_IICSTAT_ARBITR (1<<3) -#define S3C2410_IICSTAT_ASSLAVE (1<<2) -#define S3C2410_IICSTAT_ADDR0 (1<<1) -#define S3C2410_IICSTAT_LASTBIT (1<<0) - -#define S3C2410_IICLC_SDA_DELAY0 (0 << 0) -#define S3C2410_IICLC_SDA_DELAY5 (1 << 0) -#define S3C2410_IICLC_SDA_DELAY10 (2 << 0) -#define S3C2410_IICLC_SDA_DELAY15 (3 << 0) -#define S3C2410_IICLC_SDA_DELAY_MASK (3 << 0) - -#define S3C2410_IICLC_FILTER_ON (1<<2) - -#endif /* __ASM_ARCH_REGS_IIC_H */ diff --git a/arch/arm/plat-s3c/include/plat/regs-irqtype.h b/arch/arm/plat-s3c/include/plat/regs-irqtype.h deleted file mode 100644 index c63cd3f..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-irqtype.h +++ /dev/null @@ -1,21 +0,0 @@ -/* arch/arm/plat-s3c/include/plat/regs-irqtype.h - * - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C - IRQ detection types. - * - * 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. - */ - -/* values for S3C2410_EXTINT0/1/2 and other cpus in the series, including - * the S3C64XX -*/ -#define S3C2410_EXTINT_LOWLEV (0x00) -#define S3C2410_EXTINT_HILEV (0x01) -#define S3C2410_EXTINT_FALLEDGE (0x02) -#define S3C2410_EXTINT_RISEEDGE (0x04) -#define S3C2410_EXTINT_BOTHEDGE (0x06) diff --git a/arch/arm/plat-s3c/include/plat/regs-nand.h b/arch/arm/plat-s3c/include/plat/regs-nand.h deleted file mode 100644 index 238efea..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-nand.h +++ /dev/null @@ -1,123 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/regs-nand.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2410 NAND register definitions -*/ - -#ifndef __ASM_ARM_REGS_NAND -#define __ASM_ARM_REGS_NAND - - -#define S3C2410_NFREG(x) (x) - -#define S3C2410_NFCONF S3C2410_NFREG(0x00) -#define S3C2410_NFCMD S3C2410_NFREG(0x04) -#define S3C2410_NFADDR S3C2410_NFREG(0x08) -#define S3C2410_NFDATA S3C2410_NFREG(0x0C) -#define S3C2410_NFSTAT S3C2410_NFREG(0x10) -#define S3C2410_NFECC S3C2410_NFREG(0x14) - -#define S3C2440_NFCONT S3C2410_NFREG(0x04) -#define S3C2440_NFCMD S3C2410_NFREG(0x08) -#define S3C2440_NFADDR S3C2410_NFREG(0x0C) -#define S3C2440_NFDATA S3C2410_NFREG(0x10) -#define S3C2440_NFECCD0 S3C2410_NFREG(0x14) -#define S3C2440_NFECCD1 S3C2410_NFREG(0x18) -#define S3C2440_NFECCD S3C2410_NFREG(0x1C) -#define S3C2440_NFSTAT S3C2410_NFREG(0x20) -#define S3C2440_NFESTAT0 S3C2410_NFREG(0x24) -#define S3C2440_NFESTAT1 S3C2410_NFREG(0x28) -#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) -#define S3C2440_NFMECC1 S3C2410_NFREG(0x30) -#define S3C2440_NFSECC S3C2410_NFREG(0x34) -#define S3C2440_NFSBLK S3C2410_NFREG(0x38) -#define S3C2440_NFEBLK S3C2410_NFREG(0x3C) - -#define S3C2412_NFSBLK S3C2410_NFREG(0x20) -#define S3C2412_NFEBLK S3C2410_NFREG(0x24) -#define S3C2412_NFSTAT S3C2410_NFREG(0x28) -#define S3C2412_NFMECC_ERR0 S3C2410_NFREG(0x2C) -#define S3C2412_NFMECC_ERR1 S3C2410_NFREG(0x30) -#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) -#define S3C2412_NFMECC1 S3C2410_NFREG(0x38) -#define S3C2412_NFSECC S3C2410_NFREG(0x3C) - -#define S3C2410_NFCONF_EN (1<<15) -#define S3C2410_NFCONF_512BYTE (1<<14) -#define S3C2410_NFCONF_4STEP (1<<13) -#define S3C2410_NFCONF_INITECC (1<<12) -#define S3C2410_NFCONF_nFCE (1<<11) -#define S3C2410_NFCONF_TACLS(x) ((x)<<8) -#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) -#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) - -#define S3C2410_NFSTAT_BUSY (1<<0) - -#define S3C2440_NFCONF_BUSWIDTH_8 (0<<0) -#define S3C2440_NFCONF_BUSWIDTH_16 (1<<0) -#define S3C2440_NFCONF_ADVFLASH (1<<3) -#define S3C2440_NFCONF_TACLS(x) ((x)<<12) -#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) -#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) - -#define S3C2440_NFCONT_LOCKTIGHT (1<<13) -#define S3C2440_NFCONT_SOFTLOCK (1<<12) -#define S3C2440_NFCONT_ILLEGALACC_EN (1<<10) -#define S3C2440_NFCONT_RNBINT_EN (1<<9) -#define S3C2440_NFCONT_RN_FALLING (1<<8) -#define S3C2440_NFCONT_SPARE_ECCLOCK (1<<6) -#define S3C2440_NFCONT_MAIN_ECCLOCK (1<<5) -#define S3C2440_NFCONT_INITECC (1<<4) -#define S3C2440_NFCONT_nFCE (1<<1) -#define S3C2440_NFCONT_ENABLE (1<<0) - -#define S3C2440_NFSTAT_READY (1<<0) -#define S3C2440_NFSTAT_nCE (1<<1) -#define S3C2440_NFSTAT_RnB_CHANGE (1<<2) -#define S3C2440_NFSTAT_ILLEGAL_ACCESS (1<<3) - -#define S3C2412_NFCONF_NANDBOOT (1<<31) -#define S3C2412_NFCONF_ECCCLKCON (1<<30) -#define S3C2412_NFCONF_ECC_MLC (1<<24) -#define S3C2412_NFCONF_TACLS_MASK (7<<12) /* 1 extra bit of Tacls */ - -#define S3C2412_NFCONT_ECC4_DIRWR (1<<18) -#define S3C2412_NFCONT_LOCKTIGHT (1<<17) -#define S3C2412_NFCONT_SOFTLOCK (1<<16) -#define S3C2412_NFCONT_ECC4_ENCINT (1<<13) -#define S3C2412_NFCONT_ECC4_DECINT (1<<12) -#define S3C2412_NFCONT_MAIN_ECC_LOCK (1<<7) -#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5) -#define S3C2412_NFCONT_nFCE1 (1<<2) -#define S3C2412_NFCONT_nFCE0 (1<<1) - -#define S3C2412_NFSTAT_ECC_ENCDONE (1<<7) -#define S3C2412_NFSTAT_ECC_DECDONE (1<<6) -#define S3C2412_NFSTAT_ILLEGAL_ACCESS (1<<5) -#define S3C2412_NFSTAT_RnB_CHANGE (1<<4) -#define S3C2412_NFSTAT_nFCE1 (1<<3) -#define S3C2412_NFSTAT_nFCE0 (1<<2) -#define S3C2412_NFSTAT_Res1 (1<<1) -#define S3C2412_NFSTAT_READY (1<<0) - -#define S3C2412_NFECCERR_SERRDATA(x) (((x) >> 21) & 0xf) -#define S3C2412_NFECCERR_SERRBIT(x) (((x) >> 18) & 0x7) -#define S3C2412_NFECCERR_MERRDATA(x) (((x) >> 7) & 0x3ff) -#define S3C2412_NFECCERR_MERRBIT(x) (((x) >> 4) & 0x7) -#define S3C2412_NFECCERR_SPARE_ERR(x) (((x) >> 2) & 0x3) -#define S3C2412_NFECCERR_MAIN_ERR(x) (((x) >> 2) & 0x3) -#define S3C2412_NFECCERR_NONE (0) -#define S3C2412_NFECCERR_1BIT (1) -#define S3C2412_NFECCERR_MULTIBIT (2) -#define S3C2412_NFECCERR_ECCAREA (3) - - - -#endif /* __ASM_ARM_REGS_NAND */ - diff --git a/arch/arm/plat-s3c/include/plat/regs-rtc.h b/arch/arm/plat-s3c/include/plat/regs-rtc.h deleted file mode 100644 index d5837cf..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-rtc.h +++ /dev/null @@ -1,61 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/regs-rtc.h - * - * Copyright (c) 2003 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2410 Internal RTC register definition -*/ - -#ifndef __ASM_ARCH_REGS_RTC_H -#define __ASM_ARCH_REGS_RTC_H __FILE__ - -#define S3C2410_RTCREG(x) (x) - -#define S3C2410_RTCCON S3C2410_RTCREG(0x40) -#define S3C2410_RTCCON_RTCEN (1<<0) -#define S3C2410_RTCCON_CLKSEL (1<<1) -#define S3C2410_RTCCON_CNTSEL (1<<2) -#define S3C2410_RTCCON_CLKRST (1<<3) - -#define S3C2410_TICNT S3C2410_RTCREG(0x44) -#define S3C2410_TICNT_ENABLE (1<<7) - -#define S3C2410_RTCALM S3C2410_RTCREG(0x50) -#define S3C2410_RTCALM_ALMEN (1<<6) -#define S3C2410_RTCALM_YEAREN (1<<5) -#define S3C2410_RTCALM_MONEN (1<<4) -#define S3C2410_RTCALM_DAYEN (1<<3) -#define S3C2410_RTCALM_HOUREN (1<<2) -#define S3C2410_RTCALM_MINEN (1<<1) -#define S3C2410_RTCALM_SECEN (1<<0) - -#define S3C2410_RTCALM_ALL \ - S3C2410_RTCALM_ALMEN | S3C2410_RTCALM_YEAREN | S3C2410_RTCALM_MONEN |\ - S3C2410_RTCALM_DAYEN | S3C2410_RTCALM_HOUREN | S3C2410_RTCALM_MINEN |\ - S3C2410_RTCALM_SECEN - - -#define S3C2410_ALMSEC S3C2410_RTCREG(0x54) -#define S3C2410_ALMMIN S3C2410_RTCREG(0x58) -#define S3C2410_ALMHOUR S3C2410_RTCREG(0x5c) - -#define S3C2410_ALMDATE S3C2410_RTCREG(0x60) -#define S3C2410_ALMMON S3C2410_RTCREG(0x64) -#define S3C2410_ALMYEAR S3C2410_RTCREG(0x68) - -#define S3C2410_RTCRST S3C2410_RTCREG(0x6c) - -#define S3C2410_RTCSEC S3C2410_RTCREG(0x70) -#define S3C2410_RTCMIN S3C2410_RTCREG(0x74) -#define S3C2410_RTCHOUR S3C2410_RTCREG(0x78) -#define S3C2410_RTCDATE S3C2410_RTCREG(0x7c) -#define S3C2410_RTCDAY S3C2410_RTCREG(0x80) -#define S3C2410_RTCMON S3C2410_RTCREG(0x84) -#define S3C2410_RTCYEAR S3C2410_RTCREG(0x88) - - -#endif /* __ASM_ARCH_REGS_RTC_H */ diff --git a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h deleted file mode 100644 index abf2fbc..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h +++ /dev/null @@ -1,82 +0,0 @@ -/* linux/include/asm-arm/plat-s3c24xx/regs-s3c2412-iis.h - * - * Copyright 2007 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * 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. - * - * S3C2412 IIS register definition -*/ - -#ifndef __ASM_ARCH_REGS_S3C2412_IIS_H -#define __ASM_ARCH_REGS_S3C2412_IIS_H - -#define S3C2412_IISCON (0x00) -#define S3C2412_IISMOD (0x04) -#define S3C2412_IISFIC (0x08) -#define S3C2412_IISPSR (0x0C) -#define S3C2412_IISTXD (0x10) -#define S3C2412_IISRXD (0x14) - -#define S3C2412_IISCON_LRINDEX (1 << 11) -#define S3C2412_IISCON_TXFIFO_EMPTY (1 << 10) -#define S3C2412_IISCON_RXFIFO_EMPTY (1 << 9) -#define S3C2412_IISCON_TXFIFO_FULL (1 << 8) -#define S3C2412_IISCON_RXFIFO_FULL (1 << 7) -#define S3C2412_IISCON_TXDMA_PAUSE (1 << 6) -#define S3C2412_IISCON_RXDMA_PAUSE (1 << 5) -#define S3C2412_IISCON_TXCH_PAUSE (1 << 4) -#define S3C2412_IISCON_RXCH_PAUSE (1 << 3) -#define S3C2412_IISCON_TXDMA_ACTIVE (1 << 2) -#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1) -#define S3C2412_IISCON_IIS_ACTIVE (1 << 0) - -#define S3C64XX_IISMOD_BLC_16BIT (0 << 13) -#define S3C64XX_IISMOD_BLC_8BIT (1 << 13) -#define S3C64XX_IISMOD_BLC_24BIT (2 << 13) -#define S3C64XX_IISMOD_BLC_MASK (3 << 13) - -#define S3C64XX_IISMOD_IMS_PCLK (0 << 10) -#define S3C64XX_IISMOD_IMS_SYSMUX (1 << 10) - -#define S3C2412_IISMOD_MASTER_INTERNAL (0 << 10) -#define S3C2412_IISMOD_MASTER_EXTERNAL (1 << 10) -#define S3C2412_IISMOD_SLAVE (2 << 10) -#define S3C2412_IISMOD_MASTER_MASK (3 << 10) -#define S3C2412_IISMOD_MODE_TXONLY (0 << 8) -#define S3C2412_IISMOD_MODE_RXONLY (1 << 8) -#define S3C2412_IISMOD_MODE_TXRX (2 << 8) -#define S3C2412_IISMOD_MODE_MASK (3 << 8) -#define S3C2412_IISMOD_LR_LLOW (0 << 7) -#define S3C2412_IISMOD_LR_RLOW (1 << 7) -#define S3C2412_IISMOD_SDF_IIS (0 << 5) -#define S3C2412_IISMOD_SDF_MSB (1 << 5) -#define S3C2412_IISMOD_SDF_LSB (2 << 5) -#define S3C2412_IISMOD_SDF_MASK (3 << 5) -#define S3C2412_IISMOD_RCLK_256FS (0 << 3) -#define S3C2412_IISMOD_RCLK_512FS (1 << 3) -#define S3C2412_IISMOD_RCLK_384FS (2 << 3) -#define S3C2412_IISMOD_RCLK_768FS (3 << 3) -#define S3C2412_IISMOD_RCLK_MASK (3 << 3) -#define S3C2412_IISMOD_BCLK_32FS (0 << 1) -#define S3C2412_IISMOD_BCLK_48FS (1 << 1) -#define S3C2412_IISMOD_BCLK_16FS (2 << 1) -#define S3C2412_IISMOD_BCLK_24FS (3 << 1) -#define S3C2412_IISMOD_BCLK_MASK (3 << 1) -#define S3C2412_IISMOD_8BIT (1 << 0) - -#define S3C64XX_IISMOD_CDCLKCON (1 << 12) - -#define S3C2412_IISPSR_PSREN (1 << 15) - -#define S3C2412_IISFIC_TXFLUSH (1 << 15) -#define S3C2412_IISFIC_RXFLUSH (1 << 7) -#define S3C2412_IISFIC_TXCOUNT(x) (((x) >> 8) & 0xf) -#define S3C2412_IISFIC_RXCOUNT(x) (((x) >> 0) & 0xf) - - - -#endif /* __ASM_ARCH_REGS_S3C2412_IIS_H */ - diff --git a/arch/arm/plat-s3c/include/plat/regs-sdhci.h b/arch/arm/plat-s3c/include/plat/regs-sdhci.h deleted file mode 100644 index e34049ad..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-sdhci.h +++ /dev/null @@ -1,87 +0,0 @@ -/* linux/arch/arm/plat-s3c/include/plat/regs-sdhci.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C Platform - SDHCI (HSMMC) register definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __PLAT_S3C_SDHCI_REGS_H -#define __PLAT_S3C_SDHCI_REGS_H __FILE__ - -#define S3C_SDHCI_CONTROL2 (0x80) -#define S3C_SDHCI_CONTROL3 (0x84) -#define S3C64XX_SDHCI_CONTROL4 (0x8C) - -#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31) -#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK (1 << 30) -#define S3C_SDHCI_CTRL2_CDINVRXD3 (1 << 29) -#define S3C_SDHCI_CTRL2_SLCARDOUT (1 << 28) - -#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24) -#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24) -#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24) - -#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16) -#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16) -#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16) - -#define S3C_SDHCI_CTRL2_ENFBCLKTX (1 << 15) -#define S3C_SDHCI_CTRL2_ENFBCLKRX (1 << 14) -#define S3C_SDHCI_CTRL2_SDCDSEL (1 << 13) -#define S3C_SDHCI_CTRL2_SDSIGPC (1 << 12) -#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11) - -#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9) -#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9) -#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9) -#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9) -#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9) -#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9) - -#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8) -#define S3C_SDHCI_CTRL2_RWAITMODE (1 << 7) -#define S3C_SDHCI_CTRL2_DISBUFRD (1 << 6) -#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4) -#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4) -#define S3C_SDHCI_CTRL2_PWRSYNC (1 << 3) -#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1) -#define S3C_SDHCI_CTRL2_HWINITFIN (1 << 0) - -#define S3C_SDHCI_CTRL3_FCSEL3 (1 << 31) -#define S3C_SDHCI_CTRL3_FCSEL2 (1 << 23) -#define S3C_SDHCI_CTRL3_FCSEL1 (1 << 15) -#define S3C_SDHCI_CTRL3_FCSEL0 (1 << 7) - -#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24) -#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24) -#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24) - -#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16) -#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16) -#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16) - -#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8) -#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8) -#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8) - -#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0) -#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0) -#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0) - -#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16) - -#define S3C64XX_SDHCI_CONTROL4_BUSY (1) - -#endif /* __PLAT_S3C_SDHCI_REGS_H */ diff --git a/arch/arm/plat-s3c/include/plat/regs-timer.h b/arch/arm/plat-s3c/include/plat/regs-timer.h deleted file mode 100644 index d097d92..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-timer.h +++ /dev/null @@ -1,124 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/regs-timer.h - * - * Copyright (c) 2003 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2410 Timer configuration -*/ - -#ifndef __ASM_ARCH_REGS_TIMER_H -#define __ASM_ARCH_REGS_TIMER_H - -#define S3C_TIMERREG(x) (S3C_VA_TIMER + (x)) -#define S3C_TIMERREG2(tmr,reg) S3C_TIMERREG((reg)+0x0c+((tmr)*0x0c)) - -#define S3C2410_TCFG0 S3C_TIMERREG(0x00) -#define S3C2410_TCFG1 S3C_TIMERREG(0x04) -#define S3C2410_TCON S3C_TIMERREG(0x08) - -#define S3C64XX_TINT_CSTAT S3C_TIMERREG(0x44) - -#define S3C2410_TCFG_PRESCALER0_MASK (255<<0) -#define S3C2410_TCFG_PRESCALER1_MASK (255<<8) -#define S3C2410_TCFG_PRESCALER1_SHIFT (8) -#define S3C2410_TCFG_DEADZONE_MASK (255<<16) -#define S3C2410_TCFG_DEADZONE_SHIFT (16) - -#define S3C2410_TCFG1_MUX4_DIV2 (0<<16) -#define S3C2410_TCFG1_MUX4_DIV4 (1<<16) -#define S3C2410_TCFG1_MUX4_DIV8 (2<<16) -#define S3C2410_TCFG1_MUX4_DIV16 (3<<16) -#define S3C2410_TCFG1_MUX4_TCLK1 (4<<16) -#define S3C2410_TCFG1_MUX4_MASK (15<<16) -#define S3C2410_TCFG1_MUX4_SHIFT (16) - -#define S3C2410_TCFG1_MUX3_DIV2 (0<<12) -#define S3C2410_TCFG1_MUX3_DIV4 (1<<12) -#define S3C2410_TCFG1_MUX3_DIV8 (2<<12) -#define S3C2410_TCFG1_MUX3_DIV16 (3<<12) -#define S3C2410_TCFG1_MUX3_TCLK1 (4<<12) -#define S3C2410_TCFG1_MUX3_MASK (15<<12) - - -#define S3C2410_TCFG1_MUX2_DIV2 (0<<8) -#define S3C2410_TCFG1_MUX2_DIV4 (1<<8) -#define S3C2410_TCFG1_MUX2_DIV8 (2<<8) -#define S3C2410_TCFG1_MUX2_DIV16 (3<<8) -#define S3C2410_TCFG1_MUX2_TCLK1 (4<<8) -#define S3C2410_TCFG1_MUX2_MASK (15<<8) - - -#define S3C2410_TCFG1_MUX1_DIV2 (0<<4) -#define S3C2410_TCFG1_MUX1_DIV4 (1<<4) -#define S3C2410_TCFG1_MUX1_DIV8 (2<<4) -#define S3C2410_TCFG1_MUX1_DIV16 (3<<4) -#define S3C2410_TCFG1_MUX1_TCLK0 (4<<4) -#define S3C2410_TCFG1_MUX1_MASK (15<<4) - -#define S3C2410_TCFG1_MUX0_DIV2 (0<<0) -#define S3C2410_TCFG1_MUX0_DIV4 (1<<0) -#define S3C2410_TCFG1_MUX0_DIV8 (2<<0) -#define S3C2410_TCFG1_MUX0_DIV16 (3<<0) -#define S3C2410_TCFG1_MUX0_TCLK0 (4<<0) -#define S3C2410_TCFG1_MUX0_MASK (15<<0) - -#define S3C2410_TCFG1_MUX_DIV2 (0<<0) -#define S3C2410_TCFG1_MUX_DIV4 (1<<0) -#define S3C2410_TCFG1_MUX_DIV8 (2<<0) -#define S3C2410_TCFG1_MUX_DIV16 (3<<0) -#define S3C2410_TCFG1_MUX_TCLK (4<<0) -#define S3C2410_TCFG1_MUX_MASK (15<<0) - -#define S3C64XX_TCFG1_MUX_DIV1 (0<<0) -#define S3C64XX_TCFG1_MUX_DIV2 (1<<0) -#define S3C64XX_TCFG1_MUX_DIV4 (2<<0) -#define S3C64XX_TCFG1_MUX_DIV8 (3<<0) -#define S3C64XX_TCFG1_MUX_DIV16 (4<<0) -#define S3C64XX_TCFG1_MUX_TCLK (5<<0) /* 3 sets of TCLK */ -#define S3C64XX_TCFG1_MUX_MASK (15<<0) - -#define S3C2410_TCFG1_SHIFT(x) ((x) * 4) - -/* for each timer, we have an count buffer, an compare buffer and - * an observation buffer -*/ - -/* WARNING - timer 4 has no buffer reg, and it's observation is at +4 */ - -#define S3C2410_TCNTB(tmr) S3C_TIMERREG2(tmr, 0x00) -#define S3C2410_TCMPB(tmr) S3C_TIMERREG2(tmr, 0x04) -#define S3C2410_TCNTO(tmr) S3C_TIMERREG2(tmr, (((tmr) == 4) ? 0x04 : 0x08)) - -#define S3C2410_TCON_T4RELOAD (1<<22) -#define S3C2410_TCON_T4MANUALUPD (1<<21) -#define S3C2410_TCON_T4START (1<<20) - -#define S3C2410_TCON_T3RELOAD (1<<19) -#define S3C2410_TCON_T3INVERT (1<<18) -#define S3C2410_TCON_T3MANUALUPD (1<<17) -#define S3C2410_TCON_T3START (1<<16) - -#define S3C2410_TCON_T2RELOAD (1<<15) -#define S3C2410_TCON_T2INVERT (1<<14) -#define S3C2410_TCON_T2MANUALUPD (1<<13) -#define S3C2410_TCON_T2START (1<<12) - -#define S3C2410_TCON_T1RELOAD (1<<11) -#define S3C2410_TCON_T1INVERT (1<<10) -#define S3C2410_TCON_T1MANUALUPD (1<<9) -#define S3C2410_TCON_T1START (1<<8) - -#define S3C2410_TCON_T0DEADZONE (1<<4) -#define S3C2410_TCON_T0RELOAD (1<<3) -#define S3C2410_TCON_T0INVERT (1<<2) -#define S3C2410_TCON_T0MANUALUPD (1<<1) -#define S3C2410_TCON_T0START (1<<0) - -#endif /* __ASM_ARCH_REGS_TIMER_H */ - - - diff --git a/arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h b/arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h deleted file mode 100644 index 36a85f5..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h +++ /dev/null @@ -1,50 +0,0 @@ -/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C - USB2.0 Highspeed/OtG device PHY registers - * - * 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. -*/ - -/* Note, this is a seperate header file as some of the clock framework - * needs to touch this if the clk_48m is used as the USB OHCI or other - * peripheral source. -*/ - -#ifndef __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H -#define __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H __FILE__ - -/* S3C64XX_PA_USB_HSPHY */ - -#define S3C_HSOTG_PHYREG(x) ((x) + S3C_VA_USB_HSPHY) - -#define S3C_PHYPWR S3C_HSOTG_PHYREG(0x00) -#define SRC_PHYPWR_OTG_DISABLE (1 << 4) -#define SRC_PHYPWR_ANALOG_POWERDOWN (1 << 3) -#define SRC_PHYPWR_FORCE_SUSPEND (1 << 1) - -#define S3C_PHYCLK S3C_HSOTG_PHYREG(0x04) -#define S3C_PHYCLK_MODE_USB11 (1 << 6) -#define S3C_PHYCLK_EXT_OSC (1 << 5) -#define S3C_PHYCLK_CLK_FORCE (1 << 4) -#define S3C_PHYCLK_ID_PULL (1 << 2) -#define S3C_PHYCLK_CLKSEL_MASK (0x3 << 0) -#define S3C_PHYCLK_CLKSEL_SHIFT (0) -#define S3C_PHYCLK_CLKSEL_48M (0x0 << 0) -#define S3C_PHYCLK_CLKSEL_12M (0x2 << 0) -#define S3C_PHYCLK_CLKSEL_24M (0x3 << 0) - -#define S3C_RSTCON S3C_HSOTG_PHYREG(0x08) -#define S3C_RSTCON_PHYCLK (1 << 2) -#define S3C_RSTCON_HCLK (1 << 2) -#define S3C_RSTCON_PHY (1 << 0) - -#define S3C_PHYTUNE S3C_HSOTG_PHYREG(0x20) - -#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H */ diff --git a/arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h b/arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h deleted file mode 100644 index 8d18d9d..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h +++ /dev/null @@ -1,377 +0,0 @@ -/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C - USB2.0 Highspeed/OtG device block registers - * - * 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_S3C64XX_REGS_USB_HSOTG_H -#define __PLAT_S3C64XX_REGS_USB_HSOTG_H __FILE__ - -#define S3C_HSOTG_REG(x) (x) - -#define S3C_GOTGCTL S3C_HSOTG_REG(0x000) -#define S3C_GOTGCTL_BSESVLD (1 << 19) -#define S3C_GOTGCTL_ASESVLD (1 << 18) -#define S3C_GOTGCTL_DBNC_SHORT (1 << 17) -#define S3C_GOTGCTL_CONID_B (1 << 16) -#define S3C_GOTGCTL_DEVHNPEN (1 << 11) -#define S3C_GOTGCTL_HSSETHNPEN (1 << 10) -#define S3C_GOTGCTL_HNPREQ (1 << 9) -#define S3C_GOTGCTL_HSTNEGSCS (1 << 8) -#define S3C_GOTGCTL_SESREQ (1 << 1) -#define S3C_GOTGCTL_SESREQSCS (1 << 0) - -#define S3C_GOTGINT S3C_HSOTG_REG(0x004) -#define S3C_GOTGINT_DbnceDone (1 << 19) -#define S3C_GOTGINT_ADevTOUTChg (1 << 18) -#define S3C_GOTGINT_HstNegDet (1 << 17) -#define S3C_GOTGINT_HstnegSucStsChng (1 << 9) -#define S3C_GOTGINT_SesReqSucStsChng (1 << 8) -#define S3C_GOTGINT_SesEndDet (1 << 2) - -#define S3C_GAHBCFG S3C_HSOTG_REG(0x008) -#define S3C_GAHBCFG_PTxFEmpLvl (1 << 8) -#define S3C_GAHBCFG_NPTxFEmpLvl (1 << 7) -#define S3C_GAHBCFG_DMAEn (1 << 5) -#define S3C_GAHBCFG_HBstLen_MASK (0xf << 1) -#define S3C_GAHBCFG_HBstLen_SHIFT (1) -#define S3C_GAHBCFG_HBstLen_Single (0x0 << 1) -#define S3C_GAHBCFG_HBstLen_Incr (0x1 << 1) -#define S3C_GAHBCFG_HBstLen_Incr4 (0x3 << 1) -#define S3C_GAHBCFG_HBstLen_Incr8 (0x5 << 1) -#define S3C_GAHBCFG_HBstLen_Incr16 (0x7 << 1) -#define S3C_GAHBCFG_GlblIntrEn (1 << 0) - -#define S3C_GUSBCFG S3C_HSOTG_REG(0x00C) -#define S3C_GUSBCFG_PHYLPClkSel (1 << 15) -#define S3C_GUSBCFG_HNPCap (1 << 9) -#define S3C_GUSBCFG_SRPCap (1 << 8) -#define S3C_GUSBCFG_PHYIf16 (1 << 3) -#define S3C_GUSBCFG_TOutCal_MASK (0x7 << 0) -#define S3C_GUSBCFG_TOutCal_SHIFT (0) -#define S3C_GUSBCFG_TOutCal_LIMIT (0x7) -#define S3C_GUSBCFG_TOutCal(_x) ((_x) << 0) - -#define S3C_GRSTCTL S3C_HSOTG_REG(0x010) - -#define S3C_GRSTCTL_AHBIdle (1 << 31) -#define S3C_GRSTCTL_DMAReq (1 << 30) -#define S3C_GRSTCTL_TxFNum_MASK (0x1f << 6) -#define S3C_GRSTCTL_TxFNum_SHIFT (6) -#define S3C_GRSTCTL_TxFNum_LIMIT (0x1f) -#define S3C_GRSTCTL_TxFNum(_x) ((_x) << 6) -#define S3C_GRSTCTL_TxFFlsh (1 << 5) -#define S3C_GRSTCTL_RxFFlsh (1 << 4) -#define S3C_GRSTCTL_INTknQFlsh (1 << 3) -#define S3C_GRSTCTL_FrmCntrRst (1 << 2) -#define S3C_GRSTCTL_HSftRst (1 << 1) -#define S3C_GRSTCTL_CSftRst (1 << 0) - -#define S3C_GINTSTS S3C_HSOTG_REG(0x014) -#define S3C_GINTMSK S3C_HSOTG_REG(0x018) - -#define S3C_GINTSTS_WkUpInt (1 << 31) -#define S3C_GINTSTS_SessReqInt (1 << 30) -#define S3C_GINTSTS_DisconnInt (1 << 29) -#define S3C_GINTSTS_ConIDStsChng (1 << 28) -#define S3C_GINTSTS_PTxFEmp (1 << 26) -#define S3C_GINTSTS_HChInt (1 << 25) -#define S3C_GINTSTS_PrtInt (1 << 24) -#define S3C_GINTSTS_FetSusp (1 << 22) -#define S3C_GINTSTS_incompIP (1 << 21) -#define S3C_GINTSTS_IncomplSOIN (1 << 20) -#define S3C_GINTSTS_OEPInt (1 << 19) -#define S3C_GINTSTS_IEPInt (1 << 18) -#define S3C_GINTSTS_EPMis (1 << 17) -#define S3C_GINTSTS_EOPF (1 << 15) -#define S3C_GINTSTS_ISOutDrop (1 << 14) -#define S3C_GINTSTS_EnumDone (1 << 13) -#define S3C_GINTSTS_USBRst (1 << 12) -#define S3C_GINTSTS_USBSusp (1 << 11) -#define S3C_GINTSTS_ErlySusp (1 << 10) -#define S3C_GINTSTS_GOUTNakEff (1 << 7) -#define S3C_GINTSTS_GINNakEff (1 << 6) -#define S3C_GINTSTS_NPTxFEmp (1 << 5) -#define S3C_GINTSTS_RxFLvl (1 << 4) -#define S3C_GINTSTS_SOF (1 << 3) -#define S3C_GINTSTS_OTGInt (1 << 2) -#define S3C_GINTSTS_ModeMis (1 << 1) -#define S3C_GINTSTS_CurMod_Host (1 << 0) - -#define S3C_GRXSTSR S3C_HSOTG_REG(0x01C) -#define S3C_GRXSTSP S3C_HSOTG_REG(0x020) - -#define S3C_GRXSTS_FN_MASK (0x7f << 25) -#define S3C_GRXSTS_FN_SHIFT (25) - -#define S3C_GRXSTS_PktSts_MASK (0xf << 17) -#define S3C_GRXSTS_PktSts_SHIFT (17) -#define S3C_GRXSTS_PktSts_GlobalOutNAK (0x1 << 17) -#define S3C_GRXSTS_PktSts_OutRX (0x2 << 17) -#define S3C_GRXSTS_PktSts_OutDone (0x3 << 17) -#define S3C_GRXSTS_PktSts_SetupDone (0x4 << 17) -#define S3C_GRXSTS_PktSts_SetupRX (0x6 << 17) - -#define S3C_GRXSTS_DPID_MASK (0x3 << 15) -#define S3C_GRXSTS_DPID_SHIFT (15) -#define S3C_GRXSTS_ByteCnt_MASK (0x7ff << 4) -#define S3C_GRXSTS_ByteCnt_SHIFT (4) -#define S3C_GRXSTS_EPNum_MASK (0xf << 0) -#define S3C_GRXSTS_EPNum_SHIFT (0) - -#define S3C_GRXFSIZ S3C_HSOTG_REG(0x024) - -#define S3C_GNPTXFSIZ S3C_HSOTG_REG(0x028) - -#define S3C_GNPTXFSIZ_NPTxFDep_MASK (0xffff << 16) -#define S3C_GNPTXFSIZ_NPTxFDep_SHIFT (16) -#define S3C_GNPTXFSIZ_NPTxFDep_LIMIT (0xffff) -#define S3C_GNPTXFSIZ_NPTxFDep(_x) ((_x) << 16) -#define S3C_GNPTXFSIZ_NPTxFStAddr_MASK (0xffff << 0) -#define S3C_GNPTXFSIZ_NPTxFStAddr_SHIFT (0) -#define S3C_GNPTXFSIZ_NPTxFStAddr_LIMIT (0xffff) -#define S3C_GNPTXFSIZ_NPTxFStAddr(_x) ((_x) << 0) - -#define S3C_GNPTXSTS S3C_HSOTG_REG(0x02C) - -#define S3C_GNPTXSTS_NPtxQTop_MASK (0x7f << 24) -#define S3C_GNPTXSTS_NPtxQTop_SHIFT (24) - -#define S3C_GNPTXSTS_NPTxQSpcAvail_MASK (0xff << 16) -#define S3C_GNPTXSTS_NPTxQSpcAvail_SHIFT (16) -#define S3C_GNPTXSTS_NPTxQSpcAvail_GET(_v) (((_v) >> 16) & 0xff) - -#define S3C_GNPTXSTS_NPTxFSpcAvail_MASK (0xffff << 0) -#define S3C_GNPTXSTS_NPTxFSpcAvail_SHIFT (0) -#define S3C_GNPTXSTS_NPTxFSpcAvail_GET(_v) (((_v) >> 0) & 0xffff) - - -#define S3C_HPTXFSIZ S3C_HSOTG_REG(0x100) - -#define S3C_DPTXFSIZn(_a) S3C_HSOTG_REG(0x104 + (((_a) - 1) * 4)) - -#define S3C_DPTXFSIZn_DPTxFSize_MASK (0xffff << 16) -#define S3C_DPTXFSIZn_DPTxFSize_SHIFT (16) -#define S3C_DPTXFSIZn_DPTxFSize_GET(_v) (((_v) >> 16) & 0xffff) -#define S3C_DPTXFSIZn_DPTxFSize_LIMIT (0xffff) -#define S3C_DPTXFSIZn_DPTxFSize(_x) ((_x) << 16) - -#define S3C_DPTXFSIZn_DPTxFStAddr_MASK (0xffff << 0) -#define S3C_DPTXFSIZn_DPTxFStAddr_SHIFT (0) - -/* Device mode registers */ -#define S3C_DCFG S3C_HSOTG_REG(0x800) - -#define S3C_DCFG_EPMisCnt_MASK (0x1f << 18) -#define S3C_DCFG_EPMisCnt_SHIFT (18) -#define S3C_DCFG_EPMisCnt_LIMIT (0x1f) -#define S3C_DCFG_EPMisCnt(_x) ((_x) << 18) - -#define S3C_DCFG_PerFrInt_MASK (0x3 << 11) -#define S3C_DCFG_PerFrInt_SHIFT (11) -#define S3C_DCFG_PerFrInt_LIMIT (0x3) -#define S3C_DCFG_PerFrInt(_x) ((_x) << 11) - -#define S3C_DCFG_DevAddr_MASK (0x7f << 4) -#define S3C_DCFG_DevAddr_SHIFT (4) -#define S3C_DCFG_DevAddr_LIMIT (0x7f) -#define S3C_DCFG_DevAddr(_x) ((_x) << 4) - -#define S3C_DCFG_NZStsOUTHShk (1 << 2) - -#define S3C_DCFG_DevSpd_MASK (0x3 << 0) -#define S3C_DCFG_DevSpd_SHIFT (0) -#define S3C_DCFG_DevSpd_HS (0x0 << 0) -#define S3C_DCFG_DevSpd_FS (0x1 << 0) -#define S3C_DCFG_DevSpd_LS (0x2 << 0) -#define S3C_DCFG_DevSpd_FS48 (0x3 << 0) - -#define S3C_DCTL S3C_HSOTG_REG(0x804) - -#define S3C_DCTL_PWROnPrgDone (1 << 11) -#define S3C_DCTL_CGOUTNak (1 << 10) -#define S3C_DCTL_SGOUTNak (1 << 9) -#define S3C_DCTL_CGNPInNAK (1 << 8) -#define S3C_DCTL_SGNPInNAK (1 << 7) -#define S3C_DCTL_TstCtl_MASK (0x7 << 4) -#define S3C_DCTL_TstCtl_SHIFT (4) -#define S3C_DCTL_GOUTNakSts (1 << 3) -#define S3C_DCTL_GNPINNakSts (1 << 2) -#define S3C_DCTL_SftDiscon (1 << 1) -#define S3C_DCTL_RmtWkUpSig (1 << 0) - -#define S3C_DSTS S3C_HSOTG_REG(0x808) - -#define S3C_DSTS_SOFFN_MASK (0x3fff << 8) -#define S3C_DSTS_SOFFN_SHIFT (8) -#define S3C_DSTS_SOFFN_LIMIT (0x3fff) -#define S3C_DSTS_SOFFN(_x) ((_x) << 8) -#define S3C_DSTS_ErraticErr (1 << 3) -#define S3C_DSTS_EnumSpd_MASK (0x3 << 1) -#define S3C_DSTS_EnumSpd_SHIFT (1) -#define S3C_DSTS_EnumSpd_HS (0x0 << 1) -#define S3C_DSTS_EnumSpd_FS (0x1 << 1) -#define S3C_DSTS_EnumSpd_LS (0x2 << 1) -#define S3C_DSTS_EnumSpd_FS48 (0x3 << 1) - -#define S3C_DSTS_SuspSts (1 << 0) - -#define S3C_DIEPMSK S3C_HSOTG_REG(0x810) - -#define S3C_DIEPMSK_INEPNakEffMsk (1 << 6) -#define S3C_DIEPMSK_INTknEPMisMsk (1 << 5) -#define S3C_DIEPMSK_INTknTXFEmpMsk (1 << 4) -#define S3C_DIEPMSK_TimeOUTMsk (1 << 3) -#define S3C_DIEPMSK_AHBErrMsk (1 << 2) -#define S3C_DIEPMSK_EPDisbldMsk (1 << 1) -#define S3C_DIEPMSK_XferComplMsk (1 << 0) - -#define S3C_DOEPMSK S3C_HSOTG_REG(0x814) - -#define S3C_DOEPMSK_Back2BackSetup (1 << 6) -#define S3C_DOEPMSK_OUTTknEPdisMsk (1 << 4) -#define S3C_DOEPMSK_SetupMsk (1 << 3) -#define S3C_DOEPMSK_AHBErrMsk (1 << 2) -#define S3C_DOEPMSK_EPDisbldMsk (1 << 1) -#define S3C_DOEPMSK_XferComplMsk (1 << 0) - -#define S3C_DAINT S3C_HSOTG_REG(0x818) -#define S3C_DAINTMSK S3C_HSOTG_REG(0x81C) - -#define S3C_DAINT_OutEP_SHIFT (16) -#define S3C_DAINT_OutEP(x) (1 << ((x) + 16)) -#define S3C_DAINT_InEP(x) (1 << (x)) - -#define S3C_DTKNQR1 S3C_HSOTG_REG(0x820) -#define S3C_DTKNQR2 S3C_HSOTG_REG(0x824) -#define S3C_DTKNQR3 S3C_HSOTG_REG(0x830) -#define S3C_DTKNQR4 S3C_HSOTG_REG(0x834) - -#define S3C_DVBUSDIS S3C_HSOTG_REG(0x828) -#define S3C_DVBUSPULSE S3C_HSOTG_REG(0x82C) - -#define S3C_DIEPCTL0 S3C_HSOTG_REG(0x900) -#define S3C_DOEPCTL0 S3C_HSOTG_REG(0xB00) -#define S3C_DIEPCTL(_a) S3C_HSOTG_REG(0x900 + ((_a) * 0x20)) -#define S3C_DOEPCTL(_a) S3C_HSOTG_REG(0xB00 + ((_a) * 0x20)) - -/* EP0 specialness: - * bits[29..28] - reserved (no SetD0PID, SetD1PID) - * bits[25..22] - should always be zero, this isn't a periodic endpoint - * bits[10..0] - MPS setting differenct for EP0 -*/ -#define S3C_D0EPCTL_MPS_MASK (0x3 << 0) -#define S3C_D0EPCTL_MPS_SHIFT (0) -#define S3C_D0EPCTL_MPS_64 (0x0 << 0) -#define S3C_D0EPCTL_MPS_32 (0x1 << 0) -#define S3C_D0EPCTL_MPS_16 (0x2 << 0) -#define S3C_D0EPCTL_MPS_8 (0x3 << 0) - -#define S3C_DxEPCTL_EPEna (1 << 31) -#define S3C_DxEPCTL_EPDis (1 << 30) -#define S3C_DxEPCTL_SetD1PID (1 << 29) -#define S3C_DxEPCTL_SetOddFr (1 << 29) -#define S3C_DxEPCTL_SetD0PID (1 << 28) -#define S3C_DxEPCTL_SetEvenFr (1 << 28) -#define S3C_DxEPCTL_SNAK (1 << 27) -#define S3C_DxEPCTL_CNAK (1 << 26) -#define S3C_DxEPCTL_TxFNum_MASK (0xf << 22) -#define S3C_DxEPCTL_TxFNum_SHIFT (22) -#define S3C_DxEPCTL_TxFNum_LIMIT (0xf) -#define S3C_DxEPCTL_TxFNum(_x) ((_x) << 22) - -#define S3C_DxEPCTL_Stall (1 << 21) -#define S3C_DxEPCTL_Snp (1 << 20) -#define S3C_DxEPCTL_EPType_MASK (0x3 << 18) -#define S3C_DxEPCTL_EPType_SHIFT (18) -#define S3C_DxEPCTL_EPType_Control (0x0 << 18) -#define S3C_DxEPCTL_EPType_Iso (0x1 << 18) -#define S3C_DxEPCTL_EPType_Bulk (0x2 << 18) -#define S3C_DxEPCTL_EPType_Intterupt (0x3 << 18) - -#define S3C_DxEPCTL_NAKsts (1 << 17) -#define S3C_DxEPCTL_DPID (1 << 16) -#define S3C_DxEPCTL_EOFrNum (1 << 16) -#define S3C_DxEPCTL_USBActEp (1 << 15) -#define S3C_DxEPCTL_NextEp_MASK (0xf << 11) -#define S3C_DxEPCTL_NextEp_SHIFT (11) -#define S3C_DxEPCTL_NextEp_LIMIT (0xf) -#define S3C_DxEPCTL_NextEp(_x) ((_x) << 11) - -#define S3C_DxEPCTL_MPS_MASK (0x7ff << 0) -#define S3C_DxEPCTL_MPS_SHIFT (0) -#define S3C_DxEPCTL_MPS_LIMIT (0x7ff) -#define S3C_DxEPCTL_MPS(_x) ((_x) << 0) - -#define S3C_DIEPINT(_a) S3C_HSOTG_REG(0x908 + ((_a) * 0x20)) -#define S3C_DOEPINT(_a) S3C_HSOTG_REG(0xB08 + ((_a) * 0x20)) - -#define S3C_DxEPINT_INEPNakEff (1 << 6) -#define S3C_DxEPINT_Back2BackSetup (1 << 6) -#define S3C_DxEPINT_INTknEPMis (1 << 5) -#define S3C_DxEPINT_INTknTXFEmp (1 << 4) -#define S3C_DxEPINT_OUTTknEPdis (1 << 4) -#define S3C_DxEPINT_Timeout (1 << 3) -#define S3C_DxEPINT_Setup (1 << 3) -#define S3C_DxEPINT_AHBErr (1 << 2) -#define S3C_DxEPINT_EPDisbld (1 << 1) -#define S3C_DxEPINT_XferCompl (1 << 0) - -#define S3C_DIEPTSIZ0 S3C_HSOTG_REG(0x910) - -#define S3C_DIEPTSIZ0_PktCnt_MASK (0x3 << 19) -#define S3C_DIEPTSIZ0_PktCnt_SHIFT (19) -#define S3C_DIEPTSIZ0_PktCnt_LIMIT (0x3) -#define S3C_DIEPTSIZ0_PktCnt(_x) ((_x) << 19) - -#define S3C_DIEPTSIZ0_XferSize_MASK (0x7f << 0) -#define S3C_DIEPTSIZ0_XferSize_SHIFT (0) -#define S3C_DIEPTSIZ0_XferSize_LIMIT (0x7f) -#define S3C_DIEPTSIZ0_XferSize(_x) ((_x) << 0) - - -#define DOEPTSIZ0 S3C_HSOTG_REG(0xB10) -#define S3C_DOEPTSIZ0_SUPCnt_MASK (0x3 << 29) -#define S3C_DOEPTSIZ0_SUPCnt_SHIFT (29) -#define S3C_DOEPTSIZ0_SUPCnt_LIMIT (0x3) -#define S3C_DOEPTSIZ0_SUPCnt(_x) ((_x) << 29) - -#define S3C_DOEPTSIZ0_PktCnt (1 << 19) -#define S3C_DOEPTSIZ0_XferSize_MASK (0x7f << 0) -#define S3C_DOEPTSIZ0_XferSize_SHIFT (0) - -#define S3C_DIEPTSIZ(_a) S3C_HSOTG_REG(0x910 + ((_a) * 0x20)) -#define S3C_DOEPTSIZ(_a) S3C_HSOTG_REG(0xB10 + ((_a) * 0x20)) - -#define S3C_DxEPTSIZ_MC_MASK (0x3 << 29) -#define S3C_DxEPTSIZ_MC_SHIFT (29) -#define S3C_DxEPTSIZ_MC_LIMIT (0x3) -#define S3C_DxEPTSIZ_MC(_x) ((_x) << 29) - -#define S3C_DxEPTSIZ_PktCnt_MASK (0x3ff << 19) -#define S3C_DxEPTSIZ_PktCnt_SHIFT (19) -#define S3C_DxEPTSIZ_PktCnt_GET(_v) (((_v) >> 19) & 0x3ff) -#define S3C_DxEPTSIZ_PktCnt_LIMIT (0x3ff) -#define S3C_DxEPTSIZ_PktCnt(_x) ((_x) << 19) - -#define S3C_DxEPTSIZ_XferSize_MASK (0x7ffff << 0) -#define S3C_DxEPTSIZ_XferSize_SHIFT (0) -#define S3C_DxEPTSIZ_XferSize_GET(_v) (((_v) >> 0) & 0x7ffff) -#define S3C_DxEPTSIZ_XferSize_LIMIT (0x7ffff) -#define S3C_DxEPTSIZ_XferSize(_x) ((_x) << 0) - - -#define S3C_DIEPDMA(_a) S3C_HSOTG_REG(0x914 + ((_a) * 0x20)) -#define S3C_DOEPDMA(_a) S3C_HSOTG_REG(0xB14 + ((_a) * 0x20)) - -#define S3C_EPFIFO(_a) S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000)) - -#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_H */ diff --git a/arch/arm/plat-s3c/include/plat/regs-watchdog.h b/arch/arm/plat-s3c/include/plat/regs-watchdog.h deleted file mode 100644 index 4938492..0000000 --- a/arch/arm/plat-s3c/include/plat/regs-watchdog.h +++ /dev/null @@ -1,41 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/regs-watchdog.h - * - * Copyright (c) 2003 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * - * 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. - * - * S3C2410 Watchdog timer control -*/ - - -#ifndef __ASM_ARCH_REGS_WATCHDOG_H -#define __ASM_ARCH_REGS_WATCHDOG_H - -#define S3C_WDOGREG(x) ((x) + S3C_VA_WATCHDOG) - -#define S3C2410_WTCON S3C_WDOGREG(0x00) -#define S3C2410_WTDAT S3C_WDOGREG(0x04) -#define S3C2410_WTCNT S3C_WDOGREG(0x08) - -/* the watchdog can either generate a reset pulse, or an - * interrupt. - */ - -#define S3C2410_WTCON_RSTEN (0x01) -#define S3C2410_WTCON_INTEN (1<<2) -#define S3C2410_WTCON_ENABLE (1<<5) - -#define S3C2410_WTCON_DIV16 (0<<3) -#define S3C2410_WTCON_DIV32 (1<<3) -#define S3C2410_WTCON_DIV64 (2<<3) -#define S3C2410_WTCON_DIV128 (3<<3) - -#define S3C2410_WTCON_PRESCALE(x) ((x) << 8) -#define S3C2410_WTCON_PRESCALE_MASK (0xff00) - -#endif /* __ASM_ARCH_REGS_WATCHDOG_H */ - - diff --git a/arch/arm/plat-s3c/include/plat/sdhci.h b/arch/arm/plat-s3c/include/plat/sdhci.h deleted file mode 100644 index 5319867..0000000 --- a/arch/arm/plat-s3c/include/plat/sdhci.h +++ /dev/null @@ -1,226 +0,0 @@ -/* linux/arch/arm/plat-s3c/include/plat/sdhci.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C Platform - SDHCI (HSMMC) platform data definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __PLAT_S3C_SDHCI_H -#define __PLAT_S3C_SDHCI_H __FILE__ - -struct platform_device; -struct mmc_host; -struct mmc_card; -struct mmc_ios; - -/** - * struct s3c_sdhci_platdata() - Platform device data for Samsung SDHCI - * @max_width: The maximum number of data bits supported. - * @host_caps: Standard MMC host capabilities bit field. - * @cfg_gpio: Configure the GPIO for a specific card bit-width - * @cfg_card: Configure the interface for a specific card and speed. This - * is necessary the controllers and/or GPIO blocks require the - * changing of driver-strength and other controls dependant on - * the card and speed of operation. - * - * Initialisation data specific to either the machine or the platform - * for the device driver to use or call-back when configuring gpio or - * card speed information. -*/ -struct s3c_sdhci_platdata { - unsigned int max_width; - unsigned int host_caps; - - char **clocks; /* set of clock sources */ - - void (*cfg_gpio)(struct platform_device *dev, int width); - void (*cfg_card)(struct platform_device *dev, - void __iomem *regbase, - struct mmc_ios *ios, - struct mmc_card *card); -}; - -/** - * s3c_sdhci0_set_platdata - Set platform data for S3C SDHCI device. - * @pd: Platform data to register to device. - * - * Register the given platform data for use withe S3C SDHCI device. - * The call will copy the platform data, so the board definitions can - * make the structure itself __initdata. - */ -extern void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd); -extern void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd); -extern void s3c_sdhci2_set_platdata(struct s3c_sdhci_platdata *pd); - -/* Default platform data, exported so that per-cpu initialisation can - * set the correct one when there are more than one cpu type selected. -*/ - -extern struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata; -extern struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata; -extern struct s3c_sdhci_platdata s3c_hsmmc2_def_platdata; - -/* Helper function availablity */ - -extern void s3c64xx_setup_sdhci0_cfg_gpio(struct platform_device *, int w); -extern void s3c64xx_setup_sdhci1_cfg_gpio(struct platform_device *, int w); -extern void s5pc100_setup_sdhci0_cfg_gpio(struct platform_device *, int w); -extern void s5pc100_setup_sdhci1_cfg_gpio(struct platform_device *, int w); -extern void s5pc100_setup_sdhci2_cfg_gpio(struct platform_device *, int w); -extern void s3c64xx_setup_sdhci2_cfg_gpio(struct platform_device *, int w); - -/* S3C6400 SDHCI setup */ - -#ifdef CONFIG_S3C6400_SETUP_SDHCI -extern char *s3c6400_hsmmc_clksrcs[4]; - -#ifdef CONFIG_S3C_DEV_HSMMC -extern void s3c6400_setup_sdhci_cfg_card(struct platform_device *dev, - void __iomem *r, - struct mmc_ios *ios, - struct mmc_card *card); - -static inline void s3c6400_default_sdhci0(void) -{ - s3c_hsmmc0_def_platdata.clocks = s3c6400_hsmmc_clksrcs; - s3c_hsmmc0_def_platdata.cfg_gpio = s3c64xx_setup_sdhci0_cfg_gpio; - s3c_hsmmc0_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card; -} - -#else -static inline void s3c6400_default_sdhci0(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC */ - -#ifdef CONFIG_S3C_DEV_HSMMC1 -static inline void s3c6400_default_sdhci1(void) -{ - s3c_hsmmc1_def_platdata.clocks = s3c6400_hsmmc_clksrcs; - s3c_hsmmc1_def_platdata.cfg_gpio = s3c64xx_setup_sdhci1_cfg_gpio; - s3c_hsmmc1_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card; -} -#else -static inline void s3c6400_default_sdhci1(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC1 */ - -#ifdef CONFIG_S3C_DEV_HSMMC2 -static inline void s3c6400_default_sdhci2(void) -{ - s3c_hsmmc2_def_platdata.clocks = s3c6400_hsmmc_clksrcs; - s3c_hsmmc2_def_platdata.cfg_gpio = s3c64xx_setup_sdhci2_cfg_gpio; - s3c_hsmmc2_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card; -} -#else -static inline void s3c6400_default_sdhci2(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC2 */ - -#else -static inline void s3c6400_default_sdhci0(void) { } -static inline void s3c6400_default_sdhci1(void) { } -#endif /* CONFIG_S3C6400_SETUP_SDHCI */ - -/* S3C6410 SDHCI setup */ - -#ifdef CONFIG_S3C6410_SETUP_SDHCI -extern char *s3c6410_hsmmc_clksrcs[4]; - -extern void s3c6410_setup_sdhci0_cfg_card(struct platform_device *dev, - void __iomem *r, - struct mmc_ios *ios, - struct mmc_card *card); - -#ifdef CONFIG_S3C_DEV_HSMMC -static inline void s3c6410_default_sdhci0(void) -{ - s3c_hsmmc0_def_platdata.clocks = s3c6410_hsmmc_clksrcs; - s3c_hsmmc0_def_platdata.cfg_gpio = s3c64xx_setup_sdhci0_cfg_gpio; - s3c_hsmmc0_def_platdata.cfg_card = s3c6410_setup_sdhci0_cfg_card; -} -#else -static inline void s3c6410_default_sdhci0(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC */ - -#ifdef CONFIG_S3C_DEV_HSMMC1 -static inline void s3c6410_default_sdhci1(void) -{ - s3c_hsmmc1_def_platdata.clocks = s3c6410_hsmmc_clksrcs; - s3c_hsmmc1_def_platdata.cfg_gpio = s3c64xx_setup_sdhci1_cfg_gpio; - s3c_hsmmc1_def_platdata.cfg_card = s3c6410_setup_sdhci0_cfg_card; -} -#else -static inline void s3c6410_default_sdhci1(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC1 */ - -#ifdef CONFIG_S3C_DEV_HSMMC2 -static inline void s3c6410_default_sdhci2(void) -{ - s3c_hsmmc2_def_platdata.clocks = s3c6410_hsmmc_clksrcs; - s3c_hsmmc2_def_platdata.cfg_gpio = s3c64xx_setup_sdhci2_cfg_gpio; - s3c_hsmmc2_def_platdata.cfg_card = s3c6410_setup_sdhci0_cfg_card; -} -#else -static inline void s3c6410_default_sdhci2(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC2 */ - -#else -static inline void s3c6410_default_sdhci0(void) { } -static inline void s3c6410_default_sdhci1(void) { } -#endif /* CONFIG_S3C6410_SETUP_SDHCI */ - -/* S5PC100 SDHCI setup */ - -#ifdef CONFIG_S5PC100_SETUP_SDHCI -extern char *s5pc100_hsmmc_clksrcs[4]; - -extern void s5pc100_setup_sdhci0_cfg_card(struct platform_device *dev, - void __iomem *r, - struct mmc_ios *ios, - struct mmc_card *card); - -#ifdef CONFIG_S3C_DEV_HSMMC -static inline void s5pc100_default_sdhci0(void) -{ - s3c_hsmmc0_def_platdata.clocks = s5pc100_hsmmc_clksrcs; - s3c_hsmmc0_def_platdata.cfg_gpio = s5pc100_setup_sdhci0_cfg_gpio; - s3c_hsmmc0_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card; -} -#else -static inline void s5pc100_default_sdhci0(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC */ - -#ifdef CONFIG_S3C_DEV_HSMMC1 -static inline void s5pc100_default_sdhci1(void) -{ - s3c_hsmmc1_def_platdata.clocks = s5pc100_hsmmc_clksrcs; - s3c_hsmmc1_def_platdata.cfg_gpio = s5pc100_setup_sdhci1_cfg_gpio; - s3c_hsmmc1_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card; -} -#else -static inline void s5pc100_default_sdhci1(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC1 */ - -#ifdef CONFIG_S3C_DEV_HSMMC2 -static inline void s5pc100_default_sdhci2(void) -{ - s3c_hsmmc2_def_platdata.clocks = s5pc100_hsmmc_clksrcs; - s3c_hsmmc2_def_platdata.cfg_gpio = s5pc100_setup_sdhci2_cfg_gpio; - s3c_hsmmc2_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card; -} -#else -static inline void s5pc100_default_sdhci2(void) { } -#endif /* CONFIG_S3C_DEV_HSMMC1 */ - - -#else -static inline void s5pc100_default_sdhci0(void) { } -static inline void s5pc100_default_sdhci1(void) { } -static inline void s5pc100_default_sdhci2(void) { } -#endif /* CONFIG_S5PC100_SETUP_SDHCI */ - -#endif /* __PLAT_S3C_SDHCI_H */ diff --git a/arch/arm/plat-s3c/include/plat/udc-hs.h b/arch/arm/plat-s3c/include/plat/udc-hs.h deleted file mode 100644 index dd04db0..0000000 --- a/arch/arm/plat-s3c/include/plat/udc-hs.h +++ /dev/null @@ -1,29 +0,0 @@ -/* arch/arm/plat-s3c/include/plat/udc-hs.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C USB2.0 High-speed / OtG platform information - * - * 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. -*/ - -enum s3c_hostg_dmamode { - S3C_HSOTG_DMA_NONE, /* do not use DMA at-all */ - S3C_HSOTG_DMA_ONLY, /* always use DMA */ - S3C_HSOTG_DMA_DRV, /* DMA is chosen by driver */ -}; - -/** - * struct s3c_hsotg_plat - platform data for high-speed otg/udc - * @dma: Whether to use DMA or not. - * @is_osc: The clock source is an oscillator, not a crystal - */ -struct s3c_hsotg_plat { - enum s3c_hostg_dmamode dma; - unsigned int is_osc : 1; -}; diff --git a/arch/arm/plat-s3c/include/plat/watchdog-reset.h b/arch/arm/plat-s3c/include/plat/watchdog-reset.h deleted file mode 100644 index 54b762a..0000000 --- a/arch/arm/plat-s3c/include/plat/watchdog-reset.h +++ /dev/null @@ -1,49 +0,0 @@ -/* arch/arm/plat-s3c/include/plat/watchdog-reset.h - * - * Copyright (c) 2008 Simtec Electronics - * Ben Dooks - * - * S3C2410 - System define for arch_reset() function - * - * 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 - -static inline void arch_wdt_reset(void) -{ - struct clk *wdtclk; - - printk("arch_reset: attempting watchdog reset\n"); - - __raw_writel(0, S3C2410_WTCON); /* disable watchdog, to be safe */ - - wdtclk = clk_get(NULL, "watchdog"); - if (!IS_ERR(wdtclk)) { - clk_enable(wdtclk); - } else - printk(KERN_WARNING "%s: warning: cannot get watchdog clock\n", __func__); - - /* put initial values into count and data */ - __raw_writel(0x80, S3C2410_WTCNT); - __raw_writel(0x80, S3C2410_WTDAT); - - /* set the watchdog to go and reset... */ - __raw_writel(S3C2410_WTCON_ENABLE|S3C2410_WTCON_DIV16|S3C2410_WTCON_RSTEN | - S3C2410_WTCON_PRESCALE(0x20), S3C2410_WTCON); - - /* wait for reset to assert... */ - mdelay(500); - - printk(KERN_ERR "Watchdog reset failed to assert reset\n"); - - /* delay to allow the serial port to show the message */ - mdelay(50); -} diff --git a/arch/arm/plat-samsung/include/plat/dma-core.h b/arch/arm/plat-samsung/include/plat/dma-core.h new file mode 100644 index 0000000..32ff2a9 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/dma-core.h @@ -0,0 +1,22 @@ +/* arch/arm/plat-s3c/include/plat/dma.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * Samsung S3C DMA core 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. +*/ + +extern struct s3c2410_dma_chan *s3c_dma_lookup_channel(unsigned int channel); + +extern struct s3c2410_dma_chan *s3c_dma_chan_map[]; + +/* the currently allocated channel information */ +extern struct s3c2410_dma_chan s3c2410_chans[]; + + diff --git a/arch/arm/plat-samsung/include/plat/hwmon.h b/arch/arm/plat-samsung/include/plat/hwmon.h new file mode 100644 index 0000000..1ba88ea --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/hwmon.h @@ -0,0 +1,41 @@ +/* linux/arch/arm/plat-s3c/include/plat/hwmon.h + * + * Copyright 2005 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C - HWMon interface for ADC + * + * 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 __ASM_ARCH_ADC_HWMON_H +#define __ASM_ARCH_ADC_HWMON_H __FILE__ + +/** + * s3c_hwmon_chcfg - channel configuration + * @name: The name to give this channel. + * @mult: Multiply the ADC value read by this. + * @div: Divide the value from the ADC by this. + * + * The value read from the ADC is converted to a value that + * hwmon expects (mV) by result = (value_read * @mult) / @div. + */ +struct s3c_hwmon_chcfg { + const char *name; + unsigned int mult; + unsigned int div; +}; + +/** + * s3c_hwmon_pdata - HWMON platform data + * @in: One configuration for each possible channel used. + */ +struct s3c_hwmon_pdata { + struct s3c_hwmon_chcfg *in[8]; +}; + +#endif /* __ASM_ARCH_ADC_HWMON_H */ + diff --git a/arch/arm/plat-samsung/include/plat/iic-core.h b/arch/arm/plat-samsung/include/plat/iic-core.h new file mode 100644 index 0000000..36397ca --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/iic-core.h @@ -0,0 +1,35 @@ +/* arch/arm/mach-s3c2410/include/mach/iic-core.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * + * S3C - I2C Controller core functions + * + * 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 __ASM_ARCH_IIC_CORE_H +#define __ASM_ARCH_IIC_CORE_H __FILE__ + +/* These functions are only for use with the core support code, such as + * the cpu specific initialisation code + */ + +/* re-define device name depending on support. */ +static inline void s3c_i2c0_setname(char *name) +{ + /* currently this device is always compiled in */ + s3c_device_i2c0.name = name; +} + +static inline void s3c_i2c1_setname(char *name) +{ +#ifdef CONFIG_S3C_DEV_I2C1 + s3c_device_i2c1.name = name; +#endif +} + +#endif /* __ASM_ARCH_IIC_H */ diff --git a/arch/arm/plat-samsung/include/plat/iic.h b/arch/arm/plat-samsung/include/plat/iic.h new file mode 100644 index 0000000..3083df0 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/iic.h @@ -0,0 +1,62 @@ +/* arch/arm/plat-s3c/include/plat/iic.h + * + * Copyright 2004-2009 Simtec Electronics + * Ben Dooks + * + * S3C - I2C Controller platform_device info + * + * 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 __ASM_ARCH_IIC_H +#define __ASM_ARCH_IIC_H __FILE__ + +#define S3C_IICFLG_FILTER (1<<0) /* enable s3c2440 filter */ + +/** + * struct s3c2410_platform_i2c - Platform data for s3c I2C. + * @bus_num: The bus number to use (if possible). + * @flags: Any flags for the I2C bus (E.g. S3C_IICFLK_FILTER). + * @slave_addr: The I2C address for the slave device (if enabled). + * @frequency: The desired frequency in Hz of the bus. This is + * guaranteed to not be exceeded. If the caller does + * not care, use zero and the driver will select a + * useful default. + * @sda_delay: The delay (in ns) applied to SDA edges. + * @cfg_gpio: A callback to configure the pins for I2C operation. + */ +struct s3c2410_platform_i2c { + int bus_num; + unsigned int flags; + unsigned int slave_addr; + unsigned long frequency; + unsigned int sda_delay; + + void (*cfg_gpio)(struct platform_device *dev); +}; + +/** + * s3c_i2c0_set_platdata - set platform data for i2c0 device + * @i2c: The platform data to set, or NULL for default data. + * + * Register the given platform data for use with the i2c0 device. This + * call copies the platform data, so the caller can use __initdata for + * their copy. + * + * This call will set cfg_gpio if is null to the default platform + * implementation. + * + * Any user of s3c_device_i2c0 should call this, even if it is with + * NULL to ensure that the device is given the default platform data + * as the driver will no longer carry defaults. + */ +extern void s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *i2c); +extern void s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *i2c); + +/* defined by architecture to configure gpio */ +extern void s3c_i2c0_cfg_gpio(struct platform_device *dev); +extern void s3c_i2c1_cfg_gpio(struct platform_device *dev); + +#endif /* __ASM_ARCH_IIC_H */ diff --git a/arch/arm/plat-samsung/include/plat/nand.h b/arch/arm/plat-samsung/include/plat/nand.h new file mode 100644 index 0000000..226147b --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/nand.h @@ -0,0 +1,67 @@ +/* arch/arm/mach-s3c2410/include/mach/nand.h + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * S3C2410 - NAND device controller platfrom_device info + * + * 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. +*/ + +/** + * struct s3c2410_nand_set - define a set of one or more nand chips + * @disable_ecc: Entirely disable ECC - Dangerous + * @flash_bbt: Openmoko u-boot can create a Bad Block Table + * Setting this flag will allow the kernel to + * look for it at boot time and also skip the NAND + * scan. + * @options: Default value to set into 'struct nand_chip' options. + * @nr_chips: Number of chips in this set + * @nr_partitions: Number of partitions pointed to by @partitions + * @name: Name of set (optional) + * @nr_map: Map for low-layer logical to physical chip numbers (option) + * @partitions: The mtd partition list + * + * define a set of one or more nand chips registered with an unique mtd. Also + * allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger + * a warning at boot time. + */ +struct s3c2410_nand_set { + unsigned int disable_ecc:1; + unsigned int flash_bbt:1; + + unsigned int options; + int nr_chips; + int nr_partitions; + char *name; + int *nr_map; + struct mtd_partition *partitions; + struct nand_ecclayout *ecc_layout; +}; + +struct s3c2410_platform_nand { + /* timing information for controller, all times in nanoseconds */ + + int tacls; /* time for active CLE/ALE to nWE/nOE */ + int twrph0; /* active time for nWE/nOE */ + int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ + + unsigned int ignore_unset_ecc:1; + + int nr_sets; + struct s3c2410_nand_set *sets; + + void (*select_chip)(struct s3c2410_nand_set *, + int chip); +}; + +/** + * s3c_nand_set_platdata() - register NAND platform data. + * @nand: The NAND platform data to register with s3c_device_nand. + * + * This function copies the given NAND platform data, @nand and registers + * it with the s3c_device_nand. This allows @nand to be __initdata. +*/ +extern void s3c_nand_set_platdata(struct s3c2410_platform_nand *nand); diff --git a/arch/arm/plat-samsung/include/plat/regs-ac97.h b/arch/arm/plat-samsung/include/plat/regs-ac97.h new file mode 100644 index 0000000..c3878f7 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-ac97.h @@ -0,0 +1,67 @@ +/* arch/arm/mach-s3c2410/include/mach/regs-ac97.h + * + * Copyright (c) 2006 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * 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. + * + * S3C2440 AC97 Controller +*/ + +#ifndef __ASM_ARCH_REGS_AC97_H +#define __ASM_ARCH_REGS_AC97_H __FILE__ + +#define S3C_AC97_GLBCTRL (0x00) + +#define S3C_AC97_GLBCTRL_CODECREADYIE (1<<22) +#define S3C_AC97_GLBCTRL_PCMOUTURIE (1<<21) +#define S3C_AC97_GLBCTRL_PCMINORIE (1<<20) +#define S3C_AC97_GLBCTRL_MICINORIE (1<<19) +#define S3C_AC97_GLBCTRL_PCMOUTTIE (1<<18) +#define S3C_AC97_GLBCTRL_PCMINTIE (1<<17) +#define S3C_AC97_GLBCTRL_MICINTIE (1<<16) +#define S3C_AC97_GLBCTRL_PCMOUTTM_OFF (0<<12) +#define S3C_AC97_GLBCTRL_PCMOUTTM_PIO (1<<12) +#define S3C_AC97_GLBCTRL_PCMOUTTM_DMA (2<<12) +#define S3C_AC97_GLBCTRL_PCMOUTTM_MASK (3<<12) +#define S3C_AC97_GLBCTRL_PCMINTM_OFF (0<<10) +#define S3C_AC97_GLBCTRL_PCMINTM_PIO (1<<10) +#define S3C_AC97_GLBCTRL_PCMINTM_DMA (2<<10) +#define S3C_AC97_GLBCTRL_PCMINTM_MASK (3<<10) +#define S3C_AC97_GLBCTRL_MICINTM_OFF (0<<8) +#define S3C_AC97_GLBCTRL_MICINTM_PIO (1<<8) +#define S3C_AC97_GLBCTRL_MICINTM_DMA (2<<8) +#define S3C_AC97_GLBCTRL_MICINTM_MASK (3<<8) +#define S3C_AC97_GLBCTRL_TRANSFERDATAENABLE (1<<3) +#define S3C_AC97_GLBCTRL_ACLINKON (1<<2) +#define S3C_AC97_GLBCTRL_WARMRESET (1<<1) +#define S3C_AC97_GLBCTRL_COLDRESET (1<<0) + +#define S3C_AC97_GLBSTAT (0x04) + +#define S3C_AC97_GLBSTAT_CODECREADY (1<<22) +#define S3C_AC97_GLBSTAT_PCMOUTUR (1<<21) +#define S3C_AC97_GLBSTAT_PCMINORI (1<<20) +#define S3C_AC97_GLBSTAT_MICINORI (1<<19) +#define S3C_AC97_GLBSTAT_PCMOUTTI (1<<18) +#define S3C_AC97_GLBSTAT_PCMINTI (1<<17) +#define S3C_AC97_GLBSTAT_MICINTI (1<<16) +#define S3C_AC97_GLBSTAT_MAINSTATE_IDLE (0<<0) +#define S3C_AC97_GLBSTAT_MAINSTATE_INIT (1<<0) +#define S3C_AC97_GLBSTAT_MAINSTATE_READY (2<<0) +#define S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE (3<<0) +#define S3C_AC97_GLBSTAT_MAINSTATE_LP (4<<0) +#define S3C_AC97_GLBSTAT_MAINSTATE_WARM (5<<0) + +#define S3C_AC97_CODEC_CMD (0x08) + +#define S3C_AC97_CODEC_CMD_READ (1<<23) + +#define S3C_AC97_STAT (0x0c) +#define S3C_AC97_PCM_ADDR (0x10) +#define S3C_AC97_PCM_DATA (0x18) +#define S3C_AC97_MIC_DATA (0x1C) + +#endif /* __ASM_ARCH_REGS_AC97_H */ diff --git a/arch/arm/plat-samsung/include/plat/regs-adc.h b/arch/arm/plat-samsung/include/plat/regs-adc.h new file mode 100644 index 0000000..f43c8da --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-adc.h @@ -0,0 +1,63 @@ +/* arch/arm/mach-s3c2410/include/mach/regs-adc.h + * + * Copyright (c) 2004 Shannon Holland + * + * This program is free software; yosu 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. + * + * S3C2410 ADC registers +*/ + +#ifndef __ASM_ARCH_REGS_ADC_H +#define __ASM_ARCH_REGS_ADC_H "regs-adc.h" + +#define S3C2410_ADCREG(x) (x) + +#define S3C2410_ADCCON S3C2410_ADCREG(0x00) +#define S3C2410_ADCTSC S3C2410_ADCREG(0x04) +#define S3C2410_ADCDLY S3C2410_ADCREG(0x08) +#define S3C2410_ADCDAT0 S3C2410_ADCREG(0x0C) +#define S3C2410_ADCDAT1 S3C2410_ADCREG(0x10) +#define S3C64XX_ADCUPDN S3C2410_ADCREG(0x14) +#define S3C64XX_ADCCLRINT S3C2410_ADCREG(0x18) +#define S3C64XX_ADCCLRINTPNDNUP S3C2410_ADCREG(0x20) + + +/* ADCCON Register Bits */ +#define S3C2410_ADCCON_ECFLG (1<<15) +#define S3C2410_ADCCON_PRSCEN (1<<14) +#define S3C2410_ADCCON_PRSCVL(x) (((x)&0xFF)<<6) +#define S3C2410_ADCCON_PRSCVLMASK (0xFF<<6) +#define S3C2410_ADCCON_SELMUX(x) (((x)&0x7)<<3) +#define S3C2410_ADCCON_MUXMASK (0x7<<3) +#define S3C2410_ADCCON_STDBM (1<<2) +#define S3C2410_ADCCON_READ_START (1<<1) +#define S3C2410_ADCCON_ENABLE_START (1<<0) +#define S3C2410_ADCCON_STARTMASK (0x3<<0) + + +/* ADCTSC Register Bits */ +#define S3C2410_ADCTSC_YM_SEN (1<<7) +#define S3C2410_ADCTSC_YP_SEN (1<<6) +#define S3C2410_ADCTSC_XM_SEN (1<<5) +#define S3C2410_ADCTSC_XP_SEN (1<<4) +#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3) +#define S3C2410_ADCTSC_AUTO_PST (1<<2) +#define S3C2410_ADCTSC_XY_PST(x) (((x)&0x3)<<0) + +/* ADCDAT0 Bits */ +#define S3C2410_ADCDAT0_UPDOWN (1<<15) +#define S3C2410_ADCDAT0_AUTO_PST (1<<14) +#define S3C2410_ADCDAT0_XY_PST (0x3<<12) +#define S3C2410_ADCDAT0_XPDATA_MASK (0x03FF) + +/* ADCDAT1 Bits */ +#define S3C2410_ADCDAT1_UPDOWN (1<<15) +#define S3C2410_ADCDAT1_AUTO_PST (1<<14) +#define S3C2410_ADCDAT1_XY_PST (0x3<<12) +#define S3C2410_ADCDAT1_YPDATA_MASK (0x03FF) + +#endif /* __ASM_ARCH_REGS_ADC_H */ + + diff --git a/arch/arm/plat-samsung/include/plat/regs-iic.h b/arch/arm/plat-samsung/include/plat/regs-iic.h new file mode 100644 index 0000000..2f7c17d --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-iic.h @@ -0,0 +1,56 @@ +/* arch/arm/mach-s3c2410/include/mach/regs-iic.h + * + * Copyright (c) 2004 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * 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. + * + * S3C2410 I2C Controller +*/ + +#ifndef __ASM_ARCH_REGS_IIC_H +#define __ASM_ARCH_REGS_IIC_H __FILE__ + +/* see s3c2410x user guide, v1.1, section 9 (p447) for more info */ + +#define S3C2410_IICREG(x) (x) + +#define S3C2410_IICCON S3C2410_IICREG(0x00) +#define S3C2410_IICSTAT S3C2410_IICREG(0x04) +#define S3C2410_IICADD S3C2410_IICREG(0x08) +#define S3C2410_IICDS S3C2410_IICREG(0x0C) +#define S3C2440_IICLC S3C2410_IICREG(0x10) + +#define S3C2410_IICCON_ACKEN (1<<7) +#define S3C2410_IICCON_TXDIV_16 (0<<6) +#define S3C2410_IICCON_TXDIV_512 (1<<6) +#define S3C2410_IICCON_IRQEN (1<<5) +#define S3C2410_IICCON_IRQPEND (1<<4) +#define S3C2410_IICCON_SCALE(x) ((x)&15) +#define S3C2410_IICCON_SCALEMASK (0xf) + +#define S3C2410_IICSTAT_MASTER_RX (2<<6) +#define S3C2410_IICSTAT_MASTER_TX (3<<6) +#define S3C2410_IICSTAT_SLAVE_RX (0<<6) +#define S3C2410_IICSTAT_SLAVE_TX (1<<6) +#define S3C2410_IICSTAT_MODEMASK (3<<6) + +#define S3C2410_IICSTAT_START (1<<5) +#define S3C2410_IICSTAT_BUSBUSY (1<<5) +#define S3C2410_IICSTAT_TXRXEN (1<<4) +#define S3C2410_IICSTAT_ARBITR (1<<3) +#define S3C2410_IICSTAT_ASSLAVE (1<<2) +#define S3C2410_IICSTAT_ADDR0 (1<<1) +#define S3C2410_IICSTAT_LASTBIT (1<<0) + +#define S3C2410_IICLC_SDA_DELAY0 (0 << 0) +#define S3C2410_IICLC_SDA_DELAY5 (1 << 0) +#define S3C2410_IICLC_SDA_DELAY10 (2 << 0) +#define S3C2410_IICLC_SDA_DELAY15 (3 << 0) +#define S3C2410_IICLC_SDA_DELAY_MASK (3 << 0) + +#define S3C2410_IICLC_FILTER_ON (1<<2) + +#endif /* __ASM_ARCH_REGS_IIC_H */ diff --git a/arch/arm/plat-samsung/include/plat/regs-irqtype.h b/arch/arm/plat-samsung/include/plat/regs-irqtype.h new file mode 100644 index 0000000..c63cd3f --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-irqtype.h @@ -0,0 +1,21 @@ +/* arch/arm/plat-s3c/include/plat/regs-irqtype.h + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C - IRQ detection types. + * + * 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. + */ + +/* values for S3C2410_EXTINT0/1/2 and other cpus in the series, including + * the S3C64XX +*/ +#define S3C2410_EXTINT_LOWLEV (0x00) +#define S3C2410_EXTINT_HILEV (0x01) +#define S3C2410_EXTINT_FALLEDGE (0x02) +#define S3C2410_EXTINT_RISEEDGE (0x04) +#define S3C2410_EXTINT_BOTHEDGE (0x06) diff --git a/arch/arm/plat-samsung/include/plat/regs-nand.h b/arch/arm/plat-samsung/include/plat/regs-nand.h new file mode 100644 index 0000000..238efea --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-nand.h @@ -0,0 +1,123 @@ +/* arch/arm/mach-s3c2410/include/mach/regs-nand.h + * + * Copyright (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * 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. + * + * S3C2410 NAND register definitions +*/ + +#ifndef __ASM_ARM_REGS_NAND +#define __ASM_ARM_REGS_NAND + + +#define S3C2410_NFREG(x) (x) + +#define S3C2410_NFCONF S3C2410_NFREG(0x00) +#define S3C2410_NFCMD S3C2410_NFREG(0x04) +#define S3C2410_NFADDR S3C2410_NFREG(0x08) +#define S3C2410_NFDATA S3C2410_NFREG(0x0C) +#define S3C2410_NFSTAT S3C2410_NFREG(0x10) +#define S3C2410_NFECC S3C2410_NFREG(0x14) + +#define S3C2440_NFCONT S3C2410_NFREG(0x04) +#define S3C2440_NFCMD S3C2410_NFREG(0x08) +#define S3C2440_NFADDR S3C2410_NFREG(0x0C) +#define S3C2440_NFDATA S3C2410_NFREG(0x10) +#define S3C2440_NFECCD0 S3C2410_NFREG(0x14) +#define S3C2440_NFECCD1 S3C2410_NFREG(0x18) +#define S3C2440_NFECCD S3C2410_NFREG(0x1C) +#define S3C2440_NFSTAT S3C2410_NFREG(0x20) +#define S3C2440_NFESTAT0 S3C2410_NFREG(0x24) +#define S3C2440_NFESTAT1 S3C2410_NFREG(0x28) +#define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) +#define S3C2440_NFMECC1 S3C2410_NFREG(0x30) +#define S3C2440_NFSECC S3C2410_NFREG(0x34) +#define S3C2440_NFSBLK S3C2410_NFREG(0x38) +#define S3C2440_NFEBLK S3C2410_NFREG(0x3C) + +#define S3C2412_NFSBLK S3C2410_NFREG(0x20) +#define S3C2412_NFEBLK S3C2410_NFREG(0x24) +#define S3C2412_NFSTAT S3C2410_NFREG(0x28) +#define S3C2412_NFMECC_ERR0 S3C2410_NFREG(0x2C) +#define S3C2412_NFMECC_ERR1 S3C2410_NFREG(0x30) +#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) +#define S3C2412_NFMECC1 S3C2410_NFREG(0x38) +#define S3C2412_NFSECC S3C2410_NFREG(0x3C) + +#define S3C2410_NFCONF_EN (1<<15) +#define S3C2410_NFCONF_512BYTE (1<<14) +#define S3C2410_NFCONF_4STEP (1<<13) +#define S3C2410_NFCONF_INITECC (1<<12) +#define S3C2410_NFCONF_nFCE (1<<11) +#define S3C2410_NFCONF_TACLS(x) ((x)<<8) +#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) +#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) + +#define S3C2410_NFSTAT_BUSY (1<<0) + +#define S3C2440_NFCONF_BUSWIDTH_8 (0<<0) +#define S3C2440_NFCONF_BUSWIDTH_16 (1<<0) +#define S3C2440_NFCONF_ADVFLASH (1<<3) +#define S3C2440_NFCONF_TACLS(x) ((x)<<12) +#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) +#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) + +#define S3C2440_NFCONT_LOCKTIGHT (1<<13) +#define S3C2440_NFCONT_SOFTLOCK (1<<12) +#define S3C2440_NFCONT_ILLEGALACC_EN (1<<10) +#define S3C2440_NFCONT_RNBINT_EN (1<<9) +#define S3C2440_NFCONT_RN_FALLING (1<<8) +#define S3C2440_NFCONT_SPARE_ECCLOCK (1<<6) +#define S3C2440_NFCONT_MAIN_ECCLOCK (1<<5) +#define S3C2440_NFCONT_INITECC (1<<4) +#define S3C2440_NFCONT_nFCE (1<<1) +#define S3C2440_NFCONT_ENABLE (1<<0) + +#define S3C2440_NFSTAT_READY (1<<0) +#define S3C2440_NFSTAT_nCE (1<<1) +#define S3C2440_NFSTAT_RnB_CHANGE (1<<2) +#define S3C2440_NFSTAT_ILLEGAL_ACCESS (1<<3) + +#define S3C2412_NFCONF_NANDBOOT (1<<31) +#define S3C2412_NFCONF_ECCCLKCON (1<<30) +#define S3C2412_NFCONF_ECC_MLC (1<<24) +#define S3C2412_NFCONF_TACLS_MASK (7<<12) /* 1 extra bit of Tacls */ + +#define S3C2412_NFCONT_ECC4_DIRWR (1<<18) +#define S3C2412_NFCONT_LOCKTIGHT (1<<17) +#define S3C2412_NFCONT_SOFTLOCK (1<<16) +#define S3C2412_NFCONT_ECC4_ENCINT (1<<13) +#define S3C2412_NFCONT_ECC4_DECINT (1<<12) +#define S3C2412_NFCONT_MAIN_ECC_LOCK (1<<7) +#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5) +#define S3C2412_NFCONT_nFCE1 (1<<2) +#define S3C2412_NFCONT_nFCE0 (1<<1) + +#define S3C2412_NFSTAT_ECC_ENCDONE (1<<7) +#define S3C2412_NFSTAT_ECC_DECDONE (1<<6) +#define S3C2412_NFSTAT_ILLEGAL_ACCESS (1<<5) +#define S3C2412_NFSTAT_RnB_CHANGE (1<<4) +#define S3C2412_NFSTAT_nFCE1 (1<<3) +#define S3C2412_NFSTAT_nFCE0 (1<<2) +#define S3C2412_NFSTAT_Res1 (1<<1) +#define S3C2412_NFSTAT_READY (1<<0) + +#define S3C2412_NFECCERR_SERRDATA(x) (((x) >> 21) & 0xf) +#define S3C2412_NFECCERR_SERRBIT(x) (((x) >> 18) & 0x7) +#define S3C2412_NFECCERR_MERRDATA(x) (((x) >> 7) & 0x3ff) +#define S3C2412_NFECCERR_MERRBIT(x) (((x) >> 4) & 0x7) +#define S3C2412_NFECCERR_SPARE_ERR(x) (((x) >> 2) & 0x3) +#define S3C2412_NFECCERR_MAIN_ERR(x) (((x) >> 2) & 0x3) +#define S3C2412_NFECCERR_NONE (0) +#define S3C2412_NFECCERR_1BIT (1) +#define S3C2412_NFECCERR_MULTIBIT (2) +#define S3C2412_NFECCERR_ECCAREA (3) + + + +#endif /* __ASM_ARM_REGS_NAND */ + diff --git a/arch/arm/plat-samsung/include/plat/regs-rtc.h b/arch/arm/plat-samsung/include/plat/regs-rtc.h new file mode 100644 index 0000000..d5837cf --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-rtc.h @@ -0,0 +1,61 @@ +/* arch/arm/mach-s3c2410/include/mach/regs-rtc.h + * + * Copyright (c) 2003 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * 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. + * + * S3C2410 Internal RTC register definition +*/ + +#ifndef __ASM_ARCH_REGS_RTC_H +#define __ASM_ARCH_REGS_RTC_H __FILE__ + +#define S3C2410_RTCREG(x) (x) + +#define S3C2410_RTCCON S3C2410_RTCREG(0x40) +#define S3C2410_RTCCON_RTCEN (1<<0) +#define S3C2410_RTCCON_CLKSEL (1<<1) +#define S3C2410_RTCCON_CNTSEL (1<<2) +#define S3C2410_RTCCON_CLKRST (1<<3) + +#define S3C2410_TICNT S3C2410_RTCREG(0x44) +#define S3C2410_TICNT_ENABLE (1<<7) + +#define S3C2410_RTCALM S3C2410_RTCREG(0x50) +#define S3C2410_RTCALM_ALMEN (1<<6) +#define S3C2410_RTCALM_YEAREN (1<<5) +#define S3C2410_RTCALM_MONEN (1<<4) +#define S3C2410_RTCALM_DAYEN (1<<3) +#define S3C2410_RTCALM_HOUREN (1<<2) +#define S3C2410_RTCALM_MINEN (1<<1) +#define S3C2410_RTCALM_SECEN (1<<0) + +#define S3C2410_RTCALM_ALL \ + S3C2410_RTCALM_ALMEN | S3C2410_RTCALM_YEAREN | S3C2410_RTCALM_MONEN |\ + S3C2410_RTCALM_DAYEN | S3C2410_RTCALM_HOUREN | S3C2410_RTCALM_MINEN |\ + S3C2410_RTCALM_SECEN + + +#define S3C2410_ALMSEC S3C2410_RTCREG(0x54) +#define S3C2410_ALMMIN S3C2410_RTCREG(0x58) +#define S3C2410_ALMHOUR S3C2410_RTCREG(0x5c) + +#define S3C2410_ALMDATE S3C2410_RTCREG(0x60) +#define S3C2410_ALMMON S3C2410_RTCREG(0x64) +#define S3C2410_ALMYEAR S3C2410_RTCREG(0x68) + +#define S3C2410_RTCRST S3C2410_RTCREG(0x6c) + +#define S3C2410_RTCSEC S3C2410_RTCREG(0x70) +#define S3C2410_RTCMIN S3C2410_RTCREG(0x74) +#define S3C2410_RTCHOUR S3C2410_RTCREG(0x78) +#define S3C2410_RTCDATE S3C2410_RTCREG(0x7c) +#define S3C2410_RTCDAY S3C2410_RTCREG(0x80) +#define S3C2410_RTCMON S3C2410_RTCREG(0x84) +#define S3C2410_RTCYEAR S3C2410_RTCREG(0x88) + + +#endif /* __ASM_ARCH_REGS_RTC_H */ diff --git a/arch/arm/plat-samsung/include/plat/regs-s3c2412-iis.h b/arch/arm/plat-samsung/include/plat/regs-s3c2412-iis.h new file mode 100644 index 0000000..abf2fbc --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-s3c2412-iis.h @@ -0,0 +1,82 @@ +/* linux/include/asm-arm/plat-s3c24xx/regs-s3c2412-iis.h + * + * Copyright 2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * 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. + * + * S3C2412 IIS register definition +*/ + +#ifndef __ASM_ARCH_REGS_S3C2412_IIS_H +#define __ASM_ARCH_REGS_S3C2412_IIS_H + +#define S3C2412_IISCON (0x00) +#define S3C2412_IISMOD (0x04) +#define S3C2412_IISFIC (0x08) +#define S3C2412_IISPSR (0x0C) +#define S3C2412_IISTXD (0x10) +#define S3C2412_IISRXD (0x14) + +#define S3C2412_IISCON_LRINDEX (1 << 11) +#define S3C2412_IISCON_TXFIFO_EMPTY (1 << 10) +#define S3C2412_IISCON_RXFIFO_EMPTY (1 << 9) +#define S3C2412_IISCON_TXFIFO_FULL (1 << 8) +#define S3C2412_IISCON_RXFIFO_FULL (1 << 7) +#define S3C2412_IISCON_TXDMA_PAUSE (1 << 6) +#define S3C2412_IISCON_RXDMA_PAUSE (1 << 5) +#define S3C2412_IISCON_TXCH_PAUSE (1 << 4) +#define S3C2412_IISCON_RXCH_PAUSE (1 << 3) +#define S3C2412_IISCON_TXDMA_ACTIVE (1 << 2) +#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1) +#define S3C2412_IISCON_IIS_ACTIVE (1 << 0) + +#define S3C64XX_IISMOD_BLC_16BIT (0 << 13) +#define S3C64XX_IISMOD_BLC_8BIT (1 << 13) +#define S3C64XX_IISMOD_BLC_24BIT (2 << 13) +#define S3C64XX_IISMOD_BLC_MASK (3 << 13) + +#define S3C64XX_IISMOD_IMS_PCLK (0 << 10) +#define S3C64XX_IISMOD_IMS_SYSMUX (1 << 10) + +#define S3C2412_IISMOD_MASTER_INTERNAL (0 << 10) +#define S3C2412_IISMOD_MASTER_EXTERNAL (1 << 10) +#define S3C2412_IISMOD_SLAVE (2 << 10) +#define S3C2412_IISMOD_MASTER_MASK (3 << 10) +#define S3C2412_IISMOD_MODE_TXONLY (0 << 8) +#define S3C2412_IISMOD_MODE_RXONLY (1 << 8) +#define S3C2412_IISMOD_MODE_TXRX (2 << 8) +#define S3C2412_IISMOD_MODE_MASK (3 << 8) +#define S3C2412_IISMOD_LR_LLOW (0 << 7) +#define S3C2412_IISMOD_LR_RLOW (1 << 7) +#define S3C2412_IISMOD_SDF_IIS (0 << 5) +#define S3C2412_IISMOD_SDF_MSB (1 << 5) +#define S3C2412_IISMOD_SDF_LSB (2 << 5) +#define S3C2412_IISMOD_SDF_MASK (3 << 5) +#define S3C2412_IISMOD_RCLK_256FS (0 << 3) +#define S3C2412_IISMOD_RCLK_512FS (1 << 3) +#define S3C2412_IISMOD_RCLK_384FS (2 << 3) +#define S3C2412_IISMOD_RCLK_768FS (3 << 3) +#define S3C2412_IISMOD_RCLK_MASK (3 << 3) +#define S3C2412_IISMOD_BCLK_32FS (0 << 1) +#define S3C2412_IISMOD_BCLK_48FS (1 << 1) +#define S3C2412_IISMOD_BCLK_16FS (2 << 1) +#define S3C2412_IISMOD_BCLK_24FS (3 << 1) +#define S3C2412_IISMOD_BCLK_MASK (3 << 1) +#define S3C2412_IISMOD_8BIT (1 << 0) + +#define S3C64XX_IISMOD_CDCLKCON (1 << 12) + +#define S3C2412_IISPSR_PSREN (1 << 15) + +#define S3C2412_IISFIC_TXFLUSH (1 << 15) +#define S3C2412_IISFIC_RXFLUSH (1 << 7) +#define S3C2412_IISFIC_TXCOUNT(x) (((x) >> 8) & 0xf) +#define S3C2412_IISFIC_RXCOUNT(x) (((x) >> 0) & 0xf) + + + +#endif /* __ASM_ARCH_REGS_S3C2412_IIS_H */ + diff --git a/arch/arm/plat-samsung/include/plat/regs-sdhci.h b/arch/arm/plat-samsung/include/plat/regs-sdhci.h new file mode 100644 index 0000000..e34049ad --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-sdhci.h @@ -0,0 +1,87 @@ +/* linux/arch/arm/plat-s3c/include/plat/regs-sdhci.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C Platform - SDHCI (HSMMC) register definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __PLAT_S3C_SDHCI_REGS_H +#define __PLAT_S3C_SDHCI_REGS_H __FILE__ + +#define S3C_SDHCI_CONTROL2 (0x80) +#define S3C_SDHCI_CONTROL3 (0x84) +#define S3C64XX_SDHCI_CONTROL4 (0x8C) + +#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31) +#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK (1 << 30) +#define S3C_SDHCI_CTRL2_CDINVRXD3 (1 << 29) +#define S3C_SDHCI_CTRL2_SLCARDOUT (1 << 28) + +#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24) +#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24) +#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24) + +#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16) +#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16) +#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16) + +#define S3C_SDHCI_CTRL2_ENFBCLKTX (1 << 15) +#define S3C_SDHCI_CTRL2_ENFBCLKRX (1 << 14) +#define S3C_SDHCI_CTRL2_SDCDSEL (1 << 13) +#define S3C_SDHCI_CTRL2_SDSIGPC (1 << 12) +#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11) + +#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9) +#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9) +#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9) +#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9) +#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9) +#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9) + +#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8) +#define S3C_SDHCI_CTRL2_RWAITMODE (1 << 7) +#define S3C_SDHCI_CTRL2_DISBUFRD (1 << 6) +#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4) +#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4) +#define S3C_SDHCI_CTRL2_PWRSYNC (1 << 3) +#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1) +#define S3C_SDHCI_CTRL2_HWINITFIN (1 << 0) + +#define S3C_SDHCI_CTRL3_FCSEL3 (1 << 31) +#define S3C_SDHCI_CTRL3_FCSEL2 (1 << 23) +#define S3C_SDHCI_CTRL3_FCSEL1 (1 << 15) +#define S3C_SDHCI_CTRL3_FCSEL0 (1 << 7) + +#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24) +#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24) +#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24) + +#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16) +#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16) +#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16) + +#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8) +#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8) +#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8) + +#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0) +#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0) +#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0) + +#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16) + +#define S3C64XX_SDHCI_CONTROL4_BUSY (1) + +#endif /* __PLAT_S3C_SDHCI_REGS_H */ diff --git a/arch/arm/plat-samsung/include/plat/regs-timer.h b/arch/arm/plat-samsung/include/plat/regs-timer.h new file mode 100644 index 0000000..d097d92 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-timer.h @@ -0,0 +1,124 @@ +/* arch/arm/mach-s3c2410/include/mach/regs-timer.h + * + * Copyright (c) 2003 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * 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. + * + * S3C2410 Timer configuration +*/ + +#ifndef __ASM_ARCH_REGS_TIMER_H +#define __ASM_ARCH_REGS_TIMER_H + +#define S3C_TIMERREG(x) (S3C_VA_TIMER + (x)) +#define S3C_TIMERREG2(tmr,reg) S3C_TIMERREG((reg)+0x0c+((tmr)*0x0c)) + +#define S3C2410_TCFG0 S3C_TIMERREG(0x00) +#define S3C2410_TCFG1 S3C_TIMERREG(0x04) +#define S3C2410_TCON S3C_TIMERREG(0x08) + +#define S3C64XX_TINT_CSTAT S3C_TIMERREG(0x44) + +#define S3C2410_TCFG_PRESCALER0_MASK (255<<0) +#define S3C2410_TCFG_PRESCALER1_MASK (255<<8) +#define S3C2410_TCFG_PRESCALER1_SHIFT (8) +#define S3C2410_TCFG_DEADZONE_MASK (255<<16) +#define S3C2410_TCFG_DEADZONE_SHIFT (16) + +#define S3C2410_TCFG1_MUX4_DIV2 (0<<16) +#define S3C2410_TCFG1_MUX4_DIV4 (1<<16) +#define S3C2410_TCFG1_MUX4_DIV8 (2<<16) +#define S3C2410_TCFG1_MUX4_DIV16 (3<<16) +#define S3C2410_TCFG1_MUX4_TCLK1 (4<<16) +#define S3C2410_TCFG1_MUX4_MASK (15<<16) +#define S3C2410_TCFG1_MUX4_SHIFT (16) + +#define S3C2410_TCFG1_MUX3_DIV2 (0<<12) +#define S3C2410_TCFG1_MUX3_DIV4 (1<<12) +#define S3C2410_TCFG1_MUX3_DIV8 (2<<12) +#define S3C2410_TCFG1_MUX3_DIV16 (3<<12) +#define S3C2410_TCFG1_MUX3_TCLK1 (4<<12) +#define S3C2410_TCFG1_MUX3_MASK (15<<12) + + +#define S3C2410_TCFG1_MUX2_DIV2 (0<<8) +#define S3C2410_TCFG1_MUX2_DIV4 (1<<8) +#define S3C2410_TCFG1_MUX2_DIV8 (2<<8) +#define S3C2410_TCFG1_MUX2_DIV16 (3<<8) +#define S3C2410_TCFG1_MUX2_TCLK1 (4<<8) +#define S3C2410_TCFG1_MUX2_MASK (15<<8) + + +#define S3C2410_TCFG1_MUX1_DIV2 (0<<4) +#define S3C2410_TCFG1_MUX1_DIV4 (1<<4) +#define S3C2410_TCFG1_MUX1_DIV8 (2<<4) +#define S3C2410_TCFG1_MUX1_DIV16 (3<<4) +#define S3C2410_TCFG1_MUX1_TCLK0 (4<<4) +#define S3C2410_TCFG1_MUX1_MASK (15<<4) + +#define S3C2410_TCFG1_MUX0_DIV2 (0<<0) +#define S3C2410_TCFG1_MUX0_DIV4 (1<<0) +#define S3C2410_TCFG1_MUX0_DIV8 (2<<0) +#define S3C2410_TCFG1_MUX0_DIV16 (3<<0) +#define S3C2410_TCFG1_MUX0_TCLK0 (4<<0) +#define S3C2410_TCFG1_MUX0_MASK (15<<0) + +#define S3C2410_TCFG1_MUX_DIV2 (0<<0) +#define S3C2410_TCFG1_MUX_DIV4 (1<<0) +#define S3C2410_TCFG1_MUX_DIV8 (2<<0) +#define S3C2410_TCFG1_MUX_DIV16 (3<<0) +#define S3C2410_TCFG1_MUX_TCLK (4<<0) +#define S3C2410_TCFG1_MUX_MASK (15<<0) + +#define S3C64XX_TCFG1_MUX_DIV1 (0<<0) +#define S3C64XX_TCFG1_MUX_DIV2 (1<<0) +#define S3C64XX_TCFG1_MUX_DIV4 (2<<0) +#define S3C64XX_TCFG1_MUX_DIV8 (3<<0) +#define S3C64XX_TCFG1_MUX_DIV16 (4<<0) +#define S3C64XX_TCFG1_MUX_TCLK (5<<0) /* 3 sets of TCLK */ +#define S3C64XX_TCFG1_MUX_MASK (15<<0) + +#define S3C2410_TCFG1_SHIFT(x) ((x) * 4) + +/* for each timer, we have an count buffer, an compare buffer and + * an observation buffer +*/ + +/* WARNING - timer 4 has no buffer reg, and it's observation is at +4 */ + +#define S3C2410_TCNTB(tmr) S3C_TIMERREG2(tmr, 0x00) +#define S3C2410_TCMPB(tmr) S3C_TIMERREG2(tmr, 0x04) +#define S3C2410_TCNTO(tmr) S3C_TIMERREG2(tmr, (((tmr) == 4) ? 0x04 : 0x08)) + +#define S3C2410_TCON_T4RELOAD (1<<22) +#define S3C2410_TCON_T4MANUALUPD (1<<21) +#define S3C2410_TCON_T4START (1<<20) + +#define S3C2410_TCON_T3RELOAD (1<<19) +#define S3C2410_TCON_T3INVERT (1<<18) +#define S3C2410_TCON_T3MANUALUPD (1<<17) +#define S3C2410_TCON_T3START (1<<16) + +#define S3C2410_TCON_T2RELOAD (1<<15) +#define S3C2410_TCON_T2INVERT (1<<14) +#define S3C2410_TCON_T2MANUALUPD (1<<13) +#define S3C2410_TCON_T2START (1<<12) + +#define S3C2410_TCON_T1RELOAD (1<<11) +#define S3C2410_TCON_T1INVERT (1<<10) +#define S3C2410_TCON_T1MANUALUPD (1<<9) +#define S3C2410_TCON_T1START (1<<8) + +#define S3C2410_TCON_T0DEADZONE (1<<4) +#define S3C2410_TCON_T0RELOAD (1<<3) +#define S3C2410_TCON_T0INVERT (1<<2) +#define S3C2410_TCON_T0MANUALUPD (1<<1) +#define S3C2410_TCON_T0START (1<<0) + +#endif /* __ASM_ARCH_REGS_TIMER_H */ + + + diff --git a/arch/arm/plat-samsung/include/plat/regs-usb-hsotg-phy.h b/arch/arm/plat-samsung/include/plat/regs-usb-hsotg-phy.h new file mode 100644 index 0000000..36a85f5 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-usb-hsotg-phy.h @@ -0,0 +1,50 @@ +/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg-phy.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C - USB2.0 Highspeed/OtG device PHY registers + * + * 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. +*/ + +/* Note, this is a seperate header file as some of the clock framework + * needs to touch this if the clk_48m is used as the USB OHCI or other + * peripheral source. +*/ + +#ifndef __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H +#define __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H __FILE__ + +/* S3C64XX_PA_USB_HSPHY */ + +#define S3C_HSOTG_PHYREG(x) ((x) + S3C_VA_USB_HSPHY) + +#define S3C_PHYPWR S3C_HSOTG_PHYREG(0x00) +#define SRC_PHYPWR_OTG_DISABLE (1 << 4) +#define SRC_PHYPWR_ANALOG_POWERDOWN (1 << 3) +#define SRC_PHYPWR_FORCE_SUSPEND (1 << 1) + +#define S3C_PHYCLK S3C_HSOTG_PHYREG(0x04) +#define S3C_PHYCLK_MODE_USB11 (1 << 6) +#define S3C_PHYCLK_EXT_OSC (1 << 5) +#define S3C_PHYCLK_CLK_FORCE (1 << 4) +#define S3C_PHYCLK_ID_PULL (1 << 2) +#define S3C_PHYCLK_CLKSEL_MASK (0x3 << 0) +#define S3C_PHYCLK_CLKSEL_SHIFT (0) +#define S3C_PHYCLK_CLKSEL_48M (0x0 << 0) +#define S3C_PHYCLK_CLKSEL_12M (0x2 << 0) +#define S3C_PHYCLK_CLKSEL_24M (0x3 << 0) + +#define S3C_RSTCON S3C_HSOTG_PHYREG(0x08) +#define S3C_RSTCON_PHYCLK (1 << 2) +#define S3C_RSTCON_HCLK (1 << 2) +#define S3C_RSTCON_PHY (1 << 0) + +#define S3C_PHYTUNE S3C_HSOTG_PHYREG(0x20) + +#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_PHY_H */ diff --git a/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h b/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h new file mode 100644 index 0000000..8d18d9d --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h @@ -0,0 +1,377 @@ +/* arch/arm/plat-s3c/include/plat/regs-usb-hsotg.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C - USB2.0 Highspeed/OtG device block registers + * + * 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_S3C64XX_REGS_USB_HSOTG_H +#define __PLAT_S3C64XX_REGS_USB_HSOTG_H __FILE__ + +#define S3C_HSOTG_REG(x) (x) + +#define S3C_GOTGCTL S3C_HSOTG_REG(0x000) +#define S3C_GOTGCTL_BSESVLD (1 << 19) +#define S3C_GOTGCTL_ASESVLD (1 << 18) +#define S3C_GOTGCTL_DBNC_SHORT (1 << 17) +#define S3C_GOTGCTL_CONID_B (1 << 16) +#define S3C_GOTGCTL_DEVHNPEN (1 << 11) +#define S3C_GOTGCTL_HSSETHNPEN (1 << 10) +#define S3C_GOTGCTL_HNPREQ (1 << 9) +#define S3C_GOTGCTL_HSTNEGSCS (1 << 8) +#define S3C_GOTGCTL_SESREQ (1 << 1) +#define S3C_GOTGCTL_SESREQSCS (1 << 0) + +#define S3C_GOTGINT S3C_HSOTG_REG(0x004) +#define S3C_GOTGINT_DbnceDone (1 << 19) +#define S3C_GOTGINT_ADevTOUTChg (1 << 18) +#define S3C_GOTGINT_HstNegDet (1 << 17) +#define S3C_GOTGINT_HstnegSucStsChng (1 << 9) +#define S3C_GOTGINT_SesReqSucStsChng (1 << 8) +#define S3C_GOTGINT_SesEndDet (1 << 2) + +#define S3C_GAHBCFG S3C_HSOTG_REG(0x008) +#define S3C_GAHBCFG_PTxFEmpLvl (1 << 8) +#define S3C_GAHBCFG_NPTxFEmpLvl (1 << 7) +#define S3C_GAHBCFG_DMAEn (1 << 5) +#define S3C_GAHBCFG_HBstLen_MASK (0xf << 1) +#define S3C_GAHBCFG_HBstLen_SHIFT (1) +#define S3C_GAHBCFG_HBstLen_Single (0x0 << 1) +#define S3C_GAHBCFG_HBstLen_Incr (0x1 << 1) +#define S3C_GAHBCFG_HBstLen_Incr4 (0x3 << 1) +#define S3C_GAHBCFG_HBstLen_Incr8 (0x5 << 1) +#define S3C_GAHBCFG_HBstLen_Incr16 (0x7 << 1) +#define S3C_GAHBCFG_GlblIntrEn (1 << 0) + +#define S3C_GUSBCFG S3C_HSOTG_REG(0x00C) +#define S3C_GUSBCFG_PHYLPClkSel (1 << 15) +#define S3C_GUSBCFG_HNPCap (1 << 9) +#define S3C_GUSBCFG_SRPCap (1 << 8) +#define S3C_GUSBCFG_PHYIf16 (1 << 3) +#define S3C_GUSBCFG_TOutCal_MASK (0x7 << 0) +#define S3C_GUSBCFG_TOutCal_SHIFT (0) +#define S3C_GUSBCFG_TOutCal_LIMIT (0x7) +#define S3C_GUSBCFG_TOutCal(_x) ((_x) << 0) + +#define S3C_GRSTCTL S3C_HSOTG_REG(0x010) + +#define S3C_GRSTCTL_AHBIdle (1 << 31) +#define S3C_GRSTCTL_DMAReq (1 << 30) +#define S3C_GRSTCTL_TxFNum_MASK (0x1f << 6) +#define S3C_GRSTCTL_TxFNum_SHIFT (6) +#define S3C_GRSTCTL_TxFNum_LIMIT (0x1f) +#define S3C_GRSTCTL_TxFNum(_x) ((_x) << 6) +#define S3C_GRSTCTL_TxFFlsh (1 << 5) +#define S3C_GRSTCTL_RxFFlsh (1 << 4) +#define S3C_GRSTCTL_INTknQFlsh (1 << 3) +#define S3C_GRSTCTL_FrmCntrRst (1 << 2) +#define S3C_GRSTCTL_HSftRst (1 << 1) +#define S3C_GRSTCTL_CSftRst (1 << 0) + +#define S3C_GINTSTS S3C_HSOTG_REG(0x014) +#define S3C_GINTMSK S3C_HSOTG_REG(0x018) + +#define S3C_GINTSTS_WkUpInt (1 << 31) +#define S3C_GINTSTS_SessReqInt (1 << 30) +#define S3C_GINTSTS_DisconnInt (1 << 29) +#define S3C_GINTSTS_ConIDStsChng (1 << 28) +#define S3C_GINTSTS_PTxFEmp (1 << 26) +#define S3C_GINTSTS_HChInt (1 << 25) +#define S3C_GINTSTS_PrtInt (1 << 24) +#define S3C_GINTSTS_FetSusp (1 << 22) +#define S3C_GINTSTS_incompIP (1 << 21) +#define S3C_GINTSTS_IncomplSOIN (1 << 20) +#define S3C_GINTSTS_OEPInt (1 << 19) +#define S3C_GINTSTS_IEPInt (1 << 18) +#define S3C_GINTSTS_EPMis (1 << 17) +#define S3C_GINTSTS_EOPF (1 << 15) +#define S3C_GINTSTS_ISOutDrop (1 << 14) +#define S3C_GINTSTS_EnumDone (1 << 13) +#define S3C_GINTSTS_USBRst (1 << 12) +#define S3C_GINTSTS_USBSusp (1 << 11) +#define S3C_GINTSTS_ErlySusp (1 << 10) +#define S3C_GINTSTS_GOUTNakEff (1 << 7) +#define S3C_GINTSTS_GINNakEff (1 << 6) +#define S3C_GINTSTS_NPTxFEmp (1 << 5) +#define S3C_GINTSTS_RxFLvl (1 << 4) +#define S3C_GINTSTS_SOF (1 << 3) +#define S3C_GINTSTS_OTGInt (1 << 2) +#define S3C_GINTSTS_ModeMis (1 << 1) +#define S3C_GINTSTS_CurMod_Host (1 << 0) + +#define S3C_GRXSTSR S3C_HSOTG_REG(0x01C) +#define S3C_GRXSTSP S3C_HSOTG_REG(0x020) + +#define S3C_GRXSTS_FN_MASK (0x7f << 25) +#define S3C_GRXSTS_FN_SHIFT (25) + +#define S3C_GRXSTS_PktSts_MASK (0xf << 17) +#define S3C_GRXSTS_PktSts_SHIFT (17) +#define S3C_GRXSTS_PktSts_GlobalOutNAK (0x1 << 17) +#define S3C_GRXSTS_PktSts_OutRX (0x2 << 17) +#define S3C_GRXSTS_PktSts_OutDone (0x3 << 17) +#define S3C_GRXSTS_PktSts_SetupDone (0x4 << 17) +#define S3C_GRXSTS_PktSts_SetupRX (0x6 << 17) + +#define S3C_GRXSTS_DPID_MASK (0x3 << 15) +#define S3C_GRXSTS_DPID_SHIFT (15) +#define S3C_GRXSTS_ByteCnt_MASK (0x7ff << 4) +#define S3C_GRXSTS_ByteCnt_SHIFT (4) +#define S3C_GRXSTS_EPNum_MASK (0xf << 0) +#define S3C_GRXSTS_EPNum_SHIFT (0) + +#define S3C_GRXFSIZ S3C_HSOTG_REG(0x024) + +#define S3C_GNPTXFSIZ S3C_HSOTG_REG(0x028) + +#define S3C_GNPTXFSIZ_NPTxFDep_MASK (0xffff << 16) +#define S3C_GNPTXFSIZ_NPTxFDep_SHIFT (16) +#define S3C_GNPTXFSIZ_NPTxFDep_LIMIT (0xffff) +#define S3C_GNPTXFSIZ_NPTxFDep(_x) ((_x) << 16) +#define S3C_GNPTXFSIZ_NPTxFStAddr_MASK (0xffff << 0) +#define S3C_GNPTXFSIZ_NPTxFStAddr_SHIFT (0) +#define S3C_GNPTXFSIZ_NPTxFStAddr_LIMIT (0xffff) +#define S3C_GNPTXFSIZ_NPTxFStAddr(_x) ((_x) << 0) + +#define S3C_GNPTXSTS S3C_HSOTG_REG(0x02C) + +#define S3C_GNPTXSTS_NPtxQTop_MASK (0x7f << 24) +#define S3C_GNPTXSTS_NPtxQTop_SHIFT (24) + +#define S3C_GNPTXSTS_NPTxQSpcAvail_MASK (0xff << 16) +#define S3C_GNPTXSTS_NPTxQSpcAvail_SHIFT (16) +#define S3C_GNPTXSTS_NPTxQSpcAvail_GET(_v) (((_v) >> 16) & 0xff) + +#define S3C_GNPTXSTS_NPTxFSpcAvail_MASK (0xffff << 0) +#define S3C_GNPTXSTS_NPTxFSpcAvail_SHIFT (0) +#define S3C_GNPTXSTS_NPTxFSpcAvail_GET(_v) (((_v) >> 0) & 0xffff) + + +#define S3C_HPTXFSIZ S3C_HSOTG_REG(0x100) + +#define S3C_DPTXFSIZn(_a) S3C_HSOTG_REG(0x104 + (((_a) - 1) * 4)) + +#define S3C_DPTXFSIZn_DPTxFSize_MASK (0xffff << 16) +#define S3C_DPTXFSIZn_DPTxFSize_SHIFT (16) +#define S3C_DPTXFSIZn_DPTxFSize_GET(_v) (((_v) >> 16) & 0xffff) +#define S3C_DPTXFSIZn_DPTxFSize_LIMIT (0xffff) +#define S3C_DPTXFSIZn_DPTxFSize(_x) ((_x) << 16) + +#define S3C_DPTXFSIZn_DPTxFStAddr_MASK (0xffff << 0) +#define S3C_DPTXFSIZn_DPTxFStAddr_SHIFT (0) + +/* Device mode registers */ +#define S3C_DCFG S3C_HSOTG_REG(0x800) + +#define S3C_DCFG_EPMisCnt_MASK (0x1f << 18) +#define S3C_DCFG_EPMisCnt_SHIFT (18) +#define S3C_DCFG_EPMisCnt_LIMIT (0x1f) +#define S3C_DCFG_EPMisCnt(_x) ((_x) << 18) + +#define S3C_DCFG_PerFrInt_MASK (0x3 << 11) +#define S3C_DCFG_PerFrInt_SHIFT (11) +#define S3C_DCFG_PerFrInt_LIMIT (0x3) +#define S3C_DCFG_PerFrInt(_x) ((_x) << 11) + +#define S3C_DCFG_DevAddr_MASK (0x7f << 4) +#define S3C_DCFG_DevAddr_SHIFT (4) +#define S3C_DCFG_DevAddr_LIMIT (0x7f) +#define S3C_DCFG_DevAddr(_x) ((_x) << 4) + +#define S3C_DCFG_NZStsOUTHShk (1 << 2) + +#define S3C_DCFG_DevSpd_MASK (0x3 << 0) +#define S3C_DCFG_DevSpd_SHIFT (0) +#define S3C_DCFG_DevSpd_HS (0x0 << 0) +#define S3C_DCFG_DevSpd_FS (0x1 << 0) +#define S3C_DCFG_DevSpd_LS (0x2 << 0) +#define S3C_DCFG_DevSpd_FS48 (0x3 << 0) + +#define S3C_DCTL S3C_HSOTG_REG(0x804) + +#define S3C_DCTL_PWROnPrgDone (1 << 11) +#define S3C_DCTL_CGOUTNak (1 << 10) +#define S3C_DCTL_SGOUTNak (1 << 9) +#define S3C_DCTL_CGNPInNAK (1 << 8) +#define S3C_DCTL_SGNPInNAK (1 << 7) +#define S3C_DCTL_TstCtl_MASK (0x7 << 4) +#define S3C_DCTL_TstCtl_SHIFT (4) +#define S3C_DCTL_GOUTNakSts (1 << 3) +#define S3C_DCTL_GNPINNakSts (1 << 2) +#define S3C_DCTL_SftDiscon (1 << 1) +#define S3C_DCTL_RmtWkUpSig (1 << 0) + +#define S3C_DSTS S3C_HSOTG_REG(0x808) + +#define S3C_DSTS_SOFFN_MASK (0x3fff << 8) +#define S3C_DSTS_SOFFN_SHIFT (8) +#define S3C_DSTS_SOFFN_LIMIT (0x3fff) +#define S3C_DSTS_SOFFN(_x) ((_x) << 8) +#define S3C_DSTS_ErraticErr (1 << 3) +#define S3C_DSTS_EnumSpd_MASK (0x3 << 1) +#define S3C_DSTS_EnumSpd_SHIFT (1) +#define S3C_DSTS_EnumSpd_HS (0x0 << 1) +#define S3C_DSTS_EnumSpd_FS (0x1 << 1) +#define S3C_DSTS_EnumSpd_LS (0x2 << 1) +#define S3C_DSTS_EnumSpd_FS48 (0x3 << 1) + +#define S3C_DSTS_SuspSts (1 << 0) + +#define S3C_DIEPMSK S3C_HSOTG_REG(0x810) + +#define S3C_DIEPMSK_INEPNakEffMsk (1 << 6) +#define S3C_DIEPMSK_INTknEPMisMsk (1 << 5) +#define S3C_DIEPMSK_INTknTXFEmpMsk (1 << 4) +#define S3C_DIEPMSK_TimeOUTMsk (1 << 3) +#define S3C_DIEPMSK_AHBErrMsk (1 << 2) +#define S3C_DIEPMSK_EPDisbldMsk (1 << 1) +#define S3C_DIEPMSK_XferComplMsk (1 << 0) + +#define S3C_DOEPMSK S3C_HSOTG_REG(0x814) + +#define S3C_DOEPMSK_Back2BackSetup (1 << 6) +#define S3C_DOEPMSK_OUTTknEPdisMsk (1 << 4) +#define S3C_DOEPMSK_SetupMsk (1 << 3) +#define S3C_DOEPMSK_AHBErrMsk (1 << 2) +#define S3C_DOEPMSK_EPDisbldMsk (1 << 1) +#define S3C_DOEPMSK_XferComplMsk (1 << 0) + +#define S3C_DAINT S3C_HSOTG_REG(0x818) +#define S3C_DAINTMSK S3C_HSOTG_REG(0x81C) + +#define S3C_DAINT_OutEP_SHIFT (16) +#define S3C_DAINT_OutEP(x) (1 << ((x) + 16)) +#define S3C_DAINT_InEP(x) (1 << (x)) + +#define S3C_DTKNQR1 S3C_HSOTG_REG(0x820) +#define S3C_DTKNQR2 S3C_HSOTG_REG(0x824) +#define S3C_DTKNQR3 S3C_HSOTG_REG(0x830) +#define S3C_DTKNQR4 S3C_HSOTG_REG(0x834) + +#define S3C_DVBUSDIS S3C_HSOTG_REG(0x828) +#define S3C_DVBUSPULSE S3C_HSOTG_REG(0x82C) + +#define S3C_DIEPCTL0 S3C_HSOTG_REG(0x900) +#define S3C_DOEPCTL0 S3C_HSOTG_REG(0xB00) +#define S3C_DIEPCTL(_a) S3C_HSOTG_REG(0x900 + ((_a) * 0x20)) +#define S3C_DOEPCTL(_a) S3C_HSOTG_REG(0xB00 + ((_a) * 0x20)) + +/* EP0 specialness: + * bits[29..28] - reserved (no SetD0PID, SetD1PID) + * bits[25..22] - should always be zero, this isn't a periodic endpoint + * bits[10..0] - MPS setting differenct for EP0 +*/ +#define S3C_D0EPCTL_MPS_MASK (0x3 << 0) +#define S3C_D0EPCTL_MPS_SHIFT (0) +#define S3C_D0EPCTL_MPS_64 (0x0 << 0) +#define S3C_D0EPCTL_MPS_32 (0x1 << 0) +#define S3C_D0EPCTL_MPS_16 (0x2 << 0) +#define S3C_D0EPCTL_MPS_8 (0x3 << 0) + +#define S3C_DxEPCTL_EPEna (1 << 31) +#define S3C_DxEPCTL_EPDis (1 << 30) +#define S3C_DxEPCTL_SetD1PID (1 << 29) +#define S3C_DxEPCTL_SetOddFr (1 << 29) +#define S3C_DxEPCTL_SetD0PID (1 << 28) +#define S3C_DxEPCTL_SetEvenFr (1 << 28) +#define S3C_DxEPCTL_SNAK (1 << 27) +#define S3C_DxEPCTL_CNAK (1 << 26) +#define S3C_DxEPCTL_TxFNum_MASK (0xf << 22) +#define S3C_DxEPCTL_TxFNum_SHIFT (22) +#define S3C_DxEPCTL_TxFNum_LIMIT (0xf) +#define S3C_DxEPCTL_TxFNum(_x) ((_x) << 22) + +#define S3C_DxEPCTL_Stall (1 << 21) +#define S3C_DxEPCTL_Snp (1 << 20) +#define S3C_DxEPCTL_EPType_MASK (0x3 << 18) +#define S3C_DxEPCTL_EPType_SHIFT (18) +#define S3C_DxEPCTL_EPType_Control (0x0 << 18) +#define S3C_DxEPCTL_EPType_Iso (0x1 << 18) +#define S3C_DxEPCTL_EPType_Bulk (0x2 << 18) +#define S3C_DxEPCTL_EPType_Intterupt (0x3 << 18) + +#define S3C_DxEPCTL_NAKsts (1 << 17) +#define S3C_DxEPCTL_DPID (1 << 16) +#define S3C_DxEPCTL_EOFrNum (1 << 16) +#define S3C_DxEPCTL_USBActEp (1 << 15) +#define S3C_DxEPCTL_NextEp_MASK (0xf << 11) +#define S3C_DxEPCTL_NextEp_SHIFT (11) +#define S3C_DxEPCTL_NextEp_LIMIT (0xf) +#define S3C_DxEPCTL_NextEp(_x) ((_x) << 11) + +#define S3C_DxEPCTL_MPS_MASK (0x7ff << 0) +#define S3C_DxEPCTL_MPS_SHIFT (0) +#define S3C_DxEPCTL_MPS_LIMIT (0x7ff) +#define S3C_DxEPCTL_MPS(_x) ((_x) << 0) + +#define S3C_DIEPINT(_a) S3C_HSOTG_REG(0x908 + ((_a) * 0x20)) +#define S3C_DOEPINT(_a) S3C_HSOTG_REG(0xB08 + ((_a) * 0x20)) + +#define S3C_DxEPINT_INEPNakEff (1 << 6) +#define S3C_DxEPINT_Back2BackSetup (1 << 6) +#define S3C_DxEPINT_INTknEPMis (1 << 5) +#define S3C_DxEPINT_INTknTXFEmp (1 << 4) +#define S3C_DxEPINT_OUTTknEPdis (1 << 4) +#define S3C_DxEPINT_Timeout (1 << 3) +#define S3C_DxEPINT_Setup (1 << 3) +#define S3C_DxEPINT_AHBErr (1 << 2) +#define S3C_DxEPINT_EPDisbld (1 << 1) +#define S3C_DxEPINT_XferCompl (1 << 0) + +#define S3C_DIEPTSIZ0 S3C_HSOTG_REG(0x910) + +#define S3C_DIEPTSIZ0_PktCnt_MASK (0x3 << 19) +#define S3C_DIEPTSIZ0_PktCnt_SHIFT (19) +#define S3C_DIEPTSIZ0_PktCnt_LIMIT (0x3) +#define S3C_DIEPTSIZ0_PktCnt(_x) ((_x) << 19) + +#define S3C_DIEPTSIZ0_XferSize_MASK (0x7f << 0) +#define S3C_DIEPTSIZ0_XferSize_SHIFT (0) +#define S3C_DIEPTSIZ0_XferSize_LIMIT (0x7f) +#define S3C_DIEPTSIZ0_XferSize(_x) ((_x) << 0) + + +#define DOEPTSIZ0 S3C_HSOTG_REG(0xB10) +#define S3C_DOEPTSIZ0_SUPCnt_MASK (0x3 << 29) +#define S3C_DOEPTSIZ0_SUPCnt_SHIFT (29) +#define S3C_DOEPTSIZ0_SUPCnt_LIMIT (0x3) +#define S3C_DOEPTSIZ0_SUPCnt(_x) ((_x) << 29) + +#define S3C_DOEPTSIZ0_PktCnt (1 << 19) +#define S3C_DOEPTSIZ0_XferSize_MASK (0x7f << 0) +#define S3C_DOEPTSIZ0_XferSize_SHIFT (0) + +#define S3C_DIEPTSIZ(_a) S3C_HSOTG_REG(0x910 + ((_a) * 0x20)) +#define S3C_DOEPTSIZ(_a) S3C_HSOTG_REG(0xB10 + ((_a) * 0x20)) + +#define S3C_DxEPTSIZ_MC_MASK (0x3 << 29) +#define S3C_DxEPTSIZ_MC_SHIFT (29) +#define S3C_DxEPTSIZ_MC_LIMIT (0x3) +#define S3C_DxEPTSIZ_MC(_x) ((_x) << 29) + +#define S3C_DxEPTSIZ_PktCnt_MASK (0x3ff << 19) +#define S3C_DxEPTSIZ_PktCnt_SHIFT (19) +#define S3C_DxEPTSIZ_PktCnt_GET(_v) (((_v) >> 19) & 0x3ff) +#define S3C_DxEPTSIZ_PktCnt_LIMIT (0x3ff) +#define S3C_DxEPTSIZ_PktCnt(_x) ((_x) << 19) + +#define S3C_DxEPTSIZ_XferSize_MASK (0x7ffff << 0) +#define S3C_DxEPTSIZ_XferSize_SHIFT (0) +#define S3C_DxEPTSIZ_XferSize_GET(_v) (((_v) >> 0) & 0x7ffff) +#define S3C_DxEPTSIZ_XferSize_LIMIT (0x7ffff) +#define S3C_DxEPTSIZ_XferSize(_x) ((_x) << 0) + + +#define S3C_DIEPDMA(_a) S3C_HSOTG_REG(0x914 + ((_a) * 0x20)) +#define S3C_DOEPDMA(_a) S3C_HSOTG_REG(0xB14 + ((_a) * 0x20)) + +#define S3C_EPFIFO(_a) S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000)) + +#endif /* __PLAT_S3C64XX_REGS_USB_HSOTG_H */ diff --git a/arch/arm/plat-samsung/include/plat/regs-watchdog.h b/arch/arm/plat-samsung/include/plat/regs-watchdog.h new file mode 100644 index 0000000..4938492 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/regs-watchdog.h @@ -0,0 +1,41 @@ +/* arch/arm/mach-s3c2410/include/mach/regs-watchdog.h + * + * Copyright (c) 2003 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * 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. + * + * S3C2410 Watchdog timer control +*/ + + +#ifndef __ASM_ARCH_REGS_WATCHDOG_H +#define __ASM_ARCH_REGS_WATCHDOG_H + +#define S3C_WDOGREG(x) ((x) + S3C_VA_WATCHDOG) + +#define S3C2410_WTCON S3C_WDOGREG(0x00) +#define S3C2410_WTDAT S3C_WDOGREG(0x04) +#define S3C2410_WTCNT S3C_WDOGREG(0x08) + +/* the watchdog can either generate a reset pulse, or an + * interrupt. + */ + +#define S3C2410_WTCON_RSTEN (0x01) +#define S3C2410_WTCON_INTEN (1<<2) +#define S3C2410_WTCON_ENABLE (1<<5) + +#define S3C2410_WTCON_DIV16 (0<<3) +#define S3C2410_WTCON_DIV32 (1<<3) +#define S3C2410_WTCON_DIV64 (2<<3) +#define S3C2410_WTCON_DIV128 (3<<3) + +#define S3C2410_WTCON_PRESCALE(x) ((x) << 8) +#define S3C2410_WTCON_PRESCALE_MASK (0xff00) + +#endif /* __ASM_ARCH_REGS_WATCHDOG_H */ + + diff --git a/arch/arm/plat-samsung/include/plat/sdhci.h b/arch/arm/plat-samsung/include/plat/sdhci.h new file mode 100644 index 0000000..5319867 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/sdhci.h @@ -0,0 +1,226 @@ +/* linux/arch/arm/plat-s3c/include/plat/sdhci.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C Platform - SDHCI (HSMMC) platform data definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __PLAT_S3C_SDHCI_H +#define __PLAT_S3C_SDHCI_H __FILE__ + +struct platform_device; +struct mmc_host; +struct mmc_card; +struct mmc_ios; + +/** + * struct s3c_sdhci_platdata() - Platform device data for Samsung SDHCI + * @max_width: The maximum number of data bits supported. + * @host_caps: Standard MMC host capabilities bit field. + * @cfg_gpio: Configure the GPIO for a specific card bit-width + * @cfg_card: Configure the interface for a specific card and speed. This + * is necessary the controllers and/or GPIO blocks require the + * changing of driver-strength and other controls dependant on + * the card and speed of operation. + * + * Initialisation data specific to either the machine or the platform + * for the device driver to use or call-back when configuring gpio or + * card speed information. +*/ +struct s3c_sdhci_platdata { + unsigned int max_width; + unsigned int host_caps; + + char **clocks; /* set of clock sources */ + + void (*cfg_gpio)(struct platform_device *dev, int width); + void (*cfg_card)(struct platform_device *dev, + void __iomem *regbase, + struct mmc_ios *ios, + struct mmc_card *card); +}; + +/** + * s3c_sdhci0_set_platdata - Set platform data for S3C SDHCI device. + * @pd: Platform data to register to device. + * + * Register the given platform data for use withe S3C SDHCI device. + * The call will copy the platform data, so the board definitions can + * make the structure itself __initdata. + */ +extern void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd); +extern void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd); +extern void s3c_sdhci2_set_platdata(struct s3c_sdhci_platdata *pd); + +/* Default platform data, exported so that per-cpu initialisation can + * set the correct one when there are more than one cpu type selected. +*/ + +extern struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata; +extern struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata; +extern struct s3c_sdhci_platdata s3c_hsmmc2_def_platdata; + +/* Helper function availablity */ + +extern void s3c64xx_setup_sdhci0_cfg_gpio(struct platform_device *, int w); +extern void s3c64xx_setup_sdhci1_cfg_gpio(struct platform_device *, int w); +extern void s5pc100_setup_sdhci0_cfg_gpio(struct platform_device *, int w); +extern void s5pc100_setup_sdhci1_cfg_gpio(struct platform_device *, int w); +extern void s5pc100_setup_sdhci2_cfg_gpio(struct platform_device *, int w); +extern void s3c64xx_setup_sdhci2_cfg_gpio(struct platform_device *, int w); + +/* S3C6400 SDHCI setup */ + +#ifdef CONFIG_S3C6400_SETUP_SDHCI +extern char *s3c6400_hsmmc_clksrcs[4]; + +#ifdef CONFIG_S3C_DEV_HSMMC +extern void s3c6400_setup_sdhci_cfg_card(struct platform_device *dev, + void __iomem *r, + struct mmc_ios *ios, + struct mmc_card *card); + +static inline void s3c6400_default_sdhci0(void) +{ + s3c_hsmmc0_def_platdata.clocks = s3c6400_hsmmc_clksrcs; + s3c_hsmmc0_def_platdata.cfg_gpio = s3c64xx_setup_sdhci0_cfg_gpio; + s3c_hsmmc0_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card; +} + +#else +static inline void s3c6400_default_sdhci0(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC */ + +#ifdef CONFIG_S3C_DEV_HSMMC1 +static inline void s3c6400_default_sdhci1(void) +{ + s3c_hsmmc1_def_platdata.clocks = s3c6400_hsmmc_clksrcs; + s3c_hsmmc1_def_platdata.cfg_gpio = s3c64xx_setup_sdhci1_cfg_gpio; + s3c_hsmmc1_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card; +} +#else +static inline void s3c6400_default_sdhci1(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC1 */ + +#ifdef CONFIG_S3C_DEV_HSMMC2 +static inline void s3c6400_default_sdhci2(void) +{ + s3c_hsmmc2_def_platdata.clocks = s3c6400_hsmmc_clksrcs; + s3c_hsmmc2_def_platdata.cfg_gpio = s3c64xx_setup_sdhci2_cfg_gpio; + s3c_hsmmc2_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card; +} +#else +static inline void s3c6400_default_sdhci2(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC2 */ + +#else +static inline void s3c6400_default_sdhci0(void) { } +static inline void s3c6400_default_sdhci1(void) { } +#endif /* CONFIG_S3C6400_SETUP_SDHCI */ + +/* S3C6410 SDHCI setup */ + +#ifdef CONFIG_S3C6410_SETUP_SDHCI +extern char *s3c6410_hsmmc_clksrcs[4]; + +extern void s3c6410_setup_sdhci0_cfg_card(struct platform_device *dev, + void __iomem *r, + struct mmc_ios *ios, + struct mmc_card *card); + +#ifdef CONFIG_S3C_DEV_HSMMC +static inline void s3c6410_default_sdhci0(void) +{ + s3c_hsmmc0_def_platdata.clocks = s3c6410_hsmmc_clksrcs; + s3c_hsmmc0_def_platdata.cfg_gpio = s3c64xx_setup_sdhci0_cfg_gpio; + s3c_hsmmc0_def_platdata.cfg_card = s3c6410_setup_sdhci0_cfg_card; +} +#else +static inline void s3c6410_default_sdhci0(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC */ + +#ifdef CONFIG_S3C_DEV_HSMMC1 +static inline void s3c6410_default_sdhci1(void) +{ + s3c_hsmmc1_def_platdata.clocks = s3c6410_hsmmc_clksrcs; + s3c_hsmmc1_def_platdata.cfg_gpio = s3c64xx_setup_sdhci1_cfg_gpio; + s3c_hsmmc1_def_platdata.cfg_card = s3c6410_setup_sdhci0_cfg_card; +} +#else +static inline void s3c6410_default_sdhci1(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC1 */ + +#ifdef CONFIG_S3C_DEV_HSMMC2 +static inline void s3c6410_default_sdhci2(void) +{ + s3c_hsmmc2_def_platdata.clocks = s3c6410_hsmmc_clksrcs; + s3c_hsmmc2_def_platdata.cfg_gpio = s3c64xx_setup_sdhci2_cfg_gpio; + s3c_hsmmc2_def_platdata.cfg_card = s3c6410_setup_sdhci0_cfg_card; +} +#else +static inline void s3c6410_default_sdhci2(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC2 */ + +#else +static inline void s3c6410_default_sdhci0(void) { } +static inline void s3c6410_default_sdhci1(void) { } +#endif /* CONFIG_S3C6410_SETUP_SDHCI */ + +/* S5PC100 SDHCI setup */ + +#ifdef CONFIG_S5PC100_SETUP_SDHCI +extern char *s5pc100_hsmmc_clksrcs[4]; + +extern void s5pc100_setup_sdhci0_cfg_card(struct platform_device *dev, + void __iomem *r, + struct mmc_ios *ios, + struct mmc_card *card); + +#ifdef CONFIG_S3C_DEV_HSMMC +static inline void s5pc100_default_sdhci0(void) +{ + s3c_hsmmc0_def_platdata.clocks = s5pc100_hsmmc_clksrcs; + s3c_hsmmc0_def_platdata.cfg_gpio = s5pc100_setup_sdhci0_cfg_gpio; + s3c_hsmmc0_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card; +} +#else +static inline void s5pc100_default_sdhci0(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC */ + +#ifdef CONFIG_S3C_DEV_HSMMC1 +static inline void s5pc100_default_sdhci1(void) +{ + s3c_hsmmc1_def_platdata.clocks = s5pc100_hsmmc_clksrcs; + s3c_hsmmc1_def_platdata.cfg_gpio = s5pc100_setup_sdhci1_cfg_gpio; + s3c_hsmmc1_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card; +} +#else +static inline void s5pc100_default_sdhci1(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC1 */ + +#ifdef CONFIG_S3C_DEV_HSMMC2 +static inline void s5pc100_default_sdhci2(void) +{ + s3c_hsmmc2_def_platdata.clocks = s5pc100_hsmmc_clksrcs; + s3c_hsmmc2_def_platdata.cfg_gpio = s5pc100_setup_sdhci2_cfg_gpio; + s3c_hsmmc2_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card; +} +#else +static inline void s5pc100_default_sdhci2(void) { } +#endif /* CONFIG_S3C_DEV_HSMMC1 */ + + +#else +static inline void s5pc100_default_sdhci0(void) { } +static inline void s5pc100_default_sdhci1(void) { } +static inline void s5pc100_default_sdhci2(void) { } +#endif /* CONFIG_S5PC100_SETUP_SDHCI */ + +#endif /* __PLAT_S3C_SDHCI_H */ diff --git a/arch/arm/plat-samsung/include/plat/udc-hs.h b/arch/arm/plat-samsung/include/plat/udc-hs.h new file mode 100644 index 0000000..dd04db0 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/udc-hs.h @@ -0,0 +1,29 @@ +/* arch/arm/plat-s3c/include/plat/udc-hs.h + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C USB2.0 High-speed / OtG platform information + * + * 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. +*/ + +enum s3c_hostg_dmamode { + S3C_HSOTG_DMA_NONE, /* do not use DMA at-all */ + S3C_HSOTG_DMA_ONLY, /* always use DMA */ + S3C_HSOTG_DMA_DRV, /* DMA is chosen by driver */ +}; + +/** + * struct s3c_hsotg_plat - platform data for high-speed otg/udc + * @dma: Whether to use DMA or not. + * @is_osc: The clock source is an oscillator, not a crystal + */ +struct s3c_hsotg_plat { + enum s3c_hostg_dmamode dma; + unsigned int is_osc : 1; +}; diff --git a/arch/arm/plat-samsung/include/plat/watchdog-reset.h b/arch/arm/plat-samsung/include/plat/watchdog-reset.h new file mode 100644 index 0000000..54b762a --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/watchdog-reset.h @@ -0,0 +1,49 @@ +/* arch/arm/plat-s3c/include/plat/watchdog-reset.h + * + * Copyright (c) 2008 Simtec Electronics + * Ben Dooks + * + * S3C2410 - System define for arch_reset() function + * + * 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 + +static inline void arch_wdt_reset(void) +{ + struct clk *wdtclk; + + printk("arch_reset: attempting watchdog reset\n"); + + __raw_writel(0, S3C2410_WTCON); /* disable watchdog, to be safe */ + + wdtclk = clk_get(NULL, "watchdog"); + if (!IS_ERR(wdtclk)) { + clk_enable(wdtclk); + } else + printk(KERN_WARNING "%s: warning: cannot get watchdog clock\n", __func__); + + /* put initial values into count and data */ + __raw_writel(0x80, S3C2410_WTCNT); + __raw_writel(0x80, S3C2410_WTDAT); + + /* set the watchdog to go and reset... */ + __raw_writel(S3C2410_WTCON_ENABLE|S3C2410_WTCON_DIV16|S3C2410_WTCON_RSTEN | + S3C2410_WTCON_PRESCALE(0x20), S3C2410_WTCON); + + /* wait for reset to assert... */ + mdelay(500); + + printk(KERN_ERR "Watchdog reset failed to assert reset\n"); + + /* delay to allow the serial port to show the message */ + mdelay(50); +} -- cgit v0.10.2 From 106cc6aa73f594015fc8b99f7c70d1b7cf23f19a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 20 Jan 2010 12:22:24 +0900 Subject: ARM: SAMSUNG: Move pmw.c to plat-samsung Move the PWM driver support to plat-samsung, as these PWM blocks are close enough across the current range to be supported by this driver. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 20cb335..095bc97 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -22,7 +22,3 @@ obj-$(CONFIG_S3C_DMA) += dma.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o - -# PWM support - -obj-$(CONFIG_HAVE_PWM) += pwm.o diff --git a/arch/arm/plat-s3c/pwm.c b/arch/arm/plat-s3c/pwm.c deleted file mode 100644 index ef019f2..0000000 --- a/arch/arm/plat-s3c/pwm.c +++ /dev/null @@ -1,410 +0,0 @@ -/* arch/arm/plat-s3c/pwm.c - * - * Copyright (c) 2007 Ben Dooks - * Copyright (c) 2008 Simtec Electronics - * Ben Dooks , - * - * S3C series PWM device core - * - * 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. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -struct pwm_device { - struct list_head list; - struct platform_device *pdev; - - struct clk *clk_div; - struct clk *clk; - const char *label; - - unsigned int period_ns; - unsigned int duty_ns; - - unsigned char tcon_base; - unsigned char running; - unsigned char use_count; - unsigned char pwm_id; -}; - -#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) - -static struct clk *clk_scaler[2]; - -/* Standard setup for a timer block. */ - -#define TIMER_RESOURCE_SIZE (1) - -#define TIMER_RESOURCE(_tmr, _irq) \ - (struct resource [TIMER_RESOURCE_SIZE]) { \ - [0] = { \ - .start = _irq, \ - .end = _irq, \ - .flags = IORESOURCE_IRQ \ - } \ - } - -#define DEFINE_S3C_TIMER(_tmr_no, _irq) \ - .name = "s3c24xx-pwm", \ - .id = _tmr_no, \ - .num_resources = TIMER_RESOURCE_SIZE, \ - .resource = TIMER_RESOURCE(_tmr_no, _irq), \ - -/* since we already have an static mapping for the timer, we do not - * bother setting any IO resource for the base. - */ - -struct platform_device s3c_device_timer[] = { - [0] = { DEFINE_S3C_TIMER(0, IRQ_TIMER0) }, - [1] = { DEFINE_S3C_TIMER(1, IRQ_TIMER1) }, - [2] = { DEFINE_S3C_TIMER(2, IRQ_TIMER2) }, - [3] = { DEFINE_S3C_TIMER(3, IRQ_TIMER3) }, - [4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) }, -}; - -static inline int pwm_is_tdiv(struct pwm_device *pwm) -{ - return clk_get_parent(pwm->clk) == pwm->clk_div; -} - -static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_list); - -struct pwm_device *pwm_request(int pwm_id, const char *label) -{ - struct pwm_device *pwm; - int found = 0; - - mutex_lock(&pwm_lock); - - list_for_each_entry(pwm, &pwm_list, list) { - if (pwm->pwm_id == pwm_id) { - found = 1; - break; - } - } - - if (found) { - if (pwm->use_count == 0) { - pwm->use_count = 1; - pwm->label = label; - } else - pwm = ERR_PTR(-EBUSY); - } else - pwm = ERR_PTR(-ENOENT); - - mutex_unlock(&pwm_lock); - return pwm; -} - -EXPORT_SYMBOL(pwm_request); - - -void pwm_free(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - - if (pwm->use_count) { - pwm->use_count--; - pwm->label = NULL; - } else - printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id); - - mutex_unlock(&pwm_lock); -} - -EXPORT_SYMBOL(pwm_free); - -#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0)) -#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2)) -#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3)) -#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1)) - -int pwm_enable(struct pwm_device *pwm) -{ - unsigned long flags; - unsigned long tcon; - - local_irq_save(flags); - - tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_start(pwm); - __raw_writel(tcon, S3C2410_TCON); - - local_irq_restore(flags); - - pwm->running = 1; - return 0; -} - -EXPORT_SYMBOL(pwm_enable); - -void pwm_disable(struct pwm_device *pwm) -{ - unsigned long flags; - unsigned long tcon; - - local_irq_save(flags); - - tcon = __raw_readl(S3C2410_TCON); - tcon &= ~pwm_tcon_start(pwm); - __raw_writel(tcon, S3C2410_TCON); - - local_irq_restore(flags); - - pwm->running = 0; -} - -EXPORT_SYMBOL(pwm_disable); - -static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq) -{ - unsigned long tin_parent_rate; - unsigned int div; - - tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div)); - pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate); - - for (div = 2; div <= 16; div *= 2) { - if ((tin_parent_rate / (div << 16)) < freq) - return tin_parent_rate / div; - } - - return tin_parent_rate / 16; -} - -#define NS_IN_HZ (1000000000UL) - -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) -{ - unsigned long tin_rate; - unsigned long tin_ns; - unsigned long period; - unsigned long flags; - unsigned long tcon; - unsigned long tcnt; - long tcmp; - - /* We currently avoid using 64bit arithmetic by using the - * fact that anything faster than 1Hz is easily representable - * by 32bits. */ - - if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ) - return -ERANGE; - - if (duty_ns > period_ns) - return -EINVAL; - - if (period_ns == pwm->period_ns && - duty_ns == pwm->duty_ns) - return 0; - - /* The TCMP and TCNT can be read without a lock, they're not - * shared between the timers. */ - - tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id)); - tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id)); - - period = NS_IN_HZ / period_ns; - - pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n", - duty_ns, period_ns, period); - - /* Check to see if we are changing the clock rate of the PWM */ - - if (pwm->period_ns != period_ns) { - if (pwm_is_tdiv(pwm)) { - tin_rate = pwm_calc_tin(pwm, period); - clk_set_rate(pwm->clk_div, tin_rate); - } else - tin_rate = clk_get_rate(pwm->clk); - - pwm->period_ns = period_ns; - - pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate); - - tin_ns = NS_IN_HZ / tin_rate; - tcnt = period_ns / tin_ns; - } else - tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk); - - /* Note, counters count down */ - - tcmp = duty_ns / tin_ns; - tcmp = tcnt - tcmp; - /* the pwm hw only checks the compare register after a decrement, - so the pin never toggles if tcmp = tcnt */ - if (tcmp == tcnt) - tcmp--; - - pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); - - if (tcmp < 0) - tcmp = 0; - - /* Update the PWM register block. */ - - local_irq_save(flags); - - __raw_writel(tcmp, S3C2410_TCMPB(pwm->pwm_id)); - __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id)); - - tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_manulupdate(pwm); - tcon |= pwm_tcon_autoreload(pwm); - __raw_writel(tcon, S3C2410_TCON); - - tcon &= ~pwm_tcon_manulupdate(pwm); - __raw_writel(tcon, S3C2410_TCON); - - local_irq_restore(flags); - - return 0; -} - -EXPORT_SYMBOL(pwm_config); - -static int pwm_register(struct pwm_device *pwm) -{ - pwm->duty_ns = -1; - pwm->period_ns = -1; - - mutex_lock(&pwm_lock); - list_add_tail(&pwm->list, &pwm_list); - mutex_unlock(&pwm_lock); - - return 0; -} - -static int s3c_pwm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct pwm_device *pwm; - unsigned long flags; - unsigned long tcon; - unsigned int id = pdev->id; - int ret; - - if (id == 4) { - dev_err(dev, "TIMER4 is currently not supported\n"); - return -ENXIO; - } - - pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); - if (pwm == NULL) { - dev_err(dev, "failed to allocate pwm_device\n"); - return -ENOMEM; - } - - pwm->pdev = pdev; - pwm->pwm_id = id; - - /* calculate base of control bits in TCON */ - pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4; - - pwm->clk = clk_get(dev, "pwm-tin"); - if (IS_ERR(pwm->clk)) { - dev_err(dev, "failed to get pwm tin clk\n"); - ret = PTR_ERR(pwm->clk); - goto err_alloc; - } - - pwm->clk_div = clk_get(dev, "pwm-tdiv"); - if (IS_ERR(pwm->clk_div)) { - dev_err(dev, "failed to get pwm tdiv clk\n"); - ret = PTR_ERR(pwm->clk_div); - goto err_clk_tin; - } - - local_irq_save(flags); - - tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_invert(pwm); - __raw_writel(tcon, S3C2410_TCON); - - local_irq_restore(flags); - - - ret = pwm_register(pwm); - if (ret) { - dev_err(dev, "failed to register pwm\n"); - goto err_clk_tdiv; - } - - pwm_dbg(pwm, "config bits %02x\n", - (__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f); - - dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n", - clk_get_rate(pwm->clk), - clk_get_rate(pwm->clk_div), - pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base); - - platform_set_drvdata(pdev, pwm); - return 0; - - err_clk_tdiv: - clk_put(pwm->clk_div); - - err_clk_tin: - clk_put(pwm->clk); - - err_alloc: - kfree(pwm); - return ret; -} - -static int __devexit s3c_pwm_remove(struct platform_device *pdev) -{ - struct pwm_device *pwm = platform_get_drvdata(pdev); - - clk_put(pwm->clk_div); - clk_put(pwm->clk); - kfree(pwm); - - return 0; -} - -static struct platform_driver s3c_pwm_driver = { - .driver = { - .name = "s3c24xx-pwm", - .owner = THIS_MODULE, - }, - .probe = s3c_pwm_probe, - .remove = __devexit_p(s3c_pwm_remove), -}; - -static int __init pwm_init(void) -{ - int ret; - - clk_scaler[0] = clk_get(NULL, "pwm-scaler0"); - clk_scaler[1] = clk_get(NULL, "pwm-scaler1"); - - if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) { - printk(KERN_ERR "%s: failed to get scaler clocks\n", __func__); - return -EINVAL; - } - - ret = platform_driver_register(&s3c_pwm_driver); - if (ret) - printk(KERN_ERR "%s: failed to add pwm driver\n", __func__); - - return ret; -} - -arch_initcall(pwm_init); diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 27d5bfb..f31e64f 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -42,3 +42,7 @@ obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o # PM support obj-$(CONFIG_PM) += pm-gpio.o + +# PWM support + +obj-$(CONFIG_HAVE_PWM) += pwm.o diff --git a/arch/arm/plat-samsung/pwm.c b/arch/arm/plat-samsung/pwm.c new file mode 100644 index 0000000..ef019f2 --- /dev/null +++ b/arch/arm/plat-samsung/pwm.c @@ -0,0 +1,410 @@ +/* arch/arm/plat-s3c/pwm.c + * + * Copyright (c) 2007 Ben Dooks + * Copyright (c) 2008 Simtec Electronics + * Ben Dooks , + * + * S3C series PWM device core + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +struct pwm_device { + struct list_head list; + struct platform_device *pdev; + + struct clk *clk_div; + struct clk *clk; + const char *label; + + unsigned int period_ns; + unsigned int duty_ns; + + unsigned char tcon_base; + unsigned char running; + unsigned char use_count; + unsigned char pwm_id; +}; + +#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) + +static struct clk *clk_scaler[2]; + +/* Standard setup for a timer block. */ + +#define TIMER_RESOURCE_SIZE (1) + +#define TIMER_RESOURCE(_tmr, _irq) \ + (struct resource [TIMER_RESOURCE_SIZE]) { \ + [0] = { \ + .start = _irq, \ + .end = _irq, \ + .flags = IORESOURCE_IRQ \ + } \ + } + +#define DEFINE_S3C_TIMER(_tmr_no, _irq) \ + .name = "s3c24xx-pwm", \ + .id = _tmr_no, \ + .num_resources = TIMER_RESOURCE_SIZE, \ + .resource = TIMER_RESOURCE(_tmr_no, _irq), \ + +/* since we already have an static mapping for the timer, we do not + * bother setting any IO resource for the base. + */ + +struct platform_device s3c_device_timer[] = { + [0] = { DEFINE_S3C_TIMER(0, IRQ_TIMER0) }, + [1] = { DEFINE_S3C_TIMER(1, IRQ_TIMER1) }, + [2] = { DEFINE_S3C_TIMER(2, IRQ_TIMER2) }, + [3] = { DEFINE_S3C_TIMER(3, IRQ_TIMER3) }, + [4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) }, +}; + +static inline int pwm_is_tdiv(struct pwm_device *pwm) +{ + return clk_get_parent(pwm->clk) == pwm->clk_div; +} + +static DEFINE_MUTEX(pwm_lock); +static LIST_HEAD(pwm_list); + +struct pwm_device *pwm_request(int pwm_id, const char *label) +{ + struct pwm_device *pwm; + int found = 0; + + mutex_lock(&pwm_lock); + + list_for_each_entry(pwm, &pwm_list, list) { + if (pwm->pwm_id == pwm_id) { + found = 1; + break; + } + } + + if (found) { + if (pwm->use_count == 0) { + pwm->use_count = 1; + pwm->label = label; + } else + pwm = ERR_PTR(-EBUSY); + } else + pwm = ERR_PTR(-ENOENT); + + mutex_unlock(&pwm_lock); + return pwm; +} + +EXPORT_SYMBOL(pwm_request); + + +void pwm_free(struct pwm_device *pwm) +{ + mutex_lock(&pwm_lock); + + if (pwm->use_count) { + pwm->use_count--; + pwm->label = NULL; + } else + printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id); + + mutex_unlock(&pwm_lock); +} + +EXPORT_SYMBOL(pwm_free); + +#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0)) +#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2)) +#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3)) +#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1)) + +int pwm_enable(struct pwm_device *pwm) +{ + unsigned long flags; + unsigned long tcon; + + local_irq_save(flags); + + tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_start(pwm); + __raw_writel(tcon, S3C2410_TCON); + + local_irq_restore(flags); + + pwm->running = 1; + return 0; +} + +EXPORT_SYMBOL(pwm_enable); + +void pwm_disable(struct pwm_device *pwm) +{ + unsigned long flags; + unsigned long tcon; + + local_irq_save(flags); + + tcon = __raw_readl(S3C2410_TCON); + tcon &= ~pwm_tcon_start(pwm); + __raw_writel(tcon, S3C2410_TCON); + + local_irq_restore(flags); + + pwm->running = 0; +} + +EXPORT_SYMBOL(pwm_disable); + +static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq) +{ + unsigned long tin_parent_rate; + unsigned int div; + + tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div)); + pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate); + + for (div = 2; div <= 16; div *= 2) { + if ((tin_parent_rate / (div << 16)) < freq) + return tin_parent_rate / div; + } + + return tin_parent_rate / 16; +} + +#define NS_IN_HZ (1000000000UL) + +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + unsigned long tin_rate; + unsigned long tin_ns; + unsigned long period; + unsigned long flags; + unsigned long tcon; + unsigned long tcnt; + long tcmp; + + /* We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1Hz is easily representable + * by 32bits. */ + + if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ) + return -ERANGE; + + if (duty_ns > period_ns) + return -EINVAL; + + if (period_ns == pwm->period_ns && + duty_ns == pwm->duty_ns) + return 0; + + /* The TCMP and TCNT can be read without a lock, they're not + * shared between the timers. */ + + tcmp = __raw_readl(S3C2410_TCMPB(pwm->pwm_id)); + tcnt = __raw_readl(S3C2410_TCNTB(pwm->pwm_id)); + + period = NS_IN_HZ / period_ns; + + pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n", + duty_ns, period_ns, period); + + /* Check to see if we are changing the clock rate of the PWM */ + + if (pwm->period_ns != period_ns) { + if (pwm_is_tdiv(pwm)) { + tin_rate = pwm_calc_tin(pwm, period); + clk_set_rate(pwm->clk_div, tin_rate); + } else + tin_rate = clk_get_rate(pwm->clk); + + pwm->period_ns = period_ns; + + pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate); + + tin_ns = NS_IN_HZ / tin_rate; + tcnt = period_ns / tin_ns; + } else + tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk); + + /* Note, counters count down */ + + tcmp = duty_ns / tin_ns; + tcmp = tcnt - tcmp; + /* the pwm hw only checks the compare register after a decrement, + so the pin never toggles if tcmp = tcnt */ + if (tcmp == tcnt) + tcmp--; + + pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); + + if (tcmp < 0) + tcmp = 0; + + /* Update the PWM register block. */ + + local_irq_save(flags); + + __raw_writel(tcmp, S3C2410_TCMPB(pwm->pwm_id)); + __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id)); + + tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_manulupdate(pwm); + tcon |= pwm_tcon_autoreload(pwm); + __raw_writel(tcon, S3C2410_TCON); + + tcon &= ~pwm_tcon_manulupdate(pwm); + __raw_writel(tcon, S3C2410_TCON); + + local_irq_restore(flags); + + return 0; +} + +EXPORT_SYMBOL(pwm_config); + +static int pwm_register(struct pwm_device *pwm) +{ + pwm->duty_ns = -1; + pwm->period_ns = -1; + + mutex_lock(&pwm_lock); + list_add_tail(&pwm->list, &pwm_list); + mutex_unlock(&pwm_lock); + + return 0; +} + +static int s3c_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pwm_device *pwm; + unsigned long flags; + unsigned long tcon; + unsigned int id = pdev->id; + int ret; + + if (id == 4) { + dev_err(dev, "TIMER4 is currently not supported\n"); + return -ENXIO; + } + + pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); + if (pwm == NULL) { + dev_err(dev, "failed to allocate pwm_device\n"); + return -ENOMEM; + } + + pwm->pdev = pdev; + pwm->pwm_id = id; + + /* calculate base of control bits in TCON */ + pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4; + + pwm->clk = clk_get(dev, "pwm-tin"); + if (IS_ERR(pwm->clk)) { + dev_err(dev, "failed to get pwm tin clk\n"); + ret = PTR_ERR(pwm->clk); + goto err_alloc; + } + + pwm->clk_div = clk_get(dev, "pwm-tdiv"); + if (IS_ERR(pwm->clk_div)) { + dev_err(dev, "failed to get pwm tdiv clk\n"); + ret = PTR_ERR(pwm->clk_div); + goto err_clk_tin; + } + + local_irq_save(flags); + + tcon = __raw_readl(S3C2410_TCON); + tcon |= pwm_tcon_invert(pwm); + __raw_writel(tcon, S3C2410_TCON); + + local_irq_restore(flags); + + + ret = pwm_register(pwm); + if (ret) { + dev_err(dev, "failed to register pwm\n"); + goto err_clk_tdiv; + } + + pwm_dbg(pwm, "config bits %02x\n", + (__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f); + + dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n", + clk_get_rate(pwm->clk), + clk_get_rate(pwm->clk_div), + pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base); + + platform_set_drvdata(pdev, pwm); + return 0; + + err_clk_tdiv: + clk_put(pwm->clk_div); + + err_clk_tin: + clk_put(pwm->clk); + + err_alloc: + kfree(pwm); + return ret; +} + +static int __devexit s3c_pwm_remove(struct platform_device *pdev) +{ + struct pwm_device *pwm = platform_get_drvdata(pdev); + + clk_put(pwm->clk_div); + clk_put(pwm->clk); + kfree(pwm); + + return 0; +} + +static struct platform_driver s3c_pwm_driver = { + .driver = { + .name = "s3c24xx-pwm", + .owner = THIS_MODULE, + }, + .probe = s3c_pwm_probe, + .remove = __devexit_p(s3c_pwm_remove), +}; + +static int __init pwm_init(void) +{ + int ret; + + clk_scaler[0] = clk_get(NULL, "pwm-scaler0"); + clk_scaler[1] = clk_get(NULL, "pwm-scaler1"); + + if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) { + printk(KERN_ERR "%s: failed to get scaler clocks\n", __func__); + return -EINVAL; + } + + ret = platform_driver_register(&s3c_pwm_driver); + if (ret) + printk(KERN_ERR "%s: failed to add pwm driver\n", __func__); + + return ret; +} + +arch_initcall(pwm_init); -- cgit v0.10.2 From 8005745d6eea79da8ee2e7fe978d14e412aa3353 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 20 Jan 2010 12:29:25 +0900 Subject: ARM: SAMSUNG: Move pm-check.c to plat-samsung Move the pm-check.c file to plat-samsung for all Samsung SoC users, and update Kconfig names to make them SAMSUNG_ instead of S3C2410_ Sed expresions used to make the change: s/S3C2410_PM_DEBUG/SAMSUNG_PM_DEBUG/g s/S3C2410_PM_CHECK/SAMSUNG_PM_CHECK/g Signed-off-by: Ben Dooks diff --git a/arch/arm/configs/mini2440_defconfig b/arch/arm/configs/mini2440_defconfig index 4c46644..ff44bd1 100644 --- a/arch/arm/configs/mini2440_defconfig +++ b/arch/arm/configs/mini2440_defconfig @@ -199,8 +199,8 @@ CONFIG_S3C_BOOT_UART_FORCE_FIFO=y # # Power management # -# CONFIG_S3C2410_PM_DEBUG is not set -# CONFIG_S3C2410_PM_CHECK is not set +# CONFIG_SAMSUNG_PM_DEBUG is not set +# CONFIG_SAMSUNG_PM_CHECK is not set CONFIG_S3C_LOWLEVEL_UART_PORT=0 CONFIG_S3C_GPIO_SPACE=0 diff --git a/arch/arm/configs/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig index 9697e97..8e94c3c 100644 --- a/arch/arm/configs/s3c2410_defconfig +++ b/arch/arm/configs/s3c2410_defconfig @@ -203,8 +203,8 @@ CONFIG_S3C_BOOT_UART_FORCE_FIFO=y # # Power management # -# CONFIG_S3C2410_PM_DEBUG is not set -# CONFIG_S3C2410_PM_CHECK is not set +# CONFIG_SAMSUNG_PM_DEBUG is not set +# CONFIG_SAMSUNG_PM_CHECK is not set CONFIG_S3C_LOWLEVEL_UART_PORT=0 CONFIG_S3C_GPIO_SPACE=0 CONFIG_S3C_DEV_HSMMC=y diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig index 454cc39..2367908 100644 --- a/arch/arm/plat-s3c/Kconfig +++ b/arch/arm/plat-s3c/Kconfig @@ -59,50 +59,6 @@ config S3C_BOOT_UART_FORCE_FIFO Say Y here to force the UART FIFOs on during the kernel uncompressor -comment "Power management" - -config S3C2410_PM_DEBUG - bool "S3C2410 PM Suspend debug" - depends on PM - help - Say Y here if you want verbose debugging from the PM Suspend and - Resume code. See - for more information. - -config S3C_PM_DEBUG_LED_SMDK - bool "SMDK LED suspend/resume debugging" - depends on PM && (MACH_SMDK6410) - help - Say Y here to enable the use of the SMDK LEDs on the baseboard - for debugging of the state of the suspend and resume process. - - Note, this currently only works for S3C64XX based SMDK boards. - -config S3C2410_PM_CHECK - bool "S3C2410 PM Suspend Memory CRC" - depends on PM && CRC32 - help - Enable the PM code's memory area checksum over sleep. This option - will generate CRCs of all blocks of memory, and store them before - going to sleep. The blocks are then checked on resume for any - errors. - - Note, this can take several seconds depending on memory size - and CPU speed. - - See - -config S3C2410_PM_CHECK_CHUNKSIZE - int "S3C2410 PM Suspend CRC Chunksize (KiB)" - depends on PM && S3C2410_PM_CHECK - default 64 - help - Set the chunksize in Kilobytes of the CRC for checking memory - corruption over suspend and resume. A smaller value will mean that - the CRC data block will take more memory, but wil identify any - faults with better precision. - - See config S3C_LOWLEVEL_UART_PORT int "S3C UART to use for low-level messages" diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 095bc97..89dbdb0 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -21,4 +21,3 @@ obj-$(CONFIG_S3C_DMA) += dma.o # PM support obj-$(CONFIG_PM) += pm.o -obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o diff --git a/arch/arm/plat-s3c/include/plat/pm.h b/arch/arm/plat-s3c/include/plat/pm.h index 7a79719..2543bd2 100644 --- a/arch/arm/plat-s3c/include/plat/pm.h +++ b/arch/arm/plat-s3c/include/plat/pm.h @@ -111,7 +111,7 @@ extern int s3c24xx_irq_resume(struct sys_device *dev); /* PM debug functions */ -#ifdef CONFIG_S3C2410_PM_DEBUG +#ifdef CONFIG_SAMSUNG_PM_DEBUG /** * s3c_pm_dbg() - low level debug function for use in suspend/resume. * @msg: The message to print. @@ -141,7 +141,7 @@ static inline void s3c_pm_debug_smdkled(u32 set, u32 clear) { } /* suspend memory checking */ -#ifdef CONFIG_S3C2410_PM_CHECK +#ifdef CONFIG_SAMSUNG_PM_CHECK extern void s3c_pm_check_prepare(void); extern void s3c_pm_check_restore(void); extern void s3c_pm_check_cleanup(void); diff --git a/arch/arm/plat-s3c/pm-check.c b/arch/arm/plat-s3c/pm-check.c deleted file mode 100644 index 8eb1f43..0000000 --- a/arch/arm/plat-s3c/pm-check.c +++ /dev/null @@ -1,242 +0,0 @@ -/* linux/arch/arm/plat-s3c/pm-check.c - * originally in linux/arch/arm/plat-s3c24xx/pm.c - * - * Copyright (c) 2004-2008 Simtec Electronics - * http://armlinux.simtec.co.uk - * Ben Dooks - * - * S3C Power Mangament - suspend/resume memory corruptiuon check. - * - * 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 - -#if CONFIG_S3C2410_PM_CHECK_CHUNKSIZE < 1 -#error CONFIG_S3C2410_PM_CHECK_CHUNKSIZE must be a positive non-zero value -#endif - -/* suspend checking code... - * - * this next area does a set of crc checks over all the installed - * memory, so the system can verify if the resume was ok. - * - * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, - * increasing it will mean that the area corrupted will be less easy to spot, - * and reducing the size will cause the CRC save area to grow -*/ - -#define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024) - -static u32 crc_size; /* size needed for the crc block */ -static u32 *crcs; /* allocated over suspend/resume */ - -typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); - -/* s3c_pm_run_res - * - * go through the given resource list, and look for system ram -*/ - -static void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) -{ - while (ptr != NULL) { - if (ptr->child != NULL) - s3c_pm_run_res(ptr->child, fn, arg); - - if ((ptr->flags & IORESOURCE_MEM) && - strcmp(ptr->name, "System RAM") == 0) { - S3C_PMDBG("Found system RAM at %08lx..%08lx\n", - (unsigned long)ptr->start, - (unsigned long)ptr->end); - arg = (fn)(ptr, arg); - } - - ptr = ptr->sibling; - } -} - -static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg) -{ - s3c_pm_run_res(&iomem_resource, fn, arg); -} - -static u32 *s3c_pm_countram(struct resource *res, u32 *val) -{ - u32 size = (u32)(res->end - res->start)+1; - - size += CHECK_CHUNKSIZE-1; - size /= CHECK_CHUNKSIZE; - - S3C_PMDBG("Area %08lx..%08lx, %d blocks\n", - (unsigned long)res->start, (unsigned long)res->end, size); - - *val += size * sizeof(u32); - return val; -} - -/* s3c_pm_prepare_check - * - * prepare the necessary information for creating the CRCs. This - * must be done before the final save, as it will require memory - * allocating, and thus touching bits of the kernel we do not - * know about. -*/ - -void s3c_pm_check_prepare(void) -{ - crc_size = 0; - - s3c_pm_run_sysram(s3c_pm_countram, &crc_size); - - S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size); - - crcs = kmalloc(crc_size+4, GFP_KERNEL); - if (crcs == NULL) - printk(KERN_ERR "Cannot allocated CRC save area\n"); -} - -static u32 *s3c_pm_makecheck(struct resource *res, u32 *val) -{ - unsigned long addr, left; - - for (addr = res->start; addr < res->end; - addr += CHECK_CHUNKSIZE) { - left = res->end - addr; - - if (left > CHECK_CHUNKSIZE) - left = CHECK_CHUNKSIZE; - - *val = crc32_le(~0, phys_to_virt(addr), left); - val++; - } - - return val; -} - -/* s3c_pm_check_store - * - * compute the CRC values for the memory blocks before the final - * sleep. -*/ - -void s3c_pm_check_store(void) -{ - if (crcs != NULL) - s3c_pm_run_sysram(s3c_pm_makecheck, crcs); -} - -/* in_region - * - * return TRUE if the area defined by ptr..ptr+size contains the - * what..what+whatsz -*/ - -static inline int in_region(void *ptr, int size, void *what, size_t whatsz) -{ - if ((what+whatsz) < ptr) - return 0; - - if (what > (ptr+size)) - return 0; - - return 1; -} - -/** - * s3c_pm_runcheck() - helper to check a resource on restore. - * @res: The resource to check - * @vak: Pointer to list of CRC32 values to check. - * - * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this - * function runs the given memory resource checking it against the stored - * CRC to ensure that memory is restored. The function tries to skip as - * many of the areas used during the suspend process. - */ -static u32 *s3c_pm_runcheck(struct resource *res, u32 *val) -{ - void *save_at = phys_to_virt(s3c_sleep_save_phys); - unsigned long addr; - unsigned long left; - void *stkpage; - void *ptr; - u32 calc; - - stkpage = (void *)((u32)&calc & ~PAGE_MASK); - - for (addr = res->start; addr < res->end; - addr += CHECK_CHUNKSIZE) { - left = res->end - addr; - - if (left > CHECK_CHUNKSIZE) - left = CHECK_CHUNKSIZE; - - ptr = phys_to_virt(addr); - - if (in_region(ptr, left, stkpage, 4096)) { - S3C_PMDBG("skipping %08lx, has stack in\n", addr); - goto skip_check; - } - - if (in_region(ptr, left, crcs, crc_size)) { - S3C_PMDBG("skipping %08lx, has crc block in\n", addr); - goto skip_check; - } - - if (in_region(ptr, left, save_at, 32*4 )) { - S3C_PMDBG("skipping %08lx, has save block in\n", addr); - goto skip_check; - } - - /* calculate and check the checksum */ - - calc = crc32_le(~0, ptr, left); - if (calc != *val) { - printk(KERN_ERR "Restore CRC error at " - "%08lx (%08x vs %08x)\n", addr, calc, *val); - - S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n", - addr, calc, *val); - } - - skip_check: - val++; - } - - return val; -} - -/** - * s3c_pm_check_restore() - memory check called on resume - * - * check the CRCs after the restore event and free the memory used - * to hold them -*/ -void s3c_pm_check_restore(void) -{ - if (crcs != NULL) - s3c_pm_run_sysram(s3c_pm_runcheck, crcs); -} - -/** - * s3c_pm_check_cleanup() - free memory resources - * - * Free the resources that where allocated by the suspend - * memory check code. We do this separately from the - * s3c_pm_check_restore() function as we cannot call any - * functions that might sleep during that resume. - */ -void s3c_pm_check_cleanup(void) -{ - kfree(crcs); - crcs = NULL; -} - diff --git a/arch/arm/plat-s3c/pm.c b/arch/arm/plat-s3c/pm.c index 7674706..e5eef126 100644 --- a/arch/arm/plat-s3c/pm.c +++ b/arch/arm/plat-s3c/pm.c @@ -41,7 +41,7 @@ unsigned long s3c_pm_flags; * resume before the console layer is available. */ -#ifdef CONFIG_S3C2410_PM_DEBUG +#ifdef CONFIG_SAMSUNG_PM_DEBUG extern void printascii(const char *); void s3c_pm_dbg(const char *fmt, ...) @@ -65,13 +65,13 @@ static inline void s3c_pm_debug_init(void) #else #define s3c_pm_debug_init() do { } while(0) -#endif /* CONFIG_S3C2410_PM_DEBUG */ +#endif /* CONFIG_SAMSUNG_PM_DEBUG */ /* Save the UART configurations if we are configured for debug. */ unsigned char pm_uart_udivslot; -#ifdef CONFIG_S3C2410_PM_DEBUG +#ifdef CONFIG_SAMSUNG_PM_DEBUG struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS]; diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 6484b5b..1c2fe91 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -132,4 +132,49 @@ config S3C_DEV_NAND help Compile in platform device definition for NAND controller +comment "Power management" + +config SAMSUNG_PM_DEBUG + bool "S3C2410 PM Suspend debug" + depends on PM + help + Say Y here if you want verbose debugging from the PM Suspend and + Resume code. See + for more information. + +config S3C_PM_DEBUG_LED_SMDK + bool "SMDK LED suspend/resume debugging" + depends on PM && (MACH_SMDK6410) + help + Say Y here to enable the use of the SMDK LEDs on the baseboard + for debugging of the state of the suspend and resume process. + + Note, this currently only works for S3C64XX based SMDK boards. + +config SAMSUNG_PM_CHECK + bool "S3C2410 PM Suspend Memory CRC" + depends on PM && CRC32 + help + Enable the PM code's memory area checksum over sleep. This option + will generate CRCs of all blocks of memory, and store them before + going to sleep. The blocks are then checked on resume for any + errors. + + Note, this can take several seconds depending on memory size + and CPU speed. + + See + +config SAMSUNG_PM_CHECK_CHUNKSIZE + int "S3C2410 PM Suspend CRC Chunksize (KiB)" + depends on PM && SAMSUNG_PM_CHECK + default 64 + help + Set the chunksize in Kilobytes of the CRC for checking memory + corruption over suspend and resume. A smaller value will mean that + the CRC data block will take more memory, but wil identify any + faults with better precision. + + See + endif diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index f31e64f..c8c8cae 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o # PM support obj-$(CONFIG_PM) += pm-gpio.o +obj-$(CONFIG_SAMSUNG_PM_CHECK) += pm-check.o # PWM support diff --git a/arch/arm/plat-samsung/pm-check.c b/arch/arm/plat-samsung/pm-check.c new file mode 100644 index 0000000..0b5bb77 --- /dev/null +++ b/arch/arm/plat-samsung/pm-check.c @@ -0,0 +1,242 @@ +/* linux/arch/arm/plat-s3c/pm-check.c + * originally in linux/arch/arm/plat-s3c24xx/pm.c + * + * Copyright (c) 2004-2008 Simtec Electronics + * http://armlinux.simtec.co.uk + * Ben Dooks + * + * S3C Power Mangament - suspend/resume memory corruptiuon check. + * + * 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 + +#if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1 +#error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value +#endif + +/* suspend checking code... + * + * this next area does a set of crc checks over all the installed + * memory, so the system can verify if the resume was ok. + * + * CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE defines the block-size for the CRC, + * increasing it will mean that the area corrupted will be less easy to spot, + * and reducing the size will cause the CRC save area to grow +*/ + +#define CHECK_CHUNKSIZE (CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE * 1024) + +static u32 crc_size; /* size needed for the crc block */ +static u32 *crcs; /* allocated over suspend/resume */ + +typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg); + +/* s3c_pm_run_res + * + * go through the given resource list, and look for system ram +*/ + +static void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg) +{ + while (ptr != NULL) { + if (ptr->child != NULL) + s3c_pm_run_res(ptr->child, fn, arg); + + if ((ptr->flags & IORESOURCE_MEM) && + strcmp(ptr->name, "System RAM") == 0) { + S3C_PMDBG("Found system RAM at %08lx..%08lx\n", + (unsigned long)ptr->start, + (unsigned long)ptr->end); + arg = (fn)(ptr, arg); + } + + ptr = ptr->sibling; + } +} + +static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg) +{ + s3c_pm_run_res(&iomem_resource, fn, arg); +} + +static u32 *s3c_pm_countram(struct resource *res, u32 *val) +{ + u32 size = (u32)(res->end - res->start)+1; + + size += CHECK_CHUNKSIZE-1; + size /= CHECK_CHUNKSIZE; + + S3C_PMDBG("Area %08lx..%08lx, %d blocks\n", + (unsigned long)res->start, (unsigned long)res->end, size); + + *val += size * sizeof(u32); + return val; +} + +/* s3c_pm_prepare_check + * + * prepare the necessary information for creating the CRCs. This + * must be done before the final save, as it will require memory + * allocating, and thus touching bits of the kernel we do not + * know about. +*/ + +void s3c_pm_check_prepare(void) +{ + crc_size = 0; + + s3c_pm_run_sysram(s3c_pm_countram, &crc_size); + + S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size); + + crcs = kmalloc(crc_size+4, GFP_KERNEL); + if (crcs == NULL) + printk(KERN_ERR "Cannot allocated CRC save area\n"); +} + +static u32 *s3c_pm_makecheck(struct resource *res, u32 *val) +{ + unsigned long addr, left; + + for (addr = res->start; addr < res->end; + addr += CHECK_CHUNKSIZE) { + left = res->end - addr; + + if (left > CHECK_CHUNKSIZE) + left = CHECK_CHUNKSIZE; + + *val = crc32_le(~0, phys_to_virt(addr), left); + val++; + } + + return val; +} + +/* s3c_pm_check_store + * + * compute the CRC values for the memory blocks before the final + * sleep. +*/ + +void s3c_pm_check_store(void) +{ + if (crcs != NULL) + s3c_pm_run_sysram(s3c_pm_makecheck, crcs); +} + +/* in_region + * + * return TRUE if the area defined by ptr..ptr+size contains the + * what..what+whatsz +*/ + +static inline int in_region(void *ptr, int size, void *what, size_t whatsz) +{ + if ((what+whatsz) < ptr) + return 0; + + if (what > (ptr+size)) + return 0; + + return 1; +} + +/** + * s3c_pm_runcheck() - helper to check a resource on restore. + * @res: The resource to check + * @vak: Pointer to list of CRC32 values to check. + * + * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this + * function runs the given memory resource checking it against the stored + * CRC to ensure that memory is restored. The function tries to skip as + * many of the areas used during the suspend process. + */ +static u32 *s3c_pm_runcheck(struct resource *res, u32 *val) +{ + void *save_at = phys_to_virt(s3c_sleep_save_phys); + unsigned long addr; + unsigned long left; + void *stkpage; + void *ptr; + u32 calc; + + stkpage = (void *)((u32)&calc & ~PAGE_MASK); + + for (addr = res->start; addr < res->end; + addr += CHECK_CHUNKSIZE) { + left = res->end - addr; + + if (left > CHECK_CHUNKSIZE) + left = CHECK_CHUNKSIZE; + + ptr = phys_to_virt(addr); + + if (in_region(ptr, left, stkpage, 4096)) { + S3C_PMDBG("skipping %08lx, has stack in\n", addr); + goto skip_check; + } + + if (in_region(ptr, left, crcs, crc_size)) { + S3C_PMDBG("skipping %08lx, has crc block in\n", addr); + goto skip_check; + } + + if (in_region(ptr, left, save_at, 32*4 )) { + S3C_PMDBG("skipping %08lx, has save block in\n", addr); + goto skip_check; + } + + /* calculate and check the checksum */ + + calc = crc32_le(~0, ptr, left); + if (calc != *val) { + printk(KERN_ERR "Restore CRC error at " + "%08lx (%08x vs %08x)\n", addr, calc, *val); + + S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n", + addr, calc, *val); + } + + skip_check: + val++; + } + + return val; +} + +/** + * s3c_pm_check_restore() - memory check called on resume + * + * check the CRCs after the restore event and free the memory used + * to hold them +*/ +void s3c_pm_check_restore(void) +{ + if (crcs != NULL) + s3c_pm_run_sysram(s3c_pm_runcheck, crcs); +} + +/** + * s3c_pm_check_cleanup() - free memory resources + * + * Free the resources that where allocated by the suspend + * memory check code. We do this separately from the + * s3c_pm_check_restore() function as we cannot call any + * functions that might sleep during that resume. + */ +void s3c_pm_check_cleanup(void) +{ + kfree(crcs); + crcs = NULL; +} + -- cgit v0.10.2 From 9fca878623378e7dd6da8e7135a561c17bc1112b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Jan 2010 15:26:56 +0000 Subject: ARM: SMDK6410: Set up interrupt range for WM835x on 1190-EV1 PMIC board The WM835x driver won't actually use this yet but it will in the future when the changes converting it to genirq are merged. Signed-off-by: Mark Brown Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s3c6410/mach-smdk6410.c b/arch/arm/mach-s3c6410/mach-smdk6410.c index 5376880..eba345f 100644 --- a/arch/arm/mach-s3c6410/mach-smdk6410.c +++ b/arch/arm/mach-s3c6410/mach-smdk6410.c @@ -399,6 +399,7 @@ static int __init smdk6410_wm8350_init(struct wm8350 *wm8350) static struct wm8350_platform_data __initdata smdk6410_wm8350_pdata = { .init = smdk6410_wm8350_init, .irq_high = 1, + .irq_base = IRQ_BOARD_START, }; #endif diff --git a/arch/arm/plat-s3c64xx/include/plat/irqs.h b/arch/arm/plat-s3c64xx/include/plat/irqs.h index 176fe15..a227581 100644 --- a/arch/arm/plat-s3c64xx/include/plat/irqs.h +++ b/arch/arm/plat-s3c64xx/include/plat/irqs.h @@ -198,7 +198,11 @@ * interrupt controllers). */ #define IRQ_BOARD_START (IRQ_EINT_GROUP9_BASE + IRQ_EINT_GROUP9_NR + 1) +#ifdef CONFIG_SMDK6410_WM1190_EV1 +#define IRQ_BOARD_NR 64 +#else #define IRQ_BOARD_NR 16 +#endif #define IRQ_BOARD_END (IRQ_BOARD_START + IRQ_BOARD_NR) -- cgit v0.10.2 From 1c24522c05f25b89cc4df5a875d8a005a774f570 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 20 Jan 2010 12:05:55 +0000 Subject: ARM: S3C64XX: Fix dev-audio build As reported when the patch was posted commit 71269364 ("ARM: S3C64XX: Add I2S resources in platform code") uses pin names for the IISv4 data output pins which are not present in mainline, causing dev-audio to fail to build. Fix this by using the defines which are actually present in the kernel source. Signed-off-by: Mark Brown Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c index cdba3a7..5b89344 100644 --- a/arch/arm/plat-s3c64xx/dev-audio.c +++ b/arch/arm/plat-s3c64xx/dev-audio.c @@ -51,9 +51,9 @@ static int s3c64xx_i2sv3_cfg_gpio(struct platform_device *pdev) static int s3c64xx_i2sv4_cfg_gpio(struct platform_device *pdev) { - s3c_gpio_cfgpin(S3C64XX_GPC(4), S3C64XX_GPC4_I2S0_V40_DO); - s3c_gpio_cfgpin(S3C64XX_GPC(5), S3C64XX_GPC5_I2S1_V40_DO); - s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C64XX_GPC7_I2S2_V40_DO); + s3c_gpio_cfgpin(S3C64XX_GPC(4), S3C64XX_GPC4_I2S_V40_DO0); + s3c_gpio_cfgpin(S3C64XX_GPC(5), S3C64XX_GPC5_I2S_V40_DO1); + s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C64XX_GPC7_I2S_V40_DO2); s3c_gpio_cfgpin(S3C64XX_GPH(6), S3C64XX_GPH6_I2S_V40_BCLK); s3c_gpio_cfgpin(S3C64XX_GPH(7), S3C64XX_GPH7_I2S_V40_CDCLK); s3c_gpio_cfgpin(S3C64XX_GPH(8), S3C64XX_GPH8_I2S_V40_LRCLK); -- cgit v0.10.2 From 8dcdd6e6bb09ba1915668b45a0ee730e820d0de1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 20 Jan 2010 13:06:04 +0000 Subject: ARM: S3C64XX: Staticise audio platform data It's not exported. Signed-off-by: Mark Brown Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c index 5b89344..f6b7bfb 100644 --- a/arch/arm/plat-s3c64xx/dev-audio.c +++ b/arch/arm/plat-s3c64xx/dev-audio.c @@ -80,7 +80,7 @@ static struct resource s3c64xx_iis0_resource[] = { }, }; -struct s3c_audio_pdata s3c_i2s0_pdata = { +static struct s3c_audio_pdata s3c_i2s0_pdata = { .cfg_gpio = s3c64xx_i2sv3_cfg_gpio, }; @@ -113,7 +113,7 @@ static struct resource s3c64xx_iis1_resource[] = { }, }; -struct s3c_audio_pdata s3c_i2s1_pdata = { +static struct s3c_audio_pdata s3c_i2s1_pdata = { .cfg_gpio = s3c64xx_i2sv3_cfg_gpio, }; @@ -146,7 +146,7 @@ static struct resource s3c64xx_iisv4_resource[] = { }, }; -struct s3c_audio_pdata s3c_i2sv4_pdata = { +static struct s3c_audio_pdata s3c_i2sv4_pdata = { .cfg_gpio = s3c64xx_i2sv4_cfg_gpio, }; -- cgit v0.10.2 From 46f2b478b21bdb5bb588317c971a77df13702660 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 20 Jan 2010 13:06:05 +0000 Subject: ARM: SAMSUNG: Staticise gpiolib implementation functions They are not exported, they are referenced via vtables. Signed-off-by: Mark Brown Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/gpiolib.c b/arch/arm/plat-samsung/gpiolib.c index 3419b67..8a8ba8b 100644 --- a/arch/arm/plat-samsung/gpiolib.c +++ b/arch/arm/plat-samsung/gpiolib.c @@ -44,7 +44,8 @@ * the output. */ -int samsung_gpiolib_4bit_input(struct gpio_chip *chip, unsigned int offset) +static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, + unsigned int offset) { struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); void __iomem *base = ourchip->base; @@ -59,8 +60,8 @@ int samsung_gpiolib_4bit_input(struct gpio_chip *chip, unsigned int offset) return 0; } -int samsung_gpiolib_4bit_output(struct gpio_chip *chip, - unsigned int offset, int value) +static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, + unsigned int offset, int value) { struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); void __iomem *base = ourchip->base; @@ -106,9 +107,10 @@ int samsung_gpiolib_4bit_output(struct gpio_chip *chip, * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we * store the 'base + 0x4' address so that these routines see the data * register at ourchip->base + 0x04. -*/ + */ -int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned int offset) +static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, + unsigned int offset) { struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); void __iomem *base = ourchip->base; @@ -129,8 +131,8 @@ int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned int offset) return 0; } -int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, - unsigned int offset, int value) +static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, + unsigned int offset, int value) { struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); void __iomem *base = ourchip->base; -- cgit v0.10.2 From bd548e5ef21ac2ff18b262a905543d7a2340fb87 Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Thu, 21 Jan 2010 01:37:28 +0100 Subject: ARM: S3C64XX: Fix typo in s3c-hsotg platform data ARM: S3C64XX: Fix typo in s3c-hsotg platform data Signed-off-by: Maurus Cuelenaere Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/include/plat/udc-hs.h b/arch/arm/plat-samsung/include/plat/udc-hs.h index dd04db0..a22a4f2 100644 --- a/arch/arm/plat-samsung/include/plat/udc-hs.h +++ b/arch/arm/plat-samsung/include/plat/udc-hs.h @@ -12,7 +12,7 @@ * published by the Free Software Foundation. */ -enum s3c_hostg_dmamode { +enum s3c_hsotg_dmamode { S3C_HSOTG_DMA_NONE, /* do not use DMA at-all */ S3C_HSOTG_DMA_ONLY, /* always use DMA */ S3C_HSOTG_DMA_DRV, /* DMA is chosen by driver */ @@ -24,6 +24,6 @@ enum s3c_hostg_dmamode { * @is_osc: The clock source is an oscillator, not a crystal */ struct s3c_hsotg_plat { - enum s3c_hostg_dmamode dma; + enum s3c_hsotg_dmamode dma; unsigned int is_osc : 1; }; -- cgit v0.10.2 From 75497d96b2094d325594b46d5e5f12b2396c4d51 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Thu, 21 Jan 2010 13:31:38 +0900 Subject: ARM: S3C64XX: Tidy up common code in s3c64xx_spi_set_info() The s3c64xx_spi_set_info() sets one of two platform data structures depending on which controller is being specified. Change to taking a pointer to the relevant platform data structure and then having one set of code to place the data into the area being pointed to. Cc: Jassi Brar Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c64xx/dev-spi.c b/arch/arm/plat-s3c64xx/dev-spi.c index 6b6d7af..ca10388 100644 --- a/arch/arm/plat-s3c64xx/dev-spi.c +++ b/arch/arm/plat-s3c64xx/dev-spi.c @@ -18,6 +18,7 @@ #include #include + #include #include #include @@ -154,6 +155,8 @@ EXPORT_SYMBOL(s3c64xx_device_spi1); void __init s3c64xx_spi_set_info(int cntrlr, int src_clk_nr, int num_cs) { + struct s3c64xx_spi_info *pd; + /* Reject invalid configuration */ if (!num_cs || src_clk_nr < 0 || src_clk_nr > S3C64XX_SPI_SRCCLK_48M) { @@ -163,18 +166,18 @@ void __init s3c64xx_spi_set_info(int cntrlr, int src_clk_nr, int num_cs) switch (cntrlr) { case 0: - s3c64xx_spi0_pdata.num_cs = num_cs; - s3c64xx_spi0_pdata.src_clk_nr = src_clk_nr; - s3c64xx_spi0_pdata.src_clk_name = spi_src_clks[src_clk_nr]; + pd = &s3c64xx_spi0_pdata; break; case 1: - s3c64xx_spi1_pdata.num_cs = num_cs; - s3c64xx_spi1_pdata.src_clk_nr = src_clk_nr; - s3c64xx_spi1_pdata.src_clk_name = spi_src_clks[src_clk_nr]; + pd = &s3c64xx_spi1_pdata; break; default: printk(KERN_ERR "%s: Invalid SPI controller(%d)\n", __func__, cntrlr); return; } + + pd->num_cs = num_cs; + pd->src_clk_nr = src_clk_nr; + pd->src_clk_name = spi_src_clks[src_clk_nr]; } -- cgit v0.10.2 From 3a3b585aa4ecad21ae91cfa9e5f387d64a15a312 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 19 Jan 2010 11:34:58 +0900 Subject: ARM: S3C6400: Update s3c6400_defconfig for bigger initrds for SMDK6410 The Samsung initrd for SMDK6410 is now bigger than the original initrds that where used. Increase the command-line size and the size of the ramdisk that it is unpacked into. Also change the default init to /linuxrc to avoid problems with the current SMDK ramdisk image failing to run /bin/bash. Signed-off-by: Ben Dooks diff --git a/arch/arm/configs/s3c6400_defconfig b/arch/arm/configs/s3c6400_defconfig index f56e50f..c018435 100644 --- a/arch/arm/configs/s3c6400_defconfig +++ b/arch/arm/configs/s3c6400_defconfig @@ -256,7 +256,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0 CONFIG_ZBOOT_ROM_BSS=0 -CONFIG_CMDLINE="console=ttySAC0,115200 root=/dev/ram init=/bin/bash initrd=0x51000000,4M" +CONFIG_CMDLINE="console=ttySAC0,115200 root=/dev/ram init=/linuxrc initrd=0x51000000,6M ramdisk_size=6144" # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set -- cgit v0.10.2 From 1725c679a08902ce04419b274d22254155ce707c Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 19 Jan 2010 13:16:50 +0900 Subject: ARM: S3C64XX: Update s3c6400_defconfig for current kernel Update the s3c6400_defconfig with one from the current kernel which has been build and run tested on an SMDK6410. Signed-off-by: Ben Dooks diff --git a/arch/arm/configs/s3c6400_defconfig b/arch/arm/configs/s3c6400_defconfig index c018435..5e7d4c1 100644 --- a/arch/arm/configs/s3c6400_defconfig +++ b/arch/arm/configs/s3c6400_defconfig @@ -1,14 +1,11 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.28-rc3 -# Mon Nov 3 10:10:30 2008 +# Linux kernel version: 2.6.33-rc4 +# Tue Jan 19 13:12:40 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y CONFIG_GENERIC_GPIO=y -# CONFIG_GENERIC_TIME is not set -# CONFIG_GENERIC_CLOCKEVENTS is not set -CONFIG_MMU=y CONFIG_NO_IOPORT=y CONFIG_GENERIC_HARDIRQS=y CONFIG_STACKTRACE_SUPPORT=y @@ -18,13 +15,13 @@ CONFIG_TRACE_IRQFLAGS_SUPPORT=y CONFIG_HARDIRQS_SW_RESEND=y CONFIG_GENERIC_IRQ_PROBE=y CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_ARCH_HAS_ILOG2_U32 is not set -# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_ARCH_HAS_CPUFREQ=y CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y # # General setup @@ -34,13 +31,30 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y # CONFIG_SYSVIPC is not set # CONFIG_BSD_PROCESS_ACCT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set # CONFIG_IKCONFIG is not set CONFIG_LOG_BUF_SHIFT=17 -# CONFIG_CGROUPS is not set # CONFIG_GROUP_SCHED is not set +# CONFIG_CGROUPS is not set CONFIG_SYSFS_DEPRECATED=y CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_RELAY is not set @@ -50,8 +64,13 @@ CONFIG_NAMESPACES=y # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_LZO=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y # CONFIG_EMBEDDED is not set CONFIG_UID16=y CONFIG_SYSCTL_SYSCALL=y @@ -62,32 +81,38 @@ CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y CONFIG_ELF_CORE=y -CONFIG_COMPAT_BRK=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y -CONFIG_ANON_INODES=y CONFIG_EPOLL=y CONFIG_SIGNALFD=y CONFIG_TIMERFD=y CONFIG_EVENTFD=y CONFIG_SHMEM=y CONFIG_AIO=y + +# +# Kernel Performance Events And Counters +# CONFIG_VM_EVENT_COUNTERS=y CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y # CONFIG_SLAB is not set CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set -# CONFIG_MARKERS is not set CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_SLOW_WORK is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y -# CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 CONFIG_MODULES=y # CONFIG_MODULE_FORCE_LOAD is not set @@ -95,11 +120,8 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set # CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y CONFIG_BLOCK=y -CONFIG_LBD=y -# CONFIG_BLK_DEV_IO_TRACE is not set -CONFIG_LSF=y +CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set @@ -107,33 +129,62 @@ CONFIG_LSF=y # IO Schedulers # CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y -# CONFIG_DEFAULT_AS is not set # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" -CONFIG_CLASSIC_RCU=y +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set # CONFIG_FREEZER is not set # # System Type # +CONFIG_MMU=y # CONFIG_ARCH_AAEC2000 is not set # CONFIG_ARCH_INTEGRATOR is not set # CONFIG_ARCH_REALVIEW is not set # CONFIG_ARCH_VERSATILE is not set # CONFIG_ARCH_AT91 is not set -# CONFIG_ARCH_CLPS7500 is not set # CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set # CONFIG_ARCH_EBSA110 is not set # CONFIG_ARCH_EP93XX is not set # CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set # CONFIG_ARCH_NETX is not set # CONFIG_ARCH_H720X is not set -# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_NOMADIK is not set # CONFIG_ARCH_IOP13XX is not set # CONFIG_ARCH_IOP32X is not set # CONFIG_ARCH_IOP33X is not set @@ -141,35 +192,62 @@ CONFIG_CLASSIC_RCU=y # CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_IXP4XX is not set # CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set # CONFIG_ARCH_KIRKWOOD is not set -# CONFIG_ARCH_KS8695 is not set -# CONFIG_ARCH_NS9XXX is not set # CONFIG_ARCH_LOKI is not set # CONFIG_ARCH_MV78XX0 is not set -# CONFIG_ARCH_MXC is not set # CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set # CONFIG_ARCH_PNX4008 is not set # CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set # CONFIG_ARCH_RPC is not set # CONFIG_ARCH_SA1100 is not set # CONFIG_ARCH_S3C2410 is not set CONFIG_ARCH_S3C64XX=y +# CONFIG_ARCH_S5P6440 is not set +# CONFIG_ARCH_S5PC1XX is not set # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set # CONFIG_ARCH_DAVINCI is not set # CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_U8500 is not set +CONFIG_PLAT_SAMSUNG=y +CONFIG_SAMSUNG_CLKSRC=y +CONFIG_SAMSUNG_IRQ_VIC_TIMER=y +CONFIG_SAMSUNG_IRQ_UART=y +CONFIG_S3C_GPIO_CFG_S3C24XX=y +CONFIG_S3C_GPIO_CFG_S3C64XX=y +CONFIG_S3C_GPIO_PULL_UPDOWN=y +CONFIG_SAMSUNG_GPIO_EXTRA=0 +# CONFIG_S3C_ADC is not set +CONFIG_S3C_DEV_HSMMC=y +CONFIG_S3C_DEV_HSMMC1=y +CONFIG_S3C_DEV_I2C1=y +CONFIG_S3C_DEV_FB=y +CONFIG_S3C_DEV_USB_HOST=y +CONFIG_S3C_DEV_USB_HSOTG=y +CONFIG_S3C_DEV_NAND=y CONFIG_PLAT_S3C64XX=y CONFIG_CPU_S3C6400_INIT=y CONFIG_CPU_S3C6400_CLOCK=y +# CONFIG_S3C64XX_DMA is not set CONFIG_S3C64XX_SETUP_I2C0=y CONFIG_S3C64XX_SETUP_I2C1=y +CONFIG_S3C64XX_SETUP_FB_24BPP=y +CONFIG_S3C64XX_SETUP_SDHCI_GPIO=y CONFIG_PLAT_S3C=y # # Boot options # CONFIG_S3C_BOOT_ERROR_RESET=y +CONFIG_S3C_BOOT_UART_FORCE_FIFO=y # # Power management @@ -177,17 +255,16 @@ CONFIG_S3C_BOOT_ERROR_RESET=y CONFIG_S3C_LOWLEVEL_UART_PORT=0 CONFIG_S3C_GPIO_SPACE=0 CONFIG_S3C_GPIO_TRACK=y -CONFIG_S3C_GPIO_PULL_UPDOWN=y -CONFIG_S3C_GPIO_CFG_S3C24XX=y -CONFIG_S3C_GPIO_CFG_S3C64XX=y -CONFIG_S3C_DEV_HSMMC=y -CONFIG_S3C_DEV_HSMMC1=y -CONFIG_S3C_DEV_I2C1=y +# CONFIG_MACH_SMDK6400 is not set CONFIG_CPU_S3C6410=y CONFIG_S3C6410_SETUP_SDHCI=y +# CONFIG_MACH_ANW6410 is not set CONFIG_MACH_SMDK6410=y CONFIG_SMDK6410_SD_CH0=y # CONFIG_SMDK6410_SD_CH1 is not set +# CONFIG_SMDK6410_WM1190_EV1 is not set +# CONFIG_MACH_NCP is not set +# CONFIG_MACH_HMT is not set # # Processor Type @@ -196,7 +273,7 @@ CONFIG_CPU_V6=y CONFIG_CPU_32v6K=y CONFIG_CPU_32v6=y CONFIG_CPU_ABRT_EV6=y -CONFIG_CPU_PABRT_NOIFAR=y +CONFIG_CPU_PABRT_V6=y CONFIG_CPU_CACHE_V6=y CONFIG_CPU_CACHE_VIPT=y CONFIG_CPU_COPY_V6=y @@ -212,8 +289,10 @@ CONFIG_ARM_THUMB=y # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set # CONFIG_CPU_BPREDICT_DISABLE is not set -# CONFIG_OUTER_CACHE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +# CONFIG_ARM_ERRATA_411920 is not set CONFIG_ARM_VIC=y +CONFIG_ARM_VIC_NR=2 # # Bus support @@ -229,13 +308,15 @@ CONFIG_VMSPLIT_3G=y # CONFIG_VMSPLIT_2G is not set # CONFIG_VMSPLIT_1G is not set CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set # CONFIG_PREEMPT is not set CONFIG_HZ=100 CONFIG_AEABI=y CONFIG_OABI_COMPAT=y -CONFIG_ARCH_FLATMEM_HAS_HOLES=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y # CONFIG_DISCONTIGMEM_MANUAL is not set @@ -243,13 +324,14 @@ CONFIG_FLATMEM_MANUAL=y CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_PAGEFLAGS_EXTENDED=y -CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_RESOURCES_64BIT is not set +CONFIG_SPLIT_PTLOCK_CPUS=999999 # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y -CONFIG_UNEVICTABLE_LRU=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set # # Boot options @@ -263,6 +345,7 @@ CONFIG_CMDLINE="console=ttySAC0,115200 root=/dev/ram init=/linuxrc initrd=0x5100 # # CPU Power Management # +# CONFIG_CPU_FREQ is not set # CONFIG_CPU_IDLE is not set # @@ -300,6 +383,7 @@ CONFIG_ARCH_SUSPEND_POSSIBLE=y # Generic Driver Options # CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y @@ -314,14 +398,32 @@ CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 # CONFIG_BLK_DEV_XIP is not set # CONFIG_CDROM_PKTCDVD is not set +# CONFIG_MG_DISK is not set CONFIG_MISC_DEVICES=y -# CONFIG_EEPROM_93CX6 is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_ISL29003 is not set +# CONFIG_DS1682 is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +CONFIG_EEPROM_AT24=y +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -334,6 +436,7 @@ CONFIG_HAVE_IDE=y # CONFIG_SCSI_NETLINK is not set # CONFIG_ATA is not set # CONFIG_MD is not set +# CONFIG_PHONE is not set # # Input device support @@ -341,6 +444,7 @@ CONFIG_HAVE_IDE=y CONFIG_INPUT=y # CONFIG_INPUT_FF_MEMLESS is not set # CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set # # Userland interfaces @@ -357,27 +461,33 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # Input Device Drivers # CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set CONFIG_KEYBOARD_ATKBD=y -# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_QT2160 is not set # CONFIG_KEYBOARD_LKKBD is not set -# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set # CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set # CONFIG_KEYBOARD_STOWAWAY is not set -# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set CONFIG_INPUT_MOUSE=y CONFIG_MOUSE_PS2=y CONFIG_MOUSE_PS2_ALPS=y CONFIG_MOUSE_PS2_LOGIPS2PP=y CONFIG_MOUSE_PS2_SYNAPTICS=y -CONFIG_MOUSE_PS2_LIFEBOOK=y CONFIG_MOUSE_PS2_TRACKPOINT=y # CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set # CONFIG_MOUSE_PS2_TOUCHKIT is not set # CONFIG_MOUSE_SERIAL is not set # CONFIG_MOUSE_APPLETOUCH is not set # CONFIG_MOUSE_BCM5974 is not set # CONFIG_MOUSE_VSXXXAA is not set # CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set # CONFIG_INPUT_TOUCHSCREEN is not set @@ -390,6 +500,7 @@ CONFIG_SERIO=y CONFIG_SERIO_SERPORT=y CONFIG_SERIO_LIBPS2=y # CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set # CONFIG_GAMEPORT is not set # @@ -423,16 +534,18 @@ CONFIG_SERIAL_S3C6400=y CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_IPMI_HANDLER is not set CONFIG_HW_RANDOM=y -# CONFIG_NVRAM is not set +# CONFIG_HW_RANDOM_TIMERIOMEM is not set # CONFIG_R3964 is not set # CONFIG_RAW_DRIVER is not set # CONFIG_TCG_TPM is not set CONFIG_I2C=y CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_HELPER_AUTO=y @@ -443,6 +556,7 @@ CONFIG_I2C_HELPER_AUTO=y # # I2C system bus drivers (mostly embedded / system-on-chip) # +# CONFIG_I2C_DESIGNWARE is not set # CONFIG_I2C_GPIO is not set # CONFIG_I2C_OCORES is not set CONFIG_I2C_S3C2410=y @@ -463,32 +577,33 @@ CONFIG_I2C_S3C2410=y # # Miscellaneous I2C Chip support # -# CONFIG_DS1682 is not set -CONFIG_EEPROM_AT24=y -# CONFIG_EEPROM_LEGACY is not set -# CONFIG_SENSORS_PCF8574 is not set -# CONFIG_PCF8575 is not set -# CONFIG_SENSORS_PCA9539 is not set -# CONFIG_SENSORS_PCF8591 is not set -# CONFIG_TPS65010 is not set -# CONFIG_SENSORS_MAX6875 is not set # CONFIG_SENSORS_TSL2550 is not set # CONFIG_I2C_DEBUG_CORE is not set # CONFIG_I2C_DEBUG_ALGO is not set # CONFIG_I2C_DEBUG_BUS is not set # CONFIG_I2C_DEBUG_CHIP is not set # CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set CONFIG_ARCH_REQUIRE_GPIOLIB=y CONFIG_GPIOLIB=y # CONFIG_DEBUG_GPIO is not set # CONFIG_GPIO_SYSFS is not set # +# Memory mapped GPIO expanders: +# + +# # I2C GPIO expanders: # # CONFIG_GPIO_MAX732X is not set # CONFIG_GPIO_PCA953X is not set # CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_ADP5588 is not set # # PCI GPIO expanders: @@ -497,10 +612,19 @@ CONFIG_GPIOLIB=y # # SPI GPIO expanders: # + +# +# AC97 GPIO expanders: +# # CONFIG_W1 is not set # CONFIG_POWER_SUPPLY is not set CONFIG_HWMON=y # CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# # CONFIG_SENSORS_AD7414 is not set # CONFIG_SENSORS_AD7418 is not set # CONFIG_SENSORS_ADM1021 is not set @@ -509,17 +633,21 @@ CONFIG_HWMON=y # CONFIG_SENSORS_ADM1029 is not set # CONFIG_SENSORS_ADM1031 is not set # CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7462 is not set # CONFIG_SENSORS_ADT7470 is not set # CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ADT7475 is not set # CONFIG_SENSORS_ATXP1 is not set # CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_F71805F is not set # CONFIG_SENSORS_F71882FG is not set # CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set # CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_GL520SM is not set # CONFIG_SENSORS_IT87 is not set # CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM73 is not set # CONFIG_SENSORS_LM75 is not set # CONFIG_SENSORS_LM77 is not set # CONFIG_SENSORS_LM78 is not set @@ -530,16 +658,24 @@ CONFIG_HWMON=y # CONFIG_SENSORS_LM90 is not set # CONFIG_SENSORS_LM92 is not set # CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LM95241 is not set # CONFIG_SENSORS_MAX1619 is not set # CONFIG_SENSORS_MAX6650 is not set # CONFIG_SENSORS_PC87360 is not set # CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SHT15 is not set # CONFIG_SENSORS_DME1737 is not set # CONFIG_SENSORS_SMSC47M1 is not set # CONFIG_SENSORS_SMSC47M192 is not set # CONFIG_SENSORS_SMSC47B397 is not set # CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set # CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set # CONFIG_SENSORS_VT1211 is not set # CONFIG_SENSORS_W83781D is not set # CONFIG_SENSORS_W83791D is not set @@ -549,15 +685,14 @@ CONFIG_HWMON=y # CONFIG_SENSORS_W83L786NG is not set # CONFIG_SENSORS_W83627HF is not set # CONFIG_SENSORS_W83627EHF is not set -# CONFIG_HWMON_DEBUG_CHIP is not set +# CONFIG_SENSORS_LIS3_I2C is not set # CONFIG_THERMAL is not set -# CONFIG_THERMAL_HWMON is not set # CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y # # Sonics Silicon Backplane # -CONFIG_SSB_POSSIBLE=y # CONFIG_SSB is not set # @@ -568,28 +703,22 @@ CONFIG_SSB_POSSIBLE=y # CONFIG_MFD_ASIC3 is not set # CONFIG_HTC_EGPIO is not set # CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set # CONFIG_MFD_TMIO is not set # CONFIG_MFD_T7L66XB is not set # CONFIG_MFD_TC6387XB is not set # CONFIG_MFD_TC6393XB is not set # CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set # CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set # CONFIG_MFD_WM8350_I2C is not set - -# -# Multimedia devices -# - -# -# Multimedia core support -# -# CONFIG_VIDEO_DEV is not set -# CONFIG_VIDEO_MEDIA is not set - -# -# Multimedia drivers -# -# CONFIG_DAB is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_MFD_88PM8607 is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set # # Graphics support @@ -612,17 +741,15 @@ CONFIG_DUMMY_CONSOLE=y # CONFIG_SOUND is not set CONFIG_HID_SUPPORT=y CONFIG_HID=y -CONFIG_HID_DEBUG=y # CONFIG_HIDRAW is not set # CONFIG_HID_PID is not set # # Special HID drivers # -# CONFIG_HID_COMPAT is not set CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y -# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_OHCI=y # CONFIG_USB_ARCH_HAS_EHCI is not set # CONFIG_USB is not set @@ -631,9 +758,13 @@ CONFIG_USB_ARCH_HAS_HCD=y # # -# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may # # CONFIG_USB_GADGET is not set + +# +# OTG and related infrastructure +# CONFIG_MMC=y CONFIG_MMC_DEBUG=y CONFIG_MMC_UNSAFE_RESUME=y @@ -650,22 +781,24 @@ CONFIG_SDIO_UART=y # MMC/SD/SDIO Host Controller Drivers # CONFIG_MMC_SDHCI=y +# CONFIG_MMC_SDHCI_PLTFM is not set CONFIG_MMC_SDHCI_S3C=y +# CONFIG_MMC_SDHCI_S3C_DMA is not set +# CONFIG_MMC_AT91 is not set +# CONFIG_MMC_ATMELMCI is not set # CONFIG_MEMSTICK is not set -# CONFIG_ACCESSIBILITY is not set # CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set CONFIG_RTC_LIB=y # CONFIG_RTC_CLASS is not set # CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set # -# Voltage and Current regulators +# TI VLYNQ # -# CONFIG_REGULATOR is not set -# CONFIG_REGULATOR_FIXED_VOLTAGE is not set -# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set -# CONFIG_REGULATOR_BQ24022 is not set -# CONFIG_UIO is not set +# CONFIG_STAGING is not set # # File systems @@ -674,6 +807,7 @@ CONFIG_EXT2_FS=y # CONFIG_EXT2_FS_XATTR is not set # CONFIG_EXT2_FS_XIP is not set CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT3_FS_XATTR=y CONFIG_EXT3_FS_POSIX_ACL=y CONFIG_EXT3_FS_SECURITY=y @@ -683,9 +817,12 @@ CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set CONFIG_FS_POSIX_ACL=y -CONFIG_FILE_LOCKING=y # CONFIG_XFS_FS is not set # CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y CONFIG_DNOTIFY=y CONFIG_INOTIFY=y CONFIG_INOTIFY_USER=y @@ -696,6 +833,11 @@ CONFIG_INOTIFY_USER=y CONFIG_GENERIC_ACL=y # +# Caches +# +# CONFIG_FSCACHE is not set + +# # CD-ROM/DVD Filesystems # # CONFIG_ISO9660_FS is not set @@ -719,10 +861,7 @@ CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y # CONFIG_HUGETLB_PAGE is not set # CONFIG_CONFIGFS_FS is not set - -# -# Miscellaneous filesystems -# +CONFIG_MISC_FILESYSTEMS=y # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set @@ -731,12 +870,17 @@ CONFIG_TMPFS_POSIX_ACL=y # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set # CONFIG_VXFS_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_OMFS_FS is not set # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set CONFIG_ROMFS_FS=y +CONFIG_ROMFS_BACKED_BY_BLOCK=y +# CONFIG_ROMFS_BACKED_BY_MTD is not set +# CONFIG_ROMFS_BACKED_BY_BOTH is not set +CONFIG_ROMFS_ON_BLOCK=y # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set @@ -755,6 +899,7 @@ CONFIG_ENABLE_WARN_DEPRECATED=y CONFIG_ENABLE_MUST_CHECK=y CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set # CONFIG_UNUSED_SYMBOLS is not set # CONFIG_DEBUG_FS is not set # CONFIG_HEADERS_CHECK is not set @@ -763,12 +908,16 @@ CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_SOFTLOCKUP=y # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 CONFIG_SCHED_DEBUG=y # CONFIG_SCHEDSTATS is not set # CONFIG_TIMER_STATS is not set # CONFIG_DEBUG_OBJECTS is not set # CONFIG_SLUB_DEBUG_ON is not set # CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set CONFIG_DEBUG_RT_MUTEXES=y CONFIG_DEBUG_PI_LIST=y # CONFIG_RT_MUTEX_TESTER is not set @@ -787,34 +936,43 @@ CONFIG_DEBUG_INFO=y CONFIG_DEBUG_MEMORY_INIT=y # CONFIG_DEBUG_LIST is not set # CONFIG_DEBUG_SG is not set -CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set # CONFIG_BOOT_PRINTK_DELAY is not set # CONFIG_RCU_TORTURE_TEST is not set # CONFIG_RCU_CPU_STALL_DETECTOR is not set # CONFIG_BACKTRACE_SELF_TEST is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set # CONFIG_FAULT_INJECTION is not set # CONFIG_LATENCYTOP is not set CONFIG_SYSCTL_SYSCALL_CHECK=y +# CONFIG_PAGE_POISONING is not set CONFIG_HAVE_FUNCTION_TRACER=y - -# -# Tracers -# +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y # CONFIG_FUNCTION_TRACER is not set # CONFIG_SCHED_TRACER is not set -# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set # CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set # CONFIG_STACK_TRACER is not set -# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y CONFIG_DEBUG_USER=y CONFIG_DEBUG_ERRORS=y # CONFIG_DEBUG_STACK_USAGE is not set CONFIG_DEBUG_LL=y +# CONFIG_EARLY_PRINTK is not set # CONFIG_DEBUG_ICEDCC is not set +# CONFIG_OC_ETM is not set CONFIG_DEBUG_S3C_UART=0 # @@ -823,13 +981,19 @@ CONFIG_DEBUG_S3C_UART=0 # CONFIG_KEYS is not set # CONFIG_SECURITY is not set # CONFIG_SECURITYFS is not set -# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" # CONFIG_CRYPTO is not set +# CONFIG_BINARY_PRINTF is not set # # Library routines # CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y # CONFIG_CRC_CCITT is not set # CONFIG_CRC16 is not set # CONFIG_CRC_T10DIF is not set @@ -838,6 +1002,10 @@ CONFIG_CRC32=y # CONFIG_CRC7 is not set # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y -CONFIG_PLIST=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_LZO=y CONFIG_HAS_IOMEM=y CONFIG_HAS_DMA=y -- cgit v0.10.2 From 0647aee035245e0e5ea39ae17cf909b7099f988d Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 20 Jan 2010 15:09:40 +0900 Subject: ARM: S5P6440: Add HSMMC and OTG base addresses to map.h Add the HSOTG and HSMMC block addresses to the map.h file. Signed-off-by: Ben Dooks diff --git a/arch/arm/mach-s5p6440/include/mach/map.h b/arch/arm/mach-s5p6440/include/mach/map.h index dc922d6..4a73e73 100644 --- a/arch/arm/mach-s5p6440/include/mach/map.h +++ b/arch/arm/mach-s5p6440/include/mach/map.h @@ -66,6 +66,14 @@ #define S5P_PA_UART S5P6440_PA_UART #define S5P_VA_UART S3C_VA_UART +/* HS USB OtG */ +#define S5P6440_PA_HSOTG (0xED100000) + +/* HSMMC */ +#define S5P6440_PA_HSMMC0 (0xED800000) +#define S5P6440_PA_HSMMC1 (0xED900000) +#define S5P6440_PA_HSMMC2 (0xEDA00000) + #define S5P_PA_UART0 (S5P_PA_UART + 0x0) #define S5P_PA_UART1 (S5P_PA_UART + 0x400) #define S5P_PA_UART2 (S5P_PA_UART + 0x800) -- cgit v0.10.2 From 501d70383aa9ffc78b41aa7e74f6b0254c7c731c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 21 Sep 2009 09:30:09 +0200 Subject: arm/at91: Don't disable irqs in set_next_event and set_mode callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit on AT91 the timer irq is shared, so the handler might be entered without irqs being disabled. Though this should not happen as the timer irq is registered early, there have been some reports on the mailing list. To make debugging that problem easier next time it pops up a WARN_ON_ONCE is added to the handler if irqs are not off. Signed-off-by: Uwe Kleine-König diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c index 309f351..2500f41 100644 --- a/arch/arm/mach-at91/at91rm9200_time.c +++ b/arch/arm/mach-at91/at91rm9200_time.c @@ -58,6 +58,12 @@ static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) { u32 sr = at91_sys_read(AT91_ST_SR) & irqmask; + /* + * irqs should be disabled here, but as the irq is shared they are only + * guaranteed to be off if the timer irq is registered first. + */ + WARN_ON_ONCE(!irqs_disabled()); + /* simulate "oneshot" timer with alarm */ if (sr & AT91_ST_ALMS) { clkevt.event_handler(&clkevt); @@ -132,24 +138,11 @@ clkevt32k_mode(enum clock_event_mode mode, struct clock_event_device *dev) static int clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev) { - unsigned long flags; u32 alm; int status = 0; BUG_ON(delta < 2); - /* Use "raw" primitives so we behave correctly on RT kernels. */ - raw_local_irq_save(flags); - - /* - * According to Thomas Gleixner irqs are already disabled here. Simply - * removing raw_local_irq_save above (and the matching - * raw_local_irq_restore) was not accepted. See - * http://thread.gmane.org/gmane.linux.ports.arm.kernel/41174 - * So for now (2008-11-20) just warn once if irqs were not disabled ... - */ - WARN_ON_ONCE(!raw_irqs_disabled_flags(flags)); - /* The alarm IRQ uses absolute time (now+delta), not the relative * time (delta) in our calling convention. Like all clockevents * using such "match" hardware, we have a race to defend against. @@ -169,7 +162,6 @@ clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev) alm += delta; at91_sys_write(AT91_ST_RTAR, alm); - raw_local_irq_restore(flags); return status; } diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index 4bd56ae..608a632 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -62,16 +62,12 @@ static struct clocksource pit_clk = { static void pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) { - unsigned long flags; - switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - /* update clocksource counter, then enable the IRQ */ - raw_local_irq_save(flags); + /* update clocksource counter */ pit_cnt += pit_cycle * PIT_PICNT(at91_sys_read(AT91_PIT_PIVR)); at91_sys_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN); - raw_local_irq_restore(flags); break; case CLOCK_EVT_MODE_ONESHOT: BUG(); @@ -100,6 +96,11 @@ static struct clock_event_device pit_clkevt = { */ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) { + /* + * irqs should be disabled here, but as the irq is shared they are only + * guaranteed to be off if the timer irq is registered first. + */ + WARN_ON_ONCE(!irqs_disabled()); /* The PIT interrupt may be disabled, and is shared */ if ((pit_clkevt.mode == CLOCK_EVT_MODE_PERIODIC) -- cgit v0.10.2 From a602f0f2f04f150fa1f7312b9e601e8e1a5afe10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 17 Dec 2009 12:43:29 +0100 Subject: arm/{pxa,sa1100,nomadik}: Don't disable irqs in set_next_event and set_mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions are called with irqs already off. This commit removes the calls to raw_local_irq_save and raw_local_irq_restore on platforms that don't have to use a shared interrupt for their timekeeping. Signed-off-by: Uwe Kleine-König diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c index 750c448..293e40a 100644 --- a/arch/arm/mach-pxa/time.c +++ b/arch/arm/mach-pxa/time.c @@ -76,14 +76,12 @@ pxa_ost0_interrupt(int irq, void *dev_id) static int pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev) { - unsigned long flags, next, oscr; + unsigned long next, oscr; - raw_local_irq_save(flags); OIER |= OIER_E0; next = OSCR + delta; OSMR0 = next; oscr = OSCR; - raw_local_irq_restore(flags); return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; } @@ -91,23 +89,17 @@ pxa_osmr0_set_next_event(unsigned long delta, struct clock_event_device *dev) static void pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev) { - unsigned long irqflags; - switch (mode) { case CLOCK_EVT_MODE_ONESHOT: - raw_local_irq_save(irqflags); OIER &= ~OIER_E0; OSSR = OSSR_M0; - raw_local_irq_restore(irqflags); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: /* initializing, released, or preparing for suspend */ - raw_local_irq_save(irqflags); OIER &= ~OIER_E0; OSSR = OSSR_M0; - raw_local_irq_restore(irqflags); break; case CLOCK_EVT_MODE_RESUME: diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c index b9cbb56..74b6e0e 100644 --- a/arch/arm/mach-sa1100/time.c +++ b/arch/arm/mach-sa1100/time.c @@ -35,14 +35,12 @@ static irqreturn_t sa1100_ost0_interrupt(int irq, void *dev_id) static int sa1100_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c) { - unsigned long flags, next, oscr; + unsigned long next, oscr; - raw_local_irq_save(flags); OIER |= OIER_E0; next = OSCR + delta; OSMR0 = next; oscr = OSCR; - raw_local_irq_restore(flags); return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; } @@ -50,16 +48,12 @@ sa1100_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c) static void sa1100_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c) { - unsigned long flags; - switch (mode) { case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: - raw_local_irq_save(flags); OIER &= ~OIER_E0; OSSR = OSSR_M0; - raw_local_irq_restore(flags); break; case CLOCK_EVT_MODE_RESUME: diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c index 62f18ad..fa7cb3a 100644 --- a/arch/arm/plat-nomadik/timer.c +++ b/arch/arm/plat-nomadik/timer.c @@ -49,24 +49,17 @@ static struct clocksource nmdk_clksrc = { static void nmdk_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) { - unsigned long flags; - switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - /* enable interrupts -- and count current value? */ - raw_local_irq_save(flags); + /* count current value? */ writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC); - raw_local_irq_restore(flags); break; case CLOCK_EVT_MODE_ONESHOT: BUG(); /* Not supported, yet */ /* FALLTHROUGH */ case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_UNUSED: - /* disable irq */ - raw_local_irq_save(flags); writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC); - raw_local_irq_restore(flags); break; case CLOCK_EVT_MODE_RESUME: break; -- cgit v0.10.2 From ec7384ec23dc5a9ea8733e90438e16b6066bfe1b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 20 Jan 2010 15:16:41 -0800 Subject: ceph: remove duplicate variable initialization The variable client is initialized twice to the same (side effect-free) expression. Drop one initialization. A simplified version of the semantic match that finds this problem is: (http://coccinelle.lip6.fr/) // @forall@ idexpression *x; identifier f!=ERR_PTR; @@ x = f(...) ... when != x ( x = f(...,<+...x...+>,...) | * x = f(...) ) // Signed-off-by: Julia Lawall Signed-off-by: Sage Weil diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index a6850a1..a3bd9de 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -596,7 +596,7 @@ static int ceph_writepages_start(struct address_space *mapping, struct inode *inode = mapping->host; struct backing_dev_info *bdi = mapping->backing_dev_info; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_client *client = ceph_inode_to_client(inode); + struct ceph_client *client; pgoff_t index, start, end; int range_whole = 0; int should_loop = 1; -- cgit v0.10.2 From 3ea25f9441fc0951ada649105f2c57a59536b539 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 25 Jan 2010 11:18:17 -0800 Subject: ceph: mark MDS CREATE as a write op CEPH_MDS_OP_CREATE was not correctly marked as a write operation. Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index d0f2557..d8923fe 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -39,7 +39,7 @@ #define CEPH_MDS_PROTOCOL 9 /* cluster internal */ #define CEPH_MON_PROTOCOL 5 /* cluster internal */ #define CEPH_OSDC_PROTOCOL 22 /* server/client */ -#define CEPH_MDSC_PROTOCOL 31 /* server/client */ +#define CEPH_MDSC_PROTOCOL 32 /* server/client */ #define CEPH_MONC_PROTOCOL 15 /* server/client */ @@ -271,7 +271,7 @@ enum { CEPH_MDS_OP_RMDIR = 0x01221, CEPH_MDS_OP_SYMLINK = 0x01222, - CEPH_MDS_OP_CREATE = 0x00301, + CEPH_MDS_OP_CREATE = 0x01301, CEPH_MDS_OP_OPEN = 0x00302, CEPH_MDS_OP_READDIR = 0x00305, -- cgit v0.10.2 From 5b1daecd59f95eb24dc629407ed80369c9929520 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 25 Jan 2010 11:33:08 -0800 Subject: ceph: properly handle aborted mds requests Previously, if the MDS request was interrupted, we would unregister the request and ignore any reply. This could cause the caps or other cache state to become out of sync. (For instance, aborting dbench and doing rm -r on clients would complain about a non-empty directory because the client didn't realize it's aborted file create request completed.) Even we don't unregister, we still can't process the reply normally because we are no longer holding the caller's locks (like the dir i_mutex). So, mark aborted operations with r_aborted, and in the reply handler, be sure to process all the caps. Do not process the namespace changes, though, since we no longer will hold the dir i_mutex. The dentry lease state can also be ignored as it's more forgiving. Signed-off-by: Sage Weil diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 518beb6..71e107f 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -915,6 +915,16 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, } if (rinfo->head->is_dentry) { + struct inode *dir = req->r_locked_dir; + + err = fill_inode(dir, &rinfo->diri, rinfo->dirfrag, + session, req->r_request_started, -1, + &req->r_caps_reservation); + if (err < 0) + return err; + } + + if (rinfo->head->is_dentry && !req->r_aborted) { /* * lookup link rename : null -> possibly existing inode * mknod symlink mkdir : null -> new inode @@ -932,12 +942,6 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, BUG_ON(ceph_snap(dir) != le64_to_cpu(rinfo->diri.in->snapid)); - err = fill_inode(dir, &rinfo->diri, rinfo->dirfrag, - session, req->r_request_started, -1, - &req->r_caps_reservation); - if (err < 0) - return err; - /* do we have a lease on the whole dir? */ have_dir_cap = (le32_to_cpu(rinfo->diri.in->cap.caps) & diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 6e08f48..623c67c 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1624,11 +1624,29 @@ int ceph_mdsc_do_request(struct ceph_mds_client *mdsc, err = PTR_ERR(req->r_reply); req->r_reply = NULL; - /* clean up */ - __unregister_request(mdsc, req); - if (!list_empty(&req->r_unsafe_item)) - list_del_init(&req->r_unsafe_item); - complete(&req->r_safe_completion); + if (err == -ERESTARTSYS) { + /* aborted */ + req->r_aborted = true; + + if (req->r_locked_dir && + (req->r_op & CEPH_MDS_OP_WRITE)) { + struct ceph_inode_info *ci = + ceph_inode(req->r_locked_dir); + + dout("aborted, clearing I_COMPLETE on %p\n", + req->r_locked_dir); + spin_lock(&req->r_locked_dir->i_lock); + ci->i_ceph_flags &= ~CEPH_I_COMPLETE; + ci->i_release_count++; + spin_unlock(&req->r_locked_dir->i_lock); + } + } else { + /* clean up this request */ + __unregister_request(mdsc, req); + if (!list_empty(&req->r_unsafe_item)) + list_del_init(&req->r_unsafe_item); + complete(&req->r_safe_completion); + } } else if (req->r_err) { err = req->r_err; } else { diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index b1c2025..ee71495 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -188,6 +188,7 @@ struct ceph_mds_request { struct ceph_msg *r_reply; struct ceph_mds_reply_info_parsed r_reply_info; int r_err; + bool r_aborted; unsigned long r_timeout; /* optional. jiffies */ unsigned long r_started; /* start time to measure timeout against */ -- cgit v0.10.2 From 2450418c47b7998ad55a73f23707b1e21c371eef Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Fri, 8 Jan 2010 13:58:34 -0800 Subject: ceph: allocate middle of message before stating to read Both front and middle parts of the message are now being allocated at the ceph_alloc_msg(). Signed-off-by: Yehuda Sadeh diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 623c67c..93998a0 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2953,8 +2953,6 @@ const static struct ceph_connection_operations mds_con_ops = { .get_authorizer = get_authorizer, .verify_authorizer_reply = verify_authorizer_reply, .peer_reset = peer_reset, - .alloc_msg = ceph_alloc_msg, - .alloc_middle = ceph_alloc_middle, }; diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 1360708..25de15c 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1279,8 +1279,34 @@ static void process_ack(struct ceph_connection *con) +static int read_partial_message_section(struct ceph_connection *con, + struct kvec *section, unsigned int sec_len, + u32 *crc) +{ + int left; + int ret; + + BUG_ON(!section); + + while (section->iov_len < sec_len) { + BUG_ON(section->iov_base == NULL); + left = sec_len - section->iov_len; + ret = ceph_tcp_recvmsg(con->sock, (char *)section->iov_base + + section->iov_len, left); + if (ret <= 0) + return ret; + section->iov_len += ret; + if (section->iov_len == sec_len) + *crc = crc32c(0, section->iov_base, + section->iov_len); + } + return 1; +} +static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, + struct ceph_msg_header *hdr, + int *skip); /* * read (part of) a message. */ @@ -1292,6 +1318,7 @@ static int read_partial_message(struct ceph_connection *con) int to, want, left; unsigned front_len, middle_len, data_len, data_off; int datacrc = con->msgr->nocrc; + int skip; dout("read_partial_message con %p msg %p\n", con, m); @@ -1315,7 +1342,6 @@ static int read_partial_message(struct ceph_connection *con) } } } - front_len = le32_to_cpu(con->in_hdr.front_len); if (front_len > CEPH_MSG_MAX_FRONT_LEN) return -EIO; @@ -1330,8 +1356,8 @@ static int read_partial_message(struct ceph_connection *con) if (!con->in_msg) { dout("got hdr type %d front %d data %d\n", con->in_hdr.type, con->in_hdr.front_len, con->in_hdr.data_len); - con->in_msg = con->ops->alloc_msg(con, &con->in_hdr); - if (!con->in_msg) { + con->in_msg = ceph_alloc_msg(con, &con->in_hdr, &skip); + if (skip) { /* skip this message */ pr_err("alloc_msg returned NULL, skipping message\n"); con->in_base_pos = -front_len - middle_len - data_len - @@ -1342,56 +1368,28 @@ static int read_partial_message(struct ceph_connection *con) if (IS_ERR(con->in_msg)) { ret = PTR_ERR(con->in_msg); con->in_msg = NULL; - con->error_msg = "out of memory for incoming message"; + con->error_msg = "error allocating memory for incoming message"; return ret; } m = con->in_msg; m->front.iov_len = 0; /* haven't read it yet */ + if (m->middle) + m->middle->vec.iov_len = 0; memcpy(&m->hdr, &con->in_hdr, sizeof(con->in_hdr)); } /* front */ - while (m->front.iov_len < front_len) { - BUG_ON(m->front.iov_base == NULL); - left = front_len - m->front.iov_len; - ret = ceph_tcp_recvmsg(con->sock, (char *)m->front.iov_base + - m->front.iov_len, left); - if (ret <= 0) - return ret; - m->front.iov_len += ret; - if (m->front.iov_len == front_len) - con->in_front_crc = crc32c(0, m->front.iov_base, - m->front.iov_len); - } + ret = read_partial_message_section(con, &m->front, front_len, + &con->in_front_crc); + if (ret <= 0) + return ret; /* middle */ - while (middle_len > 0 && (!m->middle || - m->middle->vec.iov_len < middle_len)) { - if (m->middle == NULL) { - ret = -EOPNOTSUPP; - if (con->ops->alloc_middle) - ret = con->ops->alloc_middle(con, m); - if (ret < 0) { - pr_err("alloc_middle fail skipping payload\n"); - con->in_base_pos = -middle_len - data_len - - sizeof(m->footer); - ceph_msg_put(con->in_msg); - con->in_msg = NULL; - con->in_tag = CEPH_MSGR_TAG_READY; - return 0; - } - m->middle->vec.iov_len = 0; - } - left = middle_len - m->middle->vec.iov_len; - ret = ceph_tcp_recvmsg(con->sock, - (char *)m->middle->vec.iov_base + - m->middle->vec.iov_len, left); + if (m->middle) { + ret = read_partial_message_section(con, &m->middle->vec, middle_len, + &con->in_middle_crc); if (ret <= 0) return ret; - m->middle->vec.iov_len += ret; - if (m->middle->vec.iov_len == middle_len) - con->in_middle_crc = crc32c(0, m->middle->vec.iov_base, - m->middle->vec.iov_len); } /* (page) data */ @@ -2116,31 +2114,13 @@ out: } /* - * Generic message allocator, for incoming messages. - */ -struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, - struct ceph_msg_header *hdr) -{ - int type = le16_to_cpu(hdr->type); - int front_len = le32_to_cpu(hdr->front_len); - struct ceph_msg *msg = ceph_msg_new(type, front_len, 0, 0, NULL); - - if (!msg) { - pr_err("unable to allocate msg type %d len %d\n", - type, front_len); - return ERR_PTR(-ENOMEM); - } - return msg; -} - -/* * Allocate "middle" portion of a message, if it is needed and wasn't * allocated by alloc_msg. This allows us to read a small fixed-size * per-type header in the front and then gracefully fail (i.e., * propagate the error to the caller based on info in the front) when * the middle is too large. */ -int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg) +static int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg) { int type = le16_to_cpu(msg->hdr.type); int middle_len = le32_to_cpu(msg->hdr.middle_len); @@ -2156,6 +2136,48 @@ int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg) return 0; } +/* + * Generic message allocator, for incoming messages. + */ +static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, + struct ceph_msg_header *hdr, + int *skip) +{ + int type = le16_to_cpu(hdr->type); + int front_len = le32_to_cpu(hdr->front_len); + int middle_len = le32_to_cpu(hdr->middle_len); + struct ceph_msg *msg = NULL; + int ret; + + if (con->ops->alloc_msg) { + msg = con->ops->alloc_msg(con, hdr, skip); + if (IS_ERR(msg)) + return msg; + + if (*skip) + return NULL; + } + if (!msg) { + *skip = 0; + msg = ceph_msg_new(type, front_len, 0, 0, NULL); + if (!msg) { + pr_err("unable to allocate msg type %d len %d\n", + type, front_len); + return ERR_PTR(-ENOMEM); + } + } + + if (middle_len) { + ret = ceph_alloc_middle(con, msg); + + if (ret < 0) { + ceph_msg_put(msg); + return msg; + } + } + return msg; +} + /* * Free a generically kmalloc'd message. diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index a7b6841..b6bec590 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -44,9 +44,8 @@ struct ceph_connection_operations { void (*peer_reset) (struct ceph_connection *con); struct ceph_msg * (*alloc_msg) (struct ceph_connection *con, - struct ceph_msg_header *hdr); - int (*alloc_middle) (struct ceph_connection *con, - struct ceph_msg *msg); + struct ceph_msg_header *hdr, + int *skip); /* an incoming message has a data payload; tell me what pages I * should read the data into. */ int (*prepare_pages) (struct ceph_connection *con, struct ceph_msg *m, @@ -242,10 +241,6 @@ extern struct ceph_msg *ceph_msg_new(int type, int front_len, struct page **pages); extern void ceph_msg_kfree(struct ceph_msg *m); -extern struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, - struct ceph_msg_header *hdr); -extern int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg); - static inline struct ceph_msg *ceph_msg_get(struct ceph_msg *msg) { diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 223e8bc..6c00b37 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -692,21 +692,33 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) * Allocate memory for incoming message */ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, - struct ceph_msg_header *hdr) + struct ceph_msg_header *hdr, + int *skip) { struct ceph_mon_client *monc = con->private; int type = le16_to_cpu(hdr->type); - int front = le32_to_cpu(hdr->front_len); + int front_len = le32_to_cpu(hdr->front_len); + struct ceph_msg *m; + *skip = 0; switch (type) { case CEPH_MSG_MON_SUBSCRIBE_ACK: - return ceph_msgpool_get(&monc->msgpool_subscribe_ack, front); + m = ceph_msgpool_get(&monc->msgpool_subscribe_ack, front_len); + break; case CEPH_MSG_STATFS_REPLY: - return ceph_msgpool_get(&monc->msgpool_statfs_reply, front); + m = ceph_msgpool_get(&monc->msgpool_statfs_reply, front_len); + break; case CEPH_MSG_AUTH_REPLY: - return ceph_msgpool_get(&monc->msgpool_auth_reply, front); + m = ceph_msgpool_get(&monc->msgpool_auth_reply, front_len); + break; + default: + return NULL; } - return ceph_alloc_msg(con, hdr); + + if (!m) + *skip = 1; + + return m; } /* @@ -749,5 +761,4 @@ const static struct ceph_connection_operations mon_con_ops = { .dispatch = dispatch, .fault = mon_fault, .alloc_msg = mon_alloc_msg, - .alloc_middle = ceph_alloc_middle, }; diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 8417e21..545e936 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -1304,18 +1304,28 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) } static struct ceph_msg *alloc_msg(struct ceph_connection *con, - struct ceph_msg_header *hdr) + struct ceph_msg_header *hdr, + int *skip) { struct ceph_osd *osd = con->private; struct ceph_osd_client *osdc = osd->o_osdc; int type = le16_to_cpu(hdr->type); int front = le32_to_cpu(hdr->front_len); + struct ceph_msg *m; + *skip = 0; switch (type) { case CEPH_MSG_OSD_OPREPLY: - return ceph_msgpool_get(&osdc->msgpool_op_reply, front); + m = ceph_msgpool_get(&osdc->msgpool_op_reply, front); + break; + default: + return NULL; } - return ceph_alloc_msg(con, hdr); + + if (!m) + *skip = 1; + + return m; } /* @@ -1390,6 +1400,5 @@ const static struct ceph_connection_operations osd_con_ops = { .verify_authorizer_reply = verify_authorizer_reply, .alloc_msg = alloc_msg, .fault = osd_reset, - .alloc_middle = ceph_alloc_middle, .prepare_pages = prepare_pages, }; -- cgit v0.10.2 From 9d7f0f139edfdce1a1539b100c617fd9182b0829 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Mon, 11 Jan 2010 10:32:02 -0800 Subject: ceph: refactor messages data section allocation Signed-off-by: Yehuda Sadeh diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 25de15c..e8742cc 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1315,7 +1315,7 @@ static int read_partial_message(struct ceph_connection *con) struct ceph_msg *m = con->in_msg; void *p; int ret; - int to, want, left; + int to, left; unsigned front_len, middle_len, data_len, data_off; int datacrc = con->msgr->nocrc; int skip; @@ -1351,6 +1351,7 @@ static int read_partial_message(struct ceph_connection *con) data_len = le32_to_cpu(con->in_hdr.data_len); if (data_len > CEPH_MSG_MAX_DATA_LEN) return -EIO; + data_off = le16_to_cpu(con->in_hdr.data_off); /* allocate message? */ if (!con->in_msg) { @@ -1375,7 +1376,10 @@ static int read_partial_message(struct ceph_connection *con) m->front.iov_len = 0; /* haven't read it yet */ if (m->middle) m->middle->vec.iov_len = 0; - memcpy(&m->hdr, &con->in_hdr, sizeof(con->in_hdr)); + + con->in_msg_pos.page = 0; + con->in_msg_pos.page_pos = data_off & ~PAGE_MASK; + con->in_msg_pos.data_pos = 0; } /* front */ @@ -1393,31 +1397,6 @@ static int read_partial_message(struct ceph_connection *con) } /* (page) data */ - data_off = le16_to_cpu(m->hdr.data_off); - if (data_len == 0) - goto no_data; - - if (m->nr_pages == 0) { - con->in_msg_pos.page = 0; - con->in_msg_pos.page_pos = data_off & ~PAGE_MASK; - con->in_msg_pos.data_pos = 0; - /* find pages for data payload */ - want = calc_pages_for(data_off & ~PAGE_MASK, data_len); - ret = -1; - mutex_unlock(&con->mutex); - if (con->ops->prepare_pages) - ret = con->ops->prepare_pages(con, m, want); - mutex_lock(&con->mutex); - if (ret < 0) { - dout("%p prepare_pages failed, skipping payload\n", m); - con->in_base_pos = -data_len - sizeof(m->footer); - ceph_msg_put(con->in_msg); - con->in_msg = NULL; - con->in_tag = CEPH_MSGR_TAG_READY; - return 0; - } - BUG_ON(m->nr_pages < want); - } while (con->in_msg_pos.data_pos < data_len) { left = min((int)(data_len - con->in_msg_pos.data_pos), (int)(PAGE_SIZE - con->in_msg_pos.page_pos)); @@ -1440,7 +1419,6 @@ static int read_partial_message(struct ceph_connection *con) } } -no_data: /* footer */ to = sizeof(m->hdr) + sizeof(m->footer); while (con->in_base_pos < to) { @@ -2136,6 +2114,25 @@ static int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg) return 0; } +static int ceph_alloc_data_section(struct ceph_connection *con, struct ceph_msg *msg) +{ + int ret; + int want; + int data_len = le32_to_cpu(msg->hdr.data_len); + unsigned data_off = le16_to_cpu(msg->hdr.data_off); + + want = calc_pages_for(data_off & ~PAGE_MASK, data_len); + ret = -1; + mutex_unlock(&con->mutex); + if (con->ops->prepare_pages) + ret = con->ops->prepare_pages(con, msg, want); + mutex_lock(&con->mutex); + + BUG_ON(msg->nr_pages < want); + + return ret; +} + /* * Generic message allocator, for incoming messages. */ @@ -2146,6 +2143,7 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, int type = le16_to_cpu(hdr->type); int front_len = le32_to_cpu(hdr->front_len); int middle_len = le32_to_cpu(hdr->middle_len); + int data_len = le32_to_cpu(hdr->data_len); struct ceph_msg *msg = NULL; int ret; @@ -2166,6 +2164,7 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, return ERR_PTR(-ENOMEM); } } + memcpy(&msg->hdr, &con->in_hdr, sizeof(con->in_hdr)); if (middle_len) { ret = ceph_alloc_middle(con, msg); @@ -2175,6 +2174,18 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, return msg; } } + + if (data_len) { + ret = ceph_alloc_data_section(con, msg); + + if (ret < 0) { + *skip = 1; + ceph_msg_put(msg); + return NULL; + } + } + + return msg; } -- cgit v0.10.2 From 0547a9b30a5ac8680325752b61d3ffa9d4971b6e Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Mon, 11 Jan 2010 14:47:13 -0800 Subject: ceph: alloc message data pages and check if tid exists Now doing it in the same callback that is also responsible for allocating the 'front' part of the message. If we get a message that we haven't got a corresponding tid for, mark it for skipping. Moving the mutex unlock/lock from the osd alloc_msg callback to the calling function in the messenger. Signed-off-by: Yehuda Sadeh diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index e8742cc..f708803 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -2114,25 +2114,6 @@ static int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg) return 0; } -static int ceph_alloc_data_section(struct ceph_connection *con, struct ceph_msg *msg) -{ - int ret; - int want; - int data_len = le32_to_cpu(msg->hdr.data_len); - unsigned data_off = le16_to_cpu(msg->hdr.data_off); - - want = calc_pages_for(data_off & ~PAGE_MASK, data_len); - ret = -1; - mutex_unlock(&con->mutex); - if (con->ops->prepare_pages) - ret = con->ops->prepare_pages(con, msg, want); - mutex_lock(&con->mutex); - - BUG_ON(msg->nr_pages < want); - - return ret; -} - /* * Generic message allocator, for incoming messages. */ @@ -2143,12 +2124,13 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, int type = le16_to_cpu(hdr->type); int front_len = le32_to_cpu(hdr->front_len); int middle_len = le32_to_cpu(hdr->middle_len); - int data_len = le32_to_cpu(hdr->data_len); struct ceph_msg *msg = NULL; int ret; if (con->ops->alloc_msg) { + mutex_unlock(&con->mutex); msg = con->ops->alloc_msg(con, hdr, skip); + mutex_lock(&con->mutex); if (IS_ERR(msg)) return msg; @@ -2175,17 +2157,6 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, } } - if (data_len) { - ret = ceph_alloc_data_section(con, msg); - - if (ret < 0) { - *skip = 1; - ceph_msg_put(msg); - return NULL; - } - } - - return msg; } diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index b6bec590..dca2d32 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -46,10 +46,6 @@ struct ceph_connection_operations { struct ceph_msg * (*alloc_msg) (struct ceph_connection *con, struct ceph_msg_header *hdr, int *skip); - /* an incoming message has a data payload; tell me what pages I - * should read the data into. */ - int (*prepare_pages) (struct ceph_connection *con, struct ceph_msg *m, - int want); }; extern const char *ceph_name_type_str(int t); diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 6c00b37..3f7ae7f 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -701,6 +701,7 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, struct ceph_msg *m; *skip = 0; + switch (type) { case CEPH_MSG_MON_SUBSCRIBE_ACK: m = ceph_msgpool_get(&monc->msgpool_subscribe_ack, front_len); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 545e936..44abe29 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -998,31 +998,26 @@ bad: * find those pages. * 0 = success, -1 failure. */ -static int prepare_pages(struct ceph_connection *con, struct ceph_msg *m, - int want) +static int prepare_pages(struct ceph_connection *con, + struct ceph_msg_header *hdr, + struct ceph_osd_request *req, + u64 tid, + struct ceph_msg *m) { struct ceph_osd *osd = con->private; struct ceph_osd_client *osdc; - struct ceph_osd_request *req; - u64 tid; int ret = -1; - int type = le16_to_cpu(m->hdr.type); + int data_len = le32_to_cpu(hdr->data_len); + unsigned data_off = le16_to_cpu(hdr->data_off); + + int want = calc_pages_for(data_off & ~PAGE_MASK, data_len); if (!osd) return -1; + osdc = osd->o_osdc; dout("prepare_pages on msg %p want %d\n", m, want); - if (unlikely(type != CEPH_MSG_OSD_OPREPLY)) - return -1; /* hmm! */ - - tid = le64_to_cpu(m->hdr.tid); - mutex_lock(&osdc->request_mutex); - req = __lookup_request(osdc, tid); - if (!req) { - dout("prepare_pages unknown tid %llu\n", tid); - goto out; - } dout("prepare_pages tid %llu has %d pages, want %d\n", tid, req->r_num_pages, want); if (unlikely(req->r_num_pages < want)) @@ -1040,7 +1035,8 @@ static int prepare_pages(struct ceph_connection *con, struct ceph_msg *m, m->nr_pages = req->r_num_pages; ret = 0; /* success */ out: - mutex_unlock(&osdc->request_mutex); + BUG_ON(ret < 0 || m->nr_pages < want); + return ret; } @@ -1311,19 +1307,42 @@ static struct ceph_msg *alloc_msg(struct ceph_connection *con, struct ceph_osd_client *osdc = osd->o_osdc; int type = le16_to_cpu(hdr->type); int front = le32_to_cpu(hdr->front_len); + int data_len = le32_to_cpu(hdr->data_len); struct ceph_msg *m; + struct ceph_osd_request *req; + u64 tid; + int err; *skip = 0; - switch (type) { - case CEPH_MSG_OSD_OPREPLY: - m = ceph_msgpool_get(&osdc->msgpool_op_reply, front); - break; - default: + if (type != CEPH_MSG_OSD_OPREPLY) return NULL; - } - if (!m) + tid = le64_to_cpu(hdr->tid); + mutex_lock(&osdc->request_mutex); + req = __lookup_request(osdc, tid); + if (!req) { + *skip = 1; + m = NULL; + dout("prepare_pages unknown tid %llu\n", tid); + goto out; + } + m = ceph_msgpool_get(&osdc->msgpool_op_reply, front); + if (!m) { *skip = 1; + goto out; + } + + if (data_len > 0) { + err = prepare_pages(con, hdr, req, tid, m); + if (err < 0) { + *skip = 1; + ceph_msg_put(m); + m = ERR_PTR(err); + } + } + +out: + mutex_unlock(&osdc->request_mutex); return m; } @@ -1400,5 +1419,4 @@ const static struct ceph_connection_operations osd_con_ops = { .verify_authorizer_reply = verify_authorizer_reply, .alloc_msg = alloc_msg, .fault = osd_reset, - .prepare_pages = prepare_pages, }; -- cgit v0.10.2 From 0d59ab81c3d3adf466c3fd37d7fb6d46b05d1fd4 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Wed, 13 Jan 2010 17:03:23 -0800 Subject: ceph: keep reserved replies on the request structure This includes treating all the data preallocation and revokation at the same place, not having to have a special case for the reserved pages. Signed-off-by: Yehuda Sadeh diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index f708803..81bc779 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1985,30 +1985,30 @@ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) } /* - * Revoke a page vector that we may be reading data into + * Revoke a message that we may be reading data into */ -void ceph_con_revoke_pages(struct ceph_connection *con, struct page **pages) +void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg) { mutex_lock(&con->mutex); - if (con->in_msg && con->in_msg->pages == pages) { + if (con->in_msg && con->in_msg == msg) { + unsigned front_len = le32_to_cpu(con->in_hdr.front_len); + unsigned middle_len = le32_to_cpu(con->in_hdr.middle_len); unsigned data_len = le32_to_cpu(con->in_hdr.data_len); /* skip rest of message */ - dout("con_revoke_pages %p msg %p pages %p revoked\n", con, - con->in_msg, pages); - if (con->in_msg_pos.data_pos < data_len) - con->in_base_pos = con->in_msg_pos.data_pos - data_len; - else + dout("con_revoke_pages %p msg %p revoked\n", con, msg); con->in_base_pos = con->in_base_pos - sizeof(struct ceph_msg_header) - + front_len - + middle_len - + data_len - sizeof(struct ceph_msg_footer); - con->in_msg->pages = NULL; ceph_msg_put(con->in_msg); con->in_msg = NULL; con->in_tag = CEPH_MSGR_TAG_READY; } else { dout("con_revoke_pages %p msg %p pages %p no-op\n", - con, con->in_msg, pages); + con, con->in_msg, msg); } mutex_unlock(&con->mutex); } diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index dca2d32..c26a3d8 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -226,8 +226,8 @@ extern void ceph_con_open(struct ceph_connection *con, extern void ceph_con_close(struct ceph_connection *con); extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg); extern void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg); -extern void ceph_con_revoke_pages(struct ceph_connection *con, - struct page **pages); +extern void ceph_con_revoke_message(struct ceph_connection *con, + struct ceph_msg *msg); extern void ceph_con_keepalive(struct ceph_connection *con); extern struct ceph_connection *ceph_con_get(struct ceph_connection *con); extern void ceph_con_put(struct ceph_connection *con); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 44abe29..df21068 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -13,6 +13,8 @@ #include "decode.h" #include "auth.h" +#define OSD_REPLY_RESERVE_FRONT_LEN 512 + const static struct ceph_connection_operations osd_con_ops; static void kick_requests(struct ceph_osd_client *osdc, struct ceph_osd *osd); @@ -73,6 +75,16 @@ static void calc_layout(struct ceph_osd_client *osdc, req->r_oid, req->r_oid_len, objoff, objlen, req->r_num_pages); } +static void remove_replies(struct ceph_osd_request *req) +{ + int i; + int max = ARRAY_SIZE(req->replies); + + for (i=0; ireplies[i]) + ceph_msg_put(req->replies[i]); + } +} /* * requests @@ -87,12 +99,13 @@ void ceph_osdc_release_request(struct kref *kref) ceph_msg_put(req->r_request); if (req->r_reply) ceph_msg_put(req->r_reply); - if (req->r_con_filling_pages) { + remove_replies(req); + if (req->r_con_filling_msg) { dout("release_request revoking pages %p from con %p\n", - req->r_pages, req->r_con_filling_pages); - ceph_con_revoke_pages(req->r_con_filling_pages, - req->r_pages); - ceph_con_put(req->r_con_filling_pages); + req->r_pages, req->r_con_filling_msg); + ceph_con_revoke_message(req->r_con_filling_msg, + req->r_reply); + ceph_con_put(req->r_con_filling_msg); } if (req->r_own_pages) ceph_release_page_vector(req->r_pages, @@ -104,6 +117,60 @@ void ceph_osdc_release_request(struct kref *kref) kfree(req); } +static int alloc_replies(struct ceph_osd_request *req, int num_reply) +{ + int i; + int max = ARRAY_SIZE(req->replies); + + BUG_ON(num_reply > max); + + for (i=0; ireplies[i] = ceph_msg_new(0, OSD_REPLY_RESERVE_FRONT_LEN, 0, 0, NULL); + if (IS_ERR(req->replies[i])) { + int j; + int err = PTR_ERR(req->replies[i]); + for (j = 0; j<=i; j++) { + ceph_msg_put(req->replies[j]); + } + return err; + } + } + + for (; ireplies[i] = NULL; + } + + req->cur_reply = 0; + + return 0; +} + +static struct ceph_msg *__get_next_reply(struct ceph_connection *con, + struct ceph_osd_request *req, + int front_len) +{ + struct ceph_msg *reply; + if (req->r_con_filling_msg) { + dout("revoking reply msg %p from old con %p\n", req->r_reply, + req->r_con_filling_msg); + ceph_con_revoke_message(req->r_con_filling_msg, req->r_reply); + ceph_con_put(req->r_con_filling_msg); + req->cur_reply = 0; + } + reply = req->replies[req->cur_reply]; + if (!reply || front_len > OSD_REPLY_RESERVE_FRONT_LEN) { + /* maybe we can allocate it now? */ + reply = ceph_msg_new(0, front_len, 0, 0, NULL); + if (!reply || IS_ERR(reply)) { + pr_err(" reply alloc failed, front_len=%d\n", front_len); + return ERR_PTR(-ENOMEM); + } + } + req->r_con_filling_msg = ceph_con_get(con); + req->r_reply = ceph_msg_get(reply); /* for duration of read over socket */ + return ceph_msg_get(reply); +} + /* * build new request AND message, calculate layout, and adjust file * extent as needed. @@ -147,7 +214,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, if (req == NULL) return ERR_PTR(-ENOMEM); - err = ceph_msgpool_resv(&osdc->msgpool_op_reply, num_reply); + err = alloc_replies(req, num_reply); if (err) { ceph_osdc_put_request(req); return ERR_PTR(-ENOMEM); @@ -173,7 +240,6 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, else msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, 0, 0, NULL); if (IS_ERR(msg)) { - ceph_msgpool_resv(&osdc->msgpool_op_reply, -num_reply); ceph_osdc_put_request(req); return ERR_PTR(PTR_ERR(msg)); } @@ -471,8 +537,6 @@ static void __unregister_request(struct ceph_osd_client *osdc, rb_erase(&req->r_node, &osdc->requests); osdc->num_requests--; - ceph_msgpool_resv(&osdc->msgpool_op_reply, -req->r_num_prealloc_reply); - if (req->r_osd) { /* make sure the original request isn't in flight. */ ceph_con_revoke(&req->r_osd->o_con, req->r_request); @@ -724,12 +788,12 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, flags = le32_to_cpu(rhead->flags); /* - * if this connection filled our pages, drop our reference now, to + * if this connection filled our message, drop our reference now, to * avoid a (safe but slower) revoke later. */ - if (req->r_con_filling_pages == con && req->r_pages == msg->pages) { - dout(" got pages, dropping con_filling_pages ref %p\n", con); - req->r_con_filling_pages = NULL; + if (req->r_con_filling_msg == con && req->r_reply == msg) { + dout(" got pages, dropping con_filling_msg ref %p\n", con); + req->r_con_filling_msg = NULL; ceph_con_put(con); } @@ -998,7 +1062,7 @@ bad: * find those pages. * 0 = success, -1 failure. */ -static int prepare_pages(struct ceph_connection *con, +static int __prepare_pages(struct ceph_connection *con, struct ceph_msg_header *hdr, struct ceph_osd_request *req, u64 tid, @@ -1017,20 +1081,10 @@ static int prepare_pages(struct ceph_connection *con, osdc = osd->o_osdc; - dout("prepare_pages on msg %p want %d\n", m, want); - dout("prepare_pages tid %llu has %d pages, want %d\n", + dout("__prepare_pages on msg %p tid %llu, has %d pages, want %d\n", m, tid, req->r_num_pages, want); if (unlikely(req->r_num_pages < want)) goto out; - - if (req->r_con_filling_pages) { - dout("revoking pages %p from old con %p\n", req->r_pages, - req->r_con_filling_pages); - ceph_con_revoke_pages(req->r_con_filling_pages, req->r_pages); - ceph_con_put(req->r_con_filling_pages); - } - req->r_con_filling_pages = ceph_con_get(con); - req->r_reply = ceph_msg_get(m); /* for duration of read over socket */ m->pages = req->r_pages; m->nr_pages = req->r_num_pages; ret = 0; /* success */ @@ -1164,13 +1218,8 @@ int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client) err = ceph_msgpool_init(&osdc->msgpool_op, 4096, 10, true); if (err < 0) goto out_mempool; - err = ceph_msgpool_init(&osdc->msgpool_op_reply, 512, 0, false); - if (err < 0) - goto out_msgpool; return 0; -out_msgpool: - ceph_msgpool_destroy(&osdc->msgpool_op); out_mempool: mempool_destroy(osdc->req_mempool); out: @@ -1186,7 +1235,6 @@ void ceph_osdc_stop(struct ceph_osd_client *osdc) } mempool_destroy(osdc->req_mempool); ceph_msgpool_destroy(&osdc->msgpool_op); - ceph_msgpool_destroy(&osdc->msgpool_op_reply); } /* @@ -1323,17 +1371,17 @@ static struct ceph_msg *alloc_msg(struct ceph_connection *con, if (!req) { *skip = 1; m = NULL; - dout("prepare_pages unknown tid %llu\n", tid); + dout("alloc_msg unknown tid %llu\n", tid); goto out; } - m = ceph_msgpool_get(&osdc->msgpool_op_reply, front); - if (!m) { + m = __get_next_reply(con, req, front); + if (!m || IS_ERR(m)) { *skip = 1; goto out; } if (data_len > 0) { - err = prepare_pages(con, hdr, req, tid, m); + err = __prepare_pages(con, hdr, req, tid, m); if (err < 0) { *skip = 1; ceph_msg_put(m); diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index 4162c68..8d533d9 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -44,7 +44,7 @@ struct ceph_osd_request { struct ceph_osd *r_osd; struct ceph_pg r_pgid; - struct ceph_connection *r_con_filling_pages; + struct ceph_connection *r_con_filling_msg; struct ceph_msg *r_request, *r_reply; int r_result; @@ -75,6 +75,9 @@ struct ceph_osd_request { struct page **r_pages; /* pages for data payload */ int r_pages_from_pool; int r_own_pages; /* if true, i own page list */ + + struct ceph_msg *replies[2]; + int cur_reply; }; struct ceph_osd_client { @@ -98,8 +101,7 @@ struct ceph_osd_client { mempool_t *req_mempool; - struct ceph_msgpool msgpool_op; - struct ceph_msgpool msgpool_op_reply; + struct ceph_msgpool msgpool_op; }; extern int ceph_osdc_init(struct ceph_osd_client *osdc, -- cgit v0.10.2 From 361be8601d78e488b5249032cc4e779b81d7928e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 25 Jan 2010 16:03:02 -0800 Subject: ceph: precede encoded ceph_pg_pool struct with version Signed-off-by: Sage Weil diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index d8923fe..f3bfc3c 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -38,7 +38,7 @@ #define CEPH_OSD_PROTOCOL 8 /* cluster internal */ #define CEPH_MDS_PROTOCOL 9 /* cluster internal */ #define CEPH_MON_PROTOCOL 5 /* cluster internal */ -#define CEPH_OSDC_PROTOCOL 22 /* server/client */ +#define CEPH_OSDC_PROTOCOL 23 /* server/client */ #define CEPH_MDSC_PROTOCOL 32 /* server/client */ #define CEPH_MONC_PROTOCOL 15 /* server/client */ diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 0dbd606..a143c51 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -414,6 +414,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) struct ceph_osdmap *map; u16 version; u32 len, max, i; + u8 ev; int err = -EINVAL; void *start = *p; @@ -441,10 +442,11 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) } ceph_decode_32_safe(p, end, max, bad); while (max--) { - ceph_decode_need(p, end, 4+sizeof(map->pg_pool->v), bad); + ceph_decode_need(p, end, 4+1+sizeof(map->pg_pool->v), bad); i = ceph_decode_32(p); if (i >= map->num_pools) goto bad; + ev = ceph_decode_8(p); /* encoding version */ ceph_decode_copy(p, &map->pg_pool[i].v, sizeof(map->pg_pool->v)); calc_pg_masks(&map->pg_pool[i]); @@ -603,6 +605,8 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, /* new_pool */ ceph_decode_32_safe(p, end, len, bad); while (len--) { + __u8 ev; + ceph_decode_32_safe(p, end, pool, bad); if (pool >= map->num_pools) { void *pg_pool = kcalloc(pool + 1, @@ -618,6 +622,8 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, map->pg_pool = pg_pool; map->num_pools = pool+1; } + ceph_decode_need(p, end, 1 + sizeof(map->pg_pool->v), bad); + ev = ceph_decode_8(p); /* encoding version */ ceph_decode_copy(p, &map->pg_pool[pool].v, sizeof(map->pg_pool->v)); calc_pg_masks(&map->pg_pool[pool]); -- cgit v0.10.2 From d85127319cc36c38eb99615c52531c6ef2f11369 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Fri, 22 Jan 2010 10:50:42 +0900 Subject: ARM: S5PV210: Add serial port support This patch adds UART serial port support for S5PV210. Signed-off-by: Thomas Abraham Signed-off-by: Kukjin Kim Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-s3c/include/plat/regs-serial.h b/arch/arm/plat-s3c/include/plat/regs-serial.h index 85d8904..60d6604 100644 --- a/arch/arm/plat-s3c/include/plat/regs-serial.h +++ b/arch/arm/plat-s3c/include/plat/regs-serial.h @@ -194,6 +194,36 @@ #define S3C64XX_UINTSP 0x34 #define S3C64XX_UINTM 0x38 +/* Following are specific to S5PV210 and S5P6442 */ +#define S5PV210_UCON_CLKMASK (1<<10) +#define S5PV210_UCON_PCLK (0<<10) +#define S5PV210_UCON_UCLK (1<<10) + +#define S5PV210_UFCON_TXTRIG0 (0<<8) +#define S5PV210_UFCON_TXTRIG4 (1<<8) +#define S5PV210_UFCON_TXTRIG8 (2<<8) +#define S5PV210_UFCON_TXTRIG16 (3<<8) +#define S5PV210_UFCON_TXTRIG32 (4<<8) +#define S5PV210_UFCON_TXTRIG64 (5<<8) +#define S5PV210_UFCON_TXTRIG128 (6<<8) +#define S5PV210_UFCON_TXTRIG256 (7<<8) + +#define S5PV210_UFCON_RXTRIG1 (0<<4) +#define S5PV210_UFCON_RXTRIG4 (1<<4) +#define S5PV210_UFCON_RXTRIG8 (2<<4) +#define S5PV210_UFCON_RXTRIG16 (3<<4) +#define S5PV210_UFCON_RXTRIG32 (4<<4) +#define S5PV210_UFCON_RXTRIG64 (5<<4) +#define S5PV210_UFCON_RXTRIG128 (6<<4) +#define S5PV210_UFCON_RXTRIG256 (7<<4) + +#define S5PV210_UFSTAT_TXFULL (1<<24) +#define S5PV210_UFSTAT_RXFULL (1<<8) +#define S5PV210_UFSTAT_TXMASK (255<<16) +#define S5PV210_UFSTAT_TXSHIFT (16) +#define S5PV210_UFSTAT_RXMASK (255<<0) +#define S5PV210_UFSTAT_RXSHIFT (0) + #ifndef __ASSEMBLY__ /* struct s3c24xx_uart_clksrc diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index d7d687f..ebdd2b9 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -459,7 +459,7 @@ config SERIAL_SAMSUNG_UARTS int depends on ARM && PLAT_S3C default 2 if ARCH_S3C2400 - default 4 if ARCH_S5P6440 || ARCH_S5PC1XX || ARCH_S3C64XX || CPU_S3C2443 + default 4 if ARCH_S5P6440 || ARCH_S5PC1XX || ARCH_S5PV210 || ARCH_S3C64XX || CPU_S3C2443 default 3 help Select the number of available UART ports for the Samsung S3C @@ -540,6 +540,13 @@ config SERIAL_S5PC100 help Serial port support for the Samsung S5PC100 SoCs +config SERIAL_S5PV210 + tristate "Samsung S5PV210 Serial port support" + depends on SERIAL_SAMSUNG && CPU_S5PV210 + default y + help + Serial port support for Samsung's S5P Family of SoC's + config SERIAL_MAX3100 tristate "MAX3100 support" depends on SPI diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 5548fe7..6aa4723 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o obj-$(CONFIG_SERIAL_S5PC100) += s3c6400.o +obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o diff --git a/drivers/serial/s5pv210.c b/drivers/serial/s5pv210.c new file mode 100644 index 0000000..8dc0383 --- /dev/null +++ b/drivers/serial/s5pv210.c @@ -0,0 +1,154 @@ +/* linux/drivers/serial/s5pv210.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Based on drivers/serial/s3c6400.c + * + * Driver for Samsung S5PV210 SoC UARTs. + * + * 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 "samsung.h" + +static int s5pv210_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if (strcmp(clk->name, "pclk") == 0) + ucon &= ~S5PV210_UCON_CLKMASK; + else if (strcmp(clk->name, "uclk1") == 0) + ucon |= S5PV210_UCON_CLKMASK; + else { + printk(KERN_ERR "unknown clock source %s\n", clk->name); + return -EINVAL; + } + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + + +static int s5pv210_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + u32 ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + + switch (ucon & S5PV210_UCON_CLKMASK) { + case S5PV210_UCON_PCLK: + clk->name = "pclk"; + break; + case S5PV210_UCON_UCLK: + clk->name = "uclk1"; + break; + } + + return 0; +} + +static int s5pv210_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + ucon &= S5PV210_UCON_CLKMASK; + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +#define S5PV210_UART_DEFAULT_INFO(fifo_size) \ + .name = "Samsung S5PV210 UART0", \ + .type = PORT_S3C6400, \ + .fifosize = fifo_size, \ + .has_divslot = 1, \ + .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ + .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ + .rx_fifofull = S5PV210_UFSTAT_RXFULL, \ + .tx_fifofull = S5PV210_UFSTAT_TXFULL, \ + .tx_fifomask = S5PV210_UFSTAT_TXMASK, \ + .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT, \ + .get_clksrc = s5pv210_serial_getsource, \ + .set_clksrc = s5pv210_serial_setsource, \ + .reset_port = s5pv210_serial_resetport + +static struct s3c24xx_uart_info s5p_port_fifo256 = { + S5PV210_UART_DEFAULT_INFO(256), +}; + +static struct s3c24xx_uart_info s5p_port_fifo64 = { + S5PV210_UART_DEFAULT_INFO(64), +}; + +static struct s3c24xx_uart_info s5p_port_fifo16 = { + S5PV210_UART_DEFAULT_INFO(16), +}; + +static struct s3c24xx_uart_info *s5p_uart_inf[] = { + [0] = &s5p_port_fifo256, + [1] = &s5p_port_fifo64, + [2] = &s5p_port_fifo16, + [3] = &s5p_port_fifo16, +}; + +/* device management */ +static int s5p_serial_probe(struct platform_device *pdev) +{ + return s3c24xx_serial_probe(pdev, s5p_uart_inf[pdev->id]); +} + +static struct platform_driver s5p_serial_drv = { + .probe = s5p_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s5pv210-uart", + .owner = THIS_MODULE, + }, +}; + +static int __init s5pv210_serial_console_init(void) +{ + return s3c24xx_serial_initconsole(&s5p_serial_drv, s5p_uart_inf); +} + +console_initcall(s5pv210_serial_console_init); + +static int __init s5p_serial_init(void) +{ + return s3c24xx_serial_init(&s5p_serial_drv, *s5p_uart_inf); +} + +static void __exit s5p_serial_exit(void) +{ + platform_driver_unregister(&s5p_serial_drv); +} + +module_init(s5p_serial_init); +module_exit(s5p_serial_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:s5pv210-uart"); +MODULE_DESCRIPTION("Samsung S5PV210 UART Driver support"); +MODULE_AUTHOR("Thomas Abraham "); -- cgit v0.10.2 From f6b56704f78b2667fc8f61d2f398931fbc032b62 Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Fri, 22 Jan 2010 23:29:16 +0100 Subject: ARM: SAMSUNG: Fix null pointer dereference in ADC driver ARM: SAMSUNG: Fix null pointer dereference in ADC driver Use struct adc_device instead of relying on client (which could be NULL) when checking for SoC type. Signed-off-by: Maurus Cuelenaere Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c index 120b790..c7659b7 100644 --- a/arch/arm/plat-samsung/adc.c +++ b/arch/arm/plat-samsung/adc.c @@ -295,7 +295,7 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) } exit: - if (platform_get_device_id(client->pdev)->driver_data == TYPE_S3C64XX) { + if (platform_get_device_id(adc->pdev)->driver_data == TYPE_S3C64XX) { /* Clear ADC interrupt */ writel(0, adc->regs + S3C64XX_ADCCLRINT); } -- cgit v0.10.2 From ab5d97db1c6ced3e95c00d097931471707032b1f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 25 Jan 2010 10:39:23 +0900 Subject: ARM: SAMSUNG: Fix bad use of __initdata for s3c_register_clocks() Functions should be marked __init, not __initdata. Signed-off-by: Ben Dooks diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c index 0c746ae..e9cdbe4 100644 --- a/arch/arm/plat-samsung/clock.c +++ b/arch/arm/plat-samsung/clock.c @@ -344,7 +344,7 @@ int s3c24xx_register_clocks(struct clk **clks, int nr_clks) * Call s3c24xx_register_clock() on the @clkp array given, printing an * error if it fails to register the clock (unlikely). */ -void __initdata s3c_register_clocks(struct clk *clkp, int nr_clks) +void __init s3c_register_clocks(struct clk *clkp, int nr_clks) { int ret; -- cgit v0.10.2 From 431107ea5b680a24a4d01fbd3a178a3eb932f378 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 26 Jan 2010 10:11:04 +0900 Subject: ARM: S3C64XX: Merge mach-s3c6400 and mach-s3c6410 As per discussions with Russell King on linux-arm-kernel, it appears that both mach-s3c6400 and mach-s3c6410 are so close together that they should simply be merged into mach-s3c64xx. Note, this patch does not eliminate any of the bits that are still common, it is simply a move of the two directories together, any further common code will be eliminated or moved in further patches. Signed-off-by: Ben Dooks diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3bc5169..685ff7e 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -799,8 +799,7 @@ source "arch/arm/mach-s3c2443/Kconfig" endif if ARCH_S3C64XX -source "arch/arm/mach-s3c6400/Kconfig" -source "arch/arm/mach-s3c6410/Kconfig" +source "arch/arm/mach-s3c64xx/Kconfig" endif source "arch/arm/mach-s5p6440/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index bbcd512..ecf963d 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -160,7 +160,7 @@ machine-$(CONFIG_ARCH_REALVIEW) := realview machine-$(CONFIG_ARCH_RPC) := rpc machine-$(CONFIG_ARCH_S3C2410) := s3c2410 s3c2400 s3c2412 s3c2440 s3c2442 s3c2443 machine-$(CONFIG_ARCH_S3C24A0) := s3c24a0 -machine-$(CONFIG_ARCH_S3C64XX) := s3c6400 s3c6410 +machine-$(CONFIG_ARCH_S3C64XX) := s3c64xx machine-$(CONFIG_ARCH_S5P6440) := s5p6440 machine-$(CONFIG_ARCH_S5PC1XX) := s5pc100 machine-$(CONFIG_ARCH_SA1100) := sa1100 diff --git a/arch/arm/mach-s3c6400/Kconfig b/arch/arm/mach-s3c6400/Kconfig deleted file mode 100644 index a250bf6..0000000 --- a/arch/arm/mach-s3c6400/Kconfig +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2008 Openmoko, Inc. -# Simtec Electronics, Ben Dooks -# -# Licensed under GPLv2 - -# Configuration options for the S3C6410 CPU - -config CPU_S3C6400 - bool - select CPU_S3C6400_INIT - select CPU_S3C6400_CLOCK - help - Enable S3C6400 CPU support - -config S3C6400_SETUP_SDHCI - bool - help - Internal configuration for default SDHCI - setup for S3C6400. - -# S36400 Macchine support - -config MACH_SMDK6400 - bool "SMDK6400" - select CPU_S3C6400 - select S3C_DEV_HSMMC - select S3C_DEV_NAND - select S3C6400_SETUP_SDHCI - help - Machine support for the Samsung SMDK6400 diff --git a/arch/arm/mach-s3c6400/Makefile b/arch/arm/mach-s3c6400/Makefile deleted file mode 100644 index df1ce4a..0000000 --- a/arch/arm/mach-s3c6400/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# arch/arm/mach-s3c6400/Makefile -# -# Copyright 2008 Openmoko, Inc. -# Copyright 2008 Simtec Electronics -# -# Licensed under GPLv2 - -obj-y := -obj-m := -obj-n := -obj- := - -# Core support for S3C6400 system - -obj-$(CONFIG_CPU_S3C6400) += s3c6400.o - -# setup support - -obj-$(CONFIG_S3C6400_SETUP_SDHCI) += setup-sdhci.o - -# Machine support - -obj-$(CONFIG_MACH_SMDK6400) += mach-smdk6400.o diff --git a/arch/arm/mach-s3c6400/Makefile.boot b/arch/arm/mach-s3c6400/Makefile.boot deleted file mode 100644 index ba41fdc..0000000 --- a/arch/arm/mach-s3c6400/Makefile.boot +++ /dev/null @@ -1,2 +0,0 @@ - zreladdr-y := 0x50008000 -params_phys-y := 0x50000100 diff --git a/arch/arm/mach-s3c6400/include/mach/debug-macro.S b/arch/arm/mach-s3c6400/include/mach/debug-macro.S deleted file mode 100644 index b18ac52..0000000 --- a/arch/arm/mach-s3c6400/include/mach/debug-macro.S +++ /dev/null @@ -1,39 +0,0 @@ -/* arch/arm/mach-s3c6400/include/mach/debug-macro.S - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * 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. -*/ - -/* pull in the relevant register and map files. */ - -#include -#include - - /* note, for the boot process to work we have to keep the UART - * virtual address aligned to an 1MiB boundary for the L1 - * mapping the head code makes. We keep the UART virtual address - * aligned and add in the offset when we load the value here. - */ - - .macro addruart, rx - mrc p15, 0, \rx, c1, c0 - tst \rx, #1 - ldreq \rx, = S3C_PA_UART - ldrne \rx, = (S3C_VA_UART + S3C_PA_UART & 0xfffff) -#if CONFIG_DEBUG_S3C_UART != 0 - add \rx, \rx, #(0x400 * CONFIG_DEBUG_S3C_UART) -#endif - .endm - -/* include the reset of the code which will do the work, we're only - * compiling for a single cpu processor type so the default of s3c2440 - * will be fine with us. - */ - -#include diff --git a/arch/arm/mach-s3c6400/include/mach/dma.h b/arch/arm/mach-s3c6400/include/mach/dma.h deleted file mode 100644 index 6723860..0000000 --- a/arch/arm/mach-s3c6400/include/mach/dma.h +++ /dev/null @@ -1,70 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/dma.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C6400 - DMA support - */ - -#ifndef __ASM_ARCH_DMA_H -#define __ASM_ARCH_DMA_H __FILE__ - -#define S3C_DMA_CHANNELS (16) - -/* see mach-s3c2410/dma.h for notes on dma channel numbers */ - -/* Note, for the S3C64XX architecture we keep the DMACH_ - * defines in the order they are allocated to [S]DMA0/[S]DMA1 - * so that is easy to do DHACH_ -> DMA controller conversion - */ -enum dma_ch { - /* DMA0/SDMA0 */ - DMACH_UART0 = 0, - DMACH_UART0_SRC2, - DMACH_UART1, - DMACH_UART1_SRC2, - DMACH_UART2, - DMACH_UART2_SRC2, - DMACH_UART3, - DMACH_UART3_SRC2, - DMACH_PCM0_TX, - DMACH_PCM0_RX, - DMACH_I2S0_OUT, - DMACH_I2S0_IN, - DMACH_SPI0_TX, - DMACH_SPI0_RX, - DMACH_HSI_I2SV40_TX, - DMACH_HSI_I2SV40_RX, - - /* DMA1/SDMA1 */ - DMACH_PCM1_TX = 16, - DMACH_PCM1_RX, - DMACH_I2S1_OUT, - DMACH_I2S1_IN, - DMACH_SPI1_TX, - DMACH_SPI1_RX, - DMACH_AC97_PCMOUT, - DMACH_AC97_PCMIN, - DMACH_AC97_MICIN, - DMACH_PWM, - DMACH_IRDA, - DMACH_EXTERNAL, - DMACH_RES1, - DMACH_RES2, - DMACH_SECURITY_RX, /* SDMA1 only */ - DMACH_SECURITY_TX, /* SDMA1 only */ - DMACH_MAX /* the end */ -}; - -static __inline__ bool s3c_dma_has_circular(void) -{ - return true; -} - -#define S3C2410_DMAF_CIRCULAR (1 << 0) - -#include - -#endif /* __ASM_ARCH_IRQ_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/entry-macro.S b/arch/arm/mach-s3c6400/include/mach/entry-macro.S deleted file mode 100644 index 33a8fe2..0000000 --- a/arch/arm/mach-s3c6400/include/mach/entry-macro.S +++ /dev/null @@ -1,18 +0,0 @@ -/* arch/arm/mach-s3c6400/include/mach/entry-macro.S - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * Low-level IRQ helper macros for the Samsung S3C64XX series - * - * 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 - -#include diff --git a/arch/arm/mach-s3c6400/include/mach/gpio.h b/arch/arm/mach-s3c6400/include/mach/gpio.h deleted file mode 100644 index e8e35e8..0000000 --- a/arch/arm/mach-s3c6400/include/mach/gpio.h +++ /dev/null @@ -1,96 +0,0 @@ -/* arch/arm/mach-s3c6400/include/mach/gpio.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C6400 - GPIO lib 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. -*/ - -#define gpio_get_value __gpio_get_value -#define gpio_set_value __gpio_set_value -#define gpio_cansleep __gpio_cansleep -#define gpio_to_irq __gpio_to_irq - -/* GPIO bank sizes */ -#define S3C64XX_GPIO_A_NR (8) -#define S3C64XX_GPIO_B_NR (7) -#define S3C64XX_GPIO_C_NR (8) -#define S3C64XX_GPIO_D_NR (5) -#define S3C64XX_GPIO_E_NR (5) -#define S3C64XX_GPIO_F_NR (16) -#define S3C64XX_GPIO_G_NR (7) -#define S3C64XX_GPIO_H_NR (10) -#define S3C64XX_GPIO_I_NR (16) -#define S3C64XX_GPIO_J_NR (12) -#define S3C64XX_GPIO_K_NR (16) -#define S3C64XX_GPIO_L_NR (15) -#define S3C64XX_GPIO_M_NR (6) -#define S3C64XX_GPIO_N_NR (16) -#define S3C64XX_GPIO_O_NR (16) -#define S3C64XX_GPIO_P_NR (15) -#define S3C64XX_GPIO_Q_NR (9) - -/* GPIO bank numbes */ - -/* CONFIG_S3C_GPIO_SPACE allows the user to select extra - * space for debugging purposes so that any accidental - * change from one gpio bank to another can be caught. -*/ - -#define S3C64XX_GPIO_NEXT(__gpio) \ - ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1) - -enum s3c_gpio_number { - S3C64XX_GPIO_A_START = 0, - S3C64XX_GPIO_B_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_A), - S3C64XX_GPIO_C_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_B), - S3C64XX_GPIO_D_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_C), - S3C64XX_GPIO_E_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_D), - S3C64XX_GPIO_F_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_E), - S3C64XX_GPIO_G_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_F), - S3C64XX_GPIO_H_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_G), - S3C64XX_GPIO_I_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_H), - S3C64XX_GPIO_J_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_I), - S3C64XX_GPIO_K_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_J), - S3C64XX_GPIO_L_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_K), - S3C64XX_GPIO_M_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_L), - S3C64XX_GPIO_N_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_M), - S3C64XX_GPIO_O_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_N), - S3C64XX_GPIO_P_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_O), - S3C64XX_GPIO_Q_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_P), -}; - -/* S3C64XX GPIO number definitions. */ - -#define S3C64XX_GPA(_nr) (S3C64XX_GPIO_A_START + (_nr)) -#define S3C64XX_GPB(_nr) (S3C64XX_GPIO_B_START + (_nr)) -#define S3C64XX_GPC(_nr) (S3C64XX_GPIO_C_START + (_nr)) -#define S3C64XX_GPD(_nr) (S3C64XX_GPIO_D_START + (_nr)) -#define S3C64XX_GPE(_nr) (S3C64XX_GPIO_E_START + (_nr)) -#define S3C64XX_GPF(_nr) (S3C64XX_GPIO_F_START + (_nr)) -#define S3C64XX_GPG(_nr) (S3C64XX_GPIO_G_START + (_nr)) -#define S3C64XX_GPH(_nr) (S3C64XX_GPIO_H_START + (_nr)) -#define S3C64XX_GPI(_nr) (S3C64XX_GPIO_I_START + (_nr)) -#define S3C64XX_GPJ(_nr) (S3C64XX_GPIO_J_START + (_nr)) -#define S3C64XX_GPK(_nr) (S3C64XX_GPIO_K_START + (_nr)) -#define S3C64XX_GPL(_nr) (S3C64XX_GPIO_L_START + (_nr)) -#define S3C64XX_GPM(_nr) (S3C64XX_GPIO_M_START + (_nr)) -#define S3C64XX_GPN(_nr) (S3C64XX_GPIO_N_START + (_nr)) -#define S3C64XX_GPO(_nr) (S3C64XX_GPIO_O_START + (_nr)) -#define S3C64XX_GPP(_nr) (S3C64XX_GPIO_P_START + (_nr)) -#define S3C64XX_GPQ(_nr) (S3C64XX_GPIO_Q_START + (_nr)) - -/* the end of the S3C64XX specific gpios */ -#define S3C64XX_GPIO_END (S3C64XX_GPQ(S3C64XX_GPIO_Q_NR) + 1) -#define S3C_GPIO_END S3C64XX_GPIO_END - -/* define the number of gpios we need to the one after the GPQ() range */ -#define ARCH_NR_GPIOS (S3C64XX_GPQ(S3C64XX_GPIO_Q_NR) + 1) - -#include diff --git a/arch/arm/mach-s3c6400/include/mach/hardware.h b/arch/arm/mach-s3c6400/include/mach/hardware.h deleted file mode 100644 index 862d033..0000000 --- a/arch/arm/mach-s3c6400/include/mach/hardware.h +++ /dev/null @@ -1,16 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/hardware.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C6400 - Hardware support - */ - -#ifndef __ASM_ARCH_HARDWARE_H -#define __ASM_ARCH_HARDWARE_H __FILE__ - -/* currently nothing here, placeholder */ - -#endif /* __ASM_ARCH_IRQ_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/irqs.h b/arch/arm/mach-s3c6400/include/mach/irqs.h deleted file mode 100644 index 4c97f9a..0000000 --- a/arch/arm/mach-s3c6400/include/mach/irqs.h +++ /dev/null @@ -1,16 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/irqs.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C6400 - IRQ definitions - */ - -#ifndef __ASM_ARCH_IRQS_H -#define __ASM_ARCH_IRQS_H __FILE__ - -#include - -#endif /* __ASM_ARCH_IRQ_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h deleted file mode 100644 index 801c1c0..0000000 --- a/arch/arm/mach-s3c6400/include/mach/map.h +++ /dev/null @@ -1,107 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/map.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C64XX - Memory map definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __ASM_ARCH_MAP_H -#define __ASM_ARCH_MAP_H __FILE__ - -#include - -/* - * Post-mux Chip Select Regions Xm0CSn_ - * These may be used by SROM, NAND or CF depending on settings - */ - -#define S3C64XX_PA_XM0CSN0 (0x10000000) -#define S3C64XX_PA_XM0CSN1 (0x18000000) -#define S3C64XX_PA_XM0CSN2 (0x20000000) -#define S3C64XX_PA_XM0CSN3 (0x28000000) -#define S3C64XX_PA_XM0CSN4 (0x30000000) -#define S3C64XX_PA_XM0CSN5 (0x38000000) - -/* HSMMC units */ -#define S3C64XX_PA_HSMMC(x) (0x7C200000 + ((x) * 0x100000)) -#define S3C64XX_PA_HSMMC0 S3C64XX_PA_HSMMC(0) -#define S3C64XX_PA_HSMMC1 S3C64XX_PA_HSMMC(1) -#define S3C64XX_PA_HSMMC2 S3C64XX_PA_HSMMC(2) - -#define S3C_PA_UART (0x7F005000) -#define S3C_PA_UART0 (S3C_PA_UART + 0x00) -#define S3C_PA_UART1 (S3C_PA_UART + 0x400) -#define S3C_PA_UART2 (S3C_PA_UART + 0x800) -#define S3C_PA_UART3 (S3C_PA_UART + 0xC00) -#define S3C_UART_OFFSET (0x400) - -/* See notes on UART VA mapping in debug-macro.S */ -#define S3C_VA_UARTx(x) (S3C_VA_UART + (S3C_PA_UART & 0xfffff) + ((x) * S3C_UART_OFFSET)) - -#define S3C_VA_UART0 S3C_VA_UARTx(0) -#define S3C_VA_UART1 S3C_VA_UARTx(1) -#define S3C_VA_UART2 S3C_VA_UARTx(2) -#define S3C_VA_UART3 S3C_VA_UARTx(3) - -#define S3C64XX_PA_SROM (0x70000000) - -#define S3C64XX_PA_NAND (0x70200000) -#define S3C64XX_PA_FB (0x77100000) -#define S3C64XX_PA_USB_HSOTG (0x7C000000) -#define S3C64XX_PA_WATCHDOG (0x7E004000) -#define S3C64XX_PA_RTC (0x7E005000) -#define S3C64XX_PA_ADC (0x7E00B000) -#define S3C64XX_PA_SYSCON (0x7E00F000) -#define S3C64XX_PA_AC97 (0x7F001000) -#define S3C64XX_PA_IIS0 (0x7F002000) -#define S3C64XX_PA_IIS1 (0x7F003000) -#define S3C64XX_PA_TIMER (0x7F006000) -#define S3C64XX_PA_IIC0 (0x7F004000) -#define S3C64XX_PA_SPI0 (0x7F00B000) -#define S3C64XX_PA_SPI1 (0x7F00C000) -#define S3C64XX_PA_PCM0 (0x7F009000) -#define S3C64XX_PA_PCM1 (0x7F00A000) -#define S3C64XX_PA_IISV4 (0x7F00D000) -#define S3C64XX_PA_IIC1 (0x7F00F000) - -#define S3C64XX_PA_GPIO (0x7F008000) -#define S3C64XX_VA_GPIO S3C_ADDR_CPU(0x00000000) -#define S3C64XX_SZ_GPIO SZ_4K - -#define S3C64XX_PA_SDRAM (0x50000000) -#define S3C64XX_PA_VIC0 (0x71200000) -#define S3C64XX_PA_VIC1 (0x71300000) - -#define S3C64XX_PA_MODEM (0x74108000) -#define S3C64XX_VA_MODEM S3C_ADDR_CPU(0x00100000) - -#define S3C64XX_PA_USBHOST (0x74300000) - -#define S3C64XX_PA_USB_HSPHY (0x7C100000) -#define S3C64XX_VA_USB_HSPHY S3C_ADDR_CPU(0x00200000) - -/* place VICs close together */ -#define VA_VIC0 (S3C_VA_IRQ + 0x00) -#define VA_VIC1 (S3C_VA_IRQ + 0x10000) - -/* compatibiltiy defines. */ -#define S3C_PA_TIMER S3C64XX_PA_TIMER -#define S3C_PA_HSMMC0 S3C64XX_PA_HSMMC0 -#define S3C_PA_HSMMC1 S3C64XX_PA_HSMMC1 -#define S3C_PA_HSMMC2 S3C64XX_PA_HSMMC2 -#define S3C_PA_IIC S3C64XX_PA_IIC0 -#define S3C_PA_IIC1 S3C64XX_PA_IIC1 -#define S3C_PA_NAND S3C64XX_PA_NAND -#define S3C_PA_FB S3C64XX_PA_FB -#define S3C_PA_USBHOST S3C64XX_PA_USBHOST -#define S3C_PA_USB_HSOTG S3C64XX_PA_USB_HSOTG -#define S3C_VA_USB_HSPHY S3C64XX_VA_USB_HSPHY - -#endif /* __ASM_ARCH_6400_MAP_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/memory.h b/arch/arm/mach-s3c6400/include/mach/memory.h deleted file mode 100644 index a3ac84a..0000000 --- a/arch/arm/mach-s3c6400/include/mach/memory.h +++ /dev/null @@ -1,18 +0,0 @@ -/* arch/arm/mach-s3c6400/include/mach/memory.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * 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 __ASM_ARCH_MEMORY_H -#define __ASM_ARCH_MEMORY_H - -#define PHYS_OFFSET UL(0x50000000) - -#endif diff --git a/arch/arm/mach-s3c6400/include/mach/pwm-clock.h b/arch/arm/mach-s3c6400/include/mach/pwm-clock.h deleted file mode 100644 index b25bede..0000000 --- a/arch/arm/mach-s3c6400/include/mach/pwm-clock.h +++ /dev/null @@ -1,56 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/pwm-clock.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C64xx - pwm clock and timer support - */ - -/** - * pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk - * @tcfg: The timer TCFG1 register bits shifted down to 0. - * - * Return true if the given configuration from TCFG1 is a TCLK instead - * any of the TDIV clocks. - */ -static inline int pwm_cfg_src_is_tclk(unsigned long tcfg) -{ - return tcfg >= S3C64XX_TCFG1_MUX_TCLK; -} - -/** - * tcfg_to_divisor() - convert tcfg1 setting to a divisor - * @tcfg1: The tcfg1 setting, shifted down. - * - * Get the divisor value for the given tcfg1 setting. We assume the - * caller has already checked to see if this is not a TCLK source. - */ -static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) -{ - return 1 << tcfg1; -} - -/** - * pwm_tdiv_has_div1() - does the tdiv setting have a /1 - * - * Return true if we have a /1 in the tdiv setting. - */ -static inline unsigned int pwm_tdiv_has_div1(void) -{ - return 1; -} - -/** - * pwm_tdiv_div_bits() - calculate TCFG1 divisor value. - * @div: The divisor to calculate the bit information for. - * - * Turn a divisor into the necessary bit field for TCFG1. - */ -static inline unsigned long pwm_tdiv_div_bits(unsigned int div) -{ - return ilog2(div); -} - -#define S3C_TCFG1_MUX_TCLK S3C64XX_TCFG1_MUX_TCLK diff --git a/arch/arm/mach-s3c6400/include/mach/regs-clock.h b/arch/arm/mach-s3c6400/include/mach/regs-clock.h deleted file mode 100644 index a6c7f4e..0000000 --- a/arch/arm/mach-s3c6400/include/mach/regs-clock.h +++ /dev/null @@ -1,16 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/regs-clock.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C64XX - clock register compatibility with s3c24xx - * - * 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 - diff --git a/arch/arm/mach-s3c6400/include/mach/regs-fb.h b/arch/arm/mach-s3c6400/include/mach/regs-fb.h deleted file mode 100644 index f566115..0000000 --- a/arch/arm/mach-s3c6400/include/mach/regs-fb.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Copyright 2009 Samsung Electronics Co. - * - * Pawel Osciak - * Based on plat-s3c/include/plat/regs-fb.h by Ben Dooks - * - * Framebuffer register definitions for Samsung S3C64xx. - * - * 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 __ASM_ARCH_MACH_REGS_FB_H -#define __ASM_ARCH_MACH_REGS_FB_H __FILE__ - -#include - -/* Palette registers */ -#define WIN2_PAL(_entry) (0x300 + ((_entry) * 2)) -#define WIN3_PAL(_entry) (0x320 + ((_entry) * 2)) -#define WIN4_PAL(_entry) (0x340 + ((_entry) * 2)) -#define WIN0_PAL(_entry) (0x400 + ((_entry) * 4)) -#define WIN1_PAL(_entry) (0x800 + ((_entry) * 4)) - -static inline unsigned int s3c_fb_pal_reg(unsigned int window, int reg) -{ - switch (window) { - case 0: return WIN0_PAL(reg); - case 1: return WIN1_PAL(reg); - case 2: return WIN2_PAL(reg); - case 3: return WIN3_PAL(reg); - case 4: return WIN4_PAL(reg); - } - - BUG(); -} - -#endif /* __ASM_ARCH_MACH_REGS_FB_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/regs-irq.h b/arch/arm/mach-s3c6400/include/mach/regs-irq.h deleted file mode 100644 index bcce68a..0000000 --- a/arch/arm/mach-s3c6400/include/mach/regs-irq.h +++ /dev/null @@ -1,20 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/regs-irq.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C64XX - IRQ register definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __ASM_ARCH_REGS_IRQ_H -#define __ASM_ARCH_REGS_IRQ_H __FILE__ - -#include - -#endif /* __ASM_ARCH_6400_REGS_IRQ_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/system.h b/arch/arm/mach-s3c6400/include/mach/system.h deleted file mode 100644 index 2e58cb7..0000000 --- a/arch/arm/mach-s3c6400/include/mach/system.h +++ /dev/null @@ -1,30 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/system.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C6400 - system implementation - */ - -#ifndef __ASM_ARCH_SYSTEM_H -#define __ASM_ARCH_SYSTEM_H __FILE__ - -#include - -static void arch_idle(void) -{ - /* nothing here yet */ -} - -static void arch_reset(char mode, const char *cmd) -{ - if (mode != 's') - arch_wdt_reset(); - - /* if all else fails, or mode was for soft, jump to 0 */ - cpu_reset(0); -} - -#endif /* __ASM_ARCH_IRQ_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/tick.h b/arch/arm/mach-s3c6400/include/mach/tick.h deleted file mode 100644 index ebe18a9..0000000 --- a/arch/arm/mach-s3c6400/include/mach/tick.h +++ /dev/null @@ -1,29 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/include/mach/tick.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C64XX - Timer tick support definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __ASM_ARCH_TICK_H -#define __ASM_ARCH_TICK_H __FILE__ - -/* note, the timer interrutps turn up in 2 places, the vic and then - * the timer block. We take the VIC as the base at the moment. - */ -static inline u32 s3c24xx_ostimer_pending(void) -{ - u32 pend = __raw_readl(VA_VIC0 + VIC_RAW_STATUS); - return pend & 1 << (IRQ_TIMER4_VIC - S3C64XX_IRQ_VIC0(0)); -} - -#define TICK_MAX (0xffffffff) - -#endif /* __ASM_ARCH_6400_TICK_H */ diff --git a/arch/arm/mach-s3c6400/include/mach/uncompress.h b/arch/arm/mach-s3c6400/include/mach/uncompress.h deleted file mode 100644 index c6a82a2..0000000 --- a/arch/arm/mach-s3c6400/include/mach/uncompress.h +++ /dev/null @@ -1,28 +0,0 @@ -/* arch/arm/mach-s3c6400/include/mach/uncompress.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C6400 - uncompress code - * - * 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 __ASM_ARCH_UNCOMPRESS_H -#define __ASM_ARCH_UNCOMPRESS_H - -#include -#include - -static void arch_detect_cpu(void) -{ - /* we do not need to do any cpu detection here at the moment. */ - fifo_mask = S3C2440_UFSTAT_TXMASK; - fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT; -} - -#endif /* __ASM_ARCH_UNCOMPRESS_H */ diff --git a/arch/arm/mach-s3c6400/mach-smdk6400.c b/arch/arm/mach-s3c6400/mach-smdk6400.c deleted file mode 100644 index ab19285..0000000 --- a/arch/arm/mach-s3c6400/mach-smdk6400.c +++ /dev/null @@ -1,96 +0,0 @@ -/* linux/arch/arm/mach-s3c6400/mach-smdk6400.c - * - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * 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 - -#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK -#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB -#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE - -static struct s3c2410_uartcfg smdk6400_uartcfgs[] __initdata = { - [0] = { - .hwport = 0, - .flags = 0, - .ucon = 0x3c5, - .ulcon = 0x03, - .ufcon = 0x51, - }, - [1] = { - .hwport = 1, - .flags = 0, - .ucon = 0x3c5, - .ulcon = 0x03, - .ufcon = 0x51, - }, -}; - -static struct map_desc smdk6400_iodesc[] = {}; - -static void __init smdk6400_map_io(void) -{ - s3c64xx_init_io(smdk6400_iodesc, ARRAY_SIZE(smdk6400_iodesc)); - s3c24xx_init_clocks(12000000); - s3c24xx_init_uarts(smdk6400_uartcfgs, ARRAY_SIZE(smdk6400_uartcfgs)); -} - -static struct platform_device *smdk6400_devices[] __initdata = { - &s3c_device_hsmmc1, - &s3c_device_i2c0, -}; - -static struct i2c_board_info i2c_devs[] __initdata = { - { I2C_BOARD_INFO("wm8753", 0x1A), }, - { I2C_BOARD_INFO("24c08", 0x50), }, -}; - -static void __init smdk6400_machine_init(void) -{ - i2c_register_board_info(0, i2c_devs, ARRAY_SIZE(i2c_devs)); - platform_add_devices(smdk6400_devices, ARRAY_SIZE(smdk6400_devices)); -} - -MACHINE_START(SMDK6400, "SMDK6400") - /* Maintainer: Ben Dooks */ - .phys_io = S3C_PA_UART & 0xfff00000, - .io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc, - .boot_params = S3C64XX_PA_SDRAM + 0x100, - - .init_irq = s3c6400_init_irq, - .map_io = smdk6400_map_io, - .init_machine = smdk6400_machine_init, - .timer = &s3c24xx_timer, -MACHINE_END diff --git a/arch/arm/mach-s3c6400/s3c6400.c b/arch/arm/mach-s3c6400/s3c6400.c deleted file mode 100644 index d876ee5..0000000 --- a/arch/arm/mach-s3c6400/s3c6400.c +++ /dev/null @@ -1,92 +0,0 @@ -/* linux/arch/arm/mach-s3c6410/cpu.c - * - * Copyright 2009 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * 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 -#include - -void __init s3c6400_map_io(void) -{ - /* setup SDHCI */ - - s3c6400_default_sdhci0(); - s3c6400_default_sdhci1(); - s3c6400_default_sdhci2(); - - /* the i2c devices are directly compatible with s3c2440 */ - s3c_i2c0_setname("s3c2440-i2c"); - - s3c_device_nand.name = "s3c6400-nand"; -} - -void __init s3c6400_init_clocks(int xtal) -{ - printk(KERN_DEBUG "%s: initialising clocks\n", __func__); - s3c24xx_register_baseclocks(xtal); - s3c64xx_register_clocks(); - s3c6400_register_clocks(S3C6400_CLKDIV0_ARM_MASK); - s3c6400_setup_clocks(); -} - -void __init s3c6400_init_irq(void) -{ - /* VIC0 does not have IRQS 5..7, - * VIC1 is fully populated. */ - s3c64xx_init_irq(~0 & ~(0xf << 5), ~0); -} - -struct sysdev_class s3c6400_sysclass = { - .name = "s3c6400-core", -}; - -static struct sys_device s3c6400_sysdev = { - .cls = &s3c6400_sysclass, -}; - -static int __init s3c6400_core_init(void) -{ - return sysdev_class_register(&s3c6400_sysclass); -} - -core_initcall(s3c6400_core_init); - -int __init s3c6400_init(void) -{ - printk("S3C6400: Initialising architecture\n"); - - return sysdev_register(&s3c6400_sysdev); -} diff --git a/arch/arm/mach-s3c6400/setup-sdhci.c b/arch/arm/mach-s3c6400/setup-sdhci.c deleted file mode 100644 index 1039937..0000000 --- a/arch/arm/mach-s3c6400/setup-sdhci.c +++ /dev/null @@ -1,63 +0,0 @@ -/* linux/arch/arm/mach-s3c6410/setup-sdhci.c - * - * Copyright 2008 Simtec Electronics - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C6410 - Helper functions for settign up SDHCI device(s) (HSMMC) - * - * 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 - -/* clock sources for the mmc bus clock, order as for the ctrl2[5..4] */ - -char *s3c6400_hsmmc_clksrcs[4] = { - [0] = "hsmmc", - [1] = "hsmmc", - [2] = "mmc_bus", - /* [3] = "48m", - note not successfully used yet */ -}; - -void s3c6400_setup_sdhci_cfg_card(struct platform_device *dev, - void __iomem *r, - struct mmc_ios *ios, - struct mmc_card *card) -{ - u32 ctrl2, ctrl3; - - ctrl2 = readl(r + S3C_SDHCI_CONTROL2); - ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK; - ctrl2 |= (S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR | - S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK | - S3C_SDHCI_CTRL2_ENFBCLKRX | - S3C_SDHCI_CTRL2_DFCNT_NONE | - S3C_SDHCI_CTRL2_ENCLKOUTHOLD); - - if (ios->clock < 25 * 1000000) - ctrl3 = (S3C_SDHCI_CTRL3_FCSEL3 | - S3C_SDHCI_CTRL3_FCSEL2 | - S3C_SDHCI_CTRL3_FCSEL1 | - S3C_SDHCI_CTRL3_FCSEL0); - else - ctrl3 = (S3C_SDHCI_CTRL3_FCSEL1 | S3C_SDHCI_CTRL3_FCSEL0); - - printk(KERN_INFO "%s: CTRL 2=%08x, 3=%08x\n", __func__, ctrl2, ctrl3); - writel(ctrl2, r + S3C_SDHCI_CONTROL2); - writel(ctrl3, r + S3C_SDHCI_CONTROL3); -} - diff --git a/arch/arm/mach-s3c6410/Kconfig b/arch/arm/mach-s3c6410/Kconfig deleted file mode 100644 index 162f456..0000000 --- a/arch/arm/mach-s3c6410/Kconfig +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright 2008 Openmoko, Inc. -# Copyright 2008 Simtec Electronics -# -# Licensed under GPLv2 - -# Configuration options for the S3C6410 CPU - -config CPU_S3C6410 - bool - select CPU_S3C6400_INIT - select CPU_S3C6400_CLOCK - help - Enable S3C6410 CPU support - -config S3C6410_SETUP_SDHCI - bool - select S3C64XX_SETUP_SDHCI_GPIO - help - Internal helper functions for S3C6410 based SDHCI systems - -config MACH_ANW6410 - bool "A&W6410" - select CPU_S3C6410 - select S3C_DEV_FB - select S3C64XX_SETUP_FB_24BPP - help - Machine support for the A&W6410 - -config MACH_SMDK6410 - bool "SMDK6410" - select CPU_S3C6410 - select S3C_DEV_HSMMC - select S3C_DEV_HSMMC1 - select S3C_DEV_I2C1 - select S3C_DEV_FB - select S3C_DEV_USB_HOST - select S3C_DEV_USB_HSOTG - select S3C6410_SETUP_SDHCI - select S3C64XX_SETUP_I2C1 - select S3C64XX_SETUP_FB_24BPP - help - Machine support for the Samsung SMDK6410 - -# At least some of the SMDK6410s were shipped with the card detect -# for the MMC/SD slots connected to the same input. This means that -# either the boards need to be altered to have channel0 to an alternate -# configuration or that only one slot can be used. - -choice - prompt "SMDK6410 MMC/SD slot setup" - depends on MACH_SMDK6410 - -config SMDK6410_SD_CH0 - bool "Use channel 0 only" - depends on MACH_SMDK6410 - help - Select CON7 (channel 0) as the MMC/SD slot, as - at least some SMDK6410 boards come with the - resistors fitted so that the card detects for - channels 0 and 1 are the same. - -config SMDK6410_SD_CH1 - bool "Use channel 1 only" - depends on MACH_SMDK6410 - help - Select CON6 (channel 1) as the MMC/SD slot, as - at least some SMDK6410 boards come with the - resistors fitted so that the card detects for - channels 0 and 1 are the same. - -endchoice - -config SMDK6410_WM1190_EV1 - bool "Support Wolfson Microelectronics 1190-EV1 PMIC card" - depends on MACH_SMDK6410 - select REGULATOR - select REGULATOR_WM8350 - select S3C24XX_GPIO_EXTRA64 - select MFD_WM8350_I2C - select MFD_WM8350_CONFIG_MODE_0 - select MFD_WM8350_CONFIG_MODE_3 - select MFD_WM8352_CONFIG_MODE_0 - help - The Wolfson Microelectronics 1190-EV1 is a WM835x based PMIC - and audio daughtercard for the Samsung SMDK6410 reference - platform. Enabling this option will build support for this - module into the kernel. The presence of the module will be - detected at runtime so the the resulting kernel can be used - with or without the 1190-EV1 fitted. - -config MACH_NCP - bool "NCP" - select CPU_S3C6410 - select S3C_DEV_I2C1 - select S3C_DEV_HSMMC1 - select S3C64XX_SETUP_I2C1 - help - Machine support for the Samsung NCP - -config MACH_HMT - bool "Airgoo HMT" - select CPU_S3C6410 - select S3C_DEV_FB - select S3C_DEV_NAND - select S3C_DEV_USB_HOST - select S3C64XX_SETUP_FB_24BPP - select HAVE_PWM - help - Machine support for the Airgoo HMT diff --git a/arch/arm/mach-s3c6410/Makefile b/arch/arm/mach-s3c6410/Makefile deleted file mode 100644 index 3e48c3d..0000000 --- a/arch/arm/mach-s3c6410/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# arch/arm/plat-s3c6410/Makefile -# -# Copyright 2008 Openmoko, Inc. -# Copyright 2008 Simtec Electronics -# -# Licensed under GPLv2 - -obj-y := -obj-m := -obj-n := -obj- := - -# Core support for S3C6410 system - -obj-$(CONFIG_CPU_S3C6410) += cpu.o - -# Helper and device support - -obj-$(CONFIG_S3C6410_SETUP_SDHCI) += setup-sdhci.o - -# machine support - -obj-$(CONFIG_MACH_ANW6410) += mach-anw6410.o -obj-$(CONFIG_MACH_SMDK6410) += mach-smdk6410.o -obj-$(CONFIG_MACH_NCP) += mach-ncp.o -obj-$(CONFIG_MACH_HMT) += mach-hmt.o diff --git a/arch/arm/mach-s3c6410/cpu.c b/arch/arm/mach-s3c6410/cpu.c deleted file mode 100644 index 522c086..0000000 --- a/arch/arm/mach-s3c6410/cpu.c +++ /dev/null @@ -1,105 +0,0 @@ -/* linux/arch/arm/mach-s3c6410/cpu.c - * - * Copyright 2008 Simtec Electronics - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * 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 -#include -#include - -/* Initial IO mappings */ - -static struct map_desc s3c6410_iodesc[] __initdata = { -}; - -/* s3c6410_map_io - * - * register the standard cpu IO areas -*/ - -void __init s3c6410_map_io(void) -{ - iotable_init(s3c6410_iodesc, ARRAY_SIZE(s3c6410_iodesc)); - - /* initialise device information early */ - s3c6410_default_sdhci0(); - s3c6410_default_sdhci1(); - s3c6410_default_sdhci2(); - - /* the i2c devices are directly compatible with s3c2440 */ - s3c_i2c0_setname("s3c2440-i2c"); - s3c_i2c1_setname("s3c2440-i2c"); - - s3c_device_nand.name = "s3c6400-nand"; -} - -void __init s3c6410_init_clocks(int xtal) -{ - printk(KERN_DEBUG "%s: initialising clocks\n", __func__); - s3c24xx_register_baseclocks(xtal); - s3c64xx_register_clocks(); - s3c6400_register_clocks(S3C6410_CLKDIV0_ARM_MASK); - s3c6400_setup_clocks(); -} - -void __init s3c6410_init_irq(void) -{ - /* VIC0 is missing IRQ7, VIC1 is fully populated. */ - s3c64xx_init_irq(~0 & ~(1 << 7), ~0); -} - -struct sysdev_class s3c6410_sysclass = { - .name = "s3c6410-core", -}; - -static struct sys_device s3c6410_sysdev = { - .cls = &s3c6410_sysclass, -}; - -static int __init s3c6410_core_init(void) -{ - return sysdev_class_register(&s3c6410_sysclass); -} - -core_initcall(s3c6410_core_init); - -int __init s3c6410_init(void) -{ - printk("S3C6410: Initialising architecture\n"); - - return sysdev_register(&s3c6410_sysdev); -} diff --git a/arch/arm/mach-s3c6410/mach-anw6410.c b/arch/arm/mach-s3c6410/mach-anw6410.c deleted file mode 100644 index 661cca6..0000000 --- a/arch/arm/mach-s3c6410/mach-anw6410.c +++ /dev/null @@ -1,245 +0,0 @@ -/* linux/arch/arm/mach-s3c6410/mach-anw6410.c - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * Copyright 2009 Kwangwoo Lee - * Kwangwoo Lee - * - * 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